package main import ( "encoding/hex" "fmt" "tea.melonie54.xyz/sean/go-susapp/src/enum" "tea.melonie54.xyz/sean/go-susapp/src/gamedata" "tea.melonie54.xyz/sean/go-susapp/src/innernetobjects" "tea.melonie54.xyz/sean/go-susapp/src/packets" "tea.melonie54.xyz/sean/go-susapp/src/protocol" "tea.melonie54.xyz/sean/go-susapp/src/util" ) type PacketProcessor struct { sus *SusApp gameDataProcessor *GameDataProcessor normalPacketTagMap map[byte]func(net *protocol.PacketHandler, h *protocol.Hazel) reliablePacketTagMap map[byte]func(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) } func NewPacketProcessor(sus *SusApp) *PacketProcessor { proc := &PacketProcessor{sus: sus, gameDataProcessor: NewGameDataProcessor(sus)} proc.normalPacketTagMap = map[byte]func(net *protocol.PacketHandler, h *protocol.Hazel){ 0x05: proc.captureUnreliableGameDataAll, 0x06: proc.captureUnreliableGameDataTo, } proc.reliablePacketTagMap = map[byte]func(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel){ 0x00: proc.captureCreateGame, 0x01: proc.emptyCapture, 0x02: proc.captureStartGame, 0x04: proc.captureRemovePlayer, 0x05: proc.captureGameDataAll, 0x06: proc.captureGameDataTo, 0x07: proc.captureJoinedGame, 0x0a: proc.captureAlterGame, 0x0d: proc.captureRedirect, } return proc } func (proc *PacketProcessor) receiveNormalPacket(net *protocol.PacketHandler, h []*protocol.Hazel) { for i := 0; i < len(h); i++ { v, ok := proc.normalPacketTagMap[h[i].GetTag()] if ok { v(net, h[i]) } else { fmt.Printf("Unable to parse normal packet with tag of %x\n", h[i].GetTag()) fmt.Printf("Packet %v\n", hex.EncodeToString(h[i].GetUnderlyingPacket().Dump())) } } } func (proc *PacketProcessor) receiveReliablePacket(net *protocol.PacketHandler, nonce uint16, h []*protocol.Hazel) { for i := 0; i < len(h); i++ { v, ok := proc.reliablePacketTagMap[h[i].GetTag()] if ok { v(net, nonce, h[i]) net.SendAcknowledgementPacket(nonce, 0xff) } else { fmt.Printf("Unable to parse packets with tag of %x\n", h[i].GetTag()) fmt.Printf("Packet: %v\n", hex.EncodeToString(h[i].GetUnderlyingPacket().Dump())) } } } func (proc *PacketProcessor) emptyCapture(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { } func (proc *PacketProcessor) captureCreateGame(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { a := &packets.HostGameS2C{} a.Read(h.GetUnderlyingPacket()) proc.sus.state.TypedGameID = util.CodeFromGameID(a.GameID) var z protocol.HazelPayload = &packets.JoinGameC2S{GameID: a.GameID} proc.sus.networkHandler.SendReliablePacket(proc.sus.networkHandler.GetNonce(), []*protocol.Hazel{ protocol.NewHazelFromPayload(&z), }) } func (proc *PacketProcessor) captureStartGame(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { a := &packets.StartGameH2G{} a.Read(h.GetUnderlyingPacket()) if a.GameID == proc.sus.state.CurrentGame.GameID { proc.sus.state.Screen = enum.SCREEN_INGAME proc.sus.state.LoadCollisionForMap(proc.sus.state.CurrentGame.GameOptions.Maps) var d1 packets.GameDataSubpacket = &gamedata.ReadyC2H{ReadyClientID: proc.sus.state.CurrentGame.ClientID} var d2 protocol.HazelPayload = packets.NewGameDataToSingleSubpacket(proc.sus.state.CurrentGame.GameID, proc.sus.state.CurrentGame.HostClientID, &d1) net.SendReliablePacket(proc.sus.networkHandler.GetNonce(), []*protocol.Hazel{protocol.NewHazelFromPayload(&d2)}) } } func (proc *PacketProcessor) captureGameDataAll(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { a := &packets.GameDataAll{} a.Read(h.GetUnderlyingPacket()) proc.gameDataProcessor.receiveGameDataSubpacket(net, a.GameID, a.Subpackets) } func (proc *PacketProcessor) captureGameDataTo(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { a := &packets.GameDataTo{} a.Read(h.GetUnderlyingPacket()) if a.TargetClientID == proc.sus.state.CurrentGame.ClientID { proc.gameDataProcessor.receiveGameDataSubpacket(net, a.GameID, a.Subpackets) } } func (proc *PacketProcessor) captureUnreliableGameDataAll(net *protocol.PacketHandler, h *protocol.Hazel) { a := &packets.GameDataAll{} a.Read(h.GetUnderlyingPacket()) proc.gameDataProcessor.receiveGameDataSubpacket(net, a.GameID, a.Subpackets) } func (proc *PacketProcessor) captureUnreliableGameDataTo(net *protocol.PacketHandler, h *protocol.Hazel) { a := &packets.GameDataTo{} a.Read(h.GetUnderlyingPacket()) if a.TargetClientID == proc.sus.state.CurrentGame.ClientID { proc.gameDataProcessor.receiveGameDataSubpacket(net, a.GameID, a.Subpackets) } } func (proc *PacketProcessor) captureJoinedGame(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { a := &packets.JoinedGameH2C{} a.Read(h.GetUnderlyingPacket()) fmt.Printf("Joined game: %v\n", util.CodeFromGameID(a.GameID)) fmt.Printf("Client id: %v\n", a.JoinedClientID) fmt.Printf("Host client id: %v\n", a.HostClientID) proc.sus.state.CurrentGame = &CurrentGameData{ GameID: a.GameID, ClientID: a.JoinedClientID, HostClientID: a.HostClientID, GamePrivacy: 0, GameOptions: protocol.NewDefaultGameOptionsData(), NetObjects: map[uint32]*innernetobjects.InnerNetObject{}, PlayerNetObjects: &PlayerNetObjectMaps{}, } proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerClientID = map[uint32]*PlayerObject{} proc.sus.state.CurrentGame.PlayerNetObjects.ByPlayerNetID = map[uint32]*PlayerObject{} proc.sus.state.CurrentGame.OtherClientIDs = a.OtherClientIDs proc.sus.state.Screen = enum.SCREEN_LOBBY proc.sus.state.LoadCollision("lobby") // Send active pod type var b1 packets.GameDataSubpacket = &gamedata.SetActivePodType{PodType: "mira"} // TODO: don't hard code this var b2 protocol.HazelPayload = packets.NewGameDataSingleSubpacket(proc.sus.state.CurrentGame.GameID, &b1) net.SendReliablePacket(net.GetNonce(), []*protocol.Hazel{protocol.NewHazelFromPayload(&b2)}) // Send scene change var c1 packets.GameDataSubpacket = &gamedata.SceneChangeC2H{ ClientID: proc.sus.state.CurrentGame.ClientID, SceneName: "OnlineGame", } var c2 protocol.HazelPayload = packets.NewGameDataSingleSubpacket(proc.sus.state.CurrentGame.GameID, &c1) net.SendReliablePacket(net.GetNonce(), []*protocol.Hazel{protocol.NewHazelFromPayload(&c2)}) var d1 packets.GameDataSubpacket = gamedata.NewRpcFromRpcSubs(proc.sus.state.CurrentGame.HostClientID, []gamedata.RpcSub{ &gamedata.RpcSetHat{HatID: "hat_pk04_Bear"}, &gamedata.RpcSetPet{PetID: "pet_Robot"}, &gamedata.RpcSetSkin{SkinID: "skin_Winter"}, }) var d2 protocol.HazelPayload = packets.NewGameDataSingleSubpacket(proc.sus.state.CurrentGame.GameID, &d1) net.SendReliablePacket(net.GetNonce(), []*protocol.Hazel{protocol.NewHazelFromPayload(&d2)}) } func (proc *PacketProcessor) captureAlterGame(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { a := &packets.AlterGameH2G{} a.Read(h.GetUnderlyingPacket()) fmt.Printf("Alter game: %v\n", util.CodeFromGameID(a.GameID)) fmt.Printf("Tag ID: %v\n", a.TagID) fmt.Printf("Tag value: %v\n", a.TagValue) if a.GameID == proc.sus.state.CurrentGame.GameID && a.TagID == 1 { proc.sus.state.CurrentGame.GamePrivacy = a.TagValue } } func (proc *PacketProcessor) captureRedirect(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { a := &packets.RedirectS2C{} a.Read(h.GetUnderlyingPacket()) fmt.Printf("Redirect: %v:%v", a.IPAddress.String(), a.Port) if proc.sus.networkHandler != nil { proc.sus.networkHandler.SendNormalDisconnectPacket() proc.sus.networkHandler.Close() proc.sus.networkHandler = nil } proc.sus.setupPacketHandler(&a.IPAddress, int(a.Port), func(ph *protocol.PacketHandler) { fmt.Println("Sending first packet to new server") ph.SendHelloPacket(ph.GetNonce(), 0, proc.sus.state.Version, proc.sus.state.Settings.PlayerName, 0, proc.sus.state.Settings.Language, proc.sus.state.Settings.QuickChatMode) y, _ := util.CodeToGameID(proc.sus.state.TypedGameID) var z protocol.HazelPayload = &packets.JoinGameC2S{GameID: y} ph.SendReliablePacket(ph.GetNonce(), []*protocol.Hazel{ protocol.NewHazelFromPayload(&z), }) }) } func (proc *PacketProcessor) captureRemovePlayer(net *protocol.PacketHandler, nonce uint16, h *protocol.Hazel) { a := &packets.RemovePlayerS2G{} a.Read(h.GetUnderlyingPacket()) if proc.sus.state.CurrentGame != nil { if a.GameID == proc.sus.state.CurrentGame.GameID && a.HostClientID == proc.sus.state.CurrentGame.ClientID { fmt.Println("Host player disconnected and left me in charge") fmt.Println("I can't host a game so I'm disconnecting too") net.SendNormalDisconnectPacket() } } }