Broken bullshit
This commit is contained in:
15
game/chat.go
15
game/chat.go
@ -2,6 +2,7 @@ package game
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.boner.be/bdnugget/goonscape/types"
|
||||
@ -19,6 +20,7 @@ const (
|
||||
)
|
||||
|
||||
type Chat struct {
|
||||
sync.RWMutex
|
||||
messages []types.ChatMessage
|
||||
inputBuffer []rune
|
||||
isTyping bool
|
||||
@ -49,6 +51,8 @@ func (c *Chat) AddMessage(playerID int32, content string) {
|
||||
}
|
||||
|
||||
func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
|
||||
c.Lock()
|
||||
defer c.Unlock()
|
||||
// Convert protobuf messages to our local type
|
||||
for _, msg := range messages {
|
||||
localMsg := types.ChatMessage{
|
||||
@ -80,13 +84,14 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
|
||||
ExpireTime: time.Now().Add(6 * time.Second),
|
||||
}
|
||||
game.Player.Unlock()
|
||||
} else if otherPlayer, exists := game.OtherPlayers[msg.PlayerId]; exists {
|
||||
otherPlayer.Lock()
|
||||
otherPlayer.FloatingMessage = &types.FloatingMessage{
|
||||
} else if otherPlayer, exists := game.OtherPlayers.Load(msg.PlayerId); exists {
|
||||
other := otherPlayer.(*types.Player)
|
||||
other.Lock()
|
||||
other.FloatingMessage = &types.FloatingMessage{
|
||||
Content: msg.Content,
|
||||
ExpireTime: time.Now().Add(6 * time.Second),
|
||||
}
|
||||
otherPlayer.Unlock()
|
||||
other.Unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,6 +99,8 @@ func (c *Chat) HandleServerMessages(messages []*pb.ChatMessage) {
|
||||
}
|
||||
|
||||
func (c *Chat) Draw(screenWidth, screenHeight int32) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
// Calculate chat window width based on screen width
|
||||
chatWindowWidth := screenWidth - (chatMargin * 2)
|
||||
|
||||
|
38
game/context.go
Normal file
38
game/context.go
Normal file
@ -0,0 +1,38 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type GameContext struct {
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
assetsLock sync.RWMutex
|
||||
}
|
||||
|
||||
func NewGameContext() *GameContext {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
return &GameContext{
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
}
|
||||
}
|
||||
|
||||
func (gc *GameContext) Shutdown() {
|
||||
gc.cancel()
|
||||
gc.wg.Wait()
|
||||
}
|
||||
|
||||
func (gc *GameContext) LoadAssets(fn func() error) error {
|
||||
gc.assetsLock.Lock()
|
||||
defer gc.assetsLock.Unlock()
|
||||
return fn()
|
||||
}
|
||||
|
||||
func (gc *GameContext) UnloadAssets(fn func()) {
|
||||
gc.assetsLock.Lock()
|
||||
defer gc.assetsLock.Unlock()
|
||||
fn()
|
||||
}
|
154
game/game.go
154
game/game.go
@ -1,33 +1,44 @@
|
||||
package game
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"sync/atomic"
|
||||
|
||||
"gitea.boner.be/bdnugget/goonscape/assets"
|
||||
"gitea.boner.be/bdnugget/goonscape/config"
|
||||
"gitea.boner.be/bdnugget/goonscape/logging"
|
||||
"gitea.boner.be/bdnugget/goonscape/network"
|
||||
"gitea.boner.be/bdnugget/goonscape/types"
|
||||
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
)
|
||||
|
||||
var audioMutex sync.Mutex
|
||||
var audioInitOnce sync.Once
|
||||
|
||||
type Game struct {
|
||||
ctx *GameContext
|
||||
Player *types.Player
|
||||
OtherPlayers map[int32]*types.Player
|
||||
OtherPlayers sync.Map // Using sync.Map for concurrent access
|
||||
Camera rl.Camera3D
|
||||
Models []types.ModelAsset
|
||||
Music rl.Music
|
||||
Chat *Chat
|
||||
MenuOpen bool
|
||||
MenuOpen atomic.Bool
|
||||
QuitChan chan struct{} // Channel to signal shutdown
|
||||
loginScreen *LoginScreen
|
||||
isLoggedIn bool
|
||||
isLoggedIn atomic.Bool
|
||||
}
|
||||
|
||||
func New() *Game {
|
||||
InitWorld()
|
||||
game := &Game{
|
||||
OtherPlayers: make(map[int32]*types.Player),
|
||||
ctx: NewGameContext(),
|
||||
OtherPlayers: sync.Map{},
|
||||
Camera: rl.Camera3D{
|
||||
Position: rl.NewVector3(0, 10, 10),
|
||||
Target: rl.NewVector3(0, 0, 0),
|
||||
@ -44,22 +55,38 @@ func New() *Game {
|
||||
}
|
||||
|
||||
func (g *Game) LoadAssets() error {
|
||||
audioMutex.Lock()
|
||||
defer audioMutex.Unlock()
|
||||
|
||||
logging.Info.Println("Loading game assets")
|
||||
var err error
|
||||
|
||||
// Load models first
|
||||
g.Models, err = assets.LoadModels()
|
||||
if err != nil {
|
||||
logging.Error.Printf("Failed to load models: %v", err)
|
||||
return err
|
||||
}
|
||||
|
||||
g.Music, err = assets.LoadMusic("resources/audio/GoonScape2.mp3")
|
||||
if err != nil {
|
||||
return err
|
||||
// Load music only if enabled
|
||||
if config.Current.PlayMusic {
|
||||
logging.Info.Println("Loading music stream")
|
||||
g.Music = rl.LoadMusicStream("resources/audio/GoonScape2.mp3")
|
||||
if g.Music.CtxType == 0 {
|
||||
logging.Error.Println("Failed to load music stream")
|
||||
return fmt.Errorf("failed to load music stream")
|
||||
}
|
||||
logging.Info.Println("Music stream loaded successfully")
|
||||
} else {
|
||||
logging.Info.Println("Music disabled by config")
|
||||
}
|
||||
|
||||
logging.Info.Println("Assets loaded successfully")
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Update(deltaTime float32) {
|
||||
if !g.isLoggedIn {
|
||||
if !g.isLoggedIn.Load() {
|
||||
username, password, isRegistering, submitted := g.loginScreen.Update()
|
||||
if submitted {
|
||||
conn, playerID, err := network.ConnectToServer(username, password, isRegistering)
|
||||
@ -77,8 +104,8 @@ func (g *Game) Update(deltaTime float32) {
|
||||
}
|
||||
g.AssignModelToPlayer(g.Player)
|
||||
|
||||
go network.HandleServerCommunication(conn, playerID, g.Player, g.OtherPlayers, g.QuitChan)
|
||||
g.isLoggedIn = true
|
||||
go network.HandleServerCommunication(conn, playerID, g.Player, &g.OtherPlayers, g.QuitChan)
|
||||
g.isLoggedIn.Store(true)
|
||||
return
|
||||
}
|
||||
g.loginScreen.Draw()
|
||||
@ -87,12 +114,12 @@ func (g *Game) Update(deltaTime float32) {
|
||||
|
||||
// Handle ESC for menu
|
||||
if rl.IsKeyPressed(rl.KeyEscape) {
|
||||
g.MenuOpen = !g.MenuOpen
|
||||
g.MenuOpen.Store(!g.MenuOpen.Load())
|
||||
return
|
||||
}
|
||||
|
||||
// Don't process other inputs if menu is open
|
||||
if g.MenuOpen {
|
||||
if g.MenuOpen.Load() {
|
||||
return
|
||||
}
|
||||
|
||||
@ -109,14 +136,20 @@ func (g *Game) Update(deltaTime float32) {
|
||||
g.HandleInput()
|
||||
|
||||
if len(g.Player.TargetPath) > 0 {
|
||||
g.Player.MoveTowards(g.Player.TargetPath[0], deltaTime, GetMapGrid())
|
||||
g.Player.Lock()
|
||||
if len(g.Player.TargetPath) > 0 {
|
||||
g.Player.MoveTowards(g.Player.TargetPath[0], deltaTime, GetMapGrid())
|
||||
}
|
||||
g.Player.Unlock()
|
||||
}
|
||||
|
||||
for _, other := range g.OtherPlayers {
|
||||
g.OtherPlayers.Range(func(key, value any) bool {
|
||||
other := value.(*types.Player)
|
||||
if len(other.TargetPath) > 0 {
|
||||
other.MoveTowards(other.TargetPath[0], deltaTime, GetMapGrid())
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
UpdateCamera(&g.Camera, g.Player.PosActual, deltaTime)
|
||||
}
|
||||
@ -153,6 +186,11 @@ func (g *Game) DrawPlayer(player *types.Player) {
|
||||
player.Lock()
|
||||
defer player.Unlock()
|
||||
|
||||
if player.Model.Meshes == nil {
|
||||
logging.Error.Println("Player model not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
grid := GetMapGrid()
|
||||
modelIndex := int(player.ID) % len(g.Models)
|
||||
modelAsset := g.Models[modelIndex]
|
||||
@ -210,24 +248,36 @@ func (g *Game) DrawPlayer(player *types.Player) {
|
||||
}
|
||||
|
||||
func (g *Game) Render() {
|
||||
rl.BeginDrawing()
|
||||
rl.ClearBackground(rl.RayWhite)
|
||||
if !rl.IsWindowReady() {
|
||||
logging.Error.Println("Window not ready for rendering")
|
||||
return
|
||||
}
|
||||
|
||||
if !g.isLoggedIn {
|
||||
rl.BeginDrawing()
|
||||
defer func() {
|
||||
if rl.IsWindowReady() {
|
||||
rl.EndDrawing()
|
||||
}
|
||||
}()
|
||||
|
||||
if !g.isLoggedIn.Load() {
|
||||
g.loginScreen.Draw()
|
||||
rl.EndDrawing()
|
||||
return
|
||||
}
|
||||
|
||||
rl.BeginMode3D(g.Camera)
|
||||
g.DrawMap()
|
||||
g.DrawPlayer(g.Player)
|
||||
for _, other := range g.OtherPlayers {
|
||||
|
||||
g.OtherPlayers.Range(func(key, value any) bool {
|
||||
other := value.(*types.Player)
|
||||
if other.Model.Meshes == nil {
|
||||
g.AssignModelToPlayer(other)
|
||||
}
|
||||
g.DrawPlayer(other)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
rl.EndMode3D()
|
||||
|
||||
// Draw floating messages
|
||||
@ -255,27 +305,41 @@ func (g *Game) Render() {
|
||||
drawFloatingMessage(g.Player.FloatingMessage)
|
||||
}
|
||||
|
||||
for _, other := range g.OtherPlayers {
|
||||
g.OtherPlayers.Range(func(key, value any) bool {
|
||||
other := value.(*types.Player)
|
||||
drawFloatingMessage(other.FloatingMessage)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
// Draw menu if open
|
||||
if g.MenuOpen {
|
||||
if g.MenuOpen.Load() {
|
||||
g.DrawMenu()
|
||||
}
|
||||
|
||||
// Only draw chat if menu is not open
|
||||
if !g.MenuOpen {
|
||||
if !g.MenuOpen.Load() {
|
||||
g.Chat.Draw(int32(rl.GetScreenWidth()), int32(rl.GetScreenHeight()))
|
||||
}
|
||||
|
||||
rl.DrawFPS(10, 10)
|
||||
rl.EndDrawing()
|
||||
}
|
||||
|
||||
func (g *Game) Cleanup() {
|
||||
assets.UnloadModels(g.Models)
|
||||
assets.UnloadMusic(g.Music)
|
||||
// Unload models
|
||||
if g.Models != nil {
|
||||
assets.UnloadModels(g.Models)
|
||||
}
|
||||
|
||||
// Stop and unload music if enabled
|
||||
if config.Current.PlayMusic && g.Music.CtxType != 0 {
|
||||
rl.StopMusicStream(g.Music)
|
||||
rl.UnloadMusicStream(g.Music)
|
||||
}
|
||||
|
||||
// Close audio device if it's ready
|
||||
if rl.IsAudioDeviceReady() {
|
||||
rl.CloseAudioDevice()
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Game) HandleInput() {
|
||||
@ -334,7 +398,7 @@ func (g *Game) DrawMenu() {
|
||||
if rl.IsMouseButtonPressed(rl.MouseLeftButton) {
|
||||
switch item {
|
||||
case "Resume":
|
||||
g.MenuOpen = false
|
||||
g.MenuOpen.Store(false)
|
||||
case "Settings":
|
||||
// TODO: Implement settings
|
||||
case "Exit Game":
|
||||
@ -369,7 +433,37 @@ func (g *Game) AssignModelToPlayer(player *types.Player) {
|
||||
modelIndex := int(player.ID) % len(g.Models)
|
||||
modelAsset := g.Models[modelIndex]
|
||||
|
||||
// Just use the original model - don't try to copy it
|
||||
player.Model = modelAsset.Model
|
||||
player.Texture = modelAsset.Texture
|
||||
player.AnimationFrame = 0
|
||||
}
|
||||
|
||||
func (g *Game) Run() {
|
||||
if config.Current.PlayMusic {
|
||||
audioInitOnce.Do(func() {
|
||||
logging.Info.Println("Initializing audio device")
|
||||
rl.InitAudioDevice()
|
||||
if !rl.IsAudioDeviceReady() {
|
||||
logging.Error.Println("Failed to initialize audio device")
|
||||
}
|
||||
})
|
||||
defer func() {
|
||||
logging.Info.Println("Closing audio device")
|
||||
rl.CloseAudioDevice()
|
||||
}()
|
||||
}
|
||||
|
||||
logging.Info.Println("Starting game loop")
|
||||
for !rl.WindowShouldClose() {
|
||||
deltaTime := rl.GetFrameTime()
|
||||
if config.Current.PlayMusic {
|
||||
rl.UpdateMusicStream(g.Music)
|
||||
}
|
||||
g.Update(deltaTime)
|
||||
g.Render()
|
||||
}
|
||||
logging.Info.Println("Game loop ended")
|
||||
|
||||
logging.Info.Println("Closing quit channel")
|
||||
close(g.QuitChan)
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ func heuristic(a, b types.Tile) float32 {
|
||||
}
|
||||
|
||||
func distance(a, b types.Tile) float32 {
|
||||
_, _ = a, b
|
||||
return 1.0 // uniform cost for now
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user