diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 27528893c..2fa84a6fb 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -337,7 +337,6 @@ func logoutPlayer(s *Session) { s.server.Lock() delete(s.server.sessions, s.rawConn) s.rawConn.Close() - delete(s.server.objectIDs, s) s.server.Unlock() // Stage cleanup diff --git a/server/channelserver/handlers_object.go b/server/channelserver/handlers_object.go index 41f28e5d3..4e8284939 100644 --- a/server/channelserver/handlers_object.go +++ b/server/channelserver/handlers_object.go @@ -12,7 +12,7 @@ func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) { s.stage.Lock() newObj := &Object{ - id: s.NextObjectID(), + id: s.getObjectId(), ownerCharID: s.charID, x: pkt.X, y: pkt.Y, diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index 233b58271..fa62141fc 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -8,6 +8,7 @@ import ( "erupe-ce/common/byteframe" ps "erupe-ce/common/pascalstring" "erupe-ce/network/mhfpacket" + "go.uber.org/zap" ) @@ -65,19 +66,18 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { // Confirm the stage entry. doAckSimpleSucceed(s, ackHandle, []byte{0x00, 0x00, 0x00, 0x00}) - var temp mhfpacket.MHFPacket newNotif := byteframe.NewByteFrame() // Cast existing user data to new user - if !s.userEnteredStage { - s.userEnteredStage = true + if !s.loaded { + s.loaded = true // Lock server to safely iterate over sessions map // We need to copy the session list first to avoid holding the lock during packet building s.server.Lock() var sessionList []*Session for _, session := range s.server.sessions { - if s == session { + if s == session || !session.loaded { continue } sessionList = append(sessionList, session) @@ -85,6 +85,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { s.server.Unlock() // Build packets for each session without holding the lock + var temp mhfpacket.MHFPacket for _, session := range sessionList { temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID} newNotif.WriteUint16(uint16(temp.Opcode())) diff --git a/server/channelserver/session_lifecycle_integration_test.go b/server/channelserver/session_lifecycle_integration_test.go index 6f37eaa73..602e25b92 100644 --- a/server/channelserver/session_lifecycle_integration_test.go +++ b/server/channelserver/session_lifecycle_integration_test.go @@ -582,7 +582,6 @@ func createTestServerWithDB(t *testing.T, db *sqlx.DB) *Server { db: db, sessions: make(map[net.Conn]*Session), stages: make(map[string]*Stage), - objectIDs: make(map[*Session]uint16), userBinaryParts: make(map[userBinaryPartID][]byte), semaphore: make(map[string]*Semaphore), erupeConfig: _config.ErupeConfig, diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 268c47544..62ed7b08e 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -49,7 +49,6 @@ type Server struct { acceptConns chan net.Conn deleteConns chan net.Conn sessions map[net.Conn]*Session - objectIDs map[*Session]uint16 listener net.Listener // Listener that is created when Server.Start is called. isShuttingDown bool @@ -155,7 +154,6 @@ func NewServer(config *Config) *Server { acceptConns: make(chan net.Conn), deleteConns: make(chan net.Conn), sessions: make(map[net.Conn]*Session), - objectIDs: make(map[*Session]uint16), stages: make(map[string]*Stage), userBinaryParts: make(map[userBinaryPartID][]byte), semaphore: make(map[string]*Semaphore), @@ -280,6 +278,20 @@ func (s *Server) manageSessions() { } } +func (s *Server) getObjectId() uint16 { + ids := make(map[uint16]struct{}) + for _, sess := range s.sessions { + ids[sess.objectID] = struct{}{} + } + for i := uint16(1); i < 100; i++ { + if _, ok := ids[i]; !ok { + return i + } + } + s.logger.Warn("object ids overflowed", zap.Int("sessions", len(s.sessions))) + return 0 +} + func (s *Server) invalidateSessions() { for !s.isShuttingDown { diff --git a/server/channelserver/sys_channel_server_test.go b/server/channelserver/sys_channel_server_test.go index 9ef256e7f..6a33c629b 100644 --- a/server/channelserver/sys_channel_server_test.go +++ b/server/channelserver/sys_channel_server_test.go @@ -56,7 +56,6 @@ func createTestServer() *Server { ID: 1, logger: logger, sessions: make(map[net.Conn]*Session), - objectIDs: make(map[*Session]uint16), stages: make(map[string]*Stage), semaphore: make(map[string]*Semaphore), questCacheData: make(map[int][]byte), diff --git a/server/channelserver/sys_session.go b/server/channelserver/sys_session.go index 747f94674..03b30d153 100644 --- a/server/channelserver/sys_session.go +++ b/server/channelserver/sys_session.go @@ -37,8 +37,10 @@ type Session struct { clientContext *clientctx.ClientContext lastPacket time.Time - objectIndex uint16 - userEnteredStage bool // If the user has entered a stage before + objectID uint16 + objectIndex uint16 + loaded bool + stage *Stage reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet. stagePass string // Temporary storage @@ -84,12 +86,12 @@ func NewSession(server *Server, conn net.Conn) *Session { sendPackets: make(chan packet, 20), clientContext: &clientctx.ClientContext{}, // Unused lastPacket: time.Now(), + objectID: server.getObjectId(), sessionStart: TimeAdjusted().Unix(), stageMoveStack: stringstack.New(), ackStart: make(map[uint32]time.Time), semaphoreID: make([]uint16, 2), } - s.SetObjectID() return s } @@ -312,30 +314,9 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien } } -func (s *Session) SetObjectID() { - for i := uint16(1); i < 127; i++ { - exists := false - for _, j := range s.server.objectIDs { - if i == j { - exists = true - break - } - } - if !exists { - s.server.objectIDs[s] = i - return - } - } - s.server.objectIDs[s] = 0 -} - -func (s *Session) NextObjectID() uint32 { - bf := byteframe.NewByteFrame() - bf.WriteUint16(s.server.objectIDs[s]) +func (s *Session) getObjectId() uint32 { s.objectIndex++ - bf.WriteUint16(s.objectIndex) - bf.Seek(0, 0) - return bf.ReadUint32() + return uint32(s.objectID)<<16 | uint32(s.objectIndex) } func (s *Session) GetSemaphoreID() uint32 {