Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion include/globals.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,9 @@
// Doesn't implement authentication, hence disabled by default.
// #define DEV_ENABLE_BEEF_DUMPS

// If defined, mobs are allowed to affect terrain
#define MOB_GRIEFING

#define STATE_NONE 0
#define STATE_STATUS 1
#define STATE_LOGIN 2
Expand Down Expand Up @@ -253,12 +256,13 @@ typedef struct {

union EntityDataValue {
uint8_t byte;
int pose;
int varint; // Also used for poses
};

typedef struct {
uint8_t index;
// 0 - Byte
// 1 - VarInt
// 21 - Pose
int type;
union EntityDataValue value;
Expand Down
33 changes: 26 additions & 7 deletions src/main.c
Comment thread
ccuser44 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -377,14 +377,33 @@ void handlePacket (int client_fd, int length, int packet_id, int state) {
if (mob_y != 255) {
// Spawn passive mobs above ground during the day,
// or hostiles underground and during the night
uint32_t mob_choice = (r >> 12) & 3;
if ((world_time < 13000 || world_time > 23460) && mob_y > 48) {
uint32_t mob_choice = (r >> 12) & 3;
if (mob_choice == 0) spawnMob(25, mob_x, mob_y, mob_z, 4); // Chicken
else if (mob_choice == 1) spawnMob(28, mob_x, mob_y, mob_z, 10); // Cow
else if (mob_choice == 2) spawnMob(95, mob_x, mob_y, mob_z, 10); // Pig
else if (mob_choice == 3) spawnMob(106, mob_x, mob_y, mob_z, 8); // Sheep
switch (mob_choice) {
case 0:
spawnMob(25, mob_x, mob_y, mob_z, 4); // Chicken
case 1:
spawnMob(28, mob_x, mob_y, mob_z, 10); // Cow
case 2:
spawnMob(95, mob_x, mob_y, mob_z, 10); // Pig
case 3:
spawnMob(106, mob_x, mob_y, mob_z, 8); // Sheep
default:
break;
}
} else {
spawnMob(145, mob_x, mob_y, mob_z, 20); // Zombie
switch (mob_choice) {
case 0:
case 1:
case 2:
spawnMob(/*(biome == W_desert) ? 65 :*/ 145, mob_x, mob_y, mob_z, 20); // Zombie
break;
case 3:
spawnMob(30, mob_x, mob_y, mob_z, 8); // Creeper, aww man
break;
default:
break;
}
}
}
}
Expand Down Expand Up @@ -443,7 +462,7 @@ void handlePacket (int client_fd, int length, int packet_id, int state) {
case 0x34:
if (state == STATE_PLAY) cs_setHeldItem(client_fd);
break;
case 0x3C:
if (state == STATE_PLAY) cs_swingArm(client_fd);
break;
Expand Down
98 changes: 84 additions & 14 deletions src/procedures.c
Original file line number Diff line number Diff line change
Expand Up @@ -398,14 +398,14 @@ void broadcastPlayerMetadata (PlayerData *player) {

EntityData metadata[] = {
{
0, // Index (Entity Bit Mask)
0, // Type (Byte)
{ entity_bit_mask }, // Value
0, // Index (Entity Bit Mask)
0, // Type (Byte)
entity_bit_mask, // Value
},
{
6, // Index (Pose),
21, // Type (Pose),
{ pose }, // Value (Standing)
6, // Index (Pose),
21, // Type (Pose),
pose, // Value (Standing)
}
};

Expand All @@ -431,15 +431,28 @@ void broadcastMobMetadata (int client_fd, int entity_id) {
size_t length;

switch (mob->type) {
case 30: { // Creeper
int fuse;
if ((mob->data >> 6) & 3) fuse = 1;
else fuse = -1;
metadata = malloc(sizeof *metadata);
metadata[0] = (EntityData){
16, // Index (Fuse)
1, // Type (VarInt)
{ .varint = fuse }, // Value
};
length = 1;
break;
}
case 106: // Sheep
if (!((mob->data >> 5) & 1)) // Don't send metadata if sheep isn't sheared
return;

metadata = malloc(sizeof *metadata);
metadata[0] = (EntityData){
17, // Index (Sheep Bit Mask),
0, // Type (Byte),
{ (uint8_t)0x10 }, // Value
17, // Index (Sheep Bit Mask),
0, // Type (Byte),
(uint8_t)0x10, // Value
};
length = 1;

Expand Down Expand Up @@ -1374,6 +1387,39 @@ void handlePlayerUseItem (PlayerData *player, short x, short y, short z, uint8_t

}

void createExplosion (short x, uint8_t y, short z, short radius, int8_t damage, int attacker) {
#ifdef MOB_GRIEFING
if (true) {
#else
if (attacker == 0) {
#endif
for (short xI = x - radius; xI <= x + radius; xI++) {
for (uint8_t yI = y - radius; yI <= y + radius; yI++) {
for (short zI = z - radius; zI <= z + radius; zI++) {
short randRad = (radius * (fast_rand() / 0x1FFFFFF + 192)) / 255;

if (((xI- x) * (xI - x) + (yI - y) * (yI - y) + (zI - z) * (zI - z)) <= randRad*randRad) {
makeBlockChange(xI, yI, zI, B_air);
}
}
}
}
}

if (damage != 0) {
for (int i = 0; i < MAX_PLAYERS; i ++) {
if (player_data[i].client_fd == -1) continue;
short x2 = x - player_data[i].x, y2 = z - player_data[i].z;
if (x2 * x2 + y2 * y2 < radius * radius) hurtEntity(player_data[i].client_fd, attacker, D_explosion, 10);
}
for (int i = 0; i < MAX_MOBS; i ++) {
if (mob_data[i].type == 0) continue;
short x2 = x - mob_data[i].x, y2 = z - mob_data[i].z;
if (x2 * x2 + y2 * y2 < radius * radius) hurtEntity(-2 - i, attacker, D_explosion, 10);
}
}
}

void spawnMob (uint8_t type, short x, uint8_t y, short z, uint8_t health) {

for (int i = 0; i < MAX_MOBS; i ++) {
Expand Down Expand Up @@ -1523,6 +1569,15 @@ void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t da
// 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 (damage_type == D_explosion) {
// Killed by an explosion
if (attacker_id < -1) {
strcpy((char *)recv_buffer + player_name_len, " blown up by a mob");
recv_buffer[player_name_len + 18] = '\0';
} else {
strcpy((char *)recv_buffer + player_name_len, " blew up");
recv_buffer[player_name_len + 8] = '\0';
}
} else if (attacker_id < -1) {
// Killed by a mob
strcpy((char *)recv_buffer + player_name_len, " was slain by a mob");
Expand Down Expand Up @@ -1574,6 +1629,7 @@ void hurtEntity (int entity_id, int attacker_id, uint8_t damage_type, uint8_t da
switch (mob->type) {
case 25: givePlayerItem(player, I_chicken, 1); break;
case 28: givePlayerItem(player, I_beef, 1 + (fast_rand() % 3)); break;
case 30: givePlayerItem(player, I_gunpowder, (fast_rand() % 3)); break;
case 95: givePlayerItem(player, I_porkchop, 1 + (fast_rand() % 3)); break;
case 106: givePlayerItem(player, I_mutton, 1 + (fast_rand() & 1)); break;
case 145: givePlayerItem(player, I_rotten_flesh, (fast_rand() % 3)); break;
Expand Down Expand Up @@ -1717,8 +1773,8 @@ void handleServerTick (int64_t time_since_last_tick) {
// Currently has no effect on hostile mobs
uint8_t panic = (mob_data[i].data >> 6) & 3;

// Burn hostile mobs if above ground during sunlight
if (!passive && (world_time < 13000 || world_time > 23460) && mob_data[i].y > 48) {
// Burn undead mobs if above ground during sunlight
if (mob_data[i].type == 145 && (world_time < 13000 || world_time > 23460) && mob_data[i].y > 48) {
hurtEntity(entity_id, -1, D_on_fire, 2);
}

Expand Down Expand Up @@ -1788,8 +1844,20 @@ void handleServerTick (int64_t time_since_last_tick) {

// If we're already next to the player, hurt them and skip movement
if (closest_dist < 3 && abs(old_y - closest_player->y) < 2) {
hurtEntity(closest_player->client_fd, entity_id, D_generic, 6);
if (mob_data[i].type == 30) { // If mob is a creeper explode instead of deal meelee damage
if (panic >= 2) {
createExplosion(mob_data[i].x, mob_data[i].y, mob_data[i].z, 4, 10, entity_id);
} else if (server_ticks % (uint32_t)TICKS_PER_SECOND == 0) {
mob_data[i].data += (1 << 6);
broadcastMobMetadata(-1, entity_id);
}
} else {
hurtEntity(closest_player->client_fd, entity_id, D_generic, 6);
}
Comment thread
ccuser44 marked this conversation as resolved.
continue;
} else if (mob_data[i].type == 30 && panic != 0) { // Defuse creeper
mob_data[i].data &= 0x3F;
broadcastMobMetadata(-1, entity_id);
}

// Move towards the closest player on 8 axis
Expand Down Expand Up @@ -1936,8 +2004,9 @@ ssize_t writeEntityData (int client_fd, EntityData *data) {
switch (data->type) {
case 0: // Byte
return writeByte(client_fd, data->value.byte);
case 1: // VarInt
case 21: // Pose
writeVarInt(client_fd, data->value.pose);
writeVarInt(client_fd, data->value.varint);
return 0;

default: return -1;
Expand All @@ -1952,8 +2021,9 @@ int sizeEntityData (EntityData *data) {
case 0: // Byte
value_size = 1;
break;
case 1: // VarInt
case 21: // Pose
value_size = sizeVarInt(data->value.pose);
value_size = sizeVarInt(data->value.varint);
break;

default: return -1;
Expand Down