Merge pull request #5 from ZeruLight/feature/myseries

my series patch
This commit is contained in:
wish
2022-07-26 21:30:20 +10:00
committed by GitHub
23 changed files with 979 additions and 973 deletions

View File

@@ -2,33 +2,45 @@ package stringstack
import (
"errors"
"sync"
)
// StringStack is a basic LIFO "stack" for storing strings.
type StringStack struct {
sync.Mutex
stack []string
Locked bool
stack []string
}
// New creates a new instance of StringStack
func New() *StringStack {
return &StringStack{}
return &StringStack{Locked: false}
}
// Set sets up a new StringStack
func (s *StringStack) Set(v string) {
s.stack = []string{v}
}
// Lock freezes the StringStack
func (s *StringStack) Lock() {
if !s.Locked {
s.Locked = true
}
}
// Unlock unfreezes the StringStack
func (s *StringStack) Unlock() {
if s.Locked {
s.Locked = false
}
}
// Push pushes a string onto the stack.
func (s *StringStack) Push(v string) {
s.Lock()
defer s.Unlock()
s.stack = append(s.stack, v)
}
// Pop pops a string from the stack.
func (s *StringStack) Pop() (string, error) {
s.Lock()
defer s.Unlock()
if len(s.stack) == 0 {
return "", errors.New("no items on stack")
}

View File

@@ -1,81 +1,82 @@
{
"host_ip": "127.0.0.1",
"bin_path": "bin",
"devmode": true,
"devmodeoptions": {
"serverName" : "",
"cleandb": false,
"maxlauncherhr": true,
"LogInboundMessages": false,
"LogOutboundMessages": false,
"Event": 0,
"DivaEvent": 0,
"FestaEvent": 0,
"TournamentEvent": 0,
"MezFesEvent": true,
"SaveDumps": {
"Enabled": true,
"OutputDir": "savedata"
}
},
"discord": {
"enabled": false,
"bottoken": "",
"realtimeChannelID": "",
"serverId": "",
"devRoles": [],
"devMode": false
},
"database": {
"host": "localhost",
"port": 5432,
"user": "postgres",
"password": "",
"database": "erupe"
},
"launcher": {
"port": 80,
"UseOriginalLauncherFiles": false
},
"sign": {
"port": 53312
},
"entrance": {
"port": 53310,
"entries": [
{
"name": "Newbie", "description": "", "ip": "", "type": 3, "recommended": 2, "allowedclientflags": 0,
"channels": [
{ "port": 54001, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 },
{ "port": 54002, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "Normal", "description": "", "ip": "", "type": 1, "recommended": 0, "allowedclientflags": 0,
"channels": [
{ "port": 54003, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 },
{ "port": 54004, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "Cities", "description": "", "ip": "", "type": 2, "recommended": 0, "allowedclientflags": 0,
"channels": [
{ "port": 54005, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "Tavern", "description": "", "ip": "", "type": 4, "recommended": 0, "allowedclientflags": 0,
"channels": [
{ "port": 54006, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "Return", "description": "", "ip": "", "type": 5, "recommended": 0, "allowedclientflags": 0,
"channels": [
{ "port": 54007, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "MezFes", "description": "", "ip": "", "type": 6, "recommended": 6, "allowedclientflags": 0,
"channels": [
{ "port": 54008, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}
]
}
}
{
"host_ip": "127.0.0.1",
"bin_path": "bin",
"devmode": true,
"devmodeoptions": {
"serverName" : "",
"cleandb": false,
"maxlauncherhr": true,
"LogInboundMessages": false,
"LogOutboundMessages": false,
"MaxHexdumpLength": 256,
"Event": 0,
"DivaEvent": 0,
"FestaEvent": 0,
"TournamentEvent": 0,
"MezFesEvent": true,
"SaveDumps": {
"Enabled": true,
"OutputDir": "savedata"
}
},
"discord": {
"enabled": false,
"bottoken": "",
"realtimeChannelID": "",
"serverId": "",
"devRoles": [],
"devMode": false
},
"database": {
"host": "localhost",
"port": 5432,
"user": "postgres",
"password": "",
"database": "erupe"
},
"launcher": {
"port": 80,
"UseOriginalLauncherFiles": false
},
"sign": {
"port": 53312
},
"entrance": {
"port": 53310,
"entries": [
{
"name": "Newbie", "description": "", "ip": "", "type": 3, "recommended": 2, "allowedclientflags": 0,
"channels": [
{ "port": 54001, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 },
{ "port": 54002, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "Normal", "description": "", "ip": "", "type": 1, "recommended": 0, "allowedclientflags": 0,
"channels": [
{ "port": 54003, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 },
{ "port": 54004, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "Cities", "description": "", "ip": "", "type": 2, "recommended": 0, "allowedclientflags": 0,
"channels": [
{ "port": 54005, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "Tavern", "description": "", "ip": "", "type": 4, "recommended": 0, "allowedclientflags": 0,
"channels": [
{ "port": 54006, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "Return", "description": "", "ip": "", "type": 5, "recommended": 0, "allowedclientflags": 0,
"channels": [
{ "port": 54007, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}, {
"name": "MezFes", "description": "", "ip": "", "type": 6, "recommended": 6, "allowedclientflags": 0,
"channels": [
{ "port": 54008, "MaxPlayers": 50, "Unk0": 319, "Unk1": 252, "Unk2": 248 }
]
}
]
}
}

View File

@@ -1,145 +1,146 @@
package config
import (
"log"
"net"
"github.com/spf13/viper"
)
// Config holds the global server-wide config.
type Config struct {
HostIP string `mapstructure:"host_ip"`
BinPath string `mapstructure:"bin_path"`
DevMode bool
DevModeOptions DevModeOptions
Discord Discord
Database Database
Launcher Launcher
Sign Sign
Entrance Entrance
}
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
type DevModeOptions struct {
ServerName string // To get specific instance server about (Current Players/Event Week)
CleanDB bool // Automatically wipes the DB on server reset.
MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds.
FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages
LogInboundMessages bool // Log all messages sent to the server
LogOutboundMessages bool // Log all messages sent to the clients
DivaEvent int // Diva Defense event status
FestaEvent int // Hunter's Festa event status
TournamentEvent int // VS Tournament event status
MezFesEvent bool // MezFes status
SaveDumps SaveDumpOptions
}
type SaveDumpOptions struct {
Enabled bool
OutputDir string
}
// Discord holds the discord integration config.
type Discord struct {
Enabled bool
BotToken string
ServerID string
RealtimeChannelID string
DevRoles []string
DevMode bool
}
// Database holds the postgres database config.
type Database struct {
Host string
Port int
User string
Password string
Database string
}
// Launcher holds the launcher server config.
type Launcher struct {
Port int
UseOriginalLauncherFiles bool
}
// Sign holds the sign server config.
type Sign struct {
Port int
}
// Entrance holds the entrance server config.
type Entrance struct {
Port uint16
Entries []EntranceServerInfo
}
// EntranceServerInfo represents an entry in the serverlist.
type EntranceServerInfo struct {
IP string
Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar
Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue
Recommended uint8 // Something to do with server recommendation on 0, 3, and 5.
Name string // Server name, 66 byte null terminated Shift-JIS(JP) or Big5(TW).
Description string // Server description
// 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing?
// THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"!
AllowedClientFlags uint32
Channels []EntranceChannelInfo
}
// EntranceChannelInfo represents an entry in a server's channel list.
type EntranceChannelInfo struct {
Port uint16
MaxPlayers uint16
CurrentPlayers uint16
Unk0 uint16
Unk1 uint16
Unk2 uint16
}
// getOutboundIP4 gets the preferred outbound ip4 of this machine
// From https://stackoverflow.com/a/37382208
func getOutboundIP4() net.IP {
conn, err := net.Dial("udp4", "8.8.8.8:80")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.To4()
}
// LoadConfig loads the given config toml file.
func LoadConfig() (*Config, error) {
viper.SetConfigName("config")
viper.AddConfigPath(".")
viper.SetDefault("DevModeOptions.SaveDumps", SaveDumpOptions{
Enabled: false,
OutputDir: "savedata",
})
err := viper.ReadInConfig()
if err != nil {
return nil, err
}
c := &Config{}
err = viper.Unmarshal(c)
if err != nil {
return nil, err
}
if c.HostIP == "" {
c.HostIP = getOutboundIP4().To4().String()
}
return c, nil
}
package config
import (
"log"
"net"
"github.com/spf13/viper"
)
// Config holds the global server-wide config.
type Config struct {
HostIP string `mapstructure:"host_ip"`
BinPath string `mapstructure:"bin_path"`
DevMode bool
DevModeOptions DevModeOptions
Discord Discord
Database Database
Launcher Launcher
Sign Sign
Entrance Entrance
}
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
type DevModeOptions struct {
ServerName string // To get specific instance server about (Current Players/Event Week)
CleanDB bool // Automatically wipes the DB on server reset.
MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds.
FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages
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
SaveDumps SaveDumpOptions
}
type SaveDumpOptions struct {
Enabled bool
OutputDir string
}
// Discord holds the discord integration config.
type Discord struct {
Enabled bool
BotToken string
ServerID string
RealtimeChannelID string
DevRoles []string
DevMode bool
}
// Database holds the postgres database config.
type Database struct {
Host string
Port int
User string
Password string
Database string
}
// Launcher holds the launcher server config.
type Launcher struct {
Port int
UseOriginalLauncherFiles bool
}
// Sign holds the sign server config.
type Sign struct {
Port int
}
// Entrance holds the entrance server config.
type Entrance struct {
Port uint16
Entries []EntranceServerInfo
}
// EntranceServerInfo represents an entry in the serverlist.
type EntranceServerInfo struct {
IP string
Type uint8 // Server type. 0=?, 1=open, 2=cities, 3=newbie, 4=bar
Season uint8 // Server activity. 0 = green, 1 = orange, 2 = blue
Recommended uint8 // Something to do with server recommendation on 0, 3, and 5.
Name string // Server name, 66 byte null terminated Shift-JIS(JP) or Big5(TW).
Description string // Server description
// 4096(PC, PS3/PS4)?, 8258(PC, PS3/PS4)?, 8192 == nothing?
// THIS ONLY EXISTS IF Binary8Header.type == "SV2", NOT "SVR"!
AllowedClientFlags uint32
Channels []EntranceChannelInfo
}
// EntranceChannelInfo represents an entry in a server's channel list.
type EntranceChannelInfo struct {
Port uint16
MaxPlayers uint16
CurrentPlayers uint16
Unk0 uint16
Unk1 uint16
Unk2 uint16
}
// getOutboundIP4 gets the preferred outbound ip4 of this machine
// From https://stackoverflow.com/a/37382208
func getOutboundIP4() net.IP {
conn, err := net.Dial("udp4", "8.8.8.8:80")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
localAddr := conn.LocalAddr().(*net.UDPAddr)
return localAddr.IP.To4()
}
// LoadConfig loads the given config toml file.
func LoadConfig() (*Config, error) {
viper.SetConfigName("config")
viper.AddConfigPath(".")
viper.SetDefault("DevModeOptions.SaveDumps", SaveDumpOptions{
Enabled: false,
OutputDir: "savedata",
})
err := viper.ReadInConfig()
if err != nil {
return nil, err
}
c := &Config{}
err = viper.Unmarshal(c)
if err != nil {
return nil, err
}
if c.HostIP == "" {
c.HostIP = getOutboundIP4().To4().String()
}
return c, nil
}

26
Erupe/gook.sql Normal file
View File

@@ -0,0 +1,26 @@
BEGIN;
ALTER TABLE IF EXISTS public.gook
DROP COLUMN IF EXISTS gook0status;
ALTER TABLE IF EXISTS public.gook
DROP COLUMN IF EXISTS gook1status;
ALTER TABLE IF EXISTS public.gook
DROP COLUMN IF EXISTS gook2status;
ALTER TABLE IF EXISTS public.gook
DROP COLUMN IF EXISTS gook3status;
ALTER TABLE IF EXISTS public.gook
DROP COLUMN IF EXISTS gook4status;
ALTER TABLE IF EXISTS public.gook
DROP COLUMN IF EXISTS gook5status;
ALTER TABLE IF EXISTS public.gook
DROP COLUMN IF EXISTS gook5;
UPDATE public.gook SET gook1=NULL, gook2=NULL, gook3=NULL, gook4=NULL;
END;

View File

@@ -2,6 +2,7 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/stringsupport"
"erupe-ce/common/byteframe"
"erupe-ce/network"
@@ -14,7 +15,7 @@ type MsgMhfEnumerateHouse struct {
CharID uint32
Method uint8
Unk uint16
Name []byte
Name string
}
// Opcode returns the ID associated with this packet type.
@@ -29,7 +30,7 @@ func (m *MsgMhfEnumerateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cli
m.Method = bf.ReadUint8()
m.Unk = bf.ReadUint16()
_ = bf.ReadUint8() // len
m.Name = bf.ReadNullTerminatedBytes()
m.Name = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
return nil
}

View File

@@ -1,17 +1,17 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfEnumerateTitle represents the MSG_MHF_ENUMERATE_TITLE
type MsgMhfEnumerateTitle struct {
AckHandle uint32
Unk0 uint32
AckHandle uint32
CharID uint32
}
// Opcode returns the ID associated with this packet type.
@@ -21,9 +21,9 @@ func (m *MsgMhfEnumerateTitle) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgMhfEnumerateTitle) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint32()
return nil
m.AckHandle = bf.ReadUint32()
m.CharID = bf.ReadUint32()
return nil
}
// Build builds a binary packet from the current data.

View File

@@ -1,29 +1,23 @@
package mhfpacket
import (
"errors"
"errors"
"erupe-ce/common/stringsupport"
"erupe-ce/network/clientctx"
"erupe-ce/network"
"erupe-ce/common/byteframe"
"erupe-ce/network"
"erupe-ce/network/clientctx"
)
// MsgMhfLoadHouse represents the MSG_MHF_LOAD_HOUSE
type MsgMhfLoadHouse struct {
AckHandle uint32
CharID uint32
// dest?
// 0x3 = house
// 0x4 = bookshelf
// 0x5 = gallery
// 0x8 = tore
// 0x9 = own house
// 0xA = garden
Unk1 uint8
// bool inMezSquare?
Unk2 uint8
Unk3 uint16 // Hardcoded 0 in binary
Password []byte
AckHandle uint32
CharID uint32
Destination uint8
// False if already in hosts My Series, in case host updates PW
CheckPass bool
Unk3 uint16 // Hardcoded 0 in binary
Password string
}
// Opcode returns the ID associated with this packet type.
@@ -35,11 +29,11 @@ func (m *MsgMhfLoadHouse) Opcode() network.PacketID {
func (m *MsgMhfLoadHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.CharID = bf.ReadUint32()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint8()
m.Destination = bf.ReadUint8()
m.CheckPass = bf.ReadBool()
_ = bf.ReadUint16()
_ = bf.ReadUint8() // Password length
m.Password = bf.ReadNullTerminatedBytes()
_ = bf.ReadUint8() // Password length
m.Password = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
return nil
}

View File

@@ -1,42 +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"
)
// GuacotUpdateEntry represents an entry inside the MsgMhfUpdateGuacot packet.
type GuacotUpdateEntry struct {
Unk0 uint32
Unk1 uint16
Unk2 uint16
Unk3 uint16
Unk4 uint16
Unk5 uint16
Unk6 uint16
Unk7 uint16
Unk8 uint16
Unk9 uint16
Unk10 uint16
Unk11 uint16
Unk12 uint16
Unk13 uint16
Unk14 uint16
Unk15 uint16
Unk16 uint16
Unk17 uint16
Unk18 uint16
Unk19 uint16
Unk20 uint16
Unk21 uint16
Unk22 uint16
Unk23 uint32
Unk24 uint32
DataSize uint8
RawDataPayload []byte
type Gook struct {
Exists bool
Index uint32
Type uint16
Data []byte
NameLen uint8
Name []byte
}
// MsgMhfUpdateGuacot represents the MSG_MHF_UPDATE_GUACOT
@@ -44,7 +22,7 @@ type MsgMhfUpdateGuacot struct {
AckHandle uint32
EntryCount uint16
Unk0 uint16 // Hardcoded 0 in binary
Entries []*GuacotUpdateEntry
Gooks []Gook
}
// Opcode returns the ID associated with this packet type.
@@ -58,38 +36,18 @@ func (m *MsgMhfUpdateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
m.EntryCount = bf.ReadUint16()
m.Unk0 = bf.ReadUint16()
for i := 0; i < int(m.EntryCount); i++ {
// Yikes.
e := &GuacotUpdateEntry{}
e.Unk0 = bf.ReadUint32()
e.Unk1 = bf.ReadUint16()
e.Unk2 = bf.ReadUint16()
e.Unk3 = bf.ReadUint16()
e.Unk4 = bf.ReadUint16()
e.Unk5 = bf.ReadUint16()
e.Unk6 = bf.ReadUint16()
e.Unk7 = bf.ReadUint16()
e.Unk8 = bf.ReadUint16()
e.Unk9 = bf.ReadUint16()
e.Unk10 = bf.ReadUint16()
e.Unk11 = bf.ReadUint16()
e.Unk12 = bf.ReadUint16()
e.Unk13 = bf.ReadUint16()
e.Unk14 = bf.ReadUint16()
e.Unk15 = bf.ReadUint16()
e.Unk16 = bf.ReadUint16()
e.Unk17 = bf.ReadUint16()
e.Unk18 = bf.ReadUint16()
e.Unk19 = bf.ReadUint16()
e.Unk20 = bf.ReadUint16()
e.Unk21 = bf.ReadUint16()
e.Unk22 = bf.ReadUint16()
e.Unk23 = bf.ReadUint32()
e.Unk24 = bf.ReadUint32()
e.DataSize = bf.ReadUint8()
e.RawDataPayload = bf.ReadBytes(uint(e.DataSize))
m.Entries = append(m.Entries, e)
e := Gook{}
e.Index = bf.ReadUint32()
e.Type = bf.ReadUint16()
e.Data = bf.ReadBytes(50)
e.NameLen = bf.ReadUint8()
e.Name = bf.ReadBytes(uint(e.NameLen))
if e.Type > 0 {
e.Exists = true
} else {
e.Exists = false
}
m.Gooks = append(m.Gooks, e)
}
return nil
}
@@ -98,36 +56,3 @@ func (m *MsgMhfUpdateGuacot) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien
func (m *MsgMhfUpdateGuacot) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
return errors.New("NOT IMPLEMENTED")
}
func (m *MsgMhfUpdateGuacot) GuacotUpdateEntryToBytes(Entry *GuacotUpdateEntry) []byte {
resp:= byteframe.NewByteFrame()
resp.WriteUint32(Entry.Unk0)
resp.WriteUint16(Entry.Unk1)
resp.WriteUint16(Entry.Unk1)
resp.WriteUint16(Entry.Unk2)
resp.WriteUint16(Entry.Unk3)
resp.WriteUint16(Entry.Unk4)
resp.WriteUint16(Entry.Unk5)
resp.WriteUint16(Entry.Unk6)
resp.WriteUint16(Entry.Unk7)
resp.WriteUint16(Entry.Unk8)
resp.WriteUint16(Entry.Unk9)
resp.WriteUint16(Entry.Unk10)
resp.WriteUint16(Entry.Unk11)
resp.WriteUint16(Entry.Unk12)
resp.WriteUint16(Entry.Unk13)
resp.WriteUint16(Entry.Unk14)
resp.WriteUint16(Entry.Unk15)
resp.WriteUint16(Entry.Unk16)
resp.WriteUint16(Entry.Unk17)
resp.WriteUint16(Entry.Unk18)
resp.WriteUint16(Entry.Unk19)
resp.WriteUint16(Entry.Unk20)
resp.WriteUint16(Entry.Unk21)
resp.WriteUint16(Entry.Unk22)
resp.WriteUint32(Entry.Unk23)
resp.WriteUint32(Entry.Unk24)
resp.WriteUint8(Entry.DataSize)
resp.WriteBytes(Entry.RawDataPayload)
return resp.Data()
}

View File

@@ -2,6 +2,7 @@ package mhfpacket
import (
"errors"
"erupe-ce/common/stringsupport"
"erupe-ce/common/byteframe"
"erupe-ce/network"
@@ -11,15 +12,10 @@ import (
// MsgMhfUpdateHouse represents the MSG_MHF_UPDATE_HOUSE
type MsgMhfUpdateHouse struct {
AckHandle uint32
// 01 = closed
// 02 = open anyone
// 03 = open friends
// 04 = open guild
// 05 = open friends guild
State uint8
Unk1 uint8 // Always 0x01
Unk2 uint16 // Always 0x0000
Password string
State uint8
Unk1 uint8 // Always 0x01
Unk2 uint16 // Always 0x0000
Password string
}
// Opcode returns the ID associated with this packet type.
@@ -33,8 +29,8 @@ func (m *MsgMhfUpdateHouse) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client
m.State = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.Unk2 = bf.ReadUint16()
_ = bf.ReadUint8()
m.Password = string(bf.ReadNullTerminatedBytes())
_ = bf.ReadUint8() // Password length
m.Password = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes())
return nil
}

View File

@@ -11,7 +11,7 @@ import (
// MsgSysOperateRegister represents the MSG_SYS_OPERATE_REGISTER
type MsgSysOperateRegister struct {
AckHandle uint32
RegisterID uint32
SemaphoreID uint32
fixedZero uint16
RawDataPayload []byte
}
@@ -24,7 +24,7 @@ func (m *MsgSysOperateRegister) Opcode() network.PacketID {
// Parse parses the packet from binary
func (m *MsgSysOperateRegister) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
m.AckHandle = bf.ReadUint32()
m.RegisterID = bf.ReadUint32()
m.SemaphoreID = bf.ReadUint32()
m.fixedZero = bf.ReadUint16()
if m.fixedZero != 0 {
@@ -39,7 +39,7 @@ func (m *MsgSysOperateRegister) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Cl
// Build builds a binary packet from the current data.
func (m *MsgSysOperateRegister) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error {
bf.WriteUint32(m.AckHandle)
bf.WriteUint32(m.RegisterID)
bf.WriteUint32(m.SemaphoreID)
bf.WriteUint16(0)
bf.WriteUint16(uint16(len(m.RawDataPayload)))
bf.WriteBytes(m.RawDataPayload)

View File

@@ -4,8 +4,8 @@ import (
"bytes"
"encoding/binary"
"encoding/hex"
"fmt"
"io/ioutil"
"math/bits"
"math/rand"
@@ -579,125 +579,51 @@ func handleMsgMhfCheckWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfExchangeWeeklyStamp(s *Session, p mhfpacket.MHFPacket) {}
func getGookData(s *Session, cid uint32) (uint16, []byte) {
var data []byte
var count uint16
bf := byteframe.NewByteFrame()
for i := 0; i < 5; i++ {
err := s.server.db.QueryRow(fmt.Sprintf("SELECT gook%d FROM gook WHERE id=$1", i), cid).Scan(&data)
if err == nil && data != nil {
count++
if s.charID == cid && count == 1 {
gook := byteframe.NewByteFrameFromBytes(data)
bf.WriteBytes(gook.ReadBytes(4))
d := gook.ReadBytes(2)
bf.WriteBytes(d)
bf.WriteBytes(d)
bf.WriteBytes(gook.DataFromCurrent())
} else {
bf.WriteBytes(data)
}
}
}
return count, bf.Data()
}
func handleMsgMhfEnumerateGuacot(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateGuacot)
var data bool
err := s.server.db.QueryRow("SELECT gook0status FROM gook WHERE id = $1", s.charID).Scan(&data)
if err == nil {
tempresp := byteframe.NewByteFrame()
count := uint16(0)
var gook0 []byte
var gook1 []byte
var gook2 []byte
var gook3 []byte
var gook4 []byte
var gook5 []byte
var gook0status bool
var gook1status bool
var gook2status bool
var gook3status bool
var gook4status bool
var gook5status bool
_ = s.server.db.QueryRow("SELECT gook0 FROM gook WHERE id = $1", s.charID).Scan(&gook0)
_ = s.server.db.QueryRow("SELECT gook1 FROM gook WHERE id = $1", s.charID).Scan(&gook1)
_ = s.server.db.QueryRow("SELECT gook2 FROM gook WHERE id = $1", s.charID).Scan(&gook2)
_ = s.server.db.QueryRow("SELECT gook3 FROM gook WHERE id = $1", s.charID).Scan(&gook3)
_ = s.server.db.QueryRow("SELECT gook4 FROM gook WHERE id = $1", s.charID).Scan(&gook4)
_ = s.server.db.QueryRow("SELECT gook5 FROM gook WHERE id = $1", s.charID).Scan(&gook5)
_ = s.server.db.QueryRow("SELECT gook0status FROM gook WHERE id = $1", s.charID).Scan(&gook0status)
_ = s.server.db.QueryRow("SELECT gook1status FROM gook WHERE id = $1", s.charID).Scan(&gook1status)
_ = s.server.db.QueryRow("SELECT gook2status FROM gook WHERE id = $1", s.charID).Scan(&gook2status)
_ = s.server.db.QueryRow("SELECT gook3status FROM gook WHERE id = $1", s.charID).Scan(&gook3status)
_ = s.server.db.QueryRow("SELECT gook4status FROM gook WHERE id = $1", s.charID).Scan(&gook4status)
_ = s.server.db.QueryRow("SELECT gook5status FROM gook WHERE id = $1", s.charID).Scan(&gook5status)
if gook0status == true {
count++
tempresp.WriteBytes(gook0)
}
if gook1status == true {
count++
tempresp.WriteBytes(gook1)
}
if gook2status == true {
count++
tempresp.WriteBytes(gook2)
}
if gook3status == true {
count++
tempresp.WriteBytes(gook3)
}
if gook4status == true {
count++
tempresp.WriteBytes(gook4)
}
if gook5status == true {
count++
tempresp.WriteBytes(gook5)
}
if count == uint16(0) {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} else {
resp := byteframe.NewByteFrame()
resp.WriteUint16(count)
resp.WriteBytes(tempresp.Data())
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
} else {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
bf := byteframe.NewByteFrame()
count, data := getGookData(s, s.charID)
bf.WriteUint16(count)
bf.WriteBytes(data)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfUpdateGuacot(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateGuacot)
count := int(pkt.EntryCount)
fmt.Printf("handleMsgMhfUpdateGuacot:%d\n", count)
if count == 0 {
_, err := s.server.db.Exec("INSERT INTO gook(id,gook0status,gook1status,gook2status,gook3status,gook4status,gook5status) VALUES($1,bool(false),bool(false),bool(false),bool(false),bool(false),bool(false))", s.charID)
if err != nil {
fmt.Printf("INSERT INTO gook failure\n")
}
} else {
for i := 0; i < int(pkt.EntryCount); i++ {
gookindex := int(pkt.Entries[i].Unk0)
buf := pkt.GuacotUpdateEntryToBytes(pkt.Entries[i])
//fmt.Printf("gookindex:%d\n", gookindex)
switch gookindex {
case 0:
s.server.db.Exec("UPDATE gook SET gook0 = $1 WHERE id = $2", buf, s.charID)
if pkt.Entries[i].Unk1 != uint16(0) {
s.server.db.Exec("UPDATE gook SET gook0status = $1 WHERE id = $2", bool(true), s.charID)
} else {
s.server.db.Exec("UPDATE gook SET gook0status = $1 WHERE id = $2", bool(false), s.charID)
}
case 1:
s.server.db.Exec("UPDATE gook SET gook1 = $1 WHERE id = $2", buf, s.charID)
if pkt.Entries[i].Unk1 != uint16(0) {
s.server.db.Exec("UPDATE gook SET gook1status = $1 WHERE id = $2", bool(true), s.charID)
} else {
s.server.db.Exec("UPDATE gook SET gook1status = $1 WHERE id = $2", bool(false), s.charID)
}
case 2:
s.server.db.Exec("UPDATE gook SET gook2 = $1 WHERE id = $2", buf, s.charID)
if pkt.Entries[i].Unk1 != uint16(0) {
s.server.db.Exec("UPDATE gook SET gook2status = $1 WHERE id = $2", bool(true), s.charID)
} else {
s.server.db.Exec("UPDATE gook SET gook2status = $1 WHERE id = $2", bool(false), s.charID)
}
case 3:
s.server.db.Exec("UPDATE gook SET gook3 = $1 WHERE id = $2", buf, s.charID)
if pkt.Entries[i].Unk1 != uint16(0) {
s.server.db.Exec("UPDATE gook SET gook3status = $1 WHERE id = $2", bool(true), s.charID)
} else {
s.server.db.Exec("UPDATE gook SET gook3status = $1 WHERE id = $2", bool(false), s.charID)
}
case 4:
s.server.db.Exec("UPDATE gook SET gook4 = $1 WHERE id = $2", buf, s.charID)
if pkt.Entries[i].Unk1 != uint16(0) {
s.server.db.Exec("UPDATE gook SET gook4status = $1 WHERE id = $2", bool(true), s.charID)
} else {
s.server.db.Exec("UPDATE gook SET gook4status = $1 WHERE id = $2", bool(false), s.charID)
}
}
for _, gook := range pkt.Gooks {
if !gook.Exists {
s.server.db.Exec(fmt.Sprintf("UPDATE gook SET gook%d=NULL WHERE id=$1", gook.Index), s.charID)
} else {
bf := byteframe.NewByteFrame()
bf.WriteUint32(gook.Index)
bf.WriteUint16(gook.Type)
bf.WriteBytes(gook.Data)
bf.WriteUint8(gook.NameLen)
bf.WriteBytes(gook.Name)
s.server.db.Exec(fmt.Sprintf("UPDATE gook SET gook%d=$1 WHERE id=$2", gook.Index), bf.Data(), s.charID)
}
}
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})

View File

@@ -1,15 +1,15 @@
package channelserver
import (
"encoding/hex"
"encoding/binary"
"encoding/hex"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"erupe-ce/common/byteframe"
"erupe-ce/common/bfutil"
"erupe-ce/common/byteframe"
"erupe-ce/network/mhfpacket"
"erupe-ce/server/channelserver/compression/deltacomp"
"erupe-ce/server/channelserver/compression/nullcomp"
@@ -61,6 +61,14 @@ func handleMsgMhfSavedata(s *Session, p mhfpacket.MHFPacket) {
s.logger.Fatal("Failed to character weapon type in db", zap.Error(err))
}
s.myseries.houseTier = decompressedData[129900:129905] // 0x1FB6C + 5
s.myseries.houseData = decompressedData[130561:130756] // 0x1FE01 + 195
s.myseries.bookshelfData = decompressedData[139928:145504] // 0x22298 + 5576
// Gallery data also exists at 0x21578, is this the contest submission?
s.myseries.galleryData = decompressedData[140064:141812] // 0x22320 + 1748
s.myseries.toreData = decompressedData[130228:130468] // 0x1FCB4 + 240
s.myseries.gardenData = decompressedData[142424:142492] // 0x22C58 + 68
isMale := uint8(decompressedData[80]) // 0x50
if isMale == 1 {
_, err = s.server.db.Exec("UPDATE characters SET is_female=true WHERE id=$1", s.charID)

View File

@@ -263,7 +263,7 @@ func getCharInfo(server *Server, charName string) string {
objInfo := ""
obj := server.FindStageObjectByChar(c.charID)
obj := server.FindObjectByChar(c.charID)
// server.logger.Info("Found object: %+v", zap.Object("obj", obj))
if obj != nil {

View File

@@ -2,6 +2,8 @@ package channelserver
import (
"erupe-ce/common/byteframe"
ps "erupe-ce/common/pascalstring"
"erupe-ce/common/stringsupport"
"erupe-ce/network/mhfpacket"
"go.uber.org/zap"
)
@@ -15,29 +17,176 @@ func handleMsgMhfUpdateInterior(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateHouse)
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
type HouseData struct {
CharID uint32 `db:"id"`
HRP uint16 `db:"hrp"`
GR uint16 `db:"gr"`
Name string `db:"name"`
}
func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgMhfEnumerateHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateHouse)
bf := byteframe.NewByteFrame()
var houses []HouseData
switch pkt.Method {
case 1:
var friendsList string
s.server.db.QueryRow("SELECT friends FROM characters WHERE id=$1", s.charID).Scan(&friendsList)
cids := stringsupport.CSVElems(friendsList)
for _, cid := range cids {
house := HouseData{}
row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", cid)
err := row.StructScan(&house)
if err != nil {
panic(err)
} else {
houses = append(houses, house)
}
}
case 2:
guild, err := GetGuildInfoByCharacterId(s, s.charID)
if err != nil {
break
}
guildMembers, err := GetGuildMembers(s, guild.ID, false)
if err != nil {
break
}
for _, member := range guildMembers {
house := HouseData{}
row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", member.CharID)
err := row.StructScan(&house)
if err != nil {
panic(err)
} else {
houses = append(houses, house)
}
}
case 3:
house := HouseData{}
row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE name=$1", pkt.Name)
err := row.StructScan(&house)
if err != nil {
panic(err)
} else {
houses = append(houses, house)
}
case 4:
house := HouseData{}
row := s.server.db.QueryRowx("SELECT id, hrp, gr, name FROM characters WHERE id=$1", pkt.CharID)
err := row.StructScan(&house)
if err != nil {
panic(err)
} else {
houses = append(houses, house)
}
case 5: // Recent visitors
break
}
var exists int
for _, house := range houses {
for _, session := range s.server.sessions {
if session.charID == house.CharID {
exists++
bf.WriteUint32(house.CharID)
bf.WriteUint8(session.myseries.state)
if len(session.myseries.password) > 0 {
bf.WriteUint8(3)
} else {
bf.WriteUint8(0)
}
bf.WriteUint16(house.HRP)
bf.WriteUint16(house.GR)
ps.Uint8(bf, house.Name, true)
break
}
}
}
resp := byteframe.NewByteFrame()
resp.WriteUint16(uint16(exists))
resp.WriteBytes(bf.Data())
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
func handleMsgMhfUpdateHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfUpdateHouse)
// 01 = closed
// 02 = open anyone
// 03 = open friends
// 04 = open guild
// 05 = open friends+guild
s.myseries.state = pkt.State
s.myseries.password = pkt.Password
doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4))
}
func handleMsgMhfLoadHouse(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadHouse)
bf := byteframe.NewByteFrame()
var data []byte
err := s.server.db.QueryRow("SELECT house FROM characters WHERE id=$1", s.charID).Scan(&data)
if pkt.Destination != 9 && len(pkt.Password) > 0 && pkt.CheckPass {
for _, session := range s.server.sessions {
if session.charID == pkt.CharID && pkt.Password != session.myseries.password {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
return
}
}
}
var furniture []byte
err := s.server.db.QueryRow("SELECT house FROM characters WHERE id=$1", pkt.CharID).Scan(&furniture)
if err != nil {
panic(err)
}
if data == nil {
data = make([]byte, 20)
if furniture == nil {
furniture = make([]byte, 20)
}
if pkt.CharID != s.charID {
bf.WriteBytes(make([]byte, 219))
switch pkt.Destination {
case 3: // Others house
for _, session := range s.server.sessions {
if session.charID == pkt.CharID {
bf.WriteBytes(session.myseries.houseTier)
bf.WriteBytes(session.myseries.houseData)
bf.WriteBytes(make([]byte, 19)) // Padding?
bf.WriteBytes(furniture)
}
}
case 4: // Bookshelf
for _, session := range s.server.sessions {
if session.charID == pkt.CharID {
bf.WriteBytes(session.myseries.bookshelfData)
}
}
case 5: // Gallery
for _, session := range s.server.sessions {
if session.charID == pkt.CharID {
bf.WriteBytes(session.myseries.galleryData)
}
}
case 8: // Tore
for _, session := range s.server.sessions {
if session.charID == pkt.CharID {
bf.WriteBytes(session.myseries.toreData)
}
}
case 9: // Own house
bf.WriteBytes(furniture)
case 10: // Garden
for _, session := range s.server.sessions {
if session.charID == pkt.CharID {
bf.WriteBytes(session.myseries.gardenData)
c, d := getGookData(s, pkt.CharID)
bf.WriteUint16(c)
bf.WriteUint16(0)
bf.WriteBytes(d)
}
}
}
if len(bf.Data()) == 0 {
doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4))
} else {
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
bf.WriteBytes(data)
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}
func handleMsgMhfGetMyhouseInfo(s *Session, p mhfpacket.MHFPacket) {
@@ -145,14 +294,18 @@ func handleMsgMhfSaveDecoMyset(s *Session, p mhfpacket.MHFPacket) {
func handleMsgMhfEnumerateTitle(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfEnumerateTitle)
bf := byteframe.NewByteFrame()
titleCount := 114 // all titles unlocked
bf.WriteUint16(uint16(titleCount)) // title count
bf.WriteUint16(0) // unk
for i := 0; i < titleCount; i++ {
bf.WriteUint16(uint16(i))
bf.WriteUint16(0) // unk
bf.WriteUint32(0) // timestamp acquired
bf.WriteUint32(0) // timestamp updated
if pkt.CharID == s.charID {
titleCount := 114 // all titles unlocked
bf.WriteUint16(uint16(titleCount)) // title count
bf.WriteUint16(0) // unk
for i := 0; i < titleCount; i++ {
bf.WriteUint16(uint16(i))
bf.WriteUint16(0) // unk
bf.WriteUint32(0) // timestamp acquired
bf.WriteUint32(0) // timestamp updated
}
} else {
bf.WriteUint16(0)
}
doAckBufSucceed(s, pkt.AckHandle, bf.Data())
}

View File

@@ -10,44 +10,31 @@ import (
func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateObject)
// Lock the stage.
s.server.Lock()
// Make a new stage object and insert it into the stage.
objID := s.stage.GetNewObjectID(s.charID)
newObj := &StageObject{
id: objID,
s.stage.Lock()
newObj := &Object{
id: s.stage.NextObjectID(),
ownerCharID: s.charID,
x: pkt.X,
y: pkt.Y,
z: pkt.Z,
}
s.stage.objects[s.charID] = newObj
s.stage.Unlock()
// Unlock the stage.
s.server.Unlock()
// Response to our requesting client.
resp := byteframe.NewByteFrame()
resp.WriteUint32(objID) // New local obj handle.
resp.WriteUint32(newObj.id) // New local obj handle.
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data())
// Duplicate the object creation to all sessions in the same stage.
dupObjUpdate := &mhfpacket.MsgSysDuplicateObject{
ObjID: objID,
X: pkt.X,
Y: pkt.Y,
Z: pkt.Z,
OwnerCharID: s.charID,
ObjID: newObj.id,
X: newObj.x,
Y: newObj.y,
Z: newObj.z,
OwnerCharID: newObj.ownerCharID,
}
for i := 1; i <= 3; i++ {
s.server.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
BinaryType: uint8(i),
}, s)
}
s.logger.Info("Duplicate a new characters to others clients")
s.logger.Info(fmt.Sprintf("Broadcasting new object: %s (%d)", s.Name, s.charID))
s.stage.BroadcastMHF(dupObjUpdate, s)
}
@@ -74,7 +61,14 @@ func handleMsgSysRotateObject(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysDuplicateObject(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysSetObjectBinary)
for _, object := range s.stage.objects {
if object.id == pkt.ObjID {
object.binary = pkt.RawDataPayload
}
}
}
func handleMsgSysGetObjectBinary(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -1,7 +1,6 @@
package channelserver
import (
"encoding/hex"
"erupe-ce/common/byteframe"
"erupe-ce/network/mhfpacket"
)
@@ -10,129 +9,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysOperateRegister)
bf := byteframe.NewByteFrameFromBytes(pkt.RawDataPayload)
s.server.raviente.Lock()
switch pkt.RegisterID {
case 786461:
resp := byteframe.NewByteFrame()
size := 6
for i := 0; i < len(bf.Data())-1; i += size {
op := bf.ReadUint8()
dest := bf.ReadUint8()
data := bf.ReadUint32()
resp.WriteUint8(1)
resp.WriteUint8(dest)
switch dest {
case 0:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.nextTime = data
case 1:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.startTime = data
case 2:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.killedTime = data
case 3:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.postTime = data
case 4:
ref := &s.server.raviente.register.register[0]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
case 5:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.carveQuest = data
case 6:
ref := &s.server.raviente.register.register[1]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
case 7:
ref := &s.server.raviente.register.register[2]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
case 8:
ref := &s.server.raviente.register.register[3]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
case 9:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.maxPlayers = data
case 10:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.ravienteType = data
case 11:
ref := &s.server.raviente.register.register[4]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
default:
resp.WriteUint32(0)
resp.WriteUint32(0)
}
}
resp.WriteUint8(0)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
case 917533:
if pkt.SemaphoreID == s.server.raviente.state.semaphoreID {
resp := byteframe.NewByteFrame()
size := 6
for i := 0; i < len(bf.Data())-1; i += size {
@@ -152,12 +29,12 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
} else if dest == 17 { // Berserk poison tracker
if damageMultiplier == 1 {
resp.WriteUint32(*ref + data)
*ref += data
} else {
resp.WriteUint32(*ref + data)
*ref += data
}
} else {
resp.WriteUint32(*ref + data * damageMultiplier)
resp.WriteUint32(*ref + data*damageMultiplier)
*ref += data * damageMultiplier
}
case 13:
@@ -170,8 +47,7 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
}
resp.WriteUint8(0)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
case 851997:
} else if pkt.SemaphoreID == s.server.raviente.support.semaphoreID {
resp := byteframe.NewByteFrame()
size := 6
for i := 0; i < len(bf.Data())-1; i += size {
@@ -196,6 +72,126 @@ func handleMsgSysOperateRegister(s *Session, p mhfpacket.MHFPacket) {
}
resp.WriteUint8(0)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
} else if pkt.SemaphoreID == s.server.raviente.register.semaphoreID {
resp := byteframe.NewByteFrame()
size := 6
for i := 0; i < len(bf.Data())-1; i += size {
op := bf.ReadUint8()
dest := bf.ReadUint8()
data := bf.ReadUint32()
resp.WriteUint8(1)
resp.WriteUint8(dest)
switch dest {
case 0:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.nextTime = data
case 1:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.startTime = data
case 2:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.killedTime = data
case 3:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.postTime = data
case 4:
ref := &s.server.raviente.register.register[0]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
case 5:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.carveQuest = data
case 6:
ref := &s.server.raviente.register.register[1]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
case 7:
ref := &s.server.raviente.register.register[2]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
case 8:
ref := &s.server.raviente.register.register[3]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
case 9:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.maxPlayers = data
case 10:
resp.WriteUint32(0)
resp.WriteUint32(data)
s.server.raviente.register.ravienteType = data
case 11:
ref := &s.server.raviente.register.register[4]
switch op {
case 2:
resp.WriteUint32(*ref)
resp.WriteUint32(*ref + uint32(data))
*ref += data
case 13:
resp.WriteUint32(0)
resp.WriteUint32(data)
*ref = data
case 14:
resp.WriteUint32(0)
resp.WriteUint32(data)
}
default:
resp.WriteUint32(0)
resp.WriteUint32(0)
}
}
resp.WriteUint8(0)
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
s.notifyall()
s.server.raviente.Unlock()
@@ -205,74 +201,66 @@ func handleMsgSysLoadRegister(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysLoadRegister)
r := pkt.Unk1
switch r {
case 12:
if pkt.RegisterID == 983077 {
data, _ := hex.DecodeString("000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
doAckBufFail(s, pkt.AckHandle, data)
} else if pkt.RegisterID == 983069 {
data, _ := hex.DecodeString("000C000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
doAckBufFail(s, pkt.AckHandle, data)
}
resp := byteframe.NewByteFrame()
resp.WriteUint8(0)
resp.WriteUint8(12)
resp.WriteUint32(s.server.raviente.register.nextTime)
resp.WriteUint32(s.server.raviente.register.startTime)
resp.WriteUint32(s.server.raviente.register.killedTime)
resp.WriteUint32(s.server.raviente.register.postTime)
resp.WriteUint32(s.server.raviente.register.register[0])
resp.WriteUint32(s.server.raviente.register.carveQuest)
resp.WriteUint32(s.server.raviente.register.register[1])
resp.WriteUint32(s.server.raviente.register.register[2])
resp.WriteUint32(s.server.raviente.register.register[3])
resp.WriteUint32(s.server.raviente.register.maxPlayers)
resp.WriteUint32(s.server.raviente.register.ravienteType)
resp.WriteUint32(s.server.raviente.register.register[4])
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
case 29:
resp := byteframe.NewByteFrame()
resp.WriteUint8(0)
resp.WriteUint8(29)
for _, v := range s.server.raviente.state.stateData {
resp.WriteUint32(v)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
case 25:
resp := byteframe.NewByteFrame()
resp.WriteUint8(0)
resp.WriteUint8(25)
for _, v := range s.server.raviente.support.supportData {
resp.WriteUint32(v)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
case 12:
resp := byteframe.NewByteFrame()
resp.WriteUint8(0)
resp.WriteUint8(12)
resp.WriteUint32(s.server.raviente.register.nextTime)
resp.WriteUint32(s.server.raviente.register.startTime)
resp.WriteUint32(s.server.raviente.register.killedTime)
resp.WriteUint32(s.server.raviente.register.postTime)
resp.WriteUint32(s.server.raviente.register.register[0])
resp.WriteUint32(s.server.raviente.register.carveQuest)
resp.WriteUint32(s.server.raviente.register.register[1])
resp.WriteUint32(s.server.raviente.register.register[2])
resp.WriteUint32(s.server.raviente.register.register[3])
resp.WriteUint32(s.server.raviente.register.maxPlayers)
resp.WriteUint32(s.server.raviente.register.ravienteType)
resp.WriteUint32(s.server.raviente.register.register[4])
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
case 29:
resp := byteframe.NewByteFrame()
resp.WriteUint8(0)
resp.WriteUint8(29)
for _, v := range s.server.raviente.state.stateData {
resp.WriteUint32(v)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
case 25:
resp := byteframe.NewByteFrame()
resp.WriteUint8(0)
resp.WriteUint8(25)
for _, v := range s.server.raviente.support.supportData {
resp.WriteUint32(v)
}
doAckBufSucceed(s, pkt.AckHandle, resp.Data())
}
}
// Unused
func (s *Session) notifyplayer() {
s.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0E, 0x00, 0x1D})
s.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0D, 0x00, 0x1D})
s.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0C, 0x00, 0x1D})
}
func (s *Session) notifyall() {
var temp mhfpacket.MHFPacket
raviNotif := byteframe.NewByteFrame()
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: s.server.raviente.support.semaphoreID}
raviNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(raviNotif, s.clientContext)
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: s.server.raviente.state.semaphoreID}
raviNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(raviNotif, s.clientContext)
temp = &mhfpacket.MsgSysNotifyRegister{RegisterID: s.server.raviente.register.semaphoreID}
raviNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(raviNotif, s.clientContext)
raviNotif.WriteUint16(0x0010) // End it.
if _, exists := s.server.semaphore["hs_l0u3B51J9k3"]; exists {
for session := range s.server.semaphore["hs_l0u3B51J9k3"].clients {
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0C, 0x00, 0x1D})
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0D, 0x00, 0x1D})
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0E, 0x00, 0x1D})
session.QueueSend(raviNotif.Data())
}
} else if _, exists := s.server.semaphore["hs_l0u3B5129k3"]; exists {
for session := range s.server.semaphore["hs_l0u3B5129k3"].clients {
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0C, 0x00, 0x1D})
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0D, 0x00, 0x1D})
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0E, 0x00, 0x1D})
session.QueueSend(raviNotif.Data())
}
} else if _, exists := s.server.semaphore["hs_l0u3B512Ak3"]; exists {
for session := range s.server.semaphore["hs_l0u3B512Ak3"].clients {
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0C, 0x00, 0x1D})
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0D, 0x00, 0x1D})
session.QueueSendNonBlocking([]byte{0x00, 0x3F, 0x00, 0x0E, 0x00, 0x1D})
session.QueueSend(raviNotif.Data())
}
}
}
@@ -288,27 +276,28 @@ func checkRaviSemaphore(s *Session) bool {
return false
}
func releaseRaviSemaphore(s *Session) {
s.server.raviente.Lock()
if _, exists := s.server.semaphore["hs_l0u3B51J9k3"]; exists {
if len(s.server.semaphore["hs_l0u3B51J9k3"].reservedClientSlots) == 0 {
resetRavi(s)
}
}
if _, exists := s.server.semaphore["hs_l0u3B5129k3"]; exists {
if len(s.server.semaphore["hs_l0u3B5129k3"].reservedClientSlots) == 0 {
resetRavi(s)
}
}
if _, exists := s.server.semaphore["hs_l0u3B512Ak3"]; exists {
if len(s.server.semaphore["hs_l0u3B512Ak3"].reservedClientSlots) == 0 {
resetRavi(s)
}
}
s.server.raviente.Unlock()
}
//func releaseRaviSemaphore(s *Session) {
// s.server.raviente.Lock()
// if _, exists := s.server.semaphore["hs_l0u3B51J9k3"]; exists {
// if len(s.server.semaphore["hs_l0u3B51J9k3"].reservedClientSlots) == 0 {
// resetRavi(s)
// }
// }
// if _, exists := s.server.semaphore["hs_l0u3B5129k3"]; exists {
// if len(s.server.semaphore["hs_l0u3B5129k3"].reservedClientSlots) == 0 {
// resetRavi(s)
// }
// }
// if _, exists := s.server.semaphore["hs_l0u3B512Ak3"]; exists {
// if len(s.server.semaphore["hs_l0u3B512Ak3"].reservedClientSlots) == 0 {
// resetRavi(s)
// }
// }
// s.server.raviente.Unlock()
//}
func resetRavi(s *Session) {
s.server.raviente.Lock()
s.server.raviente.register.nextTime = 0
s.server.raviente.register.startTime = 0
s.server.raviente.register.killedTime = 0
@@ -320,6 +309,7 @@ func resetRavi(s *Session) {
s.server.raviente.register.register = []uint32{0, 0, 0, 0, 0}
s.server.raviente.state.stateData = []uint32{0, 0, 0, 0, 0, 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.raviente.support.supportData = []uint32{0, 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.raviente.Unlock()
}
// Unused
@@ -334,4 +324,4 @@ func (s *Session) notifyticker() {
}
}
func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -1,7 +1,9 @@
package channelserver
import (
"erupe-ce/common/byteframe"
"fmt"
"go.uber.org/zap"
"strings"
"erupe-ce/network/mhfpacket"
@@ -10,11 +12,13 @@ import (
func removeSessionFromSemaphore(s *Session) {
s.server.semaphoreLock.Lock()
for _, semaphore := range s.server.semaphore {
if _, exists := semaphore.reservedClientSlots[s.charID]; exists {
delete(semaphore.reservedClientSlots, s.charID)
}
if _, exists := semaphore.clients[s]; exists {
delete(semaphore.clients, s)
}
}
releaseRaviSemaphore(s)
s.server.semaphoreLock.Unlock()
}
@@ -23,54 +27,50 @@ func handleMsgSysCreateSemaphore(s *Session, p mhfpacket.MHFPacket) {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x03, 0x00, 0x0d})
}
func destructEmptySemaphores(s *Session) {
s.server.semaphoreLock.Lock()
for id, sema := range s.server.semaphore {
if len(sema.reservedClientSlots) == 0 && len(sema.clients) == 0 {
s.server.semaphoreLock.Unlock()
delete(s.server.semaphore, id)
s.server.semaphoreLock.Lock()
if strings.HasPrefix(id, "hs_l0u3B51") {
releaseRaviSemaphore(s, sema)
}
s.logger.Debug("Destructed semaphore", zap.String("sema.id_semaphore", id))
}
}
s.server.semaphoreLock.Unlock()
}
func releaseRaviSemaphore(s *Session, sema *Semaphore) {
if !strings.HasSuffix(sema.id_semaphore, "5") {
delete(sema.reservedClientSlots, s.charID)
delete(sema.clients, s)
}
if len(sema.reservedClientSlots) == 0 && len(sema.clients) == 0 {
s.logger.Debug("Raviente semaphore is empty, resetting")
resetRavi(s)
}
}
func handleMsgSysDeleteSemaphore(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysDeleteSemaphore)
sem := pkt.AckHandle
if s.server.semaphore != nil {
destructEmptySemaphores(s)
s.server.semaphoreLock.Lock()
for id := range s.server.semaphore {
switch sem {
case 917533:
if s.server.semaphore[id].id_semaphore == "hs_l0u3B51J9k3" {
delete(s.server.semaphore["hs_l0u3B51J9k3"].reservedClientSlots, s.charID)
delete(s.server.semaphore["hs_l0u3B51J9k3"].clients, s)
} else if s.server.semaphore[id].id_semaphore == "hs_l0u3B5129k3" {
delete(s.server.semaphore["hs_l0u3B5129k3"].reservedClientSlots, s.charID)
delete(s.server.semaphore["hs_l0u3B5129k3"].clients, s)
} else if s.server.semaphore[id].id_semaphore == "hs_l0u3B512Ak3" {
delete(s.server.semaphore["hs_l0u3B512Ak3"].reservedClientSlots, s.charID)
delete(s.server.semaphore["hs_l0u3B512Ak3"].clients, s)
}
case 851997:
if s.server.semaphore[id].id_semaphore == "hs_l0u3B51J9k4" {
delete(s.server.semaphore["hs_l0u3B51J9k4"].reservedClientSlots, s.charID)
} else if s.server.semaphore[id].id_semaphore == "hs_l0u3B5129k4" {
delete(s.server.semaphore["hs_l0u3B5129k4"].reservedClientSlots, s.charID)
} else if s.server.semaphore[id].id_semaphore == "hs_l0u3B512Ak4" {
delete(s.server.semaphore["hs_l0u3B512Ak4"].reservedClientSlots, s.charID)
}
case 786461:
if s.server.semaphore[id].id_semaphore == "hs_l0u3B51J9k5" {
delete(s.server.semaphore["hs_l0u3B51J9k5"].reservedClientSlots, s.charID)
} else if s.server.semaphore[id].id_semaphore == "hs_l0u3B5129k5" {
delete(s.server.semaphore["hs_l0u3B5129k5"].reservedClientSlots, s.charID)
} else if s.server.semaphore[id].id_semaphore == "hs_l0u3B512Ak5" {
delete(s.server.semaphore["hs_l0u3B512Ak5"].reservedClientSlots, s.charID)
}
default:
if len(s.server.semaphore[id].reservedClientSlots) != 0 {
if s.server.semaphore[id].id_semaphore != "hs_l0u3B51J9k3" &&
s.server.semaphore[id].id_semaphore != "hs_l0u3B51J9k4" &&
s.server.semaphore[id].id_semaphore != "hs_l0u3B51J9k5" &&
s.server.semaphore[id].id_semaphore != "hs_l0u3B5129k3" &&
s.server.semaphore[id].id_semaphore != "hs_l0u3B5129k4" &&
s.server.semaphore[id].id_semaphore != "hs_l0u3B5129k5" &&
s.server.semaphore[id].id_semaphore != "hs_l0u3B512Ak3" &&
s.server.semaphore[id].id_semaphore != "hs_l0u3B512Ak4" &&
s.server.semaphore[id].id_semaphore != "hs_l0u3B512Ak5" {
delete(s.server.semaphore[id].reservedClientSlots, s.charID)
}
for id, sema := range s.server.semaphore {
if sema.id == sem {
if strings.HasPrefix(id, "hs_l0u3B51") {
releaseRaviSemaphore(s, sema)
s.server.semaphoreLock.Unlock()
return
}
s.server.semaphoreLock.Unlock()
delete(s.server.semaphore, id)
s.logger.Debug("Destructed semaphore", zap.String("sema.id_semaphore", id))
return
}
}
s.server.semaphoreLock.Unlock()
@@ -81,15 +81,22 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateAcquireSemaphore)
SemaphoreID := pkt.SemaphoreID
newSemaphore, gotNewStage := s.server.semaphore[SemaphoreID]
newSemaphore, exists := s.server.semaphore[SemaphoreID]
fmt.Printf("Got reserve stage req, StageID: %v\n\n", SemaphoreID)
if !gotNewStage {
if !exists {
s.server.semaphoreLock.Lock()
if strings.HasPrefix(SemaphoreID, "hs_l0u3B51") {
s.server.semaphore[SemaphoreID] = NewSemaphore(SemaphoreID, 32)
s.server.semaphore[SemaphoreID] = NewSemaphore(s.server, SemaphoreID, 32)
if strings.HasSuffix(SemaphoreID, "3") {
s.server.raviente.state.semaphoreID = s.server.semaphore[SemaphoreID].id
} else if strings.HasSuffix(SemaphoreID, "4") {
s.server.raviente.support.semaphoreID = s.server.semaphore[SemaphoreID].id
} else if strings.HasSuffix(SemaphoreID, "5") {
s.server.raviente.register.semaphoreID = s.server.semaphore[SemaphoreID].id
}
} else {
s.server.semaphore[SemaphoreID] = NewSemaphore(SemaphoreID, 1)
s.server.semaphore[SemaphoreID] = NewSemaphore(s.server, SemaphoreID, 1)
}
newSemaphore = s.server.semaphore[SemaphoreID]
s.server.semaphoreLock.Unlock()
@@ -98,35 +105,18 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
newSemaphore.Lock()
defer newSemaphore.Unlock()
if _, exists := newSemaphore.reservedClientSlots[s.charID]; exists {
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0F, 0x00, 0x1D})
bf := byteframe.NewByteFrame()
bf.WriteUint32(newSemaphore.id)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
} else if uint16(len(newSemaphore.reservedClientSlots)) < newSemaphore.maxPlayers {
switch SemaphoreID {
case "hs_l0u3B51J9k3", "hs_l0u3B5129k3", "hs_l0u3B512Ak3":
newSemaphore.reservedClientSlots[s.charID] = nil
newSemaphore.clients[s] = s.charID
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0E, 0x00, 0x1D})
case "hs_l0u3B51J9k4", "hs_l0u3B5129k4", "hs_l0u3B512Ak4":
newSemaphore.reservedClientSlots[s.charID] = nil
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0D, 0x00, 0x1D})
case "hs_l0u3B51J9k5", "hs_l0u3B5129k5", "hs_l0u3B512Ak5":
newSemaphore.reservedClientSlots[s.charID] = nil
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0C, 0x00, 0x1D})
default:
newSemaphore.reservedClientSlots[s.charID] = nil
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x0F, 0x00, 0x25})
}
newSemaphore.reservedClientSlots[s.charID] = nil
newSemaphore.clients[s] = s.charID
s.Lock()
s.semaphore = newSemaphore
s.Unlock()
bf := byteframe.NewByteFrame()
bf.WriteUint32(newSemaphore.id)
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
} else {
doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
@@ -138,7 +128,6 @@ func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) {
//pkt := p.(*mhfpacket.MsgSysReleaseSemaphore)
releaseRaviSemaphore(s)
}
func handleMsgSysCheckSemaphore(s *Session, p mhfpacket.MHFPacket) {

View File

@@ -86,7 +86,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
}
}
func removeEmptyStages(s *Session) {
func destructEmptyStages(s *Session) {
s.server.Lock()
defer s.server.Unlock()
for _, stage := range s.server.stages {
@@ -108,75 +108,33 @@ func removeSessionFromStage(s *Session) {
// Delete old stage objects owned by the client.
s.logger.Info("Sending notification to old stage clients")
for objID, stageObject := range s.stage.objects {
if stageObject.ownerCharID == s.charID {
clientNotif := byteframe.NewByteFrame()
var pkt mhfpacket.MHFPacket
pkt = &mhfpacket.MsgSysDeleteObject{
ObjID: stageObject.id,
}
clientNotif.WriteUint16(uint16(pkt.Opcode()))
pkt.Build(clientNotif, s.clientContext)
clientNotif.WriteUint16(0x0010)
for client, _ := range s.stage.clients {
client.QueueSend(clientNotif.Data())
}
// TODO(Andoryuuta): Should this be sent to the owner's client as well? it currently isn't.
// Actually delete it from the objects map.
delete(s.stage.objects, objID)
}
}
for objListID, stageObjectList := range s.stage.objectList {
if stageObjectList.charid == s.charID {
// Added to prevent duplicates from flooding ObjectMap and causing server hangs
s.stage.objectList[objListID].status = false
s.stage.objectList[objListID].charid = 0
for _, object := range s.stage.objects {
if object.ownerCharID == s.charID {
s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{ObjID: object.id}, s)
delete(s.stage.objects, object.ownerCharID)
}
}
s.stage.Unlock()
removeEmptyStages(s)
destructEmptyStages(s)
destructEmptySemaphores(s)
}
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnterStage)
// Push our current stage ID to the movement stack before entering another one.
s.Lock()
s.stageMoveStack.Push(s.stageID)
s.Unlock()
if s.stageID == "" {
s.stageMoveStack.Set(pkt.StageID)
} else {
s.stageMoveStack.Push(s.stageID)
s.stageMoveStack.Lock()
}
s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{})
if s.reservationStage != nil {
s.reservationStage = nil
}
if pkt.StageID == "sl1Ns200p0a0u0" { // First entry
var temp mhfpacket.MHFPacket
loginNotif := byteframe.NewByteFrame()
s.server.Lock()
for _, session := range s.server.sessions {
if s == session || !session.binariesDone {
continue
}
temp = &mhfpacket.MsgSysInsertUser{
CharID: session.charID,
}
loginNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(loginNotif, s.clientContext)
for i := 1; i <= 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: session.charID,
BinaryType: uint8(i),
}
loginNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(loginNotif, s.clientContext)
}
}
s.server.Unlock()
loginNotif.WriteUint16(0x0010) // End it.
if len(loginNotif.Data()) > 2 {
s.QueueSend(loginNotif.Data())
}
}
doStageTransfer(s, pkt.AckHandle, pkt.StageID)
}
@@ -184,9 +142,8 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysBackStage)
// Transfer back to the saved stage ID before the previous move or enter.
s.Lock()
s.stageMoveStack.Unlock()
backStage, err := s.stageMoveStack.Pop()
s.Unlock()
if err != nil {
panic(err)
@@ -198,10 +155,10 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysMoveStage)
// Push our current stage ID to the movement stack before entering another one.
s.Lock()
s.stageMoveStack.Push(s.stageID)
s.Unlock()
// Set a new move stack from the given stage ID if unlocked
if !s.stageMoveStack.Locked {
s.stageMoveStack.Set(pkt.StageID)
}
doStageTransfer(s, pkt.AckHandle, pkt.StageID)
}
@@ -218,11 +175,9 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
s.reservationStage.RLock()
defer s.reservationStage.RUnlock()
destructMessage := &mhfpacket.MsgSysStageDestruct{}
for charID := range s.reservationStage.reservedClientSlots {
session := s.server.FindSessionByCharID(charID)
session.QueueSendMHF(destructMessage)
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
}
s.server.Lock()

View File

@@ -12,6 +12,49 @@ func handleMsgSysInsertUser(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysDeleteUser(s *Session, p mhfpacket.MHFPacket) {}
func broadcastNewUser(s *Session) {
s.logger.Debug(fmt.Sprintf("Broadcasting new user: %s (%d)", s.Name, s.charID))
clientNotif := byteframe.NewByteFrame()
var temp mhfpacket.MHFPacket
for _, session := range s.server.sessions {
if session == s || !session.binariesDone {
continue
}
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
clientNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(clientNotif, s.clientContext)
for i := 0; i < 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: session.charID,
BinaryType: uint8(i + 1),
}
clientNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(clientNotif, s.clientContext)
}
}
s.QueueSend(clientNotif.Data())
serverNotif := byteframe.NewByteFrame()
temp = &mhfpacket.MsgSysInsertUser{CharID: s.charID}
serverNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(serverNotif, s.clientContext)
for i := 0; i < 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
BinaryType: uint8(i + 1),
}
serverNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(serverNotif, s.clientContext)
}
for _, session := range s.server.sessions {
if session == s || !session.binariesDone {
continue
}
session.QueueSend(serverNotif.Data())
}
}
func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysSetUserBinary)
s.server.userBinaryPartsLock.Lock()
@@ -27,9 +70,7 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
}
}
s.binariesDone = true
s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser{
CharID: s.charID,
}, s)
broadcastNewUser(s)
return
}

