diff --git a/Erupe/server/channelserver/handlers.go b/Erupe/server/channelserver/handlers.go index 6b22ad6c7..6f2779095 100644 --- a/Erupe/server/channelserver/handlers.go +++ b/Erupe/server/channelserver/handlers.go @@ -183,10 +183,6 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { panic(err) } - s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser { - CharID: s.charID, - }, s) - doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) } diff --git a/Erupe/server/channelserver/handlers_stage.go b/Erupe/server/channelserver/handlers_stage.go index d68312f68..07054cbe4 100644 --- a/Erupe/server/channelserver/handlers_stage.go +++ b/Erupe/server/channelserver/handlers_stage.go @@ -1,12 +1,11 @@ package channelserver import ( - "fmt" "time" - "strings" - "erupe-ce/network/mhfpacket" "erupe-ce/common/byteframe" + ps "erupe-ce/common/pascalstring" + "erupe-ce/network/mhfpacket" "go.uber.org/zap" ) @@ -15,7 +14,7 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { s.server.Lock() defer s.server.Unlock() if _, exists := s.server.stages[pkt.StageID]; exists { - doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } else { stage := NewStage(pkt.StageID) stage.maxPlayers = uint16(pkt.PlayerCount) @@ -27,36 +26,33 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {} func doStageTransfer(s *Session, ackHandle uint32, stageID string) { - // Remove this session from old stage clients list and put myself in the new one. - s.server.stagesLock.Lock() - newStage, gotNewStage := s.server.stages[stageID] - s.server.stagesLock.Unlock() + s.server.Lock() + stage, exists := s.server.stages[stageID] + s.server.Unlock() - if s.stage != nil { - removeSessionFromStage(s) + if exists { + stage.Lock() + stage.clients[s] = s.charID + stage.Unlock() + } else { // Create new stage object + s.server.Lock() + s.server.stages[stageID] = NewStage(stageID) + stage = s.server.stages[stageID] + s.server.Unlock() + stage.Lock() + stage.clients[s] = s.charID + stage.Unlock() } - // Add the new stage. - if gotNewStage { - newStage.Lock() - newStage.clients[s] = s.charID - newStage.Unlock() - } else { - // Fix stages - s.logger.Info("Fix Map Appliqued") - s.server.stagesLock.Lock() - s.server.stages[stageID] = NewStage(stageID) - newStage = s.server.stages[stageID] - s.server.stagesLock.Unlock() - newStage.Lock() - newStage.clients[s] = s.charID - newStage.Unlock() + // Ensure this session no longer belongs to reservations. + if s.stage != nil { + removeSessionFromStage(s) } // Save our new stage ID and pointer to the new stage itself. s.Lock() s.stageID = string(stageID) - s.stage = newStage + s.stage = s.server.stages[stageID] s.Unlock() // Tell the client to cleanup its current stage objects. @@ -65,138 +61,133 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { // Confirm the stage entry. doAckSimpleSucceed(s, ackHandle, []byte{0x00, 0x00, 0x00, 0x00}) - // Notify existing stage clients that this new client has entered. if s.stage != nil { // avoids lock up when using bed for dream quests - var pkt mhfpacket.MHFPacket - - // Notify the entree client about all of the existing clients in the stage. - s.logger.Info("Notifying entree about existing stage clients") - - clientNotif := byteframe.NewByteFrame() - s.server.Lock() - s.server.BroadcastMHF(&mhfpacket.MsgSysDeleteUser{ - CharID: s.charID, - }, s) - s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser { - CharID: s.charID, - }, s) - - for session := range s.server.sessions { - session := s.server.sessions[session] - - // Send existing players back to the client - pkt = &mhfpacket.MsgSysInsertUser{ - CharID: session.charID, - } - clientNotif.WriteUint16(uint16(pkt.Opcode())) - pkt.Build(clientNotif, session.clientContext) - for i := 1; i <= 3; i++ { - pkt = &mhfpacket.MsgSysNotifyUserBinary{ - CharID: session.charID, - BinaryType: uint8(i), - } - clientNotif.WriteUint16(uint16(pkt.Opcode())) - pkt.Build(clientNotif, session.clientContext) - } - } - s.server.Unlock() - clientNotif.WriteUint16(0x0010) // End it. - s.QueueSend(clientNotif.Data()) - // Notify the client to duplicate the existing objects. - s.logger.Info("Notifying entree about existing stage objects") + s.logger.Info("Sending existing stage objects") clientDupObjNotif := byteframe.NewByteFrame() s.stage.RLock() for _, obj := range s.stage.objects { - cur := &mhfpacket.MsgSysDuplicateObject{ - ObjID: obj.id, - X: obj.x, - Y: obj.y, - Z: obj.z, - Unk0: 0, - OwnerCharID: obj.ownerCharID, - } - clientDupObjNotif.WriteUint16(uint16(cur.Opcode())) - cur.Build(clientDupObjNotif, s.clientContext) + cur := &mhfpacket.MsgSysDuplicateObject{ + ObjID: obj.id, + X: obj.x, + Y: obj.y, + Z: obj.z, + Unk0: 0, + OwnerCharID: obj.ownerCharID, + } + clientDupObjNotif.WriteUint16(uint16(cur.Opcode())) + cur.Build(clientDupObjNotif, s.clientContext) } s.stage.RUnlock() clientDupObjNotif.WriteUint16(0x0010) // End it. - s.QueueSend(clientDupObjNotif.Data()) + if len(clientDupObjNotif.Data()) > 2 { + s.QueueSend(clientDupObjNotif.Data()) + } } } func removeEmptyStages(s *Session) { s.server.Lock() - for sid, stage := range s.server.stages { - if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 { - if strings.HasPrefix(sid, "sl1Qs") || strings.HasPrefix(sid, "sl2Qs") || strings.HasPrefix(sid, "sl3Qs") { - delete(s.server.stages, sid) + defer s.server.Unlock() + for _, stage := range s.server.stages { + // Destroy empty Quest/My series/Guild stages. + if stage.id[3:5] == "Qs" || stage.id[3:5] == "Ms" || stage.id[3:5] == "Gs" { + if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 { + delete(s.server.stages, stage.id) + s.logger.Debug("Destructed stage", zap.String("stage.id", stage.id)) } } } - s.server.Unlock() } func removeSessionFromStage(s *Session) { - s.stage.Lock() - defer s.stage.Unlock() - // Remove client from old stage. + s.stage.Lock() delete(s.stage.clients, s) delete(s.stage.reservedClientSlots, s.charID) - // Remove client from all reservations - s.server.Lock() - for _, stage := range s.server.stages { - if _, exists := stage.reservedClientSlots[s.charID]; exists { - delete(stage.reservedClientSlots, s.charID) - } - } - s.server.Unlock() - // Delete old stage objects owned by the client. - s.logger.Info("Sending MsgSysDeleteObject to old stage clients") + s.logger.Info("Sending notification to old stage clients") for objID, stageObject := range s.stage.objects { if stageObject.ownerCharID == s.charID { - // Broadcast the deletion to clients in the stage. - s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{ + clientNotif := byteframe.NewByteFrame() + var pkt mhfpacket.MHFPacket + pkt = &mhfpacket.MsgSysDeleteObject{ ObjID: stageObject.id, - }, s) + } + 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 form the objects map. + // 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 + // Added to prevent duplicates from flooding ObjectMap and causing server hangs + s.stage.objectList[objListID].status = false + s.stage.objectList[objListID].charid = 0 } } - + s.stage.Unlock() removeEmptyStages(s) } - func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnterStage) - fmt.Printf("The Stage is %s\n",pkt.StageID) // Push our current stage ID to the movement stack before entering another one. s.Lock() s.stageMoveStack.Push(s.stageID) s.Unlock() + s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{}) + if s.reservationStage != nil { + s.reservationStage = nil + } + + if pkt.StageID == "sl1Ns200p0a0u0" { // First entry + s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser{ + CharID: s.charID, + }, s) + + var temp mhfpacket.MHFPacket + loginNotif := byteframe.NewByteFrame() + s.server.Lock() + for _, session := range s.server.sessions { + if s == session { + 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) } func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysBackStage) - if s.stage != nil { - removeSessionFromStage(s) - } - // Transfer back to the saved stage ID before the previous move or enter. s.Lock() backStage, err := s.stageMoveStack.Pop() @@ -260,24 +251,17 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers { if len(stage.password) > 0 { - // s.logger.Debug("", zap.String("stgpw", stage.password), zap.String("usrpw", s.stagePass)) - if stage.password == s.stagePass { - stage.reservedClientSlots[s.charID] = false - s.Lock() - s.reservationStage = stage - s.Unlock() - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) + if stage.password != s.stagePass { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) return } - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) - } else { - stage.reservedClientSlots[s.charID] = false - // Save the reservation stage in the session for later use in MsgSysUnreserveStage. - s.Lock() - s.reservationStage = stage - s.Unlock() - doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } + stage.reservedClientSlots[s.charID] = false + // Save the reservation stage in the session for later use in MsgSysUnreserveStage. + s.Lock() + s.reservationStage = stage + s.Unlock() + doAckSimpleSucceed(s, pkt.AckHandle, make([]byte, 4)) } else { doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } @@ -308,12 +292,13 @@ func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) { s.Unlock() if stage != nil { stage.Lock() + // Will only exist if host. if _, exists := stage.reservedClientSlots[s.charID]; exists { stage.password = pkt.Password } stage.Unlock() } else { - // Store for use on next ReserveStage + // Store for use on next ReserveStage. s.Lock() s.stagePass = pkt.Password s.Unlock() @@ -322,66 +307,66 @@ func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysSetStageBinary) - - // Try to get the stage - stageID := pkt.StageID - s.server.stagesLock.Lock() - stage, gotStage := s.server.stages[stageID] - s.server.stagesLock.Unlock() - - // If we got the stage, lock and set the data. - if gotStage { + if stage, exists := s.server.stages[pkt.StageID]; exists { stage.Lock() stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] = pkt.RawDataPayload stage.Unlock() } else { - s.logger.Warn("Failed to get stage", zap.String("StageID", stageID)) + s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID)) } - s.logger.Debug("handleMsgSysSetStageBinary Done!") } func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysGetStageBinary) - - // Try to get the stage - stageID := pkt.StageID - s.server.stagesLock.Lock() - stage, gotStage := s.server.stages[stageID] - s.server.stagesLock.Unlock() - - // If we got the stage, lock and try to get the data. - var stageBinary []byte - var gotBinary bool - if gotStage { + if stage, exists := s.server.stages[pkt.StageID]; exists { stage.Lock() - stageBinary, gotBinary = stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] + if binaryData, exists := stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]; exists { + doAckBufSucceed(s, pkt.AckHandle, binaryData) + } else if pkt.BinaryType1 == 4 { + // Unknown binary type that is supposedly generated server side + // Temporary response + doAckBufSucceed(s, pkt.AckHandle, []byte{}) + } else { + s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1)) + s.logger.Warn("Sending blank stage binary") + doAckBufSucceed(s, pkt.AckHandle, []byte{}) + } stage.Unlock() } else { - s.logger.Warn("Failed to get stage", zap.String("StageID", stageID)) + s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID)) } - - if gotBinary { - doAckBufSucceed(s, pkt.AckHandle, stageBinary) - - } else if pkt.BinaryType1 == 4 { - // This particular type seems to be expecting data that isn't set - // is it required before the party joining can be completed - //s.QueueAck(pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x10}) - - // TODO(Andoryuuta): This doesn't fit a normal ack packet? where is this from? - // This would be a buffered(0x01), non-error(0x00), with no data payload (size 0x00, 0x00) packet. - // but for some reason has a 0x10 on the end that the client shouldn't parse? - - doAckBufSucceed(s, pkt.AckHandle, []byte{}) // Without the previous 0x10 suffix - } else { - s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1)) - s.logger.Warn("Sending blank stage binary") - doAckBufSucceed(s, pkt.AckHandle, []byte{}) - } - s.logger.Debug("MsgSysGetStageBinary Done!") } +func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysWaitStageBinary) + if stage, exists := s.server.stages[pkt.StageID]; exists { + if pkt.BinaryType0 == 1 && pkt.BinaryType1 == 12 { + // This might contain the hunter count, or max player count? + doAckBufSucceed(s, pkt.AckHandle, []byte{0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + return + } + for { + s.logger.Debug("MsgSysWaitStageBinary before lock and get stage") + stage.Lock() + stageBinary, gotBinary := stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] + stage.Unlock() + s.logger.Debug("MsgSysWaitStageBinary after lock and get stage") + if gotBinary { + doAckBufSucceed(s, pkt.AckHandle, stageBinary) + break + } else { + s.logger.Debug("Waiting stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1)) + time.Sleep(1 * time.Second) + continue + } + } + } else { + s.logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID)) + } + s.logger.Debug("MsgSysWaitStageBinary Done!") +} + func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnumerateStage) @@ -409,14 +394,10 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { joinable++ resp.WriteUint16(uint16(len(stage.reservedClientSlots))) // Reserved players. - resp.WriteUint16(0) // Unknown value - - var hasDeparted uint16 - if stage.hasDeparted { - hasDeparted = 1 - } - resp.WriteUint16(hasDeparted) // HasDeparted. - resp.WriteUint16(stage.maxPlayers) // Max players. + resp.WriteUint16(0) // Unk + resp.WriteUint8(0) // Unk + resp.WriteBool(len(stage.clients) > 0) // Has departed. + resp.WriteUint16(stage.maxPlayers) // Max players. if len(stage.password) > 0 { // This byte has also been seen as 1 // The quest is also recognised as locked when this is 2 diff --git a/Erupe/server/channelserver/sys_stage.go b/Erupe/server/channelserver/sys_stage.go index 5ec438223..8c0a19568 100644 --- a/Erupe/server/channelserver/sys_stage.go +++ b/Erupe/server/channelserver/sys_stage.go @@ -55,10 +55,9 @@ type Stage struct { // other clients expect the server to echo them back in the exact same format. rawBinaryData map[stageBinaryKey][]byte - maxPlayers uint16 - hasDeparted bool - password string - createdAt string + maxPlayers uint16 + password string + createdAt string } // NewStage creates a new stage with intialized values. @@ -71,7 +70,7 @@ func NewStage(ID string) *Stage { rawBinaryData: make(map[stageBinaryKey][]byte), maxPlayers: 4, gameObjectCount: 1, - objectList: make(map[uint8]*ObjectMap), + objectList: make(map[uint8]*ObjectMap), createdAt: time.Now().Format("01-02-2006 15:04:05"), } s.InitObjectList()