Compare commits

..

No commits in common. "d86cbe15a3dbe60da8026d52b8646adf9964df2d" and "2a0f9348e9199930ba14c0fb84744384841e6018" have entirely different histories.

4 changed files with 91 additions and 157 deletions

View File

@ -168,12 +168,13 @@ func (c *Chat) Update() (string, bool) {
c.inputBuffer = c.inputBuffer[:0] c.inputBuffer = c.inputBuffer[:0]
c.cursorPos = 0 c.cursorPos = 0
c.isTyping = false c.isTyping = false
return message, true return message, true
} }
c.isTyping = false c.isTyping = false
} }
if rl.IsKeyPressed(rl.KeyEscape) && c.isTyping { if rl.IsKeyPressed(rl.KeyEscape) {
c.inputBuffer = c.inputBuffer[:0] c.inputBuffer = c.inputBuffer[:0]
c.cursorPos = 0 c.cursorPos = 0
c.isTyping = false c.isTyping = false

View File

@ -16,8 +16,6 @@ type Game struct {
Models []types.ModelAsset Models []types.ModelAsset
Music rl.Music Music rl.Music
Chat *Chat Chat *Chat
MenuOpen bool
QuitChan chan struct{} // Channel to signal shutdown
} }
func New() *Game { func New() *Game {
@ -38,8 +36,7 @@ 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
@ -62,17 +59,6 @@ func (g *Game) LoadAssets() error {
} }
func (g *Game) Update(deltaTime float32) { func (g *Game) Update(deltaTime float32) {
// Handle ESC for menu
if rl.IsKeyPressed(rl.KeyEscape) {
g.MenuOpen = !g.MenuOpen
return
}
// Don't process other inputs if menu is open
if g.MenuOpen {
return
}
if message, sent := g.Chat.Update(); sent { if message, sent := g.Chat.Update(); sent {
g.Player.Lock() g.Player.Lock()
g.Player.ActionQueue = append(g.Player.ActionQueue, &pb.Action{ g.Player.ActionQueue = append(g.Player.ActionQueue, &pb.Action{
@ -182,17 +168,36 @@ func (g *Game) Render() {
} }
rl.EndMode3D() rl.EndMode3D()
// Draw menu if open drawFloatingMessage := func(msg *types.FloatingMessage) {
if g.MenuOpen { if msg == nil || time.Now().After(msg.ExpireTime) {
g.DrawMenu() return
}
pos := msg.ScreenPos
text := msg.Content
textWidth := rl.MeasureText(text, 20)
for offsetX := -2; offsetX <= 2; offsetX++ {
for offsetY := -2; offsetY <= 2; offsetY++ {
rl.DrawText(text,
int32(pos.X)-textWidth/2+int32(offsetX),
int32(pos.Y)+int32(offsetY),
20,
rl.Black)
}
}
rl.DrawText(text, int32(pos.X)-textWidth/2, int32(pos.Y), 20, rl.Yellow)
} }
// Only draw chat if menu is not open if g.Player.FloatingMessage != nil {
if !g.MenuOpen { drawFloatingMessage(g.Player.FloatingMessage)
g.Chat.Draw(int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight())) }
for _, other := range g.OtherPlayers {
drawFloatingMessage(other.FloatingMessage)
} }
rl.DrawFPS(10, 10) rl.DrawFPS(10, 10)
g.Chat.Draw(int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight()))
rl.EndDrawing() rl.EndDrawing()
} }
@ -218,62 +223,3 @@ func (g *Game) HandleInput() {
} }
} }
} }
func (g *Game) DrawMenu() {
screenWidth := float32(rl.GetScreenWidth())
screenHeight := float32(rl.GetScreenHeight())
// Semi-transparent background
rl.DrawRectangle(0, 0, int32(screenWidth), int32(screenHeight), rl.ColorAlpha(rl.Black, 0.7))
// Menu title
title := "Menu"
titleSize := int32(40)
titleWidth := rl.MeasureText(title, titleSize)
rl.DrawText(title, int32(screenWidth/2)-titleWidth/2, 100, titleSize, rl.White)
// Menu buttons
buttonWidth := float32(200)
buttonHeight := float32(40)
buttonY := float32(200)
buttonSpacing := float32(60)
menuItems := []string{"Resume", "Settings", "Exit Game"}
for _, item := range menuItems {
buttonRect := rl.Rectangle{
X: screenWidth/2 - buttonWidth/2,
Y: buttonY,
Width: buttonWidth,
Height: buttonHeight,
}
// Check mouse hover
mousePoint := rl.GetMousePosition()
mouseHover := rl.CheckCollisionPointRec(mousePoint, buttonRect)
// Draw button
if mouseHover {
rl.DrawRectangleRec(buttonRect, rl.ColorAlpha(rl.White, 0.3))
if rl.IsMouseButtonPressed(rl.MouseLeftButton) {
switch item {
case "Resume":
g.MenuOpen = false
case "Settings":
// TODO: Implement settings
case "Exit Game":
close(g.QuitChan) // Signal all goroutines to shut down
rl.CloseWindow()
}
}
}
// Draw button text
textSize := int32(20)
textWidth := rl.MeasureText(item, textSize)
textX := int32(buttonRect.X+buttonRect.Width/2) - textWidth/2
textY := int32(buttonRect.Y + buttonRect.Height/2 - float32(textSize)/2)
rl.DrawText(item, textX, textY, textSize, rl.White)
buttonY += buttonSpacing
}
}

