From 70c1fb0e7ab7ae1e93a57267937a977ed6ee22d8 Mon Sep 17 00:00:00 2001 From: p2r3 Date: Thu, 21 Aug 2025 16:37:37 +0300 Subject: [PATCH] implement respawning --- include/globals.h | 2 ++ include/packets.h | 4 ++- include/tools.h | 2 ++ src/globals.c | 2 ++ src/main.c | 67 ++++++------------------------------- src/packets.c | 63 +++++++++++++++++++++++++++++++---- src/tools.c | 85 +++++++++++++++++++++++++++++++++++++++++++++-- 7 files changed, 159 insertions(+), 66 deletions(-) diff --git a/include/globals.h b/include/globals.h index 27e3216..66e7510 100644 --- a/include/globals.h +++ b/include/globals.h @@ -40,6 +40,8 @@ extern uint8_t recv_buffer[256]; extern uint32_t world_seed; extern uint32_t rng_seed; +extern uint64_t world_time; + extern uint16_t client_count; #pragma pack(push, 1) diff --git a/include/packets.h b/include/packets.h index 8b19753..8527265 100644 --- a/include/packets.h +++ b/include/packets.h @@ -13,6 +13,7 @@ int cs_setPlayerRotation (int client_fd, float *yaw, float *pitch, uint8_t *on_g int cs_setHeldItem (int client_fd); int cs_clickContainer (int client_fd); int cs_closeContainer (int client_fd); +int cs_clientStatus (int client_fd); int sc_loginSuccess (int client_fd, uint8_t *uuid, char *name); int sc_knownPacks (int client_fd); @@ -39,6 +40,7 @@ int sc_setHeadRotation (int client_fd, int id, uint8_t yaw); int sc_updateEntityRotation (int client_fd, int id, uint8_t yaw, uint8_t pitch); int sc_damageEvent (int client_fd, int id, int type); int sc_setHealth (int client_fd, uint8_t health, uint8_t food); -int sc_registries(int client_fd); +int sc_respawn (int client_fd); +int sc_registries (int client_fd); #endif diff --git a/include/tools.h b/include/tools.h index d5b7a1e..6ad5458 100644 --- a/include/tools.h +++ b/include/tools.h @@ -42,10 +42,12 @@ void setClientState (int client_fd, int new_state); int getClientState (int client_fd); int getClientIndex (int client_fd); +void resetPlayerData (PlayerData *player); int reservePlayerData (int client_fd, uint8_t *uuid, char* name); int getPlayerData (int client_fd, PlayerData **output); void clearPlayerFD (int client_fd); int givePlayerItem (PlayerData *player, uint16_t item, uint8_t count); +void spawnPlayer (PlayerData *player); uint8_t serverSlotToClientSlot (int window_id, uint8_t slot); uint8_t clientSlotToServerSlot (int window_id, uint8_t slot); diff --git a/src/globals.c b/src/globals.c index 0046632..d67897c 100644 --- a/src/globals.c +++ b/src/globals.c @@ -29,6 +29,8 @@ uint8_t recv_buffer[256] = {0}; uint32_t world_seed = 0xA103DE6C; uint32_t rng_seed = 0xE2B9419; +uint64_t world_time = 0; + uint16_t client_count; BlockChange block_changes[20000]; diff --git a/src/main.c b/src/main.c index d9c1d19..cbac2aa 100644 --- a/src/main.c +++ b/src/main.c @@ -31,8 +31,6 @@ #include "worldgen.h" #include "registries.h" -uint64_t world_time = 0; - void handlePacket (int client_fd, int length, int packet_id) { int state = getClientState(client_fd); @@ -72,66 +70,16 @@ void handlePacket (int client_fd, int length, int packet_id) { return; } else if (state == STATE_CONFIGURATION) { printf("Client Acknowledged Configuration\n\n"); - setClientState(client_fd, STATE_PLAY); + // Enter client into "play" state + setClientState(client_fd, STATE_PLAY); sc_loginPlay(client_fd); PlayerData *player; if (getPlayerData(client_fd, &player)) break; - float spawn_x = 8.5f, spawn_y = 80.0f, spawn_z = 8.5f; - float spawn_yaw = 0.0f, spawn_pitch = 0.0f; - - if (player->y == -32767) { // is this a new player? - int _x = 8 / CHUNK_SIZE; - int _z = 8 / CHUNK_SIZE; - int rx = 8 % CHUNK_SIZE; - int rz = 8 % CHUNK_SIZE; - spawn_y = getHeightAt(rx, rz, _x, _z, getChunkHash(_x, _z), getChunkBiome(_x, _z)) + 1; - } else { - spawn_x = player->x > 0 ? (float)player->x + 0.5 : (float)player->x - 0.5; - spawn_y = player->y; - spawn_z = player->z > 0 ? (float)player->z + 0.5 : (float)player->z - 0.5; - spawn_yaw = player->yaw * 180 / 127; - spawn_pitch = player->pitch * 90 / 127; - } - - sc_synchronizePlayerPosition(client_fd, spawn_x, spawn_y, spawn_z, spawn_yaw, spawn_pitch); - - task_yield(); - - for (uint8_t i = 0; i < 41; i ++) { - sc_setContainerSlot(client_fd, 0, serverSlotToClientSlot(0, i), player->inventory_count[i], player->inventory_items[i]); - } - sc_setHeldItem(client_fd, player->hotbar); - - sc_setHealth(client_fd, player->health, player->hunger); - - sc_playerAbilities(client_fd, 0x01 + 0x04); // invulnerability + flight - sc_updateTime(client_fd, world_time); - - short _x = player->x / 16, _z = player->z / 16; - if (player->x % 16 < 0) _x -= 1; - if (player->z % 16 < 0) _z -= 1; - - sc_setDefaultSpawnPosition(client_fd, 8, 80, 8); - sc_startWaitingForChunks(client_fd); - sc_setCenterChunk(client_fd, _x, _z); - - task_yield(); - - // Send spawn chunk first - sc_chunkDataAndUpdateLight(client_fd, _x, _z); - for (int i = -VIEW_DISTANCE; i <= VIEW_DISTANCE; i ++) { - for (int j = -VIEW_DISTANCE; j <= VIEW_DISTANCE; j ++) { - if (i == 0 && j == 0) continue; - sc_chunkDataAndUpdateLight(client_fd, _x + i, _z + j); - } - } - // Re-synchronize player position after all chunks have been sent - sc_synchronizePlayerPosition(client_fd, spawn_x, spawn_y, spawn_z, spawn_yaw, spawn_pitch); - - task_yield(); + // Send full client spawn sequence + spawnPlayer(player); // Register all existing players and spawn their entities, and broadcast // information about the joining player to all existing players. @@ -156,6 +104,13 @@ void handlePacket (int client_fd, int length, int packet_id) { } break; + case 0x0B: + if (state == STATE_PLAY) { + if (cs_clientStatus(client_fd)) break; + return; + } + break; + case 0x0C: if (state == STATE_PLAY) { // client tick diff --git a/src/packets.c b/src/packets.c index 50fd999..3b3dabe 100644 --- a/src/packets.c +++ b/src/packets.c @@ -144,7 +144,7 @@ int sc_finishConfiguration (int client_fd) { // S->C Login (play) int sc_loginPlay (int client_fd) { - writeVarInt(client_fd, 69 + sizeVarInt(MAX_PLAYERS)); + writeVarInt(client_fd, 49 + sizeVarInt(MAX_PLAYERS)); writeByte(client_fd, 0x2B); // entity id uint32_t entity_id = getClientIndex(client_fd); @@ -153,9 +153,9 @@ int sc_loginPlay (int client_fd) { writeByte(client_fd, false); // dimensions writeVarInt(client_fd, 1); - writeVarInt(client_fd, 19); - const char *dimension = "minecraft:overworld"; - send_all(client_fd, dimension, 19); + writeVarInt(client_fd, 9); + const char *dimension = "overworld"; + send_all(client_fd, dimension, 9); // maxplayers writeVarInt(client_fd, MAX_PLAYERS); // view distance @@ -171,8 +171,8 @@ int sc_loginPlay (int client_fd) { // dimension id writeVarInt(client_fd, 0); // dimension name - writeVarInt(client_fd, 19); - send_all(client_fd, dimension, 19); + writeVarInt(client_fd, 9); + send_all(client_fd, dimension, 9); // hashed seed writeUint64(client_fd, 0x0123456789ABCDEF); // gamemode @@ -850,6 +850,57 @@ int sc_setHealth (int client_fd, uint8_t health, uint8_t food) { return 0; } +// S->C Respawn +int sc_respawn (int client_fd) { + + writeVarInt(client_fd, 28); + writeByte(client_fd, 0x4B); + + // dimension id + writeVarInt(client_fd, 0); + // dimension name + const char *dimension = "overworld"; + writeVarInt(client_fd, 9); + send_all(client_fd, dimension, 9); + // hashed seed + writeUint64(client_fd, 0x0123456789ABCDEF); + // gamemode + writeByte(client_fd, GAMEMODE); + // previous gamemode + writeByte(client_fd, 0xFF); + // is debug + writeByte(client_fd, 0); + // is flat + writeByte(client_fd, 0); + // has death location + writeByte(client_fd, 0); + // portal cooldown + writeVarInt(client_fd, 0); + // sea level + writeVarInt(client_fd, 63); + // data kept + writeByte(client_fd, 0); + + return 0; +} + +// C->S Client Status +int cs_clientStatus (int client_fd) { + + uint8_t id = readByte(client_fd); + + PlayerData *player; + if (getPlayerData(client_fd, &player)) return 1; + + if (id == 0) { + sc_respawn(client_fd); + resetPlayerData(player); + spawnPlayer(player); + } + + return 0; +} + // S->C Registry Data (multiple packets) and Update Tags (configuration, multiple packets) int sc_registries (int client_fd) { diff --git a/src/tools.c b/src/tools.c index 0504bbe..83f9880 100644 --- a/src/tools.c +++ b/src/tools.c @@ -218,6 +218,21 @@ int getClientIndex (int client_fd) { return -1; } +void resetPlayerData (PlayerData *player) { + player->health = 20; + player->hunger = 20; + player->y = -32767; + player->grounded_y = 0; + for (int i = 0; i < 41; i ++) { + player->inventory_items[i] = 0; + player->inventory_count[i] = 0; + } + for (int i = 0; i < 9; i ++) { + player->craft_items[i] = 0; + player->craft_count[i] = 0; + } +} + int reservePlayerData (int client_fd, uint8_t *uuid, char *name) { for (int i = 0; i < MAX_PLAYERS; i ++) { @@ -237,9 +252,7 @@ int reservePlayerData (int client_fd, uint8_t *uuid, char *name) { player_data[i].client_fd = client_fd; memcpy(player_data[i].uuid, uuid, 16); memcpy(player_data[i].name, name, 16); - player_data[i].health = 20; - player_data[i].hunger = 20; - player_data[i].y = -32767; + resetPlayerData(&player_data[i]); return 0; } } @@ -357,6 +370,72 @@ int givePlayerItem (PlayerData *player, uint16_t item, uint8_t count) { } +// Sends the full sequence for spawning the player to the client +void spawnPlayer (PlayerData *player) { + + // Player spawn coordinates, initialized to placeholders + float spawn_x = 8.5f, spawn_y = 80.0f, spawn_z = 8.5f; + float spawn_yaw = 0.0f, spawn_pitch = 0.0f; + + if (player->y == -32767) { // Is this a new player? + // Determine spawning Y coordinate based on terrain height + int _x = 8 / CHUNK_SIZE; + int _z = 8 / CHUNK_SIZE; + int rx = 8 % CHUNK_SIZE; + int rz = 8 % CHUNK_SIZE; + spawn_y = getHeightAt(rx, rz, _x, _z, getChunkHash(_x, _z), getChunkBiome(_x, _z)) + 1; + } else { // Not a new player + // Calculate spawn position from player data + spawn_x = player->x > 0 ? (float)player->x + 0.5 : (float)player->x - 0.5; + spawn_y = player->y; + spawn_z = player->z > 0 ? (float)player->z + 0.5 : (float)player->z - 0.5; + spawn_yaw = player->yaw * 180 / 127; + spawn_pitch = player->pitch * 90 / 127; + } + + // Teleport player to spawn coordinates (first pass) + sc_synchronizePlayerPosition(player->client_fd, spawn_x, spawn_y, spawn_z, spawn_yaw, spawn_pitch); + + task_yield(); // Check task timer between packets + + // Sync client inventory and hotbar + for (uint8_t i = 0; i < 41; i ++) { + sc_setContainerSlot(player->client_fd, 0, serverSlotToClientSlot(0, i), player->inventory_count[i], player->inventory_items[i]); + } + sc_setHeldItem(player->client_fd, player->hotbar); + // Sync client health and hunger + sc_setHealth(player->client_fd, player->health, player->hunger); + // Sync client clock time + sc_updateTime(player->client_fd, world_time); + + // Give the player flight (for testing) + sc_playerAbilities(player->client_fd, 0x04); + + // Calculate player's chunk coordinates + short _x = div_floor(player->x, 16), _z = div_floor(player->z, 16); + + // Indicate that we're about to send chunk data + sc_setDefaultSpawnPosition(player->client_fd, 8, 80, 8); + sc_startWaitingForChunks(player->client_fd); + sc_setCenterChunk(player->client_fd, _x, _z); + + task_yield(); // Check task timer between packets + + // Send spawn chunk first + sc_chunkDataAndUpdateLight(player->client_fd, _x, _z); + for (int i = -VIEW_DISTANCE; i <= VIEW_DISTANCE; i ++) { + for (int j = -VIEW_DISTANCE; j <= VIEW_DISTANCE; j ++) { + if (i == 0 && j == 0) continue; + sc_chunkDataAndUpdateLight(player->client_fd, _x + i, _z + j); + } + } + // Re-teleport player after all chunks have been sent + sc_synchronizePlayerPosition(player->client_fd, spawn_x, spawn_y, spawn_z, spawn_yaw, spawn_pitch); + + task_yield(); // Check task timer between packets + +} + uint8_t getBlockChange (short x, uint8_t y, short z) { for (int i = 0; i < block_changes_count; i ++) { if (block_changes[i].block == 0xFF) continue;