refactor/less-complexity #5

Manually merged
bdnugget merged 7 commits from refactor/less-complexity into master 2025-04-16 11:10:01 +00:00
6 changed files with 80 additions and 33 deletions
Showing only changes of commit 555b8118f2 - Show all commits

View File

@ -2,20 +2,11 @@ package main
import "time" import "time"
// Game world constants
const ( const (
MapWidth = 50 // Server-related constants
MapHeight = 50 ServerTickRate = 600 * time.Millisecond // RuneScape-style tick rate
TileSize = 32 ClientTickRate = 50 * time.Millisecond // Client runs at higher rate for smooth rendering
TileHeight = 2.0 MaxTickDesync = 5 // Max ticks behind before forcing resync
TickRate = 600 * time.Millisecond // Server tick rate (600ms) DefaultPort = "6969" // Default server port
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
) )

View File

@ -2,6 +2,7 @@ package game
import ( import (
"fmt" "fmt"
"log"
"sync" "sync"
"time" "time"
@ -54,6 +55,12 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
c.mutex.Lock() c.mutex.Lock()
defer c.mutex.Unlock() defer c.mutex.Unlock()
if len(messages) == 0 {
return
}
log.Printf("Processing %d chat messages", len(messages))
// Convert protobuf messages to our local type // Convert protobuf messages to our local type
for _, msg := range messages { for _, msg := range messages {
localMsg := types.ChatMessage{ localMsg := types.ChatMessage{
@ -69,6 +76,7 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
c.messages = c.messages[1:] c.messages = c.messages[1:]
} }
c.messages = append(c.messages, localMsg) 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 // Scroll to latest message if it's not already visible
visibleMessages := int((chatHeight - inputHeight) / messageHeight) visibleMessages := int((chatHeight - inputHeight) / messageHeight)
@ -92,6 +100,9 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
ExpireTime: time.Now().Add(6 * time.Second), ExpireTime: time.Now().Add(6 * time.Second),
} }
otherPlayer.Unlock() 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)
} }
} }
} }

View File

