Switch to OpenGL instead of SDL

This commit is contained in:
OusmBlueNinja 2025-04-02 20:05:40 -05:00
parent c3e1cbe5b0
commit bf07969ee9
10 changed files with 572 additions and 0 deletions

23
.vscode/c_cpp_properties.json vendored Normal file
View File

@ -0,0 +1,23 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${default}",
"${workspaceFolder}/**",
"C:/msys64/mingw64/include"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.26100.0",
"cStandard": "c23",
"cppStandard": "c++20",
"intelliSenseMode": "windows-gcc-x64",
"compilerPath": "C:/Program Files/mingw64/bin/gcc.exe"
}
],
"version": 4
}

39
Makefile Normal file
View File

@ -0,0 +1,39 @@
# Compiler
CC = gcc
# Paths
SRC_DIR = src
BUILD_DIR = build
OUT = main.exe
# Source files
SRC = \
$(SRC_DIR)\main.c \
$(SRC_DIR)\net.c \
$(SRC_DIR)\event.c \
$(SRC_DIR)\player.c
# Object files
OBJ = $(patsubst $(SRC_DIR)\%.c, $(BUILD_DIR)\%.o, $(SRC))
# Include and linker flags
INCLUDE_FLAGS = -IC:/msys64/mingw64/include
LIBS = -LC:/msys64/mingw64/lib -static -lws2_32 -lSDL2main -lSDL2 -lwinmm -limm32 -lole32 -loleaut32 -lversion -lsetupapi -lgdi32 -lshell32 -luser32 -mconsole
CFLAGS = -Wall -Wextra -g $(INCLUDE_FLAGS)
LDFLAGS = $(LIBS)
# Default target
all: $(OUT)
$(OUT): $(OBJ)
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
# Compile each .c into .o
$(BUILD_DIR)\%.o: $(SRC_DIR)\%.c
@if not exist $(BUILD_DIR) mkdir $(BUILD_DIR)
$(CC) $(CFLAGS) -c $< -o $@
# Clean
clean:
del /Q $(BUILD_DIR)\*.o 2>nul || exit 0
del /Q $(OUT) 2>nul || exit 0

BIN
main.exe Normal file

Binary file not shown.

14
src/event.c Normal file
View File

@ -0,0 +1,14 @@
#include "event.h"
#define MAX_HANDLERS 256
static PacketHandler g_handlers[MAX_HANDLERS] = {0};
void register_handler(uint8_t type, PacketHandler handler) {
g_handlers[type] = handler;
}
void handle_packet(Packet* pkt, SOCKET client) {
if (g_handlers[pkt->type]) {
g_handlers[pkt->type](pkt, client);
}
}

15
src/event.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef EVENT_H
#define EVENT_H
#include "net.h"
// Each handler receives Packet* and the client socket
typedef void (*PacketHandler)(Packet*, SOCKET client);
// Register a handler for a certain PacketType
void register_handler(uint8_t type, PacketHandler handler);
// Dispatch a packet
void handle_packet(Packet* pkt, SOCKET client);
#endif

336
src/main.c Normal file
View File

