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
|
||||
|
||||
### 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.
|
||||
|
|
|
|||
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) {
|
||||
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<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);
|
||||
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 == "") {
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
v2.0.3
|
||||
v2.0.4
|
||||
|
|
|
|||
Loading…
Reference in New Issue