mirror of
https://github.com/p2r3/bareiron.git
synced 2025-10-01 23:25:09 +02:00
better sync client/server block state
This commit is contained in:
@@ -78,10 +78,10 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
|||||||
float spawn_yaw = 0.0f, spawn_pitch = 0.0f;
|
float spawn_yaw = 0.0f, spawn_pitch = 0.0f;
|
||||||
|
|
||||||
if (player->y == -32767) { // is this a new player?
|
if (player->y == -32767) { // is this a new player?
|
||||||
int _x = 8 / chunk_size;
|
int _x = 8 / CHUNK_SIZE;
|
||||||
int _z = 8 / chunk_size;
|
int _z = 8 / CHUNK_SIZE;
|
||||||
int rx = 8 % chunk_size;
|
int rx = 8 % CHUNK_SIZE;
|
||||||
int rz = 8 % chunk_size;
|
int rz = 8 % CHUNK_SIZE;
|
||||||
spawn_y = getHeightAt(rx, rz, _x, _z, getChunkHash(_x, _z)) + 1;
|
spawn_y = getHeightAt(rx, rz, _x, _z, getChunkHash(_x, _z)) + 1;
|
||||||
} else {
|
} else {
|
||||||
spawn_x = player->x > 0 ? (float)player->x + 0.5 : (float)player->x - 0.5;
|
spawn_x = player->x > 0 ? (float)player->x + 0.5 : (float)player->x - 0.5;
|
||||||
|
@@ -405,6 +405,14 @@ int sc_setContainerSlot (int client_fd, int window_id, uint16_t slot, uint8_t co
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// S->C Block Update
|
||||||
|
int sc_blockUpdate (int client_fd, int64_t x, int64_t y, int64_t z, uint8_t block) {
|
||||||
|
writeVarInt(client_fd, 9 + sizeVarInt(block_palette[block]));
|
||||||
|
writeByte(client_fd, 0x08);
|
||||||
|
writeUint64(client_fd, ((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF));
|
||||||
|
writeVarInt(client_fd, block_palette[block]);
|
||||||
|
}
|
||||||
|
|
||||||
// C->S Player Action
|
// C->S Player Action
|
||||||
int cs_playerAction (int client_fd) {
|
int cs_playerAction (int client_fd) {
|
||||||
|
|
||||||
@@ -490,31 +498,37 @@ int cs_useItemOn (int client_fd) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (face) {
|
||||||
|
case 0: y -= 1; break;
|
||||||
|
case 1: y += 1; break;
|
||||||
|
case 2: z -= 1; break;
|
||||||
|
case 3: z += 1; break;
|
||||||
|
case 4: x -= 1; break;
|
||||||
|
case 5: x += 1; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
PlayerData *player;
|
PlayerData *player;
|
||||||
if (getPlayerData(client_fd, &player)) return 1;
|
if (getPlayerData(client_fd, &player)) return 1;
|
||||||
|
|
||||||
uint16_t item = player->inventory_items[player->hotbar];
|
// check if the player is in the way
|
||||||
uint8_t block = I_to_B(item);
|
if (x == player->x && (y == player->y || y == player->y + 1) && z == player->z) return 0;
|
||||||
|
|
||||||
|
uint16_t *item = &player->inventory_items[player->hotbar];
|
||||||
|
uint8_t *count = &player->inventory_count[player->hotbar];
|
||||||
|
uint8_t block = I_to_B(*item);
|
||||||
|
|
||||||
// if the selected item doesn't correspond to a block, exit
|
// if the selected item doesn't correspond to a block, exit
|
||||||
if (block == 0) return 0;
|
if (block == 0) return 0;
|
||||||
// if the selected slot doesn't hold any items, exit
|
// if the selected slot doesn't hold any items, exit
|
||||||
if (player->inventory_count[player->hotbar] == 0) return 0;
|
if (*count == 0) return 0;
|
||||||
// decrease item amount in selected slot
|
// decrease item amount in selected slot
|
||||||
player->inventory_count[player->hotbar] --;
|
*count -= 1;
|
||||||
// clear item id in slot if amount is zero
|
// clear item id in slot if amount is zero
|
||||||
if (player->inventory_count[player->hotbar] == 0)
|
if (*count == 0) *item = 0;
|
||||||
player->inventory_items[player->hotbar] = 0;
|
|
||||||
|
|
||||||
switch (face) {
|
makeBlockChange(x, y, z, block);
|
||||||
case 0: makeBlockChange(x, y - 1, z, block); break;
|
sc_setContainerSlot(client_fd, 0, serverSlotToClientSlot(0, player->hotbar), *count, *item);
|
||||||
case 1: makeBlockChange(x, y + 1, z, block); break;
|
|
||||||
case 2: makeBlockChange(x, y, z - 1, block); break;
|
|
||||||
case 3: makeBlockChange(x, y, z + 1, block); break;
|
|
||||||
case 4: makeBlockChange(x - 1, y, z, block); break;
|
|
||||||
case 5: makeBlockChange(x + 1, y, z, block); break;
|
|
||||||
default: break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@@ -27,6 +27,7 @@ int sc_chunkDataAndUpdateLight (int client_fd, int _x, int _z);
|
|||||||
int sc_keepAlive (int client_fd);
|
int sc_keepAlive (int client_fd);
|
||||||
int sc_setContainerSlot (int client_fd, int window_id, uint16_t slot, uint8_t count, uint16_t item);
|
int sc_setContainerSlot (int client_fd, int window_id, uint16_t slot, uint8_t count, uint16_t item);
|
||||||
int sc_setHeldItem (int client_fd, uint8_t slot);
|
int sc_setHeldItem (int client_fd, uint8_t slot);
|
||||||
|
int sc_blockUpdate (int client_fd, int64_t x, int64_t y, int64_t z, uint8_t block);
|
||||||
int sc_openScreen (int client_fd, uint8_t window, const char *title, uint16_t length);
|
int sc_openScreen (int client_fd, uint8_t window, const char *title, uint16_t length);
|
||||||
int sc_registries(int client_fd);
|
int sc_registries(int client_fd);
|
||||||
|
|
||||||
|
33
src/tools.c
33
src/tools.c
@@ -14,6 +14,7 @@
|
|||||||
#include "registries.h"
|
#include "registries.h"
|
||||||
#include "varnum.h"
|
#include "varnum.h"
|
||||||
#include "packets.h"
|
#include "packets.h"
|
||||||
|
#include "worldgen.h"
|
||||||
#include "tools.h"
|
#include "tools.h"
|
||||||
|
|
||||||
static uint64_t htonll (uint64_t value) {
|
static uint64_t htonll (uint64_t value) {
|
||||||
@@ -298,17 +299,47 @@ uint8_t getBlockChange (short x, short y, short z) {
|
|||||||
|
|
||||||
void makeBlockChange (short x, short y, short z, uint8_t block) {
|
void makeBlockChange (short x, short y, short z, uint8_t block) {
|
||||||
|
|
||||||
|
// Transmit block update to all managed clients
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||||
|
sc_blockUpdate(player_data[i].client_fd, x, y, z, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate terrain at these coordinates and compare it to the input block.
|
||||||
|
// Since block changes get overlayed on top of terrain, we don't want to
|
||||||
|
// store blocks that don't differ from the base terrain.
|
||||||
|
ChunkAnchor anchor = {
|
||||||
|
x / CHUNK_SIZE,
|
||||||
|
z / CHUNK_SIZE
|
||||||
|
};
|
||||||
|
if (x % CHUNK_SIZE < 0) anchor.x --;
|
||||||
|
if (z % CHUNK_SIZE < 0) anchor.z --;
|
||||||
|
anchor.hash = getChunkHash(anchor.x, anchor.z);
|
||||||
|
uint8_t is_base_block = block == getTerrainAt(x, y, z, anchor);
|
||||||
|
|
||||||
|
// Look for existing block change entries and replace them
|
||||||
|
// 0xFF indicates a missing/restored entry
|
||||||
for (int i = 0; i < block_changes_count; i ++) {
|
for (int i = 0; i < block_changes_count; i ++) {
|
||||||
|
if (block_changes[i].block == 0xFF && !is_base_block) {
|
||||||
|
block_changes[i].x = x;
|
||||||
|
block_changes[i].y = y;
|
||||||
|
block_changes[i].z = z;
|
||||||
|
block_changes[i].block = block;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
block_changes[i].x == x &&
|
block_changes[i].x == x &&
|
||||||
block_changes[i].y == y &&
|
block_changes[i].y == y &&
|
||||||
block_changes[i].z == z
|
block_changes[i].z == z
|
||||||
) {
|
) {
|
||||||
block_changes[i].block = block;
|
if (is_base_block) block_changes[i].block = 0xFF;
|
||||||
|
else block_changes[i].block = block;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't create a new entry if it contains the base terrain block
|
||||||
|
if (is_base_block) return;
|
||||||
|
|
||||||
block_changes[block_changes_count].x = x;
|
block_changes[block_changes_count].x = x;
|
||||||
block_changes[block_changes_count].y = y;
|
block_changes[block_changes_count].y = y;
|
||||||
block_changes[block_changes_count].z = z;
|
block_changes[block_changes_count].z = z;
|
||||||
|
@@ -25,7 +25,7 @@ int getCornerHeight (uint32_t hash) {
|
|||||||
// Use parts of the hash as random values for the height variation.
|
// Use parts of the hash as random values for the height variation.
|
||||||
// We stack multiple different numbers to stabilize the distribution
|
// We stack multiple different numbers to stabilize the distribution
|
||||||
// while allowing for occasional variances.
|
// while allowing for occasional variances.
|
||||||
int height = terrain_base_height + (
|
int height = TERRAIN_BASE_HEIGHT + (
|
||||||
(hash & 3) +
|
(hash & 3) +
|
||||||
(hash >> 4 & 3) +
|
(hash >> 4 & 3) +
|
||||||
(hash >> 8 & 3) +
|
(hash >> 8 & 3) +
|
||||||
@@ -41,9 +41,9 @@ int getCornerHeight (uint32_t hash) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int interpolate (int a, int b, int c, int d, int x, int z) {
|
int interpolate (int a, int b, int c, int d, int x, int z) {
|
||||||
int top = a * (chunk_size - x) + b * x;
|
int top = a * (CHUNK_SIZE - x) + b * x;
|
||||||
int bottom = c * (chunk_size - x) + d * x;
|
int bottom = c * (CHUNK_SIZE - x) + d * x;
|
||||||
return (top * (chunk_size - z) + bottom * z) / (chunk_size * chunk_size);
|
return (top * (CHUNK_SIZE - z) + bottom * z) / (CHUNK_SIZE * CHUNK_SIZE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int getHeightAt (int rx, int rz, int _x, int _z, uint32_t chunk_hash) {
|
int getHeightAt (int rx, int rz, int _x, int _z, uint32_t chunk_hash) {
|
||||||
@@ -62,40 +62,34 @@ int getHeightAt (int rx, int rz, int _x, int _z, uint32_t chunk_hash) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
short x;
|
|
||||||
short z;
|
|
||||||
uint32_t hash;
|
|
||||||
} ChunkAnchor;
|
|
||||||
|
|
||||||
uint8_t getTerrainAt (int x, int y, int z, ChunkAnchor anchor) {
|
uint8_t getTerrainAt (int x, int y, int z, ChunkAnchor anchor) {
|
||||||
|
|
||||||
if (y > 80) return B_air;
|
if (y > 80) return B_air;
|
||||||
|
|
||||||
int rx = x % chunk_size;
|
int rx = x % CHUNK_SIZE;
|
||||||
int rz = z % chunk_size;
|
int rz = z % CHUNK_SIZE;
|
||||||
if (rx < 0) rx += chunk_size;
|
if (rx < 0) rx += CHUNK_SIZE;
|
||||||
if (rz < 0) rz += chunk_size;
|
if (rz < 0) rz += CHUNK_SIZE;
|
||||||
|
|
||||||
int height = getHeightAt(rx, rz, anchor.x, anchor.z, anchor.hash);
|
int height = getHeightAt(rx, rz, anchor.x, anchor.z, anchor.hash);
|
||||||
|
|
||||||
if (y >= 64 && y >= height) {
|
if (y >= 64 && y >= height) {
|
||||||
|
|
||||||
uint8_t tree_position = anchor.hash % (chunk_size * chunk_size);
|
uint8_t tree_position = anchor.hash % (CHUNK_SIZE * CHUNK_SIZE);
|
||||||
|
|
||||||
short tree_x = tree_position % chunk_size;
|
short tree_x = tree_position % CHUNK_SIZE;
|
||||||
if (tree_x < 3 || tree_x > chunk_size - 3) goto skip_tree;
|
if (tree_x < 3 || tree_x > CHUNK_SIZE - 3) goto skip_tree;
|
||||||
short tree_z = tree_position / chunk_size;
|
short tree_z = tree_position / CHUNK_SIZE;
|
||||||
if (tree_z < 3 || tree_z > chunk_size - 3) goto skip_tree;
|
if (tree_z < 3 || tree_z > CHUNK_SIZE - 3) goto skip_tree;
|
||||||
|
|
||||||
uint8_t tree_short = (anchor.hash >> (tree_x + tree_z)) & 1;
|
uint8_t tree_short = (anchor.hash >> (tree_x + tree_z)) & 1;
|
||||||
|
|
||||||
tree_x += anchor.x * chunk_size;
|
tree_x += anchor.x * CHUNK_SIZE;
|
||||||
tree_z += anchor.z * chunk_size;
|
tree_z += anchor.z * CHUNK_SIZE;
|
||||||
|
|
||||||
uint8_t tree_y = getHeightAt(
|
uint8_t tree_y = getHeightAt(
|
||||||
tree_x < 0 ? tree_x % chunk_size + chunk_size : tree_x % chunk_size,
|
tree_x < 0 ? tree_x % CHUNK_SIZE + CHUNK_SIZE : tree_x % CHUNK_SIZE,
|
||||||
tree_z < 0 ? tree_z % chunk_size + chunk_size : tree_z % chunk_size,
|
tree_z < 0 ? tree_z % CHUNK_SIZE + CHUNK_SIZE : tree_z % CHUNK_SIZE,
|
||||||
anchor.x, anchor.z, anchor.hash
|
anchor.x, anchor.z, anchor.hash
|
||||||
) + 1;
|
) + 1;
|
||||||
if (tree_y < 64) goto skip_tree;
|
if (tree_y < 64) goto skip_tree;
|
||||||
@@ -128,8 +122,8 @@ skip_tree:
|
|||||||
// Starting at 4 blocks below terrain level, generate minerals and caves
|
// Starting at 4 blocks below terrain level, generate minerals and caves
|
||||||
if (y <= height - 4) {
|
if (y <= height - 4) {
|
||||||
// Caves use the same shape as surface terrain, just mirrored
|
// Caves use the same shape as surface terrain, just mirrored
|
||||||
int8_t gap = height - terrain_base_height;
|
int8_t gap = height - TERRAIN_BASE_HEIGHT;
|
||||||
if (y < cave_base_depth + gap && y > cave_base_depth - gap) return B_air;
|
if (y < CAVE_BASE_DEPTH + gap && y > CAVE_BASE_DEPTH - gap) return B_air;
|
||||||
|
|
||||||
// The chunk-relative X and Z coordinates are used in a bit shift on the hash
|
// The chunk-relative X and Z coordinates are used in a bit shift on the hash
|
||||||
// The sum of these is then used to get the Y coordinate of the ore in this column
|
// The sum of these is then used to get the Y coordinate of the ore in this column
|
||||||
@@ -171,11 +165,11 @@ uint8_t getBlockAt (int x, int y, int z) {
|
|||||||
if (block_change != 0xFF) return block_change;
|
if (block_change != 0xFF) return block_change;
|
||||||
|
|
||||||
ChunkAnchor anchor = {
|
ChunkAnchor anchor = {
|
||||||
x / chunk_size,
|
x / CHUNK_SIZE,
|
||||||
z / chunk_size
|
z / CHUNK_SIZE
|
||||||
};
|
};
|
||||||
if (x % chunk_size < 0) anchor.x --;
|
if (x % CHUNK_SIZE < 0) anchor.x --;
|
||||||
if (z % chunk_size < 0) anchor.z --;
|
if (z % CHUNK_SIZE < 0) anchor.z --;
|
||||||
anchor.hash = getChunkHash(anchor.x, anchor.z);
|
anchor.hash = getChunkHash(anchor.x, anchor.z);
|
||||||
|
|
||||||
return getTerrainAt(x, y, z, anchor);
|
return getTerrainAt(x, y, z, anchor);
|
||||||
@@ -183,19 +177,19 @@ uint8_t getBlockAt (int x, int y, int z) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t chunk_section[4096];
|
uint8_t chunk_section[4096];
|
||||||
ChunkAnchor chunk_anchors[256 / (chunk_size * chunk_size)];
|
ChunkAnchor chunk_anchors[256 / (CHUNK_SIZE * CHUNK_SIZE)];
|
||||||
|
|
||||||
void buildChunkSection (int cx, int cy, int cz) {
|
void buildChunkSection (int cx, int cy, int cz) {
|
||||||
|
|
||||||
// Precompute the hashes and anchors for each minichunk
|
// Precompute the hashes and anchors for each minichunk
|
||||||
int anchor_index = 0;
|
int anchor_index = 0;
|
||||||
for (int i = cz; i < cz + 16; i += chunk_size) {
|
for (int i = cz; i < cz + 16; i += CHUNK_SIZE) {
|
||||||
for (int j = cx; j < cx + 16; j += chunk_size) {
|
for (int j = cx; j < cx + 16; j += CHUNK_SIZE) {
|
||||||
|
|
||||||
ChunkAnchor *anchor = chunk_anchors + anchor_index;
|
ChunkAnchor *anchor = chunk_anchors + anchor_index;
|
||||||
|
|
||||||
anchor->x = j / chunk_size;
|
anchor->x = j / CHUNK_SIZE;
|
||||||
anchor->z = i / chunk_size;
|
anchor->z = i / CHUNK_SIZE;
|
||||||
anchor->hash = getChunkHash(anchor->x, anchor->z);
|
anchor->hash = getChunkHash(anchor->x, anchor->z);
|
||||||
|
|
||||||
anchor_index ++;
|
anchor_index ++;
|
||||||
@@ -211,7 +205,7 @@ void buildChunkSection (int cx, int cy, int cz) {
|
|||||||
// The client expects "big-endian longs", which in our
|
// The client expects "big-endian longs", which in our
|
||||||
// case means reversing the order in which we store/send
|
// case means reversing the order in which we store/send
|
||||||
// each 8 block sequence.
|
// each 8 block sequence.
|
||||||
anchor_index = (j % 16) / chunk_size + (j / 16 % 16) / chunk_size * 2;
|
anchor_index = (j % 16) / CHUNK_SIZE + (j / 16 % 16) / CHUNK_SIZE * 2;
|
||||||
for (int offset = 7; offset >= 0; offset--) {
|
for (int offset = 7; offset >= 0; offset--) {
|
||||||
int k = j + offset;
|
int k = j + offset;
|
||||||
int x = k % 16 + cx;
|
int x = k % 16 + cx;
|
||||||
|
@@ -3,15 +3,22 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// For best performance, chunk_size should be a power of 2
|
// For best performance, CHUNK_SIZE should be a power of 2
|
||||||
#define chunk_size 8
|
#define CHUNK_SIZE 8
|
||||||
// Terrain low point - should start a bit below sea level for rivers/lakes
|
// Terrain low point - should start a bit below sea level for rivers/lakes
|
||||||
#define terrain_base_height 60
|
#define TERRAIN_BASE_HEIGHT 60
|
||||||
// Center point of cave generation
|
// Center point of cave generation
|
||||||
#define cave_base_depth 24
|
#define CAVE_BASE_DEPTH 24
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
short x;
|
||||||
|
short z;
|
||||||
|
uint32_t hash;
|
||||||
|
} ChunkAnchor;
|
||||||
|
|
||||||
uint32_t getChunkHash (short x, short z);
|
uint32_t getChunkHash (short x, short z);
|
||||||
int getHeightAt (int rx, int rz, int _x, int _z, uint32_t chunk_hash);
|
int getHeightAt (int rx, int rz, int _x, int _z, uint32_t chunk_hash);
|
||||||
|
uint8_t getTerrainAt (int x, int y, int z, ChunkAnchor anchor);
|
||||||
uint8_t getBlockAt (int x, int y, int z);
|
uint8_t getBlockAt (int x, int y, int z);
|
||||||
|
|
||||||
extern uint8_t chunk_section[4096];
|
extern uint8_t chunk_section[4096];
|
||||||
|
Reference in New Issue
Block a user