commit - d608014470b6cd1d42349fd916e913d3ae28c242
commit + 6bef88f60deda87f756c9aec70c0c2fe05d40657
blob - 0761a876e15066d6f44fa9aa4bbab0ef89c14259
blob + 3c95d185b5db257dc3d1b15498ee9b0bf28f3154
--- build_registries.js
+++ build_registries.js
"redstone_ore": "redstone",
"iron_ore": "raw_iron",
"coal_ore": "coal",
+ "copper_ore": "raw_copper",
"snow": "snowball",
"dead_bush": "stick"
};
"water_6",
"water_7",
"lava",
+ "lava_2",
+ "lava_4",
+ "lava_6",
"snowy_grass_block",
"mud",
"moss_carpet",
"stone_slab",
"cobblestone_slab",
"composter",
- "coal_block"
+ "coal_block",
+ "copper_ore",
+ "copper_block"
];
// Currently, only 4 biome types are supported, excluding "beach"
const tagBuffer = serializeTags({
"fluid": {
// Water and lava, both flowing and still states
- "water": [ 1, 2, 3, 4 ]
+ "water": [ 1, 2 ],
+ "lava": [ 3, 4 ]
},
"block": {
"mineable/pickaxe": [
itemsAndBlocks.blockRegistry["redstone_ore"],
itemsAndBlocks.blockRegistry["iron_ore"],
itemsAndBlocks.blockRegistry["coal_ore"],
+ itemsAndBlocks.blockRegistry["copper_ore"],
itemsAndBlocks.blockRegistry["furnace"],
itemsAndBlocks.blockRegistry["iron_block"],
itemsAndBlocks.blockRegistry["gold_block"],
itemsAndBlocks.blockRegistry["diamond_block"],
itemsAndBlocks.blockRegistry["redstone_block"],
- itemsAndBlocks.blockRegistry["coal_block"]
+ itemsAndBlocks.blockRegistry["coal_block"],
+ itemsAndBlocks.blockRegistry["copper_block"]
],
"mineable/axe": [
itemsAndBlocks.blockRegistry["oak_log"],
blob - d9bf6274667881d202903ac5a0e2dfc0f0bf6f47
blob + e6941e769649fdb6279921cd668f1c4794505133
--- src/crafting.c
+++ src/crafting.c
case 1:
switch (first_item) {
- case I_oak_log:
- *item = I_oak_planks;
- *count = 4;
- return;
- case I_oak_planks:
- *item = I_oak_button;
- *count = 1;
- return;
- case I_iron_block:
- *item = I_iron_ingot;
- *count = 9;
- return;
- case I_gold_block:
- *item = I_gold_ingot;
- *count = 9;
- return;
- case I_diamond_block:
- *item = I_diamond;
- *count = 9;
- return;
- case I_redstone_block:
- *item = I_redstone;
- *count = 9;
- return;
- case I_coal_block:
- *item = I_coal;
- *count = 9;
- return;
+ case I_oak_log: *item = I_oak_planks; *count = 4; return;
+ case I_oak_planks: *item = I_oak_button; *count = 1; return;
+ case I_iron_block: *item = I_iron_ingot; *count = 9; return;
+ case I_gold_block: *item = I_gold_ingot; *count = 9; return;
+ case I_diamond_block: *item = I_diamond; *count = 9; return;
+ case I_redstone_block: *item = I_redstone; *count = 9; return;
+ case I_coal_block: *item = I_coal; *count = 9; return;
+ case I_copper_block: *item = I_copper_ingot; *count = 9; return;
default: break;
}
case I_diamond: *item = I_diamond_block; *count = 1; return;
case I_redstone: *item = I_redstone_block; *count = 1; return;
case I_coal: *item = I_coal_block; *count = 1; return;
+ case I_copper_ingot: *item = I_copper_block; *count = 1; return;
default: break;
}
break;
blob - 8afdc87dafad893ff8f2ab6b3c6fd663e5767343
blob + bfaaaaf229820e4964b9e9467df9afb91829881b
--- src/procedures.c
+++ src/procedures.c
uint8_t isPassableBlock (uint8_t block) {
return (
block == B_air ||
- block == B_water ||
+ (block >= B_water && block < B_water + 8) ||
+ (block >= B_lava && block < B_lava + 4) ||
block == B_snow ||
block == B_moss_carpet ||
block == B_short_grass ||
return (
block == B_air ||
(block >= B_water && block < B_water + 8) ||
+ (block >= B_lava && block < B_lava + 4) ||
block == B_short_grass ||
block == B_snow
);
// a higher fluid "level" means the fluid has traveled farther
uint8_t level = block - fluid;
- // Query blocks adjacent to this water stream
+ // Query blocks adjacent to this fluid stream
uint8_t adjacent[4] = {
getBlockAt(x + 1, y, z),
getBlockAt(x - 1, y, z),
getBlockAt(x, y, z - 1)
};
- // Handle maintaining connections to a water source
+ // Handle maintaining connections to a fluid source
if (level != 0) {
// Check if this fluid is connected to a block exactly one level lower
uint8_t connected = false;
}
// Stop flowing laterally at the maximum level
+ if (level == 3 && fluid == B_lava) return;
if (level == 7) return;
// Handle lateral water flow, increasing level by 1
uint8_t fluid;
if (block >= B_water && block < B_water + 8) fluid = B_water;
- // else if (block >= B_lava && block < B_lava + 8) fluid = B_lava;
+ else if (block >= B_lava && block < B_lava + 4) fluid = B_lava;
else return;
handleFluidMovement(x, y, z, fluid, block);
// Killed by a less than 5 block fall
strcpy((char *)recv_buffer + player_name_len, " hit the ground too hard");
recv_buffer[player_name_len + 24] = '\0';
+ } else if (damage_type == D_lava) {
+ // Killed by being in lava
+ strcpy((char *)recv_buffer + player_name_len, " tried to swim in lava");
+ recv_buffer[player_name_len + 22] = '\0';
} else if (attacker_id < -1) {
// Killed by a mob
strcpy((char *)recv_buffer + player_name_len, " was slain by a mob");
strcpy((char *)recv_buffer + player_name_len, " was slain by ");
strcpy((char *)recv_buffer + player_name_len + 14, attacker->name);
recv_buffer[player_name_len + 14 + strlen(attacker->name)] = '\0';
+ } else {
+ // Unknown death reason
+ strcpy((char *)recv_buffer + player_name_len, " died");
+ recv_buffer[player_name_len + 4] = '\0';
}
} else player->health -= effective_damage;
if (player->flags & 0x20) { // Check "client loading" flag
// If 3 seconds (60 vanilla ticks) have passed, assume player has loaded
player->flagval_16 ++;
- if (player->flagval_16 > (unsigned int)(3 * TICKS_PER_SECOND)) {
+ if (player->flagval_16 > (uint16_t)(3 * TICKS_PER_SECOND)) {
player->flags &= ~0x20;
player->flagval_16 = 0;
} else continue;
}
- // Send Keep Alive and Update Time packets
- sc_keepAlive(player->client_fd);
- sc_updateTime(player->client_fd, world_time);
// Reset player attack cooldown
if (player->flags & 0x01) {
- if (player->flagval_8 >= (unsigned int)(0.6f * TICKS_PER_SECOND)) {
+ if (player->flagval_8 >= (uint8_t)(0.6f * TICKS_PER_SECOND)) {
player->flags &= ~0x01;
player->flagval_8 = 0;
} else player->flagval_8 ++;
}
// Handle eating animation
if (player->flags & 0x10) {
- if (player->flagval_16 >= (unsigned int)(1.6f * TICKS_PER_SECOND)) {
+ if (player->flagval_16 >= (uint16_t)(1.6f * TICKS_PER_SECOND)) {
handlePlayerEating(&player_data[i], false);
player->flags &= ~0x10;
player->flagval_16 = 0;
} else player->flagval_16 ++;
}
- // Heal from saturation if player is able and has enough food
+ // Below this, process events that happen once per second
if (server_ticks % (uint32_t)TICKS_PER_SECOND != 0) continue;
+ // Send Keep Alive and Update Time packets
+ sc_keepAlive(player->client_fd);
+ sc_updateTime(player->client_fd, world_time);
+ // Tick damage from lava
+ uint8_t block = getBlockAt(player->x, player->y, player->z);
+ if (block >= B_lava && block < B_lava + 4) {
+ hurtEntity(player->client_fd, -1, D_lava, 8);
+ }
+ // Heal from saturation if player is able and has enough food
if (player->health >= 20 || player->health == 0) continue;
if (player->hunger < 18) continue;
if (player->saturation >= 600) {
// Tick mob behavior
for (int i = 0; i < MAX_MOBS; i ++) {
if (mob_data[i].type == 0) continue;
+ int entity_id = -2 - i;
// Handle deallocation on mob death
if ((mob_data[i].data & 31) == 0) {
for (int j = 0; j < MAX_PLAYERS; j ++) {
if (player_data[j].client_fd == -1) continue;
// Spawn death smoke particles
- sc_entityEvent(player_data[j].client_fd, -2 - i, 60);
+ sc_entityEvent(player_data[j].client_fd, entity_id, 60);
// Remove the entity from the client
- sc_removeEntity(player_data[j].client_fd, -2 - i);
+ sc_removeEntity(player_data[j].client_fd, entity_id);
}
continue;
}
// Burn hostile mobs if above ground during sunlight
if (!passive && (world_time < 13000 || world_time > 23460) && mob_data[i].y > 48) {
- hurtEntity(-2 - i, -1, D_on_fire, 2);
+ hurtEntity(entity_id, -1, D_on_fire, 2);
}
uint32_t r = fast_rand();
// If we're already next to the player, hurt them and skip movement
if (closest_dist < 3 && abs(mob_data[i].y - closest_player->y) < 2) {
- hurtEntity(closest_player->client_fd, -2 - i, D_generic, 6);
+ hurtEntity(closest_player->client_fd, entity_id, D_generic, 6);
continue;
}
else continue;
} else {
uint8_t block_below = getBlockAt(new_x, new_y - 1, new_z);
- if (isPassableBlock(block_below)) new_y -= 1;
+ if (isPassableBlock(block_below) && block != B_water) new_y -= 1;
}
+ if ( // Hurt mobs that stumble into lava
+ (block >= B_lava && block < B_lava + 4) ||
+ (block_above >= B_lava && block_above < B_lava + 4)
+ ) hurtEntity(entity_id, -1, D_lava, 8);
+
// Store new mob position
mob_data[i].x = new_x;
mob_data[i].y = new_y;
// Broadcast relevant entity movement packets
for (int j = 0; j < MAX_PLAYERS; j ++) {
if (player_data[j].client_fd == -1) continue;
- int entity_id = -2 - i;
sc_teleportEntity (
player_data[j].client_fd, entity_id,
(double)new_x + 0.5, new_y, (double)new_z + 0.5,
blob - c6d2e122e029b88e816238347e668e451dcbbb7c
blob + fbb685e816a79f49d9662f2918d1a50b72c9903d
--- src/worldgen.c
+++ src/worldgen.c
int8_t gap = height - TERRAIN_BASE_HEIGHT;
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 sum of these is then used to get the Y coordinate of the ore in this column
- // This way, each column is guaranteed to have exactly one ore candidate
- uint8_t ore_x_component = (anchor.hash >> rx) & 31;
- uint8_t ore_z_component = (anchor.hash >> (rz + 16)) & 31;
- uint8_t ore_y = ore_x_component + ore_z_component;
+ // The chunk-relative X and Z coordinates are used as the seed for an
+ // xorshift RNG/hash function to generate the Y coordinate of the ore
+ // in this column. This way, each column is guaranteed to have exactly
+ // one ore candidate, as there will always be a Y value to reference.
+ uint8_t ore_y = ((rx & 15) << 4) + (rz & 15);
+ ore_y ^= ore_y << 4;
+ ore_y ^= ore_y >> 5;
+ ore_y ^= ore_y << 1;
+ ore_y &= 63;
if (y == ore_y) {
- // Since the ore Y coordinate is effectely a random number in range [0;64],
- // we use it in another bit shift to get a pseudo-random number for the column
- uint8_t ore_probability = (anchor.hash >> ore_y) & 255;
+ // Since the ore Y coordinate is effectely a random number in range [0;64),
+ // we use it in a bit shift with the chunk's anchor hash to get another
+ // pseudo-random number for the ore's rarity.
+ uint8_t ore_probability = (anchor.hash >> (ore_y % 24)) & 255;
// Ore placement is determined by Y level and "probability"
- if (y < 15 && ore_probability < 15) return B_diamond_ore;
+ if (y < 15) {
+ if (ore_probability < 10) return B_diamond_ore;
+ if (ore_probability < 12) return B_gold_ore;
+ if (ore_probability < 15) return B_redstone_ore;
+ }
if (y < 30) {
- if (ore_probability < 5) return B_gold_ore;
- if (ore_probability < 20) return B_redstone_ore;
+ if (ore_probability < 3) return B_gold_ore;
+ if (ore_probability < 8) return B_redstone_ore;
}
- if (y < 54 && ore_probability < 50) return B_iron_ore;
+ if (y < 54) {
+ if (ore_probability < 30) return B_iron_ore;
+ if (ore_probability < 40) return B_copper_ore;
+ }
if (ore_probability < 60) return B_coal_ore;
+ if (y < 5) return B_lava;
+ return B_cobblestone;
}
// For everything else, fall back to stone
uint8_t getBlockAt (int x, int y, int z) {
+ if (y < 0) return B_bedrock;
+
uint8_t block_change = getBlockChange(x, y, z);
if (block_change != 0xFF) return block_change;