diff --git a/go.mod b/go.mod index f484b26..f4413b9 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module goonscape go 1.23.0 require ( - gitea.boner.be/bdnugget/goonserver v0.0.0-20241011122434-4bd5303cfd46 + gitea.boner.be/bdnugget/goonserver v0.0.0-20241011195320-f16e8647dc6b github.com/gen2brain/raylib-go/raylib v0.0.0-20240930075631-c66f9e2942fe google.golang.org/protobuf v1.35.1 ) diff --git a/go.sum b/go.sum index 63d861e..868728f 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -gitea.boner.be/bdnugget/goonserver v0.0.0-20241011122434-4bd5303cfd46 h1:T2D4QcmvBqzGoHO0VJGNUd1k2lLmUcyg6Rc/vN4/Im8= -gitea.boner.be/bdnugget/goonserver v0.0.0-20241011122434-4bd5303cfd46/go.mod h1:inR1bKrr/vcTba+G1KzmmY6vssMq9oGNOk836VwPa4c= +gitea.boner.be/bdnugget/goonserver v0.0.0-20241011195320-f16e8647dc6b h1:hdhCZH0YGqCsnSl6ru+8I7rxvCyOj5pCtf92urwyruA= +gitea.boner.be/bdnugget/goonserver v0.0.0-20241011195320-f16e8647dc6b/go.mod h1:inR1bKrr/vcTba+G1KzmmY6vssMq9oGNOk836VwPa4c= github.com/ebitengine/purego v0.8.0 h1:JbqvnEzRvPpxhCJzJJ2y0RbiZ8nyjccVUrSM3q+GvvE= github.com/ebitengine/purego v0.8.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/gen2brain/raylib-go/raylib v0.0.0-20240930075631-c66f9e2942fe h1:mInjrbJkUglTM7tBmXG+epnPCE744aj15J7vjJwM4gs= diff --git a/main.go b/main.go index c75aaad..f62dea1 100644 --- a/main.go +++ b/main.go @@ -25,6 +25,7 @@ var ( cameraDistance = float32(20.0) cameraYaw = float32(145.0) cameraPitch = float32(45.0) // Adjusted for a more overhead view + mapGrid = InitMap() ) type Tile struct { @@ -52,6 +53,7 @@ type Player struct { ActionQueue []Action // Queue for player actions Model rl.Model Texture rl.Texture2D + ID int32 } // Initialize the map with some height data @@ -71,7 +73,7 @@ func InitMap() [][]Tile { return mapGrid } -func DrawMap(mapGrid [][]Tile) { +func DrawMap() { for x := 0; x < MapWidth; x++ { for y := 0; y < MapHeight; y++ { tile := mapGrid[x][y] @@ -99,15 +101,15 @@ func DrawMap(mapGrid [][]Tile) { } } -func DrawPlayer(player Player, model *rl.Model, mapGrid [][]Tile) { +func DrawPlayer(player Player, model rl.Model) { // Draw the player based on its actual position (PosActual) and current tile height playerPos := rl.Vector3{ X: player.PosActual.X, Y: mapGrid[player.PosTile.X][player.PosTile.Y].Height*TileHeight + 16.0, Z: player.PosActual.Z, } - // rl.DrawCube(playerPos, 16, 16, 16, rl.Green) // Draw player cube - rl.DrawModel(*model, playerPos, 16, rl.White) + + rl.DrawModel(model, playerPos, 16, rl.White) // Draw highlight around target tile if len(player.TargetPath) > 0 { @@ -170,7 +172,7 @@ func RayIntersectsBox(ray rl.Ray, boxMin, boxMax rl.Vector3) bool { return true } -func GetTileAtMouse(mapGrid [][]Tile, camera *rl.Camera3D) (Tile, bool) { +func GetTileAtMouse(camera *rl.Camera3D) (Tile, bool) { if !rl.IsMouseButtonPressed(rl.MouseLeftButton) { return Tile{}, false } @@ -196,7 +198,7 @@ func GetTileAtMouse(mapGrid [][]Tile, camera *rl.Camera3D) (Tile, bool) { return Tile{}, false } -func (player *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Tile) { +func (player *Player) MoveTowards(target Tile, deltaTime float32) { // Calculate the direction vector to the target tile targetPos := rl.Vector3{ X: float32(target.X * TileSize), @@ -222,10 +224,10 @@ func (player *Player) MoveTowards(target Tile, deltaTime float32, mapGrid [][]Ti } } -func HandleInput(player *Player, mapGrid [][]Tile, camera *rl.Camera) { - clickedTile, clicked := GetTileAtMouse(mapGrid, camera) +func HandleInput(player *Player, camera *rl.Camera) { + clickedTile, clicked := GetTileAtMouse(camera) if clicked { - path := FindPath(mapGrid, mapGrid[player.PosTile.X][player.PosTile.Y], clickedTile) + path := FindPath(mapGrid[player.PosTile.X][player.PosTile.Y], clickedTile) if path != nil { // Exclude the first tile (current position) if len(path) > 1 { @@ -289,8 +291,6 @@ func main() { rl.InitAudioDevice() defer rl.CloseAudioDevice() - mapGrid := InitMap() - player := Player{ PosActual: rl.NewVector3(5*TileSize, 0, 5*TileSize), PosTile: mapGrid[5][5], @@ -311,9 +311,12 @@ func main() { log.Fatalf("Failed to connect to server: %v", err) } log.Printf("Player ID: %d", playerID) + player.ID = playerID defer conn.Close() - go HandleServerCommunication(conn, playerID, &player) + otherPlayers := make(map[int32]*Player) + + go HandleServerCommunication(conn, playerID, &player, otherPlayers) goonerModel := rl.LoadModel("resources/models/goonion.obj") defer rl.UnloadModel(goonerModel) @@ -362,11 +365,11 @@ func main() { deltaTime := rl.GetFrameTime() // Handle input - HandleInput(&player, mapGrid, &camera) + HandleInput(&player, &camera) // Update player if len(player.TargetPath) > 0 { - player.MoveTowards(player.TargetPath[0], deltaTime, mapGrid) + player.MoveTowards(player.TargetPath[0], deltaTime) } // Update camera @@ -376,12 +379,15 @@ func main() { rl.BeginDrawing() rl.ClearBackground(rl.RayWhite) rl.BeginMode3D(camera) - DrawMap(mapGrid) - DrawPlayer(player, &player.Model, mapGrid) + DrawMap() + DrawPlayer(player, player.Model) - rl.DrawFPS(10, 10) + for id, other := range otherPlayers { + DrawPlayer(*other, models[int(id)%len(models)].Model) + } rl.EndMode3D() + rl.DrawFPS(10, 10) rl.EndDrawing() } } @@ -416,39 +422,76 @@ func ConnectToServer() (net.Conn, int32, error) { return conn, playerID, nil } -func HandleServerCommunication(conn net.Conn, playerID int32, player *Player) { +func HandleServerCommunication(conn net.Conn, playerID int32, player *Player, otherPlayers map[int32]*Player) { + // Goroutine to handle sending player's actions to the server + go func() { + for { + if len(player.ActionQueue) > 0 { + // Process the first action in the queue + actionData := player.ActionQueue[0] + action := &pb.Action{ + PlayerId: playerID, + Type: pb.Action_MOVE, + X: int32(actionData.X), + Y: int32(actionData.Y), + } + + // Serialize the action + data, err := proto.Marshal(action) + if err != nil { + log.Printf("Failed to marshal action: %v", err) + continue + } + + // Send action to server + _, err = conn.Write(data) + if err != nil { + log.Printf("Failed to send action to server: %v", err) + return + } + + // Remove the action from the queue once it's sent + player.ActionQueue = player.ActionQueue[1:] + } + + // Add a delay to match the server's tick rate + time.Sleep(TickRate) + } + }() + + // Main loop to handle receiving updates from the server for { - // Check if there are actions in the player's queue - if len(player.ActionQueue) > 0 { - // Process the first action in the queue - actionData := player.ActionQueue[0] - action := &pb.Action{ - PlayerId: playerID, - Type: pb.Action_MOVE, - X: int32(actionData.X), - Y: int32(actionData.Y), - } - - // Serialize the action - data, err := proto.Marshal(action) - if err != nil { - log.Printf("Failed to marshal action: %v", err) - continue - } - - // Send action to server - _, err = conn.Write(data) - if err != nil { - log.Printf("Failed to send action to server: %v", err) - return - } - - // Remove the action from the queue once it's sent - player.ActionQueue = player.ActionQueue[1:] + buf := make([]byte, 4096) + n, err := conn.Read(buf) + if err != nil { + log.Printf("Failed to read from server: %v", err) + return } - // Add a delay based on the server's tick rate - time.Sleep(TickRate) + var serverMessage pb.ServerMessage + if err := proto.Unmarshal(buf[:n], &serverMessage); err != nil { + log.Printf("Failed to unmarshal server message: %v", err) + continue + } + + // Update other players' states + for _, state := range serverMessage.Players { + if state.PlayerId != playerID { + if otherPlayer, exists := otherPlayers[state.PlayerId]; exists { + otherPlayer.PosTile = Tile{X: int(state.X), Y: int(state.Y)} + } else { + otherPlayers[state.PlayerId] = &Player{ + PosTile: Tile{X: int(state.X), Y: int(state.Y)}, + PosActual: rl.Vector3{ + X: float32(state.X * TileSize), + Y: float32(state.Y * TileHeight), + Z: float32(state.Y * TileSize), + }, + ID: state.PlayerId, + } + } + } + } } } @@ -459,7 +502,7 @@ type Node struct { G, H, F float32 } -func FindPath(mapGrid [][]Tile, start, end Tile) []Tile { +func FindPath(start, end Tile) []Tile { openList := []*Node{} closedList := make(map[[2]int]bool) @@ -495,7 +538,7 @@ func FindPath(mapGrid [][]Tile, start, end Tile) []Tile { } // Generate neighbors - neighbors := GetNeighbors(mapGrid, current.Tile) + neighbors := GetNeighbors(current.Tile) for _, neighbor := range neighbors { if !neighbor.Walkable || closedList[[2]int{neighbor.X, neighbor.Y}] { continue @@ -542,7 +585,7 @@ func distance(a, b Tile) float32 { return 1.0 //uniform cost for now } -func GetNeighbors(mapGrid [][]Tile, tile Tile) []Tile { +func GetNeighbors(tile Tile) []Tile { directions := [][2]int{ {1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {-1, -1}, {1, -1}, {-1, 1}, diff --git a/resources/models/shreke.png b/resources/models/shreke.png index 34184cb..f2849f1 100644 Binary files a/resources/models/shreke.png and b/resources/models/shreke.png differ