update ore generation

This commit is contained in:
p2r3
2025-08-31 22:23:19 +03:00
parent d608014470
commit 6bef88f60d
4 changed files with 89 additions and 62 deletions

View File

@@ -11,6 +11,7 @@ const blockToItemOverrides = {
"redstone_ore": "redstone", "redstone_ore": "redstone",
"iron_ore": "raw_iron", "iron_ore": "raw_iron",
"coal_ore": "coal", "coal_ore": "coal",
"copper_ore": "raw_copper",
"snow": "snowball", "snow": "snowball",
"dead_bush": "stick" "dead_bush": "stick"
}; };
@@ -47,6 +48,9 @@ const blockWhitelist = [
"water_6", "water_6",
"water_7", "water_7",
"lava", "lava",
"lava_2",
"lava_4",
"lava_6",
"snowy_grass_block", "snowy_grass_block",
"mud", "mud",
"moss_carpet", "moss_carpet",
@@ -54,7 +58,9 @@ const blockWhitelist = [
"stone_slab", "stone_slab",
"cobblestone_slab", "cobblestone_slab",
"composter", "composter",
"coal_block" "coal_block",
"copper_ore",
"copper_block"
]; ];
// Currently, only 4 biome types are supported, excluding "beach" // Currently, only 4 biome types are supported, excluding "beach"
@@ -346,7 +352,8 @@ async function convert () {
const tagBuffer = serializeTags({ const tagBuffer = serializeTags({
"fluid": { "fluid": {
// Water and lava, both flowing and still states // Water and lava, both flowing and still states
"water": [ 1, 2, 3, 4 ] "water": [ 1, 2 ],
"lava": [ 3, 4 ]
}, },
"block": { "block": {
"mineable/pickaxe": [ "mineable/pickaxe": [
@@ -362,12 +369,14 @@ async function convert () {
itemsAndBlocks.blockRegistry["redstone_ore"], itemsAndBlocks.blockRegistry["redstone_ore"],
itemsAndBlocks.blockRegistry["iron_ore"], itemsAndBlocks.blockRegistry["iron_ore"],
itemsAndBlocks.blockRegistry["coal_ore"], itemsAndBlocks.blockRegistry["coal_ore"],
itemsAndBlocks.blockRegistry["copper_ore"],
itemsAndBlocks.blockRegistry["furnace"], itemsAndBlocks.blockRegistry["furnace"],
itemsAndBlocks.blockRegistry["iron_block"], itemsAndBlocks.blockRegistry["iron_block"],
itemsAndBlocks.blockRegistry["gold_block"], itemsAndBlocks.blockRegistry["gold_block"],
itemsAndBlocks.blockRegistry["diamond_block"], itemsAndBlocks.blockRegistry["diamond_block"],
itemsAndBlocks.blockRegistry["redstone_block"], itemsAndBlocks.blockRegistry["redstone_block"],
itemsAndBlocks.blockRegistry["coal_block"] itemsAndBlocks.blockRegistry["coal_block"],
itemsAndBlocks.blockRegistry["copper_block"]
], ],
"mineable/axe": [ "mineable/axe": [
itemsAndBlocks.blockRegistry["oak_log"], itemsAndBlocks.blockRegistry["oak_log"],

View File

@@ -31,34 +31,14 @@ void getCraftingOutput (PlayerData *player, uint8_t *count, uint16_t *item) {
case 1: case 1:
switch (first_item) { switch (first_item) {
case I_oak_log: case I_oak_log: *item = I_oak_planks; *count = 4; return;
*item = I_oak_planks; case I_oak_planks: *item = I_oak_button; *count = 1; return;
*count = 4; case I_iron_block: *item = I_iron_ingot; *count = 9; return;
return; case I_gold_block: *item = I_gold_ingot; *count = 9; return;
case I_oak_planks: case I_diamond_block: *item = I_diamond; *count = 9; return;
*item = I_oak_button; case I_redstone_block: *item = I_redstone; *count = 9; return;
*count = 1; case I_coal_block: *item = I_coal; *count = 9; return;
return; case I_copper_block: *item = I_copper_ingot; *count = 9; 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;
default: break; default: break;
} }
@@ -345,6 +325,7 @@ void getCraftingOutput (PlayerData *player, uint8_t *count, uint16_t *item) {
case I_diamond: *item = I_diamond_block; *count = 1; return; case I_diamond: *item = I_diamond_block; *count = 1; return;
case I_redstone: *item = I_redstone_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_coal: *item = I_coal_block; *count = 1; return;
case I_copper_ingot: *item = I_copper_block; *count = 1; return;
default: break; default: break;
} }
break; break;

View File

@@ -622,7 +622,8 @@ uint8_t isColumnBlock (uint8_t block) {
uint8_t isPassableBlock (uint8_t block) { uint8_t isPassableBlock (uint8_t block) {
return ( return (
block == B_air || 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_snow ||
block == B_moss_carpet || block == B_moss_carpet ||
block == B_short_grass || block == B_short_grass ||
@@ -636,6 +637,7 @@ uint8_t isReplaceableBlock (uint8_t block) {
return ( return (
block == B_air || block == B_air ||
(block >= B_water && block < B_water + 8) || (block >= B_water && block < B_water + 8) ||
(block >= B_lava && block < B_lava + 4) ||
block == B_short_grass || block == B_short_grass ||
block == B_snow block == B_snow
); );
@@ -869,7 +871,7 @@ void handleFluidMovement (short x, uint8_t y, short z, uint8_t fluid, uint8_t bl
// a higher fluid "level" means the fluid has traveled farther // a higher fluid "level" means the fluid has traveled farther
uint8_t level = block - fluid; uint8_t level = block - fluid;
// Query blocks adjacent to this water stream // Query blocks adjacent to this fluid stream
uint8_t adjacent[4] = { uint8_t adjacent[4] = {
getBlockAt(x + 1, y, z), getBlockAt(x + 1, y, z),
getBlockAt(x - 1, y, z), getBlockAt(x - 1, y, z),
@@ -877,7 +879,7 @@ void handleFluidMovement (short x, uint8_t y, short z, uint8_t fluid, uint8_t bl
getBlockAt(x, y, z - 1) getBlockAt(x, y, z - 1)
}; };
// Handle maintaining connections to a water source // Handle maintaining connections to a fluid source
if (level != 0) { if (level != 0) {
// Check if this fluid is connected to a block exactly one level lower // Check if this fluid is connected to a block exactly one level lower
uint8_t connected = false; uint8_t connected = false;
@@ -906,6 +908,7 @@ void handleFluidMovement (short x, uint8_t y, short z, uint8_t fluid, uint8_t bl
} }
// Stop flowing laterally at the maximum level // Stop flowing laterally at the maximum level
if (level == 3 && fluid == B_lava) return;
if (level == 7) return; if (level == 7) return;
// Handle lateral water flow, increasing level by 1 // Handle lateral water flow, increasing level by 1
@@ -932,7 +935,7 @@ void checkFluidUpdate (short x, uint8_t y, short z, uint8_t block) {
uint8_t fluid; uint8_t fluid;
if (block >= B_water && block < B_water + 8) fluid = B_water; 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; else return;
handleFluidMovement(x, y, z, fluid, block); handleFluidMovement(x, y, z, fluid, block);
@@ -1303,6 +1306,10 @@ void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t da
// Killed by a less than 5 block fall // Killed by a less than 5 block fall
strcpy((char *)recv_buffer + player_name_len, " hit the ground too hard"); strcpy((char *)recv_buffer + player_name_len, " hit the ground too hard");
recv_buffer[player_name_len + 24] = '\0'; 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) { } else if (attacker_id < -1) {
// Killed by a mob // 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 a mob");
@@ -1314,6 +1321,10 @@ void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t da
strcpy((char *)recv_buffer + player_name_len, " was slain by "); strcpy((char *)recv_buffer + player_name_len, " was slain by ");
strcpy((char *)recv_buffer + player_name_len + 14, attacker->name); strcpy((char *)recv_buffer + player_name_len + 14, attacker->name);
recv_buffer[player_name_len + 14 + strlen(attacker->name)] = '\0'; 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; } else player->health -= effective_damage;
@@ -1386,31 +1397,37 @@ void handleServerTick (int64_t time_since_last_tick) {
if (player->flags & 0x20) { // Check "client loading" flag if (player->flags & 0x20) { // Check "client loading" flag
// If 3 seconds (60 vanilla ticks) have passed, assume player has loaded // If 3 seconds (60 vanilla ticks) have passed, assume player has loaded
player->flagval_16 ++; 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->flags &= ~0x20;
player->flagval_16 = 0; player->flagval_16 = 0;
} else continue; } 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 // Reset player attack cooldown
if (player->flags & 0x01) { 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->flags &= ~0x01;
player->flagval_8 = 0; player->flagval_8 = 0;
} else player->flagval_8 ++; } else player->flagval_8 ++;
} }
// Handle eating animation // Handle eating animation
if (player->flags & 0x10) { 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); handlePlayerEating(&player_data[i], false);
player->flags &= ~0x10; player->flags &= ~0x10;
player->flagval_16 = 0; player->flagval_16 = 0;
} else player->flagval_16 ++; } 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; 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->health >= 20 || player->health == 0) continue;
if (player->hunger < 18) continue; if (player->hunger < 18) continue;
if (player->saturation >= 600) { if (player->saturation >= 600) {
@@ -1438,6 +1455,7 @@ void handleServerTick (int64_t time_since_last_tick) {
// Tick mob behavior // Tick mob behavior
for (int i = 0; i < MAX_MOBS; i ++) { for (int i = 0; i < MAX_MOBS; i ++) {
if (mob_data[i].type == 0) continue; if (mob_data[i].type == 0) continue;
int entity_id = -2 - i;
// Handle deallocation on mob death // Handle deallocation on mob death
if ((mob_data[i].data & 31) == 0) { if ((mob_data[i].data & 31) == 0) {
@@ -1449,9 +1467,9 @@ void handleServerTick (int64_t time_since_last_tick) {
for (int j = 0; j < MAX_PLAYERS; j ++) { for (int j = 0; j < MAX_PLAYERS; j ++) {
if (player_data[j].client_fd == -1) continue; if (player_data[j].client_fd == -1) continue;
// Spawn death smoke particles // 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 // 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; continue;
} }
@@ -1465,7 +1483,7 @@ void handleServerTick (int64_t time_since_last_tick) {
// Burn hostile mobs if above ground during sunlight // Burn hostile mobs if above ground during sunlight
if (!passive && (world_time < 13000 || world_time > 23460) && mob_data[i].y > 48) { 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(); uint32_t r = fast_rand();
@@ -1518,7 +1536,7 @@ void handleServerTick (int64_t time_since_last_tick) {
// If we're already next to the player, hurt them and skip movement // 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) { 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; continue;
} }
@@ -1557,9 +1575,14 @@ void handleServerTick (int64_t time_since_last_tick) {
else continue; else continue;
} else { } else {
uint8_t block_below = getBlockAt(new_x, new_y - 1, new_z); 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 // Store new mob position
mob_data[i].x = new_x; mob_data[i].x = new_x;
mob_data[i].y = new_y; mob_data[i].y = new_y;
@@ -1568,7 +1591,6 @@ void handleServerTick (int64_t time_since_last_tick) {
// Broadcast relevant entity movement packets // Broadcast relevant entity movement packets
for (int j = 0; j < MAX_PLAYERS; j ++) { for (int j = 0; j < MAX_PLAYERS; j ++) {
if (player_data[j].client_fd == -1) continue; if (player_data[j].client_fd == -1) continue;
int entity_id = -2 - i;
sc_teleportEntity ( sc_teleportEntity (
player_data[j].client_fd, entity_id, player_data[j].client_fd, entity_id,
(double)new_x + 0.5, new_y, (double)new_z + 0.5, (double)new_x + 0.5, new_y, (double)new_z + 0.5,

View File

@@ -270,25 +270,38 @@ skip_feature:
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 as the seed for an
// The sum of these is then used to get the Y coordinate of the ore in this column // xorshift RNG/hash function to generate the Y coordinate of the ore
// This way, each column is guaranteed to have exactly one ore candidate // in this column. This way, each column is guaranteed to have exactly
uint8_t ore_x_component = (anchor.hash >> rx) & 31; // one ore candidate, as there will always be a Y value to reference.
uint8_t ore_z_component = (anchor.hash >> (rz + 16)) & 31; uint8_t ore_y = ((rx & 15) << 4) + (rz & 15);
uint8_t ore_y = ore_x_component + ore_z_component; ore_y ^= ore_y << 4;
ore_y ^= ore_y >> 5;
ore_y ^= ore_y << 1;
ore_y &= 63;
if (y == ore_y) { if (y == ore_y) {
// Since the ore Y coordinate is effectely a random number in range [0;64], // 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 // we use it in a bit shift with the chunk's anchor hash to get another
uint8_t ore_probability = (anchor.hash >> ore_y) & 255; // 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" // Ore placement is determined by Y level and "probability"
if (y < 15 && ore_probability < 15) return B_diamond_ore; if (y < 15) {
if (y < 30) { if (ore_probability < 10) return B_diamond_ore;
if (ore_probability < 5) return B_gold_ore; if (ore_probability < 12) return B_gold_ore;
if (ore_probability < 20) return B_redstone_ore; if (ore_probability < 15) return B_redstone_ore;
}
if (y < 30) {
if (ore_probability < 3) return B_gold_ore;
if (ore_probability < 8) return B_redstone_ore;
}
if (y < 54) {
if (ore_probability < 30) return B_iron_ore;
if (ore_probability < 40) return B_copper_ore;
} }
if (y < 54 && ore_probability < 50) return B_iron_ore;
if (ore_probability < 60) return B_coal_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 // For everything else, fall back to stone
@@ -363,6 +376,8 @@ 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) {
if (y < 0) return B_bedrock;
uint8_t block_change = getBlockChange(x, y, z); uint8_t block_change = getBlockChange(x, y, z);
if (block_change != 0xFF) return block_change; if (block_change != 0xFF) return block_change;