@ -0,0 +1,336 @@
#define WIN32_LEAN_AND_MEAN
#define SDL_MAIN_HANDLED
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h> // Sleep()
#include "net.h"
#include "event.h"
#include "player.h"
#include <SDL2/SDL.h>
CRITICAL_SECTION g_playerLock;
static Player g_players[MAX_PLAYERS];
static int g_numPlayers = 0;
static SOCKET g_clients[MAX_CLIENTS];
static int g_clientCount = 0;
static uint32_t g_nextID = 1; // to assign unique IDs
// Forward declarations
DWORD WINAPI client_thread(LPVOID param);
void broadcast_packet(Packet* pkt, SOCKET except);
void on_player_move(Packet* pkt, SOCKET client) {
PlayerMove pm;
memcpy(&pm, pkt->data, sizeof(PlayerMove));
EnterCriticalSection(&g_playerLock);
for(int i = 0; i < g_numPlayers; i++) {
if(g_players[i].id == pm.id) {
g_players[i].x += pm.dx;
g_players[i].y += pm.dy;
break;
}
}
// broadcast updated positions
int numStates = g_numPlayers;
int dataSize = sizeof(PlayerState) * numStates;
PlayerState* states = malloc(dataSize);
for(int i = 0; i < g_numPlayers; i++) {
states[i].id = g_players[i].id;
states[i].x = g_players[i].x;
states[i].y = g_players[i].y;
}
Packet* statePkt = create_packet(PACKET_PLAYER_STATE, states, dataSize);
free(states);
broadcast_packet(statePkt, INVALID_SOCKET);
destroy_packet(statePkt);
LeaveCriticalSection(&g_playerLock);
}
void broadcast_packet(Packet* pkt, SOCKET except) {
// serialize
char buffer[BUFFER_SIZE];
int len = serialize_packet(pkt, buffer);
EnterCriticalSection(&g_playerLock);
for(int i = 0; i < g_clientCount; i++) {
if(g_clients[i] == except) continue;
send(g_clients[i], buffer, len, 0);
}
LeaveCriticalSection(&g_playerLock);
}
DWORD WINAPI client_thread(LPVOID param) {
SOCKET client = *(SOCKET*)param;
free(param);
// Non-blocking for easier single-thread dispatch or we do blocking in a thread
set_socket_nonblocking(client, 0); // blocking in this thread
// Assign a new player
Player newPlayer;
newPlayer.id = g_nextID++;
newPlayer.x = 100; // start at 100,100
newPlayer.y = 100;
EnterCriticalSection(&g_playerLock);
g_players[g_numPlayers++] = newPlayer;
g_clients[g_clientCount++] = client;
LeaveCriticalSection(&g_playerLock);
printf("[SERVER] Client %u connected.\n", newPlayer.id);
// Now run receive loop
char buffer[BUFFER_SIZE];
while(1) {
int received = recv(client, buffer, BUFFER_SIZE, 0);
if (received <= 0) {
break; // client disconnected
}
Packet* pkt = deserialize_packet(buffer, received);
if (pkt) {
// Dispatch
handle_packet(pkt, client);
destroy_packet(pkt);
}
}
// Remove from global state
EnterCriticalSection(&g_playerLock);
// remove from g_clients
for(int i = 0; i < g_clientCount; i++) {
if(g_clients[i] == client) {
g_clients[i] = g_clients[--g_clientCount];
break;
}
}
// remove from g_players
for(int i = 0; i < g_numPlayers; i++) {
if(g_players[i].id == newPlayer.id) {
g_players[i] = g_players[--g_numPlayers];
break;
}
}
LeaveCriticalSection(&g_playerLock);
closesocket(client);
printf("[SERVER] Client %u disconnected.\n", newPlayer.id);
return 0;
}
void run_server(int port) {
net_init();
InitializeCriticalSection(&g_playerLock);
// Register server handlers
register_handler(PACKET_PLAYER_MOVE, on_player_move);
SOCKET server = net_bind_and_listen(port);
if (server == INVALID_SOCKET) {
printf("Failed to bind on port %d\n", port);
return;
}
printf("[SERVER] Listening on port %d...\n", port);
while(1) {
SOCKET s = net_accept(server);
if (s == INVALID_SOCKET) {
Sleep(10);
continue;
}
// Start a client thread
SOCKET* ptr = malloc(sizeof(SOCKET));
*ptr = s;
CreateThread(NULL, 0, client_thread, ptr, 0, NULL);
}
DeleteCriticalSection(&g_playerLock);
closesocket(server);
net_cleanup();
}
static Player g_localPlayer = {0};
static PlayerState g_otherPlayers[MAX_PLAYERS];
static int g_otherCount = 0;
// For now, store the socket globally
static SOCKET g_sock = INVALID_SOCKET;
// Handler: PACKET_PLAYER_STATE
void on_player_state(Packet* pkt, SOCKET _) {
// We'll parse an array of PlayerStates
int num = pkt->size / sizeof(PlayerState);
if(num > MAX_PLAYERS) num = MAX_PLAYERS;
memcpy(g_otherPlayers, pkt->data, pkt->size);
g_otherCount = num;
}
void run_client(const char* ip, int port) {
net_init();
// Register client handlers
register_handler(PACKET_PLAYER_STATE, on_player_state);
// Connect
g_sock = net_connect(ip, port);
if (g_sock == INVALID_SOCKET) {
printf("[CLIENT] Could not connect to server.\n");
return;
}
printf("[CLIENT] Connected to server %s:%d\n", ip, port);
// We'll assign an ID after reading from server in a real scenario,
// but for simplicity, let's guess the server will do so.
// We'll just store local ID = 999, or we wait for some handshake.
// For now, let's store a random number:
g_localPlayer.id = 9999;
g_localPlayer.x = 200;
g_localPlayer.y = 200;
// Setup SDL
if (SDL_Init(SDL_INIT_VIDEO) != 0) {
printf("SDL_Init failed: %s\n", SDL_GetError());
return;
}
SDL_Window* window = SDL_CreateWindow(
"MPGC Client",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
800, 600,
0
);
if(!window) {
printf("SDL_CreateWindow failed: %s\n", SDL_GetError());
SDL_Quit();
return;
}
SDL_Renderer* renderer = SDL_CreateRenderer(window, -1, 0);
if(!renderer) {
printf("SDL_CreateRenderer failed: %s\n", SDL_GetError());
SDL_DestroyWindow(window);
SDL_Quit();
return;
}
// Non-blocking so we can poll in main loop
set_socket_nonblocking(g_sock, 1);
int running = 1;
while (running) {
// ---- Handle SDL events
SDL_Event e;
while(SDL_PollEvent(&e)) {
if(e.type == SDL_QUIT) {
running = 0;
}
}
// ---- Keyboard input (WASD)
const Uint8* state = SDL_GetKeyboardState(NULL);
float dx = 0.f;
float dy = 0.f;
if(state[SDL_SCANCODE_W]) { dy -= 2.f; }
if(state[SDL_SCANCODE_S]) { dy += 2.f; }
if(state[SDL_SCANCODE_A]) { dx -= 2.f; }
if(state[SDL_SCANCODE_D]) { dx += 2.f; }
if(dx != 0 || dy != 0) {
// Move local player
g_localPlayer.x += dx;
g_localPlayer.y += dy;
// Send PACKET_PLAYER_MOVE
PlayerMove pm;
pm.id = g_localPlayer.id;
pm.dx = dx;
pm.dy = dy;
Packet* movePkt = create_packet(PACKET_PLAYER_MOVE, &pm, sizeof(pm));
char buffer[BUFFER_SIZE];
int len = serialize_packet(movePkt, buffer);
send(g_sock, buffer, len, 0);
destroy_packet(movePkt);
}
// ---- Check for incoming data
while (1) {
char netbuf[BUFFER_SIZE];
int r = recv(g_sock, netbuf, BUFFER_SIZE, 0);
if (r <= 0) break; // no data or error
Packet* pkt = deserialize_packet(netbuf, r);
if(pkt) {
handle_packet(pkt, g_sock);
destroy_packet(pkt);
}
}
// ---- Render scene
SDL_SetRenderDrawColor(renderer, 20, 20, 20, 255);
SDL_RenderClear(renderer);
// Draw local player
SDL_Rect rLoc = {
(int)g_localPlayer.x,
(int)g_localPlayer.y,
30, 30
};
SDL_SetRenderDrawColor(renderer, 200, 200, 50, 255);
SDL_RenderFillRect(renderer, &rLoc);
// Draw other players
SDL_SetRenderDrawColor(renderer, 100, 200, 200, 255);
for(int i = 0; i < g_otherCount; i++) {
SDL_Rect rOth = {
(int)g_otherPlayers[i].x,
(int)g_otherPlayers[i].y,
30, 30
};
SDL_RenderFillRect(renderer, &rOth);
}
SDL_RenderPresent(renderer);
}
// Cleanup
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
closesocket(g_sock);
net_cleanup();
}
int main(int argc, char* argv[]) {
printf("[INFO] Starting...\n");
SDL_SetMainReady();
if (argc < 3) {
printf("Usage:\n %s server <port>\n %s client <ip> <port>\n", argv[0], argv[0]);
return 1;
}
if(strcmp(argv[1], "server") == 0) {
int port = atoi(argv[2]);
run_server(port);
printf("[SERVER] Listening on port %d\n", port);
} else if(strcmp(argv[1], "client") == 0 && argc >= 4) {
const char* ip = argv[2];
int port = atoi(argv[3]);
run_client(ip, port);
} else {
printf("Invalid arguments.\n");
}
return 0;
}

