diff --git a/Erupe/network/mhfpacket/msg_sys_create_stage.go b/Erupe/network/mhfpacket/msg_sys_create_stage.go index 62c2822ff..fe6e533ff 100644 --- a/Erupe/network/mhfpacket/msg_sys_create_stage.go +++ b/Erupe/network/mhfpacket/msg_sys_create_stage.go @@ -1,6 +1,7 @@ package mhfpacket import ( + "errors" "erupe-ce/common/byteframe" "erupe-ce/common/bfutil" "erupe-ce/network" @@ -32,5 +33,5 @@ func (m *MsgSysCreateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Client // Build builds a binary packet from the current data. func (m *MsgSysCreateStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - panic("Not implemented") + return errors.New("NOT IMPLEMENTED") } diff --git a/Erupe/network/mhfpacket/msg_sys_reserve_stage.go b/Erupe/network/mhfpacket/msg_sys_reserve_stage.go index 324b131ac..13e47c41b 100644 --- a/Erupe/network/mhfpacket/msg_sys_reserve_stage.go +++ b/Erupe/network/mhfpacket/msg_sys_reserve_stage.go @@ -1,6 +1,7 @@ package mhfpacket import ( + "errors" "erupe-ce/common/byteframe" "erupe-ce/common/bfutil" "erupe-ce/network" @@ -10,7 +11,7 @@ import ( // MsgSysReserveStage represents the MSG_SYS_RESERVE_STAGE type MsgSysReserveStage struct { AckHandle uint32 - Unk0 uint8 // Made with: `16 * x | 1;`, unknown `x` values. + Ready uint8 // Bitfield but hex (0x11 or 0x01) StageID string // NULL terminated string. } @@ -22,7 +23,7 @@ func (m *MsgSysReserveStage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysReserveStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() + m.Ready = bf.ReadUint8() stageIDLength := bf.ReadUint8() m.StageID = string(bfutil.UpToNull(bf.ReadBytes(uint(stageIDLength)))) return nil @@ -30,5 +31,5 @@ func (m *MsgSysReserveStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.Clien // Build builds a binary packet from the current data. func (m *MsgSysReserveStage) Build(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - panic("Not implemented") + return errors.New("NOT IMPLEMENTED") } diff --git a/Erupe/network/mhfpacket/msg_sys_set_stage_pass.go b/Erupe/network/mhfpacket/msg_sys_set_stage_pass.go index c54e7412d..5cdbb2b88 100644 --- a/Erupe/network/mhfpacket/msg_sys_set_stage_pass.go +++ b/Erupe/network/mhfpacket/msg_sys_set_stage_pass.go @@ -1,7 +1,7 @@ package mhfpacket -import ( - "errors" +import ( + "errors" "erupe-ce/network/clientctx" "erupe-ce/network" @@ -11,7 +11,6 @@ import ( // MsgSysSetStagePass represents the MSG_SYS_SET_STAGE_PASS type MsgSysSetStagePass struct { Unk0 uint8 // Hardcoded 0 in the binary - PasswordLength uint8 Password string // NULL-terminated string } @@ -23,8 +22,8 @@ func (m *MsgSysSetStagePass) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysSetStagePass) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.Unk0 = bf.ReadUint8() - m.PasswordLength = bf.ReadUint8() - m.Password = string(bf.ReadBytes(uint(m.PasswordLength))) + _ = bf.ReadUint8() // Password length + m.Password = string(bf.ReadNullTerminatedBytes()) return nil } diff --git a/Erupe/server/channelserver/handlers_clients.go b/Erupe/server/channelserver/handlers_clients.go index 241717498..231b8e87a 100644 --- a/Erupe/server/channelserver/handlers_clients.go +++ b/Erupe/server/channelserver/handlers_clients.go @@ -10,41 +10,35 @@ import ( func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnumerateClient) - // Read-lock the stages map. s.server.stagesLock.RLock() - stage, ok := s.server.stages[pkt.StageID] if !ok { s.logger.Fatal("Can't enumerate clients for stage that doesn't exist!", zap.String("stageID", pkt.StageID)) } - - // Unlock the stages map. s.server.stagesLock.RUnlock() // Read-lock the stage and make the response with all of the charID's in the stage. resp := byteframe.NewByteFrame() stage.RLock() - - // TODO(Andoryuuta): Is only the reservations needed? Do clients send this packet for mezeporta as well? - - // Make a map to deduplicate the charIDs between the unreserved clients and the reservations. - deduped := make(map[uint32]interface{}) - - // Add the charIDs - for session := range stage.clients { - deduped[session.charID] = nil + var clients []uint32 + switch pkt.Unk1 { + case 1: // Not ready + for cid, ready := range stage.reservedClientSlots { + if !ready { + clients = append(clients, cid) + } + } + case 2: // Ready + for cid, ready := range stage.reservedClientSlots { + if ready { + clients = append(clients, cid) + } } - - for charid := range stage.reservedClientSlots { - deduped[charid] = nil } - - // Write the deduplicated response - resp.WriteUint16(uint16(len(deduped))) // Client count - for charid := range deduped { - resp.WriteUint32(charid) + resp.WriteUint16(uint16(len(clients))) + for _, cid := range clients { + resp.WriteUint32(cid) } - stage.RUnlock() doAckBufSucceed(s, pkt.AckHandle, resp.Data()) diff --git a/Erupe/server/channelserver/handlers_stage.go b/Erupe/server/channelserver/handlers_stage.go index 307346b09..d68312f68 100644 --- a/Erupe/server/channelserver/handlers_stage.go +++ b/Erupe/server/channelserver/handlers_stage.go @@ -247,119 +247,76 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysReserveStage) - - stageID := pkt.StageID - fmt.Printf("Got reserve stage req, TargetCount:%v, StageID:%v\n", pkt.Unk0, stageID) - - // Try to get the stage - s.server.stagesLock.Lock() - stage, gotStage := s.server.stages[stageID] - s.server.stagesLock.Unlock() - - if !gotStage { - s.logger.Error("Failed to get stage", zap.String("StageID", stageID)) - doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) - return - } - - // Try to reserve a slot, fail if full. - stage.Lock() - defer stage.Unlock() - - // Quick fix to allow readying up while party is full, more investigation needed - // Reserve stage is also sent when a player is ready, probably need to parse the - // request a little more thoroughly. - if _, exists := stage.reservedClientSlots[s.charID]; exists { - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) - } else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers { - // Add the charID to the stage's reservation map - stage.reservedClientSlots[s.charID] = nil - - // Save the reservation stage in the session for later use in MsgSysUnreserveStage. - s.Lock() - s.reservationStage = stage - s.Unlock() - - doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + if stage, exists := s.server.stages[pkt.StageID]; exists { + stage.Lock() + defer stage.Unlock() + if _, exists := stage.reservedClientSlots[s.charID]; exists { + switch pkt.Ready { + case 1: // 0x01 + stage.reservedClientSlots[s.charID] = false + case 17: // 0x11 + stage.reservedClientSlots[s.charID] = true + } + 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)) + 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)) + } + } else { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + } } else { - doAckSimpleFail(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) + s.logger.Error("Failed to get stage", zap.String("StageID", pkt.StageID)) + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) } } func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) { - // Clear the saved reservation stage s.Lock() stage := s.reservationStage - if stage != nil { - s.reservationStage = nil - } + s.reservationStage = nil s.Unlock() - - // Remove the charID from the stage's reservation map if stage != nil { stage.Lock() - _, exists := stage.reservedClientSlots[s.charID] - if exists { + if _, exists := stage.reservedClientSlots[s.charID]; exists { delete(stage.reservedClientSlots, s.charID) } stage.Unlock() } } -func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) {} - -func handleMsgSysWaitStageBinary(s *Session, p mhfpacket.MHFPacket) { - pkt := p.(*mhfpacket.MsgSysWaitStageBinary) - defer s.logger.Debug("MsgSysWaitStageBinary Done!") - - // Try to get the stage - stageID := pkt.StageID - s.server.stagesLock.Lock() - stage, gotStage := s.server.stages[stageID] - s.server.stagesLock.Unlock() - - // TODO(Andoryuuta): This is a hack for a binary part that none of the clients set, figure out what it represents. - // In the packet captures, it seemingly comes out of nowhere, so presumably the server makes it. - 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 - } - - // If we got the stage, lock and try to get the data. - var stageBinary []byte - var gotBinary bool - if gotStage { - 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)) - - // Couldn't get binary, sleep for some time and try again. - time.Sleep(2 * time.Second) - continue - } - - // TODO(Andoryuuta): Figure out what the game sends on timeout and implement it! - /* - if timeout { - 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{}) - return - } - */ +func handleMsgSysSetStagePass(s *Session, p mhfpacket.MHFPacket) { + pkt := p.(*mhfpacket.MsgSysSetStagePass) + s.Lock() + stage := s.reservationStage + s.Unlock() + if stage != nil { + stage.Lock() + if _, exists := stage.reservedClientSlots[s.charID]; exists { + stage.password = pkt.Password } + stage.Unlock() } else { - s.logger.Warn("Failed to get stage", zap.String("StageID", stageID)) + // Store for use on next ReserveStage + s.Lock() + s.stagePass = pkt.Password + s.Unlock() } } @@ -439,9 +396,16 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { for sid, stage := range s.server.stages { stage.RLock() defer stage.RUnlock() + if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 { continue } + + // Check for valid stage type + if sid[3:5] != "Qs" && sid[3:5] != "Ms" { + continue + } + joinable++ resp.WriteUint16(uint16(len(stage.reservedClientSlots))) // Reserved players. @@ -453,9 +417,14 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { } resp.WriteUint16(hasDeparted) // HasDeparted. resp.WriteUint16(stage.maxPlayers) // Max players. - resp.WriteBool(len(stage.password) > 0) // Password protected. - resp.WriteUint8(uint8(len(sid))) - resp.WriteBytes([]byte(sid)) + if len(stage.password) > 0 { + // This byte has also been seen as 1 + // The quest is also recognised as locked when this is 2 + resp.WriteUint8(3) + } else { + resp.WriteUint8(0) + } + ps.Uint8(resp, sid, false) } bf.WriteUint16(uint16(joinable)) bf.WriteBytes(resp.Data()) diff --git a/Erupe/server/channelserver/sys_session.go b/Erupe/server/channelserver/sys_session.go index 748d4f575..43bfca6a7 100644 --- a/Erupe/server/channelserver/sys_session.go +++ b/Erupe/server/channelserver/sys_session.go @@ -30,6 +30,7 @@ type Session struct { stageID string stage *Stage reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet. + stagePass string // Temporary storage charID uint32 logKey []byte sessionStart int64 diff --git a/Erupe/server/channelserver/sys_stage.go b/Erupe/server/channelserver/sys_stage.go index e656a3eaf..5ec438223 100644 --- a/Erupe/server/channelserver/sys_stage.go +++ b/Erupe/server/channelserver/sys_stage.go @@ -47,9 +47,9 @@ type Stage struct { // These are clients that are CURRENTLY in the stage clients map[*Session]uint32 - // Map of charID -> interface{}, only the key is used, value is always nil. + // Map of charID -> bool, key represents whether they are ready // These are clients that aren't in the stage, but have reserved a slot (for quests, etc). - reservedClientSlots map[uint32]interface{} + reservedClientSlots map[uint32]bool // These are raw binary blobs that the stage owner sets, // other clients expect the server to echo them back in the exact same format. @@ -66,7 +66,7 @@ func NewStage(ID string) *Stage { s := &Stage{ id: ID, clients: make(map[*Session]uint32), - reservedClientSlots: make(map[uint32]interface{}), + reservedClientSlots: make(map[uint32]bool), objects: make(map[uint32]*StageObject), rawBinaryData: make(map[stageBinaryKey][]byte), maxPlayers: 4,