package serverCommunication import ( "errors" "time" "github.com/gofiber/websocket/v2" "krakatoa.net/backend/modules/cache" "krakatoa.net/backend/modules/kraProtocol" "krakatoa.net/backend/modules/logger" "krakatoa.net/backend/modules/structs" ) func HandleMinecraftMessage(conn *websocket.Conn, msg []byte) { status, cmdID, dest, playerUuid, cmdNumber, args := kraProtocol.DecodeJavaMessage(msg) mcClient := GetMinecraftClientByConn(conn) if mcClient == nil { logger.Minecraft.Warnln("mcClient is nil") return } if status == kraProtocol.StatusErrTryAgain { logger.Minecraft.Warnln("err StatusErrTryAgain cmdID", cmdID, "cmdNumber", cmdNumber) return } if status == kraProtocol.StatusErrNoPerms { logger.Minecraft.Warnln("err StatusErrNoPerms cmdID", cmdID, "cmdNumber", cmdNumber) return } if status == kraProtocol.StatusErrArgLenTooBig { logger.Minecraft.Warnln("err StatusErrArgLenTooBig cmdID", cmdID, "cmdNumber", cmdNumber) return } if status == kraProtocol.StatusMessageAlreadyInQueue { logger.Minecraft.Warnln("rec StatusMessageAlreadyInQueue cmdID", cmdID, "cmdNumber", cmdNumber) return } if status == kraProtocol.StatusReply { logger.Minecraft.Debugln(mcClient.IsCmdIDInSendMessagesQueue(cmdID)) if mcClient.IsCmdIDInSendMessagesQueue(cmdID) { queueMsg, err := mcClient.GetMessageFromSendQueueByCmdID(cmdID) logger.Minecraft.Debugln("IsCmdIDInSendMessagesQueue queueMsg", queueMsg.OriginDest, queueMsg.OriginCmdID) if err != nil { return } if queueMsg.OriginDest == kraProtocol.DestVoice || queueMsg.OriginDest == kraProtocol.DestMobile { webClient := GetWebClientByUuid(playerUuid) if webClient == nil { logger.Minecraft.Warn("webClient is nil") return } raw := kraProtocol.EncodeWebMessage(kraProtocol.StatusReply, queueMsg.OriginCmdID, cmdNumber, args) var err error // TODO: handle origin dest "backend" (messages were sent from the backend to the minecraft server) if queueMsg.OriginDest == kraProtocol.DestVoice { err = webClient.SendBinaryMessage(webClient.VoiceConn, raw) } else { err = webClient.SendBinaryMessage(webClient.MobileConn, raw) } if err != nil { logger.Minecraft.Println("failed to send message") } } mcClient.RemoveMessageFromSendQueueByCmdID(cmdID) } return } var raw []byte var err error // queue handling when message already in process if status == kraProtocol.StatusGet { // check if the cmdID already exists in the received messages queue. if mcClient.IsCmdIDInReceivedMessagesQueue(cmdID) { raw := kraProtocol.EncodeJavaMessage(kraProtocol.StatusMessageAlreadyInQueue, cmdID, dest, playerUuid, cmdNumber, args) err = mcClient.SendBinaryMessage(raw) if err != nil { logger.Minecraft.Warnln("write err", err) } return } // does not exist in the list. Add to the list mcClient.AddMessageToReceivedQueue(mcClient.Dest, cmdID, raw) } // dest handling if dest == kraProtocol.DestBackend { resArgs := minecraftCommandHandler(cmdNumber, playerUuid) if status == kraProtocol.StatusGet { raw = kraProtocol.EncodeJavaMessage(kraProtocol.StatusReply, cmdID, 0, playerUuid, cmdNumber, resArgs) err = mcClient.SendBinaryMessage(raw) if err != nil { logger.Minecraft.Warnln("write:", err) } } } else if dest == kraProtocol.DestVoice || dest == kraProtocol.DestMobile { // forwarding messsage to voice or mobile webClient := GetWebClientByUuid(playerUuid) if webClient == nil || dest == kraProtocol.DestVoice && webClient.VoiceConn == nil || dest == kraProtocol.DestMobile && webClient.MobileConn == nil { raw = kraProtocol.EncodeJavaMessage(kraProtocol.StatusErrTryAgain, cmdID, 0, playerUuid, cmdNumber, args) err = mcClient.SendBinaryMessage(raw) if err != nil { logger.Minecraft.Warnln("write:", err) } return } if status == kraProtocol.StatusGet { webClient := GetWebClientByUuid(playerUuid) var webCmdID int if dest == kraProtocol.DestVoice { webCmdID = structs.GenerateWebCmdID(webClient.CurrentVoiceSendMessageCmdIDIndexByBackend) webClient.CurrentVoiceSendMessageCmdIDIndexByBackend = webCmdID logger.Minecraft.Debugln("webCmdID", webCmdID) //a := &structs.A{WebCmdID: webCmdID, CmdIDFromMinecraftServer: cmdID, DestFromMinecraftServer: mcClient.Dest} //webClient.VoiceCmdIDsByBackend = append(webClient.VoiceCmdIDsByBackend, a) //logger.Minecraft.Debugln("VoiceCMDIDsByBackend", webClient.VoiceCmdIDsByBackend) } else { // dest mobile webCmdID = structs.GenerateWebCmdID(webClient.CurrentMobileSendMessageCmdIDIndexByBackend) webClient.CurrentMobileSendMessageCmdIDIndexByBackend = webCmdID logger.Minecraft.Debugln("webCmdID", webCmdID) //a := &structs.A{WebCmdID: webCmdID, CmdIDFromMinecraftServer: cmdID, DestFromMinecraftServer: mcClient.Dest} //webClient.MobileCmdIDsByBackend = append(webClient.MobileCmdIDsByBackend, a) //logger.Minecraft.Debugln("MobileCMDIDsByBackend", webClient.MobileCmdIDsByBackend) } raw = kraProtocol.EncodeWebMessage(kraProtocol.StatusGet, webCmdID, cmdNumber, args) if dest == kraProtocol.DestVoice { webClient.SendVoiceQueueMessages = append(webClient.SendVoiceQueueMessages, &structs.SendQueueMessage{MessageRaw: raw, CmdID: webCmdID, TrySendCount: 0, OriginDest: mcClient.Dest, OriginCmdID: cmdID, Time: time.Now()}) } else { webClient.SendMobileQueueMessages = append(webClient.SendMobileQueueMessages, &structs.SendQueueMessage{MessageRaw: raw, CmdID: webCmdID, TrySendCount: 0, OriginDest: mcClient.Dest, OriginCmdID: cmdID, Time: time.Now()}) } } else { raw = kraProtocol.EncodeWebMessage(kraProtocol.StatusSend, 0, cmdNumber, args) } if dest == kraProtocol.DestVoice { webClient.SendBinaryMessage(webClient.VoiceConn, raw) } else { webClient.SendBinaryMessage(webClient.MobileConn, raw) } } else if dest == kraProtocol.DestProxy { client := GetMinecraftClientByName("proxy-1") if client == nil { raw = kraProtocol.EncodeJavaMessage(kraProtocol.StatusErrTryAgain, cmdID, 0, playerUuid, cmdNumber, "") err = mcClient.SendBinaryMessage(raw) if err != nil { logger.Minecraft.Warnln("write:", err) } return } if status == kraProtocol.StatusGet { raw = kraProtocol.EncodeJavaMessage(kraProtocol.StatusReply, cmdID, dest, playerUuid, cmdNumber, args) } else { raw = kraProtocol.EncodeJavaMessage(kraProtocol.StatusSend, 0, dest, playerUuid, cmdNumber, args) } err = client.SendBinaryMessage(raw) if err != nil { logger.Minecraft.Warnln("write:", err) } } else { // TODO: handle messages -> GameServer-1 to GameServer-2 logger.Minecraft.Debugln("dest not found", dest) } } func GetMinecraftClientByDest(dest int) *structs.MinecraftClient { for _, mcClient := range cache.MinecraftClients { if mcClient.Dest == dest { return mcClient } } return nil } // @deprecated: use GetMinecraftClientByDest instead func GetMinecraftClientByName(Name string) *structs.MinecraftClient { for name, client := range cache.MinecraftClients { if name == Name { logger.Minecraft.Debugln("return mc client", client.Name) return client } } logger.Minecraft.Warnln("mc client nil") return nil } func GetMinecraftClientByConn(conn *websocket.Conn) *structs.MinecraftClient { for _, client := range cache.MinecraftClients { if client.Conn == conn { return client } } return nil } func getMinecraftServerNameByDest(dest int) string { if dest == kraProtocol.DestProxy { return "proxy-1" } else { // get players current server from db if dest is 11 return "lobby-1" } } func minecraftCommandHandler(cmdNumber int, playerUuid string) (args string) { switch cmdNumber { case 10: // testing return IsVoiceAndMobileSocketConnected(playerUuid) default: return "default" } } // Deprecated: only for testing, use SendMessageToMinecraftServer instead func SendMessageToServer(dest int, playerUuid string, args string) { mcClient := GetMinecraftClientByName(getMinecraftServerNameByDest(dest)) raw := kraProtocol.EncodeJavaMessage(10, 1, 2, playerUuid, 15, args) if mcClient != nil { mcClient.SendBinaryMessage(raw) } } func SendMessageToMinecraftServer(originDest int, originCmdID int, status int, dest int, playerUuid string, cmdNumber int, args string) error { cmdID := kraProtocol.DefaultCmdID serverName := getMinecraftServerNameByDest(dest) mcClient := GetMinecraftClientByName(serverName) if mcClient == nil { return errors.New("mcClient nil") } if status == kraProtocol.StatusGet { cmdID = mcClient.GenerateMinecraftCmdID() } raw := kraProtocol.EncodeJavaMessage(status, cmdID, dest, playerUuid, cmdNumber, args) err := mcClient.SendBinaryMessage(raw) if err != nil { logger.Minecraft.Warnln("err", err) return errors.New("write err: " + err.Error()) } if status == kraProtocol.StatusGet { mcClient.AddMessageToSendQueue(originDest, originCmdID, raw, cmdID) } return nil } func GenerateDestForNewMinecraftClient() int { newDest := 10 for { if isDestInUsageFromOneMinecraftClient(newDest) { break } else { newDest++ } } return newDest } func isDestInUsageFromOneMinecraftClient(dest int) bool { for _, mcClient := range cache.MinecraftClients { if mcClient.Dest == dest { return false } } return true }