View File

@@ -71,8 +71,9 @@ type Server struct {
userBinaryParts map[userBinaryPartID][]byte
// Semaphore
semaphoreLock sync.RWMutex
semaphore map[string]*Semaphore
semaphoreLock sync.RWMutex
semaphore map[string]*Semaphore
semaphoreIndex uint32
// Discord chat integration
discordBot *discordbot.DiscordBot
@@ -92,55 +93,58 @@ type Raviente struct {
}
type RavienteRegister struct {
nextTime uint32
startTime uint32
postTime uint32
killedTime uint32
semaphoreID uint32
nextTime uint32
startTime uint32
postTime uint32
killedTime uint32
ravienteType uint32
maxPlayers uint32
carveQuest uint32
register []uint32
maxPlayers uint32
carveQuest uint32
register []uint32
}
type RavienteState struct {
semaphoreID uint32
damageMultiplier uint32
stateData []uint32
stateData []uint32
}
type RavienteSupport struct {
semaphoreID uint32
supportData []uint32
}
// Set up the Raviente variables for the server
func NewRaviente() *Raviente {
ravienteRegister := &RavienteRegister {
nextTime: 0,
startTime: 0,
killedTime: 0,
postTime: 0,
ravienteRegister := &RavienteRegister{
nextTime: 0,
startTime: 0,
killedTime: 0,
postTime: 0,
ravienteType: 0,
maxPlayers: 0,
carveQuest: 0,
maxPlayers: 0,
carveQuest: 0,
}
ravienteState := &RavienteState {
ravienteState := &RavienteState{
damageMultiplier: 1,
}
ravienteSupport := &RavienteSupport { }
ravienteSupport := &RavienteSupport{}
ravienteRegister.register = []uint32{0, 0, 0, 0, 0}
ravienteState.stateData = []uint32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
ravienteSupport.supportData = []uint32{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
raviente := &Raviente {
raviente := &Raviente{
register: ravienteRegister,
state: ravienteState,
support: ravienteSupport,
state: ravienteState,
support: ravienteSupport,
}
return raviente
}
// NewServer creates a new Server type.
func NewServer(config *Config) *Server {
s := &Server {
s := &Server{
ID: config.ID,
logger: config.Logger,
db: config.DB,
@@ -151,6 +155,7 @@ func NewServer(config *Config) *Server {
stages: make(map[string]*Stage),
userBinaryParts: make(map[userBinaryPartID][]byte),
semaphore: make(map[string]*Semaphore),
semaphoreIndex: 0,
discordBot: config.DiscordBot,
name: config.Name,
enable: config.Enable,
@@ -277,7 +282,7 @@ func (s *Server) manageSessions() {
func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Broadcast the data.
for _, session := range s.sessions {
if session == ignoredSession {
if session == ignoredSession || !session.binariesDone {
continue
}
@@ -295,14 +300,14 @@ func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session)
func (s *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
for _, c := range s.Channels {
for _, s := range c.sessions {
if s == ignoredSession {
for _, session := range c.sessions {
if session == ignoredSession || !session.binariesDone {
continue
}
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(pkt.Opcode()))
pkt.Build(bf, s.clientContext)
s.QueueSendNonBlocking(bf.Data())
pkt.Build(bf, session.clientContext)
session.QueueSendNonBlocking(bf.Data())
}
}
}
@@ -384,7 +389,7 @@ func (s *Server) FindSessionByCharID(charID uint32) *Session {
return nil
}
func (s *Server) FindStageObjectByChar(charID uint32) *StageObject {
func (s *Server) FindObjectByChar(charID uint32) *Object {
s.stagesLock.RLock()
defer s.stagesLock.RUnlock()
for _, stage := range s.stages {
@@ -401,3 +406,8 @@ func (s *Server) FindStageObjectByChar(charID uint32) *StageObject {
return nil
}
func (s *Server) NextSemaphoreID() uint32 {
s.semaphoreIndex = s.semaphoreIndex + 1
return s.semaphoreIndex
}

View File

@@ -14,6 +14,8 @@ type Semaphore struct {
// Stage ID string
id_semaphore string
id uint32
// Map of session -> charID.
// These are clients that are CURRENTLY in the stage
clients map[*Session]uint32
@@ -26,21 +28,21 @@ type Semaphore struct {
}
// NewStage creates a new stage with intialized values.
func NewSemaphore(ID string, MaxPlayers uint16) *Semaphore {
s := &Semaphore{
func NewSemaphore(s *Server, ID string, MaxPlayers uint16) *Semaphore {
sema := &Semaphore{
id_semaphore: ID,
id: s.NextSemaphoreID(),
clients: make(map[*Session]uint32),
reservedClientSlots: make(map[uint32]interface{}),
maxPlayers: MaxPlayers,
}
return s
return sema
}
func (s *Semaphore) BroadcastRavi(pkt mhfpacket.MHFPacket) {
// Broadcast the data.
for session := range s.clients {
// Make the header
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(pkt.Opcode()))
@@ -71,4 +73,4 @@ func (s *Semaphore) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Sessio
// Enqueue in a non-blocking way that drops the packet if the connections send buffer channel is full.
session.QueueSendNonBlocking(bf.Data())
}
}
}

View File

@@ -27,6 +27,7 @@ type Session struct {
sendPackets chan []byte
clientContext *clientctx.ClientContext
myseries MySeries
stageID string
stage *Stage
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
@@ -54,6 +55,17 @@ type Session struct {
Name string
}
type MySeries struct {
houseTier []byte
houseData []byte
bookshelfData []byte
galleryData []byte
toreData []byte
gardenData []byte
state uint8
password string
}
// NewSession creates a new Session type.
func NewSession(server *Server, conn net.Conn) *Session {
s := &Session{
@@ -239,5 +251,9 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien
}
fmt.Printf("[%s] -> [%s]\n", sender, recipient)
fmt.Printf("Opcode: %s\n", opcodePID)
fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data))
if len(data) <= s.server.erupeConfig.DevModeOptions.MaxHexdumpLength {
fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data))
} else {
fmt.Printf("Data [%d bytes]:\n(Too long!)\n\n", len(data))
}
}

View File

@@ -9,18 +9,13 @@ import (
"erupe-ce/network/mhfpacket"
)
// StageObject holds infomation about a specific stage object.
type StageObject struct {
// Object holds infomation about a specific object.
type Object struct {
sync.RWMutex
id uint32
ownerCharID uint32
x, y, z float32
}
type ObjectMap struct {
id uint8
charid uint32
status bool
binary []byte
}
// stageBinaryKey is a struct used as a map key for identifying a stage binary part.
@@ -36,13 +31,10 @@ type Stage struct {
// Stage ID string
id string
// Total count of objects ever created for this stage. Used for ObjID generation.
gameObjectCount uint32
// Objects
objects map[uint32]*Object
objectIndex uint32
// Save all object in stage
objects map[uint32]*StageObject
objectList map[uint8]*ObjectMap
// Map of session -> charID.
// These are clients that are CURRENTLY in the stage
clients map[*Session]uint32
@@ -66,14 +58,12 @@ func NewStage(ID string) *Stage {
id: ID,
clients: make(map[*Session]uint32),
reservedClientSlots: make(map[uint32]bool),
objects: make(map[uint32]*StageObject),
objects: make(map[uint32]*Object),
objectIndex: 0,
rawBinaryData: make(map[stageBinaryKey][]byte),
maxPlayers: 4,
gameObjectCount: 1,
objectList: make(map[uint8]*ObjectMap),
createdAt: time.Now().Format("01-02-2006 15:04:05"),
}
s.InitObjectList()
return s
}
@@ -81,7 +71,7 @@ func NewStage(ID string) *Stage {
func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Broadcast the data.
for session := range s.clients {
if session == ignoredSession {
if session == ignoredSession || !session.binariesDone {
continue
}
@@ -97,17 +87,6 @@ func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
}
}
func (s *Stage) InitObjectList() {
for seq := uint8(0x7f); seq > uint8(0); seq-- {
newObj := &ObjectMap{
id: seq,
charid: uint32(0),
status: false,
}
s.objectList[seq] = newObj
}
}
func (s *Stage) isCharInQuestByID(charID uint32) bool {
if _, exists := s.reservedClientSlots[charID]; exists {
return exists
@@ -120,8 +99,8 @@ func (s *Stage) isQuest() bool {
return len(s.reservedClientSlots) > 0
}
func (stage *Stage) GetName() string {
switch stage.id {
func (s *Stage) GetName() string {
switch s.id {
case MezeportaStageId:
return "Mezeporta"
case GuildHallLv1StageId:
@@ -149,20 +128,7 @@ func (stage *Stage) GetName() string {
}
}
func (s *Stage) GetNewObjectID(CharID uint32) uint32 {
ObjId := uint8(0)
for seq := uint8(0x7f); seq > uint8(0); seq-- {
if s.objectList[seq].status == false {
ObjId = seq
break
}
}
s.objectList[ObjId].status = true
s.objectList[ObjId].charid = CharID
bf := byteframe.NewByteFrame()
bf.WriteUint8(uint8(0))
bf.WriteUint8(ObjId)
bf.WriteUint16(uint16(0))
obj := uint32(bf.Data()[3]) | uint32(bf.Data()[2])<<8 | uint32(bf.Data()[1])<<16 | uint32(bf.Data()[0])<<32
return obj
func (s *Stage) NextObjectID() uint32 {
s.objectIndex = s.objectIndex + 1
return s.objectIndex
}