forked from EXTERNAL/bareiron
add player entities
This commit is contained in:
@@ -11,6 +11,8 @@ uint8_t recv_buffer[256] = {0};
|
||||
uint32_t world_seed = 0xA103DE6C;
|
||||
uint32_t rng_seed = 0xE2B9419;
|
||||
|
||||
uint16_t client_count;
|
||||
|
||||
BlockChange block_changes[20000];
|
||||
int block_changes_count = 0;
|
||||
|
||||
|
@@ -24,6 +24,10 @@
|
||||
// How many visited chunks to "remember"
|
||||
// The server will not re-send chunks that the player has recently been in
|
||||
#define VISITED_HISTORY 4
|
||||
// If defined, scales the frequency at which player movement updates are
|
||||
// broadcast based on the amount of players, reducing overhead for higher
|
||||
// player counts. For very many players, makes movement look jittery.
|
||||
#define SCALE_MOVEMENT_UPDATES_TO_PLAYER_COUNT
|
||||
|
||||
#define STATE_NONE 0
|
||||
#define STATE_STATUS 1
|
||||
@@ -38,6 +42,8 @@ extern uint8_t recv_buffer[256];
|
||||
extern uint32_t world_seed;
|
||||
extern uint32_t rng_seed;
|
||||
|
||||
extern uint16_t client_count;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
|
||||
typedef struct {
|
||||
@@ -49,12 +55,16 @@ typedef struct {
|
||||
|
||||
typedef struct {
|
||||
uint8_t uuid[16];
|
||||
char name[16];
|
||||
int client_fd;
|
||||
short x;
|
||||
short y;
|
||||
short z;
|
||||
short visited_x[VISITED_HISTORY];
|
||||
short visited_z[VISITED_HISTORY];
|
||||
#ifdef SCALE_MOVEMENT_UPDATES_TO_PLAYER_COUNT
|
||||
uint16_t packets_since_update;
|
||||
#endif
|
||||
int8_t yaw;
|
||||
int8_t pitch;
|
||||
uint8_t hotbar;
|
||||
|
74
src/main.c
74
src/main.c
@@ -43,9 +43,11 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
||||
if (cs_handshake(client_fd)) break;
|
||||
return;
|
||||
} else if (state == STATE_LOGIN) {
|
||||
if (cs_loginStart(client_fd)) break;
|
||||
if (reservePlayerData(client_fd, (char *)(recv_buffer + 17))) break;
|
||||
if (sc_loginSuccess(client_fd, (char *)recv_buffer, (char *)(recv_buffer + 17))) break;
|
||||
uint8_t uuid[16];
|
||||
char name[16];
|
||||
if (cs_loginStart(client_fd, uuid, name)) break;
|
||||
if (reservePlayerData(client_fd, uuid, name)) break;
|
||||
if (sc_loginSuccess(client_fd, uuid, name)) break;
|
||||
return;
|
||||
} else if (state == STATE_CONFIGURATION) {
|
||||
if (cs_clientInformation(client_fd)) break;
|
||||
@@ -122,6 +124,17 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
||||
// Re-synchronize player position after all chunks have been sent
|
||||
sc_synchronizePlayerPosition(client_fd, spawn_x, spawn_y, spawn_z, spawn_yaw, spawn_pitch);
|
||||
|
||||
// Register all existing players and spawn their entities, and broadcast
|
||||
// information about the joining player to all existing players.
|
||||
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||
if (player_data[i].client_fd == -1) continue;
|
||||
sc_playerInfoUpdateAddPlayer(client_fd, player_data[i]);
|
||||
if (player_data[i].client_fd == client_fd) continue;
|
||||
sc_playerInfoUpdateAddPlayer(player_data[i].client_fd, *player);
|
||||
sc_spawnEntityPlayer(client_fd, player_data[i]);
|
||||
sc_spawnEntityPlayer(player_data[i].client_fd, *player);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
break;
|
||||
@@ -157,6 +170,7 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
||||
|
||||
case 0x1D:
|
||||
case 0x1E:
|
||||
case 0x1F:
|
||||
if (state == STATE_PLAY) {
|
||||
|
||||
double x, y, z;
|
||||
@@ -164,27 +178,63 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
||||
|
||||
// Read player position (and rotation)
|
||||
if (packet_id == 0x1D) cs_setPlayerPosition(client_fd, &x, &y, &z);
|
||||
else if (packet_id == 0x1F) cs_setPlayerRotation (client_fd, &yaw, &pitch);
|
||||
else cs_setPlayerPositionAndRotation(client_fd, &x, &y, &z, &yaw, &pitch);
|
||||
// Cast the values to short to get integer position
|
||||
short cx = x, cy = y, cz = z;
|
||||
|
||||
PlayerData *player;
|
||||
if (getPlayerData(client_fd, &player)) break;
|
||||
|
||||
// Update rotation in player data (if applicable)
|
||||
if (packet_id != 0x1D) {
|
||||
player->yaw = ((short)(yaw + 540) % 360 - 180) * 127 / 180;
|
||||
player->pitch = pitch / 90.0f * 127.0f;
|
||||
}
|
||||
|
||||
// Broadcast player position to other players
|
||||
#ifdef SCALE_MOVEMENT_UPDATES_TO_PLAYER_COUNT
|
||||
// If applicable, broadcast only every client_count-th movement update
|
||||
uint8_t should_broadcast = false;
|
||||
if (player->packets_since_update++ == client_count) {
|
||||
should_broadcast = true;
|
||||
player->packets_since_update = 0;
|
||||
}
|
||||
#else
|
||||
#define should_broadcast (client_count > 0)
|
||||
#endif
|
||||
if (should_broadcast) {
|
||||
// If the packet had no rotation data, calculate it from player data
|
||||
if (packet_id == 0x1D) {
|
||||
yaw = player->yaw * 180 / 127;
|
||||
pitch = player->pitch * 90 / 127;
|
||||
}
|
||||
// Send current position data to all connected players
|
||||
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||
if (player_data[i].client_fd == -1) continue;
|
||||
if (player_data[i].client_fd == client_fd) continue;
|
||||
if (packet_id == 0x1F) {
|
||||
sc_updateEntityRotation(player_data[i].client_fd, client_fd, player->yaw, player->pitch);
|
||||
} else {
|
||||
sc_teleportEntity(player_data[i].client_fd, client_fd, x, y, z, yaw, pitch);
|
||||
}
|
||||
sc_setHeadRotation(player_data[i].client_fd, client_fd, player->yaw);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't continue if all we got was rotation data
|
||||
if (packet_id == 0x1F) return;
|
||||
|
||||
// Cast the values to short to get integer position
|
||||
short cx = x, cy = y, cz = z;
|
||||
// Determine the player's chunk coordinates
|
||||
short _x = (cx < 0 ? cx - 16 : cx) / 16, _z = (cz < 0 ? cz - 16 : cz) / 16;
|
||||
// Calculate distance between previous and current chunk coordinates
|
||||
short dx = _x - (player->x < 0 ? player->x - 16 : player->x) / 16;
|
||||
short dz = _z - (player->z < 0 ? player->z - 16 : player->z) / 16;
|
||||
|
||||
// Update position (and rotation) in player data
|
||||
// Update position in player data
|
||||
player->x = cx;
|
||||
player->y = cy;
|
||||
player->z = cz;
|
||||
if (packet_id == 0x1E) {
|
||||
player->yaw = ((short)(yaw + 540) % 360 - 180) * 127 / 180;
|
||||
player->pitch = pitch / 90.0f * 127.0f;
|
||||
}
|
||||
|
||||
// Exit early if no chunk borders were crossed
|
||||
if (dx == 0 && dz == 0) return;
|
||||
@@ -269,6 +319,8 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
||||
}
|
||||
|
||||
void disconnectClient (int *client_fd, int cause) {
|
||||
if (*client_fd == -1) return;
|
||||
client_count --;
|
||||
setClientState(*client_fd, STATE_NONE);
|
||||
clearPlayerFD(*client_fd);
|
||||
close(*client_fd);
|
||||
@@ -290,6 +342,7 @@ int main () {
|
||||
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||
clients[i] = -1;
|
||||
client_states[i * 2] = -1;
|
||||
player_data[i].client_fd = -1;
|
||||
}
|
||||
|
||||
// Create socket
|
||||
@@ -350,6 +403,7 @@ int main () {
|
||||
printf("New client, fd: %d\n", clients[i]);
|
||||
int flags = fcntl(clients[i], F_GETFL, 0);
|
||||
fcntl(clients[i], F_SETFL, flags | O_NONBLOCK);
|
||||
client_count ++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
155
src/packets.c
155
src/packets.c
@@ -36,27 +36,28 @@ int cs_handshake (int client_fd) {
|
||||
}
|
||||
|
||||
// C->S Login Start
|
||||
// Leaves player name and UUID at indexes 0 and 17 of recv_buffer, respectively
|
||||
int cs_loginStart (int client_fd) {
|
||||
int cs_loginStart (int client_fd, uint8_t *uuid, char *name) {
|
||||
printf("Received Login Start:\n");
|
||||
|
||||
readString(client_fd);
|
||||
if (recv_count == -1) return 1;
|
||||
printf(" Player name: %s\n", recv_buffer);
|
||||
recv_count = recv_all(client_fd, recv_buffer + 17, 16, false);
|
||||
memcpy(name, recv_buffer, strlen((char *)recv_buffer) + 1);
|
||||
printf(" Player name: %s\n", name);
|
||||
recv_count = recv_all(client_fd, recv_buffer, 16, false);
|
||||
if (recv_count == -1) return 1;
|
||||
memcpy(uuid, recv_buffer, 16);
|
||||
printf(" Player UUID: ");
|
||||
for (int i = 17; i < 33; i ++) printf("%x", recv_buffer[i]);
|
||||
for (int i = 0; i < 16; i ++) printf("%x", uuid[i]);
|
||||
printf("\n\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// S->C Login Success
|
||||
int sc_loginSuccess (int client_fd, char *name, char *uuid) {
|
||||
int sc_loginSuccess (int client_fd, uint8_t *uuid, char *name) {
|
||||
printf("Sending Login Success...\n\n");
|
||||
|
||||
int name_length = strlen(name);
|
||||
uint8_t name_length = strlen(name);
|
||||
writeVarInt(client_fd, 1 + 16 + sizeVarInt(name_length) + name_length + 1);
|
||||
writeVarInt(client_fd, 0x02);
|
||||
send(client_fd, uuid, 16, 0);
|
||||
@@ -649,6 +650,19 @@ int cs_setPlayerPosition (int client_fd, double *x, double *y, double *z) {
|
||||
|
||||
}
|
||||
|
||||
// C->S Set Player Rotation
|
||||
int cs_setPlayerRotation (int client_fd, float *yaw, float *pitch) {
|
||||
|
||||
*yaw = readFloat(client_fd);
|
||||
*pitch = readFloat(client_fd);
|
||||
|
||||
// ignore flags
|
||||
readByte(client_fd);
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
// C->S Set Held Item (serverbound)
|
||||
int cs_setHeldItem (int client_fd) {
|
||||
|
||||
@@ -691,6 +705,133 @@ int cs_closeContainer (int client_fd) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// S->C Player Info Update, "Add Player" action
|
||||
int sc_playerInfoUpdateAddPlayer (int client_fd, PlayerData player) {
|
||||
|
||||
writeVarInt(client_fd, 21 + strlen(player.name)); // Packet length
|
||||
writeByte(client_fd, 0x3F); // Packet ID
|
||||
|
||||
writeByte(client_fd, 0x01); // EnumSet: Add Player
|
||||
writeByte(client_fd, 1); // Player count (1 per packet)
|
||||
|
||||
// Player UUID
|
||||
send(client_fd, player.uuid, 16, 0);
|
||||
// Player name
|
||||
writeByte(client_fd, strlen(player.name));
|
||||
send(client_fd, player.name, strlen(player.name), 0);
|
||||
// Properties (don't send any)
|
||||
writeByte(client_fd, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// S->C Spawn Entity
|
||||
int sc_spawnEntity (
|
||||
int client_fd,
|
||||
int id, uint8_t *uuid, int type,
|
||||
double x, double y, double z,
|
||||
uint8_t yaw, uint8_t pitch
|
||||
) {
|
||||
|
||||
writeVarInt(client_fd, 51 + sizeVarInt(id) + sizeVarInt(type));
|
||||
writeByte(client_fd, 0x01);
|
||||
|
||||
writeVarInt(client_fd, id); // Entity ID
|
||||
send(client_fd, uuid, 16, 0); // Entity UUID
|
||||
writeVarInt(client_fd, type); // Entity type
|
||||
|
||||
// Position
|
||||
writeDouble(client_fd, x);
|
||||
writeDouble(client_fd, y);
|
||||
writeDouble(client_fd, z);
|
||||
|
||||
// Angles
|
||||
writeByte(client_fd, yaw);
|
||||
writeByte(client_fd, pitch);
|
||||
writeByte(client_fd, yaw);
|
||||
|
||||
// Data - mostly unused
|
||||
writeByte(client_fd, 0);
|
||||
|
||||
// Velocity
|
||||
writeUint16(client_fd, 0);
|
||||
writeUint16(client_fd, 0);
|
||||
writeUint16(client_fd, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// S->C Spawn Entity (from PlayerData)
|
||||
int sc_spawnEntityPlayer (int client_fd, PlayerData player) {
|
||||
return sc_spawnEntity(
|
||||
client_fd,
|
||||
player.client_fd, player.uuid, 149,
|
||||
player.x > 0 ? (double)player.x + 0.5 : (double)player.x - 0.5,
|
||||
player.y,
|
||||
player.z > 0 ? (double)player.z + 0.5 : (float)player.z - 0.5,
|
||||
player.yaw, player.pitch
|
||||
);
|
||||
}
|
||||
|
||||
// S->C Teleport Entity
|
||||
int sc_teleportEntity (
|
||||
int client_fd, int id,
|
||||
double x, double y, double z,
|
||||
float yaw, float pitch
|
||||
) {
|
||||
|
||||
// Packet length and ID
|
||||
writeVarInt(client_fd, 58 + sizeVarInt(id));
|
||||
writeByte(client_fd, 0x1F);
|
||||
|
||||
// Entity ID
|
||||
writeVarInt(client_fd, id);
|
||||
// Position
|
||||
writeDouble(client_fd, x);
|
||||
writeDouble(client_fd, y);
|
||||
writeDouble(client_fd, z);
|
||||
// Velocity
|
||||
writeUint64(client_fd, 0);
|
||||
writeUint64(client_fd, 0);
|
||||
writeUint64(client_fd, 0);
|
||||
// Angles
|
||||
writeFloat(client_fd, yaw);
|
||||
writeFloat(client_fd, pitch);
|
||||
// On ground flag
|
||||
writeByte(client_fd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_setHeadRotation (int client_fd, int id, uint8_t yaw) {
|
||||
|
||||
// Packet length and ID
|
||||
writeByte(client_fd, 2 + sizeVarInt(id));
|
||||
writeByte(client_fd, 0x4C);
|
||||
// Entity ID
|
||||
writeVarInt(client_fd, id);
|
||||
// Head yaw
|
||||
writeByte(client_fd, yaw);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sc_updateEntityRotation (int client_fd, int id, uint8_t yaw, uint8_t pitch) {
|
||||
|
||||
// Packet length and ID
|
||||
writeByte(client_fd, 4 + sizeVarInt(id));
|
||||
writeByte(client_fd, 0x31);
|
||||
// Entity ID
|
||||
writeVarInt(client_fd, id);
|
||||
// Angles
|
||||
writeByte(client_fd, yaw);
|
||||
writeByte(client_fd, pitch);
|
||||
// "On ground" flag
|
||||
writeByte(client_fd, 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// S->C Registry Data (multiple packets) and Update Tags (configuration, multiple packets)
|
||||
int sc_registries (int client_fd) {
|
||||
|
||||
|
@@ -2,18 +2,19 @@
|
||||
#define H_PACKETS
|
||||
|
||||
int cs_handshake (int client_fd);
|
||||
int cs_loginStart (int client_fd);
|
||||
int cs_loginStart (int client_fd, uint8_t *uuid, char *name);
|
||||
int cs_clientInformation (int client_fd);
|
||||
int cs_pluginMessage (int client_fd);
|
||||
int cs_playerAction (int client_fd);
|
||||
int cs_useItemOn (int client_fd);
|
||||
int cs_setPlayerPositionAndRotation (int client_fd, double *x, double *y, double *z, float *yaw, float *pitch);
|
||||
int cs_setPlayerPosition (int client_fd, double *x, double *y, double *z);
|
||||
int cs_setPlayerRotation(int client_fd, float *yaw, float *pitch);
|
||||
int cs_setHeldItem (int client_fd);
|
||||
int cs_clickContainer (int client_fd);
|
||||
int cs_closeContainer (int client_fd);
|
||||
|
||||
int sc_loginSuccess (int client_fd, char *name, char *uuid);
|
||||
int sc_loginSuccess (int client_fd, uint8_t *uuid, char *name);
|
||||
int sc_knownPacks (int client_fd);
|
||||
int sc_finishConfiguration (int client_fd);
|
||||
int sc_loginPlay (int client_fd);
|
||||
@@ -30,6 +31,12 @@ 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_acknowledgeBlockChange (int client_fd, int sequence);
|
||||
int sc_playerInfoUpdateAddPlayer (int client_fd, PlayerData player);
|
||||
int sc_spawnEntity (int client_fd, int id, uint8_t *uuid, int type, double x, double y, double z, double yaw, double pitch);
|
||||
int sc_spawnEntityPlayer (int client_fd, PlayerData player);
|
||||
int sc_teleportEntity (int client_fd, int id, double x, double y, double z, float yaw, float pitch);
|
||||
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_registries(int client_fd);
|
||||
|
||||
#endif
|
||||
|
@@ -195,11 +195,12 @@ int getClientIndex (int client_fd) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int reservePlayerData (int client_fd, char *uuid) {
|
||||
int reservePlayerData (int client_fd, uint8_t *uuid, char *name) {
|
||||
|
||||
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||
if (memcmp(player_data[i].uuid, uuid, 16) == 0) {
|
||||
player_data[i].client_fd = client_fd;
|
||||
memcpy(player_data[i].name, name, 16);
|
||||
return 0;
|
||||
}
|
||||
uint8_t empty = true;
|
||||
@@ -212,6 +213,7 @@ int reservePlayerData (int client_fd, char *uuid) {
|
||||
if (empty) {
|
||||
player_data[i].client_fd = client_fd;
|
||||
memcpy(player_data[i].uuid, uuid, 16);
|
||||
memcpy(player_data[i].name, name, 16);
|
||||
player_data[i].y = -32767;
|
||||
return 0;
|
||||
}
|
||||
@@ -234,7 +236,7 @@ int getPlayerData (int client_fd, PlayerData **output) {
|
||||
void clearPlayerFD (int client_fd) {
|
||||
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||
if (player_data[i].client_fd == client_fd) {
|
||||
player_data[i].client_fd = 0;
|
||||
player_data[i].client_fd = -1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@@ -34,7 +34,7 @@ void setClientState (int client_fd, int new_state);
|
||||
int getClientState (int client_fd);
|
||||
int getClientIndex (int client_fd);
|
||||
|
||||
int reservePlayerData (int client_fd, char *uuid);
|
||||
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);
|
||||
|
Reference in New Issue
Block a user