View File

@ -30,7 +30,6 @@ func main() {
} }
rl.InitWindow(1024, 768, "GoonScape") rl.InitWindow(1024, 768, "GoonScape")
rl.SetExitKey(0)
defer rl.CloseWindow() defer rl.CloseWindow()
rl.InitAudioDevice() rl.InitAudioDevice()
defer rl.CloseAudioDevice() defer rl.CloseAudioDevice()
@ -52,7 +51,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, game.QuitChan) go network.HandleServerCommunication(conn, playerID, game.Player, game.OtherPlayers)
rl.PlayMusicStream(game.Music) rl.PlayMusicStream(game.Music)
rl.SetMusicVolume(game.Music, 0.5) rl.SetMusicVolume(game.Music, 0.5)
@ -65,7 +64,4 @@ func main() {
game.Update(deltaTime) game.Update(deltaTime)
game.Render() game.Render()
} }
// Wait for clean shutdown
<-game.QuitChan
} }

View File

@ -56,98 +56,89 @@ 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, quitChan <-chan struct{}) { func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player) {
// 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 { for range actionTicker.C {
select { player.Lock()
case <-quitChan: if len(player.ActionQueue) > 0 {
return actions := make([]*pb.Action, len(player.ActionQueue))
case <-actionTicker.C: copy(actions, player.ActionQueue)
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 {
select { // Read message length (4 bytes)
case <-quitChan: lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
log.Printf("Failed to read message length: %v", err)
return return
default: }
// Read message length (4 bytes) messageLength := binary.BigEndian.Uint32(lengthBuf)
lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
log.Printf("Failed to read message length: %v", err)
return
}
messageLength := binary.BigEndian.Uint32(lengthBuf)
// Read the full message // Read the full message
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)
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
}
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
}
}
}
player.Unlock()
for _, state := range serverMessage.Players {
if state.PlayerId == playerID {
continue continue
} }
player.Lock() if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
player.CurrentTick = serverMessage.CurrentTick otherPlayer.UpdatePosition(state, types.ServerTickRate)
} else {
tickDiff := serverMessage.CurrentTick - player.CurrentTick otherPlayers[state.PlayerId] = types.NewPlayer(state)
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 g, ok := player.UserData.(*game.Game); ok && len(serverMessage.ChatMessages) > 0 {
if state.PlayerId == playerID { g.Chat.HandleServerMessages(serverMessage.ChatMessages)
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)
}
} }
} }
} }