69 Commits

Author SHA1 Message Date
wish
6b32e36aa4 update GetUdMonsterPoint 2024-02-25 21:54:41 +11:00
stratic-dev
6e7faf9900 Merge branch 'main' into feature/diva 2024-02-20 21:01:41 +00:00
stratic-dev
4ddcf3691b Merge branch 'main' into feature/diva 2024-02-15 23:09:37 +00:00
stratic-dev
3310248239 Merge branch 'main' into feature/diva 2024-02-10 17:42:57 +00:00
stratic-dev
e1f12eaab4 Merge branch 'main' into feature/diva 2024-02-09 16:48:15 +00:00
stratic-dev
b56e69f8fc fix rookie error 2024-02-09 16:48:05 +00:00
stratic-dev
3208e757fd Bumped to upstream 2024-02-09 15:33:26 +00:00
wish
fb3ecd0305 decode GetUdGuildMapInfo more 2023-10-30 23:08:52 +11:00
wish
e3f9b66257 fix cleanupDiva 2023-10-28 14:32:09 +11:00
wish
551038272b track Kiju & UdPoint values 2023-10-28 14:30:37 +11:00
wish
06e32cae6e decode UD packets & preliminary handling & schema 2023-10-27 00:02:35 +11:00
wish
d32cf39eea decode GetUdMyPoint 2023-10-25 23:37:54 +11:00
wish
625b58375f decode various diva packets 2023-10-25 22:50:17 +11:00
wish
1e37b3c348 decode various diva values 2023-10-24 23:51:42 +11:00
wish
db74f7ee5f Merge remote-tracking branch 'origin/main' into feature/diva 2023-10-24 23:03:33 +11:00
wish
cbe65f68fc sort imports 2023-10-24 23:01:22 +11:00
wish
e852bad657 Merge remote-tracking branch 'origin/main' into feature/diva
# Conflicts:
#	server/channelserver/handlers_diva.go
2023-10-24 22:31:08 +11:00
wish
ed414d2310 Merge branch 'main' into feature/diva
# Conflicts:
#	bundled-schema/DivaShops.sql
#	bundled-schema/OtherShops.sql
#	patch-schema/shop-db.sql
#	server/channelserver/handlers_guild.go
#	server/channelserver/handlers_tactics.go
2023-04-15 13:57:33 +10:00
wish
5fe0264b25 Merge branch 'main' into feature/diva 2023-03-10 01:14:22 +11:00
wish
3fff961e44 Merge branch 'main' into feature/diva 2023-02-28 20:22:35 +11:00
wish
60aae09c31 generate random prayer beads 2023-02-28 18:16:17 +11:00
wish
0eb4a04264 decode GetUdTotalPointInfo 2023-02-28 18:14:59 +11:00
wish
40fe5fc233 simplify shop enumeration 2023-02-28 18:12:24 +11:00
wish
60ab165729 rewrite shop item enumeration code 2023-02-28 03:55:08 +11:00
wish
aecacbaed4 remove unused prayer bead 2023-02-25 15:43:20 +11:00
wish
31f994c42f preliminary prayer bead translations 2023-02-20 23:19:12 +11:00
wish
165555c286 use ShopItem struct to write responses 2023-02-20 00:34:11 +11:00
wish
ea0b620690 decode GetUdSelectedColorInfo 2023-02-20 00:32:50 +11:00
wish
543f5c129c decode AcquireUdItem 2023-02-19 22:45:44 +11:00
wish
7b694c2ce1 convert interception clear bonus to struct 2023-02-19 22:45:15 +11:00
wish
6a22f33e22 Merge remote-tracking branch 'origin/feature/diva' into feature/diva 2023-02-19 22:44:53 +11:00
wish
eae33b8e92 convert interception rewards to db table 2023-02-19 22:44:36 +11:00
wish
e2fedd7c6d convert interception rewards to db table 2023-02-19 22:44:22 +11:00
wish
1bea151bbb stub diva exchange shop 2023-02-19 17:17:46 +11:00
wish
1c0c810a83 Merge remote-tracking branch 'origin/feature/diva' into feature/diva 2023-02-19 17:17:06 +11:00
wish
e5bd9e8e4e rewrite interception map to have unique point trackers 2023-02-19 17:16:52 +11:00
wish
6dda6614ce rewrite interception map to have unique point trackers 2023-02-19 17:16:31 +11:00
wish
1b76bb1930 Merge branch 'main' into feature/diva 2023-02-18 18:09:24 +11:00
wish
1fd2878909 Merge branch 'main' into feature/diva 2022-12-24 19:43:58 +11:00
wish
41816060f9 add comments 2022-11-18 20:44:05 +11:00
wish
b8ef805129 return fail before generating maps 2022-11-17 09:03:37 +11:00
wish
d5bc0916ba handle interception map response for guildless players 2022-11-17 05:21:38 +11:00
wish
170fae9084 handle interception quest clear tracking 2022-11-15 11:01:44 +11:00
wish
ac3d8730b5 fix tiles not having branch ID 2022-11-15 10:01:46 +11:00
wish
e3dcdf17a0 implement interception branch generation 2022-11-15 09:22:32 +11:00
wish
39fbb84294 implement interception branch struct 2022-11-15 06:22:10 +11:00
wish
bff21fae4f implement interception point collation & progression 2022-11-14 15:55:50 +11:00
wish
836737771b handle saving and loading interception maps 2022-11-14 03:08:52 +11:00
wish
9f90d46b3c handle mapID edge case 2022-11-14 02:01:05 +11:00
wish
b0f83d8943 connect UdGuildMapInfo to GenerateUdGuildMaps 2022-11-14 01:46:09 +11:00
wish
d3fbc53f9f remove debug value 2022-11-14 00:30:45 +11:00
wish
2c3686cd94 interception map generator & file refactor 2022-11-14 00:26:24 +11:00
wish
5117214df2 rewrite progression to support backpages 2022-11-13 17:03:04 +11:00
wish
4b45a33cbf enable cross-map progression 2022-11-13 15:28:35 +11:00
wish
1c350c1050 parse tactics packets 2022-11-13 13:30:29 +11:00
wish
cad5f3506b decode diva interception map 2022-11-13 03:31:40 +11:00
wish
2051fe4426 Merge branch 'main' into feature/diva
# Conflicts:
#	server/channelserver/handlers_quest.go
2022-11-12 21:12:30 +11:00
wish
88da19b002 add missing kiju descriptions 2022-11-01 13:04:57 +11:00
wish
ff00202821 merge fixes 2022-11-01 12:55:14 +11:00
wish
4c057570e2 Merge branch 'main' into feature/diva
# Conflicts:
#	server/channelserver/handlers_quest.go
2022-11-01 12:54:25 +11:00
wish
c02f392a8b map kiju strings 2022-10-23 19:26:56 +11:00
wish
619b0bf166 handle diva shop coins 2022-10-17 05:27:09 +11:00
wish
b88a128739 Merge branch 'main' into feature/diva 2022-10-13 08:37:14 +11:00
wish
e410252efd Merge remote-tracking branch 'origin/feature/quest-enum' into feature/diva 2022-10-13 07:54:48 +11:00
wish
a189919644 Merge branch 'main' into feature/diva 2022-10-12 19:30:57 +11:00
wish
eecf026c8d Merge branch 'main' into feature/diva 2022-10-09 00:53:20 +11:00
wish
ffa4bae31e handle various packets 2022-10-07 23:09:01 +11:00
wish
3c23d12ba6 handle various packets 2022-10-07 21:40:34 +11:00
wish
1b86479672 handle various packets 2022-10-07 20:43:25 +11:00
69 changed files with 2152 additions and 2084 deletions

3
.gitignore vendored
View File

@@ -7,5 +7,4 @@ savedata/*/
*.exe
*.lnk
*.bat
/docker/db-data
screenshots/*
/docker/db-data

View File

@@ -1,30 +1,29 @@
# List of authors who contributed to Erupe
# List of AUTHORS who contributed over time to the Erupe project
## Point of current development
The project is currently developed under https://github.com/ZeruLight/Erupe
## History of development
Development of this project dates back to 2019, and was developed under various umbrellas over time:
* Cappuccino (Fist/Ando/Ellie42) ("The Erupe Developers"), 2019-2020 (https://github.com/Ellie42/Erupe / https://github.com/ricochhet/Erupe-Legacy) (Still active closed source)
* Cappuccino (Fist/Ando/Ellie42) (The Erupe Developers), 2019-2020 (https://github.com/Ellie42/Erupe / https://github.com/ricochhet/Erupe-Legacy) (Still active closed source)
* Einherjar Team, ????-2022 Feb (There is no git history for this period, this team's work was taken and used as a foundation for future repositories)
* Community Edition, 2022 (https://github.com/xl3lackout/Erupe)
* sekaiwish Fork, 2022 (https://github.com/sekaiwish/Erupe)
* ZeruLight, 2022-2023 (https://github.com/ZeruLight/Erupe)
* Zerulight, 2022-2023 (https://github.com/ZeruLight/Erupe)
## Authorship of the code
Authorship is assigned for each commit within the git history, which is stored in these git repos:
* https://github.com/ZeruLight/Erupe
* https://github.com/Ellie42/Erupe
* https://github.com/Ellie42/Erupe
* https://github.com/ricochhet/Erupe-Legacy
* https://github.com/xl3lackout/Erupe
Note there is a divergence between Ellie42s branch and xl3lackout where history has been lost.
Note the divergence between Ellie42's branch and xl3lackout's where history has been lost.
Unfortunately, we have no detailed information on the history of Erupe before 2022.
If somebody can provide information, please contact us, so that we can make this history available.
Unfortunately, we have no detailed information on the history of the Erupe pre-2022
if somebody can provide information, please contact us, so that we can make this history available.
## Exceptions with third-party libraries
The third-party libraries have their own way of addressing authorship and the authorship of commits importing/updating
a third-party library reflects who did the importing instead of who wrote the code within the commit.
The authors of third-party libraries are not explicitly mentioned, and usually is possible to obtain from the files belonging to the third-party libraries.
The Authors of third-party libraries are not explicitly mentioned, and usually is possible to obtain from the files belonging to the third-party libraries.

View File

@@ -1,6 +1,6 @@
# Erupe
## Client Compatibility
## Client Compatiblity
### Platforms
- PC
- PlayStation 3
@@ -34,15 +34,15 @@ If you want to modify or compile Erupe yourself, please read on.
## Docker
Please see [docker/README.md](./docker/README.md). This is intended for quick installs and development, not for production.
Please see the readme in [docker/README.md](./docker/README.md). At the moment this is only really good for quick installs and checking out development not for production.
## Schemas
We source control the following schemas:
- Initialization Schema: This initializes the application database to a specific version (9.1.0).
- Update Schemas: These are update files that should be ran on top of the initialization schema.
- Patch Schemas: These are for development and should be run after running all initialization and update schema. These get condensed into `Update Schemas` and deleted when updated to a new release.
- Bundled Schemas: These are demo reference files to give servers standard set-ups.
- Initialisation Schemas: These initialise the application database to a clean install from a specific version.
- Update Schemas: These are update files they should be ran in order of version to get to the latest schema.
- Patch Schemas: These are for development and should be ran from the lastest available update schema or initial schema. These eventually get condensed into `Update Schemas` and then deleted when updated to a new version.
- Bundled Schemas: These are demo reference files to allow servers to be able to roll their own shops, distributions gachas and scenarios set ups.
Note: Patch schemas are subject to change! You should only be using them if you are following along with development.

View File

@@ -1,175 +0,0 @@
package mhfitem
import (
"erupe-ce/common/byteframe"
"erupe-ce/common/token"
_config "erupe-ce/config"
)
type MHFItem struct {
ItemID uint16
}
type MHFSigilEffect struct {
ID uint16
Level uint16
}
type MHFSigil struct {
Effects []MHFSigilEffect
Unk0 uint8
Unk1 uint8
Unk2 uint8
Unk3 uint8
}
type MHFEquipment struct {
WarehouseID uint32
ItemType uint8
Unk0 uint8
ItemID uint16
Level uint16
Decorations []MHFItem
Sigils []MHFSigil
Unk1 uint16
}
type MHFItemStack struct {
WarehouseID uint32
Item MHFItem
Quantity uint16
Unk0 uint32
}
func ReadWarehouseItem(bf *byteframe.ByteFrame) MHFItemStack {
var item MHFItemStack
item.WarehouseID = bf.ReadUint32()
if item.WarehouseID == 0 {
item.WarehouseID = token.RNG.Uint32()
}
item.Item.ItemID = bf.ReadUint16()
item.Quantity = bf.ReadUint16()
item.Unk0 = bf.ReadUint32()
return item
}
func DiffItemStacks(o []MHFItemStack, u []MHFItemStack) []MHFItemStack {
// o = old, u = update, f = final
var f []MHFItemStack
for _, uItem := range u {
exists := false
for i := range o {
if o[i].WarehouseID == uItem.WarehouseID {
exists = true
o[i].Quantity = uItem.Quantity
}
}
if !exists {
uItem.WarehouseID = token.RNG.Uint32()
f = append(f, uItem)
}
}
for _, oItem := range o {
if oItem.Quantity > 0 {
f = append(f, oItem)
}
}
return f
}
func (is MHFItemStack) ToBytes() []byte {
bf := byteframe.NewByteFrame()
bf.WriteUint32(is.WarehouseID)
bf.WriteUint16(is.Item.ItemID)
bf.WriteUint16(is.Quantity)
bf.WriteUint32(is.Unk0)
return bf.Data()
}
func SerializeWarehouseItems(i []MHFItemStack) []byte {
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(len(i)))
bf.WriteUint16(0) // Unused
for _, j := range i {
bf.WriteBytes(j.ToBytes())
}
return bf.Data()
}
func ReadWarehouseEquipment(bf *byteframe.ByteFrame) MHFEquipment {
var equipment MHFEquipment
equipment.Decorations = make([]MHFItem, 3)
equipment.Sigils = make([]MHFSigil, 3)
for i := 0; i < 3; i++ {
equipment.Sigils[i].Effects = make([]MHFSigilEffect, 3)
}
equipment.WarehouseID = bf.ReadUint32()
if equipment.WarehouseID == 0 {
equipment.WarehouseID = token.RNG.Uint32()
}
equipment.ItemType = bf.ReadUint8()
equipment.Unk0 = bf.ReadUint8()
equipment.ItemID = bf.ReadUint16()
equipment.Level = bf.ReadUint16()
for i := 0; i < 3; i++ {
equipment.Decorations[i].ItemID = bf.ReadUint16()
}
if _config.ErupeConfig.RealClientMode >= _config.G1 {
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
equipment.Sigils[i].Effects[j].ID = bf.ReadUint16()
}
for j := 0; j < 3; j++ {
equipment.Sigils[i].Effects[j].Level = bf.ReadUint16()
}
equipment.Sigils[i].Unk0 = bf.ReadUint8()
equipment.Sigils[i].Unk1 = bf.ReadUint8()
equipment.Sigils[i].Unk2 = bf.ReadUint8()
equipment.Sigils[i].Unk3 = bf.ReadUint8()
}
}
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
equipment.Unk1 = bf.ReadUint16()
}
return equipment
}
func (e MHFEquipment) ToBytes() []byte {
bf := byteframe.NewByteFrame()
bf.WriteUint32(e.WarehouseID)
bf.WriteUint8(e.ItemType)
bf.WriteUint8(e.Unk0)
bf.WriteUint16(e.ItemID)
bf.WriteUint16(e.Level)
for i := 0; i < 3; i++ {
bf.WriteUint16(e.Decorations[i].ItemID)
}
if _config.ErupeConfig.RealClientMode >= _config.G1 {
for i := 0; i < 3; i++ {
for j := 0; j < 3; j++ {
bf.WriteUint16(e.Sigils[i].Effects[j].ID)
}
for j := 0; j < 3; j++ {
bf.WriteUint16(e.Sigils[i].Effects[j].Level)
}
bf.WriteUint8(e.Sigils[i].Unk0)
bf.WriteUint8(e.Sigils[i].Unk1)
bf.WriteUint8(e.Sigils[i].Unk2)
bf.WriteUint8(e.Sigils[i].Unk3)
}
}
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
bf.WriteUint16(e.Unk1)
}
return bf.Data()
}
func SerializeWarehouseEquipment(i []MHFEquipment) []byte {
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(len(i)))
bf.WriteUint16(0) // Unused
for _, j := range i {
bf.WriteBytes(j.ToBytes())
}
return bf.Data()
}

View File

@@ -29,23 +29,6 @@ func SJISToUTF8(b []byte) string {
return string(result)
}
func ToNGWord(x string) []uint16 {
var w []uint16
for _, r := range []rune(x) {
if r > 0xFF {
t := UTF8ToSJIS(string(r))
if len(t) > 1 {
w = append(w, uint16(t[1])<<8|uint16(t[0]))
} else {
w = append(w, uint16(t[0]))
}
} else {
w = append(w, uint16(r))
}
}
return w
}
func PaddedString(x string, size uint, t bool) []byte {
if t {
e := japanese.ShiftJIS.NewEncoder()

View File

@@ -5,19 +5,18 @@ import (
"time"
)
var RNG = NewRNG()
// Generate returns an alphanumeric token of specified length
func Generate(length int) string {
rng := RNG()
var chars = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
b := make([]rune, length)
for i := range b {
b[i] = chars[RNG.Intn(len(chars))]
b[i] = chars[rng.Intn(len(chars))]
}
return string(b)
}
// NewRNG returns a new NewRNG generator
func NewRNG() *rand.Rand {
// RNG returns a new RNG generator
func RNG() *rand.Rand {
return rand.New(rand.NewSource(time.Now().UnixNano()))
}

View File

@@ -9,21 +9,16 @@
],
"PatchServerManifest": "",
"PatchServerFile": "",
"Screenshots":{
"Enabled":true,
"Host":"127.0.0.1",
"Port":8080,
"OutputDir":"screenshots",
"UploadQuality":100
},
"ScreenshotAPIURL": "",
"DeleteOnSaveCorruption": false,
"ClientMode": "ZZ",
"QuestCacheExpiry": 300,
"CommandPrefix": "!",
"AutoCreateAccount": true,
"DefaultCourses": [1, 23, 24],
"EarthDebug": false,
"EarthMonsters": [116, 107, 2, 36],
"EarthStatus": 0,
"EarthID": 0,
"EarthMonsters": [0, 0, 0, 0],
"SaveDumps": {
"Enabled": true,
"RawEnabled": false,
@@ -51,8 +46,7 @@
}
},
"GameplayOptions": {
"MinFeatureWeapons": 0,
"MaxFeatureWeapons": 1,
"FeaturedWeapons": 1,
"MaximumNP": 100000,
"MaximumRP": 50000,
"MaximumFP": 120000,
@@ -194,8 +188,8 @@
"Enabled": true,
"Port": 53312
},
"API": {
"Enabled": true,
"SignV2": {
"Enabled": false,
"Port": 8080,
"PatchServer": "",
"Banners": [],

View File

@@ -75,6 +75,7 @@ type Config struct {
LoginNotices []string // MHFML string of the login notices displayed
PatchServerManifest string // Manifest patch server override
PatchServerFile string // File patch server override
ScreenshotAPIURL string // Destination for screenshots uploaded to BBS
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
ClientMode string
RealClientMode Mode
@@ -82,21 +83,20 @@ type Config struct {
CommandPrefix string // The prefix for commands
AutoCreateAccount bool // Automatically create accounts if they don't exist
DefaultCourses []uint16
EarthDebug bool
EarthStatus int32
EarthID int32
EarthMonsters []int32
SaveDumps SaveDumpOptions
Screenshots ScreenshotsOptions
DebugOptions DebugOptions
GameplayOptions GameplayOptions
Discord Discord
Commands []Command
Courses []Course
Database Database
Sign Sign
API API
Channel Channel
Entrance Entrance
DebugOptions DebugOptions
GameplayOptions GameplayOptions
Discord Discord
Commands []Command
Courses []Course
Database Database
Sign Sign
SignV2 SignV2
Channel Channel
Entrance Entrance
}
type SaveDumpOptions struct {
@@ -105,14 +105,6 @@ type SaveDumpOptions struct {
OutputDir string
}
type ScreenshotsOptions struct {
Enabled bool
Host string // Destination for screenshots uploaded to BBS
Port uint32 // Port for screenshots API
OutputDir string
UploadQuality int //Determines the upload quality to the server
}
// DebugOptions holds various debug/temporary options for use while developing Erupe.
type DebugOptions struct {
CleanDB bool // Automatically wipes the DB on server reset.
@@ -140,8 +132,7 @@ type CapLinkOptions struct {
// GameplayOptions has various gameplay modifiers
type GameplayOptions struct {
MinFeatureWeapons int // Minimum number of Active Feature weapons to generate daily
MaxFeatureWeapons int // Maximum number of Active Feature weapons to generate daily
FeaturedWeapons int // Number of Active Feature weapons to generate daily
MaximumNP int // Maximum number of NP held by a player
MaximumRP uint16 // Maximum number of RP held by a player
MaximumFP uint32 // Maximum number of FP held by a player
@@ -236,29 +227,29 @@ type Sign struct {
Port int
}
// API holds server config
type API struct {
// SignV2 holds the new sign server config
type SignV2 struct {
Enabled bool
Port int
PatchServer string
Banners []APISignBanner
Messages []APISignMessage
Links []APISignLink
Banners []SignV2Banner
Messages []SignV2Message
Links []SignV2Link
}
type APISignBanner struct {
type SignV2Banner struct {
Src string `json:"src"` // Displayed image URL
Link string `json:"link"` // Link accessed on click
}
type APISignMessage struct {
type SignV2Message struct {
Message string `json:"message"` // Displayed message
Date int64 `json:"date"` // Displayed date
Kind int `json:"kind"` // 0 for 'Default', 1 for 'New'
Link string `json:"link"` // Link accessed on click
}
type APISignLink struct {
type SignV2Link struct {
Name string `json:"name"` // Displayed name
Icon string `json:"icon"` // Displayed icon. It will be cast as a monochrome color as long as it is transparent.
Link string `json:"link"` // Link accessed on click
@@ -360,10 +351,6 @@ func LoadConfig() (*Config, error) {
c.RealClientMode = ZZ
}
if c.GameplayOptions.MinFeatureWeapons > c.GameplayOptions.MaxFeatureWeapons {
c.GameplayOptions.MinFeatureWeapons = c.GameplayOptions.MaxFeatureWeapons
}
return c, nil
}

View File

@@ -42,8 +42,8 @@ services:
build:
context: ../
volumes:
- ../config.json:/app/erupe/config.json
- ../bin:/app/erupe/bin
- ./config.json:/app/erupe/config.json
- ./bin:/app/erupe/bin
- ./savedata:/app/erupe/savedata
ports:
# (Make sure these match config.json)

View File

@@ -1,7 +1,7 @@
#!/bin/bash
set -e
echo "INIT!"
pg_restore --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --verbose /schemas/init.sql
pg_restore --username="$POSTGRES_USER" --dbname="$POSTGRES_DB" --verbose /schemas/initialisation-schema/9.1-init.sql

22
main.go
View File

@@ -10,11 +10,11 @@ import (
"syscall"
"time"
"erupe-ce/server/api"
"erupe-ce/server/channelserver"
"erupe-ce/server/discordbot"
"erupe-ce/server/entranceserver"
"erupe-ce/server/signserver"
"erupe-ce/server/signv2server"
"github.com/jmoiron/sqlx"
_ "github.com/lib/pq"
@@ -181,21 +181,21 @@ func main() {
}
// New Sign server
var ApiServer *api.APIServer
if config.API.Enabled {
ApiServer = api.NewAPIServer(
&api.Config{
var newSignServer *signv2server.Server
if config.SignV2.Enabled {
newSignServer = signv2server.NewServer(
&signv2server.Config{
Logger: logger.Named("sign"),
ErupeConfig: _config.ErupeConfig,
DB: db,
})
err = ApiServer.Start()
err = newSignServer.Start()
if err != nil {
preventClose(fmt.Sprintf("API: Failed to start, %s", err.Error()))
preventClose(fmt.Sprintf("SignV2: Failed to start, %s", err.Error()))
}
logger.Info("API: Started successfully")
logger.Info("SignV2: Started successfully")
} else {
logger.Info("API: Disabled")
logger.Info("SignV2: Disabled")
}
var channels []*channelserver.Server
@@ -273,8 +273,8 @@ func main() {
signServer.Shutdown()
}
if config.API.Enabled {
ApiServer.Shutdown()
if config.SignV2.Enabled {
newSignServer.Shutdown()
}
if config.Entrance.Enabled {

View File

@@ -1,30 +1,20 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfAcquireUdItem represents the MSG_MHF_ACQUIRE_UD_ITEM
type MsgMhfAcquireUdItem struct {
AckHandle uint32
Unk0 uint8
// from gal
// daily = 0
// personal = 1
// personal rank = 2
// guild rank = 3
// gcp = 4
// from cat
// treasure achievement = 5
// personal achievement = 6
// guild achievement = 7
RewardType uint8
Unk2 uint8 // Number of uint32s to read?
Unk3 []byte
AckHandle uint32
Freeze bool
RewardType uint8
Count uint8
RewardIDs []uint32
}
// Opcode returns the ID associated with this packet type.
@@ -34,13 +24,13 @@ func (m *MsgMhfAcquireUdItem) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfAcquireUdItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
m.AckHandle = bf.ReadUint32()
m.Freeze = bf.ReadBool()
m.RewardType = bf.ReadUint8()
m.Unk2 = bf.ReadUint8()
for i := uint8(0); i < m.Unk2; i++ {
bf.ReadUint32()
}
m.Count = bf.ReadUint8()
for i := uint8(0); i < m.Count; i++ {
m.RewardIDs = append(m.RewardIDs, bf.ReadUint32())
}
return nil
}

View File

@@ -1,15 +1,21 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfAddRewardSongCount represents the MSG_MHF_ADD_REWARD_SONG_COUNT
type MsgMhfAddRewardSongCount struct{}
type MsgMhfAddRewardSongCount struct {
AckHandle uint32
PrayerID uint32
Unk1 uint16
Unk2 uint8
Unk3 []uint16
}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfAddRewardSongCount) Opcode() network.PacketID {
@@ -18,7 +24,14 @@ func (m *MsgMhfAddRewardSongCount) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfAddRewardSongCount) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
m.AckHandle = bf.ReadUint32()
m.PrayerID = bf.ReadUint32()
m.Unk1 = bf.ReadUint16()
m.Unk2 = bf.ReadUint8()
for i := uint8(0); i < m.Unk2; i++ {
m.Unk3 = append(m.Unk3, bf.ReadUint16())
}
return nil
}
// Build builds a binary packet from the current data.

View File

@@ -1,18 +1,17 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfAddUdPoint represents the MSG_MHF_ADD_UD_POINT
type MsgMhfAddUdPoint struct {
AckHandle uint32
Unk1 uint32
Unk2 uint32
Points uint32
}
// Opcode returns the ID associated with this packet type.
@@ -23,11 +22,10 @@ func (m *MsgMhfAddUdPoint) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfAddUdPoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
m.Points += bf.ReadUint32()
// Premium Course bonus
m.Points += bf.ReadUint32()
return nil
//panic("Not implemented")
}
// Build builds a binary packet from the current data.

View File

@@ -1,16 +1,17 @@
package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// MsgMhfAddUdTacticsPoint represents the MSG_MHF_ADD_UD_TACTICS_POINT
type MsgMhfAddUdTacticsPoint struct {
AckHandle uint32
Unk0 uint16
Unk1 uint32
AckHandle uint32
QuestFileID uint16
Points int32
}
// Opcode returns the ID associated with this packet type.
@@ -21,15 +22,12 @@ func (m *MsgMhfAddUdTacticsPoint) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfAddUdTacticsPoint) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint32()
m.QuestFileID = bf.ReadUint16()
m.Points = bf.ReadInt32()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfAddUdTacticsPoint) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint16(m.Unk0)
bf.WriteUint32(m.Unk1)
return nil
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -11,7 +11,7 @@ import (
// MsgMhfEnumerateWarehouse represents the MSG_MHF_ENUMERATE_WAREHOUSE
type MsgMhfEnumerateWarehouse struct {
AckHandle uint32
BoxType uint8
BoxType string
BoxIndex uint8
}
@@ -23,10 +23,15 @@ func (m *MsgMhfEnumerateWarehouse) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfEnumerateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.BoxType = bf.ReadUint8()
boxType := bf.ReadUint8()
switch boxType {
case 0:
m.BoxType = "item"
case 1:
m.BoxType = "equip"
}
m.BoxIndex = bf.ReadUint8()
bf.ReadUint8() // Zeroed
bf.ReadUint8() // Zeroed
_ = bf.ReadUint16()
return nil
}

View File

@@ -1,17 +1,17 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfGetUdRanking represents the MSG_MHF_GET_UD_RANKING
type MsgMhfGetUdRanking struct{
type MsgMhfGetUdRanking struct {
AckHandle uint32
Unk0 uint8
RankType uint8
}
// Opcode returns the ID associated with this packet type.
@@ -22,7 +22,7 @@ func (m *MsgMhfGetUdRanking) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfGetUdRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
m.RankType = bf.ReadUint8()
return nil
}

View File

@@ -1,11 +1,10 @@
package mhfpacket
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfGetUdTacticsRewardList represents the MSG_MHF_GET_UD_TACTICS_REWARD_LIST

View File

@@ -2,7 +2,6 @@ package mhfpacket
import (
"errors"
"fmt"
"erupe-ce/common/byteframe"
"erupe-ce/network"
@@ -11,11 +10,11 @@ import (
// MsgMhfGetWeeklySeibatuRankingReward represents the MSG_MHF_GET_WEEKLY_SEIBATU_RANKING_REWARD
type MsgMhfGetWeeklySeibatuRankingReward struct {
AckHandle uint32
Unk0 uint32
Operation uint32
ID uint32
EarthMonster uint32
AckHandle uint32
Unk0 uint32
Unk1 uint32
Unk2 uint32
Unk3 uint32
}
// Opcode returns the ID associated with this packet type.
@@ -27,10 +26,9 @@ func (m *MsgMhfGetWeeklySeibatuRankingReward) Opcode() network.PacketID {
func (m *MsgMhfGetWeeklySeibatuRankingReward) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Operation = bf.ReadUint32()
m.ID = bf.ReadUint32()
m.EarthMonster = bf.ReadUint32()
fmt.Printf("MsgMhfGetWeeklySeibatuRankingReward: Unk0:[%d] Operation:[%d] ID:[%d] EarthMonster:[%d]\n\n", m.Unk0, m.Operation, m.ID, m.EarthMonster)
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
m.Unk3 = bf.ReadUint32()
return nil
}

View File

@@ -13,7 +13,7 @@ import (
type MsgMhfOperateWarehouse struct {
AckHandle uint32
Operation uint8
BoxType uint8
BoxType string
BoxIndex uint8
Name string
}
@@ -27,13 +27,17 @@ func (m *MsgMhfOperateWarehouse) Opcode() network.PacketID {
func (m *MsgMhfOperateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Operation = bf.ReadUint8()
m.BoxType = bf.ReadUint8()
m.BoxIndex = bf.ReadUint8()
lenName := bf.ReadUint8()
bf.ReadUint16() // Zeroed
if lenName > 0 {
m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
boxType := bf.ReadUint8()
switch boxType {
case 0:
m.BoxType = "item"
case 1:
m.BoxType = "equip"
}
m.BoxIndex = bf.ReadUint8()
_ = bf.ReadUint8() // lenName
_ = bf.ReadUint16() // Unk
m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
return nil
}

View File

@@ -1,17 +1,17 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfSetKiju represents the MSG_MHF_SET_KIJU
type MsgMhfSetKiju struct {
AckHandle uint32
Unk1 uint16
BeadIndex uint8
}
// Opcode returns the ID associated with this packet type.
@@ -22,9 +22,8 @@ func (m *MsgMhfSetKiju) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfSetKiju) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk1 = bf.ReadUint16()
m.BeadIndex = bf.ReadUint8()
return nil
//panic("Not implemented")
}
// Build builds a binary packet from the current data.

View File

@@ -1,15 +1,21 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfSetUdTacticsFollower represents the MSG_MHF_SET_UD_TACTICS_FOLLOWER
type MsgMhfSetUdTacticsFollower struct{}
type MsgMhfSetUdTacticsFollower struct {
AckHandle uint32
Unk0 uint16
Unk1 uint16
Unk2 uint16
Unk3 uint16
}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfSetUdTacticsFollower) Opcode() network.PacketID {
@@ -18,7 +24,12 @@ func (m *MsgMhfSetUdTacticsFollower) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfSetUdTacticsFollower) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint16()
m.Unk2 = bf.ReadUint16()
m.Unk3 = bf.ReadUint16()
return nil
}
// Build builds a binary packet from the current data.

View File

@@ -32,21 +32,16 @@ func (m *MsgMhfStampcardStamp) Opcode() network.PacketID {
func (m *MsgMhfStampcardStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.HR = bf.ReadUint16()
if _config.ErupeConfig.RealClientMode >= _config.G1 {
m.GR = bf.ReadUint16()
}
m.GR = bf.ReadUint16()
m.Stamps = bf.ReadUint16()
bf.ReadUint16() // Zeroed
if _config.ErupeConfig.RealClientMode >= _config.Z2 {
if _config.ErupeConfig.RealClientMode > _config.Z1 {
m.Reward1 = uint16(bf.ReadUint32())
m.Reward2 = uint16(bf.ReadUint32())
m.Item1 = uint16(bf.ReadUint32())
m.Item2 = uint16(bf.ReadUint32())
m.Quantity1 = uint16(bf.ReadUint32())
m.Quantity2 = uint16(bf.ReadUint32())
} else {
m.Reward1 = 10
m.Reward2 = 10
}
return nil
}

View File

@@ -31,8 +31,8 @@ func (m *MsgMhfUpdateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
m.AckHandle = bf.ReadUint32()
m.EntryCount = bf.ReadUint16()
bf.ReadUint16() // Zeroed
var temp Goocoo
for i := 0; i < int(m.EntryCount); i++ {
var temp Goocoo
temp.Index = bf.ReadUint32()
for j := 0; j < 22; j++ {
temp.Data1 = append(temp.Data1, bf.ReadInt16())

View File

@@ -2,18 +2,24 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/mhfitem"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
type Item struct {
Unk0 uint32
ItemID uint16
Amount uint16
Unk1 uint32
}
// MsgMhfUpdateGuildItem represents the MSG_MHF_UPDATE_GUILD_ITEM
type MsgMhfUpdateGuildItem struct {
AckHandle uint32
GuildID uint32
UpdatedItems []mhfitem.MHFItemStack
AckHandle uint32
GuildID uint32
Items []Item
}
// Opcode returns the ID associated with this packet type.
@@ -25,12 +31,18 @@ func (m *MsgMhfUpdateGuildItem) Opcode() network.PacketID {
func (m *MsgMhfUpdateGuildItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.GuildID = bf.ReadUint32()
changes := int(bf.ReadUint16())
itemCount := int(bf.ReadUint16())
bf.ReadUint8() // Zeroed
bf.ReadUint8() // Zeroed
for i := 0; i < changes; i++ {
m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf))
m.Items = make([]Item, itemCount)
for i := 0; i < itemCount; i++ {
m.Items[i].Unk0 = bf.ReadUint32()
m.Items[i].ItemID = bf.ReadUint16()
m.Items[i].Amount = bf.ReadUint16()
m.Items[i].Unk1 = bf.ReadUint32()
}
return nil
}

View File

@@ -2,7 +2,6 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/mhfitem"
"erupe-ce/common/byteframe"
"erupe-ce/network"
@@ -11,8 +10,8 @@ import (
// MsgMhfUpdateUnionItem represents the MSG_MHF_UPDATE_UNION_ITEM
type MsgMhfUpdateUnionItem struct {
AckHandle uint32
UpdatedItems []mhfitem.MHFItemStack
AckHandle uint32
Items []Item
}
// Opcode returns the ID associated with this packet type.
@@ -23,12 +22,18 @@ func (m *MsgMhfUpdateUnionItem) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfUpdateUnionItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
changes := int(bf.ReadUint16())
itemCount := int(bf.ReadUint16())
bf.ReadUint8() // Zeroed
bf.ReadUint8() // Zeroed
for i := 0; i < changes; i++ {
m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf))
m.Items = make([]Item, itemCount)
for i := 0; i < itemCount; i++ {
m.Items[i].Unk0 = bf.ReadUint32()
m.Items[i].ItemID = bf.ReadUint16()
m.Items[i].Amount = bf.ReadUint16()
m.Items[i].Unk1 = bf.ReadUint32()
}
return nil
}

View File

@@ -3,18 +3,25 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/common/mhfitem"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
type WarehouseStack struct {
ID uint32
Index uint16
EquipType uint16
ItemID uint16
Quantity uint16
Data []byte
}
// MsgMhfUpdateWarehouse represents the MSG_MHF_UPDATE_WAREHOUSE
type MsgMhfUpdateWarehouse struct {
AckHandle uint32
BoxType uint8
BoxIndex uint8
UpdatedItems []mhfitem.MHFItemStack
UpdatedEquipment []mhfitem.MHFEquipment
AckHandle uint32
BoxType string
BoxIndex uint8
Updates []WarehouseStack
}
// Opcode returns the ID associated with this packet type.
@@ -25,19 +32,35 @@ func (m *MsgMhfUpdateWarehouse) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfUpdateWarehouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.BoxType = bf.ReadUint8()
boxType := bf.ReadUint8()
switch boxType {
case 0:
m.BoxType = "item"
case 1:
m.BoxType = "equip"
}
m.BoxIndex = bf.ReadUint8()
changes := int(bf.ReadUint16())
bf.ReadUint8() // Zeroed
bf.ReadUint8() // Zeroed
var stackUpdate WarehouseStack
for i := 0; i < changes; i++ {
switch m.BoxType {
switch boxType {
case 0:
m.UpdatedItems = append(m.UpdatedItems, mhfitem.ReadWarehouseItem(bf))
stackUpdate.ID = bf.ReadUint32()
stackUpdate.Index = bf.ReadUint16()
stackUpdate.ItemID = bf.ReadUint16()
stackUpdate.Quantity = bf.ReadUint16()
_ = bf.ReadUint16() // Unk
m.Updates = append(m.Updates, stackUpdate)
case 1:
m.UpdatedEquipment = append(m.UpdatedEquipment, mhfitem.ReadWarehouseEquipment(bf))
stackUpdate.ID = bf.ReadUint32()
stackUpdate.Index = bf.ReadUint16()
stackUpdate.EquipType = bf.ReadUint16()
stackUpdate.ItemID = bf.ReadUint16()
stackUpdate.Data = bf.ReadBytes(56)
m.Updates = append(m.Updates, stackUpdate)
}
}
_ = bf.ReadUint16()
return nil
}

View File

@@ -1,15 +1,18 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfUseUdShopCoin represents the MSG_MHF_USE_UD_SHOP_COIN
type MsgMhfUseUdShopCoin struct{}
type MsgMhfUseUdShopCoin struct {
AckHandle uint32
Cost uint8
}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfUseUdShopCoin) Opcode() network.PacketID {
@@ -18,7 +21,9 @@ func (m *MsgMhfUseUdShopCoin) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfUseUdShopCoin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
m.AckHandle = bf.ReadUint32()
m.Cost = bf.ReadUint8()
return nil
}
// Build builds a binary packet from the current data.

View File

@@ -40,8 +40,8 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
entryCount := int(bf.ReadUint16())
bf.ReadUint16() // Zeroed
var e TerminalLogEntry
for i := 0; i < entryCount; i++ {
var e TerminalLogEntry
e.Index = bf.ReadUint32()
e.Type1 = bf.ReadUint8()
e.Type2 = bf.ReadUint8()

View File

@@ -0,0 +1,254 @@
BEGIN;
-- Ripped prizes
INSERT INTO public.diva_prizes
(type, points_req, item_type, item_id, quantity, gr, repeatable)
VALUES
('personal', 1, 7, 13021, 1, false, false),
('personal', 1, 7, 13021, 1, true, false),
('personal', 200, 7, 1472, 5, false, false),
('personal', 200, 7, 7976, 5, true, false),
('personal', 400, 26, 0, 500, false, false),
('personal', 400, 26, 0, 500, true, false),
('personal', 600, 7, 1472, 5, false, false),
('personal', 600, 7, 7976, 5, true, false),
('personal', 800, 26, 0, 1000, false, false),
('personal', 800, 26, 0, 1000, true, false),
('personal', 1000, 26, 0, 1200, false, false),
('personal', 1000, 26, 0, 1200, true, false),
('personal', 1200, 26, 0, 1500, false, false),
('personal', 1200, 26, 0, 1500, true, false),
('personal', 1400, 26, 0, 2300, false, false),
('personal', 1400, 26, 0, 2300, true, false),
('personal', 1600, 26, 0, 2500, false, false),
('personal', 1600, 26, 0, 2500, true, false),
('personal', 1800, 26, 0, 3000, false, false),
('personal', 1800, 26, 0, 3000, true, false),
('personal', 2000, 7, 9722, 1, false, false),
('personal', 2000, 7, 9723, 1, false, false),
('personal', 2000, 7, 9724, 1, false, false),
('personal', 2000, 26, 0, 3300, false, false),
('personal', 2000, 7, 9722, 1, true, false),
('personal', 2000, 7, 9723, 1, true, false),
('personal', 2000, 7, 9724, 1, true, false),
('personal', 2000, 26, 0, 3300, true, false),
('personal', 3000, 7, 1472, 5, false, false),
('personal', 3000, 7, 7976, 5, true, false),
('personal', 4000, 26, 0, 3500, false, false),
('personal', 4000, 26, 0, 3500, true, false),
('personal', 5000, 7, 1472, 5, false, false),
('personal', 5000, 7, 7976, 5, true, false),
('personal', 6000, 7, 9725, 1, false, false),
('personal', 6000, 7, 9726, 1, false, false),
('personal', 6000, 7, 9727, 1, false, false),
('personal', 6000, 7, 9725, 1, true, false),
('personal', 6000, 7, 9726, 1, true, false),
('personal', 6000, 7, 9727, 1, true, false),
('personal', 7000, 26, 0, 3700, false, false),
('personal', 7000, 26, 0, 3700, true, false),
('personal', 8000, 7, 10192, 5, false, false),
('personal', 8000, 7, 10192, 5, true, false),
('personal', 9000, 26, 0, 4000, false, false),
('personal', 9000, 26, 0, 4000, true, false),
('personal', 10000, 7, 14063, 1, false, false),
('personal', 10000, 7, 14063, 1, false, false),
('personal', 10000, 7, 14063, 1, true, false),
('personal', 10000, 7, 13974, 1, true, false),
('personal', 12000, 7, 10193, 5, false, false),
('personal', 12000, 7, 10193, 5, true, false),
('personal', 14000, 29, 0, 1, false, false),
('personal', 14000, 29, 0, 1, true, false),
('personal', 15000, 7, 14063, 1, false, false),
('personal', 15000, 7, 14299, 1, true, false),
('personal', 18000, 7, 9702, 1, false, false),
('personal', 18000, 7, 9702, 1, true, false),
('personal', 20000, 7, 14063, 1, false, false),
('personal', 20000, 7, 14537, 1, true, false),
('personal', 22000, 26, 0, 4200, false, false),
('personal', 22000, 26, 0, 4200, true, false),
('personal', 25000, 7, 14063, 1, false, false),
('personal', 25000, 7, 14758, 1, true, false),
('personal', 26000, 7, 10194, 5, false, false),
('personal', 26000, 7, 10194, 5, true, false),
('personal', 30000, 7, 14063, 1, false, false),
('personal', 30000, 7, 14063, 1, false, false),
('personal', 30000, 7, 14063, 1, true, false),
('personal', 30000, 7, 14854, 1, true, false),
('personal', 34000, 29, 0, 2, false, false),
('personal', 34000, 29, 0, 2, true, false),
('personal', 40000, 7, 10195, 5, false, false),
('personal', 40000, 7, 10195, 5, true, false),
('personal', 46000, 26, 0, 4500, false, false),
('personal', 46000, 26, 0, 4500, true, false),
('personal', 50000, 7, 10196, 5, false, false),
('personal', 50000, 7, 10196, 5, true, false),
('personal', 54000, 29, 0, 3, false, false),
('personal', 54000, 29, 0, 3, true, false),
('personal', 60000, 7, 14063, 1, false, false),
('personal', 60000, 7, 14063, 1, true, false),
('personal', 63000, 26, 0, 4700, false, false),
('personal', 63000, 26, 0, 4700, true, false),
('personal', 70000, 7, 10197, 5, false, false),
('personal', 70000, 7, 10197, 5, true, false),
('personal', 72000, 7, 10198, 5, false, false),
('personal', 72000, 7, 10198, 5, true, false),
('personal', 74000, 29, 0, 4, false, false),
('personal', 74000, 29, 0, 4, true, false),
('personal', 78000, 26, 0, 5000, false, false),
('personal', 78000, 26, 0, 5000, true, false),
('personal', 82000, 7, 10199, 5, false, false),
('personal', 82000, 7, 10199, 5, true, false),
('personal', 84000, 29, 0, 5, false, false),
('personal', 84000, 29, 0, 5, true, false),
('personal', 86000, 26, 0, 5300, false, false),
('personal', 86000, 26, 0, 5300, true, false),
('personal', 90000, 7, 14063, 1, false, false),
('personal', 90000, 7, 14063, 1, true, false),
('personal', 92000, 7, 10730, 5, false, false),
('personal', 92000, 7, 10730, 5, true, false),
('personal', 94000, 29, 0, 6, false, false),
('personal', 94000, 29, 0, 6, true, false),
('personal', 98000, 7, 10731, 5, false, false),
('personal', 98000, 7, 10731, 5, true, false),
('personal', 102000, 26, 0, 5500, false, false),
('personal', 102000, 26, 0, 5500, true, false),
('personal', 104000, 29, 0, 7, false, false),
('personal', 104000, 29, 0, 7, true, false),
('personal', 106000, 7, 10732, 5, false, false),
('personal', 106000, 7, 10732, 5, true, false),
('personal', 110000, 7, 10189, 1, false, false),
('personal', 110000, 7, 10189, 1, true, false),
('personal', 114000, 29, 0, 8, false, false),
('personal', 114000, 29, 0, 8, true, false),
('personal', 118000, 26, 0, 5700, false, false),
('personal', 118000, 26, 0, 5700, true, false),
('personal', 124000, 29, 0, 9, false, false),
('personal', 124000, 29, 0, 9, true, false),
('personal', 126000, 7, 10188, 1, false, false),
('personal', 126000, 7, 10188, 1, true, false),
('personal', 134000, 29, 0, 10, false, false),
('personal', 134000, 29, 0, 10, true, false),
('personal', 146000, 26, 0, 5900, false, false),
('personal', 146000, 26, 0, 5900, true, false),
('personal', 150000, 7, 14063, 1, false, false),
('personal', 150000, 7, 14063, 1, true, false),
('personal', 160000, 26, 0, 6100, false, false),
('personal', 160000, 26, 0, 6100, true, false),
('personal', 174000, 26, 0, 6300, false, false),
('personal', 174000, 26, 0, 6300, true, false),
('personal', 180000, 7, 14063, 1, false, false),
('personal', 180000, 7, 14063, 1, true, false),
('personal', 186000, 26, 0, 6500, false, false),
('personal', 186000, 26, 0, 6500, true, false),
('personal', 200000, 7, 10187, 1, false, false),
('personal', 200000, 7, 10187, 1, true, false),
('personal', 214000, 26, 0, 6700, false, false),
('personal', 214000, 26, 0, 6700, true, false),
('personal', 226000, 7, 11440, 15, false, false),
('personal', 226000, 7, 11440, 15, true, false),
('personal', 240000, 26, 0, 7100, false, false),
('personal', 240000, 26, 0, 7100, true, false),
('personal', 260000, 26, 0, 1000, false, true),
('personal', 260000, 26, 0, 1000, true, true),
('personal', 280000, 26, 0, 1000, false, true),
('personal', 280000, 26, 0, 1000, true, true);
INSERT INTO public.diva_prizes
(type, points_req, item_type, item_id, quantity, gr, repeatable)
VALUES
('guild', 2, 7, 1026, 5, false, false),
('guild', 2, 7, 1026, 5, true, false),
('guild', 3, 7, 1026, 20, false, false),
('guild', 3, 7, 1026, 20, true, false),
('guild', 5, 7, 7456, 3, false, false),
('guild', 5, 7, 7456, 3, true, false),
('guild', 6, 7, 1026, 20, false, false),
('guild', 6, 7, 1026, 20, true, false),
('guild', 8, 7, 7457, 3, false, false),
('guild', 8, 7, 7457, 3, true, false),
('guild', 10, 7, 1026, 20, false, false),
('guild', 10, 7, 1026, 20, true, false),
('guild', 12, 7, 8940, 5, false, false),
('guild', 12, 7, 8941, 5, false, false),
('guild', 12, 7, 8943, 5, false, false),
('guild', 12, 7, 8946, 5, false, false),
('guild', 12, 7, 8940, 5, true, false),
('guild', 12, 7, 8941, 5, true, false),
('guild', 12, 7, 8943, 5, true, false),
('guild', 12, 7, 8946, 5, true, false),
('guild', 13, 26, 0, 1000, false, false),
('guild', 13, 26, 0, 1000, true, false),
('guild', 15, 7, 13692, 5, false, false),
('guild', 15, 7, 13693, 5, false, false),
('guild', 15, 7, 13692, 5, true, false),
('guild', 15, 7, 13693, 5, true, false),
('guild', 17, 26, 0, 2000, false, false),
('guild', 17, 26, 0, 2000, true, false),
('guild', 20, 28, 0, 1, false, false),
('guild', 20, 7, 7458, 5, false, false),
('guild', 20, 28, 0, 1, true, false),
('guild', 20, 7, 7458, 5, true, false),
('guild', 22, 7, 1026, 40, false, false),
('guild', 22, 7, 13692, 7, false, false),
('guild', 22, 7, 13693, 7, false, false),
('guild', 22, 7, 1026, 40, true, false),
('guild', 22, 7, 13692, 7, true, false),
('guild', 22, 7, 13693, 7, true, false),
('guild', 24, 7, 7463, 3, false, false),
('guild', 24, 7, 7463, 3, true, false),
('guild', 26, 26, 0, 3000, false, false),
('guild', 26, 26, 0, 3000, true, false),
('guild', 28, 7, 1026, 40, false, false),
('guild', 28, 7, 13692, 7, false, false),
('guild', 28, 7, 13693, 7, false, false),
('guild', 28, 7, 1026, 40, true, false),
('guild', 28, 7, 13692, 7, true, false),
('guild', 28, 7, 13693, 7, true, false),
('guild', 30, 7, 1026, 60, false, false),
('guild', 30, 7, 1026, 60, true, false),
('guild', 32, 7, 7462, 3, false, false),
('guild', 32, 7, 13692, 7, false, false),
('guild', 32, 7, 13693, 7, false, false),
('guild', 32, 7, 7462, 3, true, false),
('guild', 32, 7, 13692, 7, true, false),
('guild', 32, 7, 13693, 7, true, false),
('guild', 35, 7, 7464, 3, false, false),
('guild', 35, 7, 7464, 3, true, false),
('guild', 42, 7, 1026, 60, false, false),
('guild', 42, 7, 1026, 60, true, false),
('guild', 44, 7, 9710, 1, false, false),
('guild', 44, 7, 9710, 1, true, false),
('guild', 46, 7, 1026, 80, false, false),
('guild', 46, 7, 13692, 10, false, false),
('guild', 46, 7, 13693, 10, false, false),
('guild', 46, 7, 1026, 80, true, false),
('guild', 46, 7, 13692, 10, true, false),
('guild', 46, 7, 13693, 10, true, false),
('guild', 48, 7, 9709, 1, false, false),
('guild', 48, 7, 9709, 1, true, false),
('guild', 50, 7, 7456, 3, false, false),
('guild', 50, 7, 7456, 3, true, false),
('guild', 52, 7, 11387, 1, false, false),
('guild', 52, 7, 11387, 1, true, false),
('guild', 55, 7, 7457, 3, false, false),
('guild', 55, 7, 7457, 3, true, false),
('guild', 60, 7, 8945, 10, false, false),
('guild', 60, 7, 8945, 10, true, false),
('guild', 65, 7, 1026, 80, false, false),
('guild', 65, 7, 1026, 80, true, false),
('guild', 70, 7, 7458, 3, false, false),
('guild', 70, 7, 7458, 3, true, false),
('guild', 75, 7, 7463, 3, false, false),
('guild', 75, 7, 7463, 3, true, false),
('guild', 80, 7, 8945, 15, false, false),
('guild', 80, 7, 8945, 15, true, false),
('guild', 85, 7, 1026, 80, false, false),
('guild', 85, 7, 1026, 80, true, false),
('guild', 90, 7, 7462, 3, false, false),
('guild', 90, 7, 7462, 3, true, false),
('guild', 95, 7, 7464, 3, false, false),
('guild', 95, 7, 7464, 3, true, false),
('guild', 100, 26, 0, 50000, false, false),
('guild', 100, 26, 0, 50000, true, false);
END;

View File

@@ -1,7 +1,7 @@
BEGIN;
INSERT INTO public.shop_items
(shop_type, shop_id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity, road_floors, road_fatalis)
(shop_type, shop_id, item_id, cost, quantity, min_hr, min_sr, min_gr, store_level, max_quantity, road_floors, road_fatalis)
VALUES
(8,5,1,30,10,0,0,0,0,10,0,0),
(8,5,2,60,10,0,0,0,0,10,0,0),

View File

@@ -2,12 +2,4 @@ BEGIN;
ALTER TABLE users ADD COLUMN IF NOT EXISTS psn_id TEXT;
ALTER TABLE public.sign_sessions ADD COLUMN id SERIAL;
ALTER TABLE public.sign_sessions ADD CONSTRAINT sign_sessions_pkey PRIMARY KEY (id);
ALTER TABLE public.sign_sessions ALTER COLUMN user_id DROP NOT NULL;
ALTER TABLE public.sign_sessions ADD COLUMN psn_id TEXT;
END;

View File

@@ -1,6 +0,0 @@
BEGIN;
UPDATE guilds SET item_box=NULL;
UPDATE users SET item_box=NULL;
END;

View File

@@ -1,5 +0,0 @@
BEGIN;
ALTER TABLE IF EXISTS public.characters RENAME hrp TO hr;
END;

View File

@@ -1,6 +0,0 @@
BEGIN;
ALTER TABLE guilds ADD COLUMN IF NOT EXISTS room_rp INT DEFAULT 0;
ALTER TABLE guilds ADD COLUMN IF NOT EXISTS room_expiry TIMESTAMP WITHOUT TIME ZONE;
END;

View File

@@ -1,6 +0,0 @@
BEGIN;
-- Add 'earth' to the event_type ENUM type
ALTER TYPE event_type ADD VALUE 'earth';
END;

View File

@@ -0,0 +1,37 @@
BEGIN;
ALTER TABLE IF EXISTS public.guilds
ADD COLUMN IF NOT EXISTS interception_maps bytea;
ALTER TABLE IF EXISTS public.guild_characters
ADD COLUMN IF NOT EXISTS interception_points bytea;
CREATE TABLE IF NOT EXISTS public.diva_prizes (
id SERIAL PRIMARY KEY,
type PRIZE_TYPE,
points_req INTEGER,
item_type INTEGER,
item_id INTEGER,
quantity INTEGER,
gr BOOLEAN,
repeatable BOOLEAN
);
CREATE TABLE IF NOT EXISTS public.diva_beads (
type INTEGER
);
CREATE TABLE IF NOT EXISTS public.diva_beads_assignment (
character_id INTEGER,
bead_index INTEGER,
expiry TIMESTAMP WITH TIME ZONE
);
CREATE TABLE IF NOT EXISTS public.diva_beads_points (
character_id INTEGER,
points INTEGER,
timestamp TIMESTAMP WITH TIME ZONE,
bead_index INTEGER
);
END;

View File

@@ -1,6 +0,0 @@
BEGIN;
ALTER TABLE IF EXISTS public.stamps RENAME hl_next TO hl_checked;
ALTER TABLE IF EXISTS public.stamps RENAME ex_next TO ex_checked;
END;

View File

@@ -0,0 +1,11 @@
BEGIN;
ALTER TABLE public.sign_sessions ADD COLUMN id SERIAL;
ALTER TABLE public.sign_sessions ADD CONSTRAINT sign_sessions_pkey PRIMARY KEY (id);
ALTER TABLE public.sign_sessions ALTER COLUMN user_id DROP NOT NULL;
ALTER TABLE public.sign_sessions ADD COLUMN psn_id TEXT;
END;

View File

@@ -1,37 +0,0 @@
package api
import (
"errors"
"fmt"
"path/filepath"
)
func inTrustedRoot(path string, trustedRoot string) error {
for path != "/" {
path = filepath.Dir(path)
if path == trustedRoot {
return nil
}
}
return errors.New("path is outside of trusted root")
}
func verifyPath(path string, trustedRoot string) (string, error) {
c := filepath.Clean(path)
fmt.Println("Cleaned path: " + c)
r, err := filepath.EvalSymlinks(c)
if err != nil {
fmt.Println("Error " + err.Error())
return c, errors.New("Unsafe or invalid path specified")
}
err = inTrustedRoot(r, trustedRoot)
if err != nil {
fmt.Println("Error " + err.Error())
return r, errors.New("Unsafe or invalid path specified")
} else {
return r, nil
}
}

View File

@@ -3,7 +3,6 @@ package channelserver
import (
"encoding/binary"
"erupe-ce/common/mhfcourse"
"erupe-ce/common/mhfitem"
"erupe-ce/common/mhfmon"
ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport"
@@ -30,6 +29,18 @@ func stubEnumerateNoResults(s *Session, ackHandle uint32) {
doAckBufSucceed(s, ackHandle, enumBf.Data())
}
func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(s.server.erupeConfig.EarthID))
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(uint32(len(data)))
for i := range data {
bf.WriteBytes(data[i].Data())
}
doAckBufSucceed(s, ackHandle, bf.Data())
}
func doAckBufSucceed(s *Session, ackHandle uint32, data []byte) {
s.QueueSendMHF(&mhfpacket.MsgSysAck{
AckHandle: ackHandle,
@@ -806,33 +817,93 @@ func handleMsgMhfEnumerateOrder(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfGetExtraInfo(s *Session, p mhfpacket.MHFPacket) {}
func userGetItems(s *Session) []mhfitem.MHFItemStack {
var data []byte
var items []mhfitem.MHFItemStack
s.server.db.QueryRow(`SELECT item_box FROM users u WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$1)`, s.charID).Scan(&data)
if len(data) > 0 {
box := byteframe.NewByteFrameFromBytes(data)
numStacks := box.ReadUint16()
box.ReadUint16() // Unused
for i := 0; i < int(numStacks); i++ {
items = append(items, mhfitem.ReadWarehouseItem(box))
}
}
return items
}
func handleMsgMhfEnumerateUnionItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateUnionItem)
items := userGetItems(s)
var boxContents []byte
bf := byteframe.NewByteFrame()
bf.WriteBytes(mhfitem.SerializeWarehouseItems(items))
err := s.server.db.QueryRow("SELECT item_box FROM users, characters WHERE characters.id = $1 AND users.id = characters.user_id", int(s.charID)).Scan(&boxContents)
if err != nil {
s.logger.Error("Failed to get shared item box contents from db", zap.Error(err))
bf.WriteBytes(make([]byte, 4))
} else {
if len(boxContents) == 0 {
bf.WriteBytes(make([]byte, 4))
} else {
amount := len(boxContents) / 4
bf.WriteUint16(uint16(amount))
bf.WriteUint32(0x00)
bf.WriteUint16(0x00)
for i := 0; i < amount; i++ {
bf.WriteUint32(binary.BigEndian.Uint32(boxContents[i*4 : i*4+4]))
if i+1 != amount {
bf.WriteUint64(0x00)
}
}
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateUnionItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateUnionItem)
newStacks := mhfitem.DiffItemStacks(userGetItems(s), pkt.UpdatedItems)
s.server.db.Exec(`UPDATE users u SET item_box=$1 WHERE u.id=(SELECT c.user_id FROM characters c WHERE c.id=$2)`, mhfitem.SerializeWarehouseItems(newStacks), s.charID)
// Get item cache from DB
var boxContents []byte
var oldItems []Item
err := s.server.db.QueryRow("SELECT item_box FROM users, characters WHERE characters.id = $1 AND users.id = characters.user_id", int(s.charID)).Scan(&boxContents)
if err != nil {
s.logger.Error("Failed to get shared item box contents from db", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
} else {
amount := len(boxContents) / 4
oldItems = make([]Item, amount)
for i := 0; i < amount; i++ {
oldItems[i].ItemId = binary.BigEndian.Uint16(boxContents[i*4 : i*4+2])
oldItems[i].Amount = binary.BigEndian.Uint16(boxContents[i*4+2 : i*4+4])
}
}
// Update item stacks
newItems := make([]Item, len(oldItems))
copy(newItems, oldItems)
for i := 0; i < len(pkt.Items); i++ {
for j := 0; j <= len(oldItems); j++ {
if j == len(oldItems) {
var newItem Item
newItem.ItemId = pkt.Items[i].ItemID
newItem.Amount = pkt.Items[i].Amount
newItems = append(newItems, newItem)
break
}
if pkt.Items[i].ItemID == oldItems[j].ItemId {
newItems[j].Amount = pkt.Items[i].Amount
break
}
}
}
// Delete empty item stacks
for i := len(newItems) - 1; i >= 0; i-- {
if int(newItems[i].Amount) == 0 {
copy(newItems[i:], newItems[i+1:])
newItems[len(newItems)-1] = make([]Item, 1)[0]
newItems = newItems[:len(newItems)-1]
}
}
// Create new item cache
bf := byteframe.NewByteFrame()
for i := 0; i < len(newItems); i++ {
bf.WriteUint16(newItems[i].ItemId)
bf.WriteUint16(newItems[i].Amount)
}
// Upload new item cache
_, err = s.server.db.Exec("UPDATE users SET item_box = $1 FROM characters WHERE users.id = characters.user_id AND characters.id = $2", bf.Data(), int(s.charID))
if err != nil {
s.logger.Error("Failed to update shared item box contents in db", zap.Error(err))
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
@@ -840,54 +911,50 @@ func handleMsgMhfGetCogInfo(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCheckWeeklyStamp)
weekCurrentStart := TimeWeekStart()
weekNextStart := TimeWeekNext()
var total, redeemed, updated uint16
var lastCheck time.Time
err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_checked FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&lastCheck)
var nextClaim time.Time
err := s.server.db.QueryRow(fmt.Sprintf("SELECT %s_next FROM stamps WHERE character_id=$1", pkt.StampType), s.charID).Scan(&nextClaim)
if err != nil {
lastCheck = TimeAdjusted()
s.server.db.Exec("INSERT INTO stamps (character_id, hl_checked, ex_checked) VALUES ($1, $2, $2)", s.charID, TimeAdjusted())
} else {
s.server.db.Exec(fmt.Sprintf(`UPDATE stamps SET %s_checked=$1 WHERE character_id=$2`, pkt.StampType), TimeAdjusted(), s.charID)
s.server.db.Exec("INSERT INTO stamps (character_id, hl_next, ex_next) VALUES ($1, $2, $2)", s.charID, weekNextStart)
nextClaim = weekNextStart
}
if lastCheck.Before(TimeWeekStart()) {
s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1 WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID)
if nextClaim.Before(weekCurrentStart) {
s.server.db.Exec(fmt.Sprintf("UPDATE stamps SET %s_total=%s_total+1, %s_next=$1 WHERE character_id=$2", pkt.StampType, pkt.StampType, pkt.StampType), weekNextStart, s.charID)
updated = 1
}
s.server.db.QueryRow(fmt.Sprintf("SELECT %s_total, %s_redeemed FROM stamps WHERE character_id=$1", pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
bf := byteframe.NewByteFrame()
bf.WriteUint16(total)
bf.WriteUint16(redeemed)
bf.WriteUint16(updated)
bf.WriteUint16(0)
bf.WriteUint16(0)
bf.WriteUint32(uint32(TimeWeekStart().Unix()))
bf.WriteUint32(0) // Unk
bf.WriteUint32(uint32(weekCurrentStart.Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfExchangeWeeklyStamp)
var total, redeemed uint16
var tktStack mhfitem.MHFItemStack
if pkt.Unk1 == 10 { // Yearly Sub Ex
var tktStack mhfpacket.WarehouseStack
if pkt.Unk1 == 0xA { // Yearly Sub Ex
s.server.db.QueryRow("UPDATE stamps SET hl_total=hl_total-48, hl_redeemed=hl_redeemed-48 WHERE character_id=$1 RETURNING hl_total, hl_redeemed", s.charID).Scan(&total, &redeemed)
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 2210}, Quantity: 1}
tktStack = mhfpacket.WarehouseStack{ItemID: 0x08A2, Quantity: 1}
} else {
s.server.db.QueryRow(fmt.Sprintf("UPDATE stamps SET %s_redeemed=%s_redeemed+8 WHERE character_id=$1 RETURNING %s_total, %s_redeemed", pkt.StampType, pkt.StampType, pkt.StampType, pkt.StampType), s.charID).Scan(&total, &redeemed)
if pkt.StampType == "hl" {
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1630}, Quantity: 5}
tktStack = mhfpacket.WarehouseStack{ItemID: 0x065E, Quantity: 5}
} else {
tktStack = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: 1631}, Quantity: 5}
tktStack = mhfpacket.WarehouseStack{ItemID: 0x065F, Quantity: 5}
}
}
addWarehouseItem(s, tktStack)
addWarehouseGift(s, "item", tktStack)
bf := byteframe.NewByteFrame()
bf.WriteUint16(total)
bf.WriteUint16(redeemed)
bf.WriteUint16(0)
bf.WriteUint16(tktStack.Item.ItemID)
bf.WriteUint16(tktStack.Quantity)
bf.WriteUint32(0) // Unk, but has possible values
bf.WriteUint32(uint32(TimeWeekStart().Unix()))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
@@ -1039,58 +1106,30 @@ func handleMsgMhfUpdateEtcPoint(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfStampcardStamp(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStampcardStamp)
rewards := []struct {
HR uint16
Item1 uint16
Quantity1 uint16
Item2 uint16
Quantity2 uint16
}{
{0, 6164, 1, 6164, 2},
{50, 6164, 2, 6164, 3},
{100, 6164, 3, 5392, 1},
{300, 5392, 1, 5392, 3},
{999, 5392, 1, 5392, 4},
}
if _config.ErupeConfig.RealClientMode <= _config.Z1 {
for _, reward := range rewards {
if pkt.HR >= reward.HR {
pkt.Item1 = reward.Item1
pkt.Quantity1 = reward.Quantity1
pkt.Item2 = reward.Item2
pkt.Quantity2 = reward.Quantity2
}
}
}
bf := byteframe.NewByteFrame()
bf.WriteUint16(pkt.HR)
if _config.ErupeConfig.RealClientMode >= _config.G1 {
bf.WriteUint16(pkt.GR)
}
var stamps, rewardTier, rewardUnk uint16
reward := mhfitem.MHFItemStack{Item: mhfitem.MHFItem{}}
s.server.db.QueryRow(`UPDATE characters SET stampcard = stampcard + $1 WHERE id = $2 RETURNING stampcard`, pkt.Stamps, s.charID).Scan(&stamps)
bf.WriteUint16(stamps - pkt.Stamps)
bf.WriteUint16(pkt.GR)
var stamps uint16
_ = s.server.db.QueryRow(`SELECT stampcard FROM characters WHERE id = $1`, s.charID).Scan(&stamps)
bf.WriteUint16(stamps)
if stamps/30 > (stamps-pkt.Stamps)/30 {
rewardTier = 2
rewardUnk = pkt.Reward2
reward = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: pkt.Item2}, Quantity: pkt.Quantity2}
addWarehouseItem(s, reward)
} else if stamps/15 > (stamps-pkt.Stamps)/15 {
rewardTier = 1
rewardUnk = pkt.Reward1
reward = mhfitem.MHFItemStack{Item: mhfitem.MHFItem{ItemID: pkt.Item1}, Quantity: pkt.Quantity1}
addWarehouseItem(s, reward)
stamps += pkt.Stamps
bf.WriteUint16(stamps)
s.server.db.Exec(`UPDATE characters SET stampcard = $1 WHERE id = $2`, stamps, s.charID)
if stamps%30 == 0 {
bf.WriteUint16(2)
bf.WriteUint16(pkt.Reward2)
bf.WriteUint16(pkt.Item2)
bf.WriteUint16(pkt.Quantity2)
addWarehouseGift(s, "item", mhfpacket.WarehouseStack{ItemID: pkt.Item2, Quantity: pkt.Quantity2})
} else if stamps%15 == 0 {
bf.WriteUint16(1)
bf.WriteUint16(pkt.Reward1)
bf.WriteUint16(pkt.Item1)
bf.WriteUint16(pkt.Quantity1)
addWarehouseGift(s, "item", mhfpacket.WarehouseStack{ItemID: pkt.Item1, Quantity: pkt.Quantity1})
} else {
bf.WriteBytes(make([]byte, 8))
}
bf.WriteUint16(rewardTier)
bf.WriteUint16(rewardUnk)
bf.WriteUint16(reward.Item.ItemID)
bf.WriteUint16(reward.Quantity)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
@@ -1103,8 +1142,66 @@ func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthStatus)
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(TimeWeekStart().Unix())) // Start
bf.WriteUint32(uint32(TimeWeekNext().Unix())) // End
bf.WriteInt32(s.server.erupeConfig.EarthStatus)
bf.WriteInt32(s.server.erupeConfig.EarthID)
for i, m := range s.server.erupeConfig.EarthMonsters {
if _config.ErupeConfig.RealClientMode <= _config.G9 {
if i == 3 {
break
}
}
if i == 4 {
break
}
bf.WriteInt32(m)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfRegistSpabiTime(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthValue)
type EarthValues struct {
Value []uint32
}
var earthValues []EarthValues
switch pkt.ReqType {
case 1:
earthValues = []EarthValues{
{[]uint32{1, 312, 0, 0, 0, 0}},
{[]uint32{2, 99, 0, 0, 0, 0}},
}
case 2:
earthValues = []EarthValues{
{[]uint32{1, 5771, 0, 0, 0, 0}},
{[]uint32{2, 1847, 0, 0, 0, 0}},
}
case 3:
earthValues = []EarthValues{
{[]uint32{1001, 36, 0, 0, 0, 0}},
{[]uint32{9001, 3, 0, 0, 0, 0}},
{[]uint32{9002, 10, 300, 0, 0, 0}},
}
}
var data []*byteframe.ByteFrame
for _, i := range earthValues {
bf := byteframe.NewByteFrame()
for _, j := range i.Value {
bf.WriteUint32(j)
}
data = append(data, bf)
}
doAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfDebugPostValue(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetRandFromTable(s *Session, p mhfpacket.MHFPacket) {
@@ -1325,7 +1422,12 @@ func handleMsgMhfGetUdShopCoin(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUseUdShopCoin(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfUseUdShopCoin(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUseUdShopCoin)
bf := byteframe.NewByteFrame()
bf.WriteUint32(0)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetEnhancedMinidata(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEnhancedMinidata)

View File

@@ -7,47 +7,35 @@ import (
"erupe-ce/network/mhfpacket"
)
// Handler BBS handles all the interactions with the for the screenshot sending to bulitin board functionality. For it to work it requires the API to be hosted somehwere. This implementation supports discord.
// Checks the status of the user to see if they can use Bulitin Board yet
func handleMsgMhfGetBbsUserStatus(s *Session, p mhfpacket.MHFPacket) {
//Post Screenshot pauses till this succeedes
pkt := p.(*mhfpacket.MsgMhfGetBbsUserStatus)
bf := byteframe.NewByteFrame()
bf.WriteUint32(200) //HTTP Status Codes //200 Success //404 You wont be able to post for a certain amount of time after creating your character //401/500 A error occured server side
bf.WriteUint32(200)
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
// Checks the status of Bultin Board Server to see if authenticated
func handleMsgMhfGetBbsSnsStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetBbsSnsStatus)
bf := byteframe.NewByteFrame()
bf.WriteUint32(200) //200 Success //4XX Authentication has expired Please re-authenticate //5XX
bf.WriteUint32(401) //unk http status?
bf.WriteUint32(401) //unk http status?
bf.WriteUint32(200)
bf.WriteUint32(401)
bf.WriteUint32(401)
bf.WriteUint32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
// Tells the game client what host port and gives the bultin board article a token
func handleMsgMhfApplyBbsArticle(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfApplyBbsArticle)
bf := byteframe.NewByteFrame()
articleToken := token.Generate(40)
bf.WriteUint32(200) //http status //200 success //4XX An error occured server side
bf.WriteUint32(s.server.erupeConfig.Screenshots.Port)
bf.WriteUint32(200)
bf.WriteUint32(80)
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteBytes(stringsupport.PaddedString(articleToken, 64, false))
bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.Screenshots.Host, 64, false))
//pkt.unk1[3] == Changes sometimes?
if s.server.erupeConfig.Screenshots.Enabled && s.server.erupeConfig.Discord.Enabled {
s.server.DiscordScreenShotSend(pkt.Name, pkt.Title, pkt.Description, articleToken)
}
bf.WriteBytes(stringsupport.PaddedString(s.server.erupeConfig.ScreenshotAPIURL, 64, false))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -4,7 +4,6 @@ import (
"erupe-ce/common/byteframe"
"erupe-ce/common/mhfcourse"
ps "erupe-ce/common/pascalstring"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"fmt"
"go.uber.org/zap"
@@ -93,11 +92,10 @@ func handleMsgMhfGetCafeDuration(s *Session, p mhfpacket.MHFPacket) {
if mhfcourse.CourseExists(30, s.courses) {
cafeTime = uint32(TimeAdjusted().Unix()) - uint32(s.sessionStart) + cafeTime
}
bf.WriteUint32(cafeTime)
if _config.ErupeConfig.RealClientMode >= _config.ZZ {
bf.WriteUint16(0)
ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true)
}
bf.WriteUint32(cafeTime) // Total cafe time
bf.WriteUint16(0)
ps.Uint16(bf, fmt.Sprintf(s.server.i18n.cafe.reset, int(cafeReset.Month()), cafeReset.Day()), true)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -478,7 +478,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
m := binpacket.MsgBinChat{
Type: BinaryMessageTypeChat,
Flags: 4,
Message: fmt.Sprintf(`%d`, token.RNG.Intn(100)+1),
Message: fmt.Sprintf(`%d`, token.RNG().Intn(100)+1),
SenderName: author,
}
bf := byteframe.NewByteFrame()

View File

@@ -25,7 +25,7 @@ const (
pGardenData // +68
pWeaponType // +1
pWeaponID // +2
pHR // +2
pHRP // +2
pGRP // +4
pKQF // +8
lBookshelfData
@@ -47,7 +47,7 @@ type CharacterSaveData struct {
GardenData []byte
WeaponType uint8
WeaponID uint16
HR uint16
HRP uint16
GR uint16
KQF []byte
@@ -63,7 +63,7 @@ func getPointers() map[SavePointer]int {
pointers[pWeaponType] = 128789
pointers[pHouseTier] = 129900
pointers[pToreData] = 130228
pointers[pHR] = 130550
pointers[pHRP] = 130550
pointers[pGRP] = 130556
pointers[pHouseData] = 130561
pointers[pBookshelfData] = 139928
@@ -78,10 +78,10 @@ func getPointers() map[SavePointer]int {
pointers[pWeaponType] = 92789
pointers[pHouseTier] = 93900
pointers[pToreData] = 94228
pointers[pHR] = 94550
pointers[pHRP] = 94550
pointers[pGRP] = 94556
pointers[pHouseData] = 94561
pointers[pBookshelfData] = 89118 // TODO: fix bookshelf data pointer
pointers[pBookshelfData] = 103928
pointers[pGalleryData] = 104064
pointers[pGardenData] = 106424
pointers[pRP] = 106614
@@ -91,9 +91,9 @@ func getPointers() map[SavePointer]int {
pointers[pWeaponType] = 60789
pointers[pHouseTier] = 61900
pointers[pToreData] = 62228
pointers[pHR] = 62550
pointers[pHRP] = 62550
pointers[pHouseData] = 62561
pointers[pBookshelfData] = 57118 // TODO: fix bookshelf data pointer
pointers[pBookshelfData] = 57118 // This pointer only half works
pointers[pGalleryData] = 72064
pointers[pGardenData] = 74424
pointers[pRP] = 74614
@@ -102,9 +102,9 @@ func getPointers() map[SavePointer]int {
pointers[pWeaponType] = 12789
pointers[pHouseTier] = 13900
pointers[pToreData] = 14228
pointers[pHR] = 14550
pointers[pHRP] = 14550
pointers[pHouseData] = 14561
pointers[pBookshelfData] = 9118 // TODO: fix bookshelf data pointer
pointers[pBookshelfData] = 9118 // Probably same here
pointers[pGalleryData] = 24064
pointers[pGardenData] = 26424
pointers[pRP] = 26614
@@ -174,8 +174,8 @@ func (save *CharacterSaveData) Save(s *Session) {
save.compSave = save.decompSave
}
_, err := s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hr=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
`, save.compSave, save.HR, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
_, err := s.server.db.Exec(`UPDATE characters SET savedata=$1, is_new_character=false, hrp=$2, gr=$3, is_female=$4, weapon_type=$5, weapon_id=$6 WHERE id=$7
`, save.compSave, save.HRP, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
if err != nil {
s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
}
@@ -233,9 +233,9 @@ func (save *CharacterSaveData) updateStructWithSaveData() {
save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68]
save.WeaponType = save.decompSave[save.Pointers[pWeaponType]]
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2])
save.HR = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHR] : save.Pointers[pHR]+2])
save.HRP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHRP] : save.Pointers[pHRP]+2])
if _config.ErupeConfig.RealClientMode >= _config.G1 {
if save.HR == uint16(999) {
if save.HRP == uint16(999) {
save.GR = grpToGR(int(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4])))
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,172 +0,0 @@
package channelserver
import (
"erupe-ce/common/byteframe"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"log"
"time"
)
func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
bf := byteframe.NewByteFrame()
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(uint32(len(data)))
for i := range data {
bf.WriteBytes(data[i].Data())
}
doAckBufSucceed(s, ackHandle, bf.Data())
}
func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthValue)
type EarthValues struct {
Value []uint32
}
var earthValues []EarthValues
switch pkt.ReqType {
case 1:
earthValues = []EarthValues{
// {Block, DureSlays, Unk, Unk, Unk, Unk}
{[]uint32{1, 100, 0, 0, 0, 0}},
{[]uint32{2, 100, 0, 0, 0, 0}},
}
case 2:
earthValues = []EarthValues{
// {Block, Floors?, Unk, Unk, Unk, Unk}
{[]uint32{1, 5771, 0, 0, 0, 0}},
{[]uint32{2, 1847, 0, 0, 0, 0}},
}
case 3:
earthValues = []EarthValues{
{[]uint32{1001, 36, 0, 0, 0, 0}}, //getTouhaHistory
{[]uint32{9001, 3, 0, 0, 0, 0}}, //getKohouhinDropStopFlag // something to do with ttcSetDisableFlag?
{[]uint32{9002, 10, 300, 0, 0, 0}}, //getKohouhinForceValue
}
}
var data []*byteframe.ByteFrame
for _, i := range earthValues {
bf := byteframe.NewByteFrame()
for _, j := range i.Value {
bf.WriteUint32(j)
}
data = append(data, bf)
}
doAckEarthSucceed(s, pkt.AckHandle, data)
}
func cleanupEarthStatus(s *Session) {
s.server.db.Exec(`DELETE FROM events WHERE event_type='earth'`)
}
func generateEarthStatusTimestamps(s *Session, start uint32, debug bool) []uint32 {
timestamps := make([]uint32, 4)
midnight := TimeMidnight()
if start == 0 || TimeAdjusted().Unix() > int64(start)+1814400 {
cleanupEarthStatus(s)
start = uint32(midnight.Add(24 * time.Hour).Unix())
s.server.db.Exec("INSERT INTO events (event_type, start_time) VALUES ('earth', to_timestamp($1)::timestamp without time zone)", start)
}
if debug {
timestamps[0] = uint32(TimeWeekStart().Unix())
timestamps[1] = uint32(TimeWeekNext().Unix())
timestamps[2] = uint32(TimeWeekNext().Add(time.Duration(7) * time.Hour * 24).Unix())
timestamps[3] = uint32(TimeWeekNext().Add(time.Duration(14) * time.Hour * 24).Unix())
} else {
timestamps[0] = start
timestamps[1] = timestamps[0] + 604800
timestamps[2] = timestamps[1] + 604800
timestamps[3] = timestamps[2] + 604800
}
return timestamps
}
func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthStatus)
bf := byteframe.NewByteFrame()
var earthTimestamps []uint32
var debug = s.server.erupeConfig.EarthDebug
earthId, earthStart := int32(0x01BEEFEE), uint32(0)
rows, _ := s.server.db.Queryx("SELECT id, (EXTRACT(epoch FROM start_time)::int) as start_time FROM events WHERE event_type='earth'")
if rows == nil {
log.Println("No rows found")
} else {
for rows.Next() {
rows.Scan(&earthId, &earthStart)
}
}
earthTimestamps = generateEarthStatusTimestamps(s, earthStart, debug)
// Conquest
if uint32(TimeAdjusted().Unix()) > earthTimestamps[0] {
bf.WriteUint32(earthTimestamps[0]) // Start
bf.WriteUint32(earthTimestamps[1]) // End
bf.WriteInt32(1) //Conquest Earth Status ID //1 and 2 UNK the difference
bf.WriteInt32(earthId) //ID
} else {
bf.WriteUint32(earthTimestamps[1]) // Start
bf.WriteUint32(earthTimestamps[2]) // End
bf.WriteInt32(2) //Conquest Earth Status ID //1 and 2 UNK the difference
bf.WriteInt32(earthId) //ID
}
for i, m := range s.server.erupeConfig.EarthMonsters {
//Changed from G9 to G8 to get conquest working in g9.1
if _config.ErupeConfig.RealClientMode <= _config.G8 {
if i == 3 {
break
}
}
if i == 4 {
break
}
bf.WriteInt32(m)
}
// Pallone
if uint32(TimeAdjusted().Unix()) > earthTimestamps[1] {
bf.WriteUint32(earthTimestamps[1]) // Start
bf.WriteUint32(earthTimestamps[2]) // End
bf.WriteInt32(11) //Pallone Earth Status ID //11 is Fest //12 is Reward
bf.WriteInt32(earthId + 1) //ID
} else {
bf.WriteUint32(earthTimestamps[2]) // Start
bf.WriteUint32(earthTimestamps[3]) // End
bf.WriteInt32(12) //Pallone Earth Status ID //11 is Fest //12 is Reward
bf.WriteInt32(earthId + 1) //ID
}
for i, m := range s.server.erupeConfig.EarthMonsters {
//Changed from G9 to G8 to get conquest working in g9.1
if _config.ErupeConfig.RealClientMode <= _config.G8 {
if i == 3 {
break
}
}
if i == 4 {
break
}
bf.WriteInt32(m)
}
// Tower
if uint32(TimeAdjusted().Unix()) > earthTimestamps[2] {
bf.WriteUint32(earthTimestamps[2]) // Start
bf.WriteUint32(earthTimestamps[3]) // End
bf.WriteInt32(21) //Tower Earth Status ID
bf.WriteInt32(earthId + 2) //ID
for i, m := range s.server.erupeConfig.EarthMonsters {
if _config.ErupeConfig.RealClientMode <= _config.G8 {
if i == 3 {
break
}
}
if i == 4 {
break
}
bf.WriteInt32(m)
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -66,8 +66,7 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
var temp activeFeature
err := s.server.db.QueryRowx(`SELECT start_time, featured FROM feature_weapon WHERE start_time=$1`, t).StructScan(&temp)
if err != nil || temp.StartTime.IsZero() {
weapons := token.RNG.Intn(s.server.erupeConfig.GameplayOptions.MaxFeatureWeapons-s.server.erupeConfig.GameplayOptions.MinFeatureWeapons+1) + s.server.erupeConfig.GameplayOptions.MinFeatureWeapons
temp = generateFeatureWeapons(weapons)
temp = generateFeatureWeapons(s.server.erupeConfig.GameplayOptions.FeaturedWeapons)
temp.StartTime = t
s.server.db.Exec(`INSERT INTO feature_weapon VALUES ($1, $2)`, temp.StartTime, temp.ActiveFeatures)
}
@@ -102,7 +101,8 @@ func generateFeatureWeapons(count int) activeFeature {
nums := make([]int, 0)
var result int
for len(nums) < count {
num := token.RNG.Intn(_max)
rng := token.RNG()
num := rng.Intn(_max)
exist := false
for _, v := range nums {
if v == num {

View File

@@ -455,7 +455,7 @@ func handleMsgMhfEntryFesta(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
team := uint32(token.RNG.Intn(2))
team := uint32(token.RNG().Intn(2))
switch team {
case 0:
s.server.db.Exec("INSERT INTO festa_registrations VALUES ($1, 'blue')", guild.ID)

View File

@@ -3,9 +3,9 @@ package channelserver
import (
"database/sql"
"database/sql/driver"
"encoding/binary"
"encoding/json"
"errors"
"erupe-ce/common/mhfitem"
_config "erupe-ce/config"
"fmt"
"math"
@@ -51,8 +51,6 @@ type Guild struct {
MemberCount uint16 `db:"member_count"`
RankRP uint32 `db:"rank_rp"`
EventRP uint32 `db:"event_rp"`
RoomRP uint16 `db:"room_rp"`
RoomExpiry time.Time `db:"room_expiry"`
Comment string `db:"comment"`
PugiName1 string `db:"pugi_name_1"`
PugiName2 string `db:"pugi_name_2"`
@@ -155,8 +153,6 @@ SELECT
g.name,
rank_rp,
event_rp,
room_rp,
COALESCE(room_expiry, '1970-01-01') AS room_expiry,
main_motto,
sub_motto,
created_at,
@@ -710,7 +706,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
}
bf.WriteUint32(uint32(response))
case mhfpacket.OperateGuildDonateRank:
bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, 0))
bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false))
case mhfpacket.OperateGuildSetApplicationDeny:
s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID)
case mhfpacket.OperateGuildSetApplicationAllow:
@@ -751,11 +747,10 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
// TODO: This doesn't implement blocking, if someone unlocked the same outfit at the same time
s.server.db.Exec(`UPDATE guilds SET pugi_outfits=pugi_outfits+$1 WHERE id=$2`, int(math.Pow(float64(pkt.Data1.ReadUint32()), 2)), guild.ID)
case mhfpacket.OperateGuildDonateRoom:
quantity := uint16(pkt.Data1.ReadUint32())
bf.WriteBytes(handleDonateRP(s, quantity, guild, 2))
// TODO: Where does this go?
case mhfpacket.OperateGuildDonateEvent:
quantity := uint16(pkt.Data1.ReadUint32())
bf.WriteBytes(handleDonateRP(s, quantity, guild, 1))
bf.WriteBytes(handleDonateRP(s, quantity, guild, true))
// TODO: Move this value onto rp_yesterday and reset to 0... daily?
s.server.db.Exec(`UPDATE guild_characters SET rp_today=rp_today+$1 WHERE character_id=$2`, quantity, s.charID)
case mhfpacket.OperateGuildEventExchange:
@@ -799,37 +794,20 @@ func handleChangePugi(s *Session, outfit uint8, guild *Guild, num int) {
guild.Save(s)
}
func handleDonateRP(s *Session, amount uint16, guild *Guild, _type int) []byte {
func handleDonateRP(s *Session, amount uint16, guild *Guild, isEvent bool) []byte {
bf := byteframe.NewByteFrame()
bf.WriteUint32(0)
saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil {
return bf.Data()
}
var resetRoom bool
if _type == 2 {
var currentRP uint16
s.server.db.QueryRow(`SELECT room_rp FROM guilds WHERE id = $1`, guild.ID).Scan(&currentRP)
if currentRP+amount >= 30 {
amount = 30 - currentRP
resetRoom = true
}
}
saveData.RP -= amount
saveData.Save(s)
switch _type {
case 0:
s.server.db.Exec(`UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2`, amount, guild.ID)
case 1:
s.server.db.Exec(`UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2`, amount, guild.ID)
case 2:
if resetRoom {
s.server.db.Exec(`UPDATE guilds SET room_rp = 0 WHERE id = $1`, guild.ID)
s.server.db.Exec(`UPDATE guilds SET room_expiry = $1 WHERE id = $2`, TimeAdjusted().Add(time.Hour*24*7), guild.ID)
} else {
s.server.db.Exec(`UPDATE guilds SET room_rp = room_rp + $1 WHERE id = $2`, amount, guild.ID)
}
updateSQL := "UPDATE guilds SET rank_rp = rank_rp + $1 WHERE id = $2"
if isEvent {
updateSQL = "UPDATE guilds SET event_rp = event_rp + $1 WHERE id = $2"
}
s.server.db.Exec(updateSQL, amount, guild.ID)
bf.Seek(0, 0)
bf.WriteUint32(uint32(saveData.RP))
return bf.Data()
@@ -1023,8 +1001,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(limit)
bf.WriteUint32(55000)
bf.WriteUint32(uint32(guild.RoomExpiry.Unix()))
bf.WriteUint16(guild.RoomRP)
bf.WriteUint32(0)
bf.WriteUint16(0) // Changing Room RP
bf.WriteUint16(0) // Ignored
if guild.AllianceID > 0 {
@@ -1097,7 +1075,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
for _, applicant := range applicants {
bf.WriteUint32(applicant.CharID)
bf.WriteUint32(0)
bf.WriteUint16(applicant.HR)
bf.WriteUint16(applicant.HRP)
bf.WriteUint16(applicant.GR)
ps.Uint8(bf, applicant.Name, true)
}
@@ -1457,7 +1435,7 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
for _, member := range guildMembers {
bf.WriteUint32(member.CharID)
bf.WriteUint16(member.HR)
bf.WriteUint16(member.HRP)
if s.server.erupeConfig.RealClientMode >= _config.G10 {
bf.WriteUint16(member.GR)
}
@@ -1546,11 +1524,6 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetUdGuildMapInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdGuildMapInfo)
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGuildTargetMemberNum)
@@ -1576,34 +1549,100 @@ func handleMsgMhfGetGuildTargetMemberNum(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func guildGetItems(s *Session, guildID uint32) []mhfitem.MHFItemStack {
var data []byte
var items []mhfitem.MHFItemStack
s.server.db.QueryRow(`SELECT item_box FROM guilds WHERE id=$1`, guildID).Scan(&data)
if len(data) > 0 {
box := byteframe.NewByteFrameFromBytes(data)
numStacks := box.ReadUint16()
box.ReadUint16() // Unused
for i := 0; i < int(numStacks); i++ {
items = append(items, mhfitem.ReadWarehouseItem(box))
}
}
return items
}
func handleMsgMhfEnumerateGuildItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuildItem)
items := guildGetItems(s, pkt.GuildID)
var boxContents []byte
bf := byteframe.NewByteFrame()
bf.WriteBytes(mhfitem.SerializeWarehouseItems(items))
err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", pkt.GuildID).Scan(&boxContents)
if err != nil {
s.logger.Error("Failed to get guild item box contents from db", zap.Error(err))
bf.WriteBytes(make([]byte, 4))
} else {
if len(boxContents) == 0 {
bf.WriteBytes(make([]byte, 4))
} else {
amount := len(boxContents) / 4
bf.WriteUint16(uint16(amount))
bf.WriteUint32(0x00)
bf.WriteUint16(0x00)
for i := 0; i < amount; i++ {
bf.WriteUint32(binary.BigEndian.Uint32(boxContents[i*4 : i*4+4]))
if i+1 != amount {
bf.WriteUint64(0x00)
}
}
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type Item struct {
ItemId uint16
Amount uint16
}
func handleMsgMhfUpdateGuildItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateGuildItem)
newStacks := mhfitem.DiffItemStacks(guildGetItems(s, pkt.GuildID), pkt.UpdatedItems)
s.server.db.Exec(`UPDATE guilds SET item_box=$1 WHERE id=$2`, mhfitem.SerializeWarehouseItems(newStacks), pkt.GuildID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
// Get item cache from DB
var boxContents []byte
var oldItems []Item
err := s.server.db.QueryRow("SELECT item_box FROM guilds WHERE id = $1", pkt.GuildID).Scan(&boxContents)
if err != nil {
s.logger.Error("Failed to get guild item box contents from db", zap.Error(err))
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
return
} else {
amount := len(boxContents) / 4
oldItems = make([]Item, amount)
for i := 0; i < amount; i++ {
oldItems[i].ItemId = binary.BigEndian.Uint16(boxContents[i*4 : i*4+2])
oldItems[i].Amount = binary.BigEndian.Uint16(boxContents[i*4+2 : i*4+4])
}
}
// Update item stacks
newItems := make([]Item, len(oldItems))
copy(newItems, oldItems)
for i := 0; i < len(pkt.Items); i++ {
for j := 0; j <= len(oldItems); j++ {
if j == len(oldItems) {
var newItem Item
newItem.ItemId = pkt.Items[i].ItemID
newItem.Amount = pkt.Items[i].Amount
newItems = append(newItems, newItem)
break
}
if pkt.Items[i].ItemID == oldItems[j].ItemId {
newItems[j].Amount = pkt.Items[i].Amount
break
}
}
}
// Delete empty item stacks
for i := len(newItems) - 1; i >= 0; i-- {
if int(newItems[i].Amount) == 0 {
copy(newItems[i:], newItems[i+1:])
newItems[len(newItems)-1] = make([]Item, 1)[0]
newItems = newItems[:len(newItems)-1]
}
}
// Create new item cache
bf := byteframe.NewByteFrame()
for i := 0; i < len(newItems); i++ {
bf.WriteUint16(newItems[i].ItemId)
bf.WriteUint16(newItems[i].Amount)
}
// Upload new item cache
_, err = s.server.db.Exec("UPDATE guilds SET item_box = $1 WHERE id = $2", bf.Data(), pkt.GuildID)
if err != nil {
s.logger.Error("Failed to update guild item box contents in db", zap.Error(err))
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfUpdateGuildIcon(s *Session, p mhfpacket.MHFPacket) {
@@ -1984,11 +2023,6 @@ func handleMsgMhfAddGuildWeeklyBonusExceptionalUser(s *Session, p mhfpacket.MHFP
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfGenerateUdGuildMap(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGenerateUdGuildMap)
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfUpdateGuild(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfSetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {

View File

@@ -22,7 +22,7 @@ type GuildMember struct {
Recruiter bool `db:"recruiter"`
AvoidLeadership bool `db:"avoid_leadership"`
IsLeader bool `db:"is_leader"`
HR uint16 `db:"hr"`
HRP uint16 `db:"hrp"`
GR uint16 `db:"gr"`
WeaponID uint16 `db:"weapon_id"`
WeaponType uint8 `db:"weapon_type"`
@@ -74,7 +74,7 @@ SELECT * FROM (
c.last_login,
COALESCE(recruiter, false) AS recruiter,
COALESCE(avoid_leadership, false) AS avoid_leadership,
c.hr,
c.hrp,
c.gr,
c.weapon_id,
c.weapon_type,

View File

@@ -204,7 +204,7 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
}
rows, err := s.server.db.Queryx(`
SELECT c.id, c.name, c.hr, c.gr, ga.actor_id
SELECT c.id, c.name, c.hrp, c.gr, ga.actor_id
FROM guild_applications ga
JOIN characters c ON c.id = ga.character_id
WHERE ga.guild_id = $1 AND ga.application_type = 'invited'
@@ -230,9 +230,9 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
for rows.Next() {
var charName string
var charID, actorID uint32
var HR, GR uint16
var hrp, gr uint16
err = rows.Scan(&charID, &charName, &HR, &GR, &actorID)
err = rows.Scan(&charID, &charName, &hrp, &gr, &actorID)
if err != nil {
doAckSimpleFail(s, pkt.AckHandle, nil)
@@ -246,8 +246,8 @@ func handleMsgMhfGetGuildScoutList(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(actorID)
bf.WriteUint32(charID)
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
bf.WriteUint16(HR) // HR?
bf.WriteUint16(GR) // GR?
bf.WriteUint16(hrp) // HR?
bf.WriteUint16(gr) // GR?
bf.WriteBytes(stringsupport.PaddedString(charName, 32, true))
count++
}

View File

@@ -2,10 +2,8 @@ package channelserver
import (
"erupe-ce/common/byteframe"
"erupe-ce/common/mhfitem"
ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport"
"erupe-ce/common/token"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"fmt"
@@ -47,7 +45,7 @@ func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
type HouseData struct {
CharID uint32 `db:"id"`
HR uint16 `db:"hr"`
HRP uint16 `db:"hrp"`
GR uint16 `db:"gr"`
Name string `db:"name"`
HouseState uint8 `db:"house_state"`
@@ -59,7 +57,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
bf.WriteUint16(0)
var houses []HouseData
houseQuery := `SELECT c.id, hr, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
houseQuery := `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE c.id=$1`
switch pkt.Method {
case 1:
@@ -92,7 +90,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
}
}
case 3:
houseQuery = `SELECT c.id, hr, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
houseQuery = `SELECT c.id, hrp, gr, name, COALESCE(ub.house_state, 2) as house_state, COALESCE(ub.house_password, '') as house_password
FROM characters c LEFT JOIN user_binary ub ON ub.id = c.id WHERE name ILIKE $1`
house := HouseData{}
rows, _ := s.server.db.Queryx(houseQuery, fmt.Sprintf(`%%%s%%`, pkt.Name))
@@ -120,7 +118,7 @@ func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
} else {
bf.WriteUint8(0)
}
bf.WriteUint16(house.HR)
bf.WriteUint16(house.HRP)
if _config.ErupeConfig.RealClientMode >= _config.G10 {
bf.WriteUint16(house.GR)
}
@@ -367,17 +365,13 @@ func handleMsgMhfAcquireTitle(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfResetTitle(s *Session, p mhfpacket.MHFPacket) {}
func initializeWarehouse(s *Session) {
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateWarehouse)
var t int
err := s.server.db.QueryRow("SELECT character_id FROM warehouse WHERE character_id=$1", s.charID).Scan(&t)
if err != nil {
s.server.db.Exec("INSERT INTO warehouse (character_id) VALUES ($1)", s.charID)
}
}
func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateWarehouse)
initializeWarehouse(s)
bf := byteframe.NewByteFrame()
bf.WriteUint8(pkt.Operation)
switch pkt.Operation {
@@ -412,12 +406,7 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
case 1:
bf.WriteUint8(0)
case 2:
switch pkt.BoxType {
case 0:
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET item%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID)
case 1:
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET equip%dname=$1 WHERE character_id=$2", pkt.BoxIndex), pkt.Name, s.charID)
}
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%dname=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), pkt.Name, s.charID)
case 3:
bf.WriteUint32(0) // Usage renewal time, >1 = disabled
bf.WriteUint16(10000) // Usages
@@ -435,64 +424,81 @@ func handleMsgMhfOperateWarehouse(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func addWarehouseItem(s *Session, item mhfitem.MHFItemStack) {
giftBox := warehouseGetItems(s, 10)
item.WarehouseID = token.RNG.Uint32()
giftBox = append(giftBox, item)
s.server.db.Exec("UPDATE warehouse SET item10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseItems(giftBox), s.charID)
func addWarehouseGift(s *Session, boxType string, giftStack mhfpacket.WarehouseStack) {
giftBox := getWarehouseBox(s, boxType, 10)
if boxType == "item" {
exists := false
for i, stack := range giftBox {
if stack.ItemID == giftStack.ItemID {
exists = true
giftBox[i].Quantity += giftStack.Quantity
break
}
}
if exists == false {
giftBox = append(giftBox, giftStack)
}
} else {
giftBox = append(giftBox, giftStack)
}
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s10=$1 WHERE character_id=$2", boxType), boxToBytes(giftBox, boxType), s.charID)
}
func addWarehouseEquipment(s *Session, equipment mhfitem.MHFEquipment) {
giftBox := warehouseGetEquipment(s, 10)
equipment.WarehouseID = token.RNG.Uint32()
giftBox = append(giftBox, equipment)
s.server.db.Exec("UPDATE warehouse SET equip10=$1 WHERE character_id=$2", mhfitem.SerializeWarehouseEquipment(giftBox), s.charID)
}
func warehouseGetItems(s *Session, index uint8) []mhfitem.MHFItemStack {
initializeWarehouse(s)
func getWarehouseBox(s *Session, boxType string, boxIndex uint8) []mhfpacket.WarehouseStack {
var data []byte
var items []mhfitem.MHFItemStack
s.server.db.QueryRow(fmt.Sprintf(`SELECT item%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)
s.server.db.QueryRow(fmt.Sprintf("SELECT %s%d FROM warehouse WHERE character_id=$1", boxType, boxIndex), s.charID).Scan(&data)
if len(data) > 0 {
box := byteframe.NewByteFrameFromBytes(data)
numStacks := box.ReadUint16()
box.ReadUint16() // Unused
stacks := make([]mhfpacket.WarehouseStack, numStacks)
for i := 0; i < int(numStacks); i++ {
items = append(items, mhfitem.ReadWarehouseItem(box))
if boxType == "item" {
stacks[i].ID = box.ReadUint32()
stacks[i].Index = box.ReadUint16()
stacks[i].ItemID = box.ReadUint16()
stacks[i].Quantity = box.ReadUint16()
box.ReadUint16()
} else {
stacks[i].ID = box.ReadUint32()
stacks[i].Index = box.ReadUint16()
stacks[i].EquipType = box.ReadUint16()
stacks[i].ItemID = box.ReadUint16()
stacks[i].Data = box.ReadBytes(56)
}
}
return stacks
} else {
return make([]mhfpacket.WarehouseStack, 0)
}
return items
}
func warehouseGetEquipment(s *Session, index uint8) []mhfitem.MHFEquipment {
var data []byte
var equipment []mhfitem.MHFEquipment
s.server.db.QueryRow(fmt.Sprintf(`SELECT equip%d FROM warehouse WHERE character_id=$1`, index), s.charID).Scan(&data)
if len(data) > 0 {
box := byteframe.NewByteFrameFromBytes(data)
numStacks := box.ReadUint16()
box.ReadUint16() // Unused
for i := 0; i < int(numStacks); i++ {
equipment = append(equipment, mhfitem.ReadWarehouseEquipment(box))
func boxToBytes(stacks []mhfpacket.WarehouseStack, boxType string) []byte {
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(len(stacks)))
for i, stack := range stacks {
if boxType == "item" {
bf.WriteUint32(stack.ID)
bf.WriteUint16(uint16(i + 1))
bf.WriteUint16(stack.ItemID)
bf.WriteUint16(stack.Quantity)
bf.WriteUint16(0)
} else {
bf.WriteUint32(stack.ID)
bf.WriteUint16(uint16(i + 1))
bf.WriteUint16(stack.EquipType)
bf.WriteUint16(stack.ItemID)
bf.WriteBytes(stack.Data)
}
}
return equipment
bf.WriteUint16(0)
return bf.Data()
}
func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateWarehouse)
bf := byteframe.NewByteFrame()
switch pkt.BoxType {
case 0:
items := warehouseGetItems(s, pkt.BoxIndex)
bf.WriteBytes(mhfitem.SerializeWarehouseItems(items))
case 1:
equipment := warehouseGetEquipment(s, pkt.BoxIndex)
bf.WriteBytes(mhfitem.SerializeWarehouseEquipment(equipment))
}
if bf.Index() > 0 {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex)
if len(box) > 0 {
doAckBufSucceed(s, pkt.AckHandle, boxToBytes(box, pkt.BoxType))
} else {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
}
@@ -500,34 +506,49 @@ func handleMsgMhfEnumerateWarehouse(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfUpdateWarehouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateWarehouse)
switch pkt.BoxType {
case 0:
newStacks := mhfitem.DiffItemStacks(warehouseGetItems(s, pkt.BoxIndex), pkt.UpdatedItems)
s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET item%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseItems(newStacks), s.charID)
case 1:
var fEquip []mhfitem.MHFEquipment
oEquips := warehouseGetEquipment(s, pkt.BoxIndex)
for _, uEquip := range pkt.UpdatedEquipment {
exists := false
for i := range oEquips {
if oEquips[i].WarehouseID == uEquip.WarehouseID {
box := getWarehouseBox(s, pkt.BoxType, pkt.BoxIndex)
// Update existing stacks
var newStacks []mhfpacket.WarehouseStack
for _, update := range pkt.Updates {
exists := false
if pkt.BoxType == "item" {
for i, stack := range box {
if stack.Index == update.Index {
exists = true
// Will set removed items to 0
oEquips[i].ItemID = uEquip.ItemID
box[i].Quantity = update.Quantity
break
}
}
if !exists {
uEquip.WarehouseID = token.RNG.Uint32()
fEquip = append(fEquip, uEquip)
} else {
for i, stack := range box {
if stack.Index == update.Index {
exists = true
box[i].ItemID = update.ItemID
break
}
}
}
for _, oEquip := range oEquips {
if oEquip.ItemID > 0 {
fEquip = append(fEquip, oEquip)
}
if exists == false {
newStacks = append(newStacks, update)
}
s.server.db.Exec(fmt.Sprintf(`UPDATE warehouse SET equip%d=$1 WHERE character_id=$2`, pkt.BoxIndex), mhfitem.SerializeWarehouseEquipment(fEquip), s.charID)
}
// Append new stacks
for _, stack := range newStacks {
box = append(box, stack)
}
// Slice empty stacks
var cleanedBox []mhfpacket.WarehouseStack
for _, stack := range box {
if pkt.BoxType == "item" {
if stack.Quantity > 0 {
cleanedBox = append(cleanedBox, stack)
}
} else {
if stack.ItemID != 0 {
cleanedBox = append(cleanedBox, stack)
}
}
}
s.server.db.Exec(fmt.Sprintf("UPDATE warehouse SET %s%d=$1 WHERE character_id=$2", pkt.BoxType, pkt.BoxIndex), boxToBytes(cleanedBox, pkt.BoxType), s.charID)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -280,7 +280,7 @@ func makeEventQuest(s *Session, rows *sql.Rows) ([]byte, error) {
bf.WriteBool(true)
}
bf.WriteUint16(0) // Unk
if _config.ErupeConfig.RealClientMode >= _config.G2 {
if _config.ErupeConfig.RealClientMode >= _config.G1 {
bf.WriteUint32(mark)
}
bf.WriteUint16(0) // Unk
@@ -578,7 +578,7 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
tuneValues = temp
tuneLimit := 770
if _config.ErupeConfig.RealClientMode <= _config.G1 {
if _config.ErupeConfig.RealClientMode <= _config.F5 {
tuneLimit = 256
} else if _config.ErupeConfig.RealClientMode <= _config.G3 {
tuneLimit = 283

View File

@@ -1,8 +1,6 @@
package channelserver
import (
"encoding/hex"
"erupe-ce/common/byteframe"
"erupe-ce/network/mhfpacket"
)
@@ -17,21 +15,41 @@ func handleMsgMhfGetAdditionalBeatReward(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfGetUdRankingRewardList(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetUdRankingRewardList)
// Temporary canned response
data, _ := hex.DecodeString("0100001600000A5397DF00000000000000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
bf := byteframe.NewByteFrame()
bf.WriteUint16(0) // Len
// Format
// uint8 Unk
// uint16 Unk
// uint16 Unk
// uint8 Unk
// uint32 Unk
// uint32 Unk
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetRewardSong(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetRewardSong)
// Temporary canned response
data, _ := hex.DecodeString("0100001600000A5397DF00000000000000000000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
bf := byteframe.NewByteFrame()
bf.WriteUint8(0) // No error
bf.WriteUint8(0) // Unk
bf.WriteUint32(0) // Prayer ID
bf.WriteUint32(0xFFFFFFFF) // Prayer end
for i := 0; i < 4; i++ {
bf.WriteUint16(0)
bf.WriteUint8(0)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUseRewardSong(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfUseRewardSong(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUseRewardSong)
doAckBufSucceed(s, pkt.AckHandle, []byte{0})
}
func handleMsgMhfAddRewardSongCount(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfAddRewardSongCount(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAddRewardSongCount)
doAckBufSucceed(s, pkt.AckHandle, []byte{0})
}
func handleMsgMhfAcquireMonthlyReward(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireMonthlyReward)

View File

@@ -15,546 +15,31 @@ func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type WeeklySeibatuRankingRewardData struct {
Index0 int32 //Place Start
Index1 int32 //Place Finish
Index2 uint32 // UNK
DistributionType int32 //Type 7201:Item 7202:N Points 7203:Guild Contribution Points
ItemID int32
Amount int32
}
type WeeklySeibatuRankingRewards struct {
Unk0 int32
ItemID int32
Amount uint32
PlaceFrom int32
PlaceTo int32
type WeeklySeibatuRankingReward struct {
Unk0 int32
Unk1 int32
Unk2 uint32
Unk3 int32
Unk4 int32
Unk5 int32
}
func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetWeeklySeibatuRankingReward)
var data []*byteframe.ByteFrame
var weeklySeibatuRankingRewards []WeeklySeibatuRankingRewards
var weeklySeibatuRankingRewardsData []WeeklySeibatuRankingRewardData
switch pkt.Operation {
case 1:
//Conquest Data
switch pkt.ID { // Seems to align with EarthStatus 1 and 2 for Conquest
case 1:
switch pkt.EarthMonster {
case 116:
weeklySeibatuRankingRewards = []WeeklySeibatuRankingRewards{
{0, 2, 3, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 25, 1, 100},
{0, 2, 2, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 30, 101, 1000},
{0, 2, 2, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 6, 1000, 1001},
{0, 2, 6, 1000, 1001},
}
case 107:
weeklySeibatuRankingRewards = []WeeklySeibatuRankingRewards{
{0, 2, 3, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 25, 1, 100},
{0, 2, 2, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 30, 101, 1000},
{0, 2, 2, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 6, 1000, 1001},
{0, 2, 6, 1000, 1001},
}
case 2:
weeklySeibatuRankingRewards = []WeeklySeibatuRankingRewards{
{0, 2, 3, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 25, 1, 100},
{0, 2, 2, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 30, 101, 1000},
{0, 2, 2, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 6, 1000, 1001},
{0, 2, 6, 1000, 1001},
}
case 36:
weeklySeibatuRankingRewards = []WeeklySeibatuRankingRewards{
{0, 2, 3, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 25, 1, 100},
{0, 2, 2, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 30, 101, 1000},
{0, 2, 2, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 6, 1000, 1001},
{0, 2, 6, 1000, 1001},
}
}
case 2:
switch pkt.EarthMonster {
case 116:
weeklySeibatuRankingRewards = []WeeklySeibatuRankingRewards{
{0, 2, 3, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 25, 1, 100},
{0, 2, 2, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 30, 101, 1000},
{0, 2, 2, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 6, 1000, 1001},
{0, 2, 6, 1000, 1001},
}
case 107:
weeklySeibatuRankingRewards = []WeeklySeibatuRankingRewards{
{0, 2, 3, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 25, 1, 100},
{0, 2, 2, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 30, 101, 1000},
{0, 2, 2, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 6, 1000, 1001},
{0, 2, 6, 1000, 1001},
}
case 2:
weeklySeibatuRankingRewards = []WeeklySeibatuRankingRewards{
{0, 2, 3, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 25, 1, 100},
{0, 2, 2, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 30, 101, 1000},
{0, 2, 2, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 6, 1000, 1001},
{0, 2, 6, 1000, 1001},
}
case 36:
weeklySeibatuRankingRewards = []WeeklySeibatuRankingRewards{
{0, 2, 3, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 6, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 15, 1, 100},
{0, 2, 25, 1, 100},
{0, 2, 2, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 4, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 9, 101, 1000},
{0, 2, 30, 101, 1000},
{0, 2, 2, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 4, 1000, 1001},
{0, 2, 6, 1000, 1001},
{0, 2, 6, 1000, 1001},
}
}
}
case 3:
//Pallone Festival Data
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
//Unk0
//Unk1
//Unk2
//Unk3,
//ROUTE, (Crashes if it doesnt exist be careful with values )
//Status 1 = Only Now ! 2= Unk 3= Disabled}
//Route 0
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 1
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 2
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 3
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 4
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 5
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 6
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 7
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 8
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 9
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
//Route 10
{0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0},
}
// 0 = Max 7 Routes so value 6
//ZZ looks like it only works up to Route 2
case 5:
//Event Reward Data
switch pkt.ID {
//243400 = Route 0
//243401 = Route 1
//I have a sneaky suspicion that the above massive array is feeding into this somehow....
case 240031:
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
{1, 1, 1, 7201, 12068, 1}}
case 240041:
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
{0, 0, 1, 7201, 12068, 1}}
case 240042:
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
{0, 0, 2, 7201, 12068, 1}}
case 240051:
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
{0, 0, 1, 7201, 12068, 1}}
case 240052:
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
{1, 1, 1, 7201, 12068, 1},
}
case 260001:
//Tower Dure Kill Reward
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
//Can only have 10 in each dist (It disapears otherwise) Looks like up to dist 4 is implemented
//This is claimable for every Dure Kill, Make cliamable in bulk or mandatory claim per kill
//{unk,unk,dist,seiabtuType,ItemID,Value}
{0, 0, 1, 7201, 11463, 1},
{0, 0, 1, 7201, 11464, 1},
{0, 0, 1, 7201, 11163, 1},
{0, 0, 1, 7201, 11159, 5},
{0, 0, 1, 7201, 11160, 5},
{0, 0, 1, 7201, 11161, 5},
{0, 0, 2, 7201, 12506, 1},
{0, 0, 2, 7201, 10355, 1},
{0, 0, 2, 7201, 11163, 1},
{0, 0, 2, 7201, 11159, 5},
{0, 0, 2, 7201, 11160, 5},
{0, 0, 2, 7201, 11161, 5},
}
case 260003:
//Tower Floor Reward
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
//Adjust Floors done in database to make blue
//This is claimable for every Floor Climbed across dist 1 and 2
//{Floor,unk,unk,seiabtuType,ItemID,Value}
{1, 0, 0, 7201, 11158, 1},
{2, 0, 0, 7201, 11173, 1},
{3, 0, 0, 7201, 10813, 3},
{4, 0, 0, 7201, 11163, 1},
{5, 0, 0, 7201, 11164, 1},
{6, 0, 0, 7201, 11389, 3},
{6, 0, 0, 7201, 11381, 1},
{7, 0, 0, 7201, 11384, 1},
{8, 0, 0, 7201, 11159, 10},
{9, 0, 0, 7201, 11160, 10},
{10, 0, 0, 7201, 11161, 10},
{11, 0, 0, 7201, 11265, 2},
{11, 0, 0, 7201, 7279, 2},
{12, 0, 0, 7201, 11381, 1},
{13, 0, 0, 7201, 11384, 1},
{14, 0, 0, 7201, 11381, 1},
{15, 0, 0, 7201, 11384, 1},
{15, 0, 0, 7201, 11464, 1},
{16, 0, 0, 7201, 11381, 1},
{17, 0, 0, 7201, 11384, 1},
{18, 0, 0, 7201, 11381, 1},
{19, 0, 0, 7201, 11384, 1},
{20, 0, 0, 7201, 10778, 3},
{21, 0, 0, 7201, 11265, 2},
{21, 0, 0, 7201, 7279, 2},
{22, 0, 0, 7201, 11381, 1},
{23, 0, 0, 7201, 11384, 1},
{24, 0, 0, 7201, 11381, 1},
{25, 0, 0, 7201, 11389, 3},
{25, 0, 0, 7201, 11286, 4},
{26, 0, 0, 7201, 11384, 1},
{27, 0, 0, 7201, 11381, 1},
{28, 0, 0, 7201, 11384, 1},
{29, 0, 0, 7201, 11381, 1},
{30, 0, 0, 7201, 11209, 3},
{31, 0, 0, 7201, 11265, 2},
{31, 0, 0, 7201, 7279, 2},
{32, 0, 0, 7201, 11159, 10},
{33, 0, 0, 7201, 11463, 1},
{34, 0, 0, 7201, 11160, 10},
{35, 0, 0, 7201, 11286, 4},
{36, 0, 0, 7201, 11161, 10},
{38, 0, 0, 7201, 11384, 1},
{39, 0, 0, 7201, 11164, 1},
{40, 0, 0, 7201, 10813, 3},
{41, 0, 0, 7201, 11265, 2},
{41, 0, 0, 7201, 7280, 2},
{43, 0, 0, 7201, 11381, 1},
{45, 0, 0, 7201, 11286, 4},
{47, 0, 0, 7201, 11384, 1},
{48, 0, 0, 7201, 11358, 1},
{50, 0, 0, 7201, 11356, 1},
{51, 0, 0, 7201, 11265, 2},
{51, 0, 0, 7201, 7280, 2},
{53, 0, 0, 7201, 11381, 2},
{55, 0, 0, 7201, 11357, 1},
{57, 0, 0, 7201, 11384, 1},
{60, 0, 0, 7201, 11286, 4},
{61, 0, 0, 7201, 11265, 2},
{61, 0, 0, 7201, 7280, 2},
{63, 0, 0, 7201, 11381, 2},
{66, 0, 0, 7201, 11463, 1},
{67, 0, 0, 7201, 11384, 1},
{70, 0, 0, 7201, 11286, 4},
{71, 0, 0, 7201, 11265, 2},
{71, 0, 0, 7201, 7280, 2},
{73, 0, 0, 7201, 11381, 2},
{77, 0, 0, 7201, 11384, 1},
{79, 0, 0, 7201, 11164, 1},
{80, 0, 0, 7201, 11286, 6},
{81, 0, 0, 7201, 11265, 2},
{81, 0, 0, 7201, 7281, 1},
{83, 0, 0, 7201, 11381, 2},
{85, 0, 0, 7201, 11464, 1},
{87, 0, 0, 7201, 11384, 1},
{90, 0, 0, 7201, 11286, 6},
{91, 0, 0, 7201, 11265, 2},
{91, 0, 0, 7201, 7281, 1},
{93, 0, 0, 7201, 11381, 2},
{95, 0, 0, 7201, 10778, 3},
{97, 0, 0, 7201, 11384, 1},
{99, 0, 0, 7201, 11463, 1},
{100, 0, 0, 7201, 11286, 6},
{101, 0, 0, 7201, 11265, 2},
{101, 0, 0, 7201, 7281, 1},
{103, 0, 0, 7201, 11381, 2},
{107, 0, 0, 7201, 11384, 1},
{110, 0, 0, 7201, 11286, 6},
{113, 0, 0, 7201, 11381, 2},
{115, 0, 0, 7201, 11164, 1},
{117, 0, 0, 7201, 11384, 1},
{120, 0, 0, 7201, 11286, 12},
{123, 0, 0, 7201, 11381, 2},
{127, 0, 0, 7201, 11384, 1},
{130, 0, 0, 7201, 11286, 12},
{132, 0, 0, 7201, 11381, 2},
{134, 0, 0, 7201, 11384, 1},
{136, 0, 0, 7201, 11381, 2},
{138, 0, 0, 7201, 11384, 1},
{140, 0, 0, 7201, 11286, 12},
{142, 0, 0, 7201, 11382, 1},
{144, 0, 0, 7201, 11385, 1},
{145, 0, 0, 7201, 11464, 1},
{146, 0, 0, 7201, 11382, 1},
{148, 0, 0, 7201, 11385, 1},
{150, 0, 0, 7201, 11164, 1},
{155, 0, 0, 7201, 11382, 1},
{160, 0, 0, 7201, 11209, 3},
{165, 0, 0, 7201, 11385, 1},
{170, 0, 0, 7201, 11159, 10},
{175, 0, 0, 7201, 11382, 1},
{180, 0, 0, 7201, 11160, 10},
{185, 0, 0, 7201, 11385, 1},
{190, 0, 0, 7201, 11161, 10},
{195, 0, 0, 7201, 11382, 1},
{200, 0, 0, 7201, 11159, 15},
{210, 0, 0, 7201, 11160, 15},
{220, 0, 0, 7201, 11385, 1},
{235, 0, 0, 7201, 11382, 2},
{250, 0, 0, 7201, 11161, 15},
{265, 0, 0, 7201, 11159, 20},
{280, 0, 0, 7201, 11385, 1},
{300, 0, 0, 7201, 11160, 20},
{315, 0, 0, 7201, 11382, 2},
{330, 0, 0, 7201, 11385, 1},
{350, 0, 0, 7201, 11161, 20},
{365, 0, 0, 7201, 11382, 2},
{380, 0, 0, 7201, 11385, 1},
{400, 0, 0, 7201, 11159, 25},
{415, 0, 0, 7201, 11382, 2},
{430, 0, 0, 7201, 11385, 1},
{450, 0, 0, 7201, 11160, 25},
{465, 0, 0, 7201, 11382, 2},
{480, 0, 0, 7201, 11385, 1},
{500, 0, 0, 7201, 11161, 25},
{525, 0, 0, 7201, 11382, 2},
{550, 0, 0, 7201, 11385, 1},
{575, 0, 0, 7201, 11159, 25},
{600, 0, 0, 7201, 11382, 2},
{625, 0, 0, 7201, 11385, 1},
{650, 0, 0, 7201, 11160, 25},
{675, 0, 0, 7201, 11382, 2},
{700, 0, 0, 7201, 11385, 1},
{725, 0, 0, 7201, 11161, 25},
{750, 0, 0, 7201, 11382, 2},
{775, 0, 0, 7201, 11385, 1},
{800, 0, 0, 7201, 11159, 25},
{825, 0, 0, 7201, 11382, 2},
{850, 0, 0, 7201, 11385, 1},
{875, 0, 0, 7201, 11160, 25},
{900, 0, 0, 7201, 11382, 2},
{925, 0, 0, 7201, 11385, 1},
{950, 0, 0, 7201, 11161, 25},
{975, 0, 0, 7201, 11382, 2},
{1000, 0, 0, 7201, 11385, 1},
{1025, 0, 0, 7201, 11159, 25},
{1050, 0, 0, 7201, 11382, 2},
{1075, 0, 0, 7201, 11385, 1},
{1100, 0, 0, 7201, 11160, 25},
{1125, 0, 0, 7201, 11382, 2},
{1150, 0, 0, 7201, 11385, 1},
{1200, 0, 0, 7201, 11161, 25},
{1235, 0, 0, 7201, 11382, 2},
{1270, 0, 0, 7201, 11385, 1},
{1305, 0, 0, 7201, 11159, 25},
{1340, 0, 0, 7201, 11382, 2},
{1375, 0, 0, 7201, 11385, 1},
{1410, 0, 0, 7201, 11160, 25},
{1445, 0, 0, 7201, 11382, 2},
{1480, 0, 0, 7201, 11385, 1},
{1500, 0, 0, 7201, 11161, 25},
}
default:
//Covers all Pallone Requests... for now
weeklySeibatuRankingRewardsData = []WeeklySeibatuRankingRewardData{
//1st
{1, 0, 0, 7202, 10, 10000},
{1, 1, 0, 7201, 10, 30},
{1, 1, 0, 7201, 10, 18},
{1, 1, 0, 7201, 10, 18},
//2nd - 3rd
{2, 3, 0, 7202, 10, 6000},
{2, 3, 0, 7201, 10, 15},
{2, 3, 0, 7201, 10, 9},
{2, 3, 0, 7201, 10, 9},
//4th -10th
{4, 10, 0, 7202, 10, 5500},
{4, 10, 0, 7201, 10, 12},
{4, 10, 0, 7201, 10, 9},
}
}
weeklySeibatuRankingRewards := []WeeklySeibatuRankingReward{
{0, 0, 0, 0, 0, 0},
}
if pkt.Operation == 1 {
for _, seibatuData := range weeklySeibatuRankingRewards {
bf := byteframe.NewByteFrame()
bf.WriteInt32(seibatuData.Unk0)
bf.WriteInt32(seibatuData.ItemID)
bf.WriteUint32(seibatuData.Amount)
bf.WriteInt32(seibatuData.PlaceFrom)
bf.WriteInt32(seibatuData.PlaceTo)
data = append(data, bf)
}
} else {
for _, seibatuData := range weeklySeibatuRankingRewardsData {
bf := byteframe.NewByteFrame()
bf.WriteInt32(seibatuData.Index0)
bf.WriteInt32(seibatuData.Index1)
bf.WriteUint32(seibatuData.Index2)
bf.WriteInt32(seibatuData.DistributionType)
bf.WriteInt32(seibatuData.ItemID)
bf.WriteInt32(seibatuData.Amount)
data = append(data, bf)
}
for _, reward := range weeklySeibatuRankingRewards {
bf := byteframe.NewByteFrame()
bf.WriteInt32(reward.Unk0)
bf.WriteInt32(reward.Unk1)
bf.WriteUint32(reward.Unk2)
bf.WriteInt32(reward.Unk3)
bf.WriteInt32(reward.Unk4)
bf.WriteInt32(reward.Unk5)
data = append(data, bf)
}
doAckEarthSucceed(s, pkt.AckHandle, data)
}

View File

@@ -148,24 +148,9 @@ func removeSessionFromStage(s *Session) {
destructEmptySemaphores(s)
}
func isStageFull(s *Session, StageID string) bool {
if stage, exists := s.server.stages[StageID]; exists {
if _, exists := stage.reservedClientSlots[s.charID]; exists {
return false
}
return len(stage.reservedClientSlots)+len(stage.clients) >= int(stage.maxPlayers)
}
return false
}
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnterStage)
if isStageFull(s, pkt.StageID) {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
return
}
// Push our current stage ID to the movement stack before entering another one.
if s.stage != nil {
s.stage.Lock()
@@ -190,12 +175,6 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
backStage = "sl1Ns200p0a0u0"
}
if isStageFull(s, backStage) {
s.stageMoveStack.Push(backStage)
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
return
}
if _, exists := s.stage.reservedClientSlots[s.charID]; exists {
delete(s.stage.reservedClientSlots, s.charID)
}
@@ -209,12 +188,6 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysMoveStage)
if isStageFull(s, pkt.StageID) {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x01})
return
}
doStageTransfer(s, pkt.AckHandle, pkt.StageID)
}

File diff suppressed because one or more lines are too long

View File

@@ -367,14 +367,6 @@ func (s *Server) DiscordChannelSend(charName string, content string) {
}
}
func (s *Server) DiscordScreenShotSend(charName string, title string, description string, articleToken string) {
if s.erupeConfig.Discord.Enabled && s.discordBot != nil {
imageUrl := fmt.Sprintf("%s:%d/api/ss/bbs/%s", s.erupeConfig.Screenshots.Host, s.erupeConfig.Screenshots.Port, articleToken)
message := fmt.Sprintf("**%s**: %s - %s %s", charName, title, description, imageUrl)
s.discordBot.RealtimeChannelSend(message)
}
}
func (s *Server) FindSessionByCharID(charID uint32) *Session {
for _, c := range s.Channels {
for _, session := range c.sessions {

View File

@@ -1,5 +1,10 @@
package channelserver
type Bead struct {
id int
name string
description string
}
type i18n struct {
language string
cafe struct {
@@ -99,6 +104,11 @@ type i18n struct {
}
}
}
diva struct {
prayer struct {
beads []Bead
}
}
}
func getLangStrings(s *Server) i18n {
@@ -109,6 +119,27 @@ func getLangStrings(s *Server) i18n {
i.cafe.reset = "%d/%dにリセット"
i.timer = "タイマー:%02d'%02d\"%02d.%03d (%df)"
i.diva.prayer.beads = []Bead{
{id: 1, name: "暴風の祈珠", description: "ーあらしまかぜのきじゅー\n暴風とは猛る思い。\n聞く者に勇気を与える。"},
{id: 3, name: "断力の祈珠", description: "ーだんりきのきじゅー\n断力とは断ち切る思い。\n聴く者に新たな利からを授ける。"},
{id: 4, name: "風韻の祈珠", description: "ーふういんのきじゅー\n風韻とは歌姫の艶。\n時々で異なる趣を醸し出す。"},
{id: 8, name: "斬刃の祈珠", description: "ーざんばのきじゅー\n斬刃とはすべてを切り裂く力。\n集めるほどに声の透明感は増す。"},
{id: 9, name: "打明の祈珠", description: "ーうちあかりのきじゅー\n打明とは熱い力。\n聴く者に活力を与える。"},
{id: 10, name: "弾起の祈珠", description: "ーたまおこしのきじゅー\n弾起とは悠遠の記憶。\n聴く者に更なる力を授ける。"},
{id: 11, name: "変続の祈珠", description: "ーへんぞくのきじゅー\n変続とは永久の言葉。\n聴く者に継続力を授ける。"},
{id: 14, name: "万雷の祈珠", description: "ーばんらいのきじゅー\n万雷とは歌姫に集う民の意識。\n歌姫の声を伝播させる。"},
{id: 15, name: "不動の祈珠", description: "ーうごかずのきじゅー\n不動とは圧力。聞く者に圧倒する力を与える。"},
{id: 17, name: "結集の祈珠", description: "ーけっしゅうのきじゅー\n結集とは確固たる信頼。\n集めるほどに狩人たちの精神力となる。"},
{id: 18, name: "歌護の祈珠", description: "ーうたまもりのきじゅー\n歌護とは歌姫の護り。\n集めるほどに狩人たちの支えとなる。"},
{id: 19, name: "強撃の祈珠", description: "ーきょうげきのきじゅー\n強撃とは強い声色。\n聞く者の力を研ぎ澄ます。"},
{id: 20, name: "封火の祈珠", description: "ーふうかのきじゅー"},
{id: 21, name: "封水の祈珠", description: "ーふうすいのきじゅー"},
{id: 22, name: "封氷の祈珠", description: "ーふうひょうのきじゅー"},
{id: 23, name: "封龍の祈珠", description: "ーふうりゅうのきじゅー"},
{id: 24, name: "封雷の祈珠", description: "ーふうらいのきじゅー"},
{id: 25, name: "封属の祈珠", description: "ーふうぞくのきじゅー"},
}
i.commands.noOp = "You don't have permission to use this command"
i.commands.disabled = "%sのコマンドは無効です"
i.commands.reload = "リロードします"
@@ -170,6 +201,26 @@ func getLangStrings(s *Server) i18n {
default:
i.language = "English"
i.cafe.reset = "Resets on %d/%d"
i.diva.prayer.beads = []Bead{
{id: 1, name: "Bead of Storms", description: "ーあらしまかぜのきじゅー\n暴風とは猛る思い。\n聞く者に勇気を与える。"},
{id: 3, name: "Bead of Severing", description: "All damage types can sever tails\nPower to sever, inspire with might.\nEmpower those who hear, in new light."},
{id: 4, name: "Bead of Vitality", description: "Increased red health recovery speed\nDiva's allure, a soothing balm.\nRenews one's vigor, with vitality and calm."},
{id: 8, name: "Bead of Slashing", description: "Damage up for slashing weapons\nWith every slash, its voice rings out.\nGrowing ever sharper, without a doubt."},
{id: 9, name: "Bead of Striking", description: "Damage up for striking weapons\nWith every blow, you strike with force.\nLet the power guide your course."},
{id: 10, name: "Bead of Firing", description: "Damage up for shooting weapons\nA memory of might, empowering those who hear.\nBullet and body, soaring without fear."},
{id: 11, name: "Bead of Tenacity", description: "ーへんぞくのきじゅー\n変続とは永久の言葉。\n聴く者に継続力を授ける。"},
{id: 14, name: "Bead of Elements", description: "ーばんらいのきじゅー\n万雷とは歌姫に集う民の意識。\n歌姫の声を伝播させる。"},
{id: 15, name: "Bead of Restraint", description: "ーうごかずのきじゅー\n不動とは圧力。聞く者に圧倒する力を与える。"},
{id: 17, name: "Bead of Unity", description: "ーけっしゅうのきじゅー\n結集とは確固たる信頼。\n集めるほどに狩人たちの精神力となる。"},
{id: 18, name: "Bead of Warding", description: "ーうたまもりのきじゅー\n歌護とは歌姫の護り。\n集めるほどに狩人たちの支えとなる。"},
{id: 19, name: "Bead of Fury", description: "ーきょうげきのきじゅー\n強撃とは強い声色。\n聞く者の力を研ぎ澄ます。"},
{id: 20, name: "Bead of Fireproof", description: "ーふうかのきじゅー"},
{id: 21, name: "Bead of Waterproof", description: "ーふうすいのきじゅー"},
{id: 22, name: "Bead of Iceproof", description: "ーふうひょうのきじゅー"},
{id: 23, name: "Bead of Dragonproof", description: "ーふうりゅうのきじゅー"},
{id: 24, name: "Bead of Thunderproof", description: "ーふうらいのきじゅー"},
{id: 25, name: "Bead of Immunity", description: "ーふうぞくのきじゅー"},
}
i.timer = "Time: %02d:%02d:%02d.%03d (%df)"
i.commands.noOp = "You don't have permission to use this command"

View File

@@ -59,7 +59,7 @@ func NewStage(ID string) *Stage {
objects: make(map[uint32]*Object),
objectIndex: 0,
rawBinaryData: make(map[stageBinaryKey][]byte),
maxPlayers: 127,
maxPlayers: 4,
}
return s
}

View File

@@ -1,11 +1,10 @@
package discordbot
import (
_config "erupe-ce/config"
"regexp"
"erupe-ce/config"
"github.com/bwmarrin/discordgo"
"go.uber.org/zap"
"regexp"
)
var Commands = []*discordgo.ApplicationCommand{
@@ -114,6 +113,7 @@ func (bot *DiscordBot) RealtimeChannelSend(message string) (err error) {
return
}
func ReplaceTextAll(text string, regex *regexp.Regexp, handler func(input string) string) string {
result := regex.ReplaceAllFunc([]byte(text), func(s []byte) []byte {
input := regex.ReplaceAllString(string(s), `$1`)

View File

@@ -27,7 +27,7 @@ func (s *Server) newUserChara(uid uint32) error {
_, err = s.db.Exec(`
INSERT INTO characters (
user_id, is_female, is_new_character, name, unk_desc_string,
hr, gr, weapon_type, last_login)
hrp, gr, weapon_type, last_login)
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
uid,
uint32(time.Now().Unix()),
@@ -63,7 +63,7 @@ type character struct {
IsNewCharacter bool `db:"is_new_character"`
Name string `db:"name"`
UnkDescString string `db:"unk_desc_string"`
HR uint16 `db:"hr"`
HRP uint16 `db:"hrp"`
GR uint16 `db:"gr"`
WeaponType uint16 `db:"weapon_type"`
LastLogin uint32 `db:"last_login"`
@@ -71,7 +71,7 @@ type character struct {
func (s *Server) getCharactersForUser(uid uint32) ([]character, error) {
characters := make([]character, 0)
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hr, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id", uid)
err := s.db.Select(&characters, "SELECT id, is_female, is_new_character, name, unk_desc_string, hrp, gr, weapon_type, last_login FROM characters WHERE user_id = $1 AND deleted = false ORDER BY id", uid)
if err != nil {
return nil, err
}

View File

@@ -7,10 +7,9 @@ import (
_config "erupe-ce/config"
"erupe-ce/server/channelserver"
"fmt"
"go.uber.org/zap"
"strings"
"time"
"go.uber.org/zap"
)
func (s *Session) makeSignResponse(uid uint32) []byte {
@@ -39,24 +38,25 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
return bf.Data()
}
if s.client == PS3 && (s.server.erupeConfig.PatchServerFile == "" || s.server.erupeConfig.PatchServerManifest == "") {
bf.WriteUint8(uint8(SIGN_EABORT))
return bf.Data()
bf.WriteUint8(uint8(SIGN_SUCCESS)) // resp_code
if (s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "") || s.client == PS3 {
bf.WriteUint8(2)
} else {
bf.WriteUint8(0)
}
bf.WriteUint8(uint8(SIGN_SUCCESS))
bf.WriteUint8(2) // patch server count
bf.WriteUint8(1) // entrance server count
bf.WriteUint8(uint8(len(chars)))
bf.WriteUint32(tokenID)
bf.WriteBytes([]byte(sessToken))
bf.WriteUint32(uint32(channelserver.TimeAdjusted().Unix()))
if s.client == PS3 {
ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerManifest), false)
ps.Uint8(bf, fmt.Sprintf("%s/ps3", s.server.erupeConfig.PatchServerFile), false)
ps.Uint8(bf, fmt.Sprintf(`ps3-%s.zerulight.cc`, s.server.erupeConfig.Language), false)
ps.Uint8(bf, fmt.Sprintf(`ps3-%s.zerulight.cc`, s.server.erupeConfig.Language), false)
} else {
ps.Uint8(bf, s.server.erupeConfig.PatchServerManifest, false)
ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false)
if s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "" {
ps.Uint8(bf, s.server.erupeConfig.PatchServerManifest, false)
ps.Uint8(bf, s.server.erupeConfig.PatchServerFile, false)
}
}
if strings.Split(s.rawConn.RemoteAddr().String(), ":")[0] == "127.0.0.1" {
ps.Uint8(bf, fmt.Sprintf("127.0.0.1:%d", s.server.erupeConfig.Entrance.Port), false)
@@ -70,11 +70,14 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
lastPlayed = char.ID
}
bf.WriteUint32(char.ID)
// Exp, HR[x] is split by 0, 1, 30, 50, 99, 299, 998, 999
if s.server.erupeConfig.DebugOptions.MaxLauncherHR {
bf.WriteUint16(999)
} else {
bf.WriteUint16(char.HR)
bf.WriteUint16(char.HRP)
}
bf.WriteUint16(char.WeaponType) // Weapon, 0-13.
bf.WriteUint32(char.LastLogin) // Last login date, unix timestamp in seconds.
bf.WriteBool(char.IsFemale) // Sex, 0=male, 1=female.
@@ -135,204 +138,8 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
bf.WriteUint32(s.server.getLastCID(uid))
bf.WriteUint32(s.server.getUserRights(uid))
namNGWords := []string{}
msgNGWords := []string{}
filters := byteframe.NewByteFrame()
filters.SetLE()
filters.WriteNullTerminatedBytes([]byte("smc"))
smc := byteframe.NewByteFrame()
smc.SetLE()
smcData := []struct {
charGroup [][]rune
}{
{[][]rune{{'='}, {''}}},
{[][]rune{{')'}, {''}}},
{[][]rune{{'('}, {''}}},
{[][]rune{{'!'}, {''}}},
{[][]rune{{'/'}, {''}}},
{[][]rune{{'+'}, {''}}},
{[][]rune{{'&'}, {''}}},
{[][]rune{{'ぼ'}, {'ボ'}, {'ホ', '゙'}, {'ほ', '゙'}, {'ホ', '゙'}, {'ほ', '゛'}, {'ホ', '゛'}, {'ホ', '゛'}}},
{[][]rune{{'べ'}, {'ベ'}, {'ヘ', '゙'}, {'へ', '゙'}, {'ヘ', '゙'}, {'へ', '゛'}, {'ヘ', '゛'}, {'ヘ', '゛'}}},
{[][]rune{{'で'}, {'デ'}, {'テ', '゙'}, {'て', '゙'}, {'テ', '゙'}, {'て', '゛'}, {'テ', '゛'}, {'テ', '゛'}, {'〒', '゛'}, {'〒', '゙'}, {'乙', '゙'}, {'乙', '゛'}}},
{[][]rune{{'び'}, {'ビ'}, {'ヒ', '゙'}, {'ひ', '゙'}, {'ヒ', '゙'}, {'ひ', '゛'}, {'ヒ', '゛'}, {'ヒ', '゛'}}},
{[][]rune{{'ど'}, {'ド'}, {'ト', '゙'}, {'と', '゙'}, {'ト', '゙'}, {'と', '゛'}, {'ト', '゛'}, {'ト', '゛'}, {'┣', '゙'}, {'┣', '゛'}, {'├', '゙'}, {'├', '゛'}}},
{[][]rune{{'ば'}, {'バ'}, {'ハ', '゙'}, {'は', '゙'}, {'ハ', '゙'}, {'八', '゙'}, {'は', '゛'}, {'ハ', '゛'}, {'ハ', '゛'}, {'八', '゛'}}},
{[][]rune{{'つ', '゙'}, {'ヅ'}, {'ツ', '゙'}, {'つ', '゛'}, {'ツ', '゛'}, {'ツ', '゙'}, {'ツ', '゛'}, {'づ'}, {'っ', '゙'}, {'ッ', '゙'}, {'ッ', '゙'}, {'っ', '゛'}, {'ッ', '゛'}, {'ッ', '゛'}}},
{[][]rune{{'ぶ'}, {'ブ'}, {'フ', '゙'}, {'ヴ'}, {'ウ', '゙'}, {'う', '゛'}, {'う', '゙'}, {'ウ', '゙'}, {'ゥ', '゙'}, {'ぅ', '゙'}, {'ふ', '゙'}, {'フ', '゙'}, {'フ', '゛'}}},
{[][]rune{{'ぢ'}, {'ヂ'}, {'チ', '゙'}, {'ち', '゙'}, {'チ', '゙'}, {'ち', '゛'}, {'チ', '゛'}, {'チ', '゛'}, {'千', '゛'}, {'千', '゙'}}},
{[][]rune{{'だ'}, {'ダ'}, {'タ', '゙'}, {'た', '゙'}, {'タ', '゙'}, {'夕', '゙'}, {'た', '゛'}, {'タ', '゛'}, {'タ', '゛'}, {'夕', '゛'}}},
{[][]rune{{'ぞ'}, {'ゾ'}, {'ソ', '゙'}, {'そ', '゙'}, {'ソ', '゙'}, {'そ', '゛'}, {'ソ', '゛'}, {'ソ', '゛'}, {'ン', '゙'}, {'ン', '゛'}, {'ン', '゛'}, {'ン', '゙'}, {'リ', '゙'}, {'リ', '゙'}, {'リ', '゛'}, {'リ', '゛'}}},
{[][]rune{{'ぜ'}, {'セ', '゙'}, {'せ', '゙'}, {'セ', '゙'}, {'せ', '゛'}, {'セ', '゛'}, {'セ', '゛'}, {'ゼ'}}},
{[][]rune{{'ず'}, {'ズ'}, {'ス', '゙'}, {'す', '゙'}, {'ス', '゙'}, {'す', '゛'}, {'ス', '゛'}, {'ス', '゛'}}},
{[][]rune{{'じ'}, {'ジ'}, {'シ', '゙'}, {'し', '゙'}, {'シ', '゙'}, {'し', '゛'}, {'シ', '゛'}, {'シ', '゛'}}},
{[][]rune{{'ざ'}, {'ザ'}, {'サ', '゙'}, {'さ', '゙'}, {'サ', '゙'}, {'さ', '゛'}, {'サ', '゛'}, {'サ', '゛'}}},
{[][]rune{{'ご'}, {'ゴ'}, {'コ', '゙'}, {'こ', '゙'}, {'コ', '゙'}, {'こ', '゛'}, {'コ', '゛'}, {'コ', '゛'}}},
{[][]rune{{'げ'}, {'ゲ'}, {'ケ', '゙'}, {'け', '゙'}, {'ケ', '゙'}, {'け', '゛'}, {'ケ', '゛'}, {'ケ', '゛'}, {'ヶ', '゙'}, {'ヶ', '゛'}}},
{[][]rune{{'ぐ'}, {'グ'}, {'ク', '゙'}, {'く', '゙'}, {'ク', '゙'}, {'く', '゛'}, {'ク', '゛'}, {'ク', '゛'}}},
{[][]rune{{'ぎ'}, {'ギ'}, {'キ', '゙'}, {'き', '゙'}, {'キ', '゙'}, {'き', '゛'}, {'キ', '゛'}, {'キ', '゛'}}},
{[][]rune{{'が'}, {'ガ'}, {'カ', '゙'}, {'ヵ', '゙'}, {'カ', '゙'}, {'か', '゙'}, {'力', '゙'}, {'ヵ', '゛'}, {'カ', '゛'}, {'か', '゛'}, {'力', '゛'}, {'カ', '゛'}}},
{[][]rune{{'を'}, {'ヲ'}, {'ヲ'}}},
{[][]rune{{'わ'}, {'ワ'}, {'ワ'}, {'ヮ'}}},
{[][]rune{{'ろ'}, {'ロ'}, {'ロ'}, {'□'}, {'口'}}},
{[][]rune{{'れ'}, {'レ'}, {'レ'}}},
{[][]rune{{'る'}, {'ル'}, {'ル'}}},
{[][]rune{{'り'}, {'リ'}, {'リ'}}},
{[][]rune{{'ら'}, {'ラ'}, {'ラ'}}},
{[][]rune{{'よ'}, {'ヨ'}, {'ヨ'}, {'ョ'}, {'ょ'}, {'ョ'}}},
{[][]rune{{'ゆ'}, {'ユ'}, {'ユ'}, {'ュ'}, {'ゅ'}, {'ュ'}}},
{[][]rune{{'や'}, {'ヤ'}, {'ヤ'}, {'ャ'}, {'ゃ'}, {'ャ'}}},
{[][]rune{{'も'}, {'モ'}, {'モ'}}},
{[][]rune{{'め'}, {'メ'}, {'メ'}, {'M', 'E'}}},
{[][]rune{{'む'}, {'ム'}, {'ム'}}},
{[][]rune{{'み'}, {'ミ'}, {'ミ'}}},
{[][]rune{{'ま'}, {'マ'}, {'マ'}}},
{[][]rune{{'ほ'}, {'ホ'}, {'ホ'}}},
{[][]rune{{'へ'}, {'ヘ'}, {'ヘ'}}},
{[][]rune{{'ふ'}, {'フ'}, {'フ'}}},
{[][]rune{{'ひ'}, {'ヒ'}, {'ヒ'}}},
{[][]rune{{'は'}, {'ハ'}, {'ハ'}, {'八'}}},
{[][]rune{{'の'}, {''}, {'ノ'}}},
{[][]rune{{'ね'}, {'ネ'}, {'ネ'}}},
{[][]rune{{'ぬ'}, {'ヌ'}, {'ヌ'}}},
{[][]rune{{'に'}, {'ニ'}, {'ニ'}, {'二'}}},
{[][]rune{{'な'}, {'ナ'}, {'ナ'}}},
{[][]rune{{'と'}, {'ト'}, {'ト'}, {'┣'}, {'├'}}},
{[][]rune{{'て'}, {'テ'}, {'テ'}, {'〒'}, {'乙'}}},
{[][]rune{{'つ'}, {'ツ'}, {'ツ'}, {'っ'}, {'ッ'}, {'ッ'}}},
{[][]rune{{'ち'}, {'チ'}, {'チ'}, {'千'}}},
{[][]rune{{'た'}, {'タ'}, {'タ'}, {'夕'}}},
{[][]rune{{'そ'}, {'ソ'}, {'ソ'}}},
{[][]rune{{'せ'}, {'セ'}, {'セ'}}},
{[][]rune{{'す'}, {'ス'}, {'ス'}}},
{[][]rune{{'し'}, {'シ'}, {'シ'}}},
{[][]rune{{'さ'}, {'サ'}, {'サ'}}},
{[][]rune{{'こ'}, {'コ'}, {'コ'}}},
{[][]rune{{'け'}, {'ケ'}, {'ケ'}, {'ヶ'}}},
{[][]rune{{'く'}, {'ク'}, {'ク'}}},
{[][]rune{{'き'}, {'キ'}, {'キ'}}},
{[][]rune{{'か'}, {'カ'}, {'カ'}, {'ヵ'}, {'力'}}},
{[][]rune{{'お'}, {'オ'}, {'オ'}, {'ォ'}, {'ぉ'}, {'ォ'}}},
{[][]rune{{'え'}, {'エ'}, {'エ'}, {'ェ'}, {'ぇ'}, {'ェ'}, {'工'}}},
{[][]rune{{'う'}, {'ウ'}, {'ウ'}, {'ゥ'}, {'ぅ'}, {'ゥ'}}},
{[][]rune{{'い'}, {'イ'}, {'イ'}, {'ィ'}, {'ぃ'}, {'ィ'}}},
{[][]rune{{'あ'}, {'ア'}, {'ァ'}, {'ア'}, {'ぁ'}, {'ァ'}}},
{[][]rune{{'ー'}, {'―'}, {''}, {'-'}, {''}, {'ー'}, {'一'}}},
{[][]rune{{'9'}, {''}}},
{[][]rune{{'8'}, {''}}},
{[][]rune{{'7'}, {''}}},
{[][]rune{{'6'}, {''}}},
{[][]rune{{'5'}, {''}}},
{[][]rune{{'4'}, {''}}},
{[][]rune{{'3'}, {''}}},
{[][]rune{{'2'}, {''}}},
{[][]rune{{'1'}, {''}}},
{[][]rune{{'ぽ'}, {'ポ'}, {'ホ', '゚'}, {'ほ', '゚'}, {'ホ', '゚'}, {'ホ', '°'}, {'ほ', '°'}, {'ホ', '°'}}},
{[][]rune{{'ぺ'}, {'ペ'}, {'ヘ', '゚'}, {'へ', '゚'}, {'ヘ', '゚'}, {'ヘ', '°'}, {'へ', '°'}, {'ヘ', '°'}}},
{[][]rune{{'ぷ'}, {'プ'}, {'フ', '゚'}, {'ふ', '゚'}, {'フ', '゚'}, {'フ', '°'}, {'ふ', '°'}, {'フ', '°'}}},
{[][]rune{{'ぴ'}, {'ピ'}, {'ヒ', '゚'}, {'ひ', '゚'}, {'ヒ', '゚'}, {'ヒ', '°'}, {'ひ', '°'}, {'ヒ', '°'}}},
{[][]rune{{'ぱ'}, {'パ'}, {'ハ', '゚'}, {'は', '゚'}, {'ハ', '゚'}, {'ハ', '°'}, {'は', '°'}, {'ハ', '°'}, {'八', '゚'}, {'八', '゜'}}},
{[][]rune{{'z'}, {''}, {'Z'}, {''}, {'Ζ'}}},
{[][]rune{{'y'}, {''}, {'Y'}, {''}, {'Υ'}, {'У'}, {'у'}}},
{[][]rune{{'x'}, {''}, {'X'}, {''}, {'Χ'}, {'χ'}, {'Х'}, {'×'}, {'х'}}},
{[][]rune{{'w'}, {''}, {'W'}, {''}, {'ω'}, {'Ш'}, {'ш'}, {'щ'}}},
{[][]rune{{'v'}, {''}, {'V'}, {''}, {'ν'}, {'υ'}}},
{[][]rune{{'u'}, {''}, {'U'}, {''}, {'μ'}, {''}}},
{[][]rune{{'t'}, {''}, {'T'}, {''}, {'Τ'}, {'τ'}, {'Т'}, {'т'}}},
{[][]rune{{'s'}, {''}, {'S'}, {''}, {'∫'}, {''}, {'$'}}},
{[][]rune{{'r'}, {''}, {'R'}, {''}, {'Я'}, {'я'}}},
{[][]rune{{'q'}, {''}, {'Q'}, {''}}},
{[][]rune{{'p'}, {''}, {'P'}, {''}, {'Ρ'}, {'ρ'}, {'Р'}, {'р'}}},
{[][]rune{{'o'}, {''}, {'O'}, {''}, {'○'}, {'Ο'}, {'ο'}, {'О'}, {'о'}, {'◯'}, {''}, {'0'}, {''}}},
{[][]rune{{'n'}, {''}, {'N'}, {''}, {'Ν'}, {'η'}, {'ン'}, {'ん'}, {'ン'}}},
{[][]rune{{'m'}, {''}, {'M'}, {''}, {'Μ'}, {'М'}, {'м'}}},
{[][]rune{{'l'}, {''}, {'L'}, {''}, {'|'}}},
{[][]rune{{'k'}, {''}, {'K'}, {''}, {'Κ'}, {'κ'}, {'К'}, {'к'}}},
{[][]rune{{'j'}, {''}, {'J'}, {''}}},
{[][]rune{{'i'}, {''}, {'I'}, {''}, {'Ι'}}},
{[][]rune{{'h'}, {''}, {'H'}, {''}, {'Η'}, {'Н'}, {'н'}}},
{[][]rune{{'f'}, {''}, {'F'}, {''}}},
{[][]rune{{'g'}, {''}, {'G'}, {''}}},
{[][]rune{{'e'}, {''}, {'E'}, {''}, {'Ε'}, {'ε'}, {'Е'}, {'Ё'}, {'е'}, {'ё'}, {'∈'}}},
{[][]rune{{'d'}, {''}, {'D'}, {''}}},
{[][]rune{{'c'}, {''}, {'C'}, {'С'}, {'с'}, {''}, {'℃'}}},
{[][]rune{{'b'}, {''}, {''}, {'B'}, {'β'}, {'Β'}, {'В'}, {'в'}, {'ъ'}, {'ь'}, {'♭'}}},
{[][]rune{{'\''}, {''}}},
{[][]rune{{'a'}, {''}, {''}, {'A'}, {'α'}, {'@'}, {''}, {'а'}, {'Å'}, {'А'}, {'Α'}}},
{[][]rune{{'"'}, {'”'}}},
{[][]rune{{'%'}, {''}}},
}
for _, smcGroup := range smcData {
for _, smcPair := range smcGroup.charGroup {
smc.WriteUint16(stringsupport.ToNGWord(string(smcPair[0]))[0])
if len(smcPair) > 1 {
smc.WriteUint16(stringsupport.ToNGWord(string(smcPair[1]))[0])
} else {
smc.WriteUint16(0)
}
}
smc.WriteUint32(0)
}
filters.WriteUint32(uint32(len(smc.Data())))
filters.WriteBytes(smc.Data())
filters.WriteNullTerminatedBytes([]byte("nam"))
nam := byteframe.NewByteFrame()
nam.SetLE()
for _, word := range namNGWords {
parts := stringsupport.ToNGWord(word)
nam.WriteUint32(uint32(len(parts)))
for _, part := range parts {
nam.WriteUint16(part)
var i int16
j := int16(-1)
for _, smcGroup := range smcData {
if rune(part) == rune(stringsupport.ToNGWord(string(smcGroup.charGroup[0][0]))[0]) {
j = i
break
}
i += int16(len(smcGroup.charGroup) + 1)
}
nam.WriteInt16(j)
}
nam.WriteUint16(0)
nam.WriteInt16(-1)
}
filters.WriteUint32(uint32(len(nam.Data())))
filters.WriteBytes(nam.Data())
filters.WriteNullTerminatedBytes([]byte("msg"))
msg := byteframe.NewByteFrame()
msg.SetLE()
for _, word := range msgNGWords {
parts := stringsupport.ToNGWord(word)
msg.WriteUint32(uint32(len(parts)))
for _, part := range parts {
msg.WriteUint16(part)
var i int16
j := int16(-1)
for _, smcGroup := range smcData {
if rune(part) == rune(stringsupport.ToNGWord(string(smcGroup.charGroup[0][0]))[0]) {
j = i
break
}
i += int16(len(smcGroup.charGroup) + 1)
}
msg.WriteInt16(j)
}
msg.WriteUint16(0)
msg.WriteInt16(-1)
}
filters.WriteUint32(uint32(len(msg.Data())))
filters.WriteBytes(msg.Data())
bf.WriteUint16(uint16(len(filters.Data())))
bf.WriteBytes(filters.Data())
if s.client == VITA || s.client == PS3 || s.client == PS4 {
ps.Uint16(bf, "", false) // filters
if s.client == VITA || s.client == PS3 {
var psnUser string
s.server.db.QueryRow("SELECT psn_id FROM users WHERE id = $1", uid).Scan(&psnUser)
bf.WriteBytes(stringsupport.PaddedString(psnUser, 20, true))

View File

@@ -11,7 +11,6 @@ import (
"erupe-ce/common/byteframe"
"erupe-ce/network"
"go.uber.org/zap"
)
@@ -21,7 +20,6 @@ const (
PC100 client = iota
VITA
PS3
PS4
WIIU
)
@@ -58,9 +56,6 @@ func (s *Session) handlePacket(pkt []byte) error {
switch reqType[:len(reqType)-3] {
case "DLTSKEYSIGN:", "DSGN:", "SIGN:":
s.handleDSGN(bf)
case "PS4SGN:":
s.client = PS4
s.handlePSSGN(bf)
case "PS3SGN:":
s.client = PS3
s.handlePSSGN(bf)
@@ -132,16 +127,13 @@ func (s *Session) handleWIIUSGN(bf *byteframe.ByteFrame) {
func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) {
// Prevent reading malformed request
if s.client != PS4 {
if len(bf.DataFromCurrent()) < 128 {
s.sendCode(SIGN_EABORT)
return
}
_ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255
_ = bf.ReadBytes(2) // VITA = 1, PS3 = !
_ = bf.ReadBytes(82)
if len(bf.DataFromCurrent()) < 128 {
s.sendCode(SIGN_EABORT)
return
}
_ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255
_ = bf.ReadBytes(2) // VITA = 1, PS3 = !
_ = bf.ReadBytes(82)
s.psn = string(bf.ReadNullTerminatedBytes())
var uid uint32
err := s.server.db.QueryRow(`SELECT id FROM users WHERE psn_id = $1`, s.psn).Scan(&uid)

View File

@@ -1,4 +1,4 @@
package api
package signv2server
import (
"context"
@@ -10,7 +10,7 @@ import (
"golang.org/x/crypto/bcrypt"
)
func (s *APIServer) createNewUser(ctx context.Context, username string, password string) (uint32, uint32, error) {
func (s *Server) createNewUser(ctx context.Context, username string, password string) (uint32, uint32, error) {
// Create salted hash of user password
passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
@@ -32,7 +32,7 @@ func (s *APIServer) createNewUser(ctx context.Context, username string, password
return id, rights, err
}
func (s *APIServer) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) {
func (s *Server) createLoginToken(ctx context.Context, uid uint32) (uint32, string, error) {
loginToken := token.Generate(16)
var tid uint32
err := s.db.QueryRowContext(ctx, "INSERT INTO sign_sessions (user_id, token) VALUES ($1, $2) RETURNING id", uid, loginToken).Scan(&tid)
@@ -42,7 +42,7 @@ func (s *APIServer) createLoginToken(ctx context.Context, uid uint32) (uint32, s
return tid, loginToken, nil
}
func (s *APIServer) userIDFromToken(ctx context.Context, token string) (uint32, error) {
func (s *Server) userIDFromToken(ctx context.Context, token string) (uint32, error) {
var userID uint32
err := s.db.QueryRowContext(ctx, "SELECT user_id FROM sign_sessions WHERE token = $1", token).Scan(&userID)
if err == sql.ErrNoRows {
@@ -53,10 +53,10 @@ func (s *APIServer) userIDFromToken(ctx context.Context, token string) (uint32,
return userID, nil
}
func (s *APIServer) createCharacter(ctx context.Context, userID uint32) (Character, error) {
func (s *Server) createCharacter(ctx context.Context, userID uint32) (Character, error) {
var character Character
err := s.db.GetContext(ctx, &character,
"SELECT id, name, is_female, weapon_type, hr, gr, last_login FROM characters WHERE is_new_character = true AND user_id = $1 LIMIT 1",
"SELECT id, name, is_female, weapon_type, hrp, gr, last_login FROM characters WHERE is_new_character = true AND user_id = $1 LIMIT 1",
userID,
)
if err == sql.ErrNoRows {
@@ -68,17 +68,17 @@ func (s *APIServer) createCharacter(ctx context.Context, userID uint32) (Charact
err = s.db.GetContext(ctx, &character, `
INSERT INTO characters (
user_id, is_female, is_new_character, name, unk_desc_string,
hr, gr, weapon_type, last_login
hrp, gr, weapon_type, last_login
)
VALUES ($1, false, true, '', '', 0, 0, 0, $2)
RETURNING id, name, is_female, weapon_type, hr, gr, last_login`,
RETURNING id, name, is_female, weapon_type, hrp, gr, last_login`,
userID, uint32(time.Now().Unix()),
)
}
return character, err
}
func (s *APIServer) deleteCharacter(ctx context.Context, userID uint32, charID uint32) error {
func (s *Server) deleteCharacter(ctx context.Context, userID uint32, charID uint32) error {
var isNew bool
err := s.db.QueryRow("SELECT is_new_character FROM characters WHERE id = $1", charID).Scan(&isNew)
if err != nil {
@@ -92,11 +92,11 @@ func (s *APIServer) deleteCharacter(ctx context.Context, userID uint32, charID u
return err
}
func (s *APIServer) getCharactersForUser(ctx context.Context, uid uint32) ([]Character, error) {
func (s *Server) getCharactersForUser(ctx context.Context, uid uint32) ([]Character, error) {
var characters []Character
err := s.db.SelectContext(
ctx, &characters, `
SELECT id, name, is_female, weapon_type, hr, gr, last_login
SELECT id, name, is_female, weapon_type, hrp, gr, last_login
FROM characters
WHERE user_id = $1 AND deleted = false AND is_new_character = false ORDER BY id ASC`,
uid,
@@ -107,7 +107,7 @@ func (s *APIServer) getCharactersForUser(ctx context.Context, uid uint32) ([]Cha
return characters, nil
}
func (s *APIServer) getReturnExpiry(uid uint32) time.Time {
func (s *Server) getReturnExpiry(uid uint32) time.Time {
var returnExpiry, lastLogin time.Time
s.db.Get(&lastLogin, "SELECT COALESCE(last_login, now()) FROM users WHERE id=$1", uid)
if time.Now().Add((time.Hour * 24) * -90).After(lastLogin) {
@@ -124,7 +124,7 @@ func (s *APIServer) getReturnExpiry(uid uint32) time.Time {
return returnExpiry
}
func (s *APIServer) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) {
func (s *Server) exportSave(ctx context.Context, uid uint32, cid uint32) (map[string]interface{}, error) {
row := s.db.QueryRowxContext(ctx, "SELECT * FROM characters WHERE id=$1 AND user_id=$2", cid, uid)
result := make(map[string]interface{})
err := row.MapScan(result)

View File

@@ -1,24 +1,15 @@
package api
package signv2server
import (
"database/sql"
"encoding/json"
"encoding/xml"
"errors"
_config "erupe-ce/config"
"erupe-ce/server/channelserver"
"fmt"
"image"
"image/jpeg"
"io"
"net/http"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/lib/pq"
"go.uber.org/zap"
"golang.org/x/crypto/bcrypt"
@@ -30,9 +21,9 @@ const (
)
type LauncherResponse struct {
Banners []_config.APISignBanner `json:"banners"`
Messages []_config.APISignMessage `json:"messages"`
Links []_config.APISignLink `json:"links"`
Banners []_config.SignV2Banner `json:"banners"`
Messages []_config.SignV2Message `json:"messages"`
Links []_config.SignV2Link `json:"links"`
}
type User struct {
@@ -46,7 +37,7 @@ type Character struct {
Name string `json:"name"`
IsFemale bool `json:"isFemale" db:"is_female"`
Weapon uint32 `json:"weapon" db:"weapon_type"`
HR uint32 `json:"hr" db:"hr"`
HR uint32 `json:"hr" db:"hrp"`
GR uint32 `json:"gr"`
LastLogin int32 `json:"lastLogin" db:"last_login"`
}
@@ -75,7 +66,7 @@ type ExportData struct {
Character map[string]interface{} `json:"character"`
}
func (s *APIServer) newAuthData(userID uint32, userRights uint32, userTokenID uint32, userToken string, characters []Character) AuthData {
func (s *Server) newAuthData(userID uint32, userRights uint32, userTokenID uint32, userToken string, characters []Character) AuthData {
resp := AuthData{
CurrentTS: uint32(channelserver.TimeAdjusted().Unix()),
ExpiryTS: uint32(s.getReturnExpiry(userID).Unix()),
@@ -86,7 +77,7 @@ func (s *APIServer) newAuthData(userID uint32, userRights uint32, userTokenID ui
Token: userToken,
},
Characters: characters,
PatchServer: s.erupeConfig.API.PatchServer,
PatchServer: s.erupeConfig.SignV2.PatchServer,
Notices: []string{},
}
if s.erupeConfig.DebugOptions.MaxLauncherHR {
@@ -112,16 +103,16 @@ func (s *APIServer) newAuthData(userID uint32, userRights uint32, userTokenID ui
return resp
}
func (s *APIServer) Launcher(w http.ResponseWriter, r *http.Request) {
func (s *Server) Launcher(w http.ResponseWriter, r *http.Request) {
var respData LauncherResponse
respData.Banners = s.erupeConfig.API.Banners
respData.Messages = s.erupeConfig.API.Messages
respData.Links = s.erupeConfig.API.Links
respData.Banners = s.erupeConfig.SignV2.Banners
respData.Messages = s.erupeConfig.SignV2.Messages
respData.Links = s.erupeConfig.SignV2.Links
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(respData)
}
func (s *APIServer) Login(w http.ResponseWriter, r *http.Request) {
func (s *Server) Login(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var reqData struct {
Username string `json:"username"`
@@ -173,7 +164,7 @@ func (s *APIServer) Login(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(respData)
}
func (s *APIServer) Register(w http.ResponseWriter, r *http.Request) {
func (s *Server) Register(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var reqData struct {
Username string `json:"username"`
@@ -213,7 +204,7 @@ func (s *APIServer) Register(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(respData)
}
func (s *APIServer) CreateCharacter(w http.ResponseWriter, r *http.Request) {
func (s *Server) CreateCharacter(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var reqData struct {
Token string `json:"token"`
@@ -242,7 +233,7 @@ func (s *APIServer) CreateCharacter(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(character)
}
func (s *APIServer) DeleteCharacter(w http.ResponseWriter, r *http.Request) {
func (s *Server) DeleteCharacter(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var reqData struct {
Token string `json:"token"`
@@ -267,7 +258,7 @@ func (s *APIServer) DeleteCharacter(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(struct{}{})
}
func (s *APIServer) ExportSave(w http.ResponseWriter, r *http.Request) {
func (s *Server) ExportSave(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
var reqData struct {
Token string `json:"token"`
@@ -295,118 +286,3 @@ func (s *APIServer) ExportSave(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Content-Type", "application/json")
json.NewEncoder(w).Encode(save)
}
func (s *APIServer) ScreenShotGet(w http.ResponseWriter, r *http.Request) {
// Get the 'id' parameter from the URL
token := mux.Vars(r)["id"]
var tokenPattern = regexp.MustCompile(`[A-Za-z0-9]+`)
if !tokenPattern.MatchString(token) || token == "" {
http.Error(w, "Not Valid Token", http.StatusBadRequest)
}
// Open the image file
safePath := s.erupeConfig.Screenshots.OutputDir
path := filepath.Join(safePath, fmt.Sprintf("%s.jpg", token))
result, err := verifyPath(path, safePath)
if err != nil {
fmt.Println("Error " + err.Error())
} else {
fmt.Println("Canonical: " + result)
file, err := os.Open(result)
if err != nil {
http.Error(w, "Image not found", http.StatusNotFound)
return
}
defer file.Close()
// Set content type header to image/jpeg
w.Header().Set("Content-Type", "image/jpeg")
// Copy the image content to the response writer
if _, err := io.Copy(w, file); err != nil {
http.Error(w, "Unable to send image", http.StatusInternalServerError)
return
}
}
}
func (s *APIServer) ScreenShot(w http.ResponseWriter, r *http.Request) {
// Create a struct representing the XML result
type Result struct {
XMLName xml.Name `xml:"result"`
Code string `xml:"code"`
}
// Set the Content-Type header to specify that the response is in XML format
w.Header().Set("Content-Type", "text/xml")
result := Result{Code: "200"}
if !s.erupeConfig.Screenshots.Enabled {
result = Result{Code: "400"}
} else {
if r.Method != http.MethodPost {
result = Result{Code: "405"}
}
// Get File from Request
file, _, err := r.FormFile("img")
if err != nil {
result = Result{Code: "400"}
}
var tokenPattern = regexp.MustCompile(`[A-Za-z0-9]+`)
token := r.FormValue("token")
if !tokenPattern.MatchString(token) || token == "" {
result = Result{Code: "401"}
}
// Validate file
img, _, err := image.Decode(file)
if err != nil {
result = Result{Code: "400"}
}
safePath := s.erupeConfig.Screenshots.OutputDir
path := filepath.Join(safePath, fmt.Sprintf("%s.jpg", token))
verified, err := verifyPath(path, safePath)
if err != nil {
result = Result{Code: "500"}
} else {
_, err = os.Stat(safePath)
if err != nil {
if os.IsNotExist(err) {
err = os.MkdirAll(safePath, os.ModePerm)
if err != nil {
s.logger.Error("Error writing screenshot, could not create folder")
result = Result{Code: "500"}
}
} else {
s.logger.Error("Error writing screenshot")
result = Result{Code: "500"}
}
}
// Create or open the output file
outputFile, err := os.Create(verified)
if err != nil {
result = Result{Code: "500"}
}
defer outputFile.Close()
// Encode the image and write it to the file
err = jpeg.Encode(outputFile, img, &jpeg.Options{Quality: s.erupeConfig.Screenshots.UploadQuality})
if err != nil {
s.logger.Error("Error writing screenshot, could not write file", zap.Error(err))
result = Result{Code: "500"}
}
}
}
// Marshal the struct into XML
xmlData, err := xml.Marshal(result)
if err != nil {
http.Error(w, "Unable to marshal XML", http.StatusInternalServerError)
return
}
// Write the XML response with a 200 status code
w.WriteHeader(http.StatusOK)
w.Write(xmlData)
}

View File

@@ -1,8 +1,8 @@
package api
package signv2server
import (
"context"
_config "erupe-ce/config"
"erupe-ce/config"
"fmt"
"net/http"
"os"
@@ -21,8 +21,8 @@ type Config struct {
ErupeConfig *_config.Config
}
// APIServer is Erupes Standard API interface
type APIServer struct {
// Server is the MHF custom launcher sign server.
type Server struct {
sync.Mutex
logger *zap.Logger
erupeConfig *_config.Config
@@ -31,9 +31,9 @@ type APIServer struct {
isShuttingDown bool
}
// NewAPIServer creates a new Server type.
func NewAPIServer(config *Config) *APIServer {
s := &APIServer{
// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
s := &Server{
logger: config.Logger,
erupeConfig: config.ErupeConfig,
db: config.DB,
@@ -43,7 +43,7 @@ func NewAPIServer(config *Config) *APIServer {
}
// Start starts the server in a new goroutine.
func (s *APIServer) Start() error {
func (s *Server) Start() error {
// Set up the routes responsible for serving the launcher HTML, serverlist, unique name check, and JP auth.
r := mux.NewRouter()
r.HandleFunc("/launcher", s.Launcher)
@@ -52,11 +52,9 @@ func (s *APIServer) Start() error {
r.HandleFunc("/character/create", s.CreateCharacter)
r.HandleFunc("/character/delete", s.DeleteCharacter)
r.HandleFunc("/character/export", s.ExportSave)
r.HandleFunc("/api/ss/bbs/upload.php", s.ScreenShot)
r.HandleFunc("/api/ss/bbs/{id}", s.ScreenShotGet)
handler := handlers.CORS(handlers.AllowedHeaders([]string{"Content-Type"}))(r)
s.httpServer.Handler = handlers.LoggingHandler(os.Stdout, handler)
s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.API.Port)
s.httpServer.Addr = fmt.Sprintf(":%d", s.erupeConfig.SignV2.Port)
serveError := make(chan error, 1)
go func() {
@@ -76,7 +74,7 @@ func (s *APIServer) Start() error {
}
// Shutdown exits the server gracefully.
func (s *APIServer) Shutdown() {
func (s *Server) Shutdown() {
s.logger.Debug("Shutting down")
s.Lock()