From 2d9a26df759ae0d36dc911b706b80fec2ea491a5 Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 6 Mar 2020 19:22:37 +0000 Subject: [PATCH 01/14] Add support for more chat types Add joining player -> part host notification messages --- network/mhfpacket/msg_sys_casted_binary.go | 2 +- server/channelserver/handlers.go | 72 ++++++++++++++++++++-- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/network/mhfpacket/msg_sys_casted_binary.go b/network/mhfpacket/msg_sys_casted_binary.go index d2c0145d4..16b07573f 100644 --- a/network/mhfpacket/msg_sys_casted_binary.go +++ b/network/mhfpacket/msg_sys_casted_binary.go @@ -27,7 +27,7 @@ func (m *MsgSysCastedBinary) Parse(bf *byteframe.ByteFrame) error { func (m *MsgSysCastedBinary) Build(bf *byteframe.ByteFrame) error { bf.WriteUint32(m.CharID) bf.WriteUint8(m.Type0) - bf.WriteUint8(m.Type0) + bf.WriteUint8(m.Type1) bf.WriteUint16(uint16(len(m.RawDataPayload))) bf.WriteBytes(m.RawDataPayload) return nil diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 19433c21d..d8214fba1 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -310,19 +310,44 @@ func handleMsgSysPing(s *Session, p mhfpacket.MHFPacket) { s.QueueAck(pkt.AckHandle, bf.Data()) } +const ( + BINARY_MESSAGE_TYPE_CHAT = 1 + BINARY_MESSAGE_TYPE_EMOTE = 6 +) + +const ( + CHAT_TYPE_LOCAL = 3 + // For some reason sending private messages appears to use the same code + // however the 9th byte in payload is 0x05 and there is no reference to + // the target player, something must be wrong here. + CHAT_TYPE_LIMITED = 1 + CHAT_TYPE_GLOBAL = 0xa +) + func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysCastBinary) - if pkt.Type0 == 3 && pkt.Type1 == 1 { + if pkt.Type1 == BINARY_MESSAGE_TYPE_CHAT { fmt.Println("Got chat message!") resp := &mhfpacket.MsgSysCastedBinary{ CharID: s.charID, - Type0: 1, - Type1: 1, + Type0: pkt.Type0, + Type1: pkt.Type1, RawDataPayload: pkt.RawDataPayload, } - s.server.BroadcastMHF(resp, s) + + switch chatType := pkt.Type0; chatType { + case CHAT_TYPE_GLOBAL: + s.server.BroadcastMHF(resp, s) + case CHAT_TYPE_LOCAL: + s.stage.BroadcastMHF(resp, s) + case CHAT_TYPE_LIMITED: + if pkt.RawDataPayload[9] == 0x04 { + // TODO Send to party members only + s.stage.BroadcastMHF(resp, s) + } + } /* // Made the inside of the casted binary @@ -662,7 +687,42 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { // TODO(Andoryuuta): Add proper player-slot reservations for stages. - s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}) + + s.QueueSend([]byte{ + 0x00, 0x1b, 0x30, 0x15, 0xc2, 0x45, 0x03, 0x03, 0x00, 0x0c, 0x00, 0x02, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + }) + + notify := &mhfpacket.MsgSysNotifyUserBinary{ + CharID: s.charID, + BinaryType: 0x03, + } + + s.stage.BroadcastMHF(notify, s) + + joinMsgA := &mhfpacket.MsgSysCastedBinary{ + CharID: s.charID, + Type0: 0x03, + Type1: 0x03, + RawDataPayload: []byte{ + 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + } + + s.stage.BroadcastMHF(joinMsgA, s) + + joinMsgB := &mhfpacket.MsgSysCastedBinary{ + CharID: s.charID, + Type0: 0x03, + Type1: 0x03, + RawDataPayload: []byte{ + 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }, + } + + s.stage.BroadcastMHF(joinMsgB, s) } func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {} @@ -754,6 +814,8 @@ func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) { if gotBinary { doSizedAckResp(s, pkt.AckHandle, stageBinary) + } else if pkt.BinaryType1 == 4 { + s.QueueAck(pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x10}) } 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") From ac40e679fe2364481fdcf871633109c0091a5b69 Mon Sep 17 00:00:00 2001 From: Sophie Date: Fri, 6 Mar 2020 21:23:57 +0000 Subject: [PATCH 02/14] Add mhfpacket wrapper for party join message --- .../binpacket/msg_bin_player_joined_party.go | 46 +++++++++++++++++++ server/channelserver/handlers.go | 31 ++++++------- 2 files changed, 59 insertions(+), 18 deletions(-) create mode 100644 network/binpacket/msg_bin_player_joined_party.go diff --git a/network/binpacket/msg_bin_player_joined_party.go b/network/binpacket/msg_bin_player_joined_party.go new file mode 100644 index 000000000..5170ef276 --- /dev/null +++ b/network/binpacket/msg_bin_player_joined_party.go @@ -0,0 +1,46 @@ +package binpacket + +import ( + "github.com/Andoryuuta/Erupe/network" + "github.com/Andoryuuta/Erupe/network/mhfpacket" + "github.com/Andoryuuta/byteframe" +) + +type PartyJoinType uint8 + +const ( + JoinedLocalParty PartyJoinType = 0x01 + JoinedYourParty PartyJoinType = 0x04 +) + +type MsgBinPlayerJoinedParty struct { + CharID uint32 + PartyJoinType PartyJoinType +} + +// Opcode returns the ID associated with this packet type. +func (m *MsgBinPlayerJoinedParty) Opcode() network.PacketID { + return network.MSG_SYS_CASTED_BINARY +} + +func (m *MsgBinPlayerJoinedParty) Parse(bf *byteframe.ByteFrame) error { + panic("Not implemented") +} + +// Build builds a binary packet from the current data. +func (m *MsgBinPlayerJoinedParty) Build(bf *byteframe.ByteFrame) error { + payload := byteframe.NewByteFrame() + + payload.WriteUint16(0x2) + payload.WriteUint8(uint8(m.PartyJoinType)) + payload.WriteBytes([]byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + + pkt := &mhfpacket.MsgSysCastedBinary{ + CharID: m.CharID, + Type0: 0x03, + Type1: 0x03, + RawDataPayload: payload.Data(), + } + + return pkt.Build(bf) +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index d8214fba1..c710c2ee6 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -6,6 +6,7 @@ import ( "encoding/base64" "encoding/hex" "fmt" + "github.com/Andoryuuta/Erupe/network/binpacket" "io" "io/ioutil" "os" @@ -700,29 +701,20 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { s.stage.BroadcastMHF(notify, s) - joinMsgA := &mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, - Type0: 0x03, - Type1: 0x03, - RawDataPayload: []byte{ - 0x00, 0x02, 0x01, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, + //TODO these messages should be directed to the correct recipients + joinedAPartyMessage := &binpacket.MsgBinPlayerJoinedParty{ + CharID: s.charID, + PartyJoinType: binpacket.JoinedLocalParty, } - s.stage.BroadcastMHF(joinMsgA, s) + s.stage.BroadcastMHF(joinedAPartyMessage, s) - joinMsgB := &mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, - Type0: 0x03, - Type1: 0x03, - RawDataPayload: []byte{ - 0x00, 0x02, 0x04, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }, + joinedYourPartyMessage := &binpacket.MsgBinPlayerJoinedParty{ + CharID: s.charID, + PartyJoinType: binpacket.JoinedYourParty, } - s.stage.BroadcastMHF(joinMsgB, s) + s.stage.BroadcastMHF(joinedYourPartyMessage, s) } func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {} @@ -814,7 +806,10 @@ func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) { if gotBinary { doSizedAckResp(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}) } else { s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1)) From 795028a1caabd279d22a6c53dff1e7ee708e02a2 Mon Sep 17 00:00:00 2001 From: Sophie Date: Sat, 7 Mar 2020 14:21:08 +0000 Subject: [PATCH 03/14] Clients in stage are now notified when player chooses a quest --- .../binpacket/msg_bin_player_joined_party.go | 6 +++-- server/channelserver/handlers.go | 26 +++++++++++++++---- server/channelserver/session.go | 4 +++ 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/network/binpacket/msg_bin_player_joined_party.go b/network/binpacket/msg_bin_player_joined_party.go index 5170ef276..d3f3ff575 100644 --- a/network/binpacket/msg_bin_player_joined_party.go +++ b/network/binpacket/msg_bin_player_joined_party.go @@ -16,6 +16,7 @@ const ( type MsgBinPlayerJoinedParty struct { CharID uint32 PartyJoinType PartyJoinType + Unk1 uint16 } // Opcode returns the ID associated with this packet type. @@ -31,9 +32,10 @@ func (m *MsgBinPlayerJoinedParty) Parse(bf *byteframe.ByteFrame) error { func (m *MsgBinPlayerJoinedParty) Build(bf *byteframe.ByteFrame) error { payload := byteframe.NewByteFrame() - payload.WriteUint16(0x2) + payload.WriteUint16(0x02) payload.WriteUint8(uint8(m.PartyJoinType)) - payload.WriteBytes([]byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) + payload.WriteUint16(m.Unk1) + payload.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) pkt := &mhfpacket.MsgSysCastedBinary{ CharID: m.CharID, diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index c710c2ee6..ba31343a6 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -498,6 +498,15 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { resp := make([]byte, 8) // Unk resp. s.QueueAck(pkt.AckHandle, resp) + + createdPartyMessage := &mhfpacket.MsgSysCastedBinary{ + CharID: s.charID, + Type0: 0x03, + Type1: 0x03, + RawDataPayload: []byte{0x00, 0x02, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5b, 0x27, 0xb3, 0x2e, 0x48, 0xa3, 0x17, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, + } + + s.stage.BroadcastMHF(createdPartyMessage, s) } func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {} @@ -694,17 +703,16 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { 0x00, 0x1b, 0x30, 0x15, 0xc2, 0x45, 0x03, 0x03, 0x00, 0x0c, 0x00, 0x02, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, }) - notify := &mhfpacket.MsgSysNotifyUserBinary{ - CharID: s.charID, - BinaryType: 0x03, + // TODO remove this, temp for testing + if s.charID == 0x02 { + return } - s.stage.BroadcastMHF(notify, s) - //TODO these messages should be directed to the correct recipients joinedAPartyMessage := &binpacket.MsgBinPlayerJoinedParty{ CharID: s.charID, PartyJoinType: binpacket.JoinedLocalParty, + Unk1: 0x01, } s.stage.BroadcastMHF(joinedAPartyMessage, s) @@ -712,6 +720,7 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { joinedYourPartyMessage := &binpacket.MsgBinPlayerJoinedParty{ CharID: s.charID, PartyJoinType: binpacket.JoinedYourParty, + Unk1: 0x01, } s.stage.BroadcastMHF(joinedYourPartyMessage, s) @@ -1007,6 +1016,13 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { s.server.userBinaryPartsLock.Lock() s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload s.server.userBinaryPartsLock.Unlock() + + msg := &mhfpacket.MsgSysNotifyUserBinary{ + CharID: s.charID, + BinaryType: pkt.BinaryType, + } + + s.stage.BroadcastMHF(msg, s) } func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 002d04c93..1a24e761d 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -58,6 +58,9 @@ func (s *Session) Start() { // QueueSend queues a packet (raw []byte) to be sent. func (s *Session) QueueSend(data []byte) { + fmt.Printf("Sending To CharID: '%x'\n", s.charID) + fmt.Printf("Sent Data:\n%s\n", hex.Dump(data)) + s.sendPackets <- data } @@ -144,6 +147,7 @@ func (s *Session) handlePacketGroup(pktGroup []byte) { opcode != network.MSG_SYS_NOP && opcode != network.MSG_SYS_TIME && opcode != network.MSG_SYS_EXTEND_THRESHOLD { + fmt.Printf("CharID: '%x'\n", s.charID) fmt.Printf("Opcode: %s\n", opcode) fmt.Printf("Data:\n%s\n", hex.Dump(pktGroup)) } From b526e6682198aaaa1d3ea40082f2579d1be5a552 Mon Sep 17 00:00:00 2001 From: Sophie Date: Sat, 7 Mar 2020 17:30:07 +0000 Subject: [PATCH 04/14] Add chat type checks based on existing comments Add basic support for clients reserving stages Remove unnecessary packets Add max player count to stages --- server/channelserver/handlers.go | 101 +++++++++++++++++-------------- server/channelserver/session.go | 9 +-- server/channelserver/stage.go | 2 + 3 files changed, 63 insertions(+), 49 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index ba31343a6..4b73b3892 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -317,12 +317,12 @@ const ( ) const ( - CHAT_TYPE_LOCAL = 3 - // For some reason sending private messages appears to use the same code - // however the 9th byte in payload is 0x05 and there is no reference to - // the target player, something must be wrong here. - CHAT_TYPE_LIMITED = 1 - CHAT_TYPE_GLOBAL = 0xa + CHAT_TYPE_WORLD uint8 = iota + CHAT_TYPE_LOCAL + CHAT_TYPE_GUILD + CHAT_TYPE_ALLIANCE + CHAT_TYPE_PARTY + CHAT_TYPE_WHISPER ) func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { @@ -338,16 +338,16 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { RawDataPayload: pkt.RawDataPayload, } - switch chatType := pkt.Type0; chatType { - case CHAT_TYPE_GLOBAL: + switch chatType := pkt.RawDataPayload[2]; chatType { + case CHAT_TYPE_WORLD: s.server.BroadcastMHF(resp, s) case CHAT_TYPE_LOCAL: s.stage.BroadcastMHF(resp, s) - case CHAT_TYPE_LIMITED: - if pkt.RawDataPayload[9] == 0x04 { - // TODO Send to party members only - s.stage.BroadcastMHF(resp, s) - } + case CHAT_TYPE_PARTY: + // Party messages seem to work partially when a party member starts the quest + // In town it is not working yet + // TODO Send to party members only + s.stage.BroadcastMHF(resp, s) } /* @@ -493,20 +493,12 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) { s.server.stagesLock.Lock() stage := NewStage(stripNullTerminator(pkt.StageID)) + stage.maxClients = pkt.PlayerCount s.server.stages[stage.id] = stage s.server.stagesLock.Unlock() resp := make([]byte, 8) // Unk resp. s.QueueAck(pkt.AckHandle, resp) - - createdPartyMessage := &mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, - Type0: 0x03, - Type1: 0x03, - RawDataPayload: []byte{0x00, 0x02, 0x0b, 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x5b, 0x27, 0xb3, 0x2e, 0x48, 0xa3, 0x17, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, - } - - s.stage.BroadcastMHF(createdPartyMessage, s) } func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {} @@ -693,37 +685,56 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysReserveStage) - fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%q\n", pkt.Unk0, pkt.StageID) + stageID := stripNullTerminator(pkt.StageID) + s.server.stagesLock.RLock() + stage := s.server.stages[stageID] + s.server.stagesLock.RUnlock() + + if uint8(len(stage.clients)) >= s.stage.maxClients { + // Do something? This will probably only be possible when multiple + // players attempt joining a quest at the same time I think. + } + + s.server.stagesLock.Lock() + stage.clients[s] = s.charID + s.server.stagesLock.Unlock() + + s.reserveStage = stage + + fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%q\n", pkt.Unk0, stageID) // TODO(Andoryuuta): Add proper player-slot reservations for stages. - s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}) + s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - s.QueueSend([]byte{ - 0x00, 0x1b, 0x30, 0x15, 0xc2, 0x45, 0x03, 0x03, 0x00, 0x0c, 0x00, 0x02, 0x06, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, - }) + stage.RLock() + for _, charID := range stage.clients { + if charID == s.charID { + continue + } - // TODO remove this, temp for testing - if s.charID == 0x02 { - return + // Notify joining player of all existing party members + s.QueueSendMHF(&binpacket.MsgBinPlayerJoinedParty{ + CharID: charID, + PartyJoinType: binpacket.JoinedYourParty, + Unk1: 0x01, + }) } + stage.RUnlock() - //TODO these messages should be directed to the correct recipients - joinedAPartyMessage := &binpacket.MsgBinPlayerJoinedParty{ + // Notify players in room that player has joined a party + s.stage.BroadcastMHF(&binpacket.MsgBinPlayerJoinedParty{ CharID: s.charID, PartyJoinType: binpacket.JoinedLocalParty, Unk1: 0x01, - } + }, s) - s.stage.BroadcastMHF(joinedAPartyMessage, s) - - joinedYourPartyMessage := &binpacket.MsgBinPlayerJoinedParty{ + // Notify players in party that player has joined + stage.BroadcastMHF(&binpacket.MsgBinPlayerJoinedParty{ CharID: s.charID, PartyJoinType: binpacket.JoinedYourParty, Unk1: 0x01, - } - - s.stage.BroadcastMHF(joinedYourPartyMessage, s) + }, s) } func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {} @@ -875,14 +886,14 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { // Build the response resp := byteframe.NewByteFrame() resp.WriteUint16(uint16(len(s.server.stages))) - for sid := range s.server.stages { + for sid, stage := range s.server.stages { // Found parsing code, field sizes are correct, but unknown purposes still. //resp.WriteBytes([]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00}) - resp.WriteUint16(1) // Current players. - resp.WriteUint16(0) // Unknown value - resp.WriteUint16(0) // HasDeparted. - resp.WriteUint16(4) // Max players. - resp.WriteUint8(2) // Password protected. + resp.WriteUint16(1) // Current players. + resp.WriteUint16(0) // Unknown value + resp.WriteUint16(0) // HasDeparted. + resp.WriteUint16(uint16(stage.maxClients)) // Max players. + resp.WriteUint8(2) // Password protected. resp.WriteUint8(uint8(len(sid))) resp.WriteBytes([]byte(sid)) } diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 1a24e761d..1e7e1c47f 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -22,10 +22,11 @@ type Session struct { cryptConn *network.CryptConn sendPackets chan []byte - stageID string - stage *Stage - charID uint32 - logKey []byte + stageID string + stage *Stage + reserveStage *Stage + charID uint32 + logKey []byte // A stack containing the stage movement history (push on enter/move, pop on back) stageMoveStack *stringstack.StringStack diff --git a/server/channelserver/stage.go b/server/channelserver/stage.go index 1b996b1a1..0e84f154e 100644 --- a/server/channelserver/stage.go +++ b/server/channelserver/stage.go @@ -27,6 +27,7 @@ type Stage struct { gameObjectCount uint32 // Total count of objects ever created for this stage. Used for ObjID generation. objects map[uint32]*StageObject // Map of ObjID -> StageObject clients map[*Session]uint32 // Map of session -> charID + maxClients uint8 // Max number of clients allowed to join this stage rawBinaryData map[stageBinaryKey][]byte // Raw binary data set by the client. } @@ -37,6 +38,7 @@ func NewStage(ID string) *Stage { objects: make(map[uint32]*StageObject), clients: make(map[*Session]uint32), rawBinaryData: make(map[stageBinaryKey][]byte), + maxClients: 4, gameObjectCount: 1, } From 1041cac476eec89dc0eed0f49e80539cc7e43455 Mon Sep 17 00:00:00 2001 From: Sophie Date: Sat, 7 Mar 2020 17:37:04 +0000 Subject: [PATCH 05/14] Add config option to log outbound messages --- config/config.go | 17 +++++++++-------- server/channelserver/session.go | 6 ++++-- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/config/config.go b/config/config.go index d44b3cb89..a71b07706 100644 --- a/config/config.go +++ b/config/config.go @@ -14,18 +14,19 @@ type Config struct { DevMode bool DevModeOptions DevModeOptions - Database Database - Launcher Launcher - Sign Sign - Channel Channel - Entrance Entrance + Database Database + Launcher Launcher + Sign Sign + Channel Channel + Entrance Entrance } // DevModeOptions holds various debug/temporary options for use while developing Erupe. type DevModeOptions struct { - 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 + 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 + LogOutboundMessages bool // Log all messages sent to the clients } // Database holds the postgres database config. diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 1e7e1c47f..c7183ef8a 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -59,8 +59,10 @@ func (s *Session) Start() { // QueueSend queues a packet (raw []byte) to be sent. func (s *Session) QueueSend(data []byte) { - fmt.Printf("Sending To CharID: '%x'\n", s.charID) - fmt.Printf("Sent Data:\n%s\n", hex.Dump(data)) + if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogOutboundMessages { + fmt.Printf("Sending To CharID: '%x'\n", s.charID) + fmt.Printf("Sent Data:\n%s\n", hex.Dump(data)) + } s.sendPackets <- data } From 927f6e941941d1b9708790217fc02efbf33155cd Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 9 Mar 2020 07:06:05 +0000 Subject: [PATCH 06/14] Limit recipients of party chat messages Add vendor to gitignore --- .gitignore | 1 + server/channelserver/handlers.go | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a9d66693f..e1afd0826 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ www/tw/ www/jp/ +vendor/ bin/*.bin bin/quests/*.bin bin/scenarios/*.bin diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 4b73b3892..054db6f38 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -344,10 +344,12 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { case CHAT_TYPE_LOCAL: s.stage.BroadcastMHF(resp, s) case CHAT_TYPE_PARTY: - // Party messages seem to work partially when a party member starts the quest - // In town it is not working yet - // TODO Send to party members only - s.stage.BroadcastMHF(resp, s) + if s.reserveStage != nil { + // Party messages seem to work partially when a party member starts the quest + // In town it is not working yet, the client now sends the chat packets + // however the other member does not accept it. + s.reserveStage.BroadcastMHF(resp, s) + } } /* From 9710f1cc43e2406a70acc4cedc79626722a57490 Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 9 Mar 2020 07:18:04 +0000 Subject: [PATCH 07/14] Rollback inadvertent file commit --- config.json | 10 +++++----- go.mod | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config.json b/config.json index 74588b446..362926dc8 100644 --- a/config.json +++ b/config.json @@ -6,14 +6,14 @@ "devmodeoptions": { "cleandb": false, "maxlauncherhr": true, - "logoutboundmessages" : true + "fixedstageid": true }, "database": { "host": "localhost", - "port": 5555, + "port": 5432, "user": "postgres", - "password": "qweasd", + "password": "admin", "database": "erupe" }, "launcher": { @@ -30,7 +30,7 @@ "port": 53310, "entries": [ { - "name": "Dev New", + "name": "AErupe server noob", "ip": "127.0.0.1", "unk2": 0, "type": 3, @@ -56,7 +56,7 @@ ] }, { - "name": "Dev Open", + "name": "AErupe server open", "ip": "127.0.0.1", "unk2": 0, "type": 1, diff --git a/go.mod b/go.mod index 1bceefbde..be82752b5 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( cloud.google.com/go/spanner v1.2.1 // indirect cloud.google.com/go/storage v1.6.0 // indirect github.com/Andoryuuta/byteframe v0.0.0-20200114030334-8979c5cc4c4a - github.com/BurntSushi/toml v0.3.1 // indirect + github.com/BurntSushi/toml v0.3.1 github.com/ClickHouse/clickhouse-go v1.3.14 // indirect github.com/aws/aws-sdk-go v1.29.10 // indirect github.com/cockroachdb/cockroach-go v0.0.0-20190925194419-606b3d062051 // indirect From 83becc4c84e57145fbd7d68988e6b701cc26f385 Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 9 Mar 2020 07:37:16 +0000 Subject: [PATCH 08/14] Fix perpetual stage lock Cleanup --- server/channelserver/handlers.go | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 20b4874b9..3fff8e738 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -208,16 +208,16 @@ const ( func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysCastBinary) + resp := &mhfpacket.MsgSysCastedBinary{ + CharID: s.charID, + Type0: pkt.Type0, + Type1: pkt.Type1, + RawDataPayload: pkt.RawDataPayload, + } + if pkt.Type1 == BINARY_MESSAGE_TYPE_CHAT { fmt.Println("Got chat message!") - resp := &mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, - Type0: pkt.Type0, - Type1: pkt.Type1, - RawDataPayload: pkt.RawDataPayload, - } - switch chatType := pkt.RawDataPayload[2]; chatType { case CHAT_TYPE_WORLD: s.server.BroadcastMHF(resp, s) @@ -268,12 +268,6 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { // Simply forward the packet to all the other clients. // (The client never uses Type0 upon receiving) // TODO(Andoryuuta): Does this broadcast need to be limited? (world, stage, guild, etc). - resp := &mhfpacket.MsgSysCastedBinary{ - CharID: s.charID, - Type0: pkt.Type0, - Type1: pkt.Type1, - RawDataPayload: pkt.RawDataPayload, - } s.server.BroadcastMHF(resp, s) } } @@ -608,7 +602,6 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { s.QueueAck(pkt.AckHandle, []byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) } - stage.RLock() for _, charID := range stage.clients { if charID == s.charID { continue @@ -621,7 +614,6 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { Unk1: 0x01, }) } - stage.RUnlock() // Notify players in room that player has joined a party s.stage.BroadcastMHF(&binpacket.MsgBinPlayerJoinedParty{ From ba3df8d4d1d667a1aab27b87bc1b14826f616551 Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 9 Mar 2020 18:05:01 +0000 Subject: [PATCH 09/14] Remove unnecessary messages, these were covered by recasting user bins --- .../binpacket/msg_bin_player_joined_party.go | 48 ------------------- server/channelserver/handlers.go | 27 ----------- 2 files changed, 75 deletions(-) delete mode 100644 network/binpacket/msg_bin_player_joined_party.go diff --git a/network/binpacket/msg_bin_player_joined_party.go b/network/binpacket/msg_bin_player_joined_party.go deleted file mode 100644 index d3f3ff575..000000000 --- a/network/binpacket/msg_bin_player_joined_party.go +++ /dev/null @@ -1,48 +0,0 @@ -package binpacket - -import ( - "github.com/Andoryuuta/Erupe/network" - "github.com/Andoryuuta/Erupe/network/mhfpacket" - "github.com/Andoryuuta/byteframe" -) - -type PartyJoinType uint8 - -const ( - JoinedLocalParty PartyJoinType = 0x01 - JoinedYourParty PartyJoinType = 0x04 -) - -type MsgBinPlayerJoinedParty struct { - CharID uint32 - PartyJoinType PartyJoinType - Unk1 uint16 -} - -// Opcode returns the ID associated with this packet type. -func (m *MsgBinPlayerJoinedParty) Opcode() network.PacketID { - return network.MSG_SYS_CASTED_BINARY -} - -func (m *MsgBinPlayerJoinedParty) Parse(bf *byteframe.ByteFrame) error { - panic("Not implemented") -} - -// Build builds a binary packet from the current data. -func (m *MsgBinPlayerJoinedParty) Build(bf *byteframe.ByteFrame) error { - payload := byteframe.NewByteFrame() - - payload.WriteUint16(0x02) - payload.WriteUint8(uint8(m.PartyJoinType)) - payload.WriteUint16(m.Unk1) - payload.WriteBytes([]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) - - pkt := &mhfpacket.MsgSysCastedBinary{ - CharID: m.CharID, - Type0: 0x03, - Type1: 0x03, - RawDataPayload: payload.Data(), - } - - return pkt.Build(bf) -} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 3fff8e738..e71f52cae 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -6,7 +6,6 @@ import ( "encoding/base64" "encoding/hex" "fmt" - "github.com/Andoryuuta/Erupe/network/binpacket" "io/ioutil" "os" "path/filepath" @@ -602,32 +601,6 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { s.QueueAck(pkt.AckHandle, []byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) } - for _, charID := range stage.clients { - if charID == s.charID { - continue - } - - // Notify joining player of all existing party members - s.QueueSendMHF(&binpacket.MsgBinPlayerJoinedParty{ - CharID: charID, - PartyJoinType: binpacket.JoinedYourParty, - Unk1: 0x01, - }) - } - - // Notify players in room that player has joined a party - s.stage.BroadcastMHF(&binpacket.MsgBinPlayerJoinedParty{ - CharID: s.charID, - PartyJoinType: binpacket.JoinedLocalParty, - Unk1: 0x01, - }, s) - - // Notify players in party that player has joined - stage.BroadcastMHF(&binpacket.MsgBinPlayerJoinedParty{ - CharID: s.charID, - PartyJoinType: binpacket.JoinedYourParty, - Unk1: 0x01, - }, s) } func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) { From 78fe5c85efc334597d841a8147e1cfc82c3de709 Mon Sep 17 00:00:00 2001 From: Sophie Date: Mon, 9 Mar 2020 19:15:16 +0000 Subject: [PATCH 10/14] Add support for players logging out and closing client Removed seemingly unnecessary castedBinary messages on character login --- network/mhfpacket/msg_sys_delete_user.go | 8 +- server/channelserver/handlers.go | 105 +++++++++++++++-------- server/channelserver/session.go | 8 ++ 3 files changed, 83 insertions(+), 38 deletions(-) diff --git a/network/mhfpacket/msg_sys_delete_user.go b/network/mhfpacket/msg_sys_delete_user.go index 0a3584763..a84589897 100644 --- a/network/mhfpacket/msg_sys_delete_user.go +++ b/network/mhfpacket/msg_sys_delete_user.go @@ -6,7 +6,9 @@ import ( ) // MsgSysDeleteUser represents the MSG_SYS_DELETE_USER -type MsgSysDeleteUser struct{} +type MsgSysDeleteUser struct { + CharID uint32 +} // Opcode returns the ID associated with this packet type. func (m *MsgSysDeleteUser) Opcode() network.PacketID { @@ -20,5 +22,7 @@ func (m *MsgSysDeleteUser) Parse(bf *byteframe.ByteFrame) error { // Build builds a binary packet from the current data. func (m *MsgSysDeleteUser) Build(bf *byteframe.ByteFrame) error { - panic("Not implemented") + bf.WriteUint32(m.CharID) + + return nil } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index e71f52cae..ecb6a2715 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -177,7 +177,9 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) { s.QueueAck(pkt.AckHandle, bf.Data()) } -func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {} +func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) { + logoutPlayer(s) +} func handleMsgSysSetStatus(s *Session, p mhfpacket.MHFPacket) {} @@ -396,27 +398,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { s.server.stagesLock.Unlock() if s.stage != nil { - s.stage.Lock() - - // Remove client from old stage. - delete(s.stage.clients, s) - - // Delete old stage objects owned by the client. - s.logger.Info("Sending MsgSysDeleteObject 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{ - ObjID: stageObject.id, - }, s) - // TODO(Andoryuuta): Should this be sent to the owner's client as well? it currently isn't. - - // Actually delete it form the objects map. - delete(s.stage.objects, objID) - } - } - - s.stage.Unlock() + removeSessionFromStage(s) } // Add the new stage. @@ -439,25 +421,28 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { s.QueueAck(ackHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) // Notify existing stage clients that this new client has entered. - s.logger.Info("Sending MsgSysInsertUser & MsgSysNotifyUserBinary") + s.logger.Info("Sending MsgSysInsertUser") s.stage.BroadcastMHF(&mhfpacket.MsgSysInsertUser{ CharID: s.charID, }, s) - s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ - CharID: s.charID, - BinaryType: 1, - }, s) - s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ - CharID: s.charID, - BinaryType: 2, - }, s) - s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ - CharID: s.charID, - BinaryType: 3, - }, s) + // It seems to be acceptable to recast all MSG_SYS_SET_USER_BINARY messages so far, + // players are still notified when a new player has joined the stage. + // These extra messages may not be needed + //s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ + // CharID: s.charID, + // BinaryType: 1, + //}, s) + //s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ + // CharID: s.charID, + // BinaryType: 2, + //}, s) + //s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{ + // CharID: s.charID, + // BinaryType: 3, + //}, s) - // Notify the entree client about all of the existing clients in the stage. + //Notify the entree client about all of the existing clients in the stage. s.logger.Info("Notifying entree about existing stage clients") s.stage.RLock() clientNotif := byteframe.NewByteFrame() @@ -515,6 +500,54 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) { s.QueueSend(clientDupObjNotif.Data()) } +func removeSessionFromStage(s *Session) { + s.stage.Lock() + defer s.stage.Unlock() + + // Remove client from old stage. + delete(s.stage.clients, s) + + // Delete old stage objects owned by the client. + s.logger.Info("Sending MsgSysDeleteObject 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{ + ObjID: stageObject.id, + }, s) + // TODO(Andoryuuta): Should this be sent to the owner's client as well? it currently isn't. + + // Actually delete it form the objects map. + delete(s.stage.objects, objID) + } + } +} + +func stageContainsSession(stage *Stage, s *Session) bool { + stage.RLock() + defer stage.RUnlock() + + for session := range stage.clients { + if session == s { + return true + } + } + + return false +} + +func logoutPlayer(s *Session) { + s.stage.RLock() + for client := range s.stage.clients { + client.QueueSendMHF(&mhfpacket.MsgSysDeleteUser{ + CharID: s.charID, + }) + } + s.stage.RUnlock() + + removeSessionFromStage(s) +} + func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysEnterStage) diff --git a/server/channelserver/session.go b/server/channelserver/session.go index 0bd81d6fb..0f3329c37 100644 --- a/server/channelserver/session.go +++ b/server/channelserver/session.go @@ -3,6 +3,7 @@ package channelserver import ( "encoding/hex" "fmt" + "io" "net" "sync" @@ -124,6 +125,13 @@ func (s *Session) sendLoop() { func (s *Session) recvLoop() { for { pkt, err := s.cryptConn.ReadPacket() + + if err == io.EOF { + s.logger.Info(fmt.Sprintf("Character(%d) disconnected", s.charID)) + logoutPlayer(s) + return + } + if err != nil { s.logger.Warn("Error on ReadPacket, exiting recv loop", zap.Error(err)) return From 892f89eba981a961037b2128c52e6b7c9bf14629 Mon Sep 17 00:00:00 2001 From: Sophie Date: Tue, 10 Mar 2020 07:56:01 +0000 Subject: [PATCH 11/14] Add support for party chat in town --- .../binpacket/msg_cast_bin_private_message.go | 48 +++++++++++++++ server/channelserver/channel_server.go | 12 ++++ server/channelserver/handlers.go | 59 ++++++++++++++----- 3 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 network/binpacket/msg_cast_bin_private_message.go diff --git a/network/binpacket/msg_cast_bin_private_message.go b/network/binpacket/msg_cast_bin_private_message.go new file mode 100644 index 000000000..53a3bdac7 --- /dev/null +++ b/network/binpacket/msg_cast_bin_private_message.go @@ -0,0 +1,48 @@ +package binpacket + +import ( + "github.com/Andoryuuta/Erupe/network" + "github.com/Andoryuuta/byteframe" +) + +type ChatTargetType uint16 + +const ( + CHAT_TARGET_PRIVATE = 0x05 + CHAT_TARGET_PARTY = 0x04 +) + +type MsgBinTargetedChatMessage struct { + // I can't see a reason if this is indeed the number of targets, that + // it should use 2 bytes + TargetCount uint16 + TargetCharIDs []uint32 + TargetType uint16 + RawDataPayload []byte +} + +// Opcode returns the ID associated with this packet type. +func (m *MsgBinTargetedChatMessage) Opcode() network.PacketID { + return network.MSG_SYS_CAST_BINARY +} + +func (m *MsgBinTargetedChatMessage) Parse(bf *byteframe.ByteFrame) error { + m.TargetCount = bf.ReadUint16() + i := uint16(0) + + m.TargetCharIDs = make([]uint32, m.TargetCount) + + for ; i < m.TargetCount; i++ { + m.TargetCharIDs[i] = bf.ReadUint32() + } + + m.TargetType = bf.ReadUint16() + m.RawDataPayload = bf.DataFromCurrent() + + return nil +} + +// Build builds a binary packet from the current data. +func (m *MsgBinTargetedChatMessage) Build(bf *byteframe.ByteFrame) error { + panic("Not implemented") +} diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index 500a6bc3f..d34148cd9 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -167,3 +167,15 @@ func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) session.QueueSendNonBlocking(bf.Data()) } } + +func (s *Server) FindSessionByCharID(charID uint32) *Session { + for _, stage := range s.stages { + for client := range stage.clients { + if client.charID == charID { + return client + } + } + } + + return nil +} diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index ecb6a2715..bef9a7e1d 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -6,6 +6,8 @@ import ( "encoding/base64" "encoding/hex" "fmt" + "github.com/Andoryuuta/Erupe/network/binpacket" + "io" "io/ioutil" "os" "path/filepath" @@ -198,12 +200,9 @@ const ( ) const ( - CHAT_TYPE_WORLD uint8 = iota - CHAT_TYPE_LOCAL - CHAT_TYPE_GUILD - CHAT_TYPE_ALLIANCE - CHAT_TYPE_PARTY - CHAT_TYPE_WHISPER + CHAT_TYPE_WORLD = 0x0a + CHAT_TYPE_STAGE = 0x03 + CHAT_TYPE_TARGETED = 0x01 ) func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { @@ -217,20 +216,48 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { } if pkt.Type1 == BINARY_MESSAGE_TYPE_CHAT { + bf := byteframe.NewByteFrame() + bf.WriteBytes(pkt.RawDataPayload) + bf.Seek(0, io.SeekStart) + fmt.Println("Got chat message!") - switch chatType := pkt.RawDataPayload[2]; chatType { + switch pkt.Type0 { case CHAT_TYPE_WORLD: s.server.BroadcastMHF(resp, s) - case CHAT_TYPE_LOCAL: + case CHAT_TYPE_STAGE: s.stage.BroadcastMHF(resp, s) - case CHAT_TYPE_PARTY: - if s.reservationStage != nil { - // Party messages seem to work partially when a party member starts the quest - // In town it is not working yet, the client now sends the chat packets - // however the other member does not accept it. - s.reservationStage.BroadcastMHF(resp, s) + case CHAT_TYPE_TARGETED: + chatMessage := &binpacket.MsgBinTargetedChatMessage{} + err := chatMessage.Parse(bf) + + if err != nil { + s.logger.Warn("failed to parse chat message") + break } + + chatBf := byteframe.NewByteFrame() + + chatBf.WriteUint16(0x04) + + chatBf.WriteBytes(chatMessage.RawDataPayload) + + resp = &mhfpacket.MsgSysCastedBinary{ + CharID: s.charID, + Type0: pkt.Type0, + Type1: pkt.Type1, + RawDataPayload: chatBf.Data(), + } + + for _, targetID := range chatMessage.TargetCharIDs { + char := s.server.FindSessionByCharID(targetID) + + if char != nil { + char.QueueSendMHF(resp) + } + } + default: + s.stage.BroadcastMHF(resp, s) } /* @@ -605,7 +632,7 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysReserveStage) stageID := stripNullTerminator(pkt.StageID) - fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%v\n", pkt.Unk0, stageID) + fmt.Printf("Got reserve stage req, TargetCount:%v, StageID:%v\n", pkt.Unk0, stageID) // Try to get the stage s.server.stagesLock.Lock() @@ -910,7 +937,7 @@ func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) { // Response to our requesting client. resp := byteframe.NewByteFrame() - resp.WriteUint32(0) // Unk, is this echoed back from pkt.Unk0? + resp.WriteUint32(0) // Unk, is this echoed back from pkt.TargetCount? resp.WriteUint32(objID) // New local obj handle. s.QueueAck(pkt.AckHandle, resp.Data()) From 4c27b99e9a2ea28f3c5f748b9278250ad9df6f12 Mon Sep 17 00:00:00 2001 From: Sophie Date: Tue, 10 Mar 2020 08:25:09 +0000 Subject: [PATCH 12/14] Add support for private messages --- server/channelserver/handlers.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index bef9a7e1d..e7c206426 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -238,8 +238,7 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) { chatBf := byteframe.NewByteFrame() - chatBf.WriteUint16(0x04) - + chatBf.WriteUint16(chatMessage.TargetType) chatBf.WriteBytes(chatMessage.RawDataPayload) resp = &mhfpacket.MsgSysCastedBinary{ From 081deba17b394959b345a71b7a844222e032476d Mon Sep 17 00:00:00 2001 From: Sophie Date: Tue, 10 Mar 2020 08:30:07 +0000 Subject: [PATCH 13/14] Add stage locks on find session function --- server/channelserver/channel_server.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index d34148cd9..f8afb8caf 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -169,12 +169,16 @@ func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) } func (s *Server) FindSessionByCharID(charID uint32) *Session { + s.stagesLock.RLock() + defer s.stagesLock.RUnlock() for _, stage := range s.stages { + stage.RLock() for client := range stage.clients { if client.charID == charID { return client } } + stage.RUnlock() } return nil From ab7a52caeab7cb9e646bdc68addec7435b91e401 Mon Sep 17 00:00:00 2001 From: Sophie Date: Tue, 10 Mar 2020 08:55:52 +0000 Subject: [PATCH 14/14] Fix silly locking issue --- server/channelserver/channel_server.go | 1 + 1 file changed, 1 insertion(+) diff --git a/server/channelserver/channel_server.go b/server/channelserver/channel_server.go index f8afb8caf..6369721ca 100644 --- a/server/channelserver/channel_server.go +++ b/server/channelserver/channel_server.go @@ -175,6 +175,7 @@ func (s *Server) FindSessionByCharID(charID uint32) *Session { stage.RLock() for client := range stage.clients { if client.charID == charID { + stage.RUnlock() return client } }