Finalize Multiplayer v2.0.2 with Chat and Sync fixes
This commit is contained in:
parent
99e63720fb
commit
dbb6662538
Binary file not shown.
Binary file not shown.
192
src/main.cpp
192
src/main.cpp
|
|
@ -98,13 +98,20 @@ void LoadConfig();
|
|||
std::string GetRemoteVersion();
|
||||
|
||||
struct RemotePlayer {
|
||||
Socket sock;
|
||||
Socket sock; // Server only
|
||||
uint32_t id;
|
||||
std::string name;
|
||||
Vector3 position;
|
||||
float yaw;
|
||||
};
|
||||
static std::vector<RemotePlayer> remotePlayers;
|
||||
|
||||
struct ChatMessage {
|
||||
std::string text;
|
||||
float timer;
|
||||
};
|
||||
static std::vector<ChatMessage> chatLog;
|
||||
|
||||
// Global Networking State
|
||||
static Socket clientSocket = INVALID_SOCKET_VAL;
|
||||
static Socket serverSocket = INVALID_SOCKET_VAL;
|
||||
|
|
@ -726,7 +733,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.1");
|
||||
InitWindow(screenWidth, screenHeight, "MorriCraft v2.0.2");
|
||||
LoadConfig();
|
||||
SetExitKey(KEY_NULL); // Prevent ESC from closing the window
|
||||
|
||||
|
|
@ -791,6 +798,11 @@ int main(void)
|
|||
|
||||
InitNetworking();
|
||||
InventorySlot mouseHeldItem(AIR, 0);
|
||||
|
||||
// Chat State
|
||||
bool isChatting = false;
|
||||
char chatInput[128] = {0};
|
||||
uint32_t myNetID = 0; // Assigned by server if client
|
||||
float gameTime = 75.0f; // Start at 6:00 AM
|
||||
float breakProgress = 0.0f;
|
||||
int lastHitX = -1, lastHitY = -1, lastHitZ = -1;
|
||||
|
|
@ -961,29 +973,73 @@ int main(void)
|
|||
PacketHandshake hand;
|
||||
recv(sock, (char*)&hand, sizeof(hand), 0);
|
||||
if (isServer) {
|
||||
uint32_t newID = (uint32_t)sock;
|
||||
RemotePlayer rp;
|
||||
rp.sock = sock;
|
||||
rp.id = newID;
|
||||
rp.name = hand.name;
|
||||
rp.position = (Vector3){0,0,0};
|
||||
remotePlayers.push_back(rp);
|
||||
// Send seed to new client
|
||||
|
||||
// Notify others
|
||||
chatLog.push_back({ std::string(hand.name) + " joined the game", 5.0f });
|
||||
PacketHeader nHead = { (uint8_t)PACKET_CHAT, (uint32_t)sizeof(PacketChat) };
|
||||
PacketChat nChat;
|
||||
strncpy(nChat.name, "Server", 31);
|
||||
strncpy(nChat.message, (std::string(hand.name) + " joined the game").c_str(), 127);
|
||||
for (auto& s : clientSockets) {
|
||||
send(s, (char*)&nHead, sizeof(nHead), 0);
|
||||
send(s, (char*)&nChat, sizeof(nChat), 0);
|
||||
}
|
||||
|
||||
// Send seed and time to new client
|
||||
PacketHeader sHead = { (uint8_t)PACKET_SEED_SYNC, (uint32_t)sizeof(PacketSeedSync) };
|
||||
PacketSeedSync sData = { globalSeedHash };
|
||||
PacketSeedSync sData = { (int)globalSeedHash };
|
||||
send(sock, (char*)&sHead, sizeof(sHead), 0);
|
||||
send(sock, (char*)&sData, sizeof(sData), 0);
|
||||
|
||||
PacketHeader tHead = { (uint8_t)PACKET_TIME_SYNC, (uint32_t)sizeof(PacketTimeSync) };
|
||||
PacketTimeSync tData = { gameTime };
|
||||
send(sock, (char*)&tHead, sizeof(tHead), 0);
|
||||
send(sock, (char*)&tData, sizeof(tData), 0);
|
||||
|
||||
// Send existing players to new client
|
||||
for (auto& rp : remotePlayers) {
|
||||
if (rp.sock != sock) {
|
||||
PacketHeader pHead = { (uint8_t)PACKET_PLAYER_UPDATE, (uint32_t)sizeof(PacketPlayerUpdate) };
|
||||
PacketPlayerUpdate pData = { rp.position.x, rp.position.y, rp.position.z, rp.yaw, rp.id };
|
||||
send(sock, (char*)&pHead, sizeof(pHead), 0);
|
||||
send(sock, (char*)&pData, sizeof(pData), 0);
|
||||
}
|
||||
}
|
||||
// Send HOST to new client (Host ID is always 0)
|
||||
PacketHeader hHead = { (uint8_t)PACKET_PLAYER_UPDATE, (uint32_t)sizeof(PacketPlayerUpdate) };
|
||||
PacketPlayerUpdate hData = { camera3D.position.x, camera3D.position.y - 1.6f, camera3D.position.z, camYaw, 0 };
|
||||
send(sock, (char*)&hHead, sizeof(hHead), 0);
|
||||
send(sock, (char*)&hData, sizeof(hData), 0);
|
||||
}
|
||||
} else if (head.type == PACKET_PLAYER_UPDATE) {
|
||||
PacketPlayerUpdate pu;
|
||||
recv(sock, (char*)&pu, sizeof(pu), 0);
|
||||
bool found = false;
|
||||
for (auto& rp : remotePlayers) {
|
||||
if (rp.sock == sock) {
|
||||
if (rp.id == pu.playerID) {
|
||||
rp.position = (Vector3){ pu.x, pu.y, pu.z };
|
||||
rp.yaw = pu.yaw;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found && !isServer) {
|
||||
RemotePlayer rp;
|
||||
rp.id = pu.playerID;
|
||||
rp.name = "Remote Player";
|
||||
rp.position = (Vector3){ pu.x, pu.y, pu.z };
|
||||
rp.yaw = pu.yaw;
|
||||
remotePlayers.push_back(rp);
|
||||
}
|
||||
if (isServer) {
|
||||
// Broadcast to others
|
||||
pu.playerID = (uint32_t)sock; // Ensure ID is correct
|
||||
for (auto& other : clientSockets) {
|
||||
if (other != sock) {
|
||||
send(other, (char*)&head, sizeof(head), 0);
|
||||
|
|
@ -1016,16 +1072,41 @@ int main(void)
|
|||
for (auto& pair : worldChunks) delete pair.second;
|
||||
worldChunks.clear();
|
||||
}
|
||||
} else if (head.type == PACKET_CHAT) {
|
||||
PacketChat pc;
|
||||
recv(sock, (char*)&pc, sizeof(pc), 0);
|
||||
chatLog.push_back({ std::string(pc.name) + ": " + pc.message, 5.0f });
|
||||
if (isServer) {
|
||||
for (auto& other : clientSockets) {
|
||||
if (other != sock) {
|
||||
send(other, (char*)&head, sizeof(head), 0);
|
||||
send(other, (char*)&pc, sizeof(pc), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (bytes == 0 || (bytes < 0 && SOCKET_ERROR_VAL != -1)) {
|
||||
// Disconnect handling
|
||||
if (isServer && clientIdx != -1) {
|
||||
std::string leaver = "A player";
|
||||
for (auto it = remotePlayers.begin(); it != remotePlayers.end(); ++it) {
|
||||
if (it->sock == sock) {
|
||||
leaver = it->name;
|
||||
remotePlayers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
chatLog.push_back({ leaver + " left the game", 5.0f });
|
||||
PacketHeader nHead = { (uint8_t)PACKET_CHAT, (uint32_t)sizeof(PacketChat) };
|
||||
PacketChat nChat;
|
||||
strncpy(nChat.name, "Server", 31);
|
||||
strncpy(nChat.message, (leaver + " left the game").c_str(), 127);
|
||||
for (auto& s : clientSockets) {
|
||||
if (s != sock) {
|
||||
send(s, (char*)&nHead, sizeof(nHead), 0);
|
||||
send(s, (char*)&nChat, sizeof(nChat), 0);
|
||||
}
|
||||
}
|
||||
closesocket(sock);
|
||||
clientSockets.erase(clientSockets.begin() + clientIdx);
|
||||
}
|
||||
|
|
@ -1069,6 +1150,18 @@ int main(void)
|
|||
}
|
||||
timeSyncTimer = 0.0f;
|
||||
}
|
||||
// Send Host position periodically
|
||||
static float hostNetTimer = 0.0f;
|
||||
hostNetTimer += GetFrameTime();
|
||||
if (hostNetTimer > 0.05f) {
|
||||
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 };
|
||||
for (auto& s : clientSockets) {
|
||||
send(s, (char*)&head, sizeof(head), 0);
|
||||
send(s, (char*)&pu, sizeof(pu), 0);
|
||||
}
|
||||
hostNetTimer = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
// Handle title music loop fading
|
||||
|
|
@ -1162,8 +1255,53 @@ int main(void)
|
|||
}
|
||||
}
|
||||
|
||||
// ---- Manual Camera System (Look + WASD) ----
|
||||
if (!inventoryOpen) {
|
||||
// Handle chat input
|
||||
if (IsKeyPressed(KEY_ENTER)) {
|
||||
if (isChatting) {
|
||||
if (strlen(chatInput) > 0) {
|
||||
// Send Chat Packet
|
||||
PacketHeader head = { (uint8_t)PACKET_CHAT, (uint32_t)sizeof(PacketChat) };
|
||||
PacketChat pc;
|
||||
strncpy(pc.name, playerName.c_str(), 31);
|
||||
strncpy(pc.message, chatInput, 127);
|
||||
if (clientSocket != INVALID_SOCKET_VAL) {
|
||||
send(clientSocket, (char*)&head, sizeof(head), 0);
|
||||
send(clientSocket, (char*)&pc, sizeof(pc), 0);
|
||||
}
|
||||
if (serverSocket != INVALID_SOCKET_VAL) {
|
||||
chatLog.push_back({ std::string(playerName) + ": " + chatInput, 5.0f });
|
||||
for (auto& s : clientSockets) {
|
||||
send(s, (char*)&head, sizeof(head), 0);
|
||||
send(s, (char*)&pc, sizeof(pc), 0);
|
||||
}
|
||||
}
|
||||
chatInput[0] = '\0';
|
||||
}
|
||||
isChatting = false;
|
||||
DisableCursor();
|
||||
} else {
|
||||
isChatting = true;
|
||||
EnableCursor();
|
||||
}
|
||||
}
|
||||
|
||||
if (isChatting) {
|
||||
int c = GetCharPressed();
|
||||
while (c > 0) {
|
||||
if (c >= 32 && c <= 125 && strlen(chatInput) < 120) {
|
||||
int len = strlen(chatInput);
|
||||
chatInput[len] = (char)c;
|
||||
chatInput[len+1] = '\0';
|
||||
}
|
||||
c = GetCharPressed();
|
||||
}
|
||||
if (IsKeyPressed(KEY_BACKSPACE)) {
|
||||
int len = strlen(chatInput);
|
||||
if (len > 0) chatInput[len-1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (!inventoryOpen && !isChatting) {
|
||||
const float MOUSE_SENS = 0.002f;
|
||||
Vector2 md = GetMouseDelta();
|
||||
camYaw -= md.x * MOUSE_SENS;
|
||||
|
|
@ -1452,9 +1590,21 @@ int main(void)
|
|||
DrawRectangleLines(currentWidth/2 - bw/2, currentHeight/2, bw, bh, GRAY);
|
||||
|
||||
if (downloadProgress >= 1.0f) {
|
||||
// Simulate restart
|
||||
// Update local version file to stop update prompts
|
||||
std::ofstream vout("version.txt");
|
||||
if (vout.is_open()) {
|
||||
vout << latestVersion;
|
||||
vout.close();
|
||||
}
|
||||
std::ofstream vout2("assets/version.txt");
|
||||
if (vout2.is_open()) {
|
||||
vout2 << latestVersion;
|
||||
vout2.close();
|
||||
}
|
||||
|
||||
currentState = MAIN_MENU;
|
||||
updateReady = false;
|
||||
downloadProgress = 0.0f;
|
||||
}
|
||||
} else if (currentState == CONNECT_MENU) {
|
||||
DrawRectangle(0, 0, currentWidth, currentHeight, (Color){ 0, 0, 0, 220 });
|
||||
|
|
@ -1547,8 +1697,8 @@ int main(void)
|
|||
DrawTexturePro(titleTexture, sourceRec, destRec, origin, 0.0f, WHITE);
|
||||
EndMode2D();
|
||||
|
||||
// Show Version Number (v2.0.1) in Red
|
||||
DrawTextEx(customFont, "v2.0.1", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
|
||||
// Show Version Number (v2.0.2) in Red
|
||||
DrawTextEx(customFont, "v2.0.2", (Vector2){ (float)currentWidth - 140, (float)currentHeight - 40 }, 22, 1.0f, RED);
|
||||
|
||||
// --- PLAYER NAME POPUP (IF MISSING) ---
|
||||
if (playerName == "") {
|
||||
|
|
@ -2291,6 +2441,26 @@ int main(void)
|
|||
DrawTextEx(customFont, TextFormat("Time: %i:%02i %s", displayHours, minutes, ampm), (Vector2){ 10, 110 }, 18, 1.0f, (dayFactor > 0.5f) ? BLACK : WHITE);
|
||||
DrawTextEx(customFont, (dayFactor > 0.5f) ? "Status: Day" : "Status: Night", (Vector2){ 10, 130 }, 16, 1.0f, (dayFactor > 0.5f) ? (Color){180, 150, 0, 255} : (Color){100, 100, 255, 255});
|
||||
|
||||
// Draw Chat Log (Bottom Left, above inventory)
|
||||
int chatY = currentHeight - 120;
|
||||
for (auto it = chatLog.begin(); it != chatLog.end();) {
|
||||
it->timer -= GetFrameTime();
|
||||
if (it->timer <= 0) {
|
||||
it = chatLog.erase(it);
|
||||
} else {
|
||||
float alpha = (it->timer > 1.0f) ? 1.0f : it->timer;
|
||||
DrawTextEx(customFont, it->text.c_str(), (Vector2){ 20, (float)chatY }, 20, 1.0f, Fade(WHITE, alpha));
|
||||
chatY -= 25;
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
if (isChatting) {
|
||||
DrawRectangle(10, currentHeight - 100, 400, 40, (Color){ 0, 0, 0, 180 });
|
||||
DrawRectangleLines(10, currentHeight - 100, 400, 40, DARKGRAY);
|
||||
DrawTextEx(customFont, TextFormat("> %s", chatInput), (Vector2){ 20, (float)currentHeight - 90 }, 20, 1.0f, WHITE);
|
||||
}
|
||||
|
||||
// ---- HOTBAR ----
|
||||
const int slotSize = 50;
|
||||
const int slotGap = 4;
|
||||
|
|
|
|||
|
|
@ -53,7 +53,8 @@ enum PacketType {
|
|||
PACKET_BLOCK_CHANGE = 2,
|
||||
PACKET_TIME_SYNC = 3,
|
||||
PACKET_SEED_SYNC = 4,
|
||||
PACKET_DISCONNECT = 5
|
||||
PACKET_CHAT = 5,
|
||||
PACKET_DISCONNECT = 6
|
||||
};
|
||||
|
||||
struct PacketHeader {
|
||||
|
|
@ -84,6 +85,11 @@ struct PacketSeedSync {
|
|||
int seed;
|
||||
};
|
||||
|
||||
struct PacketChat {
|
||||
char name[32];
|
||||
char message[128];
|
||||
};
|
||||
|
||||
// Cross-platform socket initialization
|
||||
inline bool InitNetworking() {
|
||||
#ifdef _WIN32
|
||||
|
|
|
|||
Loading…
Reference in New Issue