feature/menu #3
@ -17,6 +17,7 @@ type Game struct {
|
|||||||
Music rl.Music
|
Music rl.Music
|
||||||
Chat *Chat
|
Chat *Chat
|
||||||
MenuOpen bool
|
MenuOpen bool
|
||||||
|
QuitChan chan struct{} // Channel to signal shutdown
|
||||||
}
|
}
|
||||||
|
|
||||||
func New() *Game {
|
func New() *Game {
|
||||||
@ -37,7 +38,8 @@ func New() *Game {
|
|||||||
Fovy: 45.0,
|
Fovy: 45.0,
|
||||||
Projection: rl.CameraPerspective,
|
Projection: rl.CameraPerspective,
|
||||||
},
|
},
|
||||||
Chat: NewChat(),
|
Chat: NewChat(),
|
||||||
|
QuitChan: make(chan struct{}),
|
||||||
}
|
}
|
||||||
game.Player.UserData = game
|
game.Player.UserData = game
|
||||||
game.Chat.userData = game
|
game.Chat.userData = game
|
||||||
@ -259,6 +261,7 @@ func (g *Game) DrawMenu() {
|
|||||||
case "Settings":
|
case "Settings":
|
||||||
// TODO: Implement settings
|
// TODO: Implement settings
|
||||||
case "Exit Game":
|
case "Exit Game":
|
||||||
|
close(g.QuitChan) // Signal all goroutines to shut down
|
||||||
rl.CloseWindow()
|
rl.CloseWindow()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
5
main.go
5
main.go
@ -52,7 +52,7 @@ func main() {
|
|||||||
game.Player.Model = game.Models[modelIndex].Model
|
game.Player.Model = game.Models[modelIndex].Model
|
||||||
game.Player.Texture = game.Models[modelIndex].Texture
|
game.Player.Texture = game.Models[modelIndex].Texture
|
||||||
|
|
||||||
go network.HandleServerCommunication(conn, playerID, game.Player, game.OtherPlayers)
|
go network.HandleServerCommunication(conn, playerID, game.Player, game.OtherPlayers, game.QuitChan)
|
||||||
|
|
||||||
rl.PlayMusicStream(game.Music)
|
rl.PlayMusicStream(game.Music)
|
||||||
rl.SetMusicVolume(game.Music, 0.5)
|
rl.SetMusicVolume(game.Music, 0.5)
|
||||||
@ -65,4 +65,7 @@ func main() {
|
|||||||
game.Update(deltaTime)
|
game.Update(deltaTime)
|
||||||
game.Render()
|
game.Render()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wait for clean shutdown
|
||||||
|
<-game.QuitChan
|
||||||
}
|
}
|
||||||
|
@ -56,89 +56,98 @@ func ConnectToServer() (net.Conn, int32, error) {
|
|||||||
return conn, playerID, nil
|
return conn, playerID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player) {
|
func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player, quitChan <-chan struct{}) {
|
||||||
// Create a buffered reader for the connection
|
|
||||||
reader := bufio.NewReader(conn)
|
reader := bufio.NewReader(conn)
|
||||||
|
|
||||||
actionTicker := time.NewTicker(types.ClientTickRate)
|
actionTicker := time.NewTicker(types.ClientTickRate)
|
||||||
defer actionTicker.Stop()
|
defer actionTicker.Stop()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for range actionTicker.C {
|
for {
|
||||||
player.Lock()
|
select {
|
||||||
if len(player.ActionQueue) > 0 {
|
case <-quitChan:
|
||||||
actions := make([]*pb.Action, len(player.ActionQueue))
|
return
|
||||||
copy(actions, player.ActionQueue)
|
case <-actionTicker.C:
|
||||||
|
player.Lock()
|
||||||
|
if len(player.ActionQueue) > 0 {
|
||||||
|
actions := make([]*pb.Action, len(player.ActionQueue))
|
||||||
|
copy(actions, player.ActionQueue)
|
||||||
|
|
||||||
batch := &pb.ActionBatch{
|
batch := &pb.ActionBatch{
|
||||||
PlayerId: playerID,
|
PlayerId: playerID,
|
||||||
Actions: actions,
|
Actions: actions,
|
||||||
Tick: player.CurrentTick,
|
Tick: player.CurrentTick,
|
||||||
|
}
|
||||||
|
|
||||||
|
player.ActionQueue = player.ActionQueue[:0]
|
||||||
|
player.Unlock()
|
||||||
|
|
||||||
|
if err := writeMessage(conn, batch); err != nil {
|
||||||
|
log.Printf("Failed to send actions to server: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
player.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
player.ActionQueue = player.ActionQueue[:0]
|
|
||||||
player.Unlock()
|
|
||||||
|
|
||||||
if err := writeMessage(conn, batch); err != nil {
|
|
||||||
log.Printf("Failed to send actions to server: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
player.Unlock()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
// Read message length (4 bytes)
|
select {
|
||||||
lengthBuf := make([]byte, 4)
|
case <-quitChan:
|
||||||
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
|
|
||||||
log.Printf("Failed to read message length: %v", err)
|
|
||||||
return
|
return
|
||||||
}
|
default:
|
||||||
messageLength := binary.BigEndian.Uint32(lengthBuf)
|
// Read message length (4 bytes)
|
||||||
|
lengthBuf := make([]byte, 4)
|
||||||
// Read the full message
|
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
|
||||||
messageBuf := make([]byte, messageLength)
|
log.Printf("Failed to read message length: %v", err)
|
||||||
if _, err := io.ReadFull(reader, messageBuf); err != nil {
|
return
|
||||||
log.Printf("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
|
|
||||||
}
|
|
||||||
|
|
||||||
player.Lock()
|
|
||||||
player.CurrentTick = serverMessage.CurrentTick
|
|
||||||
|
|
||||||
tickDiff := serverMessage.CurrentTick - player.CurrentTick
|
|
||||||
if tickDiff > types.MaxTickDesync {
|
|
||||||
for _, state := range serverMessage.Players {
|
|
||||||
if state.PlayerId == playerID {
|
|
||||||
player.ForceResync(state)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
messageLength := binary.BigEndian.Uint32(lengthBuf)
|
||||||
player.Unlock()
|
|
||||||
|
|
||||||
for _, state := range serverMessage.Players {
|
// Read the full message
|
||||||
if state.PlayerId == playerID {
|
messageBuf := make([]byte, messageLength)
|
||||||
|
if _, err := io.ReadFull(reader, messageBuf); err != nil {
|
||||||
|
log.Printf("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
|
||||||
}
|
}
|
||||||
|
|
||||||
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
|
player.Lock()
|
||||||
otherPlayer.UpdatePosition(state, types.ServerTickRate)
|
player.CurrentTick = serverMessage.CurrentTick
|
||||||
} else {
|
|
||||||
otherPlayers[state.PlayerId] = types.NewPlayer(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if g, ok := player.UserData.(*game.Game); ok && len(serverMessage.ChatMessages) > 0 {
|
tickDiff := serverMessage.CurrentTick - player.CurrentTick
|
||||||
g.Chat.HandleServerMessages(serverMessage.ChatMessages)
|
if tickDiff > types.MaxTickDesync {
|
||||||
|
for _, state := range serverMessage.Players {
|
||||||
|
if state.PlayerId == playerID {
|
||||||
|
player.ForceResync(state)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.Unlock()
|
||||||
|
|
||||||
|
for _, state := range serverMessage.Players {
|
||||||
|
if state.PlayerId == playerID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
|
||||||
|
otherPlayer.UpdatePosition(state, types.ServerTickRate)
|
||||||
|
} else {
|
||||||
|
otherPlayers[state.PlayerId] = types.NewPlayer(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if g, ok := player.UserData.(*game.Game); ok && len(serverMessage.ChatMessages) > 0 {
|
||||||
|
g.Chat.HandleServerMessages(serverMessage.ChatMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user