stuff
This commit is contained in:
parent
1006b231f0
commit
0c0cfef186
28 changed files with 220 additions and 331 deletions
337
game/inc/suic_net.c
Normal file
337
game/inc/suic_net.c
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
#include "suic_net.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
typedef int socklen_t;
|
||||
#define CLOSESOCK closesocket
|
||||
#else
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#define CLOSESOCK close
|
||||
#endif
|
||||
|
||||
#define PROTOCOL_VERSION 67
|
||||
|
||||
/* ===== MESSAGE TYPES ===== */
|
||||
|
||||
#pragma pack(push, 1)
|
||||
typedef enum MsgType : uint8_t {
|
||||
MSG_HELLO = 1,
|
||||
MSG_WELCOME = 2,
|
||||
MSG_INPUT = 3,
|
||||
MSG_SNAPSHOT = 4,
|
||||
MSG_SHOOT = 5,
|
||||
MSG_ITEMS = 6,
|
||||
MSG_ROOM_STATE = 7
|
||||
} MsgType;
|
||||
|
||||
typedef struct MsgHello {
|
||||
uint8_t type;
|
||||
uint32_t protocol;
|
||||
char username[SUIC_NET_USERNAME_MAX];
|
||||
} MsgHello;
|
||||
|
||||
typedef struct MsgWelcome {
|
||||
uint8_t type;
|
||||
uint8_t playerId;
|
||||
uint32_t serverTick;
|
||||
} MsgWelcome;
|
||||
|
||||
typedef struct MsgInput {
|
||||
uint8_t type;
|
||||
uint8_t playerId;
|
||||
uint32_t clientTick;
|
||||
float moveX, moveZ, yaw, pitch;
|
||||
uint8_t buttons;
|
||||
} MsgInput;
|
||||
|
||||
typedef struct MsgShoot {
|
||||
uint8_t type;
|
||||
uint8_t playerId;
|
||||
uint32_t clientTick;
|
||||
} MsgShoot;
|
||||
|
||||
typedef struct PlayerStateNet {
|
||||
uint8_t id, alive;
|
||||
int16_t hp;
|
||||
float x, y, z, yaw, pitch;
|
||||
uint8_t weapon;
|
||||
int16_t pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits;
|
||||
int16_t reloadTimeLeft;
|
||||
char username[SUIC_NET_USERNAME_MAX];
|
||||
} PlayerStateNet;
|
||||
|
||||
typedef struct MsgSnapshot {
|
||||
uint8_t type;
|
||||
uint32_t serverTick;
|
||||
uint8_t count;
|
||||
PlayerStateNet p[SUIC_NET_MAX_PLAYERS];
|
||||
} MsgSnapshot;
|
||||
|
||||
typedef struct ItemNet {
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
int16_t qty;
|
||||
float x, y, z;
|
||||
} ItemNet;
|
||||
|
||||
typedef struct MsgItems {
|
||||
uint8_t type;
|
||||
uint32_t serverTick;
|
||||
uint8_t count;
|
||||
ItemNet items[SUIC_NET_MAX_ITEMS];
|
||||
} MsgItems;
|
||||
|
||||
typedef struct MsgRoomState {
|
||||
uint8_t type;
|
||||
uint8_t state;
|
||||
float countdownRemaining;
|
||||
uint8_t winnerId;
|
||||
char winnerName[SUIC_NET_USERNAME_MAX];
|
||||
} MsgRoomState;
|
||||
#pragma pack(pop)
|
||||
|
||||
/* ===== LOCAL PLAYER DATA ===== */
|
||||
|
||||
typedef struct RemotePlayer {
|
||||
int present, alive, hp;
|
||||
float x, y, z;
|
||||
float prevX, prevY, prevZ;
|
||||
float yaw, pitch;
|
||||
uint8_t weapon;
|
||||
int16_t pistolMag, rifleMag, pistolAmmo, rifleAmmo, medkits;
|
||||
int16_t reloadTimeLeft;
|
||||
char username[SUIC_NET_USERNAME_MAX];
|
||||
} RemotePlayer;
|
||||
|
||||
typedef struct WorldItem {
|
||||
int present;
|
||||
uint16_t id;
|
||||
uint8_t type;
|
||||
int16_t qty;
|
||||
float x, y, z;
|
||||
} WorldItem;
|
||||
|
||||
/* ===== STATIC STATE ===== */
|
||||
|
||||
static int g_sock = -1;
|
||||
static struct sockaddr_in g_server = {0};
|
||||
static uint8_t g_myId = 255;
|
||||
static RemotePlayer g_players[SUIC_NET_MAX_PLAYERS] = {0};
|
||||
static WorldItem g_items[SUIC_NET_MAX_ITEMS] = {0};
|
||||
static int g_roomState = 0;
|
||||
static float g_countdown = 0.0f;
|
||||
static char g_winnerName[SUIC_NET_USERNAME_MAX] = {0};
|
||||
|
||||
static void set_nonblocking(int sock) {
|
||||
#ifdef _WIN32
|
||||
u_long mode = 1;
|
||||
ioctlsocket(sock, FIONBIO, &mode);
|
||||
#else
|
||||
int flags = fcntl(sock, F_GETFL, 0);
|
||||
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* ===== PUBLIC API ===== */
|
||||
|
||||
int suic_net_init(const char* server_ip, int port) {
|
||||
#ifdef _WIN32
|
||||
WSADATA wsa;
|
||||
WSAStartup(MAKEWORD(2, 2), &wsa);
|
||||
#endif
|
||||
|
||||
g_sock = (int)socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (g_sock < 0) {
|
||||
perror("socket");
|
||||
return -1;
|
||||
}
|
||||
set_nonblocking(g_sock);
|
||||
|
||||
memset(&g_server, 0, sizeof(g_server));
|
||||
g_server.sin_family = AF_INET;
|
||||
g_server.sin_port = htons(port);
|
||||
inet_pton(AF_INET, server_ip, &g_server.sin_addr);
|
||||
|
||||
g_myId = 255;
|
||||
memset(g_players, 0, sizeof(g_players));
|
||||
memset(g_items, 0, sizeof(g_items));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void suic_net_shutdown(void) {
|
||||
if (g_sock >= 0) {
|
||||
CLOSESOCK(g_sock);
|
||||
g_sock = -1;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool suic_net_is_connected(void) {
|
||||
return g_myId != 255;
|
||||
}
|
||||
|
||||
void suic_net_send_hello(const char* username) {
|
||||
MsgHello hello = {0};
|
||||
hello.type = MSG_HELLO;
|
||||
hello.protocol = PROTOCOL_VERSION;
|
||||
strncpy(hello.username, username, SUIC_NET_USERNAME_MAX - 1);
|
||||
sendto(g_sock, (const char*)&hello, (int)sizeof(hello), 0,
|
||||
(const struct sockaddr*)&g_server, sizeof(g_server));
|
||||
}
|
||||
|
||||
void suic_net_send_input(uint8_t playerId, uint32_t clientTick,
|
||||
float moveX, float moveZ, float yaw, float pitch,
|
||||
uint8_t buttons) {
|
||||
MsgInput in = {0};
|
||||
in.type = MSG_INPUT;
|
||||
in.playerId = playerId;
|
||||
in.clientTick = clientTick;
|
||||
in.moveX = moveX;
|
||||
in.moveZ = moveZ;
|
||||
in.yaw = yaw;
|
||||
in.pitch = pitch;
|
||||
in.buttons = buttons;
|
||||
sendto(g_sock, (const char*)&in, (int)sizeof(in), 0,
|
||||
(const struct sockaddr*)&g_server, sizeof(g_server));
|
||||
}
|
||||
|
||||
void suic_net_send_shoot(uint8_t playerId, uint32_t clientTick) {
|
||||
MsgShoot msg = {0};
|
||||
msg.type = MSG_SHOOT;
|
||||
msg.playerId = playerId;
|
||||
msg.clientTick = clientTick;
|
||||
sendto(g_sock, (const char*)&msg, (int)sizeof(msg), 0,
|
||||
(const struct sockaddr*)&g_server, sizeof(g_server));
|
||||
}
|
||||
|
||||
bool suic_net_poll(void) {
|
||||
bool receivedAny = false;
|
||||
|
||||
for (;;) {
|
||||
uint8_t buf[1400];
|
||||
struct sockaddr_in from = {0};
|
||||
socklen_t fromLen = sizeof(from);
|
||||
int n = (int)recvfrom(g_sock, (char*)buf, (int)sizeof(buf), 0,
|
||||
(struct sockaddr*)&from, &fromLen);
|
||||
if (n <= 0) {
|
||||
#ifdef _WIN32
|
||||
if (WSAGetLastError() == WSAEWOULDBLOCK) break;
|
||||
#else
|
||||
if (errno == EWOULDBLOCK || errno == EAGAIN) break;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
|
||||
receivedAny = true;
|
||||
uint8_t type = buf[0];
|
||||
|
||||
if (type == MSG_WELCOME && n >= (int)sizeof(MsgWelcome)) {
|
||||
MsgWelcome* w = (MsgWelcome*)buf;
|
||||
g_myId = w->playerId;
|
||||
} else if (type == MSG_SNAPSHOT && n >= (int)sizeof(MsgSnapshot)) {
|
||||
MsgSnapshot* s = (MsgSnapshot*)buf;
|
||||
for (int i = 0; i < SUIC_NET_MAX_PLAYERS; i++)
|
||||
g_players[i].present = 0;
|
||||
|
||||
for (int i = 0; i < (int)s->count && i < SUIC_NET_MAX_PLAYERS; i++) {
|
||||
PlayerStateNet* ps = &s->p[i];
|
||||
if (ps->id >= SUIC_NET_MAX_PLAYERS) continue;
|
||||
|
||||
RemotePlayer* p = &g_players[ps->id];
|
||||
p->prevX = p->x;
|
||||
p->prevY = p->y;
|
||||
p->prevZ = p->z;
|
||||
p->present = 1;
|
||||
p->alive = ps->alive;
|
||||
p->hp = ps->hp;
|
||||
p->x = ps->x;
|
||||
p->y = ps->y;
|
||||
p->z = ps->z;
|
||||
p->yaw = ps->yaw;
|
||||
p->pitch = ps->pitch;
|
||||
p->weapon = ps->weapon;
|
||||
p->pistolMag = ps->pistolMag;
|
||||
p->rifleMag = ps->rifleMag;
|
||||
p->pistolAmmo = ps->pistolAmmo;
|
||||
p->rifleAmmo = ps->rifleAmmo;
|
||||
p->medkits = ps->medkits;
|
||||
p->reloadTimeLeft = ps->reloadTimeLeft;
|
||||
memset(p->username, 0, SUIC_NET_USERNAME_MAX);
|
||||
strncpy(p->username, ps->username, SUIC_NET_USERNAME_MAX - 1);
|
||||
}
|
||||
} else if (type == MSG_ITEMS && n >= (int)sizeof(MsgItems)) {
|
||||
MsgItems* m = (MsgItems*)buf;
|
||||
for (int i = 0; i < SUIC_NET_MAX_ITEMS; i++)
|
||||
g_items[i].present = 0;
|
||||
|
||||
for (int i = 0; i < (int)m->count && i < SUIC_NET_MAX_ITEMS; i++) {
|
||||
g_items[i].present = 1;
|
||||
g_items[i].id = m->items[i].id;
|
||||
g_items[i].type = m->items[i].type;
|
||||
g_items[i].qty = m->items[i].qty;
|
||||
g_items[i].x = m->items[i].x;
|
||||
g_items[i].y = m->items[i].y;
|
||||
g_items[i].z = m->items[i].z;
|
||||
}
|
||||
} else if (type == MSG_ROOM_STATE && n >= (int)sizeof(MsgRoomState)) {
|
||||
const MsgRoomState* rs = (const MsgRoomState*)buf;
|
||||
g_roomState = rs->state;
|
||||
g_countdown = rs->countdownRemaining;
|
||||
memset(g_winnerName, 0, SUIC_NET_USERNAME_MAX);
|
||||
if (rs->winnerId < 255) {
|
||||
strncpy(g_winnerName, rs->winnerName, SUIC_NET_USERNAME_MAX - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return receivedAny;
|
||||
}
|
||||
|
||||
uint8_t suic_net_get_my_id(void) { return g_myId; }
|
||||
|
||||
/* Player accessors */
|
||||
bool suic_net_player_present(int i) { return i >= 0 && i < SUIC_NET_MAX_PLAYERS && g_players[i].present; }
|
||||
bool suic_net_player_alive(int i) { return suic_net_player_present(i) && g_players[i].alive; }
|
||||
int suic_net_player_hp(int i) { return suic_net_player_present(i) ? g_players[i].hp : 0; }
|
||||
float suic_net_player_x(int i) { return suic_net_player_present(i) ? g_players[i].x : 0; }
|
||||
float suic_net_player_y(int i) { return suic_net_player_present(i) ? g_players[i].y : 0; }
|
||||
float suic_net_player_z(int i) { return suic_net_player_present(i) ? g_players[i].z : 0; }
|
||||
float suic_net_player_prev_x(int i) { return suic_net_player_present(i) ? g_players[i].prevX : 0; }
|
||||
float suic_net_player_prev_y(int i) { return suic_net_player_present(i) ? g_players[i].prevY : 0; }
|
||||
float suic_net_player_prev_z(int i) { return suic_net_player_present(i) ? g_players[i].prevZ : 0; }
|
||||
float suic_net_player_yaw(int i) { return suic_net_player_present(i) ? g_players[i].yaw : 0; }
|
||||
float suic_net_player_pitch(int i) { return suic_net_player_present(i) ? g_players[i].pitch : 0; }
|
||||
uint8_t suic_net_player_weapon(int i) { return suic_net_player_present(i) ? g_players[i].weapon : 0; }
|
||||
int16_t suic_net_player_pistol_mag(int i) { return suic_net_player_present(i) ? g_players[i].pistolMag : 0; }
|
||||
int16_t suic_net_player_rifle_mag(int i) { return suic_net_player_present(i) ? g_players[i].rifleMag : 0; }
|
||||
int16_t suic_net_player_pistol_ammo(int i) { return suic_net_player_present(i) ? g_players[i].pistolAmmo : 0; }
|
||||
int16_t suic_net_player_rifle_ammo(int i) { return suic_net_player_present(i) ? g_players[i].rifleAmmo : 0; }
|
||||
int16_t suic_net_player_medkits(int i) { return suic_net_player_present(i) ? g_players[i].medkits : 0; }
|
||||
int16_t suic_net_player_reload_time(int i) { return suic_net_player_present(i) ? g_players[i].reloadTimeLeft : 0; }
|
||||
const char* suic_net_player_username(int i) { return suic_net_player_present(i) ? g_players[i].username : ""; }
|
||||
|
||||
/* Item accessors */
|
||||
bool suic_net_item_present(int i) { return i >= 0 && i < SUIC_NET_MAX_ITEMS && g_items[i].present; }
|
||||
uint16_t suic_net_item_id(int i) { return suic_net_item_present(i) ? g_items[i].id : 0; }
|
||||
uint8_t suic_net_item_type(int i) { return suic_net_item_present(i) ? g_items[i].type : 0; }
|
||||
int16_t suic_net_item_qty(int i) { return suic_net_item_present(i) ? g_items[i].qty : 0; }
|
||||
float suic_net_item_x(int i) { return suic_net_item_present(i) ? g_items[i].x : 0; }
|
||||
float suic_net_item_y(int i) { return suic_net_item_present(i) ? g_items[i].y : 0; }
|
||||
float suic_net_item_z(int i) { return suic_net_item_present(i) ? g_items[i].z : 0; }
|
||||
|
||||
/* Room state */
|
||||
int suic_net_room_state(void) { return g_roomState; }
|
||||
float suic_net_countdown(void) { return g_countdown; }
|
||||
const char* suic_net_winner_name(void) { return g_winnerName; }
|
||||
Loading…
Add table
Add a link
Reference in a new issue