Implement Full Synchronization Milestone - v2.0.4
This commit is contained in:
parent
fbf45cc41c
commit
70b417b87c
|
|
@ -13,7 +13,12 @@ MorriCraft is a high-performance voxel engine built with C++ and Raylib. It feat
|
||||||
|
|
||||||
## Version History
|
## Version History
|
||||||
|
|
||||||
### v2.0.3 - Multiplayer Protocol Hardening (Latest)
|
### v2.0.4 - Full Synchronization Milestone (Latest)
|
||||||
|
* **Robust Data Streaming**: Implemented `RecvAll` with multi-platform error handling (Winsock/POSIX) to guarantee entire packet bodies are read.
|
||||||
|
* **Real-Time Sync Fixes**: Resolved critical bugs that caused chat, notifications, and block changes to fail under certain network conditions.
|
||||||
|
* **Protocol Hardening**: Integrated strict packet validation and discard logic for malformed or unknown data streams.
|
||||||
|
|
||||||
|
### v2.0.3 - Multiplayer Protocol Hardening
|
||||||
* **Protocol Synchronization**: Overhauled the packet processing loop to prevent data drift and 'ghosting' blocks.
|
* **Protocol Synchronization**: Overhauled the packet processing loop to prevent data drift and 'ghosting' blocks.
|
||||||
* **Notification Debouncing**: Implemented filters to ensure join/leave messages are only triggered once per action.
|
* **Notification Debouncing**: Implemented filters to ensure join/leave messages are only triggered once per action.
|
||||||
* **Cross-Platform Stability**: Replaced non-portable socket flags with a robust `select()`-based architecture for Windows/Linux parity.
|
* **Cross-Platform Stability**: Replaced non-portable socket flags with a robust `select()`-based architecture for Windows/Linux parity.
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
v2.0.2
|
v2.0.4
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -1 +1 @@
|
||||||
v2.0.2
|
v2.0.4
|
||||||
|
|
|
||||||
38
src/main.cpp
38
src/main.cpp
|
|
@ -258,6 +258,23 @@ void NetSetBlock(int x, int y, int z, int type) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RecvAll(Socket s, char* buf, int len) {
|
||||||
|
int total = 0;
|
||||||
|
while (total < len) {
|
||||||
|
int r = recv(s, buf + total, len - total, 0);
|
||||||
|
if (r <= 0) {
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (r < 0 && WSAGetLastError() == WSAEWOULDBLOCK) continue;
|
||||||
|
#else
|
||||||
|
if (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) continue;
|
||||||
|
#endif
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
total += r;
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
void SetBlock(int x, int y, int z, int type) {
|
void SetBlock(int x, int y, int z, int type) {
|
||||||
if (y < 0 || y >= CHUNK_HEIGHT) return;
|
if (y < 0 || y >= CHUNK_HEIGHT) return;
|
||||||
int cx = (int)floorf((float)x / CHUNK_SIZE);
|
int cx = (int)floorf((float)x / CHUNK_SIZE);
|
||||||
|
|
@ -733,7 +750,7 @@ int main(void)
|
||||||
// By default, windows have minimize, maximize, and close buttons on the top bar.
|
// By default, windows have minimize, maximize, and close buttons on the top bar.
|
||||||
SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT);
|
SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT);
|
||||||
|
|
||||||
InitWindow(screenWidth, screenHeight, "MorriCraft v2.0.3");
|
InitWindow(screenWidth, screenHeight, "MorriCraft v2.0.4");
|
||||||
LoadConfig();
|
LoadConfig();
|
||||||
SetExitKey(KEY_NULL); // Prevent ESC from closing the window
|
SetExitKey(KEY_NULL); // Prevent ESC from closing the window
|
||||||
|
|
||||||
|
|
@ -967,7 +984,6 @@ int main(void)
|
||||||
// Handle incoming data
|
// Handle incoming data
|
||||||
auto handleIncoming = [&](Socket sock, bool isServer, int clientIdx = -1) {
|
auto handleIncoming = [&](Socket sock, bool isServer, int clientIdx = -1) {
|
||||||
while (true) {
|
while (true) {
|
||||||
// Use a quick non-blocking check to see if more data is available
|
|
||||||
fd_set readfds;
|
fd_set readfds;
|
||||||
FD_ZERO(&readfds);
|
FD_ZERO(&readfds);
|
||||||
FD_SET(sock, &readfds);
|
FD_SET(sock, &readfds);
|
||||||
|
|
@ -1005,7 +1021,7 @@ int main(void)
|
||||||
|
|
||||||
if (head.type == PACKET_HANDSHAKE) {
|
if (head.type == PACKET_HANDSHAKE) {
|
||||||
PacketHandshake hand;
|
PacketHandshake hand;
|
||||||
recv(sock, (char*)&hand, sizeof(hand), 0);
|
if (RecvAll(sock, (char*)&hand, sizeof(hand)) <= 0) break;
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
bool duplicate = false;
|
bool duplicate = false;
|
||||||
for (auto& rp : remotePlayers) { if (rp.sock == sock) { duplicate = true; break; } }
|
for (auto& rp : remotePlayers) { if (rp.sock == sock) { duplicate = true; break; } }
|
||||||
|
|
@ -1054,7 +1070,7 @@ int main(void)
|
||||||
}
|
}
|
||||||
} else if (head.type == PACKET_PLAYER_UPDATE) {
|
} else if (head.type == PACKET_PLAYER_UPDATE) {
|
||||||
PacketPlayerUpdate pu;
|
PacketPlayerUpdate pu;
|
||||||
recv(sock, (char*)&pu, sizeof(pu), 0);
|
if (RecvAll(sock, (char*)&pu, sizeof(pu)) <= 0) break;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (auto& rp : remotePlayers) {
|
for (auto& rp : remotePlayers) {
|
||||||
if (rp.id == pu.playerID) {
|
if (rp.id == pu.playerID) {
|
||||||
|
|
@ -1083,7 +1099,7 @@ int main(void)
|
||||||
}
|
}
|
||||||
} else if (head.type == PACKET_BLOCK_CHANGE) {
|
} else if (head.type == PACKET_BLOCK_CHANGE) {
|
||||||
PacketBlockChange bc;
|
PacketBlockChange bc;
|
||||||
recv(sock, (char*)&bc, sizeof(bc), 0);
|
if (RecvAll(sock, (char*)&bc, sizeof(bc)) <= 0) break;
|
||||||
SetBlock(bc.x, bc.y, bc.z, bc.blockType);
|
SetBlock(bc.x, bc.y, bc.z, bc.blockType);
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
for (auto& other : clientSockets) {
|
for (auto& other : clientSockets) {
|
||||||
|
|
@ -1095,11 +1111,11 @@ int main(void)
|
||||||
}
|
}
|
||||||
} else if (head.type == PACKET_TIME_SYNC) {
|
} else if (head.type == PACKET_TIME_SYNC) {
|
||||||
PacketTimeSync ts;
|
PacketTimeSync ts;
|
||||||
recv(sock, (char*)&ts, sizeof(ts), 0);
|
if (RecvAll(sock, (char*)&ts, sizeof(ts)) <= 0) break;
|
||||||
if (!isServer) gameTime = ts.gameTime;
|
if (!isServer) gameTime = ts.gameTime;
|
||||||
} else if (head.type == PACKET_SEED_SYNC) {
|
} else if (head.type == PACKET_SEED_SYNC) {
|
||||||
PacketSeedSync ss;
|
PacketSeedSync ss;
|
||||||
recv(sock, (char*)&ss, sizeof(ss), 0);
|
if (RecvAll(sock, (char*)&ss, sizeof(ss)) <= 0) break;
|
||||||
if (!isServer) {
|
if (!isServer) {
|
||||||
globalSeedHash = ss.seed;
|
globalSeedHash = ss.seed;
|
||||||
for (auto& pair : worldChunks) delete pair.second;
|
for (auto& pair : worldChunks) delete pair.second;
|
||||||
|
|
@ -1107,7 +1123,7 @@ int main(void)
|
||||||
}
|
}
|
||||||
} else if (head.type == PACKET_CHAT) {
|
} else if (head.type == PACKET_CHAT) {
|
||||||
PacketChat pc;
|
PacketChat pc;
|
||||||
recv(sock, (char*)&pc, sizeof(pc), 0);
|
if (RecvAll(sock, (char*)&pc, sizeof(pc)) <= 0) break;
|
||||||
chatLog.push_back({ std::string(pc.name) + ": " + pc.message, 5.0f });
|
chatLog.push_back({ std::string(pc.name) + ": " + pc.message, 5.0f });
|
||||||
if (isServer) {
|
if (isServer) {
|
||||||
for (auto& other : clientSockets) {
|
for (auto& other : clientSockets) {
|
||||||
|
|
@ -1120,7 +1136,7 @@ int main(void)
|
||||||
} else {
|
} else {
|
||||||
if (head.size > 0 && head.size < 2048) {
|
if (head.size > 0 && head.size < 2048) {
|
||||||
std::vector<char> discard(head.size);
|
std::vector<char> discard(head.size);
|
||||||
recv(sock, discard.data(), head.size, 0);
|
RecvAll(sock, discard.data(), head.size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1710,8 +1726,8 @@ int main(void)
|
||||||
DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE);
|
DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE);
|
||||||
EndMode2D();
|
EndMode2D();
|
||||||
|
|
||||||
// Show Version Number (v2.0.3) in Red
|
// Show Version Number (v2.0.4) in Red
|
||||||
DrawTextEx(customFont, "v2.0.3", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
|
DrawTextEx(customFont, "v2.0.4", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
|
||||||
|
|
||||||
// --- PLAYER NAME POPUP (IF MISSING) ---
|
// --- PLAYER NAME POPUP (IF MISSING) ---
|
||||||
if (playerName == "") {
|
if (playerName == "") {
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
v2.0.3
|
v2.0.4
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue