Prevent chat and other player rendering race conditions when logging in at the same time
This commit is contained in:
parent
62a6bb2926
commit
541f53c06a
51
game/chat.go
51
game/chat.go
@ -59,6 +59,12 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
|
|||||||
|
|
||||||
// Convert protobuf messages to our local type
|
// Convert protobuf messages to our local type
|
||||||
for _, msg := range messages {
|
for _, msg := range messages {
|
||||||
|
// Skip invalid messages
|
||||||
|
if msg == nil {
|
||||||
|
log.Printf("Warning: Received nil chat message")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
localMsg := types.ChatMessage{
|
localMsg := types.ChatMessage{
|
||||||
PlayerID: msg.PlayerId,
|
PlayerID: msg.PlayerId,
|
||||||
Username: msg.Username,
|
Username: msg.Username,
|
||||||
@ -81,24 +87,41 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add floating message to the player
|
// Add floating message to the player
|
||||||
if game, ok := c.userData.(*Game); ok {
|
if game, ok := c.userData.(*Game); ok && game != nil {
|
||||||
if msg.PlayerId == game.Player.ID {
|
// Make sure each game component exists before using it
|
||||||
game.Player.Lock()
|
if game.PlayerManager == nil {
|
||||||
game.Player.FloatingMessage = &types.FloatingMessage{
|
log.Printf("Warning: PlayerManager is nil when processing chat message")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if msg.PlayerId == game.PlayerManager.LocalPlayer.ID {
|
||||||
|
// Check if local player exists
|
||||||
|
if game.PlayerManager.LocalPlayer == nil {
|
||||||
|
log.Printf("Warning: Local player is nil when trying to add floating message")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
game.PlayerManager.LocalPlayer.Lock()
|
||||||
|
game.PlayerManager.LocalPlayer.FloatingMessage = &types.FloatingMessage{
|
||||||
Content: msg.Content,
|
Content: msg.Content,
|
||||||
ExpireTime: time.Now().Add(6 * time.Second),
|
ExpireTime: time.Now().Add(6 * time.Second),
|
||||||
}
|
}
|
||||||
game.Player.Unlock()
|
game.PlayerManager.LocalPlayer.Unlock()
|
||||||
} else if otherPlayer, exists := game.OtherPlayers[msg.PlayerId]; exists {
|
|
||||||
otherPlayer.Lock()
|
|
||||||
otherPlayer.FloatingMessage = &types.FloatingMessage{
|
|
||||||
Content: msg.Content,
|
|
||||||
ExpireTime: time.Now().Add(6 * time.Second),
|
|
||||||
}
|
|
||||||
otherPlayer.Unlock()
|
|
||||||
log.Printf("Added floating message to other player %d", msg.PlayerId)
|
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Could not find other player %d to add floating message", msg.PlayerId)
|
// The other player might not be in our list yet, handle safely
|
||||||
|
player := game.PlayerManager.GetPlayer(msg.PlayerId)
|
||||||
|
if player == nil {
|
||||||
|
log.Printf("Could not find other player %d to add floating message (player not in game yet)", msg.PlayerId)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
player.Lock()
|
||||||
|
player.FloatingMessage = &types.FloatingMessage{
|
||||||
|
Content: msg.Content,
|
||||||
|
ExpireTime: time.Now().Add(6 * time.Second),
|
||||||
|
}
|
||||||
|
player.Unlock()
|
||||||
|
log.Printf("Added floating message to other player %d", msg.PlayerId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
57
game/game.go
57
game/game.go
@ -53,6 +53,8 @@ func New() *Game {
|
|||||||
Projection: rl.CameraPerspective,
|
Projection: rl.CameraPerspective,
|
||||||
},
|
},
|
||||||
quitChan: make(chan struct{}),
|
quitChan: make(chan struct{}),
|
||||||
|
// Initialize empty maps to avoid nil references
|
||||||
|
OtherPlayers: make(map[int32]*types.Player),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize legacy fields (for backward compatibility)
|
// Initialize legacy fields (for backward compatibility)
|
||||||
@ -117,20 +119,32 @@ func (g *Game) Update(deltaTime float32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
g.PlayerManager.LocalPlayer = &types.Player{
|
g.PlayerManager.LocalPlayer = &types.Player{
|
||||||
Speed: 50.0,
|
Speed: 50.0,
|
||||||
TargetPath: []types.Tile{},
|
TargetPath: []types.Tile{},
|
||||||
UserData: g,
|
ActionQueue: []*pb.Action{},
|
||||||
QuitDone: make(chan struct{}),
|
QuitDone: make(chan struct{}),
|
||||||
ID: playerID,
|
ID: playerID,
|
||||||
}
|
}
|
||||||
g.AssignModelToPlayer(g.PlayerManager.LocalPlayer)
|
g.AssignModelToPlayer(g.PlayerManager.LocalPlayer)
|
||||||
|
|
||||||
|
// Update the legacy Player field
|
||||||
|
g.Player = g.PlayerManager.LocalPlayer
|
||||||
|
|
||||||
|
// Set user data to allow chat message handling
|
||||||
|
g.PlayerManager.LocalPlayer.UserData = g
|
||||||
|
|
||||||
go network.HandleServerCommunication(conn, playerID, g.PlayerManager.LocalPlayer, g.PlayerManager.OtherPlayers, g.quitChan)
|
go network.HandleServerCommunication(conn, playerID, g.PlayerManager.LocalPlayer, g.PlayerManager.OtherPlayers, g.quitChan)
|
||||||
g.UIManager.IsLoggedIn = true
|
g.UIManager.IsLoggedIn = true
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip update logic if player is not initialized yet
|
||||||
|
if g.PlayerManager.LocalPlayer == nil {
|
||||||
|
log.Printf("Warning: LocalPlayer is nil during update, skipping")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Handle ESC for menu
|
// Handle ESC for menu
|
||||||
if rl.IsKeyPressed(rl.KeyEscape) {
|
if rl.IsKeyPressed(rl.KeyEscape) {
|
||||||
g.UIManager.MenuOpen = !g.UIManager.MenuOpen
|
g.UIManager.MenuOpen = !g.UIManager.MenuOpen
|
||||||
@ -142,6 +156,7 @@ func (g *Game) Update(deltaTime float32) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle chat updates
|
||||||
if message, sent := g.UIManager.Chat.Update(); sent {
|
if message, sent := g.UIManager.Chat.Update(); sent {
|
||||||
g.PlayerManager.LocalPlayer.Lock()
|
g.PlayerManager.LocalPlayer.Lock()
|
||||||
g.PlayerManager.LocalPlayer.ActionQueue = append(g.PlayerManager.LocalPlayer.ActionQueue, &pb.Action{
|
g.PlayerManager.LocalPlayer.ActionQueue = append(g.PlayerManager.LocalPlayer.ActionQueue, &pb.Action{
|
||||||
@ -152,9 +167,11 @@ func (g *Game) Update(deltaTime float32) {
|
|||||||
g.PlayerManager.LocalPlayer.Unlock()
|
g.PlayerManager.LocalPlayer.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process player input
|
||||||
g.HandleInput()
|
g.HandleInput()
|
||||||
|
|
||||||
if len(g.PlayerManager.LocalPlayer.TargetPath) > 0 {
|
// Update local player movement
|
||||||
|
if g.PlayerManager.LocalPlayer.TargetPath != nil && len(g.PlayerManager.LocalPlayer.TargetPath) > 0 {
|
||||||
g.PlayerManager.LocalPlayer.MoveTowards(g.PlayerManager.LocalPlayer.TargetPath[0], deltaTime, GetMapGrid())
|
g.PlayerManager.LocalPlayer.MoveTowards(g.PlayerManager.LocalPlayer.TargetPath[0], deltaTime, GetMapGrid())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,8 +180,12 @@ func (g *Game) Update(deltaTime float32) {
|
|||||||
if g.frameCounter%300 == 0 {
|
if g.frameCounter%300 == 0 {
|
||||||
rl.TraceLog(rl.LogInfo, "There are %d other players", len(g.PlayerManager.OtherPlayers))
|
rl.TraceLog(rl.LogInfo, "There are %d other players", len(g.PlayerManager.OtherPlayers))
|
||||||
for id, other := range g.PlayerManager.OtherPlayers {
|
for id, other := range g.PlayerManager.OtherPlayers {
|
||||||
rl.TraceLog(rl.LogInfo, "Other player ID: %d, Position: (%f, %f, %f), Has model: %v",
|
if other != nil {
|
||||||
id, other.PosActual.X, other.PosActual.Y, other.PosActual.Z, other.Model.Meshes != nil)
|
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)
|
||||||
|
} else {
|
||||||
|
rl.TraceLog(rl.LogInfo, "Other player ID: %d is nil", id)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,17 +195,18 @@ func (g *Game) Update(deltaTime float32) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure other players have models assigned
|
if other.TargetPath != nil && len(other.TargetPath) > 0 {
|
||||||
|
target := other.TargetPath[0]
|
||||||
|
other.MoveTowards(target, deltaTime, GetMapGrid())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assign model if needed
|
||||||
if other.Model.Meshes == nil {
|
if other.Model.Meshes == nil {
|
||||||
g.AssignModelToPlayer(other)
|
g.AssignModelToPlayer(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update other player movement
|
|
||||||
if len(other.TargetPath) > 0 {
|
|
||||||
other.MoveTowards(other.TargetPath[0], deltaTime, GetMapGrid())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update camera position
|
||||||
UpdateCamera(&g.Camera, g.PlayerManager.LocalPlayer.PosActual, deltaTime)
|
UpdateCamera(&g.Camera, g.PlayerManager.LocalPlayer.PosActual, deltaTime)
|
||||||
|
|
||||||
// Update music if available
|
// Update music if available
|
||||||
@ -502,7 +524,12 @@ func (g *Game) Shutdown() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) HandleServerMessages(messages []*pb.ChatMessage) {
|
func (g *Game) HandleServerMessages(messages []*pb.ChatMessage) {
|
||||||
g.UIManager.Chat.HandleServerMessages(messages)
|
// Check if Chat is properly initialized
|
||||||
|
if g.UIManager != nil && g.UIManager.Chat != nil {
|
||||||
|
g.UIManager.Chat.HandleServerMessages(messages)
|
||||||
|
} else {
|
||||||
|
log.Printf("Warning: Cannot handle server messages, Chat is not initialized")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Game) AssignModelToPlayer(player *types.Player) {
|
func (g *Game) AssignModelToPlayer(player *types.Player) {
|
||||||
|
@ -91,6 +91,22 @@ func (mh *MessageHandler) WriteMessage(msg proto.Message) error {
|
|||||||
|
|
||||||
// UpdateGameState processes a server message and updates game state
|
// UpdateGameState processes a server message and updates game state
|
||||||
func UpdateGameState(serverMessage *pb.ServerMessage, player *types.Player, otherPlayers map[int32]*types.Player) {
|
func UpdateGameState(serverMessage *pb.ServerMessage, player *types.Player, otherPlayers map[int32]*types.Player) {
|
||||||
|
// Safety check for nil inputs
|
||||||
|
if serverMessage == nil {
|
||||||
|
log.Printf("Warning: Received nil server message")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if player == nil {
|
||||||
|
log.Printf("Warning: Local player is nil when updating game state")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if otherPlayers == nil {
|
||||||
|
log.Printf("Warning: otherPlayers map is nil when updating game state")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
playerID := player.ID
|
playerID := player.ID
|
||||||
|
|
||||||
player.Lock()
|
player.Lock()
|
||||||
@ -99,7 +115,7 @@ func UpdateGameState(serverMessage *pb.ServerMessage, player *types.Player, othe
|
|||||||
tickDiff := serverMessage.CurrentTick - player.CurrentTick
|
tickDiff := serverMessage.CurrentTick - player.CurrentTick
|
||||||
if tickDiff > types.MaxTickDesync {
|
if tickDiff > types.MaxTickDesync {
|
||||||
for _, state := range serverMessage.Players {
|
for _, state := range serverMessage.Players {
|
||||||
if state.PlayerId == playerID {
|
if state != nil && state.PlayerId == playerID {
|
||||||
player.ForceResync(state)
|
player.ForceResync(state)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -110,6 +126,12 @@ func UpdateGameState(serverMessage *pb.ServerMessage, player *types.Player, othe
|
|||||||
// Process player states
|
// Process player states
|
||||||
validPlayerIds := make(map[int32]bool)
|
validPlayerIds := make(map[int32]bool)
|
||||||
for _, state := range serverMessage.Players {
|
for _, state := range serverMessage.Players {
|
||||||
|
// Skip invalid player states
|
||||||
|
if state == nil {
|
||||||
|
log.Printf("Warning: Received nil player state")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
validPlayerIds[state.PlayerId] = true
|
validPlayerIds[state.PlayerId] = true
|
||||||
|
|
||||||
if state.PlayerId == playerID {
|
if state.PlayerId == playerID {
|
||||||
@ -129,7 +151,13 @@ func UpdateGameState(serverMessage *pb.ServerMessage, player *types.Player, othe
|
|||||||
|
|
||||||
// Update or create other players
|
// Update or create other players
|
||||||
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
|
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
|
||||||
otherPlayer.UpdatePosition(state, types.ServerTickRate)
|
if otherPlayer != nil {
|
||||||
|
otherPlayer.UpdatePosition(state, types.ServerTickRate)
|
||||||
|
} else {
|
||||||
|
// Replace nil player with a new one
|
||||||
|
log.Printf("Replacing nil player with ID: %d", state.PlayerId)
|
||||||
|
otherPlayers[state.PlayerId] = types.NewPlayer(state)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Creating new player with ID: %d", state.PlayerId)
|
log.Printf("Creating new player with ID: %d", state.PlayerId)
|
||||||
otherPlayers[state.PlayerId] = types.NewPlayer(state)
|
otherPlayers[state.PlayerId] = types.NewPlayer(state)
|
||||||
@ -144,14 +172,32 @@ func UpdateGameState(serverMessage *pb.ServerMessage, player *types.Player, othe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle chat messages
|
// Handle chat messages with safety checks
|
||||||
if handler, ok := player.UserData.(types.ChatMessageHandler); ok && len(serverMessage.ChatMessages) > 0 {
|
if handler, ok := player.UserData.(types.ChatMessageHandler); ok && handler != nil && len(serverMessage.ChatMessages) > 0 {
|
||||||
log.Printf("Received %d chat messages from server", len(serverMessage.ChatMessages))
|
log.Printf("Received %d chat messages from server", len(serverMessage.ChatMessages))
|
||||||
handler.HandleServerMessages(serverMessage.ChatMessages)
|
|
||||||
|
|
||||||
// Update the last seen message timestamp to the most recent message
|
// Make sure we have valid chat messages
|
||||||
if len(serverMessage.ChatMessages) > 0 {
|
validMessages := make([]*pb.ChatMessage, 0, len(serverMessage.ChatMessages))
|
||||||
lastMsg := serverMessage.ChatMessages[len(serverMessage.ChatMessages)-1]
|
for _, msg := range serverMessage.ChatMessages {
|
||||||
|
if msg != nil {
|
||||||
|
validMessages = append(validMessages, msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(validMessages) > 0 {
|
||||||
|
// Use a separate goroutine to handle messages to prevent blocking
|
||||||
|
// network handling if there's an issue with chat processing
|
||||||
|
go func(msgs []*pb.ChatMessage) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Printf("Recovered from panic in chat message handler: %v", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
handler.HandleServerMessages(msgs)
|
||||||
|
}(validMessages)
|
||||||
|
|
||||||
|
// Update the last seen message timestamp to the most recent message
|
||||||
|
lastMsg := validMessages[len(validMessages)-1]
|
||||||
lastSeenMessageTimestamp = lastMsg.Timestamp
|
lastSeenMessageTimestamp = lastMsg.Timestamp
|
||||||
log.Printf("Updated last seen message timestamp to %d", lastSeenMessageTimestamp)
|
log.Printf("Updated last seen message timestamp to %d", lastSeenMessageTimestamp)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user