diff --git a/constants.go b/constants.go index dcab734..8090263 100644 --- a/constants.go +++ b/constants.go @@ -2,20 +2,11 @@ package main import "time" +// Game world constants const ( - MapWidth = 50 - MapHeight = 50 - TileSize = 32 - TileHeight = 2.0 - TickRate = 600 * time.Millisecond // Server tick rate (600ms) - serverAddr = "localhost:6969" - - // RuneScape-style tick rate (600ms) - ServerTickRate = 600 * time.Millisecond - - // Client might run at a higher tick rate for smooth rendering - ClientTickRate = 50 * time.Millisecond - - // Maximum number of ticks we can get behind before forcing a resync - MaxTickDesync = 5 + // Server-related constants + ServerTickRate = 600 * time.Millisecond // RuneScape-style tick rate + ClientTickRate = 50 * time.Millisecond // Client runs at higher rate for smooth rendering + MaxTickDesync = 5 // Max ticks behind before forcing resync + DefaultPort = "6969" // Default server port ) diff --git a/game/chat.go b/game/chat.go index f6b3ce9..ed4b327 100644 --- a/game/chat.go +++ b/game/chat.go @@ -2,6 +2,7 @@ package game import ( "fmt" + "log" "sync" "time" @@ -54,6 +55,12 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) { c.mutex.Lock() defer c.mutex.Unlock() + if len(messages) == 0 { + return + } + + log.Printf("Processing %d chat messages", len(messages)) + // Convert protobuf messages to our local type for _, msg := range messages { localMsg := types.ChatMessage{ @@ -69,6 +76,7 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) { c.messages = c.messages[1:] } c.messages = append(c.messages, localMsg) + log.Printf("Added chat message from %s: %s", msg.Username, msg.Content) // Scroll to latest message if it's not already visible visibleMessages := int((chatHeight - inputHeight) / messageHeight) @@ -92,6 +100,9 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) { ExpireTime: time.Now().Add(6 * time.Second), } otherPlayer.Unlock() + log.Printf("Added floating message to other player %d", msg.PlayerId) + } else { + log.Printf("Could not find other player %d to add floating message", msg.PlayerId) } } } diff --git a/game/game.go b/game/game.go index acf6154..4665f73 100644 --- a/game/game.go +++ b/game/game.go @@ -24,6 +24,7 @@ type Game struct { loginScreen *LoginScreen isLoggedIn bool cleanupOnce sync.Once + frameCounter int // For periodic logging } func New() *Game { @@ -129,7 +130,28 @@ func (g *Game) Update(deltaTime float32) { g.Player.MoveTowards(g.Player.TargetPath[0], deltaTime, GetMapGrid()) } + // Periodically log information about other players + g.frameCounter++ + if g.frameCounter%300 == 0 { + rl.TraceLog(rl.LogInfo, "There are %d other players", len(g.OtherPlayers)) + for id, other := range g.OtherPlayers { + rl.TraceLog(rl.LogInfo, "Other player ID: %d, Position: (%f, %f, %f), Has model: %v", + id, other.PosActual.X, other.PosActual.Y, other.PosActual.Z, other.Model.Meshes != nil) + } + } + + // Process other players for _, other := range g.OtherPlayers { + if other == nil { + continue + } + + // Make sure other players have models assigned + if other.Model.Meshes == nil { + g.AssignModelToPlayer(other) + } + + // Update other player movement if len(other.TargetPath) > 0 { other.MoveTowards(other.TargetPath[0], deltaTime, GetMapGrid()) } @@ -167,8 +189,8 @@ func (g *Game) DrawMap() { } func (g *Game) DrawPlayer(player *types.Player, model rl.Model) { - player.Lock() - defer player.Unlock() + // No need for lock in rendering, we'll use a "take snapshot" approach + // This avoids potential deadlocks and makes the rendering more consistent // Check for invalid model if model.Meshes == nil || model.Meshes.VertexCount <= 0 { @@ -196,14 +218,14 @@ func (g *Game) DrawPlayer(player *types.Player, model rl.Model) { if player.IsMoving && len(modelAsset.Animations.Walk) > 0 { anim := modelAsset.Animations.Walk[0] // Use first walk animation if anim.FrameCount > 0 { - player.AnimationFrame = player.AnimationFrame % anim.FrameCount - rl.UpdateModelAnimation(model, anim, player.AnimationFrame) + currentFrame := player.AnimationFrame % anim.FrameCount + rl.UpdateModelAnimation(model, anim, currentFrame) } } else if len(modelAsset.Animations.Idle) > 0 { anim := modelAsset.Animations.Idle[0] // Use first idle animation if anim.FrameCount > 0 { - player.AnimationFrame = player.AnimationFrame % anim.FrameCount - rl.UpdateModelAnimation(model, anim, player.AnimationFrame) + currentFrame := player.AnimationFrame % anim.FrameCount + rl.UpdateModelAnimation(model, anim, currentFrame) } } } @@ -445,12 +467,15 @@ func (g *Game) AssignModelToPlayer(player *types.Player) { return } - modelIndex := int(player.ID) % len(g.Models) + // Make sure model index is positive for consistent player appearances + // Use abs value of ID to ensure consistent appearance for negative IDs + modelIndex := abs(int(player.ID)) % len(g.Models) if modelIndex < 0 || modelIndex >= len(g.Models) { // Prevent out of bounds access modelIndex = 0 } + rl.TraceLog(rl.LogInfo, "Assigning model %d to player %d", modelIndex, player.ID) modelAsset := g.Models[modelIndex] // Validate model before assigning diff --git a/main.go b/main.go index 3ec1a57..117a023 100644 --- a/main.go +++ b/main.go @@ -51,7 +51,7 @@ func main() { } // Initialize window with error handling - rl.SetConfigFlags(rl.FlagMsaa4xHint) // Enable MSAA for smoother rendering + rl.SetConfigFlags(rl.FlagMsaa4xHint | rl.FlagWindowResizable) // Enable MSAA and make window resizable rl.InitWindow(1024, 768, "GoonScape") rl.SetExitKey(0) diff --git a/network/network.go b/network/network.go index cf749dd..587b5d4 100644 --- a/network/network.go +++ b/network/network.go @@ -18,10 +18,11 @@ import ( const protoVersion = 1 -var serverAddr = "boner.be:6969" +var serverAddr = "boner.be:6969" // Default server address func SetServerAddr(addr string) { serverAddr = addr + log.Printf("Server address set to: %s", serverAddr) } func ConnectToServer(username, password string, isRegistering bool) (net.Conn, int32, error) { @@ -175,22 +176,33 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play lengthBuf := make([]byte, 4) if _, err := io.ReadFull(reader, lengthBuf); err != nil { if err != io.EOF { + log.Printf("Network read error: %v", err) errChan <- fmt.Errorf("failed to read message length: %v", err) + } else { + log.Printf("Connection closed by server") } return } messageLength := binary.BigEndian.Uint32(lengthBuf) + // Sanity check message size to prevent potential memory issues + if messageLength > 1024*1024 { // 1MB max message size + log.Printf("Message size too large: %d bytes", messageLength) + errChan <- fmt.Errorf("message size too large: %d bytes", messageLength) + return + } + messageBuf := make([]byte, messageLength) if _, err := io.ReadFull(reader, messageBuf); err != nil { log.Printf("Failed to read message body: %v", err) + errChan <- fmt.Errorf("failed to read message body: %v", err) return } var serverMessage pb.ServerMessage if err := proto.Unmarshal(messageBuf, &serverMessage); err != nil { log.Printf("Failed to unmarshal server message: %v", err) - continue + continue // Skip this message but don't quit } player.Lock() @@ -207,7 +219,11 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play } player.Unlock() + // Process player states + validPlayerIds := make(map[int32]bool) for _, state := range serverMessage.Players { + validPlayerIds[state.PlayerId] = true + if state.PlayerId == playerID { player.Lock() // Update initial position if not set @@ -223,21 +239,26 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play continue } + // Update or create other players if otherPlayer, exists := otherPlayers[state.PlayerId]; exists { otherPlayer.UpdatePosition(state, types.ServerTickRate) } else { + log.Printf("Creating new player with ID: %d", state.PlayerId) otherPlayers[state.PlayerId] = types.NewPlayer(state) } } - // Remove players that are no longer in the server state + // Remove players no longer in the server state for id := range otherPlayers { - if id != playerID { + if id != playerID && !validPlayerIds[id] { + log.Printf("Removing player with ID: %d", id) delete(otherPlayers, id) } } + // Handle chat messages if handler, ok := player.UserData.(types.ChatMessageHandler); ok && len(serverMessage.ChatMessages) > 0 { + log.Printf("Received %d chat messages from server", len(serverMessage.ChatMessages)) handler.HandleServerMessages(serverMessage.ChatMessages) } } diff --git a/types/player.go b/types/player.go index 1ab5a94..9ad4ef6 100644 --- a/types/player.go +++ b/types/player.go @@ -9,7 +9,7 @@ import ( ) type Player struct { - sync.RWMutex + sync.RWMutex // Keep this for network operations Model rl.Model Texture rl.Texture2D PosActual rl.Vector3 @@ -31,9 +31,7 @@ type Player struct { } func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) { - p.Lock() - defer p.Unlock() - + // No need for lock here as this is called from a single thread (game loop) targetPos := rl.Vector3{ X: float32(target.X * TileSize), Y: mapGrid[target.X][target.Y].Height * TileHeight, @@ -53,7 +51,7 @@ func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) { oldFrame := p.AnimationFrame p.AnimationFrame += int32(deltaTime * 60) - rl.TraceLog(rl.LogInfo, "Walk frame update: %d -> %d (delta: %f)", + rl.TraceLog(rl.LogDebug, "Walk frame update: %d -> %d (delta: %f)", oldFrame, p.AnimationFrame, deltaTime) } else { wasMoving := p.IsMoving @@ -65,7 +63,7 @@ func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) { oldFrame := p.AnimationFrame p.AnimationFrame += int32(deltaTime * 60) - rl.TraceLog(rl.LogInfo, "Idle frame update: %d -> %d (delta: %f)", + rl.TraceLog(rl.LogDebug, "Idle frame update: %d -> %d (delta: %f)", oldFrame, p.AnimationFrame, deltaTime) } @@ -116,6 +114,7 @@ func (p *Player) UpdatePosition(state *pb.PlayerState, tickRate time.Duration) { } func (p *Player) ForceResync(state *pb.PlayerState) { + // Keep this lock since it's called from the network goroutine p.Lock() defer p.Unlock()