1
0

fix conn handling; heartbeat and timeout

This commit is contained in:
Sky Johnson 2025-09-08 19:29:30 -05:00
parent 8d08958280
commit 34bbc39595
9 changed files with 82 additions and 11 deletions

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
*.o
server/players.json
client/game
server/server

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@ -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

View File

@ -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<uint8_t, 5> msg{};
msg[0] = static_cast<uint8_t>(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;

View File

@ -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; }

View File

@ -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
}

View File

@ -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()

View File

@ -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
}
}
}