init commit

This commit is contained in:
Jose Luis Montañes Ojados
2026-01-14 21:33:21 +01:00
commit dbab788e6b
27 changed files with 4001 additions and 0 deletions

72
internal/handlers/auth.go Normal file
View File

@@ -0,0 +1,72 @@
package handlers
import (
"customServer/internal/protocol"
"customServer/internal/state"
"fmt"
"net"
"time"
)
func HandleAsyncAuthRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling AsyncAuthRequest")
// Session Message
sessionData := make([]byte, 0)
// field 1 (Id), Varint (WireType 0)
sessionData = append(sessionData, 0x08)
sessionData = append(sessionData, protocol.EncodeVarint(uint64(time.Now().UnixNano()))...)
// Player Message
player := state.GetMockPlayerBytes()
// AsyncConnectedRequest
asyncConnected := make([]byte, 0)
// field 1 (Session), message
asyncConnected = append(asyncConnected, 0x0a)
asyncConnected = append(asyncConnected, protocol.EncodeVarint(uint64(len(sessionData)))...)
asyncConnected = append(asyncConnected, sessionData...)
// field 2 (Player), message
asyncConnected = append(asyncConnected, 0x12)
asyncConnected = append(asyncConnected, protocol.EncodeVarint(uint64(len(player)))...)
asyncConnected = append(asyncConnected, player...)
return asyncConnected, 406
}
func HandlePingRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling PingRequest")
// The client sends a PingRequest with a timestamp (field 1).
// We should respond with a PingRequest (777) containing the same timestamp.
// Extract timestamp if possible, otherwise just send back a default one
// For simplicity, we just echo back what we got if it's small, or send a new one.
return requestData, 777
}
func HandleAsyncDisconnectRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling AsyncDisconnectRequest")
return []byte{}, 401
}
func HandleAskServerStatisticsRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling AskServerStatisticsRequest")
// Return ServerStatisticsRequest (409)
// Fields: 1:HostedGames, 2:Players, 3:ConnectedPlayers (all int32)
stats := make([]byte, 0)
// Field 1: HostedGames (Varint)
stats = append(stats, 0x08)
stats = append(stats, protocol.EncodeVarint(1)...)
// Field 2: Players (Varint)
stats = append(stats, 0x10)
stats = append(stats, protocol.EncodeVarint(1)...)
// Field 3: ConnectedPlayers (Varint)
stats = append(stats, 0x18)
stats = append(stats, protocol.EncodeVarint(1)...)
return stats, 409
}

View File

@@ -0,0 +1,66 @@
package handlers
import (
"customServer/internal/protocol"
"fmt"
"net"
)
// Dispatcher Function
// Note: We might pass dependencies here if we want to avoid globals,
// but for this refactor we'll stick to dispatching to handler functions.
func Dispatch(conn net.Conn, packetID int64, requestNumber int32, requestData []byte) ([]byte, int) {
fmt.Printf("[Dispatcher] Dispatching Request %d (PacketID: %d)\n", requestNumber, packetID)
switch requestNumber {
// Auth
case 400:
return HandleAsyncAuthRequest(conn, requestData)
case 401:
return HandleAsyncDisconnectRequest(conn, requestData)
case 408:
return HandleAskServerStatisticsRequest(conn, requestData)
// System / Buddies
case 515:
return HandleAsyncBuddyListRequest(conn, requestData)
case 560:
return HandleAsyncIgnoreListRequest(conn, requestData)
// Lobby
case 600:
return HandleEnterLobbyRequest(conn, requestData)
case 604:
return HandleLobbyPlayerListRequest(conn, requestData)
case 607:
return HandleLobbyCreateGameRequest(conn, requestData)
case 609:
return HandleLobbyGameListRequest(conn, requestData)
case 610:
return HandleLobbyJoinGameRequest(conn, requestData)
case 622:
return HandleObservableGameListRequest(conn, requestData)
// Game
case 511:
return HandleWhatsNewPussycatRequest(conn, requestData)
case 608:
// Note: 608 is LobbyGameCreatedRequest (OUT), 607 is IN.
// If client sends 608, it's weird.
return nil, 0
// System / Ping
case 777:
return HandlePingRequest(conn, requestData)
default:
fmt.Printf("[Dispatcher] Unknown Request %d\n", requestNumber)
return nil, 0
}
}
// Helper to encode varint for handlers that need it locally
func encodeVarint(v uint64) []byte {
return protocol.EncodeVarint(v)
}