78
src/net.c Normal file
View File

@ -0,0 +1,78 @@
#include "net.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h>
Packet* create_packet(PacketType type, const void* data, uint16_t size) {
Packet* pkt = malloc(sizeof(Packet) + size);
pkt->type = type;
pkt->size = size;
memcpy(pkt->data, data, size);
return pkt;
}
int serialize_packet(Packet* pkt, char* buffer) {
buffer[0] = pkt->type;
buffer[1] = (pkt->size >> 8) & 0xFF;
buffer[2] = pkt->size & 0xFF;
memcpy(buffer + 3, pkt->data, pkt->size);
return 3 + pkt->size;
}
Packet* deserialize_packet(const char* buffer, int len) {
if (len < 3) return NULL;
uint8_t type = buffer[0];
uint16_t size = ((uint8_t)buffer[1] << 8) | (uint8_t)buffer[2];
if (size > len - 3) return NULL;
return create_packet(type, buffer + 3, size);
}
void destroy_packet(Packet* pkt) {
free(pkt);
}
void net_init() {
WSADATA wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
}
void net_cleanup() {
WSACleanup();
}
SOCKET net_connect(const char* ip, int port) {
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(port)
};
inet_pton(AF_INET, ip, &addr.sin_addr);
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
return sock;
}
SOCKET net_bind_and_listen(int port) {
SOCKET server = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(port),
.sin_addr.s_addr = INADDR_ANY
};
bind(server, (struct sockaddr*)&addr, sizeof(addr));
listen(server, SOMAXCONN);
return server;
}
SOCKET net_accept(SOCKET server) {
return accept(server, NULL, NULL);
}
void set_socket_nonblocking(SOCKET sock, int enable)
{
u_long mode = (enable) ? 1 : 0;
ioctlsocket(sock, FIONBIO, &mode);
}

40
src/net.h Normal file
View File

@ -0,0 +1,40 @@
#ifndef NET_H
#define NET_H
#include <winsock2.h>
#include <stdint.h>
#define MAX_CLIENTS 64
#define BUFFER_SIZE 1024
typedef enum {
PACKET_PING = 1,
PACKET_TEXT,
PACKET_CLIENT_JOINED,
PACKET_CLIENT_LEFT,
PACKET_PLAYER_MOVE,
PACKET_PLAYER_STATE,
PACKET_MAX
} PacketType;
typedef struct {
uint8_t type;
uint16_t size;
char data[];
} Packet;
Packet* create_packet(PacketType type, const void* data, uint16_t size);
int serialize_packet(Packet* pkt, char* buffer);
Packet* deserialize_packet(const char* buffer, int len);
void destroy_packet(Packet* pkt);
// Networking setup
void net_init();
void net_cleanup();
SOCKET net_connect(const char* ip, int port);
SOCKET net_bind_and_listen(int port);
SOCKET net_accept(SOCKET server);
void set_socket_nonblocking(SOCKET sock, int enable);
#endif

2
src/player.c Normal file
View File

@ -0,0 +1,2 @@
#include "player.h"

25
src/player.h Normal file
View File

@ -0,0 +1,25 @@
#ifndef PLAYER_H
#define PLAYER_H
#include <stdint.h>
typedef struct {
uint32_t id;
float x, y;
} Player;
typedef struct {
uint32_t id;
float dx;
float dy;
} PlayerMove;
typedef struct {
uint32_t id;
float x;
float y;
} PlayerState;
#define MAX_PLAYERS 64
#endif