Broken bullshit
This commit is contained in:
parent
a1aeb71512
commit
53cc9bca6b
5
2
Normal file
5
2
Normal file
@ -0,0 +1,5 @@
|
||||
INFO: 2025/02/10 14:51:58 main.go:15: Starting GoonScape client
|
||||
INFO: 2025/02/10 14:51:58 main.go:38: Initializing window
|
||||
INFO: 2025/02/10 14:51:58 main.go:55: Loading game assets
|
||||
INFO: 2025/02/10 14:51:58 game.go:54: Loading game assets
|
||||
INFO: 2025/02/10 14:51:58 assets.go:51: Loading models
|
108
assets/assets.go
108
assets/assets.go
@ -1,10 +1,25 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"gitea.boner.be/bdnugget/goonscape/logging"
|
||||
"gitea.boner.be/bdnugget/goonscape/types"
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
)
|
||||
|
||||
var (
|
||||
assetMutex sync.RWMutex
|
||||
loadedModels map[string]types.ModelAsset
|
||||
audioMutex sync.Mutex
|
||||
audioInitialized bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
loadedModels = make(map[string]types.ModelAsset)
|
||||
}
|
||||
|
||||
// Helper function to load animations for a model
|
||||
func loadModelAnimations(animPaths map[string]string) (types.AnimationSet, error) {
|
||||
var animSet types.AnimationSet
|
||||
@ -33,9 +48,28 @@ func loadModelAnimations(animPaths map[string]string) (types.AnimationSet, error
|
||||
}
|
||||
|
||||
func LoadModels() ([]types.ModelAsset, error) {
|
||||
logging.Info.Println("Loading models")
|
||||
assetMutex.Lock()
|
||||
defer assetMutex.Unlock()
|
||||
|
||||
if len(loadedModels) > 0 {
|
||||
logging.Info.Println("Returning cached models")
|
||||
models := make([]types.ModelAsset, 0, len(loadedModels))
|
||||
for _, model := range loadedModels {
|
||||
models = append(models, model)
|
||||
}
|
||||
return models, nil
|
||||
}
|
||||
|
||||
// Goonion model and animations
|
||||
goonerModel := rl.LoadModel("resources/models/gooner/walk_no_y_transform.glb")
|
||||
goonerAnims, _ := loadModelAnimations(map[string]string{"idle": "resources/models/gooner/idle_no_y_transform.glb", "walk": "resources/models/gooner/walk_no_y_transform.glb"})
|
||||
goonerAnims, err := loadModelAnimations(map[string]string{
|
||||
"idle": "resources/models/gooner/idle_no_y_transform.glb",
|
||||
"walk": "resources/models/gooner/walk_no_y_transform.glb",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Apply transformations
|
||||
transform := rl.MatrixIdentity()
|
||||
@ -46,25 +80,30 @@ func LoadModels() ([]types.ModelAsset, error) {
|
||||
|
||||
// Coomer model (ready for animations)
|
||||
coomerModel := rl.LoadModel("resources/models/coomer/idle_notransy.glb")
|
||||
// coomerTexture := rl.LoadTexture("resources/models/coomer.png")
|
||||
// rl.SetMaterialTexture(coomerModel.Materials, rl.MapDiffuse, coomerTexture)
|
||||
// When you have animations, add them like:
|
||||
coomerAnims, _ := loadModelAnimations(map[string]string{"idle": "resources/models/coomer/idle_notransy.glb", "walk": "resources/models/coomer/unsteadywalk_notransy.glb"})
|
||||
coomerAnims, err := loadModelAnimations(map[string]string{
|
||||
"idle": "resources/models/coomer/idle_notransy.glb",
|
||||
"walk": "resources/models/coomer/unsteadywalk_notransy.glb",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
coomerModel.Transform = transform
|
||||
|
||||
// Shreke model (ready for animations)
|
||||
shrekeModel := rl.LoadModel("resources/models/shreke.obj")
|
||||
shrekeTexture := rl.LoadTexture("resources/models/shreke.png")
|
||||
rl.SetMaterialTexture(shrekeModel.Materials, rl.MapDiffuse, shrekeTexture)
|
||||
// When you have animations, add them like:
|
||||
// shrekeAnims, _ := loadModelAnimations("resources/models/shreke.glb",
|
||||
// map[string]string{
|
||||
// "idle": "resources/models/shreke_idle.glb",
|
||||
// "walk": "resources/models/shreke_walk.glb",
|
||||
// })
|
||||
shrekeModel := rl.LoadModel("resources/models/shreke/Animation_Slow_Orc_Walk_withSkin.glb")
|
||||
shrekeAnims, err := loadModelAnimations(map[string]string{
|
||||
"idle": "resources/models/shreke/Animation_Slow_Orc_Walk_withSkin.glb",
|
||||
"walk": "resources/models/shreke/Animation_Excited_Walk_M_withSkin.glb",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
shrekeModel.Transform = transform
|
||||
|
||||
return []types.ModelAsset{
|
||||
// Store loaded models
|
||||
models := []types.ModelAsset{
|
||||
{
|
||||
Name: "gooner",
|
||||
Model: goonerModel,
|
||||
Animation: append(goonerAnims.Idle, goonerAnims.Walk...),
|
||||
AnimFrames: int32(len(goonerAnims.Idle) + len(goonerAnims.Walk)),
|
||||
@ -78,15 +117,47 @@ func LoadModels() ([]types.ModelAsset, error) {
|
||||
Animations: coomerAnims,
|
||||
YOffset: -4.0,
|
||||
},
|
||||
{Model: shrekeModel, Texture: shrekeTexture},
|
||||
}, nil
|
||||
{
|
||||
Model: shrekeModel,
|
||||
Animation: append(shrekeAnims.Idle, shrekeAnims.Walk...),
|
||||
AnimFrames: int32(len(shrekeAnims.Idle) + len(shrekeAnims.Walk)),
|
||||
Animations: shrekeAnims,
|
||||
YOffset: 0.0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, model := range models {
|
||||
loadedModels[model.Name] = model
|
||||
}
|
||||
|
||||
return models, nil
|
||||
}
|
||||
|
||||
func LoadMusic(filename string) (rl.Music, error) {
|
||||
return rl.LoadMusicStream(filename), nil
|
||||
logging.Info.Printf("Loading music from %s", filename)
|
||||
audioMutex.Lock()
|
||||
defer audioMutex.Unlock()
|
||||
|
||||
if !rl.IsAudioDeviceReady() {
|
||||
err := fmt.Errorf("audio device not initialized")
|
||||
logging.Error.Println(err)
|
||||
return rl.Music{}, err
|
||||
}
|
||||
|
||||
music := rl.LoadMusicStream(filename)
|
||||
if music.CtxType == 0 {
|
||||
err := fmt.Errorf("failed to load music stream")
|
||||
logging.Error.Println(err)
|
||||
return rl.Music{}, err
|
||||
}
|
||||
logging.Info.Println("Music loaded successfully")
|
||||
return music, nil
|
||||
}
|
||||
|
||||
func UnloadModels(models []types.ModelAsset) {
|
||||
assetMutex.Lock()
|
||||
defer assetMutex.Unlock()
|
||||
|
||||
for _, model := range models {
|
||||
if model.Animation != nil {
|
||||
for i := int32(0); i < model.AnimFrames; i++ {
|
||||
@ -95,6 +166,7 @@ func UnloadModels(models []types.ModelAsset) {
|
||||
}
|
||||
rl.UnloadModel(model.Model)
|
||||
rl.UnloadTexture(model.Texture)
|
||||
delete(loadedModels, model.Name)
|
||||
}
|
||||
}
|
||||
|
||||
|
7
config/config.go
Normal file
7
config/config.go
Normal file
@ -0,0 +1,7 @@
|
||||
package config
|
||||
|
||||
type Config struct {
|
||||
PlayMusic bool
|
||||
}
|
||||
|
||||
var Current = Config{PlayMusic: true}
|
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
|
||||
}
|
||||
|
||||
|
4
go.mod
4
go.mod
@ -10,8 +10,8 @@ require (
|
||||
|
||||
require (
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
)
|
||||
|
||||
replace gitea.boner.be/bdnugget/goonserver => ./goonserver
|
||||
|
4
go.sum
4
go.sum
@ -6,7 +6,11 @@ github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8 h1:yqrTHse8TCMW1M1ZCP+VAR/l0kKxwaAIqN/il7x4voA=
|
||||
golang.org/x/exp v0.0.0-20250106191152-7588d65b2ba8/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3 h1:qNgPs5exUA+G0C96DrPwNrvLSj7GT/9D+3WMWUcUg34=
|
||||
golang.org/x/exp v0.0.0-20250207012021-f9890c6ad9f3/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
|
||||
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
|
16
logging/logging.go
Normal file
16
logging/logging.go
Normal file
@ -0,0 +1,16 @@
|
||||
package logging
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
Info *log.Logger
|
||||
Error *log.Logger
|
||||
)
|
||||
|
||||
func init() {
|
||||
Info = log.New(os.Stdout, "INFO: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
Error = log.New(os.Stderr, "ERROR: ", log.Ldate|log.Ltime|log.Lshortfile)
|
||||
}
|
51
main.go
51
main.go
@ -5,17 +5,27 @@ import (
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"gitea.boner.be/bdnugget/goonscape/config"
|
||||
"gitea.boner.be/bdnugget/goonscape/game"
|
||||
"gitea.boner.be/bdnugget/goonscape/logging"
|
||||
"gitea.boner.be/bdnugget/goonscape/network"
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
)
|
||||
|
||||
func main() {
|
||||
logging.Info.Println("Starting GoonScape client")
|
||||
|
||||
// Raylib log level warn
|
||||
rl.SetTraceLogLevel(rl.LogWarning)
|
||||
// Parse command line flags
|
||||
local := flag.Bool("local", false, "Connect to local server")
|
||||
addr := flag.String("addr", "", "Server address (host or host:port)")
|
||||
noMusic := flag.Bool("no-music", false, "Disable music playback")
|
||||
flag.Parse()
|
||||
|
||||
// Set config before any game initialization
|
||||
config.Current.PlayMusic = !*noMusic
|
||||
|
||||
// Set server address based on flags
|
||||
if *local {
|
||||
if *addr != "" {
|
||||
@ -30,31 +40,40 @@ func main() {
|
||||
network.SetServerAddr(*addr)
|
||||
}
|
||||
|
||||
logging.Info.Println("Initializing window")
|
||||
rl.InitWindow(1024, 768, "GoonScape")
|
||||
rl.SetExitKey(0)
|
||||
defer rl.CloseWindow()
|
||||
defer func() {
|
||||
logging.Info.Println("Closing window")
|
||||
rl.CloseWindow()
|
||||
}()
|
||||
|
||||
rl.InitAudioDevice()
|
||||
// Initialize audio device first
|
||||
if !rl.IsAudioDeviceReady() {
|
||||
rl.InitAudioDevice()
|
||||
if !rl.IsAudioDeviceReady() {
|
||||
log.Fatal("Failed to initialize audio device")
|
||||
}
|
||||
}
|
||||
defer rl.CloseAudioDevice()
|
||||
|
||||
rl.SetTargetFPS(60)
|
||||
|
||||
game := game.New()
|
||||
logging.Info.Println("Loading game assets")
|
||||
if err := game.LoadAssets(); err != nil {
|
||||
log.Fatalf("Failed to load assets: %v", err)
|
||||
}
|
||||
defer game.Cleanup()
|
||||
defer func() {
|
||||
logging.Info.Println("Cleaning up game resources")
|
||||
game.Cleanup()
|
||||
}()
|
||||
|
||||
rl.PlayMusicStream(game.Music)
|
||||
rl.SetMusicVolume(game.Music, 0.5)
|
||||
|
||||
for !rl.WindowShouldClose() {
|
||||
deltaTime := rl.GetFrameTime()
|
||||
rl.UpdateMusicStream(game.Music)
|
||||
game.Update(deltaTime)
|
||||
game.Render()
|
||||
if config.Current.PlayMusic {
|
||||
logging.Info.Println("Starting music playback")
|
||||
rl.PlayMusicStream(game.Music)
|
||||
rl.SetMusicVolume(game.Music, 0.5)
|
||||
}
|
||||
|
||||
// Wait for clean shutdown
|
||||
<-game.QuitChan
|
||||
rl.SetTargetFPS(60)
|
||||
logging.Info.Println("Starting game loop")
|
||||
game.Run()
|
||||
logging.Info.Println("Game exited cleanly")
|
||||
}
|
||||
|
@ -2,16 +2,18 @@ package network
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gitea.boner.be/bdnugget/goonscape/logging"
|
||||
"gitea.boner.be/bdnugget/goonscape/types"
|
||||
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
@ -19,18 +21,84 @@ const protoVersion = 1
|
||||
|
||||
var serverAddr = "boner.be:6969"
|
||||
|
||||
type NetworkManager struct {
|
||||
ctx context.Context
|
||||
conn net.Conn
|
||||
reader *bufio.Reader
|
||||
writer *bufio.Writer
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewNetworkManager(ctx context.Context) *NetworkManager {
|
||||
return &NetworkManager{
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func SetServerAddr(addr string) {
|
||||
serverAddr = addr
|
||||
}
|
||||
|
||||
func ConnectToServer(username, password string, isRegistering bool) (net.Conn, int32, error) {
|
||||
conn, err := net.Dial("tcp", serverAddr)
|
||||
func (nm *NetworkManager) Connect(addr string) error {
|
||||
nm.mu.Lock()
|
||||
defer nm.mu.Unlock()
|
||||
|
||||
var err error
|
||||
nm.conn, err = net.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
log.Printf("Failed to dial server: %v", err)
|
||||
return nil, 0, err
|
||||
return err
|
||||
}
|
||||
|
||||
log.Println("Connected to server. Authenticating...")
|
||||
nm.reader = bufio.NewReader(nm.conn)
|
||||
nm.writer = bufio.NewWriter(nm.conn)
|
||||
|
||||
go nm.readLoop()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (nm *NetworkManager) readLoop() {
|
||||
for {
|
||||
select {
|
||||
case <-nm.ctx.Done():
|
||||
return
|
||||
default:
|
||||
// Read and process messages
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (nm *NetworkManager) Send(message proto.Message) error {
|
||||
nm.mu.Lock()
|
||||
defer nm.mu.Unlock()
|
||||
|
||||
data, err := proto.Marshal(message)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write length prefix
|
||||
lengthBuf := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(lengthBuf, uint32(len(data)))
|
||||
if _, err := nm.writer.Write(lengthBuf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write message body
|
||||
if _, err := nm.writer.Write(data); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nm.writer.Flush()
|
||||
}
|
||||
|
||||
func ConnectToServer(username, password string, isRegistering bool) (net.Conn, int32, error) {
|
||||
logging.Info.Println("Attempting to connect to server at", serverAddr)
|
||||
conn, err := net.Dial("tcp", serverAddr)
|
||||
if err != nil {
|
||||
logging.Error.Printf("Failed to dial server: %v", err)
|
||||
return nil, 0, err
|
||||
}
|
||||
logging.Info.Println("Connected to server. Authenticating...")
|
||||
|
||||
// Send auth message
|
||||
authAction := &pb.Action{
|
||||
@ -81,7 +149,7 @@ func ConnectToServer(username, password string, isRegistering bool) (net.Conn, i
|
||||
|
||||
if !response.AuthSuccess {
|
||||
conn.Close()
|
||||
return nil, 0, fmt.Errorf(response.ErrorMessage)
|
||||
return nil, 0, fmt.Errorf("authentication failed: %s", response.ErrorMessage)
|
||||
}
|
||||
|
||||
playerID := response.GetPlayerId()
|
||||
@ -89,145 +157,63 @@ func ConnectToServer(username, password string, isRegistering bool) (net.Conn, i
|
||||
return conn, playerID, nil
|
||||
}
|
||||
|
||||
func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player, quitChan <-chan struct{}) {
|
||||
reader := bufio.NewReader(conn)
|
||||
|
||||
actionTicker := time.NewTicker(types.ClientTickRate)
|
||||
defer actionTicker.Stop()
|
||||
defer conn.Close()
|
||||
defer close(player.QuitDone)
|
||||
|
||||
// Create a channel to signal when goroutines are done
|
||||
done := make(chan struct{})
|
||||
|
||||
// Create a set of current players to track disconnects
|
||||
currentPlayers := make(map[int32]bool)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <-quitChan:
|
||||
// Send disconnect message to server
|
||||
disconnectMsg := &pb.ActionBatch{
|
||||
PlayerId: playerID,
|
||||
Actions: []*pb.Action{{
|
||||
Type: pb.Action_DISCONNECT,
|
||||
PlayerId: playerID,
|
||||
}},
|
||||
}
|
||||
writeMessage(conn, disconnectMsg)
|
||||
done <- struct{}{}
|
||||
return
|
||||
case <-actionTicker.C:
|
||||
player.Lock()
|
||||
if len(player.ActionQueue) > 0 {
|
||||
actions := make([]*pb.Action, len(player.ActionQueue))
|
||||
copy(actions, player.ActionQueue)
|
||||
|
||||
batch := &pb.ActionBatch{
|
||||
PlayerId: playerID,
|
||||
Actions: actions,
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers *sync.Map, quitChan <-chan struct{}) {
|
||||
defer func() {
|
||||
logging.Info.Println("Closing connection and cleaning up for player", playerID)
|
||||
conn.Close()
|
||||
close(player.QuitDone)
|
||||
}()
|
||||
|
||||
reader := bufio.NewReader(conn)
|
||||
ticker := time.NewTicker(100 * time.Millisecond)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-quitChan:
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
<-done
|
||||
close(player.QuitDone)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
case <-time.After(1 * time.Second):
|
||||
log.Println("Shutdown timed out")
|
||||
}
|
||||
return
|
||||
default:
|
||||
// Read message length (4 bytes)
|
||||
case <-ticker.C:
|
||||
// Read message length
|
||||
lengthBuf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
|
||||
log.Printf("Failed to read message length: %v", err)
|
||||
return
|
||||
continue
|
||||
}
|
||||
messageLength := binary.BigEndian.Uint32(lengthBuf)
|
||||
|
||||
// Read the full message
|
||||
// Read message body
|
||||
messageBuf := make([]byte, messageLength)
|
||||
if _, err := io.ReadFull(reader, messageBuf); err != nil {
|
||||
log.Printf("Failed to read message body: %v", err)
|
||||
return
|
||||
continue
|
||||
}
|
||||
|
||||
// Process server message
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
player.Unlock()
|
||||
|
||||
// Update player states
|
||||
for _, state := range serverMessage.Players {
|
||||
currentPlayers[state.PlayerId] = true
|
||||
if state.PlayerId == playerID {
|
||||
player.Lock()
|
||||
// Update initial position if not set
|
||||
if player.PosActual.X == 0 && player.PosActual.Z == 0 {
|
||||
player.PosActual = rl.Vector3{
|
||||
X: float32(state.X * types.TileSize),
|
||||
Y: 0,
|
||||
Z: float32(state.Y * types.TileSize),
|
||||
}
|
||||
player.PosTile = types.Tile{X: int(state.X), Y: int(state.Y)}
|
||||
}
|
||||
player.Unlock()
|
||||
if state == nil {
|
||||
logging.Error.Println("Received nil player state")
|
||||
continue
|
||||
}
|
||||
|
||||
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
|
||||
otherPlayer.UpdatePosition(state, types.ServerTickRate)
|
||||
if state.PlayerId == playerID {
|
||||
player.UpdatePosition(state, types.ServerTickRate)
|
||||
} else if existing, ok := otherPlayers.Load(state.PlayerId); ok {
|
||||
existing.(*types.Player).UpdatePosition(state, types.ServerTickRate)
|
||||
} else {
|
||||
otherPlayers[state.PlayerId] = types.NewPlayer(state)
|
||||
newPlayer := types.NewPlayer(state)
|
||||
otherPlayers.Store(state.PlayerId, newPlayer)
|
||||
}
|
||||
}
|
||||
|
||||
// Remove players that are no longer in the server state
|
||||
for id := range otherPlayers {
|
||||
if !currentPlayers[id] {
|
||||
delete(otherPlayers, id)
|
||||
}
|
||||
}
|
||||
|
||||
if handler, ok := player.UserData.(types.ChatMessageHandler); ok && len(serverMessage.ChatMessages) > 0 {
|
||||
// Handle chat messages
|
||||
if handler, ok := player.UserData.(types.ChatMessageHandler); ok {
|
||||
handler.HandleServerMessages(serverMessage.ChatMessages)
|
||||
}
|
||||
}
|
||||
|
BIN
resources/models/shreke/Animation_Excited_Walk_M_withSkin.glb
Normal file
BIN
resources/models/shreke/Animation_Excited_Walk_M_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/shreke/Animation_Running_withSkin.glb
Normal file
BIN
resources/models/shreke/Animation_Running_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/shreke/Animation_Slow_Orc_Walk_withSkin.glb
Normal file
BIN
resources/models/shreke/Animation_Slow_Orc_Walk_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/shreke/Animation_Unsteady_Walk_withSkin.glb
Normal file
BIN
resources/models/shreke/Animation_Unsteady_Walk_withSkin.glb
Normal file
Binary file not shown.
BIN
resources/models/shreke/Animation_Walking_withSkin.glb
Normal file
BIN
resources/models/shreke/Animation_Walking_withSkin.glb
Normal file
Binary file not shown.
126
segfault.txt
Normal file
126
segfault.txt
Normal file
@ -0,0 +1,126 @@
|
||||
INFO: 2025/02/10 15:53:21 main.go:16: Starting GoonScape client
|
||||
INFO: 2025/02/10 15:53:21 main.go:43: Initializing window
|
||||
INFO: 2025/02/10 15:53:21 main.go:60: Loading game assets
|
||||
INFO: 2025/02/10 15:53:21 game.go:61: Loading game assets
|
||||
INFO: 2025/02/10 15:53:21 assets.go:51: Loading models
|
||||
INFO: 2025/02/10 15:53:21 game.go:81: Music disabled by config
|
||||
INFO: 2025/02/10 15:53:21 game.go:84: Assets loaded successfully
|
||||
INFO: 2025/02/10 15:53:21 main.go:76: Starting game loop
|
||||
INFO: 2025/02/10 15:53:21 game.go:456: Starting game loop
|
||||
malloc(): unsorted double linked list corrupted
|
||||
SIGABRT: abort
|
||||
PC=0x78a790604c4c m=0 sigcode=18446744073709551610
|
||||
signal arrived during cgo execution
|
||||
|
||||
goroutine 1 gp=0xc0000061c0 m=0 mp=0xaf8e20 [syscall, locked to thread]:
|
||||
runtime.cgocall(0x6da9a0, 0xc00002da50)
|
||||
/usr/lib/go/src/runtime/cgocall.go:167 +0x5b fp=0xc00002da28 sp=0xc00002d9f0 pc=0x46d0fb
|
||||
github.com/gen2brain/raylib-go/raylib._Cfunc_EndDrawing()
|
||||
_cgo_gotypes.go:2539 +0x3f fp=0xc00002da50 sp=0xc00002da28 pc=0x51cdbf
|
||||
github.com/gen2brain/raylib-go/raylib.EndDrawing(...)
|
||||
/home/bd/.cache/go/mod/github.com/gen2brain/raylib-go/raylib@v0.0.0-20250109172833-6dbba4f81a9b/rcore.go:464
|
||||
gitea.boner.be/bdnugget/goonscape/game.(*Game).Render.func1()
|
||||
/home/bd/Projects/go/goonscape/game/game.go:259 +0x36 fp=0xc00002da68 sp=0xc00002da50 pc=0x6b6fb6
|
||||
runtime.deferreturn()
|
||||
/usr/lib/go/src/runtime/panic.go:605 +0x5e fp=0xc00002daf8 sp=0xc00002da68 pc=0x4396fe
|
||||
gitea.boner.be/bdnugget/goonscape/game.(*Game).Render(0xc0001ae000)
|
||||
/home/bd/Projects/go/goonscape/game/game.go:265 +0x405 fp=0xc00002dbf8 sp=0xc00002daf8 pc=0x6b27a5
|
||||
gitea.boner.be/bdnugget/goonscape/game.(*Game).Run(0xc0001ae000)
|
||||
/home/bd/Projects/go/goonscape/game/game.go:463 +0x105 fp=0xc00002dcc0 sp=0xc00002dbf8 pc=0x6b3e85
|
||||
main.main()
|
||||
/home/bd/Projects/go/goonscape/main.go:77 +0xb85 fp=0xc00002df50 sp=0xc00002dcc0 pc=0x6b8345
|
||||
runtime.main()
|
||||
/usr/lib/go/src/runtime/proc.go:272 +0x28b fp=0xc00002dfe0 sp=0xc00002df50 pc=0x43d84b
|
||||
runtime.goexit({})
|
||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc00002dfe8 sp=0xc00002dfe0 pc=0x47ac81
|
||||
|
||||
goroutine 2 gp=0xc000006c40 m=nil [force gc (idle)]:
|
||||
runtime.gopark(0xaedc20?, 0xaf8e20?, 0x0?, 0x0?, 0x0?)
|
||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc0000567a8 sp=0xc000056788 pc=0x47356e
|
||||
runtime.goparkunlock(...)
|
||||
/usr/lib/go/src/runtime/proc.go:430
|
||||
runtime.forcegchelper()
|
||||
/usr/lib/go/src/runtime/proc.go:337 +0xb3 fp=0xc0000567e0 sp=0xc0000567a8 pc=0x43db93
|
||||
runtime.goexit({})
|
||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc0000567e8 sp=0xc0000567e0 pc=0x47ac81
|
||||
created by runtime.init.7 in goroutine 1
|
||||
/usr/lib/go/src/runtime/proc.go:325 +0x1a
|
||||
|
||||
goroutine 3 gp=0xc000007180 m=nil [GC sweep wait]:
|
||||
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
|
||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc00006af80 sp=0xc00006af60 pc=0x47356e
|
||||
runtime.goparkunlock(...)
|
||||
/usr/lib/go/src/runtime/proc.go:430
|
||||
runtime.bgsweep(0xc000080000)
|
||||
/usr/lib/go/src/runtime/mgcsweep.go:277 +0x94 fp=0xc00006afc8 sp=0xc00006af80 pc=0x428714
|
||||
runtime.gcenable.gowrap1()
|
||||
/usr/lib/go/src/runtime/mgc.go:204 +0x25 fp=0xc00006afe0 sp=0xc00006afc8 pc=0x41ce25
|
||||
runtime.goexit({})
|
||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc00006afe8 sp=0xc00006afe0 pc=0x47ac81
|
||||
created by runtime.gcenable in goroutine 1
|
||||
/usr/lib/go/src/runtime/mgc.go:204 +0x66
|
||||
|
||||
goroutine 4 gp=0xc000007340 m=nil [GC scavenge wait]:
|
||||
runtime.gopark(0xc000080000?, 0x8e5b60?, 0x1?, 0x0?, 0xc000007340?)
|
||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc000064f78 sp=0xc000064f58 pc=0x47356e
|
||||
runtime.goparkunlock(...)
|
||||
/usr/lib/go/src/runtime/proc.go:430
|
||||
runtime.(*scavengerState).park(0xaf8060)
|
||||
/usr/lib/go/src/runtime/mgcscavenge.go:425 +0x49 fp=0xc000064fa8 sp=0xc000064f78 pc=0x426149
|
||||
runtime.bgscavenge(0xc000080000)
|
||||
/usr/lib/go/src/runtime/mgcscavenge.go:653 +0x3c fp=0xc000064fc8 sp=0xc000064fa8 pc=0x4266bc
|
||||
runtime.gcenable.gowrap2()
|
||||
/usr/lib/go/src/runtime/mgc.go:205 +0x25 fp=0xc000064fe0 sp=0xc000064fc8 pc=0x41cdc5
|
||||
runtime.goexit({})
|
||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc000064fe8 sp=0xc000064fe0 pc=0x47ac81
|
||||
created by runtime.gcenable in goroutine 1
|
||||
/usr/lib/go/src/runtime/mgc.go:205 +0xa5
|
||||
|
||||
goroutine 18 gp=0xc000104700 m=nil [finalizer wait]:
|
||||
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
|
||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc000184e20 sp=0xc000184e00 pc=0x47356e
|
||||
runtime.runfinq()
|
||||
/usr/lib/go/src/runtime/mfinal.go:193 +0x145 fp=0xc000184fe0 sp=0xc000184e20 pc=0x41bea5
|
||||
runtime.goexit({})
|
||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc000184fe8 sp=0xc000184fe0 pc=0x47ac81
|
||||
created by runtime.createfing in goroutine 1
|
||||
/usr/lib/go/src/runtime/mfinal.go:163 +0x3d
|
||||
|
||||
goroutine 19 gp=0xc0001048c0 m=nil [chan receive]:
|
||||
runtime.gopark(0x0?, 0x0?, 0x0?, 0x0?, 0x0?)
|
||||
/usr/lib/go/src/runtime/proc.go:424 +0xce fp=0xc00006bf18 sp=0xc00006bef8 pc=0x47356e
|
||||
runtime.chanrecv(0xc0001140e0, 0x0, 0x1)
|
||||
/usr/lib/go/src/runtime/chan.go:639 +0x3bc fp=0xc00006bf90 sp=0xc00006bf18 pc=0x40c89c
|
||||
runtime.chanrecv1(0x0?, 0x0?)
|
||||
/usr/lib/go/src/runtime/chan.go:489 +0x12 fp=0xc00006bfb8 sp=0xc00006bf90 pc=0x40c4d2
|
||||
runtime.unique_runtime_registerUniqueMapCleanup.func1(...)
|
||||
/usr/lib/go/src/runtime/mgc.go:1781
|
||||
runtime.unique_runtime_registerUniqueMapCleanup.gowrap1()
|
||||
/usr/lib/go/src/runtime/mgc.go:1784 +0x2f fp=0xc00006bfe0 sp=0xc00006bfb8 pc=0x41fe4f
|
||||
runtime.goexit({})
|
||||
/usr/lib/go/src/runtime/asm_amd64.s:1700 +0x1 fp=0xc00006bfe8 sp=0xc00006bfe0 pc=0x47ac81
|
||||
created by unique.runtime_registerUniqueMapCleanup in goroutine 1
|
||||
/usr/lib/go/src/runtime/mgc.go:1779 +0x96
|
||||
|
||||
rax 0x0
|
||||
rbx 0x4829
|
||||
rcx 0x78a790604c4c
|
||||
rdx 0x6
|
||||
rdi 0x4829
|
||||
rsi 0x4829
|
||||
rbp 0x78a7902fc740
|
||||
rsp 0x7ffd63425870
|
||||
r8 0xffffffff
|
||||
r9 0x0
|
||||
r10 0x8
|
||||
r11 0x246
|
||||
r12 0x7ffd634259d0
|
||||
r13 0x6
|
||||
r14 0x7ffd634259d0
|
||||
r15 0x7ffd634259d0
|
||||
rip 0x78a790604c4c
|
||||
rflags 0x246
|
||||
cs 0x33
|
||||
fs 0x0
|
||||
gs 0x0
|
||||
exit status 2
|
@ -1,12 +1,34 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
||||
rl "github.com/gen2brain/raylib-go/raylib"
|
||||
)
|
||||
|
||||
type Player struct {
|
||||
sync.RWMutex
|
||||
PosActual rl.Vector3
|
||||
PosTile Tile
|
||||
TargetPath []Tile
|
||||
ActionQueue []*pb.Action
|
||||
Speed float32
|
||||
ID int32
|
||||
IsMoving bool
|
||||
AnimationFrame int32
|
||||
LastAnimUpdate time.Time
|
||||
LastUpdateTime time.Time
|
||||
InterpolationProgress float32
|
||||
FloatingMessage *FloatingMessage
|
||||
QuitDone chan struct{}
|
||||
UserData interface{}
|
||||
Model rl.Model
|
||||
Texture rl.Texture2D
|
||||
Username string
|
||||
}
|
||||
|
||||
func (p *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) {
|
||||
p.Lock()
|
||||
defer p.Unlock()
|
||||
|
@ -1,7 +1,6 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
pb "gitea.boner.be/bdnugget/goonserver/actions"
|
||||
@ -14,27 +13,6 @@ type Tile struct {
|
||||
Walkable bool
|
||||
}
|
||||
|
||||
type Player struct {
|
||||
sync.Mutex
|
||||
PosActual rl.Vector3
|
||||
PosTile Tile
|
||||
TargetPath []Tile
|
||||
ActionQueue []*pb.Action
|
||||
Speed float32
|
||||
Model rl.Model
|
||||
Texture rl.Texture2D
|
||||
ID int32
|
||||
CurrentTick int64
|
||||
LastUpdateTime time.Time
|
||||
LastAnimUpdate time.Time
|
||||
InterpolationProgress float32
|
||||
UserData interface{}
|
||||
FloatingMessage *FloatingMessage
|
||||
QuitDone chan struct{}
|
||||
AnimationFrame int32
|
||||
IsMoving bool
|
||||
}
|
||||
|
||||
type AnimationSet struct {
|
||||
Idle []rl.ModelAnimation
|
||||
Walk []rl.ModelAnimation
|
||||
@ -50,6 +28,7 @@ type ModelAsset struct {
|
||||
AnimFrames int32 // Keep this for compatibility
|
||||
Animations AnimationSet // New field for organized animations
|
||||
YOffset float32 // Additional height offset (added to default 8.0)
|
||||
Name string
|
||||
}
|
||||
|
||||
type ChatMessage struct {
|
||||
@ -69,6 +48,13 @@ type ChatMessageHandler interface {
|
||||
HandleServerMessages([]*pb.ChatMessage)
|
||||
}
|
||||
|
||||
type PlayerState struct {
|
||||
PlayerId int32
|
||||
X int32
|
||||
Y int32
|
||||
Username string
|
||||
}
|
||||
|
||||
const (
|
||||
MapWidth = 50
|
||||
MapHeight = 50
|
||||
|
Loading…
x
Reference in New Issue
Block a user