@ -24,6 +24,7 @@ type Game struct {
loginScreen *LoginScreen loginScreen *LoginScreen
isLoggedIn bool isLoggedIn bool
cleanupOnce sync.Once cleanupOnce sync.Once
frameCounter int // For periodic logging
} }
func New() *Game { func New() *Game {
@ -129,7 +130,28 @@ func (g *Game) Update(deltaTime float32) {
g.Player.MoveTowards(g.Player.TargetPath[0], deltaTime, GetMapGrid()) 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 { 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 { if len(other.TargetPath) > 0 {
other.MoveTowards(other.TargetPath[0], deltaTime, GetMapGrid()) 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) { func (g *Game) DrawPlayer(player *types.Player, model rl.Model) {
player.Lock() // No need for lock in rendering, we'll use a "take snapshot" approach
defer player.Unlock() // This avoids potential deadlocks and makes the rendering more consistent
// Check for invalid model // Check for invalid model
if model.Meshes == nil || model.Meshes.VertexCount <= 0 { 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 { if player.IsMoving && len(modelAsset.Animations.Walk) > 0 {
anim := modelAsset.Animations.Walk[0] // Use first walk animation anim := modelAsset.Animations.Walk[0] // Use first walk animation
if anim.FrameCount > 0 { if anim.FrameCount > 0 {
player.AnimationFrame = player.AnimationFrame % anim.FrameCount currentFrame := player.AnimationFrame % anim.FrameCount
rl.UpdateModelAnimation(model, anim, player.AnimationFrame) rl.UpdateModelAnimation(model, anim, currentFrame)
} }
} else if len(modelAsset.Animations.Idle) > 0 { } else if len(modelAsset.Animations.Idle) > 0 {
anim := modelAsset.Animations.Idle[0] // Use first idle animation anim := modelAsset.Animations.Idle[0] // Use first idle animation
if anim.FrameCount > 0 { if anim.FrameCount > 0 {
player.AnimationFrame = player.AnimationFrame % anim.FrameCount currentFrame := player.AnimationFrame % anim.FrameCount
rl.UpdateModelAnimation(model, anim, player.AnimationFrame) rl.UpdateModelAnimation(model, anim, currentFrame)
} }
} }
} }
@ -445,12 +467,15 @@ func (g *Game) AssignModelToPlayer(player *types.Player) {
return 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) { if modelIndex < 0 || modelIndex >= len(g.Models) {
// Prevent out of bounds access // Prevent out of bounds access
modelIndex = 0 modelIndex = 0
} }
rl.TraceLog(rl.LogInfo, "Assigning model %d to player %d", modelIndex, player.ID)
modelAsset := g.Models[modelIndex] modelAsset := g.Models[modelIndex]
// Validate model before assigning // Validate model before assigning

View File

@ -51,7 +51,7 @@ func main() {
} }
// Initialize window with error handling // 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.InitWindow(1024, 768, "GoonScape")
rl.SetExitKey(0) rl.SetExitKey(0)

View File

@ -18,10 +18,11 @@ import (
const protoVersion = 1 const protoVersion = 1
var serverAddr = "boner.be:6969" var serverAddr = "boner.be:6969" // Default server address
func SetServerAddr(addr string) { func SetServerAddr(addr string) {
serverAddr = addr serverAddr = addr
log.Printf("Server address set to: %s", serverAddr)
} }
func ConnectToServer(username, password string, isRegistering bool) (net.Conn, int32, error) { 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) lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(reader, lengthBuf); err != nil { if _, err := io.ReadFull(reader, lengthBuf); err != nil {
if err != io.EOF { if err != io.EOF {
log.Printf("Network read error: %v", err)
errChan <- fmt.Errorf("failed to read message length: %v", err) errChan <- fmt.Errorf("failed to read message length: %v", err)
} else {
log.Printf("Connection closed by server")
} }
return return
} }
messageLength := binary.BigEndian.Uint32(lengthBuf) 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) messageBuf := make([]byte, messageLength)
if _, err := io.ReadFull(reader, messageBuf); err != nil { if _, err := io.ReadFull(reader, messageBuf); err != nil {
log.Printf("Failed to read message body: %v", err) log.Printf("Failed to read message body: %v", err)
errChan <- fmt.Errorf("failed to read message body: %v", err)
return return
} }
var serverMessage pb.ServerMessage var serverMessage pb.ServerMessage
if err := proto.Unmarshal(messageBuf, &serverMessage); err != nil { if err := proto.Unmarshal(messageBuf, &serverMessage); err != nil {
log.Printf("Failed to unmarshal server message: %v", err) log.Printf("Failed to unmarshal server message: %v", err)
continue continue // Skip this message but don't quit
} }
player.Lock() player.Lock()
@ -207,7 +219,11 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
} }
player.Unlock() player.Unlock()
// Process player states
validPlayerIds := make(map[int32]bool)
for _, state := range serverMessage.Players { for _, state := range serverMessage.Players {
validPlayerIds[state.PlayerId] = true
if state.PlayerId == playerID { if state.PlayerId == playerID {
player.Lock() player.Lock()
// Update initial position if not set // Update initial position if not set
@ -223,21 +239,26 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
continue continue
} }
// Update or create other players
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists { if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
otherPlayer.UpdatePosition(state, types.ServerTickRate) otherPlayer.UpdatePosition(state, types.ServerTickRate)
} else { } else {
log.Printf("Creating new player with ID: %d", state.PlayerId)
otherPlayers[state.PlayerId] = types.NewPlayer(state) 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 { for id := range otherPlayers {
if id != playerID { if id != playerID && !validPlayerIds[id] {
log.Printf("Removing player with ID: %d", id)
delete(otherPlayers, id) delete(otherPlayers, id)
} }
} }
// Handle chat messages
if handler, ok := player.UserData.(types.ChatMessageHandler); ok && len(serverMessage.ChatMessages) > 0 { 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) handler.HandleServerMessages(serverMessage.ChatMessages)
} }
} }

View File

@ -9,7 +9,7 @@ import (
) )
type Player struct { type Player struct {
sync.RWMutex sync.RWMutex // Keep this for network operations
Model rl.Model Model rl.Model
Texture rl.Texture2D Texture rl.Texture2D
PosActual rl.Vector3 PosActual rl.Vector3
@ -31,9 +31,7 @@ type Player struct {
} }
func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) { func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) {
p.Lock() // No need for lock here as this is called from a single thread (game loop)
defer p.Unlock()
targetPos := rl.Vector3{ targetPos := rl.Vector3{
X: float32(target.X * TileSize), X: float32(target.X * TileSize),
Y: mapGrid[target.X][target.Y].Height * TileHeight, 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 oldFrame := p.AnimationFrame
p.AnimationFrame += int32(deltaTime * 60) 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) oldFrame, p.AnimationFrame, deltaTime)
} else { } else {
wasMoving := p.IsMoving wasMoving := p.IsMoving
@ -65,7 +63,7 @@ func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) {
oldFrame := p.AnimationFrame oldFrame := p.AnimationFrame
p.AnimationFrame += int32(deltaTime * 60) 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) 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) { func (p *Player) ForceResync(state *pb.PlayerState) {
// Keep this lock since it's called from the network goroutine
p.Lock() p.Lock()
defer p.Unlock() defer p.Unlock()