make server authoritative on time of day
This commit is contained in:
parent
aec43c0bcf
commit
c39715b84c
@ -228,8 +228,13 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update sky
|
// Update sky with server time if connected, otherwise use local time
|
||||||
sky->update(GetFrameTime());
|
if (network.isConnected()) {
|
||||||
|
float serverTime = network.getServerTimeOfDay();
|
||||||
|
sky->updateFromServerTime(serverTime);
|
||||||
|
} else {
|
||||||
|
sky->update(GetFrameTime());
|
||||||
|
}
|
||||||
|
|
||||||
// Time of day controls (for testing)
|
// Time of day controls (for testing)
|
||||||
if (IsKeyPressed(KEY_T)) {
|
if (IsKeyPressed(KEY_T)) {
|
||||||
|
|||||||
@ -56,11 +56,24 @@ void NetworkManager::processMessage(const uint8_t* data, std::size_t size) {
|
|||||||
case MessageType::LoginResponse:
|
case MessageType::LoginResponse:
|
||||||
handleLoginResponse(data, size);
|
handleLoginResponse(data, size);
|
||||||
break;
|
break;
|
||||||
|
case MessageType::TimeSync:
|
||||||
|
handleTimeSync(data, size);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkManager::handleTimeSync(const uint8_t* data, std::size_t size) {
|
||||||
|
// Message format: [type(1)][timeOfDay(4)]
|
||||||
|
if (size < 5) return;
|
||||||
|
|
||||||
|
float timeOfDay;
|
||||||
|
std::memcpy(&timeOfDay, &data[1], sizeof(timeOfDay));
|
||||||
|
|
||||||
|
serverTimeOfDay.store(timeOfDay);
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkManager::handleSpawn(const uint8_t* data, std::size_t size) {
|
void NetworkManager::handleSpawn(const uint8_t* data, std::size_t size) {
|
||||||
// Message format: [type(1)][id(4)][x(4)][y(4)][z(4)][colorLen(1)][color(colorLen)]
|
// Message format: [type(1)][id(4)][x(4)][y(4)][z(4)][colorLen(1)][color(colorLen)]
|
||||||
if (size < 18) return;
|
if (size < 18) return;
|
||||||
|
|||||||
@ -25,7 +25,8 @@ enum class MessageType : uint8_t {
|
|||||||
ColorChanged = 0x0A,
|
ColorChanged = 0x0A,
|
||||||
LoginResponse = 0x0B,
|
LoginResponse = 0x0B,
|
||||||
Logout = 0x0C,
|
Logout = 0x0C,
|
||||||
Heartbeat = 0x0D
|
Heartbeat = 0x0D,
|
||||||
|
TimeSync = 0x0E
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RemotePlayer {
|
struct RemotePlayer {
|
||||||
@ -55,6 +56,7 @@ public:
|
|||||||
std::string getPlayerColor() const { return playerColor; }
|
std::string getPlayerColor() const { return playerColor; }
|
||||||
|
|
||||||
std::unordered_map<uint32_t, RemotePlayer> getRemotePlayers();
|
std::unordered_map<uint32_t, RemotePlayer> getRemotePlayers();
|
||||||
|
float getServerTimeOfDay() const { return serverTimeOfDay.load(); }
|
||||||
|
|
||||||
// Available colors for cycling
|
// Available colors for cycling
|
||||||
static const std::vector<std::string> AVAILABLE_COLORS;
|
static const std::vector<std::string> AVAILABLE_COLORS;
|
||||||
@ -76,6 +78,7 @@ private:
|
|||||||
|
|
||||||
std::mutex remotePlayersMutex;
|
std::mutex remotePlayersMutex;
|
||||||
std::unordered_map<uint32_t, RemotePlayer> remotePlayers;
|
std::unordered_map<uint32_t, RemotePlayer> remotePlayers;
|
||||||
|
std::atomic<float> serverTimeOfDay{0.0f};
|
||||||
|
|
||||||
void startReceive();
|
void startReceive();
|
||||||
void processMessage(const uint8_t* data, std::size_t size);
|
void processMessage(const uint8_t* data, std::size_t size);
|
||||||
@ -86,4 +89,5 @@ private:
|
|||||||
void handlePlayerList(const uint8_t* data, std::size_t size);
|
void handlePlayerList(const uint8_t* data, std::size_t size);
|
||||||
void handleColorChanged(const uint8_t* data, std::size_t size);
|
void handleColorChanged(const uint8_t* data, std::size_t size);
|
||||||
void handleLoginResponse(const uint8_t* data, std::size_t size);
|
void handleLoginResponse(const uint8_t* data, std::size_t size);
|
||||||
|
void handleTimeSync(const uint8_t* data, std::size_t size);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -75,6 +75,13 @@ void Sky::update(float) {
|
|||||||
moon.updatePosition(timeOfDay);
|
moon.updatePosition(timeOfDay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Sky::updateFromServerTime(float serverTimeOfDay) {
|
||||||
|
timeOfDay = serverTimeOfDay;
|
||||||
|
updateColors();
|
||||||
|
sun.updatePosition(timeOfDay);
|
||||||
|
moon.updatePosition(timeOfDay);
|
||||||
|
}
|
||||||
|
|
||||||
void Sky::Sun::updatePosition(float timeOfDay) {
|
void Sky::Sun::updatePosition(float timeOfDay) {
|
||||||
// Convert time of day to angle (0.25 = sunrise at 6am, 0.75 = sunset at 6pm)
|
// Convert time of day to angle (0.25 = sunrise at 6am, 0.75 = sunset at 6pm)
|
||||||
// Adjust so that 0.25 (6am) = 0 degrees, 0.75 (6pm) = 180 degrees
|
// Adjust so that 0.25 (6am) = 0 degrees, 0.75 (6pm) = 180 degrees
|
||||||
|
|||||||
@ -57,6 +57,7 @@ public:
|
|||||||
~Sky();
|
~Sky();
|
||||||
|
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
|
void updateFromServerTime(float serverTimeOfDay);
|
||||||
void render(const Camera3D& camera);
|
void render(const Camera3D& camera);
|
||||||
void renderSkybox(const Camera3D& camera); // Render skybox that follows camera
|
void renderSkybox(const Camera3D& camera); // Render skybox that follows camera
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ const (
|
|||||||
MSG_LOGIN_RESPONSE = 0x0B
|
MSG_LOGIN_RESPONSE = 0x0B
|
||||||
MSG_LOGOUT = 0x0C
|
MSG_LOGOUT = 0x0C
|
||||||
MSG_HEARTBEAT = 0x0D
|
MSG_HEARTBEAT = 0x0D
|
||||||
|
MSG_TIME_SYNC = 0x0E
|
||||||
)
|
)
|
||||||
|
|
||||||
// Vec3 represents a 3D vector
|
// Vec3 represents a 3D vector
|
||||||
@ -194,4 +195,12 @@ func DecodeHeartbeatPacket(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
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeTimeSyncPacket creates a time sync packet
|
||||||
|
func EncodeTimeSyncPacket(timeOfDay float32) []byte {
|
||||||
|
msg := make([]byte, 5)
|
||||||
|
msg[0] = MSG_TIME_SYNC
|
||||||
|
binary.LittleEndian.PutUint32(msg[1:5], math.Float32bits(timeOfDay))
|
||||||
|
return msg
|
||||||
}
|
}
|
||||||
@ -40,6 +40,8 @@ type Server struct {
|
|||||||
heightmap [][]float32
|
heightmap [][]float32
|
||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
nextID uint32
|
nextID uint32
|
||||||
|
timeOfDay float32
|
||||||
|
startTime time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new game server
|
// NewServer creates a new game server
|
||||||
@ -61,6 +63,8 @@ func NewServer(port string, heightmap [][]float32) (*Server, error) {
|
|||||||
userData: make(map[string]*UserData),
|
userData: make(map[string]*UserData),
|
||||||
heightmap: heightmap,
|
heightmap: heightmap,
|
||||||
nextID: 0,
|
nextID: 0,
|
||||||
|
timeOfDay: 0.0,
|
||||||
|
startTime: time.Now(),
|
||||||
}
|
}
|
||||||
|
|
||||||
server.loadUserData()
|
server.loadUserData()
|
||||||
@ -90,6 +94,15 @@ func (s *Server) Run() error {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Start time of day updater and broadcaster
|
||||||
|
go func() {
|
||||||
|
ticker := time.NewTicker(100 * time.Millisecond) // Update time 10 times per second
|
||||||
|
defer ticker.Stop()
|
||||||
|
for range ticker.C {
|
||||||
|
s.updateAndBroadcastTime()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
buffer := make([]byte, 1024)
|
buffer := make([]byte, 1024)
|
||||||
log.Println("Server running...")
|
log.Println("Server running...")
|
||||||
|
|
||||||
@ -121,6 +134,29 @@ func (s *Server) Run() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateAndBroadcastTime updates the server time and broadcasts it to all clients
|
||||||
|
func (s *Server) updateAndBroadcastTime() {
|
||||||
|
// Calculate elapsed time since server start
|
||||||
|
elapsed := time.Since(s.startTime).Seconds()
|
||||||
|
|
||||||
|
// Day cycle duration in seconds (e.g., 10 minutes = 600 seconds)
|
||||||
|
dayDuration := 600.0
|
||||||
|
|
||||||
|
// Calculate time of day (0.0 to 1.0, where 0.5 is noon)
|
||||||
|
s.timeOfDay = float32(math.Mod(elapsed/dayDuration, 1.0))
|
||||||
|
|
||||||
|
// Broadcast to all connected players
|
||||||
|
msg := EncodeTimeSyncPacket(s.timeOfDay)
|
||||||
|
|
||||||
|
s.mutex.RLock()
|
||||||
|
for _, p := range s.players {
|
||||||
|
if p.Address != nil {
|
||||||
|
s.conn.WriteToUDP(msg, p.Address)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.mutex.RUnlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) handleLogin(data []byte, addr *net.UDPAddr) {
|
func (s *Server) handleLogin(data []byte, addr *net.UDPAddr) {
|
||||||
username, ok := DecodeLoginPacket(data)
|
username, ok := DecodeLoginPacket(data)
|
||||||
if !ok || username == "" {
|
if !ok || username == "" {
|
||||||
@ -202,6 +238,10 @@ func (s *Server) handleLogin(data []byte, addr *net.UDPAddr) {
|
|||||||
listMsg := EncodePlayerListPacket(existingPlayers)
|
listMsg := EncodePlayerListPacket(existingPlayers)
|
||||||
s.conn.WriteToUDP(listMsg, addr)
|
s.conn.WriteToUDP(listMsg, addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send current time of day to new player
|
||||||
|
timeMsg := EncodeTimeSyncPacket(s.timeOfDay)
|
||||||
|
s.conn.WriteToUDP(timeMsg, addr)
|
||||||
|
|
||||||
// Notify other players about new player
|
// Notify other players about new player
|
||||||
s.broadcastPlayerJoined(player)
|
s.broadcastPlayerJoined(player)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user