diff --git a/README.md b/README.md index c7e2aba..f9805a7 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,12 @@ MorriCraft is a high-performance voxel engine built with C++ and Raylib. It feat ## 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. * **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. diff --git a/build-linux/MorriCraft b/build-linux/MorriCraft index 308c391..6c6ee72 100755 Binary files a/build-linux/MorriCraft and b/build-linux/MorriCraft differ diff --git a/build-linux/assets/version.txt b/build-linux/assets/version.txt index f3b15f3..8a5b818 100644 --- a/build-linux/assets/version.txt +++ b/build-linux/assets/version.txt @@ -1 +1 @@ -v2.0.2 +v2.0.4 diff --git a/build-windows/MorriCraft.exe b/build-windows/MorriCraft.exe index 7007bf4..2cc28f1 100755 Binary files a/build-windows/MorriCraft.exe and b/build-windows/MorriCraft.exe differ diff --git a/build-windows/assets/version.txt b/build-windows/assets/version.txt index f3b15f3..8a5b818 100644 --- a/build-windows/assets/version.txt +++ b/build-windows/assets/version.txt @@ -1 +1 @@ -v2.0.2 +v2.0.4 diff --git a/src/main.cpp b/src/main.cpp index 101109e..9081131 100644 --- a/src/main.cpp +++ b/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) { if (y < 0 || y >= CHUNK_HEIGHT) return; 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. SetConfigFlags(FLAG_WINDOW_RESIZABLE | FLAG_VSYNC_HINT); - InitWindow(screenWidth, screenHeight, "MorriCraft v2.0.3"); + InitWindow(screenWidth, screenHeight, "MorriCraft v2.0.4"); LoadConfig(); SetExitKey(KEY_NULL); // Prevent ESC from closing the window @@ -967,7 +984,6 @@ int main(void) // Handle incoming data auto handleIncoming = [&](Socket sock, bool isServer, int clientIdx = -1) { while (true) { - // Use a quick non-blocking check to see if more data is available fd_set readfds; FD_ZERO(&readfds); FD_SET(sock, &readfds); @@ -1005,7 +1021,7 @@ int main(void) if (head.type == PACKET_HANDSHAKE) { PacketHandshake hand; - recv(sock, (char*)&hand, sizeof(hand), 0); + if (RecvAll(sock, (char*)&hand, sizeof(hand)) <= 0) break; if (isServer) { bool duplicate = false; 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) { PacketPlayerUpdate pu; - recv(sock, (char*)&pu, sizeof(pu), 0); + if (RecvAll(sock, (char*)&pu, sizeof(pu)) <= 0) break; bool found = false; for (auto& rp : remotePlayers) { if (rp.id == pu.playerID) { @@ -1083,7 +1099,7 @@ int main(void) } } else if (head.type == PACKET_BLOCK_CHANGE) { 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); if (isServer) { for (auto& other : clientSockets) { @@ -1095,11 +1111,11 @@ int main(void) } } else if (head.type == PACKET_TIME_SYNC) { PacketTimeSync ts; - recv(sock, (char*)&ts, sizeof(ts), 0); + if (RecvAll(sock, (char*)&ts, sizeof(ts)) <= 0) break; if (!isServer) gameTime = ts.gameTime; } else if (head.type == PACKET_SEED_SYNC) { PacketSeedSync ss; - recv(sock, (char*)&ss, sizeof(ss), 0); + if (RecvAll(sock, (char*)&ss, sizeof(ss)) <= 0) break; if (!isServer) { globalSeedHash = ss.seed; for (auto& pair : worldChunks) delete pair.second; @@ -1107,7 +1123,7 @@ int main(void) } } else if (head.type == PACKET_CHAT) { 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 }); if (isServer) { for (auto& other : clientSockets) { @@ -1120,7 +1136,7 @@ int main(void) } else { if (head.size > 0 && head.size < 2048) { std::vector 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); EndMode2D(); - // Show Version Number (v2.0.3) in Red - DrawTextEx(customFont, "v2.0.3", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED); + // Show Version Number (v2.0.4) in Red + DrawTextEx(customFont, "v2.0.4", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED); // --- PLAYER NAME POPUP (IF MISSING) --- if (playerName == "") { diff --git a/version.txt b/version.txt index f256be6..8a5b818 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -v2.0.3 +v2.0.4