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/server/channelserver/handlers.go b/Erupe/server/channelserver/handlers.go index 6dd69381b..868f3cb3c 100644 --- a/Erupe/server/channelserver/handlers.go +++ b/Erupe/server/channelserver/handlers.go @@ -181,6 +181,8 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { } doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) + + updateRights(s) } func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) { 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_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_quest.go b/Erupe/server/channelserver/handlers_quest.go index 81cc03cdf..86a0003b9 100644 --- a/Erupe/server/channelserver/handlers_quest.go +++ b/Erupe/server/channelserver/handlers_quest.go @@ -34,7 +34,7 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) { // Get quest file. data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename))) if err != nil { - panic(err) + s.logger.Fatal(fmt.Sprintf("Failed to open quest file: quests/%s.bin", pkt.Filename)) } doAckBufSucceed(s, pkt.AckHandle, data) } @@ -45,7 +45,7 @@ func handleMsgMhfLoadFavoriteQuest(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfLoadFavoriteQuest) var data []byte err := s.server.db.QueryRow("SELECT savefavoritequest FROM characters WHERE id = $1", s.charID).Scan(&data) - if err == nil { + if err == nil && len(data) > 0 { doAckBufSucceed(s, pkt.AckHandle, data) } else { doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) @@ -68,8 +68,6 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) { } else { doAckBufSucceed(s, pkt.AckHandle, data) } - // Update the client's rights as well: - updateRights(s) } func handleMsgMhfEnterTournamentQuest(s *Session, p mhfpacket.MHFPacket) {} diff --git a/Erupe/server/channelserver/handlers_stage.go b/Erupe/server/channelserver/handlers_stage.go index 8f03dd4dd..221a99ce4 100644 --- a/Erupe/server/channelserver/handlers_stage.go +++ b/Erupe/server/channelserver/handlers_stage.go @@ -108,29 +108,10 @@ 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() @@ -140,44 +121,20 @@ func removeSessionFromStage(s *Session) { 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) } @@ -185,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) @@ -199,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) } @@ -219,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 6d6933062..8b530ee13 100644 --- a/Erupe/server/channelserver/sys_channel_server.go +++ b/Erupe/server/channelserver/sys_channel_server.go @@ -282,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 } @@ -300,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()) } } } @@ -389,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 { diff --git a/Erupe/server/channelserver/sys_session.go b/Erupe/server/channelserver/sys_session.go index d3c7aa676..b2a591c8d 100644 --- a/Erupe/server/channelserver/sys_session.go +++ b/Erupe/server/channelserver/sys_session.go @@ -251,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 }