diff --git a/config.json b/config.json index 976756597..29af1fb8d 100644 --- a/config.json +++ b/config.json @@ -17,6 +17,7 @@ "FestaEvent": 0, "TournamentEvent": 0, "MezFesEvent": true, + "MezFesAlt": false, "DisableMailItems": true, "DisableTokenCheck": false, "SaveDumps": { diff --git a/config/config.go b/config/config.go index 8b7daf7ee..b025e7a0a 100644 --- a/config/config.go +++ b/config/config.go @@ -37,6 +37,7 @@ type DevModeOptions struct { FestaEvent int // Hunter's Festa event status TournamentEvent int // VS Tournament event status MezFesEvent bool // MezFes status + MezFesAlt bool // Swaps out Volpakkun for Tokotoko DisableTokenCheck bool // Disables checking login token exists in the DB (security risk!) DisableMailItems bool // Hack to prevent english versions of MHF from crashing SaveDumps SaveDumpOptions diff --git a/main.go b/main.go index 3638a0093..c5ac726b3 100644 --- a/main.go +++ b/main.go @@ -180,7 +180,13 @@ func main() { DB: db, DiscordBot: discordBot, }) - err = c.Start(int(ce.Port)) + if ee.IP == "" { + c.IP = erupeConfig.Host + } else { + c.IP = ee.IP + } + c.Port = ce.Port + err = c.Start() if err != nil { preventClose(fmt.Sprintf("Failed to start channel server: %s", err.Error())) } else { diff --git a/network/mhfpacket/msg_mhf_transit_message.go b/network/mhfpacket/msg_mhf_transit_message.go index 32dba46d6..1d15c6d42 100644 --- a/network/mhfpacket/msg_mhf_transit_message.go +++ b/network/mhfpacket/msg_mhf_transit_message.go @@ -1,20 +1,20 @@ package mhfpacket import ( - "errors" + "errors" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgMhfTransitMessage represents the MSG_MHF_TRANSIT_MESSAGE type MsgMhfTransitMessage struct { - AckHandle uint32 - Unk0 uint8 - Unk1 uint8 - Unk2 uint16 - MessageData []byte + AckHandle uint32 + Unk0 uint8 + Unk1 uint8 + SearchType uint16 + MessageData []byte } // Opcode returns the ID associated with this packet type. @@ -24,12 +24,12 @@ func (m *MsgMhfTransitMessage) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgMhfTransitMessage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - m.AckHandle = bf.ReadUint32() - m.Unk0 = bf.ReadUint8() - m.Unk1 = bf.ReadUint8() - m.Unk2 = bf.ReadUint16() - m.MessageData = bf.ReadBytes(uint(bf.ReadUint16())) - return nil + m.AckHandle = bf.ReadUint32() + m.Unk0 = bf.ReadUint8() + m.Unk1 = bf.ReadUint8() + m.SearchType = bf.ReadUint16() + m.MessageData = bf.ReadBytes(uint(bf.ReadUint16())) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_sys_acquire_semaphore.go b/network/mhfpacket/msg_sys_acquire_semaphore.go index 89b974ff4..2be7284d9 100644 --- a/network/mhfpacket/msg_sys_acquire_semaphore.go +++ b/network/mhfpacket/msg_sys_acquire_semaphore.go @@ -1,15 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" + "erupe-ce/common/bfutil" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysAcquireSemaphore represents the MSG_SYS_ACQUIRE_SEMAPHORE -type MsgSysAcquireSemaphore struct{} +type MsgSysAcquireSemaphore struct { + AckHandle uint32 + SemaphoreID string +} // Opcode returns the ID associated with this packet type. func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID { @@ -18,7 +22,10 @@ func (m *MsgSysAcquireSemaphore) Opcode() network.PacketID { // Parse parses the packet from binary func (m *MsgSysAcquireSemaphore) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { - return errors.New("NOT IMPLEMENTED") + m.AckHandle = bf.ReadUint32() + SemaphoreIDLength := bf.ReadUint8() + m.SemaphoreID = string(bfutil.UpToNull(bf.ReadBytes(uint(SemaphoreIDLength)))) + return nil } // Build builds a binary packet from the current data. diff --git a/network/mhfpacket/msg_sys_enumerate_stage.go b/network/mhfpacket/msg_sys_enumerate_stage.go index 802b385f5..a3f125941 100644 --- a/network/mhfpacket/msg_sys_enumerate_stage.go +++ b/network/mhfpacket/msg_sys_enumerate_stage.go @@ -1,19 +1,19 @@ package mhfpacket -import ( - "errors" +import ( + "errors" + "erupe-ce/common/stringsupport" - "erupe-ce/network/clientctx" - "erupe-ce/network" "erupe-ce/common/byteframe" + "erupe-ce/network" + "erupe-ce/network/clientctx" ) // MsgSysEnumerateStage represents the MSG_SYS_ENUMERATE_STAGE type MsgSysEnumerateStage struct { - AckHandle uint32 - Unk0 uint8 // Hardcoded 1 in the binary - StageIDLength uint8 - StageID string // NULL terminated string. + AckHandle uint32 + Unk0 uint8 // Hardcoded 1 in the binary + StagePrefix string // NULL terminated string. } // Opcode returns the ID associated with this packet type. @@ -25,8 +25,8 @@ func (m *MsgSysEnumerateStage) Opcode() network.PacketID { func (m *MsgSysEnumerateStage) Parse(bf *byteframe.ByteFrame, ctx *clientctx.ClientContext) error { m.AckHandle = bf.ReadUint32() m.Unk0 = bf.ReadUint8() - m.StageIDLength = bf.ReadUint8() - m.StageID = string(bf.ReadBytes(uint(m.StageIDLength))) + bf.ReadUint8() + m.StagePrefix = stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) return nil } diff --git a/server/channelserver/handlers.go b/server/channelserver/handlers.go index 60e120c42..1cfe9e3ff 100644 --- a/server/channelserver/handlers.go +++ b/server/channelserver/handlers.go @@ -4,7 +4,11 @@ import ( "bytes" "encoding/binary" "encoding/hex" + "erupe-ce/common/stringsupport" "fmt" + "io" + "net" + "strings" "io/ioutil" "math/bits" @@ -341,10 +345,157 @@ func handleMsgSysRightsReload(s *Session, p mhfpacket.MHFPacket) { func handleMsgMhfTransitMessage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgMhfTransitMessage) - // TODO: figure out what this is supposed to return - // probably what world+land the targeted character is on? - // stubbed response will just say user not found - doAckBufSucceed(s, pkt.AckHandle, make([]byte, 4)) + resp := byteframe.NewByteFrame() + resp.WriteUint16(0) + var count uint16 + switch pkt.SearchType { + case 1: // CID + bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) + CharID := bf.ReadUint32() + for _, c := range s.server.Channels { + for _, session := range c.sessions { + if session.charID == CharID { + count++ + sessionName := stringsupport.UTF8ToSJIS(session.Name) + sessionStage := stringsupport.UTF8ToSJIS(session.stageID) + resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) + resp.WriteUint16(c.Port) + resp.WriteUint32(session.charID) + resp.WriteBool(true) + resp.WriteUint8(uint8(len(sessionName) + 1)) + resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}]))) + resp.WriteBytes(make([]byte, 40)) + resp.WriteUint8(uint8(len(sessionStage) + 1)) + resp.WriteBytes(make([]byte, 8)) + resp.WriteNullTerminatedBytes(sessionName) + resp.WriteBytes(c.userBinaryParts[userBinaryPartID{session.charID, 3}]) + resp.WriteNullTerminatedBytes(sessionStage) + } + } + } + case 2: // Name + bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) + bf.ReadUint16() // lenSearchTerm + bf.ReadUint16() // maxResults + bf.ReadUint8() // Unk + searchTerm := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + for _, c := range s.server.Channels { + for _, session := range c.sessions { + if count == 100 { + break + } + if strings.Contains(session.Name, searchTerm) { + count++ + sessionName := stringsupport.UTF8ToSJIS(session.Name) + sessionStage := stringsupport.UTF8ToSJIS(session.stageID) + resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) + resp.WriteUint16(c.Port) + resp.WriteUint32(session.charID) + resp.WriteBool(true) + resp.WriteUint8(uint8(len(sessionName) + 1)) + resp.WriteUint16(uint16(len(c.userBinaryParts[userBinaryPartID{session.charID, 3}]))) + resp.WriteBytes(make([]byte, 40)) + resp.WriteUint8(uint8(len(sessionStage) + 1)) + resp.WriteBytes(make([]byte, 8)) + resp.WriteNullTerminatedBytes(sessionName) + resp.WriteBytes(c.userBinaryParts[userBinaryPartID{charID: session.charID, index: 3}]) + resp.WriteNullTerminatedBytes(sessionStage) + } + } + } + case 3: // Enumerate Party + bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) + ip := bf.ReadBytes(4) + ipString := fmt.Sprintf("%d.%d.%d.%d", ip[3], ip[2], ip[1], ip[0]) + port := bf.ReadUint16() + bf.ReadUint16() // lenStage + maxResults := bf.ReadUint16() + bf.ReadBytes(1) + stageID := stringsupport.SJISToUTF8(bf.ReadNullTerminatedBytes()) + for _, c := range s.server.Channels { + if c.IP == ipString && c.Port == port { + for _, stage := range c.stages { + if stage.id == stageID { + if count == maxResults { + break + } + for session := range stage.clients { + count++ + sessionStage := stringsupport.UTF8ToSJIS(session.stageID) + sessionName := stringsupport.UTF8ToSJIS(session.Name) + resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) + resp.WriteUint16(c.Port) + resp.WriteUint32(session.charID) + resp.WriteUint8(uint8(len(sessionStage) + 1)) + resp.WriteUint8(uint8(len(sessionName) + 1)) + resp.WriteUint8(0) + resp.WriteUint8(7) // lenBinary + resp.WriteBytes(make([]byte, 48)) + resp.WriteNullTerminatedBytes(sessionStage) + resp.WriteNullTerminatedBytes(sessionName) + resp.WriteUint16(999) // HR + resp.WriteUint16(999) // GR + resp.WriteBytes([]byte{0x06, 0x10, 0x00}) // Unk + } + } + } + } + } + case 4: // Find Party + bf := byteframe.NewByteFrameFromBytes(pkt.MessageData) + bf.ReadUint8() + maxResults := bf.ReadUint16() + bf.ReadUint8() + bf.ReadUint8() + partyType := bf.ReadUint16() + _ = bf.DataFromCurrent() // Restrictions + var stagePrefix string + switch partyType { + case 0: // Public Bar + stagePrefix = "sl2Ls210" + case 1: // Tokotoko Partnya + stagePrefix = "sl2Ls463" + case 2: // Hunting Prowess Match + stagePrefix = "sl2Ls286" + case 3: // Volpakkun Together + stagePrefix = "sl2Ls465" + case 5: // Quick Party + // Unk + } + for _, c := range s.server.Channels { + for _, stage := range c.stages { + if count == maxResults { + break + } + if strings.HasPrefix(stage.id, stagePrefix) { + count++ + sessionStage := stringsupport.UTF8ToSJIS(stage.id) + resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) + resp.WriteUint16(c.Port) + + // TODO: This is half right, could be trimmed + resp.WriteUint16(0) + resp.WriteUint16(uint16(len(stage.clients))) + resp.WriteUint16(uint16(len(stage.clients))) + resp.WriteUint16(stage.maxPlayers) + resp.WriteUint16(0) + resp.WriteUint16(uint16(len(stage.clients))) + // + + resp.WriteUint16(uint16(len(sessionStage) + 1)) + resp.WriteUint8(1) + resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}]))) + resp.WriteBytes(make([]byte, 16)) + resp.WriteNullTerminatedBytes(sessionStage) + resp.WriteBytes([]byte{0x00}) + resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}]) + } + } + } + } + resp.Seek(0, io.SeekStart) + resp.WriteUint16(count) + doAckBufSucceed(s, pkt.AckHandle, resp.Data()) } func handleMsgCaExchangeItem(s *Session, p mhfpacket.MHFPacket) {} diff --git a/server/channelserver/handlers_semaphore.go b/server/channelserver/handlers_semaphore.go index e7b9834ab..6659bd2da 100644 --- a/server/channelserver/handlers_semaphore.go +++ b/server/channelserver/handlers_semaphore.go @@ -124,7 +124,16 @@ func handleMsgSysCreateAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { } func handleMsgSysAcquireSemaphore(s *Session, p mhfpacket.MHFPacket) { - //pkt := p.(*mhfpacket.MsgSysAcquireSemaphore) + pkt := p.(*mhfpacket.MsgSysAcquireSemaphore) + if sema, exists := s.server.semaphore[pkt.SemaphoreID]; exists { + sema.clients[s] = s.charID + bf := byteframe.NewByteFrame() + bf.WriteUint32(sema.id) + doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) + } else { + doAckSimpleFail(s, pkt.AckHandle, make([]byte, 4)) + } + } func handleMsgSysReleaseSemaphore(s *Session, p mhfpacket.MHFPacket) { diff --git a/server/channelserver/handlers_stage.go b/server/channelserver/handlers_stage.go index 4fcdcbfc2..4bf19d114 100644 --- a/server/channelserver/handlers_stage.go +++ b/server/channelserver/handlers_stage.go @@ -2,6 +2,7 @@ package channelserver import ( "fmt" + "strings" "time" "erupe-ce/common/byteframe" @@ -198,22 +199,24 @@ func handleMsgSysLeaveStage(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysLockStage(s *Session, p mhfpacket.MHFPacket) { pkt := p.(*mhfpacket.MsgSysLockStage) // TODO(Andoryuuta): What does this packet _actually_ do? + // I think this is supposed to mark a stage as no longer able to accept client reservations doAckSimpleSucceed(s, pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00}) } func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) { - s.reservationStage.RLock() - defer s.reservationStage.RUnlock() + if s.reservationStage != nil { + s.reservationStage.RLock() + defer s.reservationStage.RUnlock() - for charID := range s.reservationStage.reservedClientSlots { - session := s.server.FindSessionByCharID(charID) - session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) + for charID := range s.reservationStage.reservedClientSlots { + session := s.server.FindSessionByCharID(charID) + session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) + } + + delete(s.server.stages, s.reservationStage.id) } - s.server.Lock() - defer s.server.Unlock() - - delete(s.server.stages, s.reservationStage.id) + destructEmptyStages(s) } func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) { @@ -366,8 +369,7 @@ func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { continue } - // Check for valid stage type - if sid[3:5] != "Qs" && sid[3:5] != "Ms" && sid[3:5] != "Ls" { + if !strings.Contains(stage.id, pkt.StagePrefix) { continue } diff --git a/server/channelserver/sys_channel_server.go b/server/channelserver/sys_channel_server.go index 69335280c..a23ec2834 100644 --- a/server/channelserver/sys_channel_server.go +++ b/server/channelserver/sys_channel_server.go @@ -54,6 +54,8 @@ type Server struct { sync.Mutex Channels []*Server ID uint16 + IP string + Port uint16 logger *zap.Logger db *sqlx.DB erupeConfig *config.Config @@ -196,8 +198,8 @@ func NewServer(config *Config) *Server { } // Start starts the server in a new goroutine. -func (s *Server) Start(port int) error { - l, err := net.Listen("tcp", fmt.Sprintf(":%d", port)) +func (s *Server) Start() error { + l, err := net.Listen("tcp", fmt.Sprintf(":%d", s.Port)) if err != nil { return err } diff --git a/server/signserver/dsgn_resp.go b/server/signserver/dsgn_resp.go index fee38c1d3..650188000 100644 --- a/server/signserver/dsgn_resp.go +++ b/server/signserver/dsgn_resp.go @@ -128,7 +128,7 @@ func (s *Session) makeSignInResp(uid int) []byte { bf.WriteUint32(0x0A5197DF) mezfes := s.server.erupeConfig.DevModeOptions.MezFesEvent - alt := false + alt := s.server.erupeConfig.DevModeOptions.MezFesAlt if mezfes { // Start time bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(-5 * time.Minute).Unix())) @@ -136,7 +136,7 @@ func (s *Session) makeSignInResp(uid int) []byte { bf.WriteUint32(uint32(channelserver.Time_Current_Adjusted().Add(24 * time.Hour * 7).Unix())) bf.WriteUint8(2) // Unk bf.WriteUint32(20) // Single tickets - bf.WriteUint32(0) // Group tickets + bf.WriteUint32(10) // Group tickets bf.WriteUint8(8) // Stalls open bf.WriteUint8(0xA) // Unk bf.WriteUint8(0x3) // Pachinko