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
@@ -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.
### Requirements
## Requirements
- [Go](https://go.dev/dl/)
- [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).
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.
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
- [Quest and Scenario Binary Files](https://files.catbox.moe/xf0l7w.7z)
- [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
func GetCourseStruct(rights uint32) ([]Course, uint32) {
resp := []Course{{ID: 1}, {ID: 24}}
resp := []Course{{ID: 1}, {ID: 23}, {ID: 24}}
s := Courses()
slices.SortStableFunc(s, func(i, j Course) bool {
return i.ID > j.ID

View File

@@ -2,6 +2,7 @@ package stringsupport
import (
"bytes"
"fmt"
"io"
"strconv"
"strings"
@@ -96,3 +97,23 @@ func CSVElems(csv string) []int {
}
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": "",
"ScreenshotAPIURL": "",
"DeleteOnSaveCorruption": false,
"ClientMode": "ZZ",
"DevMode": true,
"DevModeOptions": {
"AutoCreateAccount": true,
@@ -26,21 +27,39 @@
"MezFesAlt": false,
"DisableTokenCheck": false,
"QuestDebugTools": false,
"EarthStatusOverride": 0,
"EarthIDOverride": 0,
"EarthMonsterOverride": 0,
"SaveDumps": {
"Enabled": true,
"OutputDir": "savedata"
"OutputDir": "save-backups"
}
},
"GameplayOptions": {
"FeaturedWeapons": 1,
"MaximumNP": 100000,
"MaximumRP": 50000,
"MaximumFP": 120000,
"DisableLoginBoost": false,
"DisableBoostTime": false,
"BoostTimeDuration": 120,
"GuildMealDuration": 60,
"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": {
"Enabled": false,

View File

@@ -1,15 +1,70 @@
package config
package _config
import (
"fmt"
"log"
"net"
"os"
"strings"
"time"
"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.
type Config struct {
Host string `mapstructure:"Host"`
@@ -22,6 +77,8 @@ type Config struct {
PatchServerFile string // File patch server override
ScreenshotAPIURL string // Destination for screenshots uploaded to BBS
DeleteOnSaveCorruption bool // Attempts to save corrupted data will flag the save for deletion
ClientMode string
RealClientMode Mode
DevMode bool
DevModeOptions DevModeOptions
@@ -38,20 +95,23 @@ type Config struct {
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
type DevModeOptions struct {
AutoCreateAccount bool // Automatically create accounts if they don't exist
CleanDB bool // Automatically wipes the DB on server reset.
MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds.
LogInboundMessages bool // Log all messages sent to the server
LogOutboundMessages bool // Log all messages sent to the clients
MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
DivaEvent int // Diva Defense event status
FestaEvent int // Hunter's Festa event status
TournamentEvent int // VS Tournament event status
MezFesEvent bool // MezFes status
MezFesAlt bool // Swaps out Volpakkun for Tokotoko
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
QuestDebugTools bool // Enable various quest debug logs
SaveDumps SaveDumpOptions
AutoCreateAccount bool // Automatically create accounts if they don't exist
CleanDB bool // Automatically wipes the DB on server reset.
MaxLauncherHR bool // Sets the HR returned in the launcher to HR7 so that you can join non-beginner worlds.
LogInboundMessages bool // Log all messages sent to the server
LogOutboundMessages bool // Log all messages sent to the clients
MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
DivaEvent int // Diva Defense event status
FestaEvent int // Hunter's Festa event status
TournamentEvent int // VS Tournament event status
MezFesEvent bool // MezFes status
MezFesAlt bool // Swaps out Volpakkun for Tokotoko
DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!)
QuestDebugTools bool // Enable various quest debug logs
EarthStatusOverride int32
EarthIDOverride int32
EarthMonsterOverride int32
SaveDumps SaveDumpOptions
}
type SaveDumpOptions struct {
@@ -61,15 +121,30 @@ type SaveDumpOptions struct {
// GameplayOptions has various gameplay modifiers
type GameplayOptions struct {
FeaturedWeapons int // Number of Active Feature weapons to generate daily
MaximumNP int // Maximum number of NP held by a player
MaximumRP uint16 // Maximum number of RP held by a player
DisableLoginBoost bool // Disables the Login Boost system
DisableBoostTime bool // Disables the daily NetCafe Boost Time
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
BonusQuestAllowance uint32 // Number of Bonus Point Quests to allow daily
DailyQuestAllowance uint32 // Number of Daily Quests to allow daily
FeaturedWeapons int // Number of Active Feature weapons to generate daily
MaximumNP int // Maximum number of NP held by a player
MaximumRP uint16 // Maximum number of RP held by a player
MaximumFP uint32 // Maximum number of FP held by a player
DisableLoginBoost bool // Disables the Login Boost system
DisableBoostTime bool // Disables the daily NetCafe Boost Time
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
BonusQuestAllowance uint32 // Number of Bonus Point 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.
@@ -154,7 +229,6 @@ func init() {
if err != nil {
preventClose(fmt.Sprintf("Failed to load config: %s", err.Error()))
}
}
// getOutboundIP4 gets the preferred outbound ip4 of this machine
@@ -196,6 +270,20 @@ func LoadConfig() (*Config, error) {
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
}

64
main.go
View File

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

View File

@@ -3,6 +3,7 @@ package network
import (
"encoding/hex"
"errors"
_config "erupe-ce/config"
"fmt"
"io"
"net"
@@ -48,7 +49,14 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
}
// 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)
if err != nil {
return nil, err
@@ -56,7 +64,7 @@ func (cc *CryptConn) ReadPacket() ([]byte, error) {
// Update the key rotation before decrypting.
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)
@@ -94,7 +102,7 @@ func (cc *CryptConn) SendPacket(data []byte) error {
keyRotDelta := byte(3)
if keyRotDelta != 0 {
cc.sendKeyRot = (uint32(keyRotDelta) * (cc.sendKeyRot + 1))
cc.sendKeyRot = uint32(keyRotDelta) * (cc.sendKeyRot + 1)
}
// Encrypt the data

View File

@@ -2,10 +2,11 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// 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.ItemID = bf.ReadUint16()
m.Quant = bf.ReadUint16()
m.PointCost = bf.ReadUint32()
if _config.ErupeConfig.RealClientMode >= _config.G1 {
m.PointCost = bf.ReadUint32()
} else {
m.PointCost = uint32(bf.ReadUint16())
}
m.Unk0 = bf.ReadUint16()
return nil
}

View File

@@ -1,15 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfAcquireItem) Opcode() network.PacketID {
@@ -18,7 +23,13 @@ func (m *MsgMhfAcquireItem) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,18 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfAcquireTournament) Opcode() network.PacketID {
@@ -18,7 +21,9 @@ func (m *MsgMhfAcquireTournament) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,17 +1,18 @@
package mhfpacket
import (
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// MsgMhfApplyCampaign represents the MSG_MHF_APPLY_CAMPAIGN
type MsgMhfApplyCampaign struct {
AckHandle uint32
Unk0 uint8
Unk1 uint8
Unk2 uint16
Unk0 uint32
Unk1 uint16
Unk2 []byte
}
// 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
func (m *MsgMhfApplyCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint16()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint16()
m.Unk2 = bf.ReadBytes(16)
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfApplyCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint8(m.Unk0)
bf.WriteUint8(m.Unk1)
bf.WriteUint16(m.Unk2)
return nil
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,15 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfCaravanMyRank) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfCaravanMyRank) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,24 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfCaravanMyScore) Opcode() network.PacketID {
@@ -18,7 +27,15 @@ func (m *MsgMhfCaravanMyScore) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfCaravanRanking) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfCaravanRanking) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,19 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfEntryTournament) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfEntryTournament) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,17 +1,16 @@
package mhfpacket
import (
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
)
// MsgMhfEnumerateCampaign represents the MSG_MHF_ENUMERATE_CAMPAIGN
type MsgMhfEnumerateCampaign struct {
AckHandle uint32
Unk0 uint8
Unk1 uint8
Unk2 uint16
Unk0 uint16
Unk1 uint16
}
// 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
func (m *MsgMhfEnumerateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint16()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint16()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfEnumerateCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint8(m.Unk0)
bf.WriteUint8(m.Unk1)
bf.WriteUint16(m.Unk2)
bf.WriteUint16(m.Unk0)
bf.WriteUint16(m.Unk1)
return nil
}

View File

@@ -1,15 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfEnumerateItem) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfEnumerateItem) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

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

View File

@@ -1,11 +1,12 @@
package mhfpacket
import (
"errors"
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.Unk2 = bf.ReadUint16()
m.Unk3 = bf.ReadUint8()
m.Unk4 = bf.ReadUint8()
m.Unk5 = bf.ReadUint32()
if _config.ErupeConfig.RealClientMode >= _config.G2 {
m.Unk4 = bf.ReadUint8()
m.Unk5 = bf.ReadUint32()
}
return nil
}

View File

@@ -1,15 +1,19 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfGetBreakSeibatuLevelReward) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfGetBreakSeibatuLevelReward) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,22 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfGetFixedSeibatuRankingTable) Opcode() network.PacketID {
@@ -18,7 +25,13 @@ func (m *MsgMhfGetFixedSeibatuRankingTable) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -11,8 +11,13 @@ import (
// MsgMhfGetGemInfo represents the MSG_MHF_GET_GEM_INFO
type MsgMhfGetGemInfo struct {
AckHandle uint32
Unk uint32
Unk1 []byte
Unk0 uint32
Unk1 uint32
Unk2 int32
Unk3 int32
Unk4 int32
Unk5 int32
Unk6 int32
}
// 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
func (m *MsgMhfGetGemInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk = bf.ReadUint32()
m.Unk1 = bf.ReadBytes(24)
m.Unk0 = bf.ReadUint32()
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
}

View File

@@ -1,15 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfGetNotice) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfGetNotice) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

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

View File

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

View File

@@ -1,15 +1,17 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfGetSenyuDailyCount) Opcode() network.PacketID {
@@ -18,7 +20,8 @@ func (m *MsgMhfGetSenyuDailyCount) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,20 +1,21 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfGetTenrouirai represents the MSG_MHF_GET_TENROUIRAI
type MsgMhfGetTenrouirai struct {
// Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32
Unk0 uint16
Unk1 uint32
Unk2 uint16
Unk0 uint8
Unk1 uint8
GuildID uint32
Unk3 uint8
Unk4 uint8
}
// 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
func (m *MsgMhfGetTenrouirai) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint16()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.GuildID = bf.ReadUint32()
m.Unk3 = bf.ReadUint8()
m.Unk4 = bf.ReadUint8()
return nil
}

View File

@@ -1,19 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfGetTinyBin represents the MSG_MHF_GET_TINY_BIN
type MsgMhfGetTinyBin struct {
// Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32
Unk0 uint16
Unk0 uint8
Unk1 uint8
Unk2 uint8
}
// 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
func (m *MsgMhfGetTinyBin) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint8()
return nil
}

View File

@@ -8,19 +8,8 @@ import (
"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
type MsgMhfGetTowerInfo struct {
// Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32
InfoType uint32 // Requested response type
Unk0 uint32

View File

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

View File

@@ -1,15 +1,18 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfPostBoostTimeLimit) Opcode() network.PacketID {
@@ -18,7 +21,9 @@ func (m *MsgMhfPostBoostTimeLimit) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,24 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfPostGemInfo) Opcode() network.PacketID {
@@ -18,7 +27,15 @@ func (m *MsgMhfPostGemInfo) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,21 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfPostNotice) Opcode() network.PacketID {
@@ -18,7 +24,12 @@ func (m *MsgMhfPostNotice) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,22 +1,34 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfPostTenrouirai represents the MSG_MHF_POST_TENROUIRAI
type MsgMhfPostTenrouirai struct{
AckHandle uint32
Unk0 uint16
Unk1 uint32
Unk2 uint32
Unk3 uint32
Unk4 uint32
Unk5 uint8
type MsgMhfPostTenrouirai struct {
AckHandle uint32
Unk0 uint8
Op uint8
GuildID uint32
Unk1 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.
@@ -27,12 +39,28 @@ func (m *MsgMhfPostTenrouirai) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfPostTenrouirai) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
m.Unk3 = bf.ReadUint32()
m.Unk4 = bf.ReadUint32()
m.Unk5 = bf.ReadUint8()
m.Unk0 = bf.ReadUint8()
m.Op = bf.ReadUint8()
m.GuildID = bf.ReadUint32()
m.Unk1 = 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
}

View File

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

View File

@@ -1,28 +1,26 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfPostTowerInfo represents the MSG_MHF_POST_TOWER_INFO
type MsgMhfPostTowerInfo struct {
// Communicator type, multi-format. This might be valid for only one type.
AckHandle uint32
Unk0 uint32
InfoType uint32
Unk1 uint32
Unk2 uint32
Unk3 uint32
Unk4 uint32
Unk5 uint32
Unk6 uint32
Unk7 uint32
Unk8 uint32
Unk9 uint32
Unk10 uint32
Skill int32
TR int32
TRP int32
Cost int32
Unk6 int32
Unk7 int32
Block1 int32
Unk9 int64
}
// 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
func (m *MsgMhfPostTowerInfo) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.InfoType = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
m.Unk3 = bf.ReadUint32()
m.Unk4 = bf.ReadUint32()
m.Unk5 = bf.ReadUint32()
m.Unk6 = bf.ReadUint32()
m.Unk7 = bf.ReadUint32()
m.Unk8 = bf.ReadUint32()
m.Unk9 = bf.ReadUint32()
m.Unk10 = bf.ReadUint32()
m.Skill = bf.ReadInt32()
m.TR = bf.ReadInt32()
m.TRP = bf.ReadInt32()
m.Cost = bf.ReadInt32()
m.Unk6 = bf.ReadInt32()
m.Unk7 = bf.ReadInt32()
m.Block1 = bf.ReadInt32()
m.Unk9 = bf.ReadInt64()
return nil
}

View File

@@ -18,8 +18,7 @@ type MsgMhfPresentBox struct {
Unk4 uint32
Unk5 uint32
Unk6 uint32
Unk7 uint32
Unk8 uint32
Unk7 []uint32
}
// 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.Unk5 = bf.ReadUint32()
m.Unk6 = bf.ReadUint32()
m.Unk7 = bf.ReadUint32()
m.Unk8 = bf.ReadUint32()
for i := uint32(0); i < m.Unk2; i++ {
m.Unk7 = append(m.Unk7, bf.ReadUint32())
}
return nil
}

View File

@@ -1,15 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfReadBeatLevelAllRanking) Opcode() network.PacketID {
@@ -18,7 +23,11 @@ func (m *MsgMhfReadBeatLevelAllRanking) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfReadBeatLevelMyRanking) Opcode() network.PacketID {
@@ -18,7 +23,13 @@ func (m *MsgMhfReadBeatLevelMyRanking) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,15 +1,19 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.
func (m *MsgMhfReadLastWeekBeatRanking) Opcode() network.PacketID {
@@ -18,7 +22,10 @@ func (m *MsgMhfReadLastWeekBeatRanking) Opcode() network.PacketID {
// Parse parses the packet from binary
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.

View File

@@ -1,11 +1,12 @@
package mhfpacket
import (
"errors"
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// 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.SaveType = bf.ReadUint8()
m.Unk1 = bf.ReadUint32()
m.DataSize = bf.ReadUint32()
if _config.ErupeConfig.RealClientMode >= _config.G1 {
m.DataSize = bf.ReadUint32()
}
if m.DataSize == 0 { // seems to be used when DataSize = 0 rather than on savetype?
m.RawDataPayload = bf.ReadBytes(uint(m.AllocMemSize))
} else {

View File

@@ -1,18 +1,24 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfSetCaAchievementHist represents the MSG_MHF_SET_CA_ACHIEVEMENT_HIST
type MsgMhfSetCaAchievementHist struct{
AckHandle uint32
type CaAchievementHist struct {
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.
@@ -23,8 +29,14 @@ func (m *MsgMhfSetCaAchievementHist) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfSetCaAchievementHist) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
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
}

View File

@@ -1,17 +1,20 @@
package mhfpacket
import (
"errors"
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfSexChanger represents the MSG_MHF_SEX_CHANGER
type MsgMhfSexChanger struct {
AckHandle uint32
Gender uint8
Unk0 uint8
Unk1 uint8
Unk2 uint8
}
// 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 {
m.AckHandle = bf.ReadUint32()
m.Gender = bf.ReadUint8()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint8()
return nil
}

View File

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

View File

@@ -1,18 +1,18 @@
package mhfpacket
import (
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
)
// MsgMhfStateCampaign represents the MSG_MHF_STATE_CAMPAIGN
type MsgMhfStateCampaign struct {
AckHandle uint32
Unk0 uint8
Unk1 uint8
Unk2 uint16
AckHandle uint32
CampaignID uint32
Unk1 uint16
}
// 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
func (m *MsgMhfStateCampaign) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint16()
m.CampaignID = bf.ReadUint32()
m.Unk1 = bf.ReadUint16()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfStateCampaign) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint8(m.Unk0)
bf.WriteUint8(m.Unk1)
bf.WriteUint16(m.Unk2)
return nil
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -1,45 +1,42 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfUpdateBeatLevel represents the MSG_MHF_UPDATE_BEAT_LEVEL
type MsgMhfUpdateBeatLevel struct {
AckHandle uint32
Unk1 uint32
Unk2 uint32
MonsterData []byte
Unk3 uint8
Unk4 uint32
Unk5 uint16
Unk6 uint8
AckHandle uint32
Unk1 uint32
Unk2 uint32
Data1 []int32
Data2 []int32
}
// Opcode returns the ID associated with this packet type.
func (m *MsgMhfUpdateBeatLevel) Opcode() network.PacketID {
return network.MSG_MHF_UPDATE_BEAT_LEVEL
return network.MSG_MHF_UPDATE_BEAT_LEVEL
}
// Parse parses the packet from binary
func (m *MsgMhfUpdateBeatLevel) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
m.MonsterData = bf.ReadBytes(uint(120))
m.Unk3 = bf.ReadUint8()
m.Unk4 = bf.ReadUint32()
m.Unk5 = bf.ReadUint16()
m.Unk6 = bf.ReadUint8()
return nil
m.AckHandle = bf.ReadUint32()
m.Unk1 = bf.ReadUint32()
m.Unk2 = bf.ReadUint32()
for i := 0; i < 16; i++ {
m.Data1 = append(m.Data1, bf.ReadInt32())
}
for i := 0; i < 16; i++ {
m.Data2 = append(m.Data2, bf.ReadInt32())
}
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgMhfUpdateBeatLevel) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
return errors.New("NOT IMPLEMENTED")
}

View File

@@ -2,10 +2,9 @@ package mhfpacket
import (
"errors"
"fmt"
"erupe-ce/common/byteframe"
"erupe-ce/common/bfutil"
"erupe-ce/common/byteframe"
_config "erupe-ce/config"
"erupe-ce/network"
"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 {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint16()
m.PlayerCount = bf.ReadUint8()
fmt.Printf("PLAYER COUNT :: %d", m.PlayerCount)
if _config.ErupeConfig.RealClientMode >= _config.G1 {
m.PlayerCount = bf.ReadUint8()
}
SemaphoreIDLength := bf.ReadUint8()
m.SemaphoreID = string(bfutil.UpToNull(bf.ReadBytes(uint(SemaphoreIDLength))))
return nil

View File

@@ -2,6 +2,7 @@ package mhfpacket
import (
"errors"
_config "erupe-ce/config"
"erupe-ce/common/byteframe"
"erupe-ce/network"
@@ -37,12 +38,16 @@ func (m *MsgSysTerminalLog) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
m.EntryCount = bf.ReadUint16()
m.Unk0 = bf.ReadUint16()
values := 15
if _config.ErupeConfig.RealClientMode <= _config.F5 {
values = 7
}
for i := 0; i < int(m.EntryCount); i++ {
e := &TerminalLogEntry{}
e.Index = bf.ReadUint32()
e.Type1 = 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())
}
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"
ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"fmt"
"io"
"net"
@@ -27,15 +28,16 @@ func stubEnumerateNoResults(s *Session, ackHandle uint32) {
doAckBufSucceed(s, ackHandle, enumBf.Data())
}
// Temporary function to just return no results for many MSG_MHF_GET* packets.
func stubGetNoResults(s *Session, ackHandle uint32) {
resp := byteframe.NewByteFrame()
resp.WriteUint32(0x0A218EAD) // Unk shared ID. Sent in response of MSG_MHF_GET_TOWER_INFO, MSG_MHF_GET_PAPER_DATA etc. (World ID?)
resp.WriteUint32(0) // Unk
resp.WriteUint32(0) // Unk
resp.WriteUint32(0) // Entry count
doAckBufSucceed(s, ackHandle, resp.Data())
func doAckEarthSucceed(s *Session, ackHandle uint32, data []*byteframe.ByteFrame) {
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(s.server.erupeConfig.DevModeOptions.EarthIDOverride))
bf.WriteUint32(0)
bf.WriteUint32(0)
bf.WriteUint32(uint32(len(data)))
for i := range data {
bf.WriteBytes(data[i].Data())
}
doAckBufSucceed(s, ackHandle, bf.Data())
}
func doAckBufSucceed(s *Session, ackHandle uint32, data []byte) {
@@ -245,7 +247,7 @@ func logoutPlayer(s *Session) {
removeSessionFromStage(s)
saveData, err := GetCharacterSaveData(s, s.charID)
if err != nil {
if err != nil || saveData == nil {
s.logger.Error("Failed to get savedata")
return
}
@@ -446,48 +448,110 @@ func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) {
}
}
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)
setting := bf.ReadUint8()
numParams := int(bf.ReadUint8())
maxResults := bf.ReadUint16()
bf.Seek(2, 1)
partyType := bf.ReadUint16()
rankRestriction := uint16(0)
if setting >= 2 {
bf.Seek(2, 1)
rankRestriction = bf.ReadUint16()
}
targets := make([]uint16, 4)
if setting >= 3 {
bf.Seek(1, 1)
lenTargets := int(bf.ReadUint8())
for i := 0; i < lenTargets; i++ {
targets[i] = bf.ReadUint16()
for i := 0; i < numParams; i++ {
switch bf.ReadUint8() {
case 0:
values := int(bf.ReadUint8())
for i := 0; i < values; i++ {
if _config.ErupeConfig.RealClientMode >= _config.Z1 {
findPartyParams.RankRestriction = bf.ReadUint16()
} else {
findPartyParams.RankRestriction = uint16(bf.ReadInt8())
}
}
case 1:
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
findPartyParams.StagePrefix = "sl2Ls210"
case 1: // Tokotoko Partnya
findPartyParams.StagePrefix = "sl2Ls463"
case 2: // Hunting Prowess Match
findPartyParams.StagePrefix = "sl2Ls286"
case 3: // Volpakkun Together
findPartyParams.StagePrefix = "sl2Ls465"
case 5: // Quick Party
// 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()))
}
}
}
}
var stagePrefix string
switch partyType {
case 0: // Public Bar
stagePrefix = "sl2Ls210"
case 1: // Tokotoko Partnya
stagePrefix = "sl2Ls463"
case 2: // Hunting Prowess Match
stagePrefix = "sl2Ls286"
case 3: // Volpakkun Together
stagePrefix = "sl2Ls465"
case 5: // Quick Party
// Unk
}
for _, c := range s.server.Channels {
for _, stage := range c.stages {
if count == maxResults {
break
}
if strings.HasPrefix(stage.id, stagePrefix) {
if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) {
sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}])
sb3.Seek(4, 0)
stageRankRestriction := 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
}
count++
@@ -551,10 +615,6 @@ func handleMsgSysInfokyserver(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) {
pkt := p.(*mhfpacket.MsgMhfTransferItem)
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 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 handleMsgMhfGetEarthStatus(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthStatus)
// TODO(Andoryuuta): Track down format for this data,
// it can somehow be parsed as 8*uint32 chunks if the header is right.
/*
BEFORE ack-refactor:
resp := byteframe.NewByteFrame()
resp.WriteUint32(0)
resp.WriteUint32(0)
s.QueueAck(pkt.AckHandle, resp.Data())
*/
doAckBufSucceed(s, pkt.AckHandle, []byte{})
bf := byteframe.NewByteFrame()
bf.WriteUint32(uint32(TimeWeekStart().Add(time.Hour * -24).Unix())) // Start
bf.WriteUint32(uint32(TimeWeekNext().Add(time.Hour * 24).Unix())) // End
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthStatusOverride)
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthIDOverride)
bf.WriteInt32(s.server.erupeConfig.DevModeOptions.EarthMonsterOverride)
bf.WriteInt32(0)
bf.WriteInt32(0)
bf.WriteInt32(0)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfRegistSpabiTime(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfGetEarthValue(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetEarthValue)
var earthValues []struct{ Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32 }
if pkt.ReqType == 3 {
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,
},
type EarthValues struct {
Value []uint32
}
var earthValues []EarthValues
switch pkt.ReqType {
case 1:
earthValues = []EarthValues{
{[]uint32{1, 312, 0, 0, 0, 0}},
{[]uint32{2, 99, 0, 0, 0, 0}},
}
} else if pkt.ReqType == 2 {
earthValues = []struct {
Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32
}{
// JP response was empty
{
Unk0: 0x01,
Unk1: 0x168B,
},
{
Unk0: 0x02,
Unk1: 0x0737,
},
case 2:
earthValues = []EarthValues{
{[]uint32{1, 5771, 0, 0, 0, 0}},
{[]uint32{2, 1847, 0, 0, 0, 0}},
}
} else if pkt.ReqType == 1 {
earthValues = []struct {
Unk0, Unk1, Unk2, Unk3, Unk4, Unk5 uint32
}{
// JP simply sent 01 and 02 respectively
{
Unk0: 0x01,
Unk1: 0x0138,
},
{
Unk0: 0x02,
Unk1: 0x63,
},
case 3:
earthValues = []EarthValues{
{[]uint32{1001, 36, 0, 0, 0, 0}},
{[]uint32{9001, 3, 0, 0, 0, 0}},
{[]uint32{9002, 10, 300, 0, 0, 0}},
}
}
resp := byteframe.NewByteFrame()
resp.WriteUint32(0x0A218EAD) // Unk shared ID. Sent in response of MSG_MHF_GET_TOWER_INFO, MSG_MHF_GET_PAPER_DATA etc.
resp.WriteUint32(0) // Unk
resp.WriteUint32(0) // Unk
resp.WriteUint32(uint32(len(earthValues))) // value count
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)
var data []*byteframe.ByteFrame
for _, i := range earthValues {
bf := byteframe.NewByteFrame()
for _, j := range i.Value {
bf.WriteUint32(j)
}
data = append(data, bf)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
doAckEarthSucceed(s, pkt.AckHandle, data)
}
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 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) {
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) {}

View File

@@ -270,6 +270,12 @@ func handleMsgMhfPostBoostTimeQuestReturn(s *Session, p mhfpacket.MHFPacket) {
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
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) {
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) {
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) {
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
import (
"encoding/hex"
"erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport"
"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) {
pkt := p.(*mhfpacket.MsgMhfGetRyoudama)
// likely guild related
// REQ: 00 04 13 53 8F 18 00
// RSP: 0A 21 8E AD 00 00 00 00 00 00 00 00 00 00 00 01 00 01 FE 4E
// REQ: 00 06 13 53 8F 18 00
// 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
// REQ: 00 05 13 53 8F 18 00
// 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
data, _ := hex.DecodeString("0A218EAD0000000000000000000000010000000000000000")
doAckBufSucceed(s, pkt.AckHandle, data)
var data []*byteframe.ByteFrame
ryoudama := Ryoudama{Score: []int32{0}}
switch pkt.Request2 {
case 4:
for _, score := range ryoudama.Score {
bf := byteframe.NewByteFrame()
bf.WriteInt32(score)
data = append(data, bf)
}
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) {}
@@ -31,8 +83,41 @@ func handleMsgMhfPostTinyBin(s *Session, p mhfpacket.MHFPacket) {
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
)
var commands map[string]config.Command
var commands map[string]_config.Command
func init() {
commands = make(map[string]config.Command)
commands = make(map[string]_config.Command)
zapConfig := zap.NewDevelopmentConfig()
zapConfig.DisableCaller = true
zapLogger, _ := zapConfig.Build()
defer zapLogger.Sync()
logger := zapLogger.Named("commands")
cmds := config.ErupeConfig.Commands
cmds := _config.ErupeConfig.Commands
for _, cmd := range cmds {
commands[cmd.Name] = cmd
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))
}
@@ -211,7 +211,7 @@ func parseChatCommand(s *Session, command string) {
for _, course := range mhfcourse.Courses() {
for _, alias := range course.Aliases() {
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
if mhfcourse.CourseExists(course.ID, s.courses) {
ei := slices.IndexFunc(s.courses, func(c mhfcourse.Course) bool {

View File

@@ -5,33 +5,36 @@ import (
"errors"
"erupe-ce/common/bfutil"
"erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/nullcomp"
"go.uber.org/zap"
)
type SavePointer int
const (
pointerGender = 0x51 // +1
pointerRP = 0x22D16 // +2
pointerHouseTier = 0x1FB6C // +5
pointerHouseData = 0x1FE01 // +195
pointerBookshelfData = 0x22298 // +5576
// Gallery data also exists at 0x21578, is this the contest submission?
pointerGalleryData = 0x22320 // +1748
pointerToreData = 0x1FCB4 // +240
pointerGardenData = 0x22C58 // +68
pointerWeaponType = 0x1F715 // +1
pointerWeaponID = 0x1F60A // +2
pointerHRP = 0x1FDF6 // +2
pointerGRP = 0x1FDFC // +4
pointerKQF = 0x23D20 // +8
pGender = iota // +1
pRP // +2
pHouseTier // +5
pHouseData // +195
pBookshelfData // +5576
pGalleryData // +1748
pToreData // +240
pGardenData // +68
pWeaponType // +1
pWeaponID // +2
pHRP // +2
pGRP // +4
pKQF // +8
)
type CharacterSaveData struct {
CharID uint32
Name string
IsNewCharacter bool
Pointers map[SavePointer]int
Gender bool
RP uint16
@@ -51,6 +54,39 @@ type CharacterSaveData struct {
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) {
result, err := s.server.db.Query("SELECT id, savedata, is_new_character, name FROM characters WHERE id = $1", charID)
if err != nil {
@@ -64,7 +100,9 @@ func GetCharacterSaveData(s *Session, charID uint32) (*CharacterSaveData, error)
return nil, err
}
saveData := &CharacterSaveData{}
saveData := &CharacterSaveData{
Pointers: getPointers(),
}
err = result.Scan(&saveData.CharID, &saveData.compSave, &saveData.IsNewCharacter, &saveData.Name)
if err != nil {
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()
err := save.Compress()
if err != nil {
s.logger.Error("Failed to compress savedata", zap.Error(err))
return
if _config.ErupeConfig.RealClientMode >= _config.G1 {
err := save.Compress()
if err != nil {
s.logger.Error("Failed to compress savedata", zap.Error(err))
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)
if err != nil {
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() {
rpBytes := make([]byte, 2)
binary.LittleEndian.PutUint16(rpBytes, save.RP)
copy(save.decompSave[pointerRP:pointerRP+2], rpBytes)
copy(save.decompSave[pointerKQF:pointerKQF+8], save.KQF)
if _config.ErupeConfig.RealClientMode >= _config.G10 {
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
func (save *CharacterSaveData) updateStructWithSaveData() {
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
} else {
save.Gender = false
}
if !save.IsNewCharacter {
save.RP = binary.LittleEndian.Uint16(save.decompSave[pointerRP : pointerRP+2])
save.HouseTier = save.decompSave[pointerHouseTier : pointerHouseTier+5]
save.HouseData = save.decompSave[pointerHouseData : pointerHouseData+195]
save.BookshelfData = save.decompSave[pointerBookshelfData : pointerBookshelfData+5576]
save.GalleryData = save.decompSave[pointerGalleryData : pointerGalleryData+1748]
save.ToreData = save.decompSave[pointerToreData : pointerToreData+240]
save.GardenData = save.decompSave[pointerGardenData : pointerGardenData+68]
save.WeaponType = save.decompSave[pointerWeaponType]
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[pointerWeaponID : pointerWeaponID+2])
save.HRP = binary.LittleEndian.Uint16(save.decompSave[pointerHRP : pointerHRP+2])
if save.HRP == uint16(999) {
save.GR = grpToGR(binary.LittleEndian.Uint32(save.decompSave[pointerGRP : pointerGRP+4]))
if _config.ErupeConfig.RealClientMode >= _config.G10 {
save.RP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pRP] : save.Pointers[pRP]+2])
save.HouseTier = save.decompSave[save.Pointers[pHouseTier] : save.Pointers[pHouseTier]+5]
save.HouseData = save.decompSave[save.Pointers[pHouseData] : save.Pointers[pHouseData]+195]
save.BookshelfData = save.decompSave[save.Pointers[pBookshelfData] : save.Pointers[pBookshelfData]+5576]
save.GalleryData = save.decompSave[save.Pointers[pGalleryData] : save.Pointers[pGalleryData]+1748]
save.ToreData = save.decompSave[save.Pointers[pToreData] : save.Pointers[pToreData]+240]
save.GardenData = save.decompSave[save.Pointers[pGardenData] : save.Pointers[pGardenData]+68]
save.WeaponType = save.decompSave[save.Pointers[pWeaponType]]
save.WeaponID = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pWeaponID] : save.Pointers[pWeaponID]+2])
save.HRP = binary.LittleEndian.Uint16(save.decompSave[save.Pointers[pHRP] : save.Pointers[pHRP]+2])
if save.HRP == uint16(999) {
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
}
func handleMsgMhfSexChanger(s *Session, p mhfpacket.MHFPacket) {
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 (
"encoding/hex"
"erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"time"
"erupe-ce/common/byteframe"
@@ -71,7 +72,11 @@ func handleMsgMhfGetUdSchedule(s *Session, p mhfpacket.MHFPacket) {
var timestamps []uint32
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.DivaEvent >= 0 {
if s.server.erupeConfig.DevModeOptions.DivaEvent == 0 {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
if s.server.erupeConfig.RealClientMode <= _config.Z1 {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 32))
} else {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 36))
}
return
}
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)
}
bf.WriteUint32(id)
for _, timestamp := range timestamps {
bf.WriteUint32(timestamp)
if s.server.erupeConfig.RealClientMode <= _config.Z1 {
bf.WriteUint32(id)
}
for i := range timestamps {
bf.WriteUint32(timestamps[i])
}
bf.WriteUint16(0x19) // Unk 00011001

View File

@@ -2,6 +2,7 @@ package channelserver
import (
"erupe-ce/common/token"
_config "erupe-ce/config"
"math"
"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) {
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 {
@@ -90,14 +123,24 @@ func handleMsgMhfGetWeeklySchedule(s *Session, p mhfpacket.MHFPacket) {
}
func generateFeatureWeapons(count int) activeFeature {
if count > 14 {
count = 14
max := 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)
var result int
for len(nums) < count {
rng := token.RNG()
num := rng.Intn(14)
num := rng.Intn(max)
exist := false
for _, v := range nums {
if v == num {

View File

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

View File

@@ -7,6 +7,7 @@ import (
"encoding/hex"
"encoding/json"
"errors"
_config "erupe-ce/config"
"fmt"
"math"
"sort"
@@ -62,7 +63,6 @@ type Guild struct {
Recruiting bool `db:"recruiting"`
FestivalColour FestivalColour `db:"festival_colour"`
Souls uint32 `db:"souls"`
Rank uint16 `db:"rank"`
AllianceID uint32 `db:"alliance_id"`
Icon *GuildIcon `db:"icon"`
@@ -115,6 +115,39 @@ func (gi *GuildIcon) Value() (valuer driver.Value, err error) {
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 = `
SELECT
g.id,
@@ -137,14 +170,6 @@ SELECT
recruiting,
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,
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((
SELECT id FROM guild_alliances ga WHERE
ga.parent_id = g.id OR
@@ -615,13 +640,7 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfOperateGuild)
guild, err := GetGuildInfoByID(s, pkt.GuildID)
if err != nil {
return
}
characterGuildInfo, err := GetCharacterGuildData(s, s.charID)
if err != nil {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
@@ -630,22 +649,19 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
bf := byteframe.NewByteFrame()
switch pkt.Action {
case mhfpacket.OPERATE_GUILD_DISBAND:
case mhfpacket.OperateGuildDisband:
response := 1
if guild.LeaderCharID != s.charID {
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)
if err != nil {
response = 0
}
}
err = guild.Disband(s)
response := 0x01
if err != nil {
// All successful acks return 0x01, assuming 0x00 is failure
response = 0x00
}
bf.WriteUint32(uint32(response))
case mhfpacket.OPERATE_GUILD_RESIGN:
case mhfpacket.OperateGuildResign:
guildMembers, err := GetGuildMembers(s, guild.ID, false)
if err == nil {
sort.Slice(guildMembers[:], func(i, j int) bool {
@@ -664,25 +680,22 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
}
guild.Save(s)
}
case mhfpacket.OPERATE_GUILD_APPLY:
case mhfpacket.OperateGuildApply:
err = guild.CreateApplication(s, s.charID, GuildApplicationTypeApplied, nil)
if err == nil {
bf.WriteUint32(guild.LeaderCharID)
} else {
bf.WriteUint32(0)
}
case mhfpacket.OPERATE_GUILD_LEAVE:
var err error
case mhfpacket.OperateGuildLeave:
if characterGuildInfo.IsApplicant {
err = guild.RejectApplication(s, s.charID)
} else {
err = guild.RemoveCharacter(s, s.charID)
}
response := 0x01
response := 1
if err != nil {
// All successful acks return 0x01, assuming 0x00 is failure
response = 0x00
response = 0
} else {
mail := Mail{
RecipientID: s.charID,
@@ -692,26 +705,25 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
}
mail.Send(s, nil)
}
bf.WriteUint32(uint32(response))
case mhfpacket.OPERATE_GUILD_DONATE_RANK:
case mhfpacket.OperateGuildDonateRank:
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)
case mhfpacket.OPERATE_GUILD_SET_APPLICATION_ALLOW:
case mhfpacket.OperateGuildSetApplicationAllow:
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)
case mhfpacket.OPERATE_GUILD_SET_AVOID_LEADERSHIP_FALSE:
case mhfpacket.OperateGuildSetAvoidLeadershipFalse:
handleAvoidLeadershipUpdate(s, pkt, false)
case mhfpacket.OPERATE_GUILD_UPDATE_COMMENT:
case mhfpacket.OperateGuildUpdateComment:
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
guild.Comment = stringsupport.SJISToUTF8(pkt.Data2.ReadNullTerminatedBytes())
guild.Save(s)
case mhfpacket.OPERATE_GUILD_UPDATE_MOTTO:
case mhfpacket.OperateGuildUpdateMotto:
if !characterGuildInfo.IsLeader && !characterGuildInfo.IsSubLeader() {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
@@ -720,27 +732,29 @@ func handleMsgMhfOperateGuild(s *Session, p mhfpacket.MHFPacket) {
guild.SubMotto = pkt.Data1.ReadUint8()
guild.MainMotto = pkt.Data1.ReadUint8()
guild.Save(s)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_1:
case mhfpacket.OperateGuildRenamePugi1:
handleRenamePugi(s, pkt.Data2, guild, 1)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_2:
case mhfpacket.OperateGuildRenamePugi2:
handleRenamePugi(s, pkt.Data2, guild, 2)
case mhfpacket.OPERATE_GUILD_RENAME_PUGI_3:
case mhfpacket.OperateGuildRenamePugi3:
handleRenamePugi(s, pkt.Data2, guild, 3)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_1:
case mhfpacket.OperateGuildChangePugi1:
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)
case mhfpacket.OPERATE_GUILD_CHANGE_PUGI_3:
case mhfpacket.OperateGuildChangePugi3:
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
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())
bf.WriteBytes(handleDonateRP(s, quantity, guild, true))
// TODO: Move this value onto rp_yesterday and reset to 0... daily?
s.server.db.Exec(`UPDATE guild_characters SET rp_today=rp_today+$1 WHERE character_id=$2`, quantity, s.charID)
case mhfpacket.OPERATE_GUILD_EVENT_EXCHANGE:
case mhfpacket.OperateGuildEventExchange:
rp := uint16(pkt.Data1.ReadUint32())
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)
@@ -922,14 +936,19 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(guild.ID)
bf.WriteUint32(guild.LeaderCharID)
bf.WriteUint16(guild.Rank)
bf.WriteUint16(guild.Rank())
bf.WriteUint16(guild.MemberCount)
bf.WriteUint8(guild.MainMotto)
bf.WriteUint8(guild.SubMotto)
// 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)
@@ -952,28 +971,39 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint8(FestivalColourCodes[guild.FestivalColour])
bf.WriteUint32(guild.RankRP)
bf.WriteBytes(guildLeaderName)
bf.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00}) // Unk
bf.WriteBool(false) // isReturnGuild
bf.WriteBool(false) // earnedSpecialHall
bf.WriteBytes([]byte{0x02, 0x02}) // Unk
bf.WriteUint32(guild.EventRP)
bf.WriteUint32(0) // Unk
bf.WriteBool(false) // isReturnGuild
bf.WriteBool(false) // earnedSpecialHall
bf.WriteUint8(2)
bf.WriteUint8(2)
bf.WriteUint32(guild.EventRP) // Skipped if last byte is <2?
ps.Uint8(bf, guild.PugiName1, true)
ps.Uint8(bf, guild.PugiName2, true)
ps.Uint8(bf, guild.PugiName3, true)
bf.WriteUint8(guild.PugiOutfit1)
bf.WriteUint8(guild.PugiOutfit2)
bf.WriteUint8(guild.PugiOutfit3)
bf.WriteUint8(guild.PugiOutfit1)
bf.WriteUint8(guild.PugiOutfit2)
bf.WriteUint8(guild.PugiOutfit3)
if s.server.erupeConfig.RealClientMode >= _config.Z1 {
bf.WriteUint8(guild.PugiOutfit1)
bf.WriteUint8(guild.PugiOutfit2)
bf.WriteUint8(guild.PugiOutfit3)
}
bf.WriteUint32(guild.PugiOutfits)
// Unk flags
bf.WriteUint8(0x3C) // also seen as 0x32 on JP and 0x64 on TW
if guild.Rank() >= 3 {
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{
0x00, 0x00, 0xD6, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
})
bf.WriteUint32(55000)
bf.WriteUint32(0)
bf.WriteUint16(0) // Changing Room RP
bf.WriteUint16(0)
if guild.AllianceID > 0 {
alliance, err := GetAllianceData(s, guild.AllianceID)
@@ -983,7 +1013,8 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(alliance.ID)
bf.WriteUint32(uint32(alliance.CreatedAt.Unix()))
bf.WriteUint16(alliance.TotalMembers)
bf.WriteUint16(0) // Unk0
bf.WriteUint8(0)
bf.WriteUint8(0)
ps.Uint16(bf, alliance.Name, true)
if alliance.SubGuild1ID > 0 {
if alliance.SubGuild2ID > 0 {
@@ -1001,7 +1032,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
} else {
bf.WriteUint16(0)
}
bf.WriteUint16(alliance.ParentGuild.Rank)
bf.WriteUint16(alliance.ParentGuild.Rank())
bf.WriteUint16(alliance.ParentGuild.MemberCount)
ps.Uint16(bf, alliance.ParentGuild.Name, true)
ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
@@ -1013,7 +1044,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
} else {
bf.WriteUint16(0)
}
bf.WriteUint16(alliance.SubGuild1.Rank)
bf.WriteUint16(alliance.SubGuild1.Rank())
bf.WriteUint16(alliance.SubGuild1.MemberCount)
ps.Uint16(bf, alliance.SubGuild1.Name, true)
ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
@@ -1026,7 +1057,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
} else {
bf.WriteUint16(0)
}
bf.WriteUint16(alliance.SubGuild2.Rank)
bf.WriteUint16(alliance.SubGuild2.Rank())
bf.WriteUint16(alliance.SubGuild2.MemberCount)
ps.Uint16(bf, alliance.SubGuild2.Name, true)
ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)
@@ -1043,29 +1074,46 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(uint16(len(applicants)))
for _, applicant := range applicants {
bf.WriteUint32(applicant.CharID)
bf.WriteUint16(0)
bf.WriteUint16(0)
bf.WriteUint32(0)
bf.WriteUint16(applicant.HRP)
bf.WriteUint16(applicant.GR)
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)
}
/*
alliance application format
uint16 numapplicants (above)
uint32 guild id
uint32 guild leader id (for mail)
uint32 unk (always null in pcap)
uint16 member count
uint16 len guild name
string nullterm guild name
uint16 len guild leader name
string nullterm guild leader name
*/
type AllianceInvite struct {
GuildID uint32
LeaderID uint32
Unk0 uint16
Unk1 uint16
Members uint16
GuildName string
LeaderName string
}
allianceInvites := []AllianceInvite{}
bf.WriteUint8(uint8(len(allianceInvites)))
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 {
bf.WriteUint8(uint8(len(guild.Icon.Parts)))
@@ -1083,7 +1131,7 @@ func handleMsgMhfInfoGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint16(p.PosY)
}
} else {
bf.WriteUint8(0x00)
bf.WriteUint8(0)
}
bf.WriteUint8(0) // Unk
@@ -1285,7 +1333,7 @@ func handleMsgMhfEnumerateGuild(s *Session, p mhfpacket.MHFPacket) {
bf.WriteUint32(guild.LeaderCharID)
bf.WriteUint16(guild.MemberCount)
bf.WriteUint16(0x0000) // Unk
bf.WriteUint16(guild.Rank)
bf.WriteUint16(guild.Rank())
bf.WriteUint32(uint32(guild.CreatedAt.Unix()))
ps.Uint8(bf, guild.Name, true)
ps.Uint8(bf, guild.LeaderName, true)
@@ -1347,7 +1395,7 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
if guild != nil {
isApplicant, _ := guild.HasApplicationForCharID(s, s.charID)
if isApplicant {
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4))
doAckBufSucceed(s, pkt.AckHandle, make([]byte, 2))
return
}
}
@@ -1389,8 +1437,15 @@ func handleMsgMhfEnumerateGuildMember(s *Session, p mhfpacket.MHFPacket) {
for _, member := range guildMembers {
bf.WriteUint32(member.CharID)
bf.WriteUint16(member.HRP)
bf.WriteUint16(member.GR)
bf.WriteUint16(member.WeaponID)
if s.server.erupeConfig.RealClientMode > _config.G7 {
bf.WriteUint16(member.GR)
}
if s.server.erupeConfig.RealClientMode < _config.ZZ {
// Magnet Spike crash workaround
bf.WriteUint16(0)
} else {
bf.WriteUint16(member.WeaponID)
}
if member.WeaponType == 1 || member.WeaponType == 5 || member.WeaponType == 10 { // If weapon is ranged
bf.WriteUint8(7)
} else {
@@ -1450,7 +1505,6 @@ func handleMsgMhfGetGuildManageRight(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfGetGuildManageRight)
guild, err := GetGuildInfoByCharacterId(s, s.charID)
if guild == nil && s.prevGuildID != 0 {
guild, err = GetGuildInfoByID(s, s.prevGuildID)
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.WriteUint16(0x00) // Unk
bf.WriteUint16(0x00) // Member count
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
return
}
bf := byteframe.NewByteFrame()
bf.WriteUint16(0x00) // Unk
bf.WriteUint16(guild.MemberCount)
bf.WriteUint32(uint32(guild.MemberCount))
members, _ := GetGuildMembers(s, guild.ID, false)
for _, member := range members {
bf.WriteUint32(member.CharID)
bf.WriteBool(member.Recruiter)
bf.WriteBytes(make([]byte, 3))
}
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.ParentGuild.LeaderCharID)
bf.WriteUint16(alliance.ParentGuild.Rank)
bf.WriteUint16(alliance.ParentGuild.Rank())
bf.WriteUint16(alliance.ParentGuild.MemberCount)
ps.Uint16(bf, alliance.ParentGuild.Name, true)
ps.Uint16(bf, alliance.ParentGuild.LeaderName, true)
if alliance.SubGuild1ID > 0 {
bf.WriteUint32(alliance.SubGuild1ID)
bf.WriteUint32(alliance.SubGuild1.LeaderCharID)
bf.WriteUint16(alliance.SubGuild1.Rank)
bf.WriteUint16(alliance.SubGuild1.Rank())
bf.WriteUint16(alliance.SubGuild1.MemberCount)
ps.Uint16(bf, alliance.SubGuild1.Name, true)
ps.Uint16(bf, alliance.SubGuild1.LeaderName, true)
@@ -224,7 +224,7 @@ func handleMsgMhfInfoJoint(s *Session, p mhfpacket.MHFPacket) {
if alliance.SubGuild2ID > 0 {
bf.WriteUint32(alliance.SubGuild2ID)
bf.WriteUint32(alliance.SubGuild2.LeaderCharID)
bf.WriteUint16(alliance.SubGuild2.Rank)
bf.WriteUint16(alliance.SubGuild2.Rank())
bf.WriteUint16(alliance.SubGuild2.MemberCount)
ps.Uint16(bf, alliance.SubGuild2.Name, true)
ps.Uint16(bf, alliance.SubGuild2.LeaderName, true)

View File

@@ -4,6 +4,7 @@ import (
"erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"fmt"
"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))
}
if len(data) == 0 {
if s.server.erupeConfig.RealClientMode <= _config.G7 {
data = []byte{0x00, 0x00}
}
data = []byte{0x01, 0x00}
}
doAckBufSucceed(s, pkt.AckHandle, data)

View File

@@ -3,6 +3,7 @@ package channelserver
import (
"erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/deltacomp"
"erupe-ce/server/channelserver/compression/nullcomp"
@@ -56,11 +57,15 @@ func handleMsgMhfLoadLegendDispatch(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadHunterNavi)
naviLength := 552
if s.server.erupeConfig.RealClientMode <= _config.G7 {
naviLength = 280
}
var data []byte
err := s.server.db.QueryRow("SELECT hunternavi FROM characters WHERE id = $1", s.charID).Scan(&data)
if len(data) == 0 {
s.logger.Error("Failed to load hunternavi", zap.Error(err))
data = make([]byte, 0x226)
data = make([]byte, naviLength)
}
doAckBufSucceed(s, pkt.AckHandle, data)
}
@@ -68,6 +73,10 @@ func handleMsgMhfLoadHunterNavi(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfSaveHunterNavi(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfSaveHunterNavi)
if pkt.IsDataDiff {
naviLength := 552
if s.server.erupeConfig.RealClientMode <= _config.G7 {
naviLength = 280
}
var data []byte
// Load existing save
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.
// This is requried as the client will try to send a diff after character creation without a prior MsgMhfSaveHunterNavi packet.
if len(data) == 0 {
data = make([]byte, 0x226)
data = make([]byte, naviLength)
}
// 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) {
pkt := p.(*mhfpacket.MsgMhfContractMercenary)
switch pkt.Op {
case 0:
s.server.db.Exec("UPDATE characters SET pact_id=$1 WHERE id=$2", pkt.PactMercID, s.charID)
case 0: // Form loan
s.server.db.Exec("UPDATE characters SET pact_id=$1 WHERE id=$2", pkt.PactMercID, pkt.CID)
case 1: // Cancel lend
s.server.db.Exec("UPDATE characters SET pact_id=0 WHERE id=$1", s.charID)
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 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 {
if session.charID == s.charID {
s.server.userBinaryPartsLock.Lock()
@@ -91,6 +92,7 @@ func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {
s.server.BroadcastMHF(msg, s)
}
}
*/
}
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"
)
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) {
pkt := p.(*mhfpacket.MsgMhfInfoTournament)
bf := byteframe.NewByteFrame()
tournamentInfo0 := []TournamentInfo0{}
tournamentInfo21 := []TournamentInfo21{}
tournamentInfo22 := []TournamentInfo22{}
switch pkt.Unk0 {
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(uint32(time.Now().Add(time.Hour * -10).Unix()))
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
bf.WriteUint32(uint32(time.Now().Add(time.Hour * 10).Unix()))
bf.WriteBool(true) // Unk
bf.WriteBool(false) // Cafe-only
bf.WriteUint32(0) // Min HR
bf.WriteUint32(0) // Max HR
ps.Uint8(bf, "Test", false)
// ...
bf.WriteUint32(uint32(len(tournamentInfo0)))
for _, tinfo := range tournamentInfo0 {
bf.WriteUint32(tinfo.ID)
bf.WriteUint32(tinfo.MaxPlayers)
bf.WriteUint32(tinfo.CurrentPlayers)
bf.WriteUint16(tinfo.Unk1)
bf.WriteUint16(tinfo.TextColor)
bf.WriteUint32(tinfo.Unk2)
bf.WriteUint32(uint32(tinfo.Time1.Unix()))
bf.WriteUint32(uint32(tinfo.Time2.Unix()))
bf.WriteUint32(uint32(tinfo.Time3.Unix()))
bf.WriteUint32(uint32(tinfo.Time4.Unix()))
bf.WriteUint32(uint32(tinfo.Time5.Unix()))
bf.WriteUint32(uint32(tinfo.Time6.Unix()))
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())
}
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
import (
"encoding/hex"
"fmt"
"go.uber.org/zap"
"time"
"erupe-ce/common/byteframe"
"erupe-ce/common/stringsupport"
"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) {
pkt := p.(*mhfpacket.MsgMhfGetTowerInfo)
var data []byte
var err error
/*
type:
1 == TOWER_RANK_POINT,
2 == GET_OWN_TOWER_SKILL
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")
var data []*byteframe.ByteFrame
type TowerInfo struct {
TRP []TowerInfoTRP
Skill []TowerInfoSkill
History []TowerInfoHistory
Level []TowerInfoLevel
}
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 {
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) {
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) {
// if the game gets bad responses for this it breaks the ability to save
pkt := p.(*mhfpacket.MsgMhfGetTenrouirai)
var data []byte
var err error
if pkt.Unk0 == 1 {
data, err = hex.DecodeString("0A218EAD000000000000000000000001010000000000060010")
} else if pkt.Unk2 == 4 {
data, err = hex.DecodeString("0A218EAD0000000000000000000000210101005000000202010102020104001000000202010102020106003200000202010002020104000C003202020101020201030032000002020101020202059C4000000202010002020105C35000320202010102020201003C00000202010102020203003200000201010001020203002800320201010101020204000C00000201010101020206002800000201010001020101003C00320201020101020105C35000000301020101020106003200000301020001020104001000320301020101020105C350000003010201010202030028000003010200010201030032003203010201010202059C4000000301020101010206002800000301020001010201003C00320301020101010206003200000301020101010204000C000003010200010101010050003203010201010101059C40000003010201010101030032000003010200010101040010003203010001010101060032000003010001010102030028000003010001010101010050003203010000010102059C4000000301000001010206002800000301000001010010")
} else {
data = []byte{0x00, 0x00, 0x00, 0x00}
s.logger.Info("GET_TENROUIRAI request for unknown type")
var data []*byteframe.ByteFrame
tenrouirai := Tenrouirai{
Progress: []TenrouiraiProgress{{1, 0, 0, 0}},
Data: tenrouiraiData,
Ticket: []TenrouiraiTicket{{0, 0, 0}},
}
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)
}
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)
}
}
doAckBufSucceed(s, pkt.AckHandle, data)
doAckEarthSucceed(s, pkt.AckHandle, data)
}
func handleMsgMhfPostTenrouirai(s *Session, p mhfpacket.MHFPacket) {
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) {
pkt := p.(*mhfpacket.MsgMhfGetWeeklySeibatuRankingReward)
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
if pkt.Op == 2 {
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))
}
}
func handleMsgMhfPresentBox(s *Session, p mhfpacket.MHFPacket) {
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) {
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))
}
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
DB *sqlx.DB
DiscordBot *discordbot.DiscordBot
ErupeConfig *config.Config
ErupeConfig *_config.Config
Name string
Enable bool
}
@@ -43,7 +43,7 @@ type Server struct {
Port uint16
logger *zap.Logger
db *sqlx.DB
erupeConfig *config.Config
erupeConfig *_config.Config
acceptConns chan net.Conn
deleteConns chan net.Conn
sessions map[net.Conn]*Session

View File

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

View File

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

View File

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

View File

@@ -3,13 +3,12 @@ package entranceserver
import (
"encoding/binary"
"encoding/hex"
"erupe-ce/common/stringsupport"
_config "erupe-ce/config"
"fmt"
"net"
"erupe-ce/common/stringsupport"
"erupe-ce/common/byteframe"
"erupe-ce/config"
"erupe-ce/server/channelserver"
)
@@ -19,11 +18,22 @@ var season uint8
// Server Channels
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
bf := byteframe.NewByteFrame()
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
err := s.db.QueryRow("SELECT season FROM servers WHERE server_id=$1", sid).Scan(&season)
if err != nil {
@@ -42,12 +52,29 @@ func encodeServerInfo(config *config.Config, s *Server, local bool) []byte {
bf.WriteUint16(uint16(len(si.Channels)))
bf.WriteUint8(si.Type)
bf.WriteUint8(season)
bf.WriteUint8(si.Recommended)
bf.WriteUint8(0) // Prevents malformed server name
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false))
bf.WriteUint32(si.AllowedClientFlags)
if s.erupeConfig.RealClientMode >= _config.G1 {
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
combined := append(stringsupport.UTF8ToSJIS(si.Name), []byte{0x00}...)
combined = append(combined, stringsupport.UTF8ToSJIS(si.Description)...)
bf.WriteBytes(stringsupport.PaddedString(string(combined), 65, false))
}
if s.erupeConfig.RealClientMode >= _config.GG {
bf.WriteUint32(si.AllowedClientFlags)
}
for channelIdx, ci := range si.Channels {
sid = (4096 + serverIdx*256) + (16 + channelIdx)
@@ -91,16 +118,39 @@ func makeHeader(data []byte, respType string, entryCount uint16, key byte) []byt
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
// 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)
if s.erupeConfig.DevMode && s.erupeConfig.DevModeOptions.LogOutboundMessages {
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.WriteBytes(makeHeader(rawServerData, "SV2", uint16(len(serverInfos)), 0x00))
bf.WriteBytes(makeHeader(rawServerData, respType, uint16(len(serverInfos)-(mf+ret)), 0x00))
return bf.Data()
}

View File

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

View File

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

View File

@@ -62,7 +62,7 @@ func (s *Session) handlePacket(pkt []byte) error {
case "VITASGN:100":
s.client = VITA
s.handlePSSGN(bf)
case "WIIUSGN:100":
case "WIIUSGN:100", "WIIUSGN:000":
s.client = WIIU
s.handleWIIUSGN(bf)
case "VITACOGLNK:100":
@@ -124,6 +124,13 @@ func (s *Session) handleWIIUSGN(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.ReadBytes(2) // VITA = 1, PS3 = !
_ = bf.ReadBytes(82)

View File

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

View File

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