diff --git a/Erupe/common/stringstack/stringstack.go b/Erupe/common/stringstack/stringstack.go index aff33842e..5f936e6a2 100644 --- a/Erupe/common/stringstack/stringstack.go +++ b/Erupe/common/stringstack/stringstack.go @@ -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") } diff --git a/Erupe/config.json b/Erupe/config.json index 1523d9d14..eb59db613 100644 --- a/Erupe/config.json +++ b/Erupe/config.json @@ -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 } + ] + } + ] + } +} diff --git a/Erupe/config/config.go b/Erupe/config/config.go index 193c76e27..5f0079fad 100644 --- a/Erupe/config/config.go +++ b/Erupe/config/config.go @@ -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 +} diff --git a/Erupe/gook.sql b/Erupe/gook.sql new file mode 100644 index 000000000..d9b2f1f05 --- /dev/null +++ b/Erupe/gook.sql @@ -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; \ No newline at end of file diff --git a/Erupe/network/mhfpacket/msg_mhf_enumerate_house.go b/Erupe/network/mhfpacket/msg_mhf_enumerate_house.go index 3758a8c38..9bd1f30ef 100644 --- a/Erupe/network/mhfpacket/msg_mhf_enumerate_house.go +++ b/Erupe/network/mhfpacket/msg_mhf_enumerate_house.go @@ -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 } diff --git a/Erupe/network/mhfpacket/msg_mhf_enumerate_title.go b/Erupe/network/mhfpacket/msg_mhf_enumerate_title.go index e7bc8398e..8506e7071 100644 --- a/Erupe/network/mhfpacket/msg_mhf_enumerate_title.go +++ b/Erupe/network/mhfpacket/msg_mhf_enumerate_title.go @@ -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. diff --git a/Erupe/network/mhfpacket/msg_mhf_load_house.go b/Erupe/network/mhfpacket/msg_mhf_load_house.go index db0a04307..a012b2f1c 100644 --- a/Erupe/network/mhfpacket/msg_mhf_load_house.go +++ b/Erupe/network/mhfpacket/msg_mhf_load_house.go @@ -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 } diff --git a/Erupe/network/mhfpacket/msg_mhf_update_guacot.go b/Erupe/network/mhfpacket/msg_mhf_update_guacot.go index 5c220aad2..99aa215e2 100644 --- a/Erupe/network/mhfpacket/msg_mhf_update_guacot.go +++ b/Erupe/network/mhfpacket/msg_mhf_update_guacot.go @@ -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() -} \ No newline at end of file diff --git a/Erupe/network/mhfpacket/msg_mhf_update_house.go b/Erupe/network/mhfpacket/msg_mhf_update_house.go index 47354a23f..320972673 100644 --- a/Erupe/network/mhfpacket/msg_mhf_update_house.go +++ b/Erupe/network/mhfpacket/msg_mhf_update_house.go @@ -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 } diff --git a/Erupe/network/mhfpacket/msg_sys_operate_register.go b/Erupe/network/mhfpacket/msg_sys_operate_register.go index 62e551d78..e4213d45d 100644 --- a/Erupe/network/mhfpacket/msg_sys_operate_register.go +++ b/Erupe/network/mhfpacket/msg_sys_operate_register.go @@ -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) diff --git a/Erupe/server/channelserver/handlers.go b/Erupe/server/channelserver/handlers.go index e4b268786..868f3cb3c 100644 --- a/Erupe/server/channelserver/handlers.go +++ b/Erupe/server/channelserver/handlers.go @@ -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}) diff --git a/Erupe/server/channelserver/handlers_data.go b/Erupe/server/channelserver/handlers_data.go index ea6724eec..9d1bf0c6a 100644 --- a/Erupe/server/channelserver/handlers_data.go +++ b/Erupe/server/channelserver/handlers_data.go @@ -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) diff --git a/Erupe/server/channelserver/handlers_discord.go b/Erupe/server/channelserver/handlers_discord.go index 94c92c58f..38619e079 100644 --- a/Erupe/server/channelserver/handlers_discord.go +++ b/Erupe/server/channelserver/handlers_discord.go @@ -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 { diff --git a/Erupe/server/channelserver/handlers_house.go b/Erupe/server/channelserver/handlers_house.go index 94ce5e3b4..9af91074e 100644 --- a/Erupe/server/channelserver/handlers_house.go +++ b/Erupe/server/channelserver/handlers_house.go @@ -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()) } diff --git a/Erupe/server/channelserver/handlers_object.go b/Erupe/server/channelserver/handlers_object.go index 548d891fe..0b24c0f66 100644 --- a/Erupe/server/channelserver/handlers_object.go +++ b/Erupe/server/channelserver/handlers_object.go @@ -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) {} diff --git a/Erupe/server/channelserver/handlers_register.go b/Erupe/server/channelserver/handlers_register.go index 7d8293cbe..4da6e9a8c 100644 --- a/Erupe/server/channelserver/handlers_register.go +++ b/Erupe/server/channelserver/handlers_register.go @@ -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) {} \ No newline at end of file +func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {} diff --git a/Erupe/server/channelserver/handlers_semaphore.go b/Erupe/server/channelserver/handlers_semaphore.go index 117e46d4f..630105ea3 100644 --- a/Erupe/server/channelserver/handlers_semaphore.go +++ b/Erupe/server/channelserver/handlers_semaphore.go @@ -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) { diff --git a/Erupe/server/channelserver/handlers_stage.go b/Erupe/server/channelserver/handlers_stage.go index d7891ba7b..221a99ce4 100644 --- a/Erupe/server/channelserver/handlers_stage.go +++ b/Erupe/server/channelserver/handlers_stage.go @@ -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() diff --git a/Erupe/server/channelserver/handlers_users.go b/Erupe/server/channelserver/handlers_users.go index 623beced5..482d83674 100644 --- a/Erupe/server/channelserver/handlers_users.go +++ b/Erupe/server/channelserver/handlers_users.go @@ -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 } diff --git a/Erupe/server/channelserver/sys_channel_server.go b/Erupe/server/channelserver/sys_channel_server.go index 89bfa2584..8b530ee13 100644 --- a/Erupe/server/channelserver/sys_channel_server.go +++ b/Erupe/server/channelserver/sys_channel_server.go @@ -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 +} diff --git a/Erupe/server/channelserver/sys_semaphore.go b/Erupe/server/channelserver/sys_semaphore.go index c923d4247..369e481b6 100644 --- a/Erupe/server/channelserver/sys_semaphore.go +++ b/Erupe/server/channelserver/sys_semaphore.go @@ -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()) } -} \ No newline at end of file +} diff --git a/Erupe/server/channelserver/sys_session.go b/Erupe/server/channelserver/sys_session.go index a023381ff..b2a591c8d 100644 --- a/Erupe/server/channelserver/sys_session.go +++ b/Erupe/server/channelserver/sys_session.go @@ -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)) + } } diff --git a/Erupe/server/channelserver/sys_stage.go b/Erupe/server/channelserver/sys_stage.go index 8c0a19568..3d0d68d28 100644 --- a/Erupe/server/channelserver/sys_stage.go +++ b/Erupe/server/channelserver/sys_stage.go @@ -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 }