mirror of
https://github.com/p2r3/bareiron.git
synced 2025-10-02 07:35:08 +02:00
support multiple simultaneous connections
This commit is contained in:
159
src/main.c
159
src/main.c
@@ -2,6 +2,8 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#ifndef CLOCK_REALTIME
|
#ifndef CLOCK_REALTIME
|
||||||
#define CLOCK_REALTIME 0
|
#define CLOCK_REALTIME 0
|
||||||
@@ -266,16 +268,30 @@ void handlePacket (int client_fd, int length, int packet_id) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void disconnectClient (int *client_fd, int cause) {
|
||||||
|
setClientState(*client_fd, STATE_NONE);
|
||||||
|
clearPlayerFD(*client_fd);
|
||||||
|
close(*client_fd);
|
||||||
|
*client_fd = -1;
|
||||||
|
printf("Disconnected client %d, cause: %d, errno: %d\n\n", *client_fd, cause, errno);
|
||||||
|
}
|
||||||
|
|
||||||
int main () {
|
int main () {
|
||||||
|
|
||||||
for (int i = 0; i < sizeof(block_changes) / sizeof(BlockChange); i ++) {
|
for (int i = 0; i < sizeof(block_changes) / sizeof(BlockChange); i ++) {
|
||||||
block_changes[i].block = 0xFF;
|
block_changes[i].block = 0xFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
int server_fd, client_fd, opt = 1;
|
int server_fd, opt = 1;
|
||||||
struct sockaddr_in server_addr, client_addr;
|
struct sockaddr_in server_addr, client_addr;
|
||||||
socklen_t addr_len = sizeof(client_addr);
|
socklen_t addr_len = sizeof(client_addr);
|
||||||
|
|
||||||
|
int clients[MAX_PLAYERS], client_index = 0;
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||||
|
clients[i] = -1;
|
||||||
|
client_states[i * 2] = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// Create socket
|
// Create socket
|
||||||
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
server_fd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (server_fd == -1) {
|
if (server_fd == -1) {
|
||||||
@@ -305,62 +321,99 @@ int main () {
|
|||||||
close(server_fd);
|
close(server_fd);
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Server listening on port %d...\n", PORT);
|
printf("Server listening on port %d...\n", PORT);
|
||||||
|
|
||||||
|
// Set non-blocking socket flag
|
||||||
|
int flags = fcntl(server_fd, F_GETFL, 0);
|
||||||
|
fcntl(server_fd, F_SETFL, flags | O_NONBLOCK);
|
||||||
|
|
||||||
|
// Track client keep-alives
|
||||||
|
struct timespec time_now;
|
||||||
|
struct timespec keepalive_last;
|
||||||
|
clock_gettime(CLOCK_REALTIME, &time_now);
|
||||||
|
clock_gettime(CLOCK_REALTIME, &keepalive_last);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cycles through all connected clients, handling one packet at a time
|
||||||
|
* from each player. With every iteration, attempts to accept a new
|
||||||
|
* client connection.
|
||||||
|
*/
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
// Accept a connection
|
|
||||||
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
|
|
||||||
if (client_fd < 0) {
|
|
||||||
perror("accept failed");
|
|
||||||
close(server_fd);
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Client connected.\n");
|
|
||||||
|
|
||||||
struct timespec time_now;
|
|
||||||
struct timespec keepalive_last;
|
|
||||||
clock_gettime(CLOCK_REALTIME, &time_now);
|
|
||||||
clock_gettime(CLOCK_REALTIME, &keepalive_last);
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
|
|
||||||
if (getClientState(client_fd) == STATE_PLAY) {
|
|
||||||
clock_gettime(CLOCK_REALTIME, &time_now);
|
|
||||||
if (time_now.tv_sec - keepalive_last.tv_sec > 10) {
|
|
||||||
sc_keepAlive(client_fd);
|
|
||||||
sc_updateTime(client_fd, world_time += 200);
|
|
||||||
clock_gettime(CLOCK_REALTIME, &keepalive_last);
|
|
||||||
/**
|
|
||||||
* If the RNG seed ever hits 0, it'll never generate anything
|
|
||||||
* else. This is because the fast_rand function uses a simple
|
|
||||||
* XORshift. This isn't a common concern, so we only check for
|
|
||||||
* this periodically. If it does become zero, we reset it to
|
|
||||||
* the world seed as a good-enough fallback.
|
|
||||||
*/
|
|
||||||
if (rng_seed == 0) rng_seed = world_seed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int length = readVarInt(client_fd);
|
|
||||||
if (length == VARNUM_ERROR) break;
|
|
||||||
int packet_id = readVarInt(client_fd);
|
|
||||||
if (packet_id == VARNUM_ERROR) break;
|
|
||||||
handlePacket(client_fd, length - sizeVarInt(packet_id), packet_id);
|
|
||||||
if (recv_count == -1) break;
|
|
||||||
|
|
||||||
wdt_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
setClientState(client_fd, STATE_NONE);
|
|
||||||
clearPlayerFD(client_fd);
|
|
||||||
|
|
||||||
close(client_fd);
|
|
||||||
printf("Connection closed.\n");
|
|
||||||
|
|
||||||
wdt_reset();
|
wdt_reset();
|
||||||
|
|
||||||
|
// Attempt to accept a new connection
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||||
|
if (clients[i] != -1) continue;
|
||||||
|
clients[i] = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
|
||||||
|
// If the accept was successful, make the client non-blocking too
|
||||||
|
if (clients[i] != -1) {
|
||||||
|
printf("New client, fd: %d\n", clients[i]);
|
||||||
|
int flags = fcntl(clients[i], F_GETFL, 0);
|
||||||
|
fcntl(clients[i], F_SETFL, flags | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for valid connected clients
|
||||||
|
client_index ++;
|
||||||
|
if (client_index == MAX_PLAYERS) client_index = 0;
|
||||||
|
if (clients[client_index] == -1) continue;
|
||||||
|
|
||||||
|
// Handle infrequent periodic events every 10 seconds
|
||||||
|
clock_gettime(CLOCK_REALTIME, &time_now);
|
||||||
|
time_t seconds_since_update = time_now.tv_sec - keepalive_last.tv_sec;
|
||||||
|
if (seconds_since_update > 10) {
|
||||||
|
// Send Keep Alive and Update Time packets to all in-game clients
|
||||||
|
world_time += 20 * seconds_since_update;
|
||||||
|
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||||
|
if (clients[i] == -1) continue;
|
||||||
|
if (getClientState(clients[i]) != STATE_PLAY) continue;
|
||||||
|
sc_keepAlive(clients[i]);
|
||||||
|
sc_updateTime(clients[i], world_time);
|
||||||
|
}
|
||||||
|
// Reset keep-alive timer
|
||||||
|
clock_gettime(CLOCK_REALTIME, &keepalive_last);
|
||||||
|
/**
|
||||||
|
* If the RNG seed ever hits 0, it'll never generate anything
|
||||||
|
* else. This is because the fast_rand function uses a simple
|
||||||
|
* XORshift. This isn't a common concern, so we only check for
|
||||||
|
* this periodically. If it does become zero, we reset it to
|
||||||
|
* the world seed as a good-enough fallback.
|
||||||
|
*/
|
||||||
|
if (rng_seed == 0) rng_seed = world_seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle this individual client
|
||||||
|
int client_fd = clients[client_index];
|
||||||
|
|
||||||
|
// Check if at least 2 bytes are available for reading
|
||||||
|
ssize_t recv_count = recv(client_fd, &recv_buffer, 2, MSG_PEEK);
|
||||||
|
if (recv_count < 2) {
|
||||||
|
if (recv_count == 0 || (recv_count < 0 && errno != EAGAIN && errno != EWOULDBLOCK)) {
|
||||||
|
disconnectClient(&clients[client_index], 1);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read packet length
|
||||||
|
int length = readVarInt(client_fd);
|
||||||
|
if (length == VARNUM_ERROR) {
|
||||||
|
disconnectClient(&clients[client_index], 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Read packet ID
|
||||||
|
int packet_id = readVarInt(client_fd);
|
||||||
|
if (packet_id == VARNUM_ERROR) {
|
||||||
|
disconnectClient(&clients[client_index], 3);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Handle packet data
|
||||||
|
handlePacket(client_fd, length - sizeVarInt(packet_id), packet_id);
|
||||||
|
if (recv_count == 0 || (recv_count == -1 && errno != EAGAIN && errno != EWOULDBLOCK)) {
|
||||||
|
disconnectClient(&clients[client_index], 4);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close(server_fd);
|
close(server_fd);
|
||||||
|
@@ -43,7 +43,7 @@ int cs_loginStart (int client_fd) {
|
|||||||
readString(client_fd);
|
readString(client_fd);
|
||||||
if (recv_count == -1) return 1;
|
if (recv_count == -1) return 1;
|
||||||
printf(" Player name: %s\n", recv_buffer);
|
printf(" Player name: %s\n", recv_buffer);
|
||||||
recv_count = recv(client_fd, recv_buffer + 17, 16, MSG_WAITALL);
|
recv_count = recv_all(client_fd, recv_buffer + 17, 16, false);
|
||||||
if (recv_count == -1) return 1;
|
if (recv_count == -1) return 1;
|
||||||
printf(" Player UUID: ");
|
printf(" Player UUID: ");
|
||||||
for (int i = 17; i < 33; i ++) printf("%x", recv_buffer[i]);
|
for (int i = 17; i < 33; i ++) printf("%x", recv_buffer[i]);
|
||||||
@@ -413,6 +413,14 @@ int sc_blockUpdate (int client_fd, int64_t x, int64_t y, int64_t z, uint8_t bloc
|
|||||||
writeVarInt(client_fd, block_palette[block]);
|
writeVarInt(client_fd, block_palette[block]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// S->C Acknowledge Block Change
|
||||||
|
int sc_acknowledgeBlockChange (int client_fd, int sequence) {
|
||||||
|
writeVarInt(client_fd, 1 + sizeVarInt(sequence));
|
||||||
|
writeByte(client_fd, 0x04);
|
||||||
|
writeVarInt(client_fd, sequence);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// C->S Player Action
|
// C->S Player Action
|
||||||
int cs_playerAction (int client_fd) {
|
int cs_playerAction (int client_fd) {
|
||||||
|
|
||||||
@@ -426,6 +434,7 @@ int cs_playerAction (int client_fd) {
|
|||||||
readByte(client_fd); // ignore face
|
readByte(client_fd); // ignore face
|
||||||
|
|
||||||
int sequence = readVarInt(client_fd);
|
int sequence = readVarInt(client_fd);
|
||||||
|
sc_acknowledgeBlockChange(client_fd, sequence);
|
||||||
|
|
||||||
if ((action == 0 && GAMEMODE == 1)) {
|
if ((action == 0 && GAMEMODE == 1)) {
|
||||||
// block was mined in creative
|
// block was mined in creative
|
||||||
@@ -488,6 +497,7 @@ int cs_useItemOn (int client_fd) {
|
|||||||
readByte(client_fd);
|
readByte(client_fd);
|
||||||
|
|
||||||
int sequence = readVarInt(client_fd);
|
int sequence = readVarInt(client_fd);
|
||||||
|
sc_acknowledgeBlockChange(client_fd, sequence);
|
||||||
|
|
||||||
uint8_t target = getBlockAt(x, y, z);
|
uint8_t target = getBlockAt(x, y, z);
|
||||||
if (target == B_crafting_table) {
|
if (target == B_crafting_table) {
|
||||||
@@ -572,9 +582,9 @@ int cs_clickContainer (int client_fd) {
|
|||||||
|
|
||||||
// ignore components
|
// ignore components
|
||||||
tmp = readVarInt(client_fd);
|
tmp = readVarInt(client_fd);
|
||||||
recv(client_fd, recv_buffer, tmp, MSG_WAITALL);
|
recv_all(client_fd, recv_buffer, tmp, false);
|
||||||
tmp = readVarInt(client_fd);
|
tmp = readVarInt(client_fd);
|
||||||
recv(client_fd, recv_buffer, tmp, MSG_WAITALL);
|
recv_all(client_fd, recv_buffer, tmp, false);
|
||||||
|
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
player->inventory_items[slot] = item;
|
player->inventory_items[slot] = item;
|
||||||
@@ -599,9 +609,9 @@ int cs_clickContainer (int client_fd) {
|
|||||||
readVarInt(client_fd);
|
readVarInt(client_fd);
|
||||||
readVarInt(client_fd);
|
readVarInt(client_fd);
|
||||||
tmp = readVarInt(client_fd);
|
tmp = readVarInt(client_fd);
|
||||||
recv(client_fd, recv_buffer, tmp, MSG_WAITALL);
|
recv_all(client_fd, recv_buffer, tmp, false);
|
||||||
tmp = readVarInt(client_fd);
|
tmp = readVarInt(client_fd);
|
||||||
recv(client_fd, recv_buffer, tmp, MSG_WAITALL);
|
recv_all(client_fd, recv_buffer, tmp, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -29,6 +29,7 @@ int sc_setContainerSlot (int client_fd, int window_id, uint16_t slot, uint8_t co
|
|||||||
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_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_acknowledgeBlockChange (int client_fd, int sequence);
|
||||||
int sc_registries(int client_fd);
|
int sc_registries(int client_fd);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
63
src/tools.c
63
src/tools.c
@@ -1,6 +1,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#ifdef ESP_PLATFORM
|
#ifdef ESP_PLATFORM
|
||||||
#include "lwip/sockets.h"
|
#include "lwip/sockets.h"
|
||||||
@@ -26,6 +27,42 @@ static uint64_t htonll (uint64_t value) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first) {
|
||||||
|
char *p = buf;
|
||||||
|
size_t total = 0;
|
||||||
|
|
||||||
|
// First-byte check if requested
|
||||||
|
if (require_first) {
|
||||||
|
ssize_t r = recv(client_fd, p, 1, MSG_PEEK);
|
||||||
|
if (r <= 0) {
|
||||||
|
if (r < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
|
||||||
|
return 0; // no first byte available yet
|
||||||
|
}
|
||||||
|
return -1; // error or connection closed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Busy-wait until we get exactly n bytes
|
||||||
|
while (total < n) {
|
||||||
|
ssize_t r = recv(client_fd, p + total, n - total, 0);
|
||||||
|
if (r < 0) {
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK) {
|
||||||
|
// spin until data arrives
|
||||||
|
wdt_reset();
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return -1; // real error
|
||||||
|
}
|
||||||
|
} else if (r == 0) {
|
||||||
|
// connection closed before full read
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
total += r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total; // got exactly n bytes
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t writeByte (int client_fd, uint8_t byte) {
|
ssize_t writeByte (int client_fd, uint8_t byte) {
|
||||||
return send(client_fd, &byte, 1, 0);
|
return send(client_fd, &byte, 1, 0);
|
||||||
}
|
}
|
||||||
@@ -55,22 +92,22 @@ ssize_t writeDouble (int client_fd, double num) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t readByte (int client_fd) {
|
uint8_t readByte (int client_fd) {
|
||||||
recv_count = recv(client_fd, recv_buffer, 1, MSG_WAITALL);
|
recv_count = recv_all(client_fd, recv_buffer, 1, false);
|
||||||
return recv_buffer[0];
|
return recv_buffer[0];
|
||||||
}
|
}
|
||||||
uint16_t readUint16 (int client_fd) {
|
uint16_t readUint16 (int client_fd) {
|
||||||
recv_count = recv(client_fd, recv_buffer, 2, MSG_WAITALL);
|
recv_count = recv_all(client_fd, recv_buffer, 2, false);
|
||||||
return ((uint16_t)recv_buffer[0] << 8) | recv_buffer[1];
|
return ((uint16_t)recv_buffer[0] << 8) | recv_buffer[1];
|
||||||
}
|
}
|
||||||
uint32_t readUint32 (int client_fd) {
|
uint32_t readUint32 (int client_fd) {
|
||||||
recv_count = recv(client_fd, recv_buffer, 4, MSG_WAITALL);
|
recv_count = recv_all(client_fd, recv_buffer, 4, false);
|
||||||
return ((uint32_t)recv_buffer[0] << 24) |
|
return ((uint32_t)recv_buffer[0] << 24) |
|
||||||
((uint32_t)recv_buffer[1] << 16) |
|
((uint32_t)recv_buffer[1] << 16) |
|
||||||
((uint32_t)recv_buffer[2] << 8) |
|
((uint32_t)recv_buffer[2] << 8) |
|
||||||
((uint32_t)recv_buffer[3]);
|
((uint32_t)recv_buffer[3]);
|
||||||
}
|
}
|
||||||
uint64_t readUint64 (int client_fd) {
|
uint64_t readUint64 (int client_fd) {
|
||||||
recv_count = recv(client_fd, recv_buffer, 8, MSG_WAITALL);
|
recv_count = recv_all(client_fd, recv_buffer, 8, false);
|
||||||
return ((uint64_t)recv_buffer[0] << 56) |
|
return ((uint64_t)recv_buffer[0] << 56) |
|
||||||
((uint64_t)recv_buffer[1] << 48) |
|
((uint64_t)recv_buffer[1] << 48) |
|
||||||
((uint64_t)recv_buffer[2] << 40) |
|
((uint64_t)recv_buffer[2] << 40) |
|
||||||
@@ -81,7 +118,7 @@ uint64_t readUint64 (int client_fd) {
|
|||||||
((uint64_t)recv_buffer[7]);
|
((uint64_t)recv_buffer[7]);
|
||||||
}
|
}
|
||||||
int64_t readInt64 (int client_fd) {
|
int64_t readInt64 (int client_fd) {
|
||||||
recv_count = recv(client_fd, recv_buffer, 8, MSG_WAITALL);
|
recv_count = recv_all(client_fd, recv_buffer, 8, false);
|
||||||
return ((int64_t)recv_buffer[0] << 56) |
|
return ((int64_t)recv_buffer[0] << 56) |
|
||||||
((int64_t)recv_buffer[1] << 48) |
|
((int64_t)recv_buffer[1] << 48) |
|
||||||
((int64_t)recv_buffer[2] << 40) |
|
((int64_t)recv_buffer[2] << 40) |
|
||||||
@@ -106,7 +143,7 @@ double readDouble (int client_fd) {
|
|||||||
|
|
||||||
void readString (int client_fd) {
|
void readString (int client_fd) {
|
||||||
uint32_t length = readVarInt(client_fd);
|
uint32_t length = readVarInt(client_fd);
|
||||||
recv_count = recv(client_fd, recv_buffer, length, MSG_WAITALL);
|
recv_count = recv_all(client_fd, recv_buffer, length, false);
|
||||||
recv_buffer[recv_count] = '\0';
|
recv_buffer[recv_count] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,10 +164,18 @@ uint64_t splitmix64 (uint64_t state) {
|
|||||||
int client_states[MAX_PLAYERS * 2];
|
int client_states[MAX_PLAYERS * 2];
|
||||||
|
|
||||||
void setClientState (int client_fd, int new_state) {
|
void setClientState (int client_fd, int new_state) {
|
||||||
|
// Look for a client state with a matching file descriptor
|
||||||
for (int i = 0; i < MAX_PLAYERS * 2; i += 2) {
|
for (int i = 0; i < MAX_PLAYERS * 2; i += 2) {
|
||||||
if (client_states[i] != client_fd && client_states[i] != 0) continue;
|
if (client_states[i] != client_fd) continue;
|
||||||
|
client_states[i + 1] = new_state;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If the above failed, look for an unused client state slot
|
||||||
|
for (int i = 0; i < MAX_PLAYERS * 2; i += 2) {
|
||||||
|
if (client_states[i] != -1) continue;
|
||||||
client_states[i] = client_fd;
|
client_states[i] = client_fd;
|
||||||
client_states[i + 1] = new_state;
|
client_states[i + 1] = new_state;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,8 +344,10 @@ 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
|
// Transmit block update to all in-game clients
|
||||||
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
for (int i = 0; i < MAX_PLAYERS; i ++) {
|
||||||
|
if (player_data[i].client_fd == -1) continue;
|
||||||
|
if (getClientState(player_data[i].client_fd) != STATE_PLAY) continue;
|
||||||
sc_blockUpdate(player_data[i].client_fd, x, y, z, block);
|
sc_blockUpdate(player_data[i].client_fd, x, y, z, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
|
|
||||||
|
ssize_t recv_all (int client_fd, void *buf, size_t n, uint8_t require_first);
|
||||||
|
|
||||||
ssize_t writeByte (int client_fd, uint8_t byte);
|
ssize_t writeByte (int client_fd, uint8_t byte);
|
||||||
ssize_t writeUint16 (int client_fd, uint16_t num);
|
ssize_t writeUint16 (int client_fd, uint16_t num);
|
||||||
ssize_t writeUint32 (int client_fd, uint32_t num);
|
ssize_t writeUint32 (int client_fd, uint32_t num);
|
||||||
|
Reference in New Issue
Block a user