diff --git a/.gitignore b/.gitignore index 8840fe9..9cd8624 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ *.o - server/players.json client/game +server/server diff --git a/assets/heightmap.bin b/assets/heightmap.bin index 9647afc..9416b45 100644 Binary files a/assets/heightmap.bin and b/assets/heightmap.bin differ diff --git a/assets/heightmap.png b/assets/heightmap.png index 38acdbb..41d7688 100644 Binary files a/assets/heightmap.png and b/assets/heightmap.png differ diff --git a/client/main.cpp b/client/main.cpp index b56cc57..f7f6540 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -123,6 +123,10 @@ class Game { bool editMode = false; std::string loginError = ""; std::string currentUsername = ""; + + // Heartbeat timing + float lastHeartbeatTime = 0.0f; + const float HEARTBEAT_INTERVAL = 5.0f; // Send heartbeat every 5 seconds public: Game() { @@ -261,6 +265,14 @@ private: // Send normalized movement direction to server (server handles speed) if (moveInput.x != 0 || moveInput.z != 0) { network.sendMove(moveInput.x, 0, moveInput.z); + lastHeartbeatTime = GetTime(); // Reset heartbeat timer when moving + } + + // Send periodic heartbeats when not moving + float currentTime = GetTime(); + if (currentTime - lastHeartbeatTime >= HEARTBEAT_INTERVAL) { + network.sendHeartbeat(); + lastHeartbeatTime = currentTime; } // Handle color change with arrow keys diff --git a/client/net/NetworkManager.cpp b/client/net/NetworkManager.cpp index 37733f1..7175ef1 100644 --- a/client/net/NetworkManager.cpp +++ b/client/net/NetworkManager.cpp @@ -250,6 +250,16 @@ void NetworkManager::sendColorChange(const std::string& newColor) { socket.send_to(buffer(msg), serverEndpoint); } +void NetworkManager::sendHeartbeat() { + if (!connected) return; + + std::array msg{}; + msg[0] = static_cast(MessageType::Heartbeat); + uint32_t id = playerID; + std::memcpy(&msg[1], &id, sizeof(id)); + socket.send_to(buffer(msg), serverEndpoint); +} + Vector3 NetworkManager::getPosition() { std::lock_guard lock(positionMutex); return serverPosition; diff --git a/client/net/NetworkManager.hpp b/client/net/NetworkManager.hpp index db2fd8a..d91a60b 100644 --- a/client/net/NetworkManager.hpp +++ b/client/net/NetworkManager.hpp @@ -24,7 +24,8 @@ enum class MessageType : uint8_t { ChangeColor = 0x09, ColorChanged = 0x0A, LoginResponse = 0x0B, - Logout = 0x0C + Logout = 0x0C, + Heartbeat = 0x0D }; struct RemotePlayer { @@ -44,6 +45,7 @@ public: void sendLogout(); void sendMove(float dx, float dy, float dz); void sendColorChange(const std::string& newColor); + void sendHeartbeat(); Vector3 getPosition(); bool isConnected() const { return connected; } diff --git a/server/net/packets.go b/server/net/packets.go index 895a7d8..feb2811 100644 --- a/server/net/packets.go +++ b/server/net/packets.go @@ -19,6 +19,7 @@ const ( MSG_COLOR_CHANGED = 0x0A MSG_LOGIN_RESPONSE = 0x0B MSG_LOGOUT = 0x0C + MSG_HEARTBEAT = 0x0D ) // Vec3 represents a 3D vector @@ -181,6 +182,16 @@ func DecodeLogoutPacket(data []byte) (playerID uint32, ok bool) { return 0, false } + playerID = binary.LittleEndian.Uint32(data[1:5]) + return playerID, true +} + +// DecodeHeartbeatPacket decodes a heartbeat packet +func DecodeHeartbeatPacket(data []byte) (playerID uint32, ok bool) { + if len(data) < 5 { + return 0, false + } + playerID = binary.LittleEndian.Uint32(data[1:5]) return playerID, true } \ No newline at end of file diff --git a/server/net/server.go b/server/net/server.go index 730d8b0..69f3320 100644 --- a/server/net/server.go +++ b/server/net/server.go @@ -83,7 +83,7 @@ func (s *Server) Run() error { // Start player timeout checker go func() { - ticker := time.NewTicker(5 * time.Second) + ticker := time.NewTicker(10 * time.Second) defer ticker.Stop() for range ticker.C { s.checkTimeouts() @@ -115,6 +115,8 @@ func (s *Server) Run() error { s.handleColorChange(buffer[:n], addr) case MSG_LOGOUT: s.handleLogout(buffer[:n], addr) + case MSG_HEARTBEAT: + s.handleHeartbeat(buffer[:n], addr) } } } @@ -210,7 +212,7 @@ func (s *Server) handleLogin(data []byte, addr *net.UDPAddr) { s.saveUserData() } -func (s *Server) handleMove(data []byte, _ *net.UDPAddr) { +func (s *Server) handleMove(data []byte, addr *net.UDPAddr) { playerID, delta, ok := DecodeMovePacket(data) if !ok { return @@ -223,6 +225,9 @@ func (s *Server) handleMove(data []byte, _ *net.UDPAddr) { return } + // Update the player's address in case it changed (NAT, port change, etc.) + player.Address = addr + // Server-authoritative movement deltaTime := float32(0.016) // 60fps newX := player.Position.X + delta.X*15.0*deltaTime @@ -289,7 +294,7 @@ func (s *Server) handleLogout(data []byte, _ *net.UDPAddr) { s.saveUserData() } -func (s *Server) handleColorChange(data []byte, _ *net.UDPAddr) { +func (s *Server) handleColorChange(data []byte, addr *net.UDPAddr) { playerID, newColor, ok := DecodeColorChangePacket(data) if !ok { return @@ -310,6 +315,9 @@ func (s *Server) handleColorChange(data []byte, _ *net.UDPAddr) { return } + // Update the player's address and last seen time + player.Address = addr + player.LastSeen = time.Now() player.Color = newColor // Update persistent user data @@ -404,6 +412,25 @@ func (s *Server) broadcastColorChanged(playerID uint32, color string) { s.mutex.RUnlock() } +func (s *Server) handleHeartbeat(data []byte, addr *net.UDPAddr) { + playerID, ok := DecodeHeartbeatPacket(data) + if !ok { + return + } + + s.mutex.Lock() + player, exists := s.players[playerID] + if !exists { + s.mutex.Unlock() + return + } + + // Update the player's address and last seen time + player.Address = addr + player.LastSeen = time.Now() + s.mutex.Unlock() +} + func (s *Server) checkTimeouts() { s.mutex.Lock() defer s.mutex.Unlock() diff --git a/server/players.json b/server/players.json index fdb0455..c5a26b5 100644 --- a/server/players.json +++ b/server/players.json @@ -1,20 +1,29 @@ { + "foo": { + "username": "foo", + "color": "orange", + "position": { + "X": -25.698553, + "Y": 4.9015007, + "Z": 39.29553 + } + }, "sky": { "username": "sky", "color": "purple", "position": { - "X": 3.9765263, - "Y": -5.033655, - "Z": 19.824585 + "X": 5.9400105, + "Y": -0.055064563, + "Z": -3.896852 } }, "test": { "username": "test", "color": "red", "position": { - "X": 14.141681, - "Y": 2.2383373, - "Z": -0.06184849 + "X": 2.1930797, + "Y": -0.08324346, + "Z": -1.550154 } } } \ No newline at end of file