79
internal/handlers/game.go Normal file
View File

@@ -0,0 +1,79 @@
package handlers
import (
"customServer/internal/protocol"
"customServer/internal/state"
"fmt"
"net"
)
func HandleWhatsNewPussycatRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling WhatsNewPussycatRequest")
mgr := state.GlobalManager
if mgr.HasActiveGame() {
activeGameID := mgr.GetActiveGameID()
fmt.Printf("[TCP] StatusReport: Reporting Active Game %d\n", activeGameID)
// StatusReport Message (Inner)
innerReport := make([]byte, 0)
// Field 1: GameId (int64)
innerReport = append(innerReport, 0x08)
innerReport = append(innerReport, protocol.EncodeVarint(uint64(activeGameID))...)
// Field 2: Status (Enum) = 1 (IN_PROGRESS)
innerReport = append(innerReport, 0x10)
innerReport = append(innerReport, protocol.EncodeVarint(uint64(protocol.GameStatus_IN_PROGRESS))...)
// Field 3: Data (Bytes) - Dynamic IronGameState
dataBytes := state.GetMockIronGameStateBytes()
innerReport = append(innerReport, 0x1a)
innerReport = append(innerReport, protocol.EncodeVarint(uint64(len(dataBytes)))...)
innerReport = append(innerReport, dataBytes...)
// Field 4: TurnId (int32) = 4
innerReport = append(innerReport, 0x20)
innerReport = append(innerReport, protocol.EncodeVarint(4)...)
// Field 5: NextPlayerIds (Repeated Int32)
nextPlayers := []uint64{uint64(state.MockUserID)}
npBytes := make([]byte, 0)
for _, pid := range nextPlayers {
npBytes = append(npBytes, protocol.EncodeVarint(pid)...)
}
innerReport = append(innerReport, 0x2a)
innerReport = append(innerReport, protocol.EncodeVarint(uint64(len(npBytes)))...)
innerReport = append(innerReport, npBytes...)
// Field 6: Players (Repeated Player)
mockPlayer := state.GetMockPlayerBytes()
innerReport = append(innerReport, 0x32)
innerReport = append(innerReport, protocol.EncodeVarint(uint64(len(mockPlayer)))...)
innerReport = append(innerReport, mockPlayer...)
// Field 14: Configuration (GameConfiguration)
fallbackConfig := getFallbackConfigBytes()
innerReport = append(innerReport, 0x72)
innerReport = append(innerReport, protocol.EncodeVarint(uint64(len(fallbackConfig)))...)
innerReport = append(innerReport, fallbackConfig...)
// Field 16: ActivePlayer (Int32)
innerReport = append(innerReport, 0x80, 0x01)
innerReport = append(innerReport, protocol.EncodeVarint(uint64(state.MockUserID))...)
// Wrap in GameStatusReportRequest (Field 1: repeated StatusReport)
responsePayload := make([]byte, 0)
responsePayload = append(responsePayload, 0x0a)
responsePayload = append(responsePayload, protocol.EncodeVarint(uint64(len(innerReport)))...)
responsePayload = append(responsePayload, innerReport...)
return responsePayload, 512
} else {
fmt.Println("[TCP] StatusReport: Reporting Idle/Lobby State (Dynamic)")
// Idle Status (Empty reports list)
responsePayload := make([]byte, 0)
return responsePayload, 512
}
}

208
internal/handlers/lobby.go Normal file
View File

@@ -0,0 +1,208 @@
package handlers
import (
"bytes"
"customServer/internal/protocol"
"customServer/internal/state"
"fmt"
"net"
)
func HandleEnterLobbyRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling EnterLobbyRequest")
// Pre-push the game list so it's there when the client transitions
gameList := getMockGameListBytes()
conn.Write(protocol.WrapPacket(609, gameList, 999))
return []byte{}, 601
}
func HandleLobbyPlayerListRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling LobbyPlayerListRequest")
mockSmallPlayer := state.GetMockSmallPlayerBytes()
// PlayerList Message
playerList := make([]byte, 0)
playerList = append(playerList, 0x0a)
playerList = append(playerList, protocol.EncodeVarint(uint64(len(mockSmallPlayer)))...)
playerList = append(playerList, mockSmallPlayer...)
// Deflate
compressed := protocol.ZlibCompress(playerList)
responsePayload := make([]byte, 0)
responsePayload = append(responsePayload, 0x0a) // Field 1: playerList (ByteString)
responsePayload = append(responsePayload, protocol.EncodeVarint(uint64(len(compressed)))...)
responsePayload = append(responsePayload, compressed...)
return responsePayload, 604
}
func HandleLobbyGameListRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling LobbyGameListRequest")
return getMockGameListBytes(), 609
}
func HandleObservableGameListRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling ObservableGameListRequest")
return getMockGameListBytes(), 622
}
func HandleLobbyCreateGameRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling LobbyCreateGameRequest")
// 1. Scan the request data for Field 607 (LobbyCreateGameRequest)
// Actually, the dispatcher already gives us the requestData which IS the payload of the request number?
// In the original main.go, the Switch handled requestNumber.
// For 607, it manually scanned payloadBytes again.
var configBytes []byte
msgReader := bytes.NewReader(requestData)
for {
tag, err := protocol.ReadVarint(msgReader)
if err != nil {
break
}
fieldNum := tag >> 3
wireType := tag & 0x7
if fieldNum == 607 && wireType == 2 {
length, _ := protocol.ReadVarint(msgReader)
createGameReqBytes := make([]byte, length)
msgReader.Read(createGameReqBytes)
// Extract Configuration (Field 1) from LobbyCreateGameRequest
reqReader := bytes.NewReader(createGameReqBytes)
for {
rtag, rerr := protocol.ReadVarint(reqReader)
if rerr != nil {
break
}
rfieldNum := rtag >> 3
rwireType := rtag & 0x7
if rfieldNum == 1 && rwireType == 2 {
rlength, _ := protocol.ReadVarint(reqReader)
configBytes = make([]byte, rlength)
reqReader.Read(configBytes)
break
} else {
protocol.SkipField(reqReader, rwireType)
}
}
break
} else {
protocol.SkipField(msgReader, wireType)
}
}
// Update State
newGameID := int64(4016461897007108096)
state.GlobalManager.CreateGame(newGameID)
// Construct Response (LobbyGameCreatedRequest 608)
gameDetails := getMockGameDetailsBytes(newGameID, configBytes, [][]byte{state.GetMockPlayerBytes()})
// Push updated game list
gameList := getMockGameListBytes()
conn.Write(protocol.WrapPacket(609, gameList, 1000))
responsePayload := make([]byte, 0)
responsePayload = append(responsePayload, 0x0a)
responsePayload = append(responsePayload, protocol.EncodeVarint(uint64(len(gameDetails)))...)
responsePayload = append(responsePayload, gameDetails...)
return responsePayload, 608
}
func HandleLobbyJoinGameRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling LobbyJoinGameRequest")
state.GlobalManager.CreateGame(4016461897007108096)
// Construct LobbyNewPlayerRequest (611)
gameDetails := getMockGameDetailsBytes(4016461897007108096, nil, [][]byte{state.GetMockPlayerBytes()})
// LobbyNewPlayerRequest Message
newPlayerReq := make([]byte, 0)
// Field 1: GameDetails
newPlayerReq = append(newPlayerReq, 0x0a)
newPlayerReq = append(newPlayerReq, protocol.EncodeVarint(uint64(len(gameDetails)))...)
newPlayerReq = append(newPlayerReq, gameDetails...)
// Field 2: JoiningPlayer
newPlayerReq = append(newPlayerReq, 0x10)
newPlayerReq = append(newPlayerReq, protocol.EncodeVarint(uint64(state.MockUserID))...)
return newPlayerReq, 611
}
// Helpers
func getMockGameDetailsBytes(gameID int64, configBytes []byte, players [][]byte) []byte {
gameDetails := make([]byte, 0)
gameDetails = append(gameDetails, 0x08)
gameDetails = append(gameDetails, protocol.EncodeVarint(uint64(gameID))...)
for _, p := range players {
gameDetails = append(gameDetails, 0x12)
gameDetails = append(gameDetails, protocol.EncodeVarint(uint64(len(p)))...)
gameDetails = append(gameDetails, p...)
}
if len(configBytes) > 0 {
// Field 3: Configuration (From request)
gameDetails = append(gameDetails, 0x1a)
gameDetails = append(gameDetails, protocol.EncodeVarint(uint64(len(configBytes)))...)
gameDetails = append(gameDetails, configBytes...)
} else {
// Field 3: Configuration (Fallback)
fallbackConfig := getFallbackConfigBytes()
gameDetails = append(gameDetails, 0x1a)
gameDetails = append(gameDetails, protocol.EncodeVarint(uint64(len(fallbackConfig)))...)
gameDetails = append(gameDetails, fallbackConfig...)
}
return gameDetails
}
func getMockGameListBytes() []byte {
// For the lobby list, show a game with NO players so anyone can join
gameData := getMockGameDetailsBytes(4016461897007108096, nil, [][]byte{})
// GameList Message
gameList := make([]byte, 0)
gameList = append(gameList, 0x0a)
gameList = append(gameList, protocol.EncodeVarint(uint64(len(gameData)))...)
gameList = append(gameList, gameData...)
// Deflate
compressed := protocol.ZlibCompress(gameList)
responsePayload := make([]byte, 0)
responsePayload = append(responsePayload, 0x0a) // Field 1: gameList (ByteString)
responsePayload = append(responsePayload, protocol.EncodeVarint(uint64(len(compressed)))...)
responsePayload = append(responsePayload, compressed...)
return responsePayload
}
func getFallbackConfigBytes() []byte {
fallbackConfig := make([]byte, 0)
name := "Fallback Game"
fallbackConfig = append(fallbackConfig, 0x0a)
fallbackConfig = append(fallbackConfig, protocol.EncodeVarint(uint64(len(name)))...)
fallbackConfig = append(fallbackConfig, name...)
fallbackConfig = append(fallbackConfig, 0x10, 0x00, 0x18, 0x01, 0x20, 0x00)
fallbackConfig = append(fallbackConfig, 0x28)
fallbackConfig = append(fallbackConfig, protocol.EncodeVarint(1)...)
fallbackConfig = append(fallbackConfig, 0x30)
fallbackConfig = append(fallbackConfig, protocol.EncodeVarint(6)...)
fallbackConfig = append(fallbackConfig, 0x48, 0x00, 0x50, 0x2d)
// Field 11: Data (IronGameConfiguration)
ironData := state.GetMockIronGameConfigurationBytes()
fallbackConfig = append(fallbackConfig, 0x5a) // (11 << 3) | 2 = 88 | 2 = 90 = 0x5a
fallbackConfig = append(fallbackConfig, protocol.EncodeVarint(uint64(len(ironData)))...)
fallbackConfig = append(fallbackConfig, ironData...)
return fallbackConfig
}

View File

@@ -0,0 +1,20 @@
package handlers
import (
"fmt"
"net"
)
func HandleAsyncBuddyListRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling AsyncBuddyListRequest")
// Return AsyncBuddyListContentRequest (516)
// Field 2: Buddies (AsyncBuddyList)
return []byte{0x12, 0x00}, 516
}
func HandleAsyncIgnoreListRequest(conn net.Conn, requestData []byte) ([]byte, int) {
fmt.Println("[TCP] Handling AsyncIgnoreListRequest")
// Return AsyncIgnoreListContentRequest (516)
// Field 2: Ignores (AsyncBuddyList)
return []byte{0x12, 0x00}, 561
}