Implement world state synchronization - v2.0.1
This commit is contained in:
parent
dea13eaf2c
commit
99e63720fb
Binary file not shown.
Binary file not shown.
149
src/main.cpp
149
src/main.cpp
|
|
@ -105,6 +105,12 @@ struct RemotePlayer {
|
|||
};
|
||||
static std::vector<RemotePlayer> remotePlayers;
|
||||
|
||||
// Global Networking State
|
||||
static Socket clientSocket = INVALID_SOCKET_VAL;
|
||||
static Socket serverSocket = INVALID_SOCKET_VAL;
|
||||
static std::vector<Socket> clientSockets;
|
||||
static bool isConnecting = false;
|
||||
|
||||
// ---- Inventory System ----
|
||||
struct InventorySlot {
|
||||
int blockType = AIR;
|
||||
|
|
@ -228,6 +234,23 @@ void RebuildChunkRenderList(Chunk* chunk, int cx, int cz) {
|
|||
chunk->dirty = false;
|
||||
}
|
||||
|
||||
void NetSetBlock(int x, int y, int z, int type) {
|
||||
SetBlock(x, y, z, type);
|
||||
PacketHeader head = { (uint8_t)PACKET_BLOCK_CHANGE, (uint32_t)sizeof(PacketBlockChange) };
|
||||
PacketBlockChange bc = { x, y, z, type };
|
||||
|
||||
if (clientSocket != INVALID_SOCKET_VAL) {
|
||||
send(clientSocket, (char*)&head, sizeof(head), 0);
|
||||
send(clientSocket, (char*)&bc, sizeof(bc), 0);
|
||||
}
|
||||
if (serverSocket != INVALID_SOCKET_VAL) {
|
||||
for (auto& s : clientSockets) {
|
||||
send(s, (char*)&head, sizeof(head), 0);
|
||||
send(s, (char*)&bc, sizeof(bc), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -703,7 +726,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.0");
|
||||
InitWindow(screenWidth, screenHeight, "MorriCraft v2.0.1");
|
||||
LoadConfig();
|
||||
SetExitKey(KEY_NULL); // Prevent ESC from closing the window
|
||||
|
||||
|
|
@ -761,14 +784,10 @@ int main(void)
|
|||
vfile.close();
|
||||
}
|
||||
|
||||
// Networking State
|
||||
// Networking State (Target IP/Port remain local for UI)
|
||||
char targetIP[128] = "127.0.0.1";
|
||||
char targetPort[16] = "12345";
|
||||
int activeNetField = 0; // 0=none, 1=IP, 2=Port
|
||||
bool isConnecting = false;
|
||||
Socket clientSocket = INVALID_SOCKET_VAL;
|
||||
Socket serverSocket = INVALID_SOCKET_VAL;
|
||||
std::vector<Socket> clientSockets;
|
||||
|
||||
InitNetworking();
|
||||
InventorySlot mouseHeldItem(AIR, 0);
|
||||
|
|
@ -923,6 +942,7 @@ int main(void)
|
|||
if (select((int)clientSocket + 1, NULL, &writefds, NULL, &tv) > 0) {
|
||||
isConnecting = false;
|
||||
currentState = GAMEPLAY;
|
||||
DisableCursor();
|
||||
// Send Handshake
|
||||
PacketHeader head = { (uint8_t)PACKET_HANDSHAKE, (uint32_t)sizeof(PacketHandshake) };
|
||||
PacketHandshake hand;
|
||||
|
|
@ -932,6 +952,100 @@ int main(void)
|
|||
}
|
||||
}
|
||||
|
||||
// Handle incoming data
|
||||
auto handleIncoming = [&](Socket sock, bool isServer, int clientIdx = -1) {
|
||||
PacketHeader head;
|
||||
int bytes = recv(sock, (char*)&head, sizeof(head), 0);
|
||||
if (bytes > 0) {
|
||||
if (head.type == PACKET_HANDSHAKE) {
|
||||
PacketHandshake hand;
|
||||
recv(sock, (char*)&hand, sizeof(hand), 0);
|
||||
if (isServer) {
|
||||
RemotePlayer rp;
|
||||
rp.sock = sock;
|
||||
rp.name = hand.name;
|
||||
rp.position = (Vector3){0,0,0};
|
||||
remotePlayers.push_back(rp);
|
||||
// Send seed to new client
|
||||
PacketHeader sHead = { (uint8_t)PACKET_SEED_SYNC, (uint32_t)sizeof(PacketSeedSync) };
|
||||
PacketSeedSync sData = { globalSeedHash };
|
||||
send(sock, (char*)&sHead, sizeof(sHead), 0);
|
||||
send(sock, (char*)&sData, sizeof(sData), 0);
|
||||
}
|
||||
} else if (head.type == PACKET_PLAYER_UPDATE) {
|
||||
PacketPlayerUpdate pu;
|
||||
recv(sock, (char*)&pu, sizeof(pu), 0);
|
||||
for (auto& rp : remotePlayers) {
|
||||
if (rp.sock == sock) {
|
||||
rp.position = (Vector3){ pu.x, pu.y, pu.z };
|
||||
rp.yaw = pu.yaw;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isServer) {
|
||||
// Broadcast to others
|
||||
for (auto& other : clientSockets) {
|
||||
if (other != sock) {
|
||||
send(other, (char*)&head, sizeof(head), 0);
|
||||
send(other, (char*)&pu, sizeof(pu), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (head.type == PACKET_BLOCK_CHANGE) {
|
||||
PacketBlockChange bc;
|
||||
recv(sock, (char*)&bc, sizeof(bc), 0);
|
||||
SetBlock(bc.x, bc.y, bc.z, bc.blockType);
|
||||
if (isServer) {
|
||||
for (auto& other : clientSockets) {
|
||||
if (other != sock) {
|
||||
send(other, (char*)&head, sizeof(head), 0);
|
||||
send(other, (char*)&bc, sizeof(bc), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (head.type == PACKET_TIME_SYNC) {
|
||||
PacketTimeSync ts;
|
||||
recv(sock, (char*)&ts, sizeof(ts), 0);
|
||||
if (!isServer) gameTime = ts.gameTime;
|
||||
} else if (head.type == PACKET_SEED_SYNC) {
|
||||
PacketSeedSync ss;
|
||||
recv(sock, (char*)&ss, sizeof(ss), 0);
|
||||
if (!isServer) {
|
||||
globalSeedHash = ss.seed;
|
||||
// Regerate world if seed changed
|
||||
for (auto& pair : worldChunks) delete pair.second;
|
||||
worldChunks.clear();
|
||||
}
|
||||
}
|
||||
} else if (bytes == 0 || (bytes < 0 && SOCKET_ERROR_VAL != -1)) {
|
||||
// Disconnect handling
|
||||
if (isServer && clientIdx != -1) {
|
||||
for (auto it = remotePlayers.begin(); it != remotePlayers.end(); ++it) {
|
||||
if (it->sock == sock) {
|
||||
remotePlayers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
closesocket(sock);
|
||||
clientSockets.erase(clientSockets.begin() + clientIdx);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (clientSocket != INVALID_SOCKET_VAL && !isConnecting) {
|
||||
handleIncoming(clientSocket, false);
|
||||
// Send our position
|
||||
static float netTimer = 0.0f;
|
||||
netTimer += GetFrameTime();
|
||||
if (netTimer > 0.05f) { // 20Hz update
|
||||
PacketHeader head = { (uint8_t)PACKET_PLAYER_UPDATE, (uint32_t)sizeof(PacketPlayerUpdate) };
|
||||
PacketPlayerUpdate pu = { camera3D.position.x, camera3D.position.y - 1.6f, camera3D.position.z, camYaw, 0 };
|
||||
send(clientSocket, (char*)&head, sizeof(head), 0);
|
||||
send(clientSocket, (char*)&pu, sizeof(pu), 0);
|
||||
netTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (serverSocket != INVALID_SOCKET_VAL) {
|
||||
struct sockaddr_in client_addr;
|
||||
socklen_t client_len = sizeof(client_addr);
|
||||
|
|
@ -940,6 +1054,21 @@ int main(void)
|
|||
SetNonBlocking(newClient);
|
||||
clientSockets.push_back(newClient);
|
||||
}
|
||||
for (size_t i = 0; i < clientSockets.size(); i++) {
|
||||
handleIncoming(clientSockets[i], true, i);
|
||||
}
|
||||
// Send time sync periodically
|
||||
static float timeSyncTimer = 0.0f;
|
||||
timeSyncTimer += GetFrameTime();
|
||||
if (timeSyncTimer > 2.0f) {
|
||||
PacketHeader head = { (uint8_t)PACKET_TIME_SYNC, (uint32_t)sizeof(PacketTimeSync) };
|
||||
PacketTimeSync ts = { gameTime };
|
||||
for (auto& s : clientSockets) {
|
||||
send(s, (char*)&head, sizeof(head), 0);
|
||||
send(s, (char*)&ts, sizeof(ts), 0);
|
||||
}
|
||||
timeSyncTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle title music loop fading
|
||||
|
|
@ -1187,7 +1316,7 @@ int main(void)
|
|||
|
||||
if (breakProgress >= breakSpeed) {
|
||||
AddToInventory(targetBlock);
|
||||
SetBlock(hitX, hitY, hitZ, AIR);
|
||||
NetSetBlock(hitX, hitY, hitZ, AIR);
|
||||
breakProgress = 0.0f;
|
||||
isSwinging = true; // Swing when finishing
|
||||
}
|
||||
|
|
@ -1208,7 +1337,7 @@ int main(void)
|
|||
int placeX = hitX + (int)roundf(closestNormal.x);
|
||||
int placeY = hitY + (int)roundf(closestNormal.y);
|
||||
int placeZ = hitZ + (int)roundf(closestNormal.z);
|
||||
SetBlock(placeX, placeY, placeZ, hotbar[activeHotbarSlot].blockType);
|
||||
NetSetBlock(placeX, placeY, placeZ, hotbar[activeHotbarSlot].blockType);
|
||||
hotbar[activeHotbarSlot].count--;
|
||||
if (hotbar[activeHotbarSlot].count == 0)
|
||||
hotbar[activeHotbarSlot].blockType = AIR;
|
||||
|
|
@ -1418,8 +1547,8 @@ int main(void)
|
|||
DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE);
|
||||
EndMode2D();
|
||||
|
||||
// Show Version Number (v2.0.0) in Red
|
||||
DrawTextEx(customFont, "v2.0.0", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
|
||||
// Show Version Number (v2.0.1) in Red
|
||||
DrawTextEx(customFont, "v2.0.1", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
|
||||
|
||||
// --- PLAYER NAME POPUP (IF MISSING) ---
|
||||
if (playerName == "") {
|
||||
|
|
|
|||
|
|
@ -50,8 +50,10 @@
|
|||
enum PacketType {
|
||||
PACKET_HANDSHAKE = 0,
|
||||
PACKET_PLAYER_UPDATE = 1,
|
||||
PACKET_CHUNK_DATA = 2,
|
||||
PACKET_DISCONNECT = 3
|
||||
PACKET_BLOCK_CHANGE = 2,
|
||||
PACKET_TIME_SYNC = 3,
|
||||
PACKET_SEED_SYNC = 4,
|
||||
PACKET_DISCONNECT = 5
|
||||
};
|
||||
|
||||
struct PacketHeader {
|
||||
|
|
@ -66,6 +68,20 @@ struct PacketHandshake {
|
|||
struct PacketPlayerUpdate {
|
||||
float x, y, z;
|
||||
float yaw;
|
||||
uint32_t playerID; // Assigned by server
|
||||
};
|
||||
|
||||
struct PacketBlockChange {
|
||||
int x, y, z;
|
||||
int blockType;
|
||||
};
|
||||
|
||||
struct PacketTimeSync {
|
||||
float gameTime;
|
||||
};
|
||||
|
||||
struct PacketSeedSync {
|
||||
int seed;
|
||||
};
|
||||
|
||||
// Cross-platform socket initialization
|
||||
|
|
|
|||
Loading…
Reference in New Issue