fix conn handling; heartbeat and timeout
This commit is contained in:
parent
8d08958280
commit
34bbc39595
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
*.o
|
*.o
|
||||||
|
|
||||||
server/players.json
|
server/players.json
|
||||||
client/game
|
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 |
@ -124,6 +124,10 @@ class Game {
|
|||||||
std::string loginError = "";
|
std::string loginError = "";
|
||||||
std::string currentUsername = "";
|
std::string currentUsername = "";
|
||||||
|
|
||||||
|
// Heartbeat timing
|
||||||
|
float lastHeartbeatTime = 0.0f;
|
||||||
|
const float HEARTBEAT_INTERVAL = 5.0f; // Send heartbeat every 5 seconds
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Game() {
|
Game() {
|
||||||
InitWindow(1280, 720, "Multiplayer Terrain Game");
|
InitWindow(1280, 720, "Multiplayer Terrain Game");
|
||||||
@ -261,6 +265,14 @@ private:
|
|||||||
// Send normalized movement direction to server (server handles speed)
|
// Send normalized movement direction to server (server handles speed)
|
||||||
if (moveInput.x != 0 || moveInput.z != 0) {
|
if (moveInput.x != 0 || moveInput.z != 0) {
|
||||||
network.sendMove(moveInput.x, 0, moveInput.z);
|
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
|
// Handle color change with arrow keys
|
||||||
|
|||||||
@ -250,6 +250,16 @@ void NetworkManager::sendColorChange(const std::string& newColor) {
|
|||||||
socket.send_to(buffer(msg), serverEndpoint);
|
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() {
|
Vector3 NetworkManager::getPosition() {
|
||||||
std::lock_guard lock(positionMutex);
|
std::lock_guard lock(positionMutex);
|
||||||
return serverPosition;
|
return serverPosition;
|
||||||
|
|||||||
@ -24,7 +24,8 @@ enum class MessageType : uint8_t {
|
|||||||
ChangeColor = 0x09,
|
ChangeColor = 0x09,
|
||||||
ColorChanged = 0x0A,
|
ColorChanged = 0x0A,
|
||||||
LoginResponse = 0x0B,
|
LoginResponse = 0x0B,
|
||||||
Logout = 0x0C
|
Logout = 0x0C,
|
||||||
|
Heartbeat = 0x0D
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RemotePlayer {
|
struct RemotePlayer {
|
||||||
@ -44,6 +45,7 @@ public:
|
|||||||
void sendLogout();
|
void sendLogout();
|
||||||
void sendMove(float dx, float dy, float dz);
|
void sendMove(float dx, float dy, float dz);
|
||||||
void sendColorChange(const std::string& newColor);
|
void sendColorChange(const std::string& newColor);
|
||||||
|
void sendHeartbeat();
|
||||||
|
|
||||||
Vector3 getPosition();
|
Vector3 getPosition();
|
||||||
bool isConnected() const { return connected; }
|
bool isConnected() const { return connected; }
|
||||||
|
|||||||
@ -19,6 +19,7 @@ const (
|
|||||||
MSG_COLOR_CHANGED = 0x0A
|
MSG_COLOR_CHANGED = 0x0A
|
||||||
MSG_LOGIN_RESPONSE = 0x0B
|
MSG_LOGIN_RESPONSE = 0x0B
|
||||||
MSG_LOGOUT = 0x0C
|
MSG_LOGOUT = 0x0C
|
||||||
|
MSG_HEARTBEAT = 0x0D
|
||||||
)
|
)
|
||||||
|
|
||||||
// Vec3 represents a 3D vector
|
// Vec3 represents a 3D vector
|
||||||
@ -184,3 +185,13 @@ func DecodeLogoutPacket(data []byte) (playerID uint32, ok bool) {
|
|||||||
playerID = binary.LittleEndian.Uint32(data[1:5])
|
playerID = binary.LittleEndian.Uint32(data[1:5])
|
||||||
return playerID, true
|
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
|
||||||
|
}
|
||||||
@ -83,7 +83,7 @@ func (s *Server) Run() error {
|
|||||||
|
|
||||||
// Start player timeout checker
|
// Start player timeout checker
|
||||||
go func() {
|
go func() {
|
||||||
ticker := time.NewTicker(5 * time.Second)
|
ticker := time.NewTicker(10 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
for range ticker.C {
|
for range ticker.C {
|
||||||
s.checkTimeouts()
|
s.checkTimeouts()
|
||||||
@ -115,6 +115,8 @@ func (s *Server) Run() error {
|
|||||||
s.handleColorChange(buffer[:n], addr)
|
s.handleColorChange(buffer[:n], addr)
|
||||||
case MSG_LOGOUT:
|
case MSG_LOGOUT:
|
||||||
s.handleLogout(buffer[:n], addr)
|
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()
|
s.saveUserData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleMove(data []byte, _ *net.UDPAddr) {
|
func (s *Server) handleMove(data []byte, addr *net.UDPAddr) {
|
||||||
playerID, delta, ok := DecodeMovePacket(data)
|
playerID, delta, ok := DecodeMovePacket(data)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@ -223,6 +225,9 @@ func (s *Server) handleMove(data []byte, _ *net.UDPAddr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the player's address in case it changed (NAT, port change, etc.)
|
||||||
|
player.Address = addr
|
||||||
|
|
||||||
// Server-authoritative movement
|
// Server-authoritative movement
|
||||||
deltaTime := float32(0.016) // 60fps
|
deltaTime := float32(0.016) // 60fps
|
||||||
newX := player.Position.X + delta.X*15.0*deltaTime
|
newX := player.Position.X + delta.X*15.0*deltaTime
|
||||||
@ -289,7 +294,7 @@ func (s *Server) handleLogout(data []byte, _ *net.UDPAddr) {
|
|||||||
s.saveUserData()
|
s.saveUserData()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) handleColorChange(data []byte, _ *net.UDPAddr) {
|
func (s *Server) handleColorChange(data []byte, addr *net.UDPAddr) {
|
||||||
playerID, newColor, ok := DecodeColorChangePacket(data)
|
playerID, newColor, ok := DecodeColorChangePacket(data)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
@ -310,6 +315,9 @@ func (s *Server) handleColorChange(data []byte, _ *net.UDPAddr) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the player's address and last seen time
|
||||||
|
player.Address = addr
|
||||||
|
player.LastSeen = time.Now()
|
||||||
player.Color = newColor
|
player.Color = newColor
|
||||||
|
|
||||||
// Update persistent user data
|
// Update persistent user data
|
||||||
@ -404,6 +412,25 @@ func (s *Server) broadcastColorChanged(playerID uint32, color string) {
|
|||||||
s.mutex.RUnlock()
|
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() {
|
func (s *Server) checkTimeouts() {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
defer s.mutex.Unlock()
|
defer s.mutex.Unlock()
|
||||||
|
|||||||
@ -1,20 +1,29 @@
|
|||||||
{
|
{
|
||||||
|
"foo": {
|
||||||
|
"username": "foo",
|
||||||
|
"color": "orange",
|
||||||
|
"position": {
|
||||||
|
"X": -25.698553,
|
||||||
|
"Y": 4.9015007,
|
||||||
|
"Z": 39.29553
|
||||||
|
}
|
||||||
|
},
|
||||||
"sky": {
|
"sky": {
|
||||||
"username": "sky",
|
"username": "sky",
|
||||||
"color": "purple",
|
"color": "purple",
|
||||||
"position": {
|
"position": {
|
||||||
"X": 3.9765263,
|
"X": 5.9400105,
|
||||||
"Y": -5.033655,
|
"Y": -0.055064563,
|
||||||
"Z": 19.824585
|
"Z": -3.896852
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"test": {
|
"test": {
|
||||||
"username": "test",
|
"username": "test",
|
||||||
"color": "red",
|
"color": "red",
|
||||||
"position": {
|
"position": {
|
||||||
"X": 14.141681,
|
"X": 2.1930797,
|
||||||
"Y": 2.2383373,
|
"Y": -0.08324346,
|
||||||
"Z": -0.06184849
|
"Z": -1.550154
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user