Merge remote-tracking branch 'origin/main' into feature/psn-link

# Conflicts:
#	server/signserver/dbutils.go
#	server/signserver/dsgn_resp.go
#	server/signserver/session.go
This commit is contained in:
wish
2023-07-24 23:36:38 +10:00
78 changed files with 4417 additions and 997 deletions

134
README.md
View File

@@ -1,4 +1,15 @@
# Erupe Community Edition # Erupe
## Client Compatiblity
### Platforms
- PC
- PlayStation 3
- PlayStation Vita
- Wii U (Up to Z2)
### Versions
- ZZ
- Z2
- Z1
## Setup ## Setup
@@ -6,24 +17,131 @@ If you are only looking to install Erupe, please use [a pre-compiled binary](htt
If you want to modify or compile Erupe yourself, please read on. If you want to modify or compile Erupe yourself, please read on.
### Requirements ## Requirements
- [Go](https://go.dev/dl/) - [Go](https://go.dev/dl/)
- [PostgreSQL](https://www.postgresql.org/download/) - [PostgreSQL](https://www.postgresql.org/download/)
### Installation ## Installation
1. Bring up a fresh database by using the [backup file attached with the latest release](https://github.com/ZeruLight/Erupe/releases/latest/download/SCHEMA.sql). 1. Bring up a fresh database by using the [backup file attached with the latest release](https://github.com/ZeruLight/Erupe/releases/latest/download/SCHEMA.sql).
2. Run each script under [patch-schema](./patch-schema) as they introduce newer schema. 2. Run each script under [patch-schema](./patch-schema) as they introduce newer schema.
3. Edit [config.json](./config.json) such that the database password matches your PostgreSQL setup. 3. Edit [config.json](./config.json) such that the database password matches your PostgreSQL setup.
4. Run `go build` or `go run .` to compile Erupe. 4. Run `go build` or `go run .` to compile Erupe.
### Note
- You will need to acquire and install the client files and quest binaries separately.
## Resources ## Resources
- [Quest and Scenario Binary Files](https://files.catbox.moe/xf0l7w.7z) - [Quest and Scenario Binary Files](https://files.catbox.moe/xf0l7w.7z)
- [PewPewDojo Discord](https://discord.gg/CFnzbhQ) - [PewPewDojo Discord](https://discord.gg/CFnzbhQ)
- [Community FAQ Pastebin](https://pastebin.com/QqAwZSTC)
## Configuration
This portion of the documentation goes over the `config.json` file.
### General Configuration
| Variable | Description | Default | Options |
|------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|---------------------------------|
| Host | The IP or host address the server is running from | 127.0.0.1 | |
| BinPath | The bin path folder is where you place files needed for various parts of the game such as scenario and quest files | bin | |
| Language | This is the language the server will run in. Only English `en` and Japanese `ja` are available, if you wish to contribute to tranlation, get in touch | en | en/jp |
| DisableSoftCrash | | false | |
| HideLoginNotice | This hides the notices that appear on login from `LoginNotices` | true | |
| LoginNotices | This is where you place notices for users, you can have multiple notices | | |
| PatchServerManifest | | | |
| PatchServerFile | | | |
| ScreenshotAPIURL | This is the URL you want user sreenshots to go to | | |
| DeleteOnSaveCorruption | This option deletes a users save from the database if they corrupt it, can be used as punishment for cheaters | false | |
| ClientMode | This tells the server what client version it should target | ZZ | Check compatible versions above |
| DevMode | This enables DevModeOptions to be configured | true | |
### `DevModeOptions` Configuraiton
| Variable | Description | Default | Options |
|----------------------|---------------------------------------------------------------------------------------------|----------|----------------------------------|
| AutoCreateAccount | This allows users that don't exist to auto create there account from initial login | true | |
| CleanDB | This cleans the database down | false | |
| MaxLauncherHR | This sets the launcher value to HR7 to allow you to break World HR requirements | false | |
| LogInboundMessages | This will allow inbound messages to be logged to stdout | false | |
| LogOutboundMessages | This will allow outbound messages to be logged to stdout | false | |
| MaxHexdumpLength | This is the maximum amount of hex bytes that will be dumped to stdout | 0 | |
| DivaEvent | This overrides the Diva event stage in game | 2 | 0/1/2/3/-1 |
| FestaEvent | This overrides the Hunter Festival event stage in game | 2 | 0/1/2/3/-1 |
| TournamentEvent | This overrides the Hunter Tournament event stage in game | 2 | 0/1/2/3/-1 |
| MezFesEvent | Enables whether the MezFes event & World are active | true | |
| MezFesAlt | Switches the multiplayer MezFes event | false | |
| DisableTokenCheck | This disables the random token that is generated at login from being checked, very insecure | false | |
| QuestDebugTools | Enable various quest debug logs | false | |
| EarthStatusOverride | Enables Pallone Fest, Tower and Conquest War events | 0 | 2=Conquest, 11=Pallone, 21=Tower |
| EarthIDOverride | A random event ID | 0 | |
| EarthMonsterOverride | Sets the ID of the monster targeted in the Conquest War | 0 | |
| SaveDumps.Enables | Enables save dumps to a folder that is set at `SaveDumps.OutputDir` | true | |
| SaveDumps.OutputDir | The folder that save dumps are saved to | savedata | |
### `GameplayOptions` Configuraiton
| Variable | Description | Default | Options |
|----------------------|-----------------------------------------------------------------------------|---------|---------|
| FeaturedWeapons | Number of Active Feature weapons to generate daily | 0 | |
| MaximumNP | Maximum number of NP held by a player | 100000 | |
| MaximumRP | Maximum number of RP held by a player | 100000 | |
| DisableLoginBoost | Disables the Login Boost system | false | |
| DisableBoostTime | Disables the daily NetCafe Boost Time | false | |
| BoostTimeDuration | The number of minutes NetCafe Boost Time lasts for | 120 | |
| GuildMealDuration | The number of minutes a Guild Meal can be activated for after cooking | 60 | |
| BonusQuestAllowance | Number of Bonus Point Quests to allow daily | 3 | |
| DailyQuestAllowance | Number of Daily Quests to allow daily | 1 | |
| MezfesSoloTickets | Number of solo tickets given weekly | 10 | |
| MezfesGroupTickets | Number of group tickets given weekly | 4 | |
| GUrgentRate | Adjusts the rate of G Urgent quests spawning | 10 | |
| GCPMultiplier | Adjusts the multiplier of GCP rewarded for quest completion | 1.00 | |
| GRPMultiplier | Adjusts the multiplier of G Rank Points rewarded for quest completion | 1.00 | |
| GSRPMultiplier | Adjusts the multiplier of G Skill Rank Points rewarded for quest completion | 1.00 | |
| GZennyMultiplier | Adjusts the multiplier of G Zenny rewarded for quest completion | 1.00 | |
| MaterialMultiplier | Adjusts the multiplier of Monster Materials rewarded for quest completion | 1.00 | |
| ExtraCarves | Grant n extra chances to carve ALL carcasses | 0 | |
| DisableHunterNavi | Disables the Hunter Navi | false | |
| EnableHiganjimaEvent | Enables the Higanjima event in the Rasta Bar | false | |
| EnableNierEvent | Enables the Nier event in the Rasta Bar | false | |
| DisableRoad | Disables the Hunting Road | false | |
### Discord
There is limited Discord capability in Erupe. The feature allows you to replay messages from your server into a channel.
This may be either be removed or revamped in a future version.
### Commands
There are several chat commands that can be turned on and off. Most of them are really for admins or debugging purposes.
| Name | command | Description | Options |
|----------|----------------|--------------------------------------------|---------------------|
| Rights | !rights VALUE | Sets the rights integer for your account | |
| Teleport | !tele X,Y | Teleports user to specific x,y coordinate | |
| Reload | !reload | Reloads all users and character objects | |
| KeyQuest | !kqf FLAGS | Sets the Key Quest Flag for your character | |
| Course | !course OPTION | Enables/Disables a course for your account | HL,EX,Premium,Boost |
| PSN | !psn USERNAME | Links the specified PSN to your account | |
### Ravi Sub Commands
| Name | command | Description |
|----------|----------------------------------|-------------------------------|
| Raviente | !ravi start | Starts Ravi Event |
| Raviente | !ravi cm / !ravi checkmultiplier | Checks Ravi Damage Multiplier |
| Raviente | !ravi ss | Send Sedation Support |
| Raviente | !ravi sr | Send Resurrection Support |
| Raviente | !ravi rs | Request Sedation Support |
## World `Entries` config
| Config Item | Description | Options |
|-------------|------------------|------------------------------------------------------------|
| Type | Server type. | 1=Normal, 2=Cities, 3=Newbie, 4=Tavern, 5=Return, 6=MezFes |
| Season | Server activity. | 0=Green/Breeding, 1=Orange/Warm, 2=Blue/Cold |
### `Recommend`
This sets the types of quest that can be ordered from a world.
* 0 = All quests
* 1 = Up to 2 star quests
* 2 = Up to 4 star quests
* 4 = All Quests in HR (Enables G Experience Tab)
* 5 = Only G rank quests
* 6 = Mini games world there is no place to order quests

View File

@@ -66,7 +66,7 @@ func CourseExists(ID uint16, c []Course) bool {
// GetCourseStruct returns a slice of Course(s) from a rights integer // GetCourseStruct returns a slice of Course(s) from a rights integer
func GetCourseStruct(rights uint32) ([]Course, uint32) { func GetCourseStruct(rights uint32) ([]Course, uint32) {
resp := []Course{{ID: 1}, {ID: 24}} resp := []Course{{ID: 1}, {ID: 23}, {ID: 24}}
s := Courses() s := Courses()
slices.SortStableFunc(s, func(i, j Course) bool { slices.SortStableFunc(s, func(i, j Course) bool {
return i.ID > j.ID return i.ID > j.ID

View File

@@ -2,6 +2,7 @@ package stringsupport
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"strconv" "strconv"
"strings" "strings"
@@ -96,3 +97,23 @@ func CSVElems(csv string) []int {
} }
return r return r
} }
func CSVGetIndex(csv string, i int) int {
s := CSVElems(csv)
if i < len(s) {
return s[i]
}
return 0
}
func CSVSetIndex(csv string, i int, v int) string {
s := CSVElems(csv)
if i < len(s) {
s[i] = v
}
var r []string
for j := 0; j < len(s); j++ {
r = append(r, fmt.Sprintf(`%d`, s[j]))
}
return strings.Join(r, ",")
}

View File

@@ -11,6 +11,7 @@
"PatchServerFile": "", "PatchServerFile": "",
"ScreenshotAPIURL": "", "ScreenshotAPIURL": "",
"DeleteOnSaveCorruption": false, "DeleteOnSaveCorruption": false,
"ClientMode": "ZZ",
"DevMode": true, "DevMode": true,
"DevModeOptions": { "DevModeOptions": {
"AutoCreateAccount": true, "AutoCreateAccount": true,
@@ -26,21 +27,39 @@
"MezFesAlt": false, "MezFesAlt": false,
"DisableTokenCheck": false, "DisableTokenCheck": false,
"QuestDebugTools": false, "QuestDebugTools": false,
"EarthStatusOverride": 0,
"EarthIDOverride": 0,
"EarthMonsterOverride": 0,
"SaveDumps": { "SaveDumps": {
"Enabled": true, "Enabled": true,
"OutputDir": "savedata" "OutputDir": "save-backups"
} }
}, },
"GameplayOptions": { "GameplayOptions": {
"FeaturedWeapons": 1, "FeaturedWeapons": 1,
"MaximumNP": 100000, "MaximumNP": 100000,
"MaximumRP": 50000, "MaximumRP": 50000,
"MaximumFP": 120000,
"DisableLoginBoost": false, "DisableLoginBoost": false,
"DisableBoostTime": false, "DisableBoostTime": false,
"BoostTimeDuration": 120, "BoostTimeDuration": 120,
"GuildMealDuration": 60, "GuildMealDuration": 60,
"BonusQuestAllowance": 3, "BonusQuestAllowance": 3,
"DailyQuestAllowance": 1 "DailyQuestAllowance": 1,
"MezfesSoloTickets": 10,
"MezfesGroupTickets": 4,
"GUrgentRate": 10,
"GCPMultiplier": 1.00,
"GRPMultiplier": 1.00,
"GSRPMultiplier": 1.00,
"GZennyMultiplier": 1.00,
"MaterialMultiplier": 1.00,
"ExtraCarves": 0,
"DisableHunterNavi": false,
"EnableKaijiEvent": false,
"EnableHiganjimaEvent": false,
"EnableNierEvent": false,
"DisableRoad": false
}, },
"Discord": { "Discord": {
"Enabled": false, "Enabled": false,

View File

@@ -1,15 +1,70 @@
package config package _config
import ( import (
"fmt" "fmt"
"log" "log"
"net" "net"
"os" "os"
"strings"
"time" "time"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
type Mode int
const (
S1 Mode = iota + 1
S15
S2
S25
S3
S35
S4
S5
S55
S6
S7
S8
S85
S9
S10
F1
F2
F3
F4
F5
G1
G2
G3
G31
G32
GG
G5
G51
G52
G6
G61
G7
G8
G81
G9
G91
G10
G101
Z1
Z2
ZZ
)
var versionStrings = []string{"S1.0", "S1.5", "S2.0", "S2.5", "S3.0", "S3.5", "S4.0", "S5.0", "S5.5", "S6.0", "S7.0",
"S8.0", "S8.5", "S9", "S10", "FW.1", "FW.2", "FW.3", "FW.4", "FW.5", "G1", "G2", "G3", "G3.1", "G3.2", "GG", "G5",
"G5.1", "G5.2", "G6", "G6.1", "G7", "G8", "G8.1", "G9", "G9.1", "G10", "G10.1", "Z1", "Z2", "ZZ"}
func (m Mode) String() string {
return versionStrings[m]
}
// Config holds the global server-wide config. // Config holds the global server-wide config.
type Config struct { type Config struct {
Host string `mapstructure:"Host"` Host string `mapstructure:"Host"`
@@ -22,6 +77,8 @@ type Config struct {
PatchServerFile string // File patch server override PatchServerFile string // File patch server override
ScreenshotAPIURL string // Destination for screenshots uploaded to BBS ScreenshotAPIURL string // Destination for screenshots uploaded to BBS
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
ClientMode string
RealClientMode Mode
DevMode bool DevMode bool
DevModeOptions DevModeOptions DevModeOptions DevModeOptions
@@ -51,6 +108,9 @@ type DevModeOptions struct {
MezFesAlt bool // Swaps out Volpakkun for Tokotoko MezFesAlt bool // Swaps out Volpakkun for Tokotoko
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
QuestDebugTools bool // Enable various quest debug logs QuestDebugTools bool // Enable various quest debug logs
EarthStatusOverride int32
EarthIDOverride int32
EarthMonsterOverride int32
SaveDumps SaveDumpOptions SaveDumps SaveDumpOptions
} }
@@ -64,12 +124,27 @@ type GameplayOptions struct {
FeaturedWeapons int // 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 MaximumNP int // Maximum number of NP held by a player
MaximumRP uint16 // Maximum number of RP held by a player MaximumRP uint16 // Maximum number of RP held by a player
MaximumFP uint32 // Maximum number of FP held by a player
DisableLoginBoost bool // Disables the Login Boost system DisableLoginBoost bool // Disables the Login Boost system
DisableBoostTime bool // Disables the daily NetCafe Boost Time DisableBoostTime bool // Disables the daily NetCafe Boost Time
BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for BoostTimeDuration int // The number of minutes NetCafe Boost Time lasts for
GuildMealDuration int // The number of minutes a Guild Meal can be activated for after cooking GuildMealDuration int // The number of minutes a Guild Meal can be activated for after cooking
BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily
DailyQuestAllowance uint32 // Number of Daily Quests to allow daily DailyQuestAllowance uint32 // Number of Daily Quests to allow daily
MezfesSoloTickets uint32 // Number of solo tickets given weekly
MezfesGroupTickets uint32 // Number of group tickets given weekly
GUrgentRate uint16 // Adjusts the rate of G Urgent quests spawning
GCPMultiplier float32 // Adjusts the multiplier of GCP rewarded for quest completion
GRPMultiplier float32 // Adjusts the multiplier of G Rank Points rewarded for quest completion
GSRPMultiplier float32 // Adjusts the multiplier of G Skill Rank Points rewarded for quest completion
GZennyMultiplier float32 // Adjusts the multiplier of G Zenny rewarded for quest completion
MaterialMultiplier float32 // Adjusts the multiplier of Monster Materials rewarded for quest completion
ExtraCarves uint16 // Grant n extra chances to carve ALL carcasses
DisableHunterNavi bool // Disables the Hunter Navi
EnableKaijiEvent bool // Enables the Kaiji event in the Rasta Bar
EnableHiganjimaEvent bool // Enables the Higanjima event in the Rasta Bar
EnableNierEvent bool // Enables the Nier event in the Rasta Bar
DisableRoad bool // Disables the Hunting Road
} }
// Discord holds the discord integration config. // Discord holds the discord integration config.
@@ -154,7 +229,6 @@ func init() {
if err != nil { if err != nil {
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error())) preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
} }
} }
// getOutboundIP4 gets the preferred outbound ip4 of this machine // getOutboundIP4 gets the preferred outbound ip4 of this machine
@@ -196,6 +270,20 @@ func LoadConfig() (*Config, error) {
c.Host = getOutboundIP4().To4().String() c.Host = getOutboundIP4().To4().String()
} }
for i := range versionStrings {
if strings.ToUpper(c.ClientMode) == versionStrings[i] {
c.RealClientMode = Mode(i + 1)
c.ClientMode = strings.ToUpper(c.ClientMode)
if c.RealClientMode < Z1 {
c.ClientMode += " (Debug only)"
}
}
}
if c.RealClientMode == 0 {
c.ClientMode = versionStrings[len(versionStrings)-1]
c.RealClientMode = ZZ
}
return c, nil return c, nil
} }

64
main.go
View File

@@ -1,6 +1,7 @@
package main package main
import ( import (
_config "erupe-ce/config"
"fmt" "fmt"
"net" "net"
"os" "os"
@@ -9,7 +10,6 @@ import (
"syscall" "syscall"
"time" "time"
"erupe-ce/config"
"erupe-ce/server/channelserver" "erupe-ce/server/channelserver"
"erupe-ce/server/discordbot" "erupe-ce/server/discordbot"
"erupe-ce/server/entranceserver" "erupe-ce/server/entranceserver"
@@ -45,7 +45,8 @@ func main() {
var err error var err error
var zapLogger *zap.Logger var zapLogger *zap.Logger
if config.ErupeConfig.DevMode { config := _config.ErupeConfig
if config.DevMode {
zapLogger, _ = zap.NewDevelopment() zapLogger, _ = zap.NewDevelopment()
} else { } else {
zapLogger, _ = zap.NewProduction() zapLogger, _ = zap.NewProduction()
@@ -55,20 +56,21 @@ func main() {
logger := zapLogger.Named("main") logger := zapLogger.Named("main")
logger.Info(fmt.Sprintf("Starting Erupe (9.3b-%s)", Commit())) logger.Info(fmt.Sprintf("Starting Erupe (9.3b-%s)", Commit()))
logger.Info(fmt.Sprintf("Client Mode: %s (%d)", config.ClientMode, config.RealClientMode))
if config.ErupeConfig.Database.Password == "" { if config.Database.Password == "" {
preventClose("Database password is blank") preventClose("Database password is blank")
} }
if net.ParseIP(config.ErupeConfig.Host) == nil { if net.ParseIP(config.Host) == nil {
ips, _ := net.LookupIP(config.ErupeConfig.Host) ips, _ := net.LookupIP(config.Host)
for _, ip := range ips { for _, ip := range ips {
if ip != nil { if ip != nil {
config.ErupeConfig.Host = ip.String() config.Host = ip.String()
break break
} }
} }
if net.ParseIP(config.ErupeConfig.Host) == nil { if net.ParseIP(config.Host) == nil {
preventClose("Invalid host address") preventClose("Invalid host address")
} }
} }
@@ -76,10 +78,10 @@ func main() {
// Discord bot // Discord bot
var discordBot *discordbot.DiscordBot = nil var discordBot *discordbot.DiscordBot = nil
if config.ErupeConfig.Discord.Enabled { if config.Discord.Enabled {
bot, err := discordbot.NewDiscordBot(discordbot.Options{ bot, err := discordbot.NewDiscordBot(discordbot.Options{
Logger: logger, Logger: logger,
Config: config.ErupeConfig, Config: _config.ErupeConfig,
}) })
if err != nil { if err != nil {
@@ -102,11 +104,11 @@ func main() {
// Create the postgres DB pool. // Create the postgres DB pool.
connectString := fmt.Sprintf( connectString := fmt.Sprintf(
"host='%s' port='%d' user='%s' password='%s' dbname='%s' sslmode=disable", "host='%s' port='%d' user='%s' password='%s' dbname='%s' sslmode=disable",
config.ErupeConfig.Database.Host, config.Database.Host,
config.ErupeConfig.Database.Port, config.Database.Port,
config.ErupeConfig.Database.User, config.Database.User,
config.ErupeConfig.Database.Password, config.Database.Password,
config.ErupeConfig.Database.Database, config.Database.Database,
) )
db, err := sqlx.Open("postgres", connectString) db, err := sqlx.Open("postgres", connectString)
@@ -126,7 +128,7 @@ func main() {
_ = db.MustExec("DELETE FROM servers") _ = db.MustExec("DELETE FROM servers")
// Clean the DB if the option is on. // Clean the DB if the option is on.
if config.ErupeConfig.DevMode && config.ErupeConfig.DevModeOptions.CleanDB { if config.DevMode && config.DevModeOptions.CleanDB {
logger.Info("Database: Started clearing...") logger.Info("Database: Started clearing...")
cleanDB(db) cleanDB(db)
logger.Info("Database: Finished clearing") logger.Info("Database: Finished clearing")
@@ -139,11 +141,11 @@ func main() {
// Entrance server. // Entrance server.
var entranceServer *entranceserver.Server var entranceServer *entranceserver.Server
if config.ErupeConfig.Entrance.Enabled { if config.Entrance.Enabled {
entranceServer = entranceserver.NewServer( entranceServer = entranceserver.NewServer(
&entranceserver.Config{ &entranceserver.Config{
Logger: logger.Named("entrance"), Logger: logger.Named("entrance"),
ErupeConfig: config.ErupeConfig, ErupeConfig: _config.ErupeConfig,
DB: db, DB: db,
}) })
err = entranceServer.Start() err = entranceServer.Start()
@@ -158,11 +160,11 @@ func main() {
// Sign server. // Sign server.
var signServer *signserver.Server var signServer *signserver.Server
if config.ErupeConfig.Sign.Enabled { if config.Sign.Enabled {
signServer = signserver.NewServer( signServer = signserver.NewServer(
&signserver.Config{ &signserver.Config{
Logger: logger.Named("sign"), Logger: logger.Named("sign"),
ErupeConfig: config.ErupeConfig, ErupeConfig: _config.ErupeConfig,
DB: db, DB: db,
}) })
err = signServer.Start() err = signServer.Start()
@@ -176,11 +178,11 @@ func main() {
// New Sign server // New Sign server
var newSignServer *signv2server.Server var newSignServer *signv2server.Server
if config.ErupeConfig.SignV2.Enabled { if config.SignV2.Enabled {
newSignServer = signv2server.NewServer( newSignServer = signv2server.NewServer(
&signv2server.Config{ &signv2server.Config{
Logger: logger.Named("sign"), Logger: logger.Named("sign"),
ErupeConfig: config.ErupeConfig, ErupeConfig: _config.ErupeConfig,
DB: db, DB: db,
}) })
err = newSignServer.Start() err = newSignServer.Start()
@@ -194,23 +196,23 @@ func main() {
var channels []*channelserver.Server var channels []*channelserver.Server
if config.ErupeConfig.Channel.Enabled { if config.Channel.Enabled {
channelQuery := "" channelQuery := ""
si := 0 si := 0
ci := 0 ci := 0
count := 1 count := 1
for j, ee := range config.ErupeConfig.Entrance.Entries { for j, ee := range config.Entrance.Entries {
for i, ce := range ee.Channels { for i, ce := range ee.Channels {
sid := (4096 + si*256) + (16 + ci) sid := (4096 + si*256) + (16 + ci)
c := *channelserver.NewServer(&channelserver.Config{ c := *channelserver.NewServer(&channelserver.Config{
ID: uint16(sid), ID: uint16(sid),
Logger: logger.Named("channel-" + fmt.Sprint(count)), Logger: logger.Named("channel-" + fmt.Sprint(count)),
ErupeConfig: config.ErupeConfig, ErupeConfig: _config.ErupeConfig,
DB: db, DB: db,
DiscordBot: discordBot, DiscordBot: discordBot,
}) })
if ee.IP == "" { if ee.IP == "" {
c.IP = config.ErupeConfig.Host c.IP = config.Host
} else { } else {
c.IP = ee.IP c.IP = ee.IP
} }
@@ -246,7 +248,7 @@ func main() {
signal.Notify(c, os.Interrupt, syscall.SIGTERM) signal.Notify(c, os.Interrupt, syscall.SIGTERM)
<-c <-c
if !config.ErupeConfig.DisableSoftCrash { if !config.DisableSoftCrash {
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
message := fmt.Sprintf("Shutting down in %d...", 10-i) message := fmt.Sprintf("Shutting down in %d...", 10-i)
for _, c := range channels { for _, c := range channels {
@@ -257,21 +259,21 @@ func main() {
} }
} }
if config.ErupeConfig.Channel.Enabled { if config.Channel.Enabled {
for _, c := range channels { for _, c := range channels {
c.Shutdown() c.Shutdown()
} }
} }
if config.ErupeConfig.Sign.Enabled { if config.Sign.Enabled {
signServer.Shutdown() signServer.Shutdown()
} }
if config.ErupeConfig.SignV2.Enabled { if config.SignV2.Enabled {
newSignServer.Shutdown() newSignServer.Shutdown()
} }
if config.ErupeConfig.Entrance.Enabled { if config.Entrance.Enabled {
entranceServer.Shutdown() entranceServer.Shutdown()
} }
@@ -285,7 +287,7 @@ func wait() {
} }
func preventClose(text string) { func preventClose(text string) {
if config.ErupeConfig.DisableSoftCrash { if _config.ErupeConfig.DisableSoftCrash {
os.Exit(0) os.Exit(0)
} }
fmt.Println("\nFailed to start Erupe:\n" + text) fmt.Println("\nFailed to start Erupe:\n" + text)

View File

@@ -3,6 +3,7 @@ package network
import ( import (
"encoding/hex" "encoding/hex"
"errors" "errors"
_config "erupe-ce/config"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -48,7 +49,14 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
} }
// Now read the encrypted packet body after getting its size from the header. // Now read the encrypted packet body after getting its size from the header.
encryptedPacketBody := make([]byte, cph.DataSize) var encryptedPacketBody []byte
// Don't know when support for this was added, works in Forward.4, doesn't work in Season 6.0
if _config.ErupeConfig.RealClientMode < _config.F1 {
encryptedPacketBody = make([]byte, cph.DataSize)
} else {
encryptedPacketBody = make([]byte, uint32(cph.DataSize)+(uint32(cph.Pf0-0x03)*0x1000))
}
_, err = io.ReadFull(cc.conn, encryptedPacketBody) _, err = io.ReadFull(cc.conn, encryptedPacketBody)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -56,7 +64,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
// Update the key rotation before decrypting. // Update the key rotation before decrypting.
if cph.KeyRotDelta != 0 { if cph.KeyRotDelta != 0 {
cc.readKeyRot = (uint32(cph.KeyRotDelta) * (cc.readKeyRot + 1)) cc.readKeyRot = uint32(cph.KeyRotDelta) * (cc.readKeyRot + 1)
} }
out, combinedCheck, check0, check1, check2 := crypto.Decrypt(encryptedPacketBody, cc.readKeyRot, nil) out, combinedCheck, check0, check1, check2 := crypto.Decrypt(encryptedPacketBody, cc.readKeyRot, nil)
@@ -94,7 +102,7 @@ func (cc *CryptConn) SendPacket(data []byte) error {
keyRotDelta := byte(3) keyRotDelta := byte(3)
if keyRotDelta != 0 { if keyRotDelta != 0 {
cc.sendKeyRot = (uint32(keyRotDelta) * (cc.sendKeyRot + 1)) cc.sendKeyRot = uint32(keyRotDelta) * (cc.sendKeyRot + 1)
} }
// Encrypt the data // Encrypt the data

View File

@@ -2,10 +2,11 @@ package mhfpacket
import ( import (
"errors" "errors"
_config "erupe-ce/config"
"erupe-ce/common/byteframe"
"erupe-ce/network" "erupe-ce/network"
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
) )
// MsgMhfAcquireCafeItem represents the MSG_MHF_ACQUIRE_CAFE_ITEM // MsgMhfAcquireCafeItem represents the MSG_MHF_ACQUIRE_CAFE_ITEM
@@ -30,7 +31,11 @@ func (m *MsgMhfAcquireCafeItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl
m.ItemType = bf.ReadUint16() m.ItemType = bf.ReadUint16()
m.ItemID = bf.ReadUint16() m.ItemID = bf.ReadUint16()
m.Quant = bf.ReadUint16() m.Quant = bf.ReadUint16()
if _config.ErupeConfig.RealClientMode >= _config.G1 {
m.PointCost = bf.ReadUint32() m.PointCost = bf.ReadUint32()
} else {
m.PointCost = uint32(bf.ReadUint16())
}
m.Unk0 = bf.ReadUint16() m.Unk0 = bf.ReadUint16()
return nil return nil
} }

View File

@@ -3,13 +3,18 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfAcquireItem represents the MSG_MHF_ACQUIRE_ITEM // MsgMhfAcquireItem represents the MSG_MHF_ACQUIRE_ITEM
type MsgMhfAcquireItem struct{} type MsgMhfAcquireItem struct {
AckHandle uint32
Unk0 uint16
Length uint16
Unk1 []uint32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfAcquireItem) Opcode() network.PacketID { func (m *MsgMhfAcquireItem) Opcode() network.PacketID {
@@ -18,7 +23,13 @@ func (m *MsgMhfAcquireItem) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfAcquireItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfAcquireItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Length = bf.ReadUint16()
for i := 0; i < int(m.Length); i++ {
m.Unk1 = append(m.Unk1, bf.ReadUint32())
}
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

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

View File

@@ -1,17 +1,18 @@
package mhfpacket package mhfpacket
import ( import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network" "erupe-ce/network"
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
) )
// MsgMhfApplyCampaign represents the MSG_MHF_APPLY_CAMPAIGN // MsgMhfApplyCampaign represents the MSG_MHF_APPLY_CAMPAIGN
type MsgMhfApplyCampaign struct { type MsgMhfApplyCampaign struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 Unk0 uint32
Unk1 uint8 Unk1 uint16
Unk2 uint16 Unk2 []byte
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -22,17 +23,13 @@ func (m *MsgMhfApplyCampaign) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfApplyCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfApplyCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint8() m.Unk1 = bf.ReadUint16()
m.Unk2 = bf.ReadUint16() m.Unk2 = bf.ReadBytes(16)
return nil return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.
func (m *MsgMhfApplyCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfApplyCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle) return errors.New("NOT IMPLEMENTED")
bf.WriteUint8(m.Unk0)
bf.WriteUint8(m.Unk1)
bf.WriteUint16(m.Unk2)
return nil
} }

View File

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

View File

@@ -3,13 +3,22 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfCaravanMyScore represents the MSG_MHF_CARAVAN_MY_SCORE // MsgMhfCaravanMyScore represents the MSG_MHF_CARAVAN_MY_SCORE
type MsgMhfCaravanMyScore struct{} type MsgMhfCaravanMyScore struct {
AckHandle uint32
Unk0 uint32
Unk1 uint32
Unk2 int32
Unk3 int32
Unk4 uint32
Unk5 int32
Unk6 int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfCaravanMyScore) Opcode() network.PacketID { func (m *MsgMhfCaravanMyScore) Opcode() network.PacketID {
@@ -18,7 +27,15 @@ func (m *MsgMhfCaravanMyScore) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfCaravanMyScore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfCaravanMyScore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadInt32()
m.Unk3 = bf.ReadInt32()
m.Unk4 = bf.ReadUint32()
m.Unk5 = bf.ReadInt32()
m.Unk6 = bf.ReadInt32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -3,13 +3,18 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfCaravanRanking represents the MSG_MHF_CARAVAN_RANKING // MsgMhfCaravanRanking represents the MSG_MHF_CARAVAN_RANKING
type MsgMhfCaravanRanking struct{} type MsgMhfCaravanRanking struct {
AckHandle uint32
Unk0 uint32
Unk1 uint32
Unk2 int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfCaravanRanking) Opcode() network.PacketID { func (m *MsgMhfCaravanRanking) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfCaravanRanking) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfCaravanRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfCaravanRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadInt32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -3,13 +3,17 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfEntryTournament represents the MSG_MHF_ENTRY_TOURNAMENT // MsgMhfEntryTournament represents the MSG_MHF_ENTRY_TOURNAMENT
type MsgMhfEntryTournament struct{} type MsgMhfEntryTournament struct {
AckHandle uint32
TournamentID uint32
Unk0 uint8
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfEntryTournament) Opcode() network.PacketID { func (m *MsgMhfEntryTournament) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfEntryTournament) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfEntryTournament) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfEntryTournament) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.TournamentID = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -1,17 +1,16 @@
package mhfpacket package mhfpacket
import ( import (
"erupe-ce/common/byteframe"
"erupe-ce/network" "erupe-ce/network"
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
) )
// MsgMhfEnumerateCampaign represents the MSG_MHF_ENUMERATE_CAMPAIGN // MsgMhfEnumerateCampaign represents the MSG_MHF_ENUMERATE_CAMPAIGN
type MsgMhfEnumerateCampaign struct { type MsgMhfEnumerateCampaign struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 Unk0 uint16
Unk1 uint8 Unk1 uint16
Unk2 uint16
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -22,17 +21,15 @@ func (m *MsgMhfEnumerateCampaign) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfEnumerateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfEnumerateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint8() m.Unk1 = bf.ReadUint16()
m.Unk2 = bf.ReadUint16()
return nil return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.
func (m *MsgMhfEnumerateCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfEnumerateCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle) bf.WriteUint32(m.AckHandle)
bf.WriteUint8(m.Unk0) bf.WriteUint16(m.Unk0)
bf.WriteUint8(m.Unk1) bf.WriteUint16(m.Unk1)
bf.WriteUint16(m.Unk2)
return nil return nil
} }

View File

@@ -3,13 +3,18 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfEnumerateItem represents the MSG_MHF_ENUMERATE_ITEM // MsgMhfEnumerateItem represents the MSG_MHF_ENUMERATE_ITEM
type MsgMhfEnumerateItem struct{} type MsgMhfEnumerateItem struct {
AckHandle uint32
Unk0 uint16
Unk1 uint16
CampaignID uint32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfEnumerateItem) Opcode() network.PacketID { func (m *MsgMhfEnumerateItem) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfEnumerateItem) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfEnumerateItem) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfEnumerateItem) 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.CampaignID = bf.ReadUint32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -2,6 +2,7 @@ package mhfpacket
import ( import (
"errors" "errors"
_config "erupe-ce/config"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network" "erupe-ce/network"
@@ -30,7 +31,9 @@ func (m *MsgMhfEnumerateQuest) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli
m.World = bf.ReadUint8() m.World = bf.ReadUint8()
m.Counter = bf.ReadUint16() m.Counter = bf.ReadUint16()
m.Offset = bf.ReadUint16() m.Offset = bf.ReadUint16()
if _config.ErupeConfig.RealClientMode > _config.Z1 {
m.Unk4 = bf.ReadUint8() m.Unk4 = bf.ReadUint8()
}
return nil return nil
} }

View File

@@ -2,10 +2,11 @@ package mhfpacket
import ( import (
"errors" "errors"
_config "erupe-ce/config"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfEnumerateShop represents the MSG_MHF_ENUMERATE_SHOP // MsgMhfEnumerateShop represents the MSG_MHF_ENUMERATE_SHOP
@@ -31,8 +32,10 @@ func (m *MsgMhfEnumerateShop) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clie
m.ShopID = bf.ReadUint32() m.ShopID = bf.ReadUint32()
m.Unk2 = bf.ReadUint16() m.Unk2 = bf.ReadUint16()
m.Unk3 = bf.ReadUint8() m.Unk3 = bf.ReadUint8()
if _config.ErupeConfig.RealClientMode >= _config.G2 {
m.Unk4 = bf.ReadUint8() m.Unk4 = bf.ReadUint8()
m.Unk5 = bf.ReadUint32() m.Unk5 = bf.ReadUint32()
}
return nil return nil
} }

View File

@@ -3,13 +3,17 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfGetBreakSeibatuLevelReward represents the MSG_MHF_GET_BREAK_SEIBATU_LEVEL_REWARD // MsgMhfGetBreakSeibatuLevelReward represents the MSG_MHF_GET_BREAK_SEIBATU_LEVEL_REWARD
type MsgMhfGetBreakSeibatuLevelReward struct{} type MsgMhfGetBreakSeibatuLevelReward struct {
AckHandle uint32
Unk0 uint32
Unk1 int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfGetBreakSeibatuLevelReward) Opcode() network.PacketID { func (m *MsgMhfGetBreakSeibatuLevelReward) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfGetBreakSeibatuLevelReward) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetBreakSeibatuLevelReward) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetBreakSeibatuLevelReward) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadInt32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -3,13 +3,20 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfGetFixedSeibatuRankingTable represents the MSG_MHF_GET_FIXED_SEIBATU_RANKING_TABLE // MsgMhfGetFixedSeibatuRankingTable represents the MSG_MHF_GET_FIXED_SEIBATU_RANKING_TABLE
type MsgMhfGetFixedSeibatuRankingTable struct{} type MsgMhfGetFixedSeibatuRankingTable struct {
AckHandle uint32
Unk0 uint32
Unk1 int32
Unk2 int32
Unk3 int32
Unk4 int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfGetFixedSeibatuRankingTable) Opcode() network.PacketID { func (m *MsgMhfGetFixedSeibatuRankingTable) Opcode() network.PacketID {
@@ -18,7 +25,13 @@ func (m *MsgMhfGetFixedSeibatuRankingTable) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetFixedSeibatuRankingTable) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetFixedSeibatuRankingTable) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadInt32()
m.Unk2 = bf.ReadInt32()
m.Unk3 = bf.ReadInt32()
m.Unk4 = bf.ReadInt32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -11,8 +11,13 @@ import (
// MsgMhfGetGemInfo represents the MSG_MHF_GET_GEM_INFO // MsgMhfGetGemInfo represents the MSG_MHF_GET_GEM_INFO
type MsgMhfGetGemInfo struct { type MsgMhfGetGemInfo struct {
AckHandle uint32 AckHandle uint32
Unk uint32 Unk0 uint32
Unk1 []byte Unk1 uint32
Unk2 int32
Unk3 int32
Unk4 int32
Unk5 int32
Unk6 int32
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -23,8 +28,13 @@ func (m *MsgMhfGetGemInfo) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetGemInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetGemInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk = bf.ReadUint32() m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadBytes(24) m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadInt32()
m.Unk3 = bf.ReadInt32()
m.Unk4 = bf.ReadInt32()
m.Unk5 = bf.ReadInt32()
m.Unk6 = bf.ReadInt32()
return nil return nil
} }

View File

@@ -3,13 +3,18 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfGetNotice represents the MSG_MHF_GET_NOTICE // MsgMhfGetNotice represents the MSG_MHF_GET_NOTICE
type MsgMhfGetNotice struct{} type MsgMhfGetNotice struct {
AckHandle uint32
Unk0 uint32
Unk1 uint32
Unk2 int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfGetNotice) Opcode() network.PacketID { func (m *MsgMhfGetNotice) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfGetNotice) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetNotice) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetNotice) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadInt32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -11,8 +11,8 @@ import (
// MsgMhfGetRyoudama represents the MSG_MHF_GET_RYOUDAMA // MsgMhfGetRyoudama represents the MSG_MHF_GET_RYOUDAMA
type MsgMhfGetRyoudama struct { type MsgMhfGetRyoudama struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 Request1 uint8
Unk1 uint8 Request2 uint8
GuildID uint32 GuildID uint32
Unk3 uint8 Unk3 uint8
} }
@@ -25,8 +25,8 @@ func (m *MsgMhfGetRyoudama) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetRyoudama) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetRyoudama) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.Request1 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8() m.Request2 = bf.ReadUint8()
m.GuildID = bf.ReadUint32() m.GuildID = bf.ReadUint32()
m.Unk3 = bf.ReadUint8() m.Unk3 = bf.ReadUint8()
return nil return nil

View File

@@ -3,18 +3,17 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfGetSeibattle represents the MSG_MHF_GET_SEIBATTLE // MsgMhfGetSeibattle represents the MSG_MHF_GET_SEIBATTLE
type MsgMhfGetSeibattle struct { type MsgMhfGetSeibattle struct {
// Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32 AckHandle uint32
Unk0 uint8 Unk0 uint8
Unk1 uint8 Type uint8
Unk2 uint32 // Some shared ID with MSG_SYS_RECORD_LOG, world ID? GuildID uint32
Unk3 uint8 Unk3 uint8
Unk4 uint16 Unk4 uint16
} }
@@ -28,8 +27,8 @@ func (m *MsgMhfGetSeibattle) Opcode() network.PacketID {
func (m *MsgMhfGetSeibattle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetSeibattle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8() m.Type = bf.ReadUint8()
m.Unk2 = bf.ReadUint32() m.GuildID = bf.ReadUint32()
m.Unk3 = bf.ReadUint8() m.Unk3 = bf.ReadUint8()
m.Unk4 = bf.ReadUint16() m.Unk4 = bf.ReadUint16()
return nil return nil

View File

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

View File

@@ -3,18 +3,19 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfGetTenrouirai represents the MSG_MHF_GET_TENROUIRAI // MsgMhfGetTenrouirai represents the MSG_MHF_GET_TENROUIRAI
type MsgMhfGetTenrouirai struct { type MsgMhfGetTenrouirai struct {
// Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32 AckHandle uint32
Unk0 uint16 Unk0 uint8
Unk1 uint32 Unk1 uint8
Unk2 uint16 GuildID uint32
Unk3 uint8
Unk4 uint8
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -25,9 +26,11 @@ func (m *MsgMhfGetTenrouirai) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetTenrouirai) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetTenrouirai) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16() m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint32() m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint16() m.GuildID = bf.ReadUint32()
m.Unk3 = bf.ReadUint8()
m.Unk4 = bf.ReadUint8()
return nil return nil
} }

View File

@@ -3,17 +3,18 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfGetTinyBin represents the MSG_MHF_GET_TINY_BIN // MsgMhfGetTinyBin represents the MSG_MHF_GET_TINY_BIN
type MsgMhfGetTinyBin struct { type MsgMhfGetTinyBin struct {
// Communicator type, multi-format. This might be valid for only one type. // Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32 AckHandle uint32
Unk0 uint16 Unk0 uint8
Unk1 uint8 Unk1 uint8
Unk2 uint8
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -24,8 +25,9 @@ func (m *MsgMhfGetTinyBin) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfGetTinyBin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfGetTinyBin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16() m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8() m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint8()
return nil return nil
} }

View File

@@ -8,19 +8,8 @@ import (
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
) )
// The server sends different responses based on these values.
const (
TowerInfoTypeUnk0 = iota
TowerInfoTypeTowerRankPoint
TowerInfoTypeGetOwnTowerSkill
TowerInfoTypeGetOwnTowerLevelV3
TowerInfoTypeTowerTouhaHistory
TowerInfoTypeUnk5
)
// MsgMhfGetTowerInfo represents the MSG_MHF_GET_TOWER_INFO // MsgMhfGetTowerInfo represents the MSG_MHF_GET_TOWER_INFO
type MsgMhfGetTowerInfo struct { type MsgMhfGetTowerInfo struct {
// Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32 AckHandle uint32
InfoType uint32 // Requested response type InfoType uint32 // Requested response type
Unk0 uint32 Unk0 uint32

View File

@@ -11,33 +11,33 @@ import (
type OperateGuildAction uint8 type OperateGuildAction uint8
const ( const (
OPERATE_GUILD_DISBAND = 0x01 OperateGuildDisband = iota + 1
OPERATE_GUILD_APPLY = 0x02 OperateGuildApply
OPERATE_GUILD_LEAVE = 0x03 OperateGuildLeave
OPERATE_GUILD_RESIGN = 0x04 OperateGuildResign
OPERATE_GUILD_SET_APPLICATION_DENY = 0x05 OperateGuildSetApplicationDeny
OPERATE_GUILD_SET_APPLICATION_ALLOW = 0x06 OperateGuildSetApplicationAllow
OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE = 0x07 OperateGuildSetAvoidLeadershipTrue
OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE = 0x08 OperateGuildSetAvoidLeadershipFalse
OPERATE_GUILD_UPDATE_COMMENT = 0x09 OperateGuildUpdateComment
OPERATE_GUILD_DONATE_RANK = 0x0a OperateGuildDonateRank
OPERATE_GUILD_UPDATE_MOTTO = 0x0b OperateGuildUpdateMotto
OPERATE_GUILD_RENAME_PUGI_1 = 0x0c OperateGuildRenamePugi1
OPERATE_GUILD_RENAME_PUGI_2 = 0x0d OperateGuildRenamePugi2
OPERATE_GUILD_RENAME_PUGI_3 = 0x0e OperateGuildRenamePugi3
OPERATE_GUILD_CHANGE_PUGI_1 = 0x0f OperateGuildChangePugi1
OPERATE_GUILD_CHANGE_PUGI_2 = 0x10 OperateGuildChangePugi2
OPERATE_GUILD_CHANGE_PUGI_3 = 0x11 OperateGuildChangePugi3
OPERATE_GUILD_UNLOCK_OUTFIT = 0x12 OperateGuildUnlockOutfit
// 0x13 Unk OperateGuildDonateRoom
// 0x14 Unk OperateGuildGraduateRookie
OPERATE_GUILD_DONATE_EVENT = 0x15 OperateGuildDonateEvent
OPERATE_GUILD_EVENT_EXCHANGE = 0x16 OperateGuildEventExchange
// 0x17 Unk OperateGuildUnknown // I don't think this op exists
// 0x18 Unk OperateGuildGraduateReturn
OPERATE_GUILD_CHANGE_DIVA_PUGI_1 = 0x19 OperateGuildChangeDivaPugi1
OPERATE_GUILD_CHANGE_DIVA_PUGI_2 = 0x1a OperateGuildChangeDivaPugi2
OPERATE_GUILD_CHANGE_DIVA_PUGI_3 = 0x1b OperateGuildChangeDivaPugi3
) )
// MsgMhfOperateGuild represents the MSG_MHF_OPERATE_GUILD // MsgMhfOperateGuild represents the MSG_MHF_OPERATE_GUILD

View File

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

View File

@@ -3,13 +3,22 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfPostGemInfo represents the MSG_MHF_POST_GEM_INFO // MsgMhfPostGemInfo represents the MSG_MHF_POST_GEM_INFO
type MsgMhfPostGemInfo struct{} type MsgMhfPostGemInfo struct {
AckHandle uint32
Op uint32
Unk1 uint32
Gem int32
Quantity int32
CID int32
Message int32
Unk6 int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfPostGemInfo) Opcode() network.PacketID { func (m *MsgMhfPostGemInfo) Opcode() network.PacketID {
@@ -18,7 +27,15 @@ func (m *MsgMhfPostGemInfo) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfPostGemInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfPostGemInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Op = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Gem = bf.ReadInt32()
m.Quantity = bf.ReadInt32()
m.CID = bf.ReadInt32()
m.Message = bf.ReadInt32()
m.Unk6 = bf.ReadInt32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

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

View File

@@ -3,20 +3,32 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfPostTenrouirai represents the MSG_MHF_POST_TENROUIRAI // MsgMhfPostTenrouirai represents the MSG_MHF_POST_TENROUIRAI
type MsgMhfPostTenrouirai struct{ type MsgMhfPostTenrouirai struct {
AckHandle uint32 AckHandle uint32
Unk0 uint16 Unk0 uint8
Unk1 uint32 Op uint8
Unk2 uint32 GuildID uint32
Unk3 uint32 Unk1 uint8
Unk4 uint32
Unk5 uint8 Floors uint16
Antiques uint16
Chests uint16
Cats uint16
TRP uint16
Slays uint16
DonatedRP uint16
PreviousRP uint16
Unk2_0 uint16
Unk2_1 uint16
Unk2_2 uint16
Unk2_3 uint16
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -27,12 +39,28 @@ func (m *MsgMhfPostTenrouirai) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfPostTenrouirai) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfPostTenrouirai) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16() m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint32() m.Op = bf.ReadUint8()
m.Unk2 = bf.ReadUint32() m.GuildID = bf.ReadUint32()
m.Unk3 = bf.ReadUint32() m.Unk1 = bf.ReadUint8()
m.Unk4 = bf.ReadUint32()
m.Unk5 = bf.ReadUint8() switch m.Op {
case 1:
m.Floors = bf.ReadUint16()
m.Antiques = bf.ReadUint16()
m.Chests = bf.ReadUint16()
m.Cats = bf.ReadUint16()
m.TRP = bf.ReadUint16()
m.Slays = bf.ReadUint16()
case 2:
m.DonatedRP = bf.ReadUint16()
m.PreviousRP = bf.ReadUint16()
m.Unk2_0 = bf.ReadUint16()
m.Unk2_1 = bf.ReadUint16()
m.Unk2_2 = bf.ReadUint16()
m.Unk2_3 = bf.ReadUint16()
}
return nil return nil
} }

View File

@@ -11,9 +11,10 @@ import (
// MsgMhfPostTinyBin represents the MSG_MHF_POST_TINY_BIN // MsgMhfPostTinyBin represents the MSG_MHF_POST_TINY_BIN
type MsgMhfPostTinyBin struct { type MsgMhfPostTinyBin struct {
AckHandle uint32 AckHandle uint32
Unk0 uint16 Unk0 uint8
Unk1 uint8 Unk1 uint8
Unk2 uint8 Unk2 uint8
Unk3 uint8
Data []byte Data []byte
} }
@@ -25,9 +26,10 @@ func (m *MsgMhfPostTinyBin) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfPostTinyBin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfPostTinyBin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16() m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8() m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint8() m.Unk2 = bf.ReadUint8()
m.Unk3 = bf.ReadUint8()
m.Data = bf.ReadBytes(uint(bf.ReadUint16())) m.Data = bf.ReadBytes(uint(bf.ReadUint16()))
return nil return nil
} }

View File

@@ -3,26 +3,24 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfPostTowerInfo represents the MSG_MHF_POST_TOWER_INFO // MsgMhfPostTowerInfo represents the MSG_MHF_POST_TOWER_INFO
type MsgMhfPostTowerInfo struct { type MsgMhfPostTowerInfo struct {
// Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32 AckHandle uint32
Unk0 uint32 InfoType uint32
Unk1 uint32 Unk1 uint32
Unk2 uint32 Skill int32
Unk3 uint32 TR int32
Unk4 uint32 TRP int32
Unk5 uint32 Cost int32
Unk6 uint32 Unk6 int32
Unk7 uint32 Unk7 int32
Unk8 uint32 Block1 int32
Unk9 uint32 Unk9 int64
Unk10 uint32
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -33,17 +31,16 @@ func (m *MsgMhfPostTowerInfo) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfPostTowerInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfPostTowerInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32() m.InfoType = bf.ReadUint32()
m.Unk1 = bf.ReadUint32() m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32() m.Skill = bf.ReadInt32()
m.Unk3 = bf.ReadUint32() m.TR = bf.ReadInt32()
m.Unk4 = bf.ReadUint32() m.TRP = bf.ReadInt32()
m.Unk5 = bf.ReadUint32() m.Cost = bf.ReadInt32()
m.Unk6 = bf.ReadUint32() m.Unk6 = bf.ReadInt32()
m.Unk7 = bf.ReadUint32() m.Unk7 = bf.ReadInt32()
m.Unk8 = bf.ReadUint32() m.Block1 = bf.ReadInt32()
m.Unk9 = bf.ReadUint32() m.Unk9 = bf.ReadInt64()
m.Unk10 = bf.ReadUint32()
return nil return nil
} }

View File

@@ -18,8 +18,7 @@ type MsgMhfPresentBox struct {
Unk4 uint32 Unk4 uint32
Unk5 uint32 Unk5 uint32
Unk6 uint32 Unk6 uint32
Unk7 uint32 Unk7 []uint32
Unk8 uint32
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -37,8 +36,9 @@ func (m *MsgMhfPresentBox) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientC
m.Unk4 = bf.ReadUint32() m.Unk4 = bf.ReadUint32()
m.Unk5 = bf.ReadUint32() m.Unk5 = bf.ReadUint32()
m.Unk6 = bf.ReadUint32() m.Unk6 = bf.ReadUint32()
m.Unk7 = bf.ReadUint32() for i := uint32(0); i < m.Unk2; i++ {
m.Unk8 = bf.ReadUint32() m.Unk7 = append(m.Unk7, bf.ReadUint32())
}
return nil return nil
} }

View File

@@ -3,13 +3,18 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfReadBeatLevelAllRanking represents the MSG_MHF_READ_BEAT_LEVEL_ALL_RANKING // MsgMhfReadBeatLevelAllRanking represents the MSG_MHF_READ_BEAT_LEVEL_ALL_RANKING
type MsgMhfReadBeatLevelAllRanking struct{} type MsgMhfReadBeatLevelAllRanking struct {
AckHandle uint32
Unk0 uint32
GuildID int32
Unk2 int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfReadBeatLevelAllRanking) Opcode() network.PacketID { func (m *MsgMhfReadBeatLevelAllRanking) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfReadBeatLevelAllRanking) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfReadBeatLevelAllRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfReadBeatLevelAllRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.GuildID = bf.ReadInt32()
m.Unk2 = bf.ReadInt32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -3,13 +3,18 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfReadBeatLevelMyRanking represents the MSG_MHF_READ_BEAT_LEVEL_MY_RANKING // MsgMhfReadBeatLevelMyRanking represents the MSG_MHF_READ_BEAT_LEVEL_MY_RANKING
type MsgMhfReadBeatLevelMyRanking struct{} type MsgMhfReadBeatLevelMyRanking struct {
AckHandle uint32
Unk0 uint32
Unk1 uint32
Unk2 []int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfReadBeatLevelMyRanking) Opcode() network.PacketID { func (m *MsgMhfReadBeatLevelMyRanking) Opcode() network.PacketID {
@@ -18,7 +23,13 @@ func (m *MsgMhfReadBeatLevelMyRanking) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfReadBeatLevelMyRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfReadBeatLevelMyRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
for i := 0; i < 16; i++ {
m.Unk2 = append(m.Unk2, bf.ReadInt32())
}
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -3,13 +3,17 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfReadLastWeekBeatRanking represents the MSG_MHF_READ_LAST_WEEK_BEAT_RANKING // MsgMhfReadLastWeekBeatRanking represents the MSG_MHF_READ_LAST_WEEK_BEAT_RANKING
type MsgMhfReadLastWeekBeatRanking struct{} type MsgMhfReadLastWeekBeatRanking struct {
AckHandle uint32
Unk0 uint32
Unk1 int32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgMhfReadLastWeekBeatRanking) Opcode() network.PacketID { func (m *MsgMhfReadLastWeekBeatRanking) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfReadLastWeekBeatRanking) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfReadLastWeekBeatRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfReadLastWeekBeatRanking) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadInt32()
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -2,10 +2,11 @@ package mhfpacket
import ( import (
"errors" "errors"
_config "erupe-ce/config"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfSavedata represents the MSG_MHF_SAVEDATA // MsgMhfSavedata represents the MSG_MHF_SAVEDATA
@@ -29,7 +30,9 @@ func (m *MsgMhfSavedata) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientCon
m.AllocMemSize = bf.ReadUint32() m.AllocMemSize = bf.ReadUint32()
m.SaveType = bf.ReadUint8() m.SaveType = bf.ReadUint8()
m.Unk1 = bf.ReadUint32() m.Unk1 = bf.ReadUint32()
if _config.ErupeConfig.RealClientMode >= _config.G1 {
m.DataSize = bf.ReadUint32() m.DataSize = bf.ReadUint32()
}
if m.DataSize == 0 { // seems to be used when DataSize = 0 rather than on savetype? if m.DataSize == 0 { // seems to be used when DataSize = 0 rather than on savetype?
m.RawDataPayload = bf.ReadBytes(uint(m.AllocMemSize)) m.RawDataPayload = bf.ReadBytes(uint(m.AllocMemSize))
} else { } else {

View File

@@ -3,16 +3,22 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfSetCaAchievementHist represents the MSG_MHF_SET_CA_ACHIEVEMENT_HIST type CaAchievementHist struct {
type MsgMhfSetCaAchievementHist struct{
AckHandle uint32
Unk0 uint32 Unk0 uint32
Unk1 uint32 Unk1 uint8
}
// MsgMhfSetCaAchievementHist represents the MSG_MHF_SET_CA_ACHIEVEMENT_HIST
type MsgMhfSetCaAchievementHist struct {
AckHandle uint32
Unk0 uint16
Unk1 uint8
Unk2 []CaAchievementHist
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -23,8 +29,14 @@ func (m *MsgMhfSetCaAchievementHist) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfSetCaAchievementHist) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfSetCaAchievementHist) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32() m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint32() m.Unk1 = bf.ReadUint8()
for i := 0; i < int(m.Unk1); i++ {
var temp CaAchievementHist
temp.Unk0 = bf.ReadUint32()
temp.Unk1 = bf.ReadUint8()
m.Unk2 = append(m.Unk2, temp)
}
return nil return nil
} }

View File

@@ -3,15 +3,18 @@ package mhfpacket
import ( import (
"errors" "errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
) )
// MsgMhfSexChanger represents the MSG_MHF_SEX_CHANGER // MsgMhfSexChanger represents the MSG_MHF_SEX_CHANGER
type MsgMhfSexChanger struct { type MsgMhfSexChanger struct {
AckHandle uint32 AckHandle uint32
Gender uint8 Gender uint8
Unk0 uint8
Unk1 uint8
Unk2 uint8
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -23,6 +26,9 @@ func (m *MsgMhfSexChanger) Opcode() network.PacketID {
func (m *MsgMhfSexChanger) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfSexChanger) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Gender = bf.ReadUint8() m.Gender = bf.ReadUint8()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint8()
return nil return nil
} }

View File

@@ -2,6 +2,7 @@ package mhfpacket
import ( import (
"errors" "errors"
_config "erupe-ce/config"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network" "erupe-ce/network"
@@ -34,12 +35,14 @@ func (m *MsgMhfStampcardStamp) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli
m.GR = bf.ReadUint16() m.GR = bf.ReadUint16()
m.Stamps = bf.ReadUint16() m.Stamps = bf.ReadUint16()
_ = bf.ReadUint16() _ = bf.ReadUint16()
if _config.ErupeConfig.RealClientMode > _config.Z1 {
m.Reward1 = uint16(bf.ReadUint32()) m.Reward1 = uint16(bf.ReadUint32())
m.Reward2 = uint16(bf.ReadUint32()) m.Reward2 = uint16(bf.ReadUint32())
m.Item1 = uint16(bf.ReadUint32()) m.Item1 = uint16(bf.ReadUint32())
m.Item2 = uint16(bf.ReadUint32()) m.Item2 = uint16(bf.ReadUint32())
m.Quantity1 = uint16(bf.ReadUint32()) m.Quantity1 = uint16(bf.ReadUint32())
m.Quantity2 = uint16(bf.ReadUint32()) m.Quantity2 = uint16(bf.ReadUint32())
}
return nil return nil
} }

View File

@@ -1,18 +1,18 @@
package mhfpacket package mhfpacket
import ( import (
"errors"
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network"
) )
// MsgMhfStateCampaign represents the MSG_MHF_STATE_CAMPAIGN // MsgMhfStateCampaign represents the MSG_MHF_STATE_CAMPAIGN
type MsgMhfStateCampaign struct { type MsgMhfStateCampaign struct {
AckHandle uint32 AckHandle uint32
Unk0 uint8 CampaignID uint32
Unk1 uint8 Unk1 uint16
Unk2 uint16
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -23,17 +23,12 @@ func (m *MsgMhfStateCampaign) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgMhfStateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfStateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8() m.CampaignID = bf.ReadUint32()
m.Unk1 = bf.ReadUint8() m.Unk1 = bf.ReadUint16()
m.Unk2 = bf.ReadUint16()
return nil return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.
func (m *MsgMhfStateCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgMhfStateCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle) return errors.New("NOT IMPLEMENTED")
bf.WriteUint8(m.Unk0)
bf.WriteUint8(m.Unk1)
bf.WriteUint16(m.Unk2)
return nil
} }

View File

@@ -13,12 +13,8 @@ type MsgMhfUpdateBeatLevel struct {
AckHandle uint32 AckHandle uint32
Unk1 uint32 Unk1 uint32
Unk2 uint32 Unk2 uint32
MonsterData []byte Data1 []int32
Unk3 uint8 Data2 []int32
Unk4 uint32
Unk5 uint16
Unk6 uint8
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -31,11 +27,12 @@ func (m *MsgMhfUpdateBeatLevel) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk1 = bf.ReadUint32() m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32() m.Unk2 = bf.ReadUint32()
m.MonsterData = bf.ReadBytes(uint(120)) for i := 0; i < 16; i++ {
m.Unk3 = bf.ReadUint8() m.Data1 = append(m.Data1, bf.ReadInt32())
m.Unk4 = bf.ReadUint32() }
m.Unk5 = bf.ReadUint16() for i := 0; i < 16; i++ {
m.Unk6 = bf.ReadUint8() m.Data2 = append(m.Data2, bf.ReadInt32())
}
return nil return nil
} }

View File

@@ -2,10 +2,9 @@ package mhfpacket
import ( import (
"errors" "errors"
"fmt"
"erupe-ce/common/byteframe"
"erupe-ce/common/bfutil" "erupe-ce/common/bfutil"
"erupe-ce/common/byteframe"
_config "erupe-ce/config"
"erupe-ce/network" "erupe-ce/network"
"erupe-ce/network/clientctx" "erupe-ce/network/clientctx"
) )
@@ -27,8 +26,9 @@ func (m *MsgSysCreateAcquireSemaphore) Opcode() network.PacketID {
func (m *MsgSysCreateAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { func (m *MsgSysCreateAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16() m.Unk0 = bf.ReadUint16()
if _config.ErupeConfig.RealClientMode >= _config.G1 {
m.PlayerCount = bf.ReadUint8() m.PlayerCount = bf.ReadUint8()
fmt.Printf("PLAYER COUNT :: %d", m.PlayerCount) }
SemaphoreIDLength := bf.ReadUint8() SemaphoreIDLength := bf.ReadUint8()
m.SemaphoreID = string(bfutil.UpToNull(bf.ReadBytes(uint(SemaphoreIDLength)))) m.SemaphoreID = string(bfutil.UpToNull(bf.ReadBytes(uint(SemaphoreIDLength))))
return nil return nil

View File

@@ -2,6 +2,7 @@ package mhfpacket
import ( import (
"errors" "errors"
_config "erupe-ce/config"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/network" "erupe-ce/network"
@@ -37,12 +38,16 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
m.EntryCount = bf.ReadUint16() m.EntryCount = bf.ReadUint16()
m.Unk0 = bf.ReadUint16() m.Unk0 = bf.ReadUint16()
values := 15
if _config.ErupeConfig.RealClientMode <= _config.F5 {
values = 7
}
for i := 0; i < int(m.EntryCount); i++ { for i := 0; i < int(m.EntryCount); i++ {
e := &TerminalLogEntry{} e := &TerminalLogEntry{}
e.Index = bf.ReadUint32() e.Index = bf.ReadUint32()
e.Type1 = bf.ReadUint8() e.Type1 = bf.ReadUint8()
e.Type2 = bf.ReadUint8() e.Type2 = bf.ReadUint8()
for j := 0; j < 15; j++ { for j := 0; j < values; j++ {
e.Data = append(e.Data, bf.ReadInt16()) e.Data = append(e.Data, bf.ReadInt16())
} }
m.Entries = append(m.Entries, e) m.Entries = append(m.Entries, e)

29
patch-schema/tower.sql Normal file
View File

@@ -0,0 +1,29 @@
BEGIN;
CREATE TABLE IF NOT EXISTS tower (
char_id INT,
tr INT,
trp INT,
tsp INT,
block1 INT,
block2 INT,
skills TEXT DEFAULT '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0',
gems TEXT DEFAULT '0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'
);
ALTER TABLE IF EXISTS guild_characters
ADD COLUMN IF NOT EXISTS tower_mission_1 INT;
ALTER TABLE IF EXISTS guild_characters
ADD COLUMN IF NOT EXISTS tower_mission_2 INT;
ALTER TABLE IF EXISTS guild_characters
ADD COLUMN IF NOT EXISTS tower_mission_3 INT;
ALTER TABLE IF EXISTS guilds
ADD COLUMN IF NOT EXISTS tower_mission_page INT DEFAULT 1;
ALTER TABLE IF EXISTS guilds
ADD COLUMN IF NOT EXISTS tower_rp INT DEFAULT 0;
END;

View File

@@ -6,6 +6,7 @@ import (
"erupe-ce/common/mhfcourse" "erupe-ce/common/mhfcourse"
ps "erupe-ce/common/pascalstring" ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport" "erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"fmt" "fmt"
"io" "io"
"net" "net"
@@ -27,15 +28,16 @@ func stubEnumerateNoResults(s *Session, ackHandle uint32) {
doAckBufSucceed(s, ackHandle, enumBf.Data()) doAckBufSucceed(s, ackHandle, enumBf.Data())
} }
// Temporary function to just return no results for many MSG_MHF_GET* packets. func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
func stubGetNoResults(s *Session, ackHandle uint32) { bf := byteframe.NewByteFrame()
resp := byteframe.NewByteFrame() bf.WriteUint32(uint32(s.server.erupeConfig.DevModeOptions.EarthIDOverride))
resp.WriteUint32(0x0A218EAD) // Unk shared ID. Sent in response of MSG_MHF_GET_TOWER_INFO, MSG_MHF_GET_PAPER_DATA etc. (World ID?) bf.WriteUint32(0)
resp.WriteUint32(0) // Unk bf.WriteUint32(0)
resp.WriteUint32(0) // Unk bf.WriteUint32(uint32(len(data)))
resp.WriteUint32(0) // Entry count for i := range data {
bf.WriteBytes(data[i].Data())
doAckBufSucceed(s, ackHandle, resp.Data()) }
doAckBufSucceed(s, ackHandle, bf.Data())
} }
func doAckBufSucceed(s *Session, ackHandle uint32, data []byte) { func doAckBufSucceed(s *Session, ackHandle uint32, data []byte) {
@@ -245,7 +247,7 @@ func logoutPlayer(s *Session) {
removeSessionFromStage(s) removeSessionFromStage(s)
saveData, err := GetCharacterSaveData(s, s.charID) saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil { if err != nil || saveData == nil {
s.logger.Error("Failed to get savedata") s.logger.Error("Failed to get savedata")
return return
} }
@@ -446,48 +448,110 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
} }
} }
case 4: // Find Party case 4: // Find Party
type FindPartyParams struct {
StagePrefix string
RankRestriction uint16
Targets []uint16
Unk0 []uint16
Unk1 []uint16
QuestID []uint16
}
findPartyParams := FindPartyParams{
StagePrefix: "sl2Ls210",
}
bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) bf := byteframe.NewByteFrameFromBytes(pkt.MessageData)
setting := bf.ReadUint8() numParams := int(bf.ReadUint8())
maxResults := bf.ReadUint16() maxResults := bf.ReadUint16()
bf.Seek(2, 1) for i := 0; i < numParams; i++ {
partyType := bf.ReadUint16() switch bf.ReadUint8() {
rankRestriction := uint16(0) case 0:
if setting >= 2 { values := int(bf.ReadUint8())
bf.Seek(2, 1) for i := 0; i < values; i++ {
rankRestriction = bf.ReadUint16() if _config.ErupeConfig.RealClientMode >= _config.Z1 {
} findPartyParams.RankRestriction = bf.ReadUint16()
targets := make([]uint16, 4) } else {
if setting >= 3 { findPartyParams.RankRestriction = uint16(bf.ReadInt8())
bf.Seek(1, 1)
lenTargets := int(bf.ReadUint8())
for i := 0; i < lenTargets; i++ {
targets[i] = bf.ReadUint16()
} }
} }
var stagePrefix string case 1:
switch partyType { values := int(bf.ReadUint8())
for i := 0; i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.Targets = append(findPartyParams.Targets, bf.ReadUint16())
} else {
findPartyParams.Targets = append(findPartyParams.Targets, uint16(bf.ReadInt8()))
}
}
case 2:
values := int(bf.ReadUint8())
for i := 0; i < values; i++ {
var value int16
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
value = bf.ReadInt16()
} else {
value = int16(bf.ReadInt8())
}
switch value {
case 0: // Public Bar case 0: // Public Bar
stagePrefix = "sl2Ls210" findPartyParams.StagePrefix = "sl2Ls210"
case 1: // Tokotoko Partnya case 1: // Tokotoko Partnya
stagePrefix = "sl2Ls463" findPartyParams.StagePrefix = "sl2Ls463"
case 2: // Hunting Prowess Match case 2: // Hunting Prowess Match
stagePrefix = "sl2Ls286" findPartyParams.StagePrefix = "sl2Ls286"
case 3: // Volpakkun Together case 3: // Volpakkun Together
stagePrefix = "sl2Ls465" findPartyParams.StagePrefix = "sl2Ls465"
case 5: // Quick Party case 5: // Quick Party
// Unk // Unk
} }
}
case 3: // Unknown
values := int(bf.ReadUint8())
for i := 0; i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.Unk0 = append(findPartyParams.Unk0, bf.ReadUint16())
} else {
findPartyParams.Unk0 = append(findPartyParams.Unk0, uint16(bf.ReadInt8()))
}
}
case 4: // Looking for n or already have n
values := int(bf.ReadUint8())
for i := 0; i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.Unk1 = append(findPartyParams.Unk1, bf.ReadUint16())
} else {
findPartyParams.Unk1 = append(findPartyParams.Unk1, uint16(bf.ReadInt8()))
}
}
case 5:
values := int(bf.ReadUint8())
for i := 0; i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.QuestID = append(findPartyParams.QuestID, bf.ReadUint16())
} else {
findPartyParams.QuestID = append(findPartyParams.QuestID, uint16(bf.ReadInt8()))
}
}
}
}
for _, c := range s.server.Channels { for _, c := range s.server.Channels {
for _, stage := range c.stages { for _, stage := range c.stages {
if count == maxResults { if count == maxResults {
break break
} }
if strings.HasPrefix(stage.id, stagePrefix) { if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) {
sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}]) sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}])
sb3.Seek(4, 0) sb3.Seek(4, 0)
stageRankRestriction := sb3.ReadUint16() stageRankRestriction := sb3.ReadUint16()
stageTarget := sb3.ReadUint16() stageTarget := sb3.ReadUint16()
if rankRestriction != 0xFFFF && stageRankRestriction < rankRestriction { if stageRankRestriction > findPartyParams.RankRestriction {
continue
}
if len(findPartyParams.Targets) > 0 {
for _, target := range findPartyParams.Targets {
if target == stageTarget {
break
}
}
continue continue
} }
count++ count++
@@ -551,10 +615,6 @@ func handleMsgSysInfokyserver(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetCaUniqueID(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetCaUniqueID(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfEnumerateItem(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfAcquireItem(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfTransferItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfTransferItem) pkt := p.(*mhfpacket.MsgMhfTransferItem)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
@@ -1574,138 +1634,209 @@ func handleMsgMhfStampcardPrize(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfUnreserveSrg(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadBeatLevel)
// This response is fixed and will never change on JP,
// but I've left it dynamic for possible other client differences.
resp := byteframe.NewByteFrame()
for i := 0; i < int(pkt.ValidIDCount); i++ {
resp.WriteUint32(pkt.IDs[i])
resp.WriteUint32(1)
resp.WriteUint32(1)
resp.WriteUint32(1)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfUpdateBeatLevel(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateBeatLevel)
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfReadBeatLevelAllRanking(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfReadBeatLevelMyRanking(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfReadLastWeekBeatRanking(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetFixedSeibatuRankingTable(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfKickExportForce(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthStatus) pkt := p.(*mhfpacket.MsgMhfGetEarthStatus)
bf := byteframe.NewByteFrame()
// TODO(Andoryuuta): Track down format for this data, bf.WriteUint32(uint32(TimeWeekStart().Add(time.Hour * -24).Unix())) // Start
// it can somehow be parsed as 8*uint32 chunks if the header is right. bf.WriteUint32(uint32(TimeWeekNext().Add(time.Hour * 24).Unix())) // End
/* bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride)
BEFORE ack-refactor: bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride)
resp := byteframe.NewByteFrame() bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthMonsterOverride)
resp.WriteUint32(0) bf.WriteInt32(0)
resp.WriteUint32(0) bf.WriteInt32(0)
bf.WriteInt32(0)
s.QueueAck(pkt.AckHandle, resp.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
*/
doAckBufSucceed(s, pkt.AckHandle, []byte{})
} }
func handleMsgMhfRegistSpabiTime(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfRegistSpabiTime(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthValue) pkt := p.(*mhfpacket.MsgMhfGetEarthValue)
var earthValues []struct{ Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 } type EarthValues struct {
if pkt.ReqType == 3 { Value []uint32
earthValues = []struct {
Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32
}{
// TW identical to JP
{
Unk0: 0x03E9,
Unk1: 0x24,
},
{
Unk0: 0x2329,
Unk1: 0x03,
},
{
Unk0: 0x232A,
Unk1: 0x0A,
Unk2: 0x012C,
},
} }
} else if pkt.ReqType == 2 {
earthValues = []struct { var earthValues []EarthValues
Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 switch pkt.ReqType {
}{ case 1:
// JP response was empty earthValues = []EarthValues{
{ {[]uint32{1, 312, 0, 0, 0, 0}},
Unk0: 0x01, {[]uint32{2, 99, 0, 0, 0, 0}},
Unk1: 0x168B,
},
{
Unk0: 0x02,
Unk1: 0x0737,
},
} }
} else if pkt.ReqType == 1 { case 2:
earthValues = []struct { earthValues = []EarthValues{
Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 {[]uint32{1, 5771, 0, 0, 0, 0}},
}{ {[]uint32{2, 1847, 0, 0, 0, 0}},
// JP simply sent 01 and 02 respectively }
{ case 3:
Unk0: 0x01, earthValues = []EarthValues{
Unk1: 0x0138, {[]uint32{1001, 36, 0, 0, 0, 0}},
}, {[]uint32{9001, 3, 0, 0, 0, 0}},
{ {[]uint32{9002, 10, 300, 0, 0, 0}},
Unk0: 0x02,
Unk1: 0x63,
},
} }
} }
resp := byteframe.NewByteFrame() var data []*byteframe.ByteFrame
resp.WriteUint32(0x0A218EAD) // Unk shared ID. Sent in response of MSG_MHF_GET_TOWER_INFO, MSG_MHF_GET_PAPER_DATA etc. for _, i := range earthValues {
resp.WriteUint32(0) // Unk bf := byteframe.NewByteFrame()
resp.WriteUint32(0) // Unk for _, j := range i.Value {
resp.WriteUint32(uint32(len(earthValues))) // value count bf.WriteUint32(j)
for _, v := range earthValues {
resp.WriteUint32(v.Unk0)
resp.WriteUint32(v.Unk1)
resp.WriteUint32(v.Unk2)
resp.WriteUint32(v.Unk3)
resp.WriteUint32(v.Unk4)
resp.WriteUint32(v.Unk5)
} }
data = append(data, bf)
doAckBufSucceed(s, pkt.AckHandle, resp.Data()) }
doAckEarthSucceed(s, pkt.AckHandle, data)
} }
func handleMsgMhfDebugPostValue(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfDebugPostValue(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetNotice(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfPostNotice(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetRandFromTable(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetRandFromTable(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetSenyuDailyCount(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetSenyuDailyCount)
bf := byteframe.NewByteFrame()
bf.WriteUint16(0)
bf.WriteUint16(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
type SeibattleTimetable struct {
Start time.Time
End time.Time
}
type SeibattleKeyScore struct {
Unk0 uint8
Unk1 int32
}
type SeibattleCareer struct {
Unk0 uint16
Unk1 uint16
Unk2 uint16
}
type SeibattleOpponent struct {
Unk0 int32
Unk1 int8
}
type SeibattleConventionResult struct {
Unk0 uint32
Unk1 uint16
Unk2 uint16
Unk3 uint16
Unk4 uint16
}
type SeibattleCharScore struct {
Unk0 uint32
}
type SeibattleCurResult struct {
Unk0 uint32
Unk1 uint16
Unk2 uint16
Unk3 uint16
}
type Seibattle struct {
Timetable []SeibattleTimetable
KeyScore []SeibattleKeyScore
Career []SeibattleCareer
Opponent []SeibattleOpponent
ConventionResult []SeibattleConventionResult
CharScore []SeibattleCharScore
CurResult []SeibattleCurResult
}
func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetSeibattle(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetSeibattle) pkt := p.(*mhfpacket.MsgMhfGetSeibattle)
stubGetNoResults(s, pkt.AckHandle) var data []*byteframe.ByteFrame
seibattle := Seibattle{
Timetable: []SeibattleTimetable{
{TimeMidnight(), TimeMidnight().Add(time.Hour * 8)},
{TimeMidnight().Add(time.Hour * 8), TimeMidnight().Add(time.Hour * 16)},
{TimeMidnight().Add(time.Hour * 16), TimeMidnight().Add(time.Hour * 24)},
},
KeyScore: []SeibattleKeyScore{
{0, 0},
},
Career: []SeibattleCareer{
{0, 0, 0},
},
Opponent: []SeibattleOpponent{
{1, 1},
},
ConventionResult: []SeibattleConventionResult{
{0, 0, 0, 0, 0},
},
CharScore: []SeibattleCharScore{
{0},
},
CurResult: []SeibattleCurResult{
{0, 0, 0, 0},
},
}
switch pkt.Type {
case 1:
for _, timetable := range seibattle.Timetable {
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(timetable.Start.Unix()))
bf.WriteUint32(uint32(timetable.End.Unix()))
data = append(data, bf)
}
case 3: // Key score?
for _, keyScore := range seibattle.KeyScore {
bf := byteframe.NewByteFrame()
bf.WriteUint8(keyScore.Unk0)
bf.WriteInt32(keyScore.Unk1)
data = append(data, bf)
}
case 4: // Career?
for _, career := range seibattle.Career {
bf := byteframe.NewByteFrame()
bf.WriteUint16(career.Unk0)
bf.WriteUint16(career.Unk1)
bf.WriteUint16(career.Unk2)
data = append(data, bf)
}
case 5: // Opponent?
for _, opponent := range seibattle.Opponent {
bf := byteframe.NewByteFrame()
bf.WriteInt32(opponent.Unk0)
bf.WriteInt8(opponent.Unk1)
data = append(data, bf)
}
case 6: // Convention result?
for _, conventionResult := range seibattle.ConventionResult {
bf := byteframe.NewByteFrame()
bf.WriteUint32(conventionResult.Unk0)
bf.WriteUint16(conventionResult.Unk1)
bf.WriteUint16(conventionResult.Unk2)
bf.WriteUint16(conventionResult.Unk3)
bf.WriteUint16(conventionResult.Unk4)
data = append(data, bf)
}
case 7: // Char score?
for _, charScore := range seibattle.CharScore {
bf := byteframe.NewByteFrame()
bf.WriteUint32(charScore.Unk0)
data = append(data, bf)
}
case 8: // Cur result?
for _, curResult := range seibattle.CurResult {
bf := byteframe.NewByteFrame()
bf.WriteUint32(curResult.Unk0)
bf.WriteUint16(curResult.Unk1)
bf.WriteUint16(curResult.Unk2)
bf.WriteUint16(curResult.Unk3)
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
} }
func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostSeibattle(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -270,6 +270,12 @@ func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} }
func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostBoostTime(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostBoostTime)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostBoostTimeLimit(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostBoostTimeLimit)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -1,18 +1,173 @@
package channelserver package channelserver
import "erupe-ce/network/mhfpacket" import (
"erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport"
"erupe-ce/network/mhfpacket"
"time"
)
type CampaignEvent struct {
ID uint32
Unk0 uint32
MinHR int16
MaxHR int16
MinSR int16
MaxSR int16
MinGR int16
MaxGR int16
Unk1 uint16
Unk2 uint8
Unk3 uint8
Unk4 uint16
Unk5 uint16
Start time.Time
End time.Time
Unk6 uint8
String0 string
String1 string
String2 string
String3 string
Link string
Prefix string
Categories []uint16
}
type CampaignCategory struct {
ID uint16
Type uint8
Title string
Description string
}
type CampaignLink struct {
CategoryID uint16
CampaignID uint32
}
func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateCampaign(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateCampaign) pkt := p.(*mhfpacket.MsgMhfEnumerateCampaign)
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) bf := byteframe.NewByteFrame()
events := []CampaignEvent{}
categories := []CampaignCategory{}
var campaignLinks []CampaignLink
if len(events) > 255 {
bf.WriteUint8(255)
bf.WriteUint16(uint16(len(events)))
} else {
bf.WriteUint8(uint8(len(events)))
}
for _, event := range events {
bf.WriteUint32(event.ID)
bf.WriteUint32(event.Unk0)
bf.WriteInt16(event.MinHR)
bf.WriteInt16(event.MaxHR)
bf.WriteInt16(event.MinSR)
bf.WriteInt16(event.MaxSR)
bf.WriteInt16(event.MinGR)
bf.WriteInt16(event.MaxGR)
bf.WriteUint16(event.Unk1)
bf.WriteUint8(event.Unk2)
bf.WriteUint8(event.Unk3)
bf.WriteUint16(event.Unk4)
bf.WriteUint16(event.Unk5)
bf.WriteUint32(uint32(event.Start.Unix()))
bf.WriteUint32(uint32(event.End.Unix()))
bf.WriteUint8(event.Unk6)
ps.Uint8(bf, event.String0, true)
ps.Uint8(bf, event.String1, true)
ps.Uint8(bf, event.String2, true)
ps.Uint8(bf, event.String3, true)
ps.Uint8(bf, event.Link, true)
for i := range event.Categories {
campaignLinks = append(campaignLinks, CampaignLink{event.Categories[i], event.ID})
}
}
if len(events) > 255 {
bf.WriteUint8(255)
bf.WriteUint16(uint16(len(events)))
} else {
bf.WriteUint8(uint8(len(events)))
}
for _, event := range events {
bf.WriteUint32(event.ID)
bf.WriteUint8(1) // Always 1?
bf.WriteBytes([]byte(event.Prefix))
}
if len(categories) > 255 {
bf.WriteUint8(255)
bf.WriteUint16(uint16(len(categories)))
} else {
bf.WriteUint8(uint8(len(categories)))
}
for _, category := range categories {
bf.WriteUint16(category.ID)
bf.WriteUint8(category.Type)
xTitle := stringsupport.UTF8ToSJIS(category.Title)
xDescription := stringsupport.UTF8ToSJIS(category.Description)
bf.WriteUint8(uint8(len(xTitle)))
bf.WriteUint8(uint8(len(xDescription)))
bf.WriteBytes(xTitle)
bf.WriteBytes(xDescription)
}
if len(campaignLinks) > 255 {
bf.WriteUint8(255)
bf.WriteUint16(uint16(len(campaignLinks)))
} else {
bf.WriteUint8(uint8(len(campaignLinks)))
}
for _, link := range campaignLinks {
bf.WriteUint16(link.CategoryID)
bf.WriteUint32(link.CampaignID)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfStateCampaign(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfStateCampaign(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfStateCampaign) pkt := p.(*mhfpacket.MsgMhfStateCampaign)
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) bf := byteframe.NewByteFrame()
bf.WriteUint16(1)
bf.WriteUint16(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfApplyCampaign(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfApplyCampaign(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfApplyCampaign) pkt := p.(*mhfpacket.MsgMhfApplyCampaign)
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) bf := byteframe.NewByteFrame()
bf.WriteUint32(1)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfEnumerateItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateItem)
items := []struct {
Unk0 uint32
Unk1 uint16
Unk2 uint16
Unk3 uint16
Unk4 uint32
Unk5 uint32
}{}
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(len(items)))
for _, item := range items {
bf.WriteUint32(item.Unk0)
bf.WriteUint16(item.Unk1)
bf.WriteUint16(item.Unk2)
bf.WriteUint16(item.Unk3)
bf.WriteUint32(item.Unk4)
bf.WriteUint32(item.Unk5)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfAcquireItem(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireItem)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }

View File

@@ -1,21 +1,73 @@
package channelserver package channelserver
import ( import (
"encoding/hex" "erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"time"
) )
type RyoudamaReward struct {
Unk0 uint8
Unk1 uint8
Unk2 uint16
Unk3 uint16
Unk4 uint16
Unk5 uint16
}
type RyoudamaKeyScore struct {
Unk0 uint8
Unk1 int32
}
type RyoudamaCharInfo struct {
CID uint32
Unk0 int32
Name string
}
type RyoudamaBoostInfo struct {
Start time.Time
End time.Time
}
type Ryoudama struct {
Reward []RyoudamaReward
KeyScore []RyoudamaKeyScore
CharInfo []RyoudamaCharInfo
BoostInfo []RyoudamaBoostInfo
Score []int32
}
func handleMsgMhfGetRyoudama(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetRyoudama(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetRyoudama) pkt := p.(*mhfpacket.MsgMhfGetRyoudama)
// likely guild related var data []*byteframe.ByteFrame
// REQ: 00 04 13 53 8F 18 00 ryoudama := Ryoudama{Score: []int32{0}}
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 01 FE 4E switch pkt.Request2 {
// REQ: 00 06 13 53 8F 18 00 case 4:
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 for _, score := range ryoudama.Score {
// REQ: 00 05 13 53 8F 18 00 bf := byteframe.NewByteFrame()
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 0E 2A 15 9E CC 00 00 00 01 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 1E 55 B0 2F 00 00 00 01 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 2A 15 9E CC 00 00 00 02 82 79 83 4E 83 8A 81 5B 83 69 00 00 00 00 03 D5 30 56 00 00 00 02 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 3F 57 76 9F 00 00 00 03 93 56 92 6E 96 B3 97 70 00 00 00 00 00 00 38 D9 0E C4 00 00 00 03 87 64 83 78 83 42 00 00 00 00 00 00 00 00 23 F3 B9 77 00 00 00 04 82 B3 82 CC 82 DC 82 E9 81 99 00 00 00 00 3F 1B 17 9C 00 00 00 04 82 B1 82 A4 82 BD 00 00 00 00 00 00 00 00 00 B9 F9 C0 00 00 00 05 82 CD 82 E9 82 A9 00 00 00 00 00 00 00 00 23 9F 9A EA 00 00 00 05 83 70 83 62 83 4C 83 83 83 49 00 00 00 00 38 D9 0E C4 00 00 00 06 87 64 83 78 83 42 00 00 00 00 00 00 00 00 1E 55 B0 2F 00 00 00 06 8D F7 00 00 00 00 00 00 00 00 00 00 00 00 03 D5 30 56 00 00 00 07 95 BD 91 F2 97 42 00 00 00 00 00 00 00 00 02 D3 B8 77 00 00 00 07 6F 77 6C 32 35 32 35 00 00 00 00 00 00 00 bf.WriteInt32(score)
data, _ := hex.DecodeString("0A218EAD0000000000000000000000010000000000000000") data = append(data, bf)
doAckBufSucceed(s, pkt.AckHandle, data) }
case 5:
for _, info := range ryoudama.CharInfo {
bf := byteframe.NewByteFrame()
bf.WriteUint32(info.CID)
bf.WriteInt32(info.Unk0)
bf.WriteBytes(stringsupport.PaddedString(info.Name, 14, true))
data = append(data, bf)
}
case 6:
for _, info := range ryoudama.BoostInfo {
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(info.Start.Unix()))
bf.WriteUint32(uint32(info.End.Unix()))
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
} }
func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfPostRyoudama(s *Session, p mhfpacket.MHFPacket) {}
@@ -31,8 +83,41 @@ func handleMsgMhfPostTinyBin(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
func handleMsgMhfCaravanMyScore(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCaravanMyScore(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCaravanMyScore)
var data []*byteframe.ByteFrame
/*
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
*/
doAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfCaravanRanking(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfCaravanRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCaravanRanking)
var data []*byteframe.ByteFrame
/* RYOUDAN
bf.WriteInt32(1)
bf.WriteUint32(2)
bf.WriteBytes(stringsupport.PaddedString("Test", 26, true))
*/
func handleMsgMhfCaravanMyRank(s *Session, p mhfpacket.MHFPacket) {} /* PERSONAL
bf.WriteInt32(1)
bf.WriteBytes(stringsupport.PaddedString("Test", 14, true))
*/
doAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfCaravanMyRank(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfCaravanMyRank)
var data []*byteframe.ByteFrame
/*
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
*/
doAckEarthSucceed(s, pkt.AckHandle, data)
}

View File

@@ -35,16 +35,16 @@ const (
BroadcastTypeWorld = 0x0a BroadcastTypeWorld = 0x0a
) )
var commands map[string]config.Command var commands map[string]_config.Command
func init() { func init() {
commands = make(map[string]config.Command) commands = make(map[string]_config.Command)
zapConfig := zap.NewDevelopmentConfig() zapConfig := zap.NewDevelopmentConfig()
zapConfig.DisableCaller = true zapConfig.DisableCaller = true
zapLogger, _ := zapConfig.Build() zapLogger, _ := zapConfig.Build()
defer zapLogger.Sync() defer zapLogger.Sync()
logger := zapLogger.Named("commands") logger := zapLogger.Named("commands")
cmds := config.ErupeConfig.Commands cmds := _config.ErupeConfig.Commands
for _, cmd := range cmds { for _, cmd := range cmds {
commands[cmd.Name] = cmd commands[cmd.Name] = cmd
if cmd.Enabled { if cmd.Enabled {
@@ -55,7 +55,7 @@ func init() {
} }
} }
func sendDisabledCommandMessage(s *Session, cmd config.Command) { func sendDisabledCommandMessage(s *Session, cmd _config.Command) {
sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandDisabled"], cmd.Name)) sendServerChatMessage(s, fmt.Sprintf(s.server.dict["commandDisabled"], cmd.Name))
} }
@@ -211,7 +211,7 @@ func parseChatCommand(s *Session, command string) {
for _, course := range mhfcourse.Courses() { for _, course := range mhfcourse.Courses() {
for _, alias := range course.Aliases() { for _, alias := range course.Aliases() {
if strings.ToLower(name) == strings.ToLower(alias) { if strings.ToLower(name) == strings.ToLower(alias) {
if slices.Contains(s.server.erupeConfig.Courses, config.Course{Name: course.Aliases()[0], Enabled: true}) { if slices.Contains(s.server.erupeConfig.Courses, _config.Course{Name: course.Aliases()[0], Enabled: true}) {
var delta, rightsInt uint32 var delta, rightsInt uint32
if mhfcourse.CourseExists(course.ID, s.courses) { if mhfcourse.CourseExists(course.ID, s.courses) {
ei := slices.IndexFunc(s.courses, func(c mhfcourse.Course) bool { ei := slices.IndexFunc(s.courses, func(c mhfcourse.Course) bool {

View File

@@ -5,33 +5,36 @@ import (
"errors" "errors"
"erupe-ce/common/bfutil" "erupe-ce/common/bfutil"
"erupe-ce/common/stringsupport" "erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/nullcomp" "erupe-ce/server/channelserver/compression/nullcomp"
"go.uber.org/zap" "go.uber.org/zap"
) )
type SavePointer int
const ( const (
pointerGender = 0x51 // +1 pGender = iota // +1
pointerRP = 0x22D16 // +2 pRP // +2
pointerHouseTier = 0x1FB6C // +5 pHouseTier // +5
pointerHouseData = 0x1FE01 // +195 pHouseData // +195
pointerBookshelfData = 0x22298 // +5576 pBookshelfData // +5576
// Gallery data also exists at 0x21578, is this the contest submission? pGalleryData // +1748
pointerGalleryData = 0x22320 // +1748 pToreData // +240
pointerToreData = 0x1FCB4 // +240 pGardenData // +68
pointerGardenData = 0x22C58 // +68 pWeaponType // +1
pointerWeaponType = 0x1F715 // +1 pWeaponID // +2
pointerWeaponID = 0x1F60A // +2 pHRP // +2
pointerHRP = 0x1FDF6 // +2 pGRP // +4
pointerGRP = 0x1FDFC // +4 pKQF // +8
pointerKQF = 0x23D20 // +8
) )
type CharacterSaveData struct { type CharacterSaveData struct {
CharID uint32 CharID uint32
Name string Name string
IsNewCharacter bool IsNewCharacter bool
Pointers map[SavePointer]int
Gender bool Gender bool
RP uint16 RP uint16
@@ -51,6 +54,39 @@ type CharacterSaveData struct {
decompSave []byte decompSave []byte
} }
func getPointers() map[SavePointer]int {
pointers := map[SavePointer]int{pGender: 81}
switch _config.ErupeConfig.RealClientMode {
case _config.ZZ:
pointers[pWeaponID] = 128522
pointers[pWeaponType] = 128789
pointers[pHouseTier] = 129900
pointers[pToreData] = 130228
pointers[pHRP] = 130550
pointers[pGRP] = 130556
pointers[pHouseData] = 130561
pointers[pBookshelfData] = 139928
pointers[pGalleryData] = 140064
pointers[pGardenData] = 142424
pointers[pRP] = 142614
pointers[pKQF] = 146720
case _config.Z2, _config.Z1, _config.G101, _config.G10:
pointers[pWeaponID] = 92522
pointers[pWeaponType] = 92789
pointers[pHouseTier] = 93900
pointers[pToreData] = 94228
pointers[pHRP] = 94550
pointers[pGRP] = 94556
pointers[pHouseData] = 94561
pointers[pBookshelfData] = 103928
pointers[pGalleryData] = 104064
pointers[pGardenData] = 106424
pointers[pRP] = 106614
pointers[pKQF] = 110720
}
return pointers
}
func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) { func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error) {
result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID) result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)
if err != nil { if err != nil {
@@ -64,7 +100,9 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error)
return nil, err return nil, err
} }
saveData := &CharacterSaveData{} saveData := &CharacterSaveData{
Pointers: getPointers(),
}
err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name) err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name)
if err != nil { if err != nil {
s.logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID)) s.logger.Error("Failed to scan savedata", zap.Error(err), zap.Uint32("charID", charID))
@@ -95,13 +133,18 @@ func (save *CharacterSaveData) Save(s *Session) {
save.updateSaveDataWithStruct() save.updateSaveDataWithStruct()
if _config.ErupeConfig.RealClientMode >= _config.G1 {
err := save.Compress() err := save.Compress()
if err != nil { if err != nil {
s.logger.Error("Failed to compress savedata", zap.Error(err)) s.logger.Error("Failed to compress savedata", zap.Error(err))
return return
} }
} else {
// Saves were not compressed
save.compSave = save.decompSave
}
_, 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 _, 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) `, save.compSave, save.HRP, save.GR, save.Gender, save.WeaponType, save.WeaponID, save.CharID)
if err != nil { if err != nil {
s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID)) s.logger.Error("Failed to update savedata", zap.Error(err), zap.Uint32("charID", save.CharID))
@@ -133,38 +176,42 @@ func (save *CharacterSaveData) Decompress() error {
func (save *CharacterSaveData) updateSaveDataWithStruct() { func (save *CharacterSaveData) updateSaveDataWithStruct() {
rpBytes := make([]byte, 2) rpBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(rpBytes, save.RP) binary.LittleEndian.PutUint16(rpBytes, save.RP)
copy(save.decompSave[pointerRP:pointerRP+2], rpBytes) if _config.ErupeConfig.RealClientMode >= _config.G10 {
copy(save.decompSave[pointerKQF:pointerKQF+8], save.KQF) copy(save.decompSave[save.Pointers[pRP]:save.Pointers[pRP]+2], rpBytes)
copy(save.decompSave[save.Pointers[pKQF]:save.Pointers[pKQF]+8], save.KQF)
}
} }
// This will update the save struct with the values stored in the character save // This will update the save struct with the values stored in the character save
func (save *CharacterSaveData) updateStructWithSaveData() { func (save *CharacterSaveData) updateStructWithSaveData() {
save.Name = stringsupport.SJISToUTF8(bfutil.UpToNull(save.decompSave[88:100])) save.Name = stringsupport.SJISToUTF8(bfutil.UpToNull(save.decompSave[88:100]))
if save.decompSave[pointerGender] == 1 { if save.decompSave[save.Pointers[pGender]] == 1 {
save.Gender = true save.Gender = true
} else { } else {
save.Gender = false save.Gender = false
} }
if !save.IsNewCharacter { if !save.IsNewCharacter {
save.RP = binary.LittleEndian.Uint16(save.decompSave[pointerRP : pointerRP+2]) if _config.ErupeConfig.RealClientMode >= _config.G10 {
save.HouseTier = save.decompSave[pointerHouseTier : pointerHouseTier+5] save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+2])
save.HouseData = save.decompSave[pointerHouseData : pointerHouseData+195] save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+5]
save.BookshelfData = save.decompSave[pointerBookshelfData : pointerBookshelfData+5576] save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+195]
save.GalleryData = save.decompSave[pointerGalleryData : pointerGalleryData+1748] save.BookshelfData = save.decompSave[save.Pointers[pBookshelfData] : save.Pointers[pBookshelfData]+5576]
save.ToreData = save.decompSave[pointerToreData : pointerToreData+240] save.GalleryData = save.decompSave[save.Pointers[pGalleryData] : save.Pointers[pGalleryData]+1748]
save.GardenData = save.decompSave[pointerGardenData : pointerGardenData+68] save.ToreData = save.decompSave[save.Pointers[pToreData] : save.Pointers[pToreData]+240]
save.WeaponType = save.decompSave[pointerWeaponType] save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68]
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2]) save.WeaponType = save.decompSave[save.Pointers[pWeaponType]]
save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2]) save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2])
save.HRP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHRP] : save.Pointers[pHRP]+2])
if save.HRP == uint16(999) { if save.HRP == uint16(999) {
save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4])) save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[save.Pointers[pGRP] : save.Pointers[pGRP]+4]))
}
save.KQF = save.decompSave[save.Pointers[pKQF] : save.Pointers[pKQF]+8]
} }
save.KQF = save.decompSave[pointerKQF : pointerKQF+8]
} }
return return
} }
func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSexChanger) pkt := p.(*mhfpacket.MsgMhfSexChanger)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }

File diff suppressed because one or more lines are too long

View File

@@ -3,6 +3,7 @@ package channelserver
import ( import (
"encoding/hex" "encoding/hex"
"erupe-ce/common/stringsupport" "erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"time" "time"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
@@ -71,7 +72,11 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
var timestamps []uint32 var timestamps []uint32
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DivaEvent >= 0 { if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DivaEvent >= 0 {
if s.server.erupeConfig.DevModeOptions.DivaEvent == 0 { if s.server.erupeConfig.DevModeOptions.DivaEvent == 0 {
if s.server.erupeConfig.RealClientMode <= _config.Z1 {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 32))
} else {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36)) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
}
return return
} }
timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.DivaEvent), true) timestamps = generateDivaTimestamps(s, uint32(s.server.erupeConfig.DevModeOptions.DivaEvent), true)
@@ -79,9 +84,11 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
timestamps = generateDivaTimestamps(s, start, false) timestamps = generateDivaTimestamps(s, start, false)
} }
if s.server.erupeConfig.RealClientMode <= _config.Z1 {
bf.WriteUint32(id) bf.WriteUint32(id)
for _, timestamp := range timestamps { }
bf.WriteUint32(timestamp) for i := range timestamps {
bf.WriteUint32(timestamps[i])
} }
bf.WriteUint16(0x19) // Unk 00011001 bf.WriteUint16(0x19) // Unk 00011001

View File

@@ -2,6 +2,7 @@ package channelserver
import ( import (
"erupe-ce/common/token" "erupe-ce/common/token"
_config "erupe-ce/config"
"math" "math"
"time" "time"
@@ -47,9 +48,41 @@ func handleMsgMhfReleaseEvent(s *Session, p mhfpacket.MHFPacket) {
}) })
} }
type Event struct {
Unk0 uint16
Unk1 uint16
Unk2 uint16
Unk3 uint16
Unk4 uint16
Unk5 uint32
Unk6 uint32
Unk7 []uint16
}
func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfEnumerateEvent(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateEvent) pkt := p.(*mhfpacket.MsgMhfEnumerateEvent)
stubEnumerateNoResults(s, pkt.AckHandle) bf := byteframe.NewByteFrame()
events := []Event{}
bf.WriteUint8(uint8(len(events)))
for _, event := range events {
bf.WriteUint16(event.Unk0)
bf.WriteUint16(event.Unk1)
bf.WriteUint16(event.Unk2)
bf.WriteUint16(event.Unk3)
bf.WriteUint16(event.Unk4)
bf.WriteUint32(event.Unk5)
bf.WriteUint32(event.Unk6)
if event.Unk0 == 2 {
bf.WriteUint8(uint8(len(event.Unk7)))
for _, u := range event.Unk7 {
bf.WriteUint16(u)
}
}
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
type activeFeature struct { type activeFeature struct {
@@ -90,14 +123,24 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
} }
func generateFeatureWeapons(count int) activeFeature { func generateFeatureWeapons(count int) activeFeature {
if count > 14 { max := 14
count = 14 if _config.ErupeConfig.RealClientMode < _config.ZZ {
max = 13
}
if _config.ErupeConfig.RealClientMode < _config.G10 {
max = 12
}
if _config.ErupeConfig.RealClientMode < _config.GG {
max = 11
}
if count > max {
count = max
} }
nums := make([]int, 0) nums := make([]int, 0)
var result int var result int
for len(nums) < count { for len(nums) < count {
rng := token.RNG() rng := token.RNG()
num := rng.Intn(14) num := rng.Intn(max)
exist := false exist := false
for _, v := range nums { for _, v := range nums {
if v == num { if v == num {

View File

@@ -1,7 +1,6 @@
package channelserver package channelserver
import ( import (
"encoding/hex"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring" ps "erupe-ce/common/pascalstring"
"erupe-ce/common/token" "erupe-ce/common/token"
@@ -140,13 +139,26 @@ func generateFestaTimestamps(s *Session, start uint32, debug bool) []uint32 {
return timestamps return timestamps
} }
type Trial struct { type FestaTrial struct {
ID uint32 `db:"id"` ID uint32 `db:"id"`
Objective uint8 `db:"objective"` Objective uint16 `db:"objective"`
GoalID uint32 `db:"goal_id"` GoalID uint32 `db:"goal_id"`
TimesReq uint16 `db:"times_req"` TimesReq uint16 `db:"times_req"`
Locale uint16 `db:"locale_req"` Locale uint16 `db:"locale_req"`
Reward uint16 `db:"reward"` Reward uint16 `db:"reward"`
Monopoly uint16
Unk uint16
}
type FestaReward struct {
Unk0 uint8
Unk1 uint8
ItemType uint16
Quantity uint16
ItemID uint16
Unk5 uint16
Unk6 uint16
Unk7 uint8
} }
func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
@@ -190,36 +202,71 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(blueSouls) bf.WriteUint32(blueSouls)
bf.WriteUint32(redSouls) bf.WriteUint32(redSouls)
var trials []FestaTrial
var trial FestaTrial
rows, _ = s.server.db.Queryx("SELECT * FROM festa_trials") rows, _ = s.server.db.Queryx("SELECT * FROM festa_trials")
trialData := byteframe.NewByteFrame()
var count uint16
for rows.Next() { for rows.Next() {
trial := &Trial{}
err := rows.StructScan(&trial) err := rows.StructScan(&trial)
if err != nil { if err != nil {
continue continue
} }
count++ trials = append(trials, trial)
trialData.WriteUint32(trial.ID) }
trialData.WriteUint8(0) // Unk bf.WriteUint16(uint16(len(trials)))
trialData.WriteUint8(trial.Objective) for _, trial := range trials {
trialData.WriteUint32(trial.GoalID) bf.WriteUint32(trial.ID)
trialData.WriteUint16(trial.TimesReq) bf.WriteUint16(trial.Objective)
trialData.WriteUint16(trial.Locale) bf.WriteUint32(trial.GoalID)
trialData.WriteUint16(trial.Reward) bf.WriteUint16(trial.TimesReq)
trialData.WriteUint8(0xFF) // Unk bf.WriteUint16(trial.Locale)
trialData.WriteUint8(0xFF) // MonopolyState bf.WriteUint16(trial.Reward)
trialData.WriteUint16(0) // Unk trial.Monopoly = 0xFFFF // NYI
bf.WriteUint16(trial.Monopoly)
bf.WriteUint16(trial.Unk)
} }
bf.WriteUint16(count)
bf.WriteBytes(trialData.Data())
// Static bonus rewards // The Winner and Loser Armor IDs are missing
rewards, _ := hex.DecodeString("001901000007015E05F000000000000100000703E81B6300000000010100000C03E8000000000000000100000D0000000000000000000100000100000000000000000002000007015E05F000000000000200000703E81B6300000000010200000C03E8000000000000000200000D0000000000000000000200000400000000000000000003000007015E05F000000000000300000703E81B6300000000010300000C03E8000000000000000300000D0000000000000000000300000100000000000000000004000007015E05F000000000000400000703E81B6300000000010400000C03E8000000000000000400000D0000000000000000000400000400000000000000000005000007015E05F000000000000500000703E81B6300000000010500000C03E8000000000000000500000D00000000000000000005000001000000000000000000") rewards := []FestaReward{
bf.WriteBytes(rewards) {1, 0, 7, 350, 1520, 0, 0, 0},
{1, 0, 7, 1000, 7011, 0, 0, 1},
{1, 0, 12, 1000, 0, 0, 0, 0},
{1, 0, 13, 0, 0, 0, 0, 0},
//{1, 0, 1, 0, 0, 0, 0, 0},
{2, 0, 7, 350, 1520, 0, 0, 0},
{2, 0, 7, 1000, 7011, 0, 0, 1},
{2, 0, 12, 1000, 0, 0, 0, 0},
{2, 0, 13, 0, 0, 0, 0, 0},
//{2, 0, 4, 0, 0, 0, 0, 0},
{3, 0, 7, 350, 1520, 0, 0, 0},
{3, 0, 7, 1000, 7011, 0, 0, 1},
{3, 0, 12, 1000, 0, 0, 0, 0},
{3, 0, 13, 0, 0, 0, 0, 0},
//{3, 0, 1, 0, 0, 0, 0, 0},
{4, 0, 7, 350, 1520, 0, 0, 0},
{4, 0, 7, 1000, 7011, 0, 0, 1},
{4, 0, 12, 1000, 0, 0, 0, 0},
{4, 0, 13, 0, 0, 0, 0, 0},
//{4, 0, 4, 0, 0, 0, 0, 0},
{5, 0, 7, 350, 1520, 0, 0, 0},
{5, 0, 7, 1000, 7011, 0, 0, 1},
{5, 0, 12, 1000, 0, 0, 0, 0},
{5, 0, 13, 0, 0, 0, 0, 0},
//{5, 0, 1, 0, 0, 0, 0, 0},
}
bf.WriteUint16(uint16(len(rewards)))
for _, reward := range rewards {
bf.WriteUint8(reward.Unk0)
bf.WriteUint8(reward.Unk1)
bf.WriteUint16(reward.ItemType)
bf.WriteUint16(reward.Quantity)
bf.WriteUint16(reward.ItemID)
bf.WriteUint16(reward.Unk5)
bf.WriteUint16(reward.Unk6)
bf.WriteUint8(reward.Unk7)
}
bf.WriteUint16(0x0001) bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MaximumFP)
bf.WriteUint32(0xD4C001F4) bf.WriteUint16(500)
categoryWinners := uint16(0) // NYI categoryWinners := uint16(0) // NYI
bf.WriteUint16(categoryWinners) bf.WriteUint16(categoryWinners)
@@ -239,8 +286,18 @@ func handleMsgMhfInfoFesta(s *Session, p mhfpacket.MHFPacket) {
ps.Uint8(bf, "", true) // Guild Name ps.Uint8(bf, "", true) // Guild Name
} }
d, _ := hex.DecodeString("000000000000000100001388000007D0000003E800000064012C00C8009600640032") // Unknown values
bf.WriteBytes(d) bf.WriteUint32(1)
bf.WriteUint32(5000)
bf.WriteUint32(2000)
bf.WriteUint32(1000)
bf.WriteUint32(100)
bf.WriteUint16(300)
bf.WriteUint16(200)
bf.WriteUint16(150)
bf.WriteUint16(100)
bf.WriteUint16(50)
ps.Uint16(bf, "", false) ps.Uint16(bf, "", false)
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }

View File

@@ -7,6 +7,7 @@ import (
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"errors" "errors"
_config "erupe-ce/config"
"fmt" "fmt"
"math" "math"
"sort" "sort"
@@ -62,7 +63,6 @@ type Guild struct {
Recruiting bool `db:"recruiting"` Recruiting bool `db:"recruiting"`
FestivalColour FestivalColour `db:"festival_colour"` FestivalColour FestivalColour `db:"festival_colour"`
Souls uint32 `db:"souls"` Souls uint32 `db:"souls"`
Rank uint16 `db:"rank"`
AllianceID uint32 `db:"alliance_id"` AllianceID uint32 `db:"alliance_id"`
Icon *GuildIcon `db:"icon"` Icon *GuildIcon `db:"icon"`
@@ -115,6 +115,39 @@ func (gi *GuildIcon) Value() (valuer driver.Value, err error) {
return json.Marshal(gi) return json.Marshal(gi)
} }
func (g *Guild) Rank() uint16 {
rpMap := []uint32{
24, 48, 96, 144, 192, 240, 288, 360, 432,
504, 600, 696, 792, 888, 984, 1080, 1200,
}
if _config.ErupeConfig.RealClientMode <= _config.Z2 {
rpMap = []uint32{
3500, 6000, 8500, 11000, 13500, 16000, 20000, 24000, 28000,
33000, 38000, 43000, 48000, 55000, 70000, 90000, 120000,
}
}
for i, u := range rpMap {
if g.RankRP < u {
if _config.ErupeConfig.RealClientMode <= _config.S6 && i >= 12 {
return 12
} else if _config.ErupeConfig.RealClientMode <= _config.F5 && i >= 13 {
return 13
} else if _config.ErupeConfig.RealClientMode <= _config.G32 && i >= 14 {
return 14
}
return uint16(i)
}
}
if _config.ErupeConfig.RealClientMode <= _config.S6 {
return 12
} else if _config.ErupeConfig.RealClientMode <= _config.F5 {
return 13
} else if _config.ErupeConfig.RealClientMode <= _config.G32 {
return 14
}
return 17
}
const guildInfoSelectQuery = ` const guildInfoSelectQuery = `
SELECT SELECT
g.id, g.id,
@@ -137,14 +170,6 @@ SELECT
recruiting, recruiting,
COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_colour, COALESCE((SELECT team FROM festa_registrations fr WHERE fr.guild_id = g.id), 'none') AS festival_colour,
(SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id) AS souls, (SELECT SUM(souls) FROM guild_characters gc WHERE gc.guild_id = g.id) AS souls,
CASE
WHEN rank_rp <= 48 THEN rank_rp/24
WHEN rank_rp <= 288 THEN rank_rp/48+1
WHEN rank_rp <= 504 THEN rank_rp/72+3
WHEN rank_rp <= 1080 THEN (rank_rp-24)/96+5
WHEN rank_rp < 1200 THEN 16
ELSE 17
END rank,
COALESCE(( COALESCE((
SELECT id FROM guild_alliances ga WHERE SELECT id FROM guild_alliances ga WHERE
ga.parent_id = g.id OR ga.parent_id = g.id OR
@@ -615,13 +640,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateGuild) pkt := p.(*mhfpacket.MsgMhfOperateGuild)
guild, err := GetGuildInfoByID(s, pkt.GuildID) guild, err := GetGuildInfoByID(s, pkt.GuildID)
if err != nil {
return
}
characterGuildInfo, err := GetCharacterGuildData(s, s.charID) characterGuildInfo, err := GetCharacterGuildData(s, s.charID)
if err != nil { if err != nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return return
@@ -630,22 +649,19 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
switch pkt.Action { switch pkt.Action {
case mhfpacket.OPERATE_GUILD_DISBAND: case mhfpacket.OperateGuildDisband:
response := 1
if guild.LeaderCharID != s.charID { if guild.LeaderCharID != s.charID {
s.logger.Warn(fmt.Sprintf("character '%d' is attempting to manage guild '%d' without permission", s.charID, guild.ID)) s.logger.Warn(fmt.Sprintf("character '%d' is attempting to manage guild '%d' without permission", s.charID, guild.ID))
return response = 0
} } else {
err = guild.Disband(s) err = guild.Disband(s)
response := 0x01
if err != nil { if err != nil {
// All successful acks return 0x01, assuming 0x00 is failure response = 0
response = 0x00 }
} }
bf.WriteUint32(uint32(response)) bf.WriteUint32(uint32(response))
case mhfpacket.OPERATE_GUILD_RESIGN: case mhfpacket.OperateGuildResign:
guildMembers, err := GetGuildMembers(s, guild.ID, false) guildMembers, err := GetGuildMembers(s, guild.ID, false)
if err == nil { if err == nil {
sort.Slice(guildMembers[:], func(i, j int) bool { sort.Slice(guildMembers[:], func(i, j int) bool {
@@ -664,25 +680,22 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
} }
guild.Save(s) guild.Save(s)
} }
case mhfpacket.OPERATE_GUILD_APPLY: case mhfpacket.OperateGuildApply:
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil) err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
if err == nil { if err == nil {
bf.WriteUint32(guild.LeaderCharID) bf.WriteUint32(guild.LeaderCharID)
} else {
bf.WriteUint32(0)
} }
case mhfpacket.OPERATE_GUILD_LEAVE: case mhfpacket.OperateGuildLeave:
var err error
if characterGuildInfo.IsApplicant { if characterGuildInfo.IsApplicant {
err = guild.RejectApplication(s, s.charID) err = guild.RejectApplication(s, s.charID)
} else { } else {
err = guild.RemoveCharacter(s, s.charID) err = guild.RemoveCharacter(s, s.charID)
} }
response := 1
response := 0x01
if err != nil { if err != nil {
// All successful acks return 0x01, assuming 0x00 is failure response = 0
response = 0x00
} else { } else {
mail := Mail{ mail := Mail{
RecipientID: s.charID, RecipientID: s.charID,
@@ -692,26 +705,25 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
} }
mail.Send(s, nil) mail.Send(s, nil)
} }
bf.WriteUint32(uint32(response)) bf.WriteUint32(uint32(response))
case mhfpacket.OPERATE_GUILD_DONATE_RANK: case mhfpacket.OperateGuildDonateRank:
bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false)) bf.WriteBytes(handleDonateRP(s, uint16(pkt.Data1.ReadUint32()), guild, false))
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_DENY: case mhfpacket.OperateGuildSetApplicationDeny:
s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID) s.server.db.Exec("UPDATE guilds SET recruiting=false WHERE id=$1", guild.ID)
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW: case mhfpacket.OperateGuildSetApplicationAllow:
s.server.db.Exec("UPDATE guilds SET recruiting=true WHERE id=$1", guild.ID) s.server.db.Exec("UPDATE guilds SET recruiting=true WHERE id=$1", guild.ID)
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_TRUE: case mhfpacket.OperateGuildSetAvoidLeadershipTrue:
handleAvoidLeadershipUpdate(s, pkt, true) handleAvoidLeadershipUpdate(s, pkt, true)
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE: case mhfpacket.OperateGuildSetAvoidLeadershipFalse:
handleAvoidLeadershipUpdate(s, pkt, false) handleAvoidLeadershipUpdate(s, pkt, false)
case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT: case mhfpacket.OperateGuildUpdateComment:
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() { if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return return
} }
guild.Comment = stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes()) guild.Comment = stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())
guild.Save(s) guild.Save(s)
case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO: case mhfpacket.OperateGuildUpdateMotto:
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() { if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return return
@@ -720,27 +732,29 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
guild.SubMotto = pkt.Data1.ReadUint8() guild.SubMotto = pkt.Data1.ReadUint8()
guild.MainMotto = pkt.Data1.ReadUint8() guild.MainMotto = pkt.Data1.ReadUint8()
guild.Save(s) guild.Save(s)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1: case mhfpacket.OperateGuildRenamePugi1:
handleRenamePugi(s, pkt.Data2, guild, 1) handleRenamePugi(s, pkt.Data2, guild, 1)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2: case mhfpacket.OperateGuildRenamePugi2:
handleRenamePugi(s, pkt.Data2, guild, 2) handleRenamePugi(s, pkt.Data2, guild, 2)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3: case mhfpacket.OperateGuildRenamePugi3:
handleRenamePugi(s, pkt.Data2, guild, 3) handleRenamePugi(s, pkt.Data2, guild, 3)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1: case mhfpacket.OperateGuildChangePugi1:
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 1) handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 1)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_2: case mhfpacket.OperateGuildChangePugi2:
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 2) handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 2)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3: case mhfpacket.OperateGuildChangePugi3:
handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 3) handleChangePugi(s, uint8(pkt.Data1.ReadUint32()), guild, 3)
case mhfpacket.OPERATE_GUILD_UNLOCK_OUTFIT: case mhfpacket.OperateGuildUnlockOutfit:
// TODO: This doesn't implement blocking, if someone unlocked the same outfit at the same time // 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) 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.OPERATE_GUILD_DONATE_EVENT: case mhfpacket.OperateGuildDonateRoom:
// TODO: Where does this go?
case mhfpacket.OperateGuildDonateEvent:
quantity := uint16(pkt.Data1.ReadUint32()) quantity := uint16(pkt.Data1.ReadUint32())
bf.WriteBytes(handleDonateRP(s, quantity, guild, true)) bf.WriteBytes(handleDonateRP(s, quantity, guild, true))
// TODO: Move this value onto rp_yesterday and reset to 0... daily? // 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) s.server.db.Exec(`UPDATE guild_characters SET rp_today=rp_today+$1 WHERE character_id=$2`, quantity, s.charID)
case mhfpacket.OPERATE_GUILD_EVENT_EXCHANGE: case mhfpacket.OperateGuildEventExchange:
rp := uint16(pkt.Data1.ReadUint32()) rp := uint16(pkt.Data1.ReadUint32())
var balance uint32 var balance uint32
s.server.db.QueryRow(`UPDATE guilds SET event_rp=event_rp-$1 WHERE id=$2 RETURNING event_rp`, rp, guild.ID).Scan(&balance) s.server.db.QueryRow(`UPDATE guilds SET event_rp=event_rp-$1 WHERE id=$2 RETURNING event_rp`, rp, guild.ID).Scan(&balance)
@@ -922,14 +936,19 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(guild.ID) bf.WriteUint32(guild.ID)
bf.WriteUint32(guild.LeaderCharID) bf.WriteUint32(guild.LeaderCharID)
bf.WriteUint16(guild.Rank) bf.WriteUint16(guild.Rank())
bf.WriteUint16(guild.MemberCount) bf.WriteUint16(guild.MemberCount)
bf.WriteUint8(guild.MainMotto) bf.WriteUint8(guild.MainMotto)
bf.WriteUint8(guild.SubMotto) bf.WriteUint8(guild.SubMotto)
// Unk appears to be static // Unk appears to be static
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) bf.WriteUint8(0)
bf.WriteUint8(0)
bf.WriteUint8(0)
bf.WriteUint8(0)
bf.WriteUint8(0)
bf.WriteUint8(0)
bf.WriteBool(!guild.Recruiting) bf.WriteBool(!guild.Recruiting)
@@ -952,28 +971,39 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(FestivalColourCodes[guild.FestivalColour]) bf.WriteUint8(FestivalColourCodes[guild.FestivalColour])
bf.WriteUint32(guild.RankRP) bf.WriteUint32(guild.RankRP)
bf.WriteBytes(guildLeaderName) bf.WriteBytes(guildLeaderName)
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00}) // Unk bf.WriteUint32(0) // Unk
bf.WriteBool(false) // isReturnGuild bf.WriteBool(false) // isReturnGuild
bf.WriteBool(false) // earnedSpecialHall bf.WriteBool(false) // earnedSpecialHall
bf.WriteBytes([]byte{0x02, 0x02}) // Unk bf.WriteUint8(2)
bf.WriteUint32(guild.EventRP) bf.WriteUint8(2)
bf.WriteUint32(guild.EventRP) // Skipped if last byte is <2?
ps.Uint8(bf, guild.PugiName1, true) ps.Uint8(bf, guild.PugiName1, true)
ps.Uint8(bf, guild.PugiName2, true) ps.Uint8(bf, guild.PugiName2, true)
ps.Uint8(bf, guild.PugiName3, true) ps.Uint8(bf, guild.PugiName3, true)
bf.WriteUint8(guild.PugiOutfit1) bf.WriteUint8(guild.PugiOutfit1)
bf.WriteUint8(guild.PugiOutfit2) bf.WriteUint8(guild.PugiOutfit2)
bf.WriteUint8(guild.PugiOutfit3) bf.WriteUint8(guild.PugiOutfit3)
if s.server.erupeConfig.RealClientMode >= _config.Z1 {
bf.WriteUint8(guild.PugiOutfit1) bf.WriteUint8(guild.PugiOutfit1)
bf.WriteUint8(guild.PugiOutfit2) bf.WriteUint8(guild.PugiOutfit2)
bf.WriteUint8(guild.PugiOutfit3) bf.WriteUint8(guild.PugiOutfit3)
}
bf.WriteUint32(guild.PugiOutfits) bf.WriteUint32(guild.PugiOutfits)
// Unk flags if guild.Rank() >= 3 {
bf.WriteUint8(0x3C) // also seen as 0x32 on JP and 0x64 on TW bf.WriteUint8(40)
} else if guild.Rank() >= 7 {
bf.WriteUint8(50)
} else if guild.Rank() >= 10 {
bf.WriteUint8(60)
} else {
bf.WriteUint8(30)
}
bf.WriteBytes([]byte{ bf.WriteUint32(55000)
0x00, 0x00, 0xD6, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, bf.WriteUint32(0)
}) bf.WriteUint16(0) // Changing Room RP
bf.WriteUint16(0)
if guild.AllianceID > 0 { if guild.AllianceID > 0 {
alliance, err := GetAllianceData(s, guild.AllianceID) alliance, err := GetAllianceData(s, guild.AllianceID)
@@ -983,7 +1013,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(alliance.ID) bf.WriteUint32(alliance.ID)
bf.WriteUint32(uint32(alliance.CreatedAt.Unix())) bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
bf.WriteUint16(alliance.TotalMembers) bf.WriteUint16(alliance.TotalMembers)
bf.WriteUint16(0) // Unk0 bf.WriteUint8(0)
bf.WriteUint8(0)
ps.Uint16(bf, alliance.Name, true) ps.Uint16(bf, alliance.Name, true)
if alliance.SubGuild1ID > 0 { if alliance.SubGuild1ID > 0 {
if alliance.SubGuild2ID > 0 { if alliance.SubGuild2ID > 0 {
@@ -1001,7 +1032,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
} else { } else {
bf.WriteUint16(0) bf.WriteUint16(0)
} }
bf.WriteUint16(alliance.ParentGuild.Rank) bf.WriteUint16(alliance.ParentGuild.Rank())
bf.WriteUint16(alliance.ParentGuild.MemberCount) bf.WriteUint16(alliance.ParentGuild.MemberCount)
ps.Uint16(bf, alliance.ParentGuild.Name, true) ps.Uint16(bf, alliance.ParentGuild.Name, true)
ps.Uint16(bf, alliance.ParentGuild.LeaderName, true) ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
@@ -1013,7 +1044,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
} else { } else {
bf.WriteUint16(0) bf.WriteUint16(0)
} }
bf.WriteUint16(alliance.SubGuild1.Rank) bf.WriteUint16(alliance.SubGuild1.Rank())
bf.WriteUint16(alliance.SubGuild1.MemberCount) bf.WriteUint16(alliance.SubGuild1.MemberCount)
ps.Uint16(bf, alliance.SubGuild1.Name, true) ps.Uint16(bf, alliance.SubGuild1.Name, true)
ps.Uint16(bf, alliance.SubGuild1.LeaderName, true) ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
@@ -1026,7 +1057,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
} else { } else {
bf.WriteUint16(0) bf.WriteUint16(0)
} }
bf.WriteUint16(alliance.SubGuild2.Rank) bf.WriteUint16(alliance.SubGuild2.Rank())
bf.WriteUint16(alliance.SubGuild2.MemberCount) bf.WriteUint16(alliance.SubGuild2.MemberCount)
ps.Uint16(bf, alliance.SubGuild2.Name, true) ps.Uint16(bf, alliance.SubGuild2.Name, true)
ps.Uint16(bf, alliance.SubGuild2.LeaderName, true) ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)
@@ -1043,29 +1074,46 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(uint16(len(applicants))) bf.WriteUint16(uint16(len(applicants)))
for _, applicant := range applicants { for _, applicant := range applicants {
bf.WriteUint32(applicant.CharID) bf.WriteUint32(applicant.CharID)
bf.WriteUint16(0) bf.WriteUint32(0)
bf.WriteUint16(0)
bf.WriteUint16(applicant.HRP) bf.WriteUint16(applicant.HRP)
bf.WriteUint16(applicant.GR) bf.WriteUint16(applicant.GR)
ps.Uint8(bf, applicant.Name, true) ps.Uint8(bf, applicant.Name, true)
} }
} }
bf.WriteUint16(0x0000) // lenAllianceApplications type UnkGuildInfo struct {
Unk0 uint8
Unk1 uint8
Unk2 uint8
}
unkGuildInfo := []UnkGuildInfo{}
bf.WriteUint8(uint8(len(unkGuildInfo)))
for _, info := range unkGuildInfo {
bf.WriteUint8(info.Unk0)
bf.WriteUint8(info.Unk1)
bf.WriteUint8(info.Unk2)
}
/* type AllianceInvite struct {
alliance application format GuildID uint32
uint16 numapplicants (above) LeaderID uint32
Unk0 uint16
uint32 guild id Unk1 uint16
uint32 guild leader id (for mail) Members uint16
uint32 unk (always null in pcap) GuildName string
uint16 member count LeaderName string
uint16 len guild name }
string nullterm guild name allianceInvites := []AllianceInvite{}
uint16 len guild leader name bf.WriteUint8(uint8(len(allianceInvites)))
string nullterm guild leader name for _, invite := range allianceInvites {
*/ bf.WriteUint32(invite.GuildID)
bf.WriteUint32(invite.LeaderID)
bf.WriteUint16(invite.Unk0)
bf.WriteUint16(invite.Unk1)
bf.WriteUint16(invite.Members)
ps.Uint16(bf, invite.GuildName, true)
ps.Uint16(bf, invite.LeaderName, true)
}
if guild.Icon != nil { if guild.Icon != nil {
bf.WriteUint8(uint8(len(guild.Icon.Parts))) bf.WriteUint8(uint8(len(guild.Icon.Parts)))
@@ -1083,7 +1131,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(p.PosY) bf.WriteUint16(p.PosY)
} }
} else { } else {
bf.WriteUint8(0x00) bf.WriteUint8(0)
} }
bf.WriteUint8(0) // Unk bf.WriteUint8(0) // Unk
@@ -1285,7 +1333,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(guild.LeaderCharID) bf.WriteUint32(guild.LeaderCharID)
bf.WriteUint16(guild.MemberCount) bf.WriteUint16(guild.MemberCount)
bf.WriteUint16(0x0000) // Unk bf.WriteUint16(0x0000) // Unk
bf.WriteUint16(guild.Rank) bf.WriteUint16(guild.Rank())
bf.WriteUint32(uint32(guild.CreatedAt.Unix())) bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
ps.Uint8(bf, guild.Name, true) ps.Uint8(bf, guild.Name, true)
ps.Uint8(bf, guild.LeaderName, true) ps.Uint8(bf, guild.LeaderName, true)
@@ -1347,7 +1395,7 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
if guild != nil { if guild != nil {
isApplicant, _ := guild.HasApplicationForCharID(s, s.charID) isApplicant, _ := guild.HasApplicationForCharID(s, s.charID)
if isApplicant { if isApplicant {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2))
return return
} }
} }
@@ -1389,8 +1437,15 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
for _, member := range guildMembers { for _, member := range guildMembers {
bf.WriteUint32(member.CharID) bf.WriteUint32(member.CharID)
bf.WriteUint16(member.HRP) bf.WriteUint16(member.HRP)
if s.server.erupeConfig.RealClientMode > _config.G7 {
bf.WriteUint16(member.GR) bf.WriteUint16(member.GR)
}
if s.server.erupeConfig.RealClientMode < _config.ZZ {
// Magnet Spike crash workaround
bf.WriteUint16(0)
} else {
bf.WriteUint16(member.WeaponID) bf.WriteUint16(member.WeaponID)
}
if member.WeaponType == 1 || member.WeaponType == 5 || member.WeaponType == 10 { // If weapon is ranged if member.WeaponType == 1 || member.WeaponType == 5 || member.WeaponType == 10 { // If weapon is ranged
bf.WriteUint8(7) bf.WriteUint8(7)
} else { } else {
@@ -1450,7 +1505,6 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGuildManageRight) pkt := p.(*mhfpacket.MsgMhfGetGuildManageRight)
guild, err := GetGuildInfoByCharacterId(s, s.charID) guild, err := GetGuildInfoByCharacterId(s, s.charID)
if guild == nil && s.prevGuildID != 0 { if guild == nil && s.prevGuildID != 0 {
guild, err = GetGuildInfoByID(s, s.prevGuildID) guild, err = GetGuildInfoByID(s, s.prevGuildID)
s.prevGuildID = 0 s.prevGuildID = 0
@@ -1460,31 +1514,14 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
} }
} }
if err != nil {
s.logger.Warn("failed to respond to manage rights message")
return
} else if guild == nil {
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint16(0x00) // Unk bf.WriteUint32(uint32(guild.MemberCount))
bf.WriteUint16(0x00) // Member count
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
return
}
bf := byteframe.NewByteFrame()
bf.WriteUint16(0x00) // Unk
bf.WriteUint16(guild.MemberCount)
members, _ := GetGuildMembers(s, guild.ID, false) members, _ := GetGuildMembers(s, guild.ID, false)
for _, member := range members { for _, member := range members {
bf.WriteUint32(member.CharID) bf.WriteUint32(member.CharID)
bf.WriteBool(member.Recruiter) bf.WriteBool(member.Recruiter)
bf.WriteBytes(make([]byte, 3)) bf.WriteBytes(make([]byte, 3))
} }
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }

View File

@@ -209,14 +209,14 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
} }
bf.WriteUint32(alliance.ParentGuildID) bf.WriteUint32(alliance.ParentGuildID)
bf.WriteUint32(alliance.ParentGuild.LeaderCharID) bf.WriteUint32(alliance.ParentGuild.LeaderCharID)
bf.WriteUint16(alliance.ParentGuild.Rank) bf.WriteUint16(alliance.ParentGuild.Rank())
bf.WriteUint16(alliance.ParentGuild.MemberCount) bf.WriteUint16(alliance.ParentGuild.MemberCount)
ps.Uint16(bf, alliance.ParentGuild.Name, true) ps.Uint16(bf, alliance.ParentGuild.Name, true)
ps.Uint16(bf, alliance.ParentGuild.LeaderName, true) ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
if alliance.SubGuild1ID > 0 { if alliance.SubGuild1ID > 0 {
bf.WriteUint32(alliance.SubGuild1ID) bf.WriteUint32(alliance.SubGuild1ID)
bf.WriteUint32(alliance.SubGuild1.LeaderCharID) bf.WriteUint32(alliance.SubGuild1.LeaderCharID)
bf.WriteUint16(alliance.SubGuild1.Rank) bf.WriteUint16(alliance.SubGuild1.Rank())
bf.WriteUint16(alliance.SubGuild1.MemberCount) bf.WriteUint16(alliance.SubGuild1.MemberCount)
ps.Uint16(bf, alliance.SubGuild1.Name, true) ps.Uint16(bf, alliance.SubGuild1.Name, true)
ps.Uint16(bf, alliance.SubGuild1.LeaderName, true) ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
@@ -224,7 +224,7 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
if alliance.SubGuild2ID > 0 { if alliance.SubGuild2ID > 0 {
bf.WriteUint32(alliance.SubGuild2ID) bf.WriteUint32(alliance.SubGuild2ID)
bf.WriteUint32(alliance.SubGuild2.LeaderCharID) bf.WriteUint32(alliance.SubGuild2.LeaderCharID)
bf.WriteUint16(alliance.SubGuild2.Rank) bf.WriteUint16(alliance.SubGuild2.Rank())
bf.WriteUint16(alliance.SubGuild2.MemberCount) bf.WriteUint16(alliance.SubGuild2.MemberCount)
ps.Uint16(bf, alliance.SubGuild2.Name, true) ps.Uint16(bf, alliance.SubGuild2.Name, true)
ps.Uint16(bf, alliance.SubGuild2.LeaderName, true) ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)

View File

@@ -4,6 +4,7 @@ import (
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring" ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport" "erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"fmt" "fmt"
"go.uber.org/zap" "go.uber.org/zap"
@@ -249,6 +250,9 @@ func handleMsgMhfLoadDecoMyset(s *Session, p mhfpacket.MHFPacket) {
s.logger.Error("Failed to load decomyset", zap.Error(err)) s.logger.Error("Failed to load decomyset", zap.Error(err))
} }
if len(data) == 0 { if len(data) == 0 {
if s.server.erupeConfig.RealClientMode <= _config.G7 {
data = []byte{0x00, 0x00}
}
data = []byte{0x01, 0x00} data = []byte{0x01, 0x00}
} }
doAckBufSucceed(s, pkt.AckHandle, data) doAckBufSucceed(s, pkt.AckHandle, data)

View File

@@ -3,6 +3,7 @@ package channelserver
import ( import (
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport" "erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/deltacomp" "erupe-ce/server/channelserver/compression/deltacomp"
"erupe-ce/server/channelserver/compression/nullcomp" "erupe-ce/server/channelserver/compression/nullcomp"
@@ -56,11 +57,15 @@ func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadHunterNavi) pkt := p.(*mhfpacket.MsgMhfLoadHunterNavi)
naviLength := 552
if s.server.erupeConfig.RealClientMode <= _config.G7 {
naviLength = 280
}
var data []byte var data []byte
err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data) err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data)
if len(data) == 0 { if len(data) == 0 {
s.logger.Error("Failed to load hunternavi", zap.Error(err)) s.logger.Error("Failed to load hunternavi", zap.Error(err))
data = make([]byte, 0x226) data = make([]byte, naviLength)
} }
doAckBufSucceed(s, pkt.AckHandle, data) doAckBufSucceed(s, pkt.AckHandle, data)
} }
@@ -68,6 +73,10 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi) pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi)
if pkt.IsDataDiff { if pkt.IsDataDiff {
naviLength := 552
if s.server.erupeConfig.RealClientMode <= _config.G7 {
naviLength = 280
}
var data []byte var data []byte
// Load existing save // Load existing save
err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data) err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data)
@@ -78,7 +87,7 @@ func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
// Check if we actually had any hunternavi data, using a blank buffer if not. // Check if we actually had any hunternavi data, using a blank buffer if not.
// This is requried as the client will try to send a diff after character creation without a prior MsgMhfSaveHunterNavi packet. // This is requried as the client will try to send a diff after character creation without a prior MsgMhfSaveHunterNavi packet.
if len(data) == 0 { if len(data) == 0 {
data = make([]byte, 0x226) data = make([]byte, naviLength)
} }
// Perform diff and compress it to write back to db // Perform diff and compress it to write back to db
@@ -222,8 +231,8 @@ func handleMsgMhfReadMercenaryM(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfContractMercenary(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfContractMercenary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfContractMercenary) pkt := p.(*mhfpacket.MsgMhfContractMercenary)
switch pkt.Op { switch pkt.Op {
case 0: case 0: // Form loan
s.server.db.Exec("UPDATE characters SET pact_id=$1 WHERE id=$2", pkt.PactMercID, s.charID) s.server.db.Exec("UPDATE characters SET pact_id=$1 WHERE id=$2", pkt.PactMercID, pkt.CID)
case 1: // Cancel lend case 1: // Cancel lend
s.server.db.Exec("UPDATE characters SET pact_id=0 WHERE id=$1", s.charID) s.server.db.Exec("UPDATE characters SET pact_id=0 WHERE id=$1", s.charID)
case 2: // Cancel loan case 2: // Cancel loan

View File

@@ -78,7 +78,8 @@ func handleMsgSysRotateObject(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysDuplicateObject(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysDuplicateObject(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysSetObjectBinary) _ = p.(*mhfpacket.MsgSysSetObjectBinary)
/* This causes issues with PS3 as this actually sends with endiness!
for _, session := range s.server.sessions { for _, session := range s.server.sessions {
if session.charID == s.charID { if session.charID == s.charID {
s.server.userBinaryPartsLock.Lock() s.server.userBinaryPartsLock.Lock()
@@ -91,6 +92,7 @@ func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {
s.server.BroadcastMHF(msg, s) s.server.BroadcastMHF(msg, s)
} }
} }
*/
} }
func handleMsgSysGetObjectBinary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysGetObjectBinary(s *Session, p mhfpacket.MHFPacket) {}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,106 @@
package channelserver
import (
"erupe-ce/common/byteframe"
"erupe-ce/network/mhfpacket"
)
func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetBreakSeibatuLevelReward)
bf := byteframe.NewByteFrame()
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
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
weeklySeibatuRankingRewards := []WeeklySeibatuRankingReward{
{0, 0, 0, 0, 0, 0},
}
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)
}
func handleMsgMhfGetFixedSeibatuRankingTable(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetFixedSeibatuRankingTable)
bf := byteframe.NewByteFrame()
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteBytes(make([]byte, 32))
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfReadBeatLevel(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadBeatLevel)
// This response is fixed and will never change on JP,
// but I've left it dynamic for possible other client differences.
resp := byteframe.NewByteFrame()
for i := 0; i < int(pkt.ValidIDCount); i++ {
resp.WriteUint32(pkt.IDs[i])
resp.WriteUint32(1)
resp.WriteUint32(1)
resp.WriteUint32(1)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfReadLastWeekBeatRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadLastWeekBeatRanking)
bf := byteframe.NewByteFrame()
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateBeatLevel(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateBeatLevel)
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfReadBeatLevelAllRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadBeatLevelAllRanking)
bf := byteframe.NewByteFrame()
bf.WriteUint32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
for i := 0; i < 100; i++ {
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteBytes(make([]byte, 32))
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfReadBeatLevelMyRanking(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfReadBeatLevelMyRanking)
bf := byteframe.NewByteFrame()
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -7,54 +7,124 @@ import (
"time" "time"
) )
type TournamentInfo0 struct {
ID uint32
MaxPlayers uint32
CurrentPlayers uint32
Unk1 uint16
TextColor uint16
Unk2 uint32
Time1 time.Time
Time2 time.Time
Time3 time.Time
Time4 time.Time
Time5 time.Time
Time6 time.Time
Unk3 uint8
Unk4 uint8
MinHR uint32
MaxHR uint32
Unk5 string
Unk6 string
}
type TournamentInfo21 struct {
Unk0 uint32
Unk1 uint32
Unk2 uint32
Unk3 uint8
}
type TournamentInfo22 struct {
Unk0 uint32
Unk1 uint32
Unk2 uint32
Unk3 uint8
Unk4 string
}
func handleMsgMhfInfoTournament(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfInfoTournament(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfInfoTournament) pkt := p.(*mhfpacket.MsgMhfInfoTournament)
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
tournamentInfo0 := []TournamentInfo0{}
tournamentInfo21 := []TournamentInfo21{}
tournamentInfo22 := []TournamentInfo22{}
switch pkt.Unk0 { switch pkt.Unk0 {
case 0: case 0:
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
bf.WriteUint32(0) // Tied to schedule ID?
case 1:
bf.WriteBytes(make([]byte, 21))
ps.Uint8(bf, "", false)
break
bf.WriteUint32(0xACEDCAFE)
bf.WriteUint32(5) // Active schedule?
bf.WriteUint32(1) // Schedule ID?
bf.WriteUint32(32) // Max players
bf.WriteUint32(0) // Registered players
bf.WriteUint16(0)
bf.WriteUint16(2) // Color code for schedule item
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint32(uint32(len(tournamentInfo0)))
bf.WriteUint32(uint32(time.Now().Add(time.Hour * -10).Unix())) for _, tinfo := range tournamentInfo0 {
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix())) bf.WriteUint32(tinfo.ID)
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix())) bf.WriteUint32(tinfo.MaxPlayers)
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix())) bf.WriteUint32(tinfo.CurrentPlayers)
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix())) bf.WriteUint16(tinfo.Unk1)
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix())) bf.WriteUint16(tinfo.TextColor)
bf.WriteUint32(tinfo.Unk2)
bf.WriteBool(true) // Unk bf.WriteUint32(uint32(tinfo.Time1.Unix()))
bf.WriteBool(false) // Cafe-only bf.WriteUint32(uint32(tinfo.Time2.Unix()))
bf.WriteUint32(uint32(tinfo.Time3.Unix()))
bf.WriteUint32(0) // Min HR bf.WriteUint32(uint32(tinfo.Time4.Unix()))
bf.WriteUint32(0) // Max HR bf.WriteUint32(uint32(tinfo.Time5.Unix()))
bf.WriteUint32(uint32(tinfo.Time6.Unix()))
ps.Uint8(bf, "Test", false) bf.WriteUint8(tinfo.Unk3)
bf.WriteUint8(tinfo.Unk4)
// ... bf.WriteUint32(tinfo.MinHR)
bf.WriteUint32(tinfo.MaxHR)
ps.Uint8(bf, tinfo.Unk5, true)
ps.Uint16(bf, tinfo.Unk6, true)
}
case 1:
bf.WriteUint32(uint32(TimeAdjusted().Unix()))
bf.WriteUint32(0) // Registered ID
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint8(0)
bf.WriteUint32(0)
ps.Uint8(bf, "", true)
case 2:
bf.WriteUint32(0)
bf.WriteUint32(uint32(len(tournamentInfo21)))
for _, info := range tournamentInfo21 {
bf.WriteUint32(info.Unk0)
bf.WriteUint32(info.Unk1)
bf.WriteUint32(info.Unk2)
bf.WriteUint8(info.Unk3)
}
bf.WriteUint32(uint32(len(tournamentInfo22)))
for _, info := range tournamentInfo22 {
bf.WriteUint32(info.Unk0)
bf.WriteUint32(info.Unk1)
bf.WriteUint32(info.Unk2)
bf.WriteUint8(info.Unk3)
ps.Uint8(bf, info.Unk4, true)
}
} }
doAckBufSucceed(s, pkt.AckHandle, bf.Data()) doAckBufSucceed(s, pkt.AckHandle, bf.Data())
} }
func handleMsgMhfEntryTournament(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfEntryTournament(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEntryTournament)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfAcquireTournament(s *Session, p mhfpacket.MHFPacket) {} type TournamentReward struct {
Unk0 uint16
Unk1 uint16
Unk2 uint16
}
func handleMsgMhfAcquireTournament(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfAcquireTournament)
rewards := []TournamentReward{}
bf := byteframe.NewByteFrame()
bf.WriteUint8(uint8(len(rewards)))
for _, reward := range rewards {
bf.WriteUint16(reward.Unk0)
bf.WriteUint16(reward.Unk1)
bf.WriteUint16(reward.Unk2)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -1,102 +1,477 @@
package channelserver package channelserver
import ( import (
"encoding/hex" "fmt"
"go.uber.org/zap"
"time"
"erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
) )
type TowerInfoTRP struct {
TR int32
TRP int32
}
type TowerInfoSkill struct {
TSP int32
Unk1 []int16 // 40
}
type TowerInfoHistory struct {
Unk0 []int16 // 5
Unk1 []int16 // 5
}
type TowerInfoLevel struct {
Floors int32
Unk1 int32
Unk2 int32
Unk3 int32
}
func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetTowerInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetTowerInfo) pkt := p.(*mhfpacket.MsgMhfGetTowerInfo)
var data []byte var data []*byteframe.ByteFrame
var err error type TowerInfo struct {
/* TRP []TowerInfoTRP
type: Skill []TowerInfoSkill
1 == TOWER_RANK_POINT, History []TowerInfoHistory
2 == GET_OWN_TOWER_SKILL Level []TowerInfoLevel
3 == GET_OWN_TOWER_LEVEL_V3
4 == TOWER_TOUHA_HISTORY
5 = ?
[] = type
req
resp
01 1d 01 fc 00 09 [00 00 00 01] 00 00 00 02 00 00 00 00
00 12 01 fc 00 09 01 00 00 18 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00
01 1d 01 fc 00 0a [00 00 00 02] 00 00 00 00 00 00 00 00
00 12 01 fc 00 0a 01 00 00 94 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 1d 01 ff 00 0f [00 00 00 04] 00 00 00 00 00 00 00 00
00 12 01 ff 00 0f 01 00 00 24 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
01 1d 01 fc 00 0b [00 00 00 05] 00 00 00 00 00 00 00 00
00 12 01 fc 00 0b 01 00 00 10 0a 21 8e ad 00 00 00 00 00 00 00 00 00 00 00 00
*/
switch pkt.InfoType {
case mhfpacket.TowerInfoTypeTowerRankPoint:
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000000000000000")
case mhfpacket.TowerInfoTypeGetOwnTowerSkill:
//data, err = hex.DecodeString("0A218EAD000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000001C0000000500050000000000020000000000000000000000000000000000030003000000000003000500050000000300030003000300030003000200030001000300020002000300010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
case mhfpacket.TowerInfoTypeGetOwnTowerLevelV3:
panic("No known response values for GetOwnTowerLevelV3")
case mhfpacket.TowerInfoTypeTowerTouhaHistory:
data, err = hex.DecodeString("0A218EAD0000000000000000000000010000000000000000000000000000000000000000")
case mhfpacket.TowerInfoTypeUnk5:
data, err = hex.DecodeString("0A218EAD000000000000000000000000")
} }
towerInfo := TowerInfo{
TRP: []TowerInfoTRP{{0, 0}},
Skill: []TowerInfoSkill{{0, make([]int16, 40)}},
History: []TowerInfoHistory{{make([]int16, 5), make([]int16, 5)}},
Level: []TowerInfoLevel{{0, 0, 0, 0}, {0, 0, 0, 0}},
}
tempSkills := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
err := s.server.db.QueryRow(`SELECT COALESCE(tr, 0), COALESCE(trp, 0), COALESCE(tsp, 0), COALESCE(block1, 0), COALESCE(block2, 0), skills FROM tower WHERE char_id=$1
`, s.charID).Scan(&towerInfo.TRP[0].TR, &towerInfo.TRP[0].TRP, &towerInfo.Skill[0].TSP, &towerInfo.Level[0].Floors, &towerInfo.Level[1].Floors, &tempSkills)
if err != nil { if err != nil {
stubGetNoResults(s, pkt.AckHandle) s.server.db.Exec(`INSERT INTO tower (char_id) VALUES ($1)`, s.charID)
} }
doAckBufSucceed(s, pkt.AckHandle, data)
for i, skill := range stringsupport.CSVElems(tempSkills) {
towerInfo.Skill[0].Unk1[i] = int16(skill)
}
switch pkt.InfoType {
case 1:
for _, trp := range towerInfo.TRP {
bf := byteframe.NewByteFrame()
bf.WriteInt32(trp.TR)
bf.WriteInt32(trp.TRP)
data = append(data, bf)
}
case 2:
for _, skills := range towerInfo.Skill {
bf := byteframe.NewByteFrame()
bf.WriteInt32(skills.TSP)
for i := range skills.Unk1 {
bf.WriteInt16(skills.Unk1[i])
}
data = append(data, bf)
}
case 4:
for _, history := range towerInfo.History {
bf := byteframe.NewByteFrame()
for i := range history.Unk0 {
bf.WriteInt16(history.Unk0[i])
}
for i := range history.Unk1 {
bf.WriteInt16(history.Unk1[i])
}
data = append(data, bf)
}
case 5:
for _, level := range towerInfo.Level {
bf := byteframe.NewByteFrame()
bf.WriteInt32(level.Floors)
bf.WriteInt32(level.Unk1)
bf.WriteInt32(level.Unk2)
bf.WriteInt32(level.Unk3)
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
} }
func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPostTowerInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostTowerInfo) pkt := p.(*mhfpacket.MsgMhfPostTowerInfo)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
s.logger.Debug(
p.Opcode().String(),
zap.Uint32("InfoType", pkt.InfoType),
zap.Uint32("Unk1", pkt.Unk1),
zap.Int32("Skill", pkt.Skill),
zap.Int32("TR", pkt.TR),
zap.Int32("TRP", pkt.TRP),
zap.Int32("Cost", pkt.Cost),
zap.Int32("Unk6", pkt.Unk6),
zap.Int32("Unk7", pkt.Unk7),
zap.Int32("Block1", pkt.Block1),
zap.Int64("Unk9", pkt.Unk9),
)
}
switch pkt.InfoType {
case 2:
skills := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
s.server.db.QueryRow(`SELECT skills FROM tower WHERE char_id=$1`, s.charID).Scan(&skills)
s.server.db.Exec(`UPDATE tower SET skills=$1, tsp=tsp-$2 WHERE char_id=$3`, stringsupport.CSVSetIndex(skills, int(pkt.Skill), stringsupport.CSVGetIndex(skills, int(pkt.Skill))+1), pkt.Cost, s.charID)
case 7:
s.server.db.Exec(`UPDATE tower SET tr=$1, trp=trp+$2, block1=block1+$3 WHERE char_id=$4`, pkt.TR, pkt.TRP, pkt.Block1, s.charID)
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
// Default missions
var tenrouiraiData = []TenrouiraiData{
{1, 1, 80, 0, 2, 2, 1, 1, 2, 2},
{1, 4, 16, 0, 2, 2, 1, 1, 2, 2},
{1, 6, 50, 0, 2, 2, 1, 0, 2, 2},
{1, 4, 12, 50, 2, 2, 1, 1, 2, 2},
{1, 3, 50, 0, 2, 2, 1, 1, 2, 2},
{2, 5, 40000, 0, 2, 2, 1, 0, 2, 2},
{1, 5, 50000, 50, 2, 2, 1, 1, 2, 2},
{2, 1, 60, 0, 2, 2, 1, 1, 2, 2},
{2, 3, 50, 0, 2, 1, 1, 0, 1, 2},
{2, 3, 40, 50, 2, 1, 1, 1, 1, 2},
{2, 4, 12, 0, 2, 1, 1, 1, 1, 2},
{2, 6, 40, 0, 2, 1, 1, 0, 1, 2},
{1, 1, 60, 50, 2, 1, 2, 1, 1, 2},
{1, 5, 50000, 0, 3, 1, 2, 1, 1, 2},
{1, 6, 50, 0, 3, 1, 2, 0, 1, 2},
{1, 4, 16, 50, 3, 1, 2, 1, 1, 2},
{1, 5, 50000, 0, 3, 1, 2, 1, 1, 2},
{2, 3, 40, 0, 3, 1, 2, 0, 1, 2},
{1, 3, 50, 50, 3, 1, 2, 1, 1, 2},
{2, 5, 40000, 0, 3, 1, 2, 1, 1, 1},
{2, 6, 40, 0, 3, 1, 2, 0, 1, 1},
{2, 1, 60, 50, 3, 1, 2, 1, 1, 1},
{2, 6, 50, 0, 3, 1, 2, 1, 1, 1},
{2, 4, 12, 0, 3, 1, 2, 0, 1, 1},
{1, 1, 80, 50, 3, 1, 2, 1, 1, 1},
{1, 5, 40000, 0, 3, 1, 2, 1, 1, 1},
{1, 3, 50, 0, 3, 1, 2, 0, 1, 1},
{1, 4, 16, 50, 3, 1, 0, 1, 1, 1},
{1, 6, 50, 0, 3, 1, 0, 1, 1, 1},
{2, 3, 40, 0, 3, 1, 0, 1, 1, 1},
{1, 1, 80, 50, 3, 1, 0, 0, 1, 1},
{2, 5, 40000, 0, 3, 1, 0, 0, 1, 1},
{2, 6, 40, 0, 3, 1, 0, 0, 1, 1},
}
type TenrouiraiProgress struct {
Page uint8
Mission1 uint16
Mission2 uint16
Mission3 uint16
}
type TenrouiraiReward struct {
Index uint8
Item []uint16 // 5
Quantity []uint8 // 5
}
type TenrouiraiKeyScore struct {
Unk0 uint8
Unk1 int32
}
type TenrouiraiData struct {
Block uint8
Mission uint8
// 1 = Floors climbed
// 2 = Collect antiques
// 3 = Open chests
// 4 = Cats saved
// 5 = TRP acquisition
// 6 = Monster slays
Goal uint16
Cost uint16
Skill1 uint8 // 80
Skill2 uint8 // 40
Skill3 uint8 // 40
Skill4 uint8 // 20
Skill5 uint8 // 40
Skill6 uint8 // 50
}
type TenrouiraiCharScore struct {
Score int32
Name string
}
type TenrouiraiTicket struct {
Unk0 uint8
RP uint32
Unk2 uint32
}
type Tenrouirai struct {
Progress []TenrouiraiProgress
Reward []TenrouiraiReward
KeyScore []TenrouiraiKeyScore
Data []TenrouiraiData
CharScore []TenrouiraiCharScore
Ticket []TenrouiraiTicket
} }
func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetTenrouirai(s *Session, p mhfpacket.MHFPacket) {
// if the game gets bad responses for this it breaks the ability to save
pkt := p.(*mhfpacket.MsgMhfGetTenrouirai) pkt := p.(*mhfpacket.MsgMhfGetTenrouirai)
var data []byte var data []*byteframe.ByteFrame
var err error
if pkt.Unk0 == 1 { tenrouirai := Tenrouirai{
data, err = hex.DecodeString("0A218EAD000000000000000000000001010000000000060010") Progress: []TenrouiraiProgress{{1, 0, 0, 0}},
} else if pkt.Unk2 == 4 { Data: tenrouiraiData,
data, err = hex.DecodeString("0A218EAD0000000000000000000000210101005000000202010102020104001000000202010102020106003200000202010002020104000C003202020101020201030032000002020101020202059C4000000202010002020105C35000320202010102020201003C00000202010102020203003200000201010001020203002800320201010101020204000C00000201010101020206002800000201010001020101003C00320201020101020105C35000000301020101020106003200000301020001020104001000320301020101020105C350000003010201010202030028000003010200010201030032003203010201010202059C4000000301020101010206002800000301020001010201003C00320301020101010206003200000301020101010204000C000003010200010101010050003203010201010101059C40000003010201010101030032000003010200010101040010003203010001010101060032000003010001010102030028000003010001010101010050003203010000010102059C4000000301000001010206002800000301000001010010") Ticket: []TenrouiraiTicket{{0, 0, 0}},
} else {
data = []byte{0x00, 0x00, 0x00, 0x00}
s.logger.Info("GET_TENROUIRAI request for unknown type")
} }
if err != nil {
panic(err) switch pkt.Unk1 {
case 1:
for _, tdata := range tenrouirai.Data {
bf := byteframe.NewByteFrame()
bf.WriteUint8(tdata.Block)
bf.WriteUint8(tdata.Mission)
bf.WriteUint16(tdata.Goal)
bf.WriteUint16(tdata.Cost)
bf.WriteUint8(tdata.Skill1)
bf.WriteUint8(tdata.Skill2)
bf.WriteUint8(tdata.Skill3)
bf.WriteUint8(tdata.Skill4)
bf.WriteUint8(tdata.Skill5)
bf.WriteUint8(tdata.Skill6)
data = append(data, bf)
} }
doAckBufSucceed(s, pkt.AckHandle, data) case 2:
for _, reward := range tenrouirai.Reward {
bf := byteframe.NewByteFrame()
bf.WriteUint8(reward.Index)
bf.WriteUint16(reward.Item[0])
bf.WriteUint16(reward.Item[1])
bf.WriteUint16(reward.Item[2])
bf.WriteUint16(reward.Item[3])
bf.WriteUint16(reward.Item[4])
bf.WriteUint8(reward.Quantity[0])
bf.WriteUint8(reward.Quantity[1])
bf.WriteUint8(reward.Quantity[2])
bf.WriteUint8(reward.Quantity[3])
bf.WriteUint8(reward.Quantity[4])
data = append(data, bf)
}
case 4:
s.server.db.QueryRow(`SELECT tower_mission_page FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&tenrouirai.Progress[0].Page)
s.server.db.QueryRow(`SELECT SUM(tower_mission_1) AS _, SUM(tower_mission_2) AS _, SUM(tower_mission_3) AS _ FROM guild_characters WHERE guild_id=$1
`, pkt.GuildID).Scan(&tenrouirai.Progress[0].Mission1, &tenrouirai.Progress[0].Mission2, &tenrouirai.Progress[0].Mission3)
if tenrouirai.Progress[0].Mission1 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-3].Goal {
tenrouirai.Progress[0].Mission1 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-3].Goal
}
if tenrouirai.Progress[0].Mission2 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-2].Goal {
tenrouirai.Progress[0].Mission2 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-2].Goal
}
if tenrouirai.Progress[0].Mission3 > tenrouiraiData[(tenrouirai.Progress[0].Page*3)-1].Goal {
tenrouirai.Progress[0].Mission3 = tenrouiraiData[(tenrouirai.Progress[0].Page*3)-1].Goal
}
for _, progress := range tenrouirai.Progress {
bf := byteframe.NewByteFrame()
bf.WriteUint8(progress.Page)
bf.WriteUint16(progress.Mission1)
bf.WriteUint16(progress.Mission2)
bf.WriteUint16(progress.Mission3)
data = append(data, bf)
}
case 5:
if pkt.Unk3 > 3 {
pkt.Unk3 %= 3
if pkt.Unk3 == 0 {
pkt.Unk3 = 3
}
}
rows, _ := s.server.db.Query(fmt.Sprintf(`SELECT name, tower_mission_%d FROM guild_characters gc INNER JOIN characters c ON gc.character_id = c.id WHERE guild_id=$1 AND tower_mission_%d IS NOT NULL ORDER BY tower_mission_%d DESC`, pkt.Unk3, pkt.Unk3, pkt.Unk3), pkt.GuildID)
for rows.Next() {
temp := TenrouiraiCharScore{}
rows.Scan(&temp.Name, &temp.Score)
tenrouirai.CharScore = append(tenrouirai.CharScore, temp)
}
for _, charScore := range tenrouirai.CharScore {
bf := byteframe.NewByteFrame()
bf.WriteInt32(charScore.Score)
bf.WriteBytes(stringsupport.PaddedString(charScore.Name, 14, true))
data = append(data, bf)
}
case 6:
s.server.db.QueryRow(`SELECT tower_rp FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&tenrouirai.Ticket[0].RP)
for _, ticket := range tenrouirai.Ticket {
bf := byteframe.NewByteFrame()
bf.WriteUint8(ticket.Unk0)
bf.WriteUint32(ticket.RP)
bf.WriteUint32(ticket.Unk2)
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
} }
func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostTenrouirai) pkt := p.(*mhfpacket.MsgMhfPostTenrouirai)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
}
func handleMsgMhfGetBreakSeibatuLevelReward(s *Session, p mhfpacket.MHFPacket) {} if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
s.logger.Debug(
p.Opcode().String(),
zap.Uint8("Unk0", pkt.Unk0),
zap.Uint8("Op", pkt.Op),
zap.Uint32("GuildID", pkt.GuildID),
zap.Uint8("Unk1", pkt.Unk1),
zap.Uint16("Floors", pkt.Floors),
zap.Uint16("Antiques", pkt.Antiques),
zap.Uint16("Chests", pkt.Chests),
zap.Uint16("Cats", pkt.Cats),
zap.Uint16("TRP", pkt.TRP),
zap.Uint16("Slays", pkt.Slays),
)
}
func handleMsgMhfGetWeeklySeibatuRankingReward(s *Session, p mhfpacket.MHFPacket) { if pkt.Op == 2 {
pkt := p.(*mhfpacket.MsgMhfGetWeeklySeibatuRankingReward) var page, requirement, donated int
s.server.db.QueryRow(`SELECT tower_mission_page, tower_rp FROM guilds WHERE id=$1`, pkt.GuildID).Scan(&page, &donated)
for i := 0; i < (page*3)+1; i++ {
requirement += int(tenrouiraiData[i].Cost)
}
bf := byteframe.NewByteFrame()
sd, err := GetCharacterSaveData(s, s.charID)
if err == nil && sd != nil {
sd.RP -= pkt.DonatedRP
sd.Save(s)
if donated+int(pkt.DonatedRP) >= requirement {
s.server.db.Exec(`UPDATE guilds SET tower_mission_page=tower_mission_page+1 WHERE id=$1`, pkt.GuildID)
s.server.db.Exec(`UPDATE guild_characters SET tower_mission_1=NULL, tower_mission_2=NULL, tower_mission_3=NULL WHERE guild_id=$1`, pkt.GuildID)
pkt.DonatedRP = uint16(requirement - donated)
}
bf.WriteUint32(uint32(pkt.DonatedRP))
s.server.db.Exec(`UPDATE guilds SET tower_rp=tower_rp+$1 WHERE id=$2`, pkt.DonatedRP, pkt.GuildID)
} else {
bf.WriteUint32(0)
}
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
} else {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
} }
func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPresentBox) pkt := p.(*mhfpacket.MsgMhfPresentBox)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) var data []*byteframe.ByteFrame
/*
bf.WriteUint32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
*/
doAckEarthSucceed(s, pkt.AckHandle, data)
}
type GemInfo struct {
Gem uint16
Quantity uint16
}
type GemHistory struct {
Gem uint16
Message uint16
Timestamp time.Time
Sender string
} }
func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfGetGemInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGemInfo) pkt := p.(*mhfpacket.MsgMhfGetGemInfo)
var data []*byteframe.ByteFrame
gemInfo := []GemInfo{}
gemHistory := []GemHistory{}
tempGems := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
s.server.db.QueryRow(`SELECT gems FROM tower WHERE char_id=$1`, s.charID).Scan(&tempGems)
for i, v := range stringsupport.CSVElems(tempGems) {
gemInfo = append(gemInfo, GemInfo{uint16(((i / 5) * 256) + ((i % 5) + 1)), uint16(v)})
}
switch pkt.Unk0 {
case 1:
for _, info := range gemInfo {
bf := byteframe.NewByteFrame()
bf.WriteUint16(info.Gem)
bf.WriteUint16(info.Quantity)
data = append(data, bf)
}
case 2:
for _, history := range gemHistory {
bf := byteframe.NewByteFrame()
bf.WriteUint16(history.Gem)
bf.WriteUint16(history.Message)
bf.WriteUint32(uint32(history.Timestamp.Unix()))
bf.WriteBytes(stringsupport.PaddedString(history.Sender, 14, true))
data = append(data, bf)
}
}
doAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostGemInfo)
if s.server.erupeConfig.DevModeOptions.QuestDebugTools {
s.logger.Debug(
p.Opcode().String(),
zap.Uint32("Op", pkt.Op),
zap.Uint32("Unk1", pkt.Unk1),
zap.Int32("Gem", pkt.Gem),
zap.Int32("Quantity", pkt.Quantity),
zap.Int32("CID", pkt.CID),
zap.Int32("Message", pkt.Message),
zap.Int32("Unk6", pkt.Unk6),
)
}
gems := "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0"
s.server.db.QueryRow(`SELECT gems FROM tower WHERE char_id=$1`, s.charID).Scan(&gems)
switch pkt.Op {
case 1: // Add gem
i := int(((pkt.Gem / 256) * 5) + (((pkt.Gem - ((pkt.Gem / 256) * 256)) - 1) % 5))
s.server.db.Exec(`UPDATE tower SET gems=$1 WHERE char_id=$2`, stringsupport.CSVSetIndex(gems, i, stringsupport.CSVGetIndex(gems, i)+int(pkt.Quantity)), s.charID)
case 2: // Transfer gem
// no way im doing this for now
}
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
} }
func handleMsgMhfPostGemInfo(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfGetNotice(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetNotice)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfPostNotice(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfPostNotice)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}

View File

@@ -22,7 +22,7 @@ type Config struct {
Logger *zap.Logger Logger *zap.Logger
DB *sqlx.DB DB *sqlx.DB
DiscordBot *discordbot.DiscordBot DiscordBot *discordbot.DiscordBot
ErupeConfig *config.Config ErupeConfig *_config.Config
Name string Name string
Enable bool Enable bool
} }
@@ -43,7 +43,7 @@ type Server struct {
Port uint16 Port uint16
logger *zap.Logger logger *zap.Logger
db *sqlx.DB db *sqlx.DB
erupeConfig *config.Config erupeConfig *_config.Config
acceptConns chan net.Conn acceptConns chan net.Conn
deleteConns chan net.Conn deleteConns chan net.Conn
sessions map[net.Conn]*Session sessions map[net.Conn]*Session

View File

@@ -30,7 +30,7 @@ type Stage struct {
// Objects // Objects
objects map[uint32]*Object objects map[uint32]*Object
objectIndex uint8 objectIndex uint16
// Map of session -> charID. // Map of session -> charID.
// These are clients that are CURRENTLY in the stage // These are clients that are CURRENTLY in the stage
@@ -56,7 +56,6 @@ func NewStage(ID string) *Stage {
clients: make(map[*Session]uint32), clients: make(map[*Session]uint32),
reservedClientSlots: make(map[uint32]bool), reservedClientSlots: make(map[uint32]bool),
objects: make(map[uint32]*Object), objects: make(map[uint32]*Object),
objectIndex: 0,
rawBinaryData: make(map[stageBinaryKey][]byte), rawBinaryData: make(map[stageBinaryKey][]byte),
maxPlayers: 4, maxPlayers: 4,
} }
@@ -97,16 +96,10 @@ func (s *Stage) isQuest() bool {
} }
func (s *Stage) NextObjectID() uint32 { func (s *Stage) NextObjectID() uint32 {
s.objectIndex = s.objectIndex + 1 s.objectIndex++
// Objects beyond 127 do not duplicate correctly
// Indexes 0 and 127 does not update position correctly
if s.objectIndex == 127 {
s.objectIndex = 1
}
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint8(0) bf.WriteUint16(127)
bf.WriteUint8(s.objectIndex) bf.WriteUint16(s.objectIndex)
bf.WriteUint16(0) bf.Seek(0, 0)
obj := uint32(bf.Data()[3]) | uint32(bf.Data()[2])<<8 | uint32(bf.Data()[1])<<16 | uint32(bf.Data()[0])<<24 return bf.ReadUint32()
return obj
} }

View File

@@ -9,14 +9,14 @@ import (
type DiscordBot struct { type DiscordBot struct {
Session *discordgo.Session Session *discordgo.Session
config *config.Config config *_config.Config
logger *zap.Logger logger *zap.Logger
MainGuild *discordgo.Guild MainGuild *discordgo.Guild
RealtimeChannel *discordgo.Channel RealtimeChannel *discordgo.Channel
} }
type Options struct { type Options struct {
Config *config.Config Config *_config.Config
Logger *zap.Logger Logger *zap.Logger
} }

View File

@@ -18,7 +18,7 @@ import (
type Server struct { type Server struct {
sync.Mutex sync.Mutex
logger *zap.Logger logger *zap.Logger
erupeConfig *config.Config erupeConfig *_config.Config
db *sqlx.DB db *sqlx.DB
listener net.Listener listener net.Listener
isShuttingDown bool isShuttingDown bool
@@ -28,7 +28,7 @@ type Server struct {
type Config struct { type Config struct {
Logger *zap.Logger Logger *zap.Logger
DB *sqlx.DB DB *sqlx.DB
ErupeConfig *config.Config ErupeConfig *_config.Config
} }
// NewServer creates a new Server type. // NewServer creates a new Server type.
@@ -68,7 +68,7 @@ func (s *Server) Shutdown() {
s.listener.Close() s.listener.Close()
} }
//acceptClients handles accepting new clients in a loop. // acceptClients handles accepting new clients in a loop.
func (s *Server) acceptClients() { func (s *Server) acceptClients() {
for { for {
conn, err := s.listener.Accept() conn, err := s.listener.Accept()

View File

@@ -3,13 +3,12 @@ package entranceserver
import ( import (
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
"erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"fmt" "fmt"
"net" "net"
"erupe-ce/common/stringsupport"
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
"erupe-ce/config"
"erupe-ce/server/channelserver" "erupe-ce/server/channelserver"
) )
@@ -19,11 +18,22 @@ var season uint8
// Server Channels // Server Channels
var currentplayers uint16 var currentplayers uint16
func encodeServerInfo(config *config.Config, s *Server, local bool) []byte { func encodeServerInfo(config *_config.Config, s *Server, local bool) []byte {
serverInfos := config.Entrance.Entries serverInfos := config.Entrance.Entries
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
for serverIdx, si := range serverInfos { for serverIdx, si := range serverInfos {
// Prevent MezFes Worlds displaying on Z1
if config.RealClientMode <= _config.Z1 {
if si.Type == 6 {
continue
}
}
if config.RealClientMode <= _config.G6 {
if si.Type == 5 {
continue
}
}
sid := (4096 + serverIdx*256) + 16 sid := (4096 + serverIdx*256) + 16
err := s.db.QueryRow("SELECT season FROM servers WHERE server_id=$1", sid).Scan(&season) err := s.db.QueryRow("SELECT season FROM servers WHERE server_id=$1", sid).Scan(&season)
if err != nil { if err != nil {
@@ -42,12 +52,29 @@ func encodeServerInfo(config *config.Config, s *Server, local bool) []byte {
bf.WriteUint16(uint16(len(si.Channels))) bf.WriteUint16(uint16(len(si.Channels)))
bf.WriteUint8(si.Type) bf.WriteUint8(si.Type)
bf.WriteUint8(season) bf.WriteUint8(season)
if s.erupeConfig.RealClientMode >= _config.G1 {
bf.WriteUint8(si.Recommended) bf.WriteUint8(si.Recommended)
}
if s.erupeConfig.RealClientMode <= _config.F5 {
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false))
} else if s.erupeConfig.RealClientMode <= _config.GG {
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
bf.WriteUint8(uint8(len(combined)))
bf.WriteBytes(combined)
} else {
bf.WriteUint8(0) // Prevents malformed server name bf.WriteUint8(0) // Prevents malformed server name
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...) combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...) combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false)) bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false))
}
if s.erupeConfig.RealClientMode >= _config.GG {
bf.WriteUint32(si.AllowedClientFlags) bf.WriteUint32(si.AllowedClientFlags)
}
for channelIdx, ci := range si.Channels { for channelIdx, ci := range si.Channels {
sid = (4096 + serverIdx*256) + (16 + channelIdx) sid = (4096 + serverIdx*256) + (16 + channelIdx)
@@ -91,16 +118,39 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt
return bf.Data() return bf.Data()
} }
func makeSv2Resp(config *config.Config, s *Server, local bool) []byte { func makeSv2Resp(config *_config.Config, s *Server, local bool) []byte {
serverInfos := config.Entrance.Entries serverInfos := config.Entrance.Entries
// Decrease by the number of MezFes Worlds
var mf int
if config.RealClientMode <= _config.Z1 {
for _, si := range serverInfos {
if si.Type == 6 {
mf++
}
}
}
// and Return Worlds
var ret int
if config.RealClientMode <= _config.G6 {
for _, si := range serverInfos {
if si.Type == 5 {
ret++
}
}
}
rawServerData := encodeServerInfo(config, s, local) rawServerData := encodeServerInfo(config, s, local)
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages { if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages {
fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(rawServerData), hex.Dump(rawServerData)) fmt.Printf("[Server] -> [Client]\nData [%d bytes]:\n%s\n", len(rawServerData), hex.Dump(rawServerData))
} }
respType := "SV2"
if config.RealClientMode <= _config.G32 {
respType = "SVR"
}
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteBytes(makeHeader(rawServerData, "SV2", uint16(len(serverInfos)), 0x00)) bf.WriteBytes(makeHeader(rawServerData, respType, uint16(len(serverInfos)-(mf+ret)), 0x00))
return bf.Data() return bf.Data()
} }

View File

@@ -12,15 +12,9 @@ import (
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
) )
func (s *Server) newUserChara(username string) error { func (s *Server) newUserChara(uid int) error {
var id int
err := s.db.QueryRow("SELECT id FROM users WHERE username = $1", username).Scan(&id)
if err != nil {
return err
}
var numNewChars int var numNewChars int
err = s.db.QueryRow("SELECT COUNT(*) FROM characters WHERE user_id = $1 AND is_new_character = true", id).Scan(&numNewChars) err := s.db.QueryRow("SELECT COUNT(*) FROM characters WHERE user_id = $1 AND is_new_character = true", uid).Scan(&numNewChars)
if err != nil { if err != nil {
return err return err
} }
@@ -35,7 +29,7 @@ func (s *Server) newUserChara(username string) error {
user_id, is_female, is_new_character, name, unk_desc_string, user_id, is_female, is_new_character, name, unk_desc_string,
hrp, gr, weapon_type, last_login) hrp, gr, weapon_type, last_login)
VALUES($1, False, True, '', '', 0, 0, 0, $2)`, VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
id, uid,
uint32(time.Now().Unix()), uint32(time.Now().Unix()),
) )
if err != nil { if err != nil {
@@ -60,19 +54,6 @@ func (s *Server) registerDBAccount(username string, password string) (uint32, er
return 0, err return 0, err
} }
// Create a base new character.
_, err = s.db.Exec(`
INSERT INTO characters (
user_id, is_female, is_new_character, name, unk_desc_string,
hrp, gr, weapon_type, last_login)
VALUES($1, False, True, '', '', 0, 0, 0, $2)`,
uid,
uint32(time.Now().Unix()),
)
if err != nil {
return 0, err
}
return uid, nil return uid, nil
} }
@@ -90,7 +71,7 @@ type character struct {
func (s *Server) getCharactersForUser(uid uint32) ([]character, error) { func (s *Server) getCharactersForUser(uid uint32) ([]character, error) {
characters := make([]character, 0) characters := make([]character, 0)
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 ASC", 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 { if err != nil {
return nil, err return nil, err
} }
@@ -158,9 +139,6 @@ func (s *Server) getFriendsForCharacters(chars []character) []members {
} }
friends = append(friends, charFriends...) friends = append(friends, charFriends...)
} }
if len(friends) > 255 { // Uint8
friends = friends[:255]
}
return friends return friends
} }
@@ -180,15 +158,12 @@ func (s *Server) getGuildmatesForCharacters(chars []character) []members {
if err != nil { if err != nil {
continue continue
} }
for i, _ := range charGuildmates { for i := range charGuildmates {
charGuildmates[i].CID = char.ID charGuildmates[i].CID = char.ID
} }
guildmates = append(guildmates, charGuildmates...) guildmates = append(guildmates, charGuildmates...)
} }
} }
if len(guildmates) > 255 { // Uint8
guildmates = guildmates[:255]
}
return guildmates return guildmates
} }

View File

@@ -4,6 +4,7 @@ import (
"erupe-ce/common/byteframe" "erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring" ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport" "erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"erupe-ce/server/channelserver" "erupe-ce/server/channelserver"
"fmt" "fmt"
"go.uber.org/zap" "go.uber.org/zap"
@@ -13,6 +14,12 @@ import (
func (s *Session) makeSignResponse(uid uint32) []byte { func (s *Session) makeSignResponse(uid uint32) []byte {
// Get the characters from the DB. // Get the characters from the DB.
chars, err := s.server.getCharactersForUser(uid) chars, err := s.server.getCharactersForUser(uid)
if len(chars) == 0 {
err = s.server.newUserChara(uid)
if err == nil {
chars, err = s.server.getCharactersForUser(uid)
}
}
if err != nil { if err != nil {
s.logger.Warn("Error getting characters from DB", zap.Error(err)) s.logger.Warn("Error getting characters from DB", zap.Error(err))
} }
@@ -30,7 +37,7 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
return bf.Data() return bf.Data()
} }
bf.WriteUint8(1) // resp_code bf.WriteUint8(uint8(SIGN_SUCCESS)) // resp_code
if (s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "") || s.client == PS3 { if (s.server.erupeConfig.PatchServerManifest != "" && s.server.erupeConfig.PatchServerFile != "") || s.client == PS3 {
bf.WriteUint8(2) bf.WriteUint8(2)
} else { } else {
@@ -78,15 +85,23 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
bf.WriteBool(true) // Use uint16 GR, no reason not to bf.WriteBool(true) // Use uint16 GR, no reason not to
bf.WriteBytes(stringsupport.PaddedString(char.Name, 16, true)) // Character name bf.WriteBytes(stringsupport.PaddedString(char.Name, 16, true)) // Character name
bf.WriteBytes(stringsupport.PaddedString(char.UnkDescString, 32, false)) // unk str bf.WriteBytes(stringsupport.PaddedString(char.UnkDescString, 32, false)) // unk str
if s.server.erupeConfig.RealClientMode >= _config.G7 {
bf.WriteUint16(char.GR) bf.WriteUint16(char.GR)
bf.WriteUint16(0) // Unk bf.WriteUint8(0) // Unk
bf.WriteUint8(0) // Unk
}
} }
friends := s.server.getFriendsForCharacters(chars) friends := s.server.getFriendsForCharacters(chars)
if len(friends) == 0 { if len(friends) == 0 {
bf.WriteUint8(0) bf.WriteUint8(0)
} else {
if len(friends) > 255 {
bf.WriteUint8(255)
bf.WriteUint16(uint16(len(friends)))
} else { } else {
bf.WriteUint8(uint8(len(friends))) bf.WriteUint8(uint8(len(friends)))
}
for _, friend := range friends { for _, friend := range friends {
bf.WriteUint32(friend.CID) bf.WriteUint32(friend.CID)
bf.WriteUint32(friend.ID) bf.WriteUint32(friend.ID)
@@ -97,8 +112,13 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
guildmates := s.server.getGuildmatesForCharacters(chars) guildmates := s.server.getGuildmatesForCharacters(chars)
if len(guildmates) == 0 { if len(guildmates) == 0 {
bf.WriteUint8(0) bf.WriteUint8(0)
} else {
if len(guildmates) > 255 {
bf.WriteUint8(255)
bf.WriteUint16(uint16(len(guildmates)))
} else { } else {
bf.WriteUint8(uint8(len(guildmates))) bf.WriteUint8(uint8(len(guildmates)))
}
for _, guildmate := range guildmates { for _, guildmate := range guildmates {
bf.WriteUint32(guildmate.CID) bf.WriteUint32(guildmate.CID)
bf.WriteUint32(guildmate.ID) bf.WriteUint32(guildmate.ID)
@@ -107,12 +127,12 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
} }
if s.server.erupeConfig.HideLoginNotice { if s.server.erupeConfig.HideLoginNotice {
bf.WriteUint8(0) bf.WriteBool(false)
} else { } else {
bf.WriteUint8(uint8(len(s.server.erupeConfig.LoginNotices))) bf.WriteBool(true)
for _, notice := range s.server.erupeConfig.LoginNotices { bf.WriteUint8(0)
ps.Uint32(bf, notice, true) bf.WriteUint8(0)
} ps.Uint16(bf, strings.Join(s.server.erupeConfig.LoginNotices[:], "<PAGE>"), true)
} }
bf.WriteUint32(s.server.getLastCID(uid)) bf.WriteUint32(s.server.getLastCID(uid))
@@ -132,35 +152,37 @@ func (s *Session) makeSignResponse(uid uint32) []byte {
bf.WriteUint16(0x4E20) bf.WriteUint16(0x4E20)
ps.Uint16(bf, "", false) // unk ipv4 ps.Uint16(bf, "", false) // unk ipv4
bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix())) bf.WriteUint32(uint32(s.server.getReturnExpiry(uid).Unix()))
bf.WriteUint32(0x00000000) bf.WriteUint32(0)
bf.WriteUint32(0x0A5197DF) // unk id
mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent
alt := s.server.erupeConfig.DevModeOptions.MezFesAlt alt := s.server.erupeConfig.DevModeOptions.MezFesAlt
if mezfes { if mezfes {
// We can just use the start timestamp as the event ID
bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix()))
// Start time // Start time
bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix())) bf.WriteUint32(uint32(channelserver.TimeWeekStart().Unix()))
// End time // End time
bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix())) bf.WriteUint32(uint32(channelserver.TimeWeekNext().Unix()))
bf.WriteUint8(2) // Unk bf.WriteUint8(2) // Unk
bf.WriteUint32(20) // Single tickets bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MezfesSoloTickets)
bf.WriteUint32(10) // Group tickets bf.WriteUint32(s.server.erupeConfig.GameplayOptions.MezfesGroupTickets)
bf.WriteUint8(8) // Stalls open bf.WriteUint8(8) // Stalls open
bf.WriteUint8(0xA) // Unk bf.WriteUint8(10) // Stall Map
bf.WriteUint8(0x3) // Pachinko bf.WriteUint8(3) // Pachinko
bf.WriteUint8(0x6) // Nyanrendo bf.WriteUint8(6) // Nyanrendo
bf.WriteUint8(0x9) // Point stall bf.WriteUint8(9) // Point stall
if alt { if alt {
bf.WriteUint8(0x2) // Tokotoko bf.WriteUint8(2) // Tokotoko Partnya
} else { } else {
bf.WriteUint8(0x4) // Volpakkun bf.WriteUint8(4) // Volpakkun Together
} }
bf.WriteUint8(0x8) // Battle cats bf.WriteUint8(8) // Dokkan Battle Cats
bf.WriteUint8(0x5) // Gook bf.WriteUint8(5) // Goocoo Scoop
bf.WriteUint8(0x7) // Honey bf.WriteUint8(7) // Honey Panic
} else { } else {
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint32(0) bf.WriteUint32(0)
bf.WriteUint32(0)
} }
return bf.Data() return bf.Data()
} }

View File

@@ -62,7 +62,7 @@ func (s *Session) handlePacket(pkt []byte) error {
case "VITASGN:100": case "VITASGN:100":
s.client = VITA s.client = VITA
s.handlePSSGN(bf) s.handlePSSGN(bf)
case "WIIUSGN:100": case "WIIUSGN:100", "WIIUSGN:000":
s.client = WIIU s.client = WIIU
s.handleWIIUSGN(bf) s.handleWIIUSGN(bf)
case "VITACOGLNK:100": case "VITACOGLNK:100":
@@ -124,6 +124,13 @@ func (s *Session) handleWIIUSGN(bf *byteframe.ByteFrame) {
} }
func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) { func (s *Session) handlePSSGN(bf *byteframe.ByteFrame) {
// Prevent reading malformed request
if len(bf.DataFromCurrent()) < 128 {
resp := byteframe.NewByteFrame()
resp.WriteUint8(uint8(SIGN_EABORT))
s.cryptConn.SendPacket(resp.Data())
return
}
_ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255 _ = bf.ReadNullTerminatedBytes() // VITA = 0000000256, PS3 = 0000000255
_ = bf.ReadBytes(2) // VITA = 1, PS3 = ! _ = bf.ReadBytes(2) // VITA = 1, PS3 = !
_ = bf.ReadBytes(82) _ = bf.ReadBytes(82)

View File

@@ -16,14 +16,14 @@ import (
type Config struct { type Config struct {
Logger *zap.Logger Logger *zap.Logger
DB *sqlx.DB DB *sqlx.DB
ErupeConfig *config.Config ErupeConfig *_config.Config
} }
// Server is a MHF sign server. // Server is a MHF sign server.
type Server struct { type Server struct {
sync.Mutex sync.Mutex
logger *zap.Logger logger *zap.Logger
erupeConfig *config.Config erupeConfig *_config.Config
sessions map[int]*Session sessions map[int]*Session
db *sqlx.DB db *sqlx.DB
listener net.Listener listener net.Listener

View File

@@ -18,14 +18,14 @@ import (
type Config struct { type Config struct {
Logger *zap.Logger Logger *zap.Logger
DB *sqlx.DB DB *sqlx.DB
ErupeConfig *config.Config ErupeConfig *_config.Config
} }
// Server is the MHF custom launcher sign server. // Server is the MHF custom launcher sign server.
type Server struct { type Server struct {
sync.Mutex sync.Mutex
logger *zap.Logger logger *zap.Logger
erupeConfig *config.Config erupeConfig *_config.Config
db *sqlx.DB db *sqlx.DB
httpServer *http.Server httpServer *http.Server
isShuttingDown bool isShuttingDown bool