feat(server) Added client/server for online play
Note: This is fucking broken! it's too early in the morning. Fix tomorrow.
This commit is contained in:
parent
5a4d4ff7a3
commit
6e935918a3
@ -23,3 +23,6 @@ add_executable(bettola ${SOURCES})
|
|||||||
|
|
||||||
target_link_libraries(bettola PRIVATE bettola_lib SDL3::SDL3 GLEW::glew OpenGL::GL)
|
target_link_libraries(bettola PRIVATE bettola_lib SDL3::SDL3 GLEW::glew OpenGL::GL)
|
||||||
|
|
||||||
|
# Server executable.
|
||||||
|
add_executable(bettola_server srv/main.cpp)
|
||||||
|
target_link_libraries(bettola_server PRIVATE bettola_lib)
|
||||||
|
|||||||
9
libbettola/include/bettola/network/net_common.h
Normal file
9
libbettola/include/bettola/network/net_common.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace BettolaLib {
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
const unsigned short DEFAULT_PORT = 12345; /* This will do for now. */
|
||||||
|
|
||||||
|
} /* namespace Network. */
|
||||||
|
} /* namespace BettolaLib. */
|
||||||
37
libbettola/include/bettola/network/socket.h
Normal file
37
libbettola/include/bettola/network/socket.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace BettolaLib {
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
class Socket {
|
||||||
|
public:
|
||||||
|
Socket(void);
|
||||||
|
~Socket(void);
|
||||||
|
|
||||||
|
bool create(void);
|
||||||
|
bool bind(unsigned short port);
|
||||||
|
bool listen(int backlog = 5);
|
||||||
|
Socket* accept(void); /* Return a new Socket for the accepted connection. */
|
||||||
|
bool connect(const std::string& ip_address, unsigned short port);
|
||||||
|
|
||||||
|
ssize_t send(const void* buffer, size_t length);
|
||||||
|
ssize_t recv(void* buffer, size_t length);
|
||||||
|
|
||||||
|
void close(void);
|
||||||
|
|
||||||
|
int get_sockfd(void) const { return _sockfd; }
|
||||||
|
bool is_valid(void) const { return _sockfd != -1; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _sockfd;
|
||||||
|
struct sockaddr_in _address;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /* namespace Network. */
|
||||||
|
} /* namespace BettolaLib. */
|
||||||
130
libbettola/src/bettola/network/socket.cpp
Normal file
130
libbettola/src/bettola/network/socket.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <asm-generic/socket.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
#include "bettola/network/socket.h"
|
||||||
|
#include "bettola/network/net_common.h"
|
||||||
|
|
||||||
|
namespace BettolaLib {
|
||||||
|
namespace Network {
|
||||||
|
|
||||||
|
Socket::Socket(void) : _sockfd(-1) {
|
||||||
|
memset(&_address, 0, sizeof(_address));
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket::~Socket(void) {
|
||||||
|
this->close();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Socket::create(void) {
|
||||||
|
_sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if(_sockfd == -1) {
|
||||||
|
fprintf(stderr, "Socket::create() failed with errno %d: %s\n", errno, strerror(errno));
|
||||||
|
perror("Failed to create socket.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Attempt to set socket to blocking mode explicitly??? */
|
||||||
|
int flags = fcntl(_sockfd, F_GETFL, 0);
|
||||||
|
if(flags == -1) {
|
||||||
|
perror("fcntl F_GETFL failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(fcntl(_sockfd, F_SETFL, flags & ~O_NONBLOCK) == -1) {
|
||||||
|
perror("fcntl F_SETFL O_NONBLOCK failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Socket::bind(unsigned short port) {
|
||||||
|
_address.sin_family = AF_INET;
|
||||||
|
_address.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
_address.sin_port = htons(port);
|
||||||
|
|
||||||
|
int opt = 1;
|
||||||
|
if(setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
|
||||||
|
perror("setsockopt SO_REUSEADDR failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(::bind(_sockfd, (struct sockaddr*)&_address, sizeof(_address)) == -1) {
|
||||||
|
perror("Socket bind failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Socket::listen(int backlog) {
|
||||||
|
if(::listen(_sockfd, backlog) == -1) {
|
||||||
|
perror("Socket accept failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket* Socket::accept(void) {
|
||||||
|
struct sockaddr_in client_addr;
|
||||||
|
socklen_t client_len = sizeof(client_addr);
|
||||||
|
int client_sockfd = ::accept(_sockfd, (struct sockaddr*)&client_addr, &client_len);
|
||||||
|
if(client_sockfd == -1) {
|
||||||
|
perror("Socket accept failed.");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Socket* new_socket = new Socket();
|
||||||
|
new_socket->_sockfd = client_sockfd;
|
||||||
|
new_socket->_address = client_addr;
|
||||||
|
return new_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Socket::connect(const std::string& ip_address, unsigned short port) {
|
||||||
|
_address.sin_family = AF_INET;
|
||||||
|
_address.sin_port = htons(port);
|
||||||
|
|
||||||
|
if(inet_pton(AF_INET, ip_address.c_str(), &_address.sin_addr) <= 0) {
|
||||||
|
perror("Invalid address / Address not supported.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(::connect(_sockfd, (struct sockaddr*)&_address, sizeof(_address)) <= 0) {
|
||||||
|
fprintf(stderr, "Socket::connect() failed for sockfd %d with errno %d: %s\n", _sockfd,
|
||||||
|
errno, strerror(errno));
|
||||||
|
perror("Connection failed.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t Socket::send(const void* buffer, size_t length) {
|
||||||
|
ssize_t bytes_sent = ::send(_sockfd, buffer, length, 0);
|
||||||
|
if(bytes_sent == -1) {
|
||||||
|
perror("Send failed.");
|
||||||
|
}
|
||||||
|
return bytes_sent;
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t Socket::recv(void* buffer, size_t length) {
|
||||||
|
ssize_t bytes_received = ::recv(_sockfd, buffer, length, 0);
|
||||||
|
if(bytes_received == -1) {
|
||||||
|
perror("Receive failed.");
|
||||||
|
}
|
||||||
|
return bytes_received;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Socket::close(void) {
|
||||||
|
if(_sockfd != -1) {
|
||||||
|
::close(_sockfd);
|
||||||
|
_sockfd = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace Network. */
|
||||||
|
} /* namespace BettolaLib. */
|
||||||
@ -6,8 +6,11 @@
|
|||||||
#include <SDL3/SDL_timer.h>
|
#include <SDL3/SDL_timer.h>
|
||||||
#include <SDL3/SDL_video.h>
|
#include <SDL3/SDL_video.h>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#include "bettola.h"
|
#include "bettola.h"
|
||||||
|
#include "bettola/network/socket.h"
|
||||||
|
#include "bettola/network/net_common.h"
|
||||||
|
|
||||||
/* Dacav's resolution ;) */
|
/* Dacav's resolution ;) */
|
||||||
const int SCREEN_WIDTH = 800;
|
const int SCREEN_WIDTH = 800;
|
||||||
@ -25,6 +28,8 @@ Bettola::~Bettola(void) {
|
|||||||
SDL_GL_DestroyContext(_gl_context);
|
SDL_GL_DestroyContext(_gl_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_client_socket.close(); /* Explicity close client socket. */
|
||||||
|
|
||||||
if(_window) {
|
if(_window) {
|
||||||
SDL_DestroyWindow(_window);
|
SDL_DestroyWindow(_window);
|
||||||
}
|
}
|
||||||
@ -32,16 +37,20 @@ Bettola::~Bettola(void) {
|
|||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
if(_gl_context) {
|
if(_gl_context) {
|
||||||
|
/* Should only be deleted if it was successfully created
|
||||||
|
* and if _gl_context is valid.
|
||||||
|
*/
|
||||||
glDeleteVertexArrays(1, &_vao);
|
glDeleteVertexArrays(1, &_vao);
|
||||||
glDeleteBuffers(1, &_vbo);
|
glDeleteBuffers(1, &_vbo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int Bettola::run(void) {
|
int Bettola::run(void) {
|
||||||
if(!init_sdl()) return -1;
|
if(!init_sdl()) return -1;
|
||||||
if(!create_window()) return -1;
|
if(!create_window()) return -1;
|
||||||
if(!create_gl_context()) return -1;
|
if(!create_gl_context()) return -1;
|
||||||
if(!init_glew()) return -1;
|
if(!init_client_connection()) return -1;
|
||||||
|
if(!init_glew()) return -1;
|
||||||
|
|
||||||
if(!_shader.load_from_files("assets/shaders/simple.vert",
|
if(!_shader.load_from_files("assets/shaders/simple.vert",
|
||||||
"assets/shaders/simple.frag")) {
|
"assets/shaders/simple.frag")) {
|
||||||
@ -68,6 +77,18 @@ int Bettola::run(void) {
|
|||||||
delta_time = (double)(current_counter-last_count) / (double)perf_freq;
|
delta_time = (double)(current_counter-last_count) / (double)perf_freq;
|
||||||
last_count = current_counter;
|
last_count = current_counter;
|
||||||
|
|
||||||
|
/* Pretty simple network ineteraction currently. */
|
||||||
|
static double network_time = 0.0;
|
||||||
|
network_time += delta_time;
|
||||||
|
if(network_time > 2.0) {
|
||||||
|
/* Send/receive every 2 seconds. */
|
||||||
|
send_data("Player position: " + std::to_string(_player.get_x()) + "," +
|
||||||
|
std::to_string(_player.get_y()));
|
||||||
|
std::string response = receive_data();
|
||||||
|
printf("Bettola Client (in game loop): Received: '%s'\n", response.c_str());
|
||||||
|
network_time = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
process_events();
|
process_events();
|
||||||
update(delta_time);
|
update(delta_time);
|
||||||
render();
|
render();
|
||||||
@ -215,3 +236,44 @@ void Bettola::setup_quad(void) {
|
|||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Bettola::init_client_connection(void) {
|
||||||
|
if(!_client_socket.create()) {
|
||||||
|
printf("Bettola client: Failed to create socket.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_client_socket.connect("127.0.0.1", BettolaLib::Network::DEFAULT_PORT)) {
|
||||||
|
perror("Bettola Client: Failed to connect to server.\n");
|
||||||
|
//_client_socket.close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Bettola Client: Connected to server at 127.0.01.:%hu\n", BettolaLib::Network::DEFAULT_PORT);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Bettola::send_data(const std::string& data) {
|
||||||
|
ssize_t bytes_sent = _client_socket.send(data.c_str(), data.length());
|
||||||
|
if(bytes_sent == -1) {
|
||||||
|
perror("Bettola Client: Failed to send data.");
|
||||||
|
} else {
|
||||||
|
printf("Bettola Client: Sent %zd bytes: '%s'\n", bytes_sent, data.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Bettola::receive_data(void) {
|
||||||
|
char buffer[256]; /* Just a small buffer currently. */
|
||||||
|
ssize_t bytes_received = _client_socket.recv(buffer, sizeof(buffer) - 1);
|
||||||
|
if(bytes_received == -1) {
|
||||||
|
perror("Bettola Client: Failed to receive data.");
|
||||||
|
return "";
|
||||||
|
} else if(bytes_received == 0) {
|
||||||
|
printf("Bettola Client: Server disconnected.\n");
|
||||||
|
/* Attempt to reconnect. */
|
||||||
|
_client_socket.close(); /* Close the old socket. */
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
buffer[bytes_received] = '\0';
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "graphics/shader.h"
|
#include "graphics/shader.h"
|
||||||
#include "game/player.h"
|
#include "game/player.h"
|
||||||
#include "math/mat4.h"
|
#include "math/mat4.h"
|
||||||
|
#include "bettola/network/socket.h"
|
||||||
|
|
||||||
class Bettola {
|
class Bettola {
|
||||||
public:
|
public:
|
||||||
@ -22,6 +23,11 @@ private:
|
|||||||
bool init_glew(void);
|
bool init_glew(void);
|
||||||
bool create_window(void);
|
bool create_window(void);
|
||||||
bool create_gl_context(void);
|
bool create_gl_context(void);
|
||||||
|
|
||||||
|
bool init_client_connection(void);
|
||||||
|
void send_data(const std::string& data);
|
||||||
|
std::string receive_data(void);
|
||||||
|
|
||||||
void setup_quad(void);
|
void setup_quad(void);
|
||||||
|
|
||||||
struct InputState {
|
struct InputState {
|
||||||
@ -43,4 +49,5 @@ private:
|
|||||||
BettolaMath::Mat4 _projection;
|
BettolaMath::Mat4 _projection;
|
||||||
Player _player;
|
Player _player;
|
||||||
InputState _input;
|
InputState _input;
|
||||||
|
BettolaLib::Network::Socket _client_socket;
|
||||||
};
|
};
|
||||||
|
|||||||
64
srv/main.cpp
Normal file
64
srv/main.cpp
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "bettola/network/socket.h"
|
||||||
|
#include "bettola/network/net_common.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
printf("=== Bettola Server: Starting ===\n");
|
||||||
|
|
||||||
|
BettolaLib::Network::Socket server_socket;
|
||||||
|
|
||||||
|
if(!server_socket.create()) {
|
||||||
|
printf("Bettola Server: Failed to create socket.\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!server_socket.bind(BettolaLib::Network::DEFAULT_PORT)) {
|
||||||
|
printf("Bettola Server: Failed to bind socket to port %hu.\n",
|
||||||
|
BettolaLib::Network::DEFAULT_PORT);
|
||||||
|
server_socket.close();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!server_socket.listen()) {
|
||||||
|
printf("Bettola Server: Failed to listen on socket.\n");
|
||||||
|
server_socket.close();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Bettola Server: Listening on port %hu...\n", BettolaLib::Network::DEFAULT_PORT);
|
||||||
|
|
||||||
|
/* Main server loop. */
|
||||||
|
while(true) {
|
||||||
|
BettolaLib::Network::Socket* client_socket = server_socket.accept();
|
||||||
|
|
||||||
|
if(client_socket == nullptr) {
|
||||||
|
printf("Bettola Server: Failed to accept client connection.\n");
|
||||||
|
continue; /* try accepting again. */
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Bettola Server: Client connected!\n");
|
||||||
|
|
||||||
|
char buffer[256]; /* Small buffer for echo. */
|
||||||
|
ssize_t bytes_received;
|
||||||
|
do {
|
||||||
|
bytes_received = client_socket->recv(buffer, sizeof(buffer) - 1);
|
||||||
|
if(bytes_received > 0) {
|
||||||
|
buffer[bytes_received] = '\n';
|
||||||
|
printf("Bettola Server: Received from client: '%s'\n", buffer);
|
||||||
|
client_socket->send(buffer, bytes_received); /* Echo back. */
|
||||||
|
} else if(bytes_received == -1) {
|
||||||
|
perror("Bettola Server: Error receiving from client.");
|
||||||
|
}
|
||||||
|
} while(bytes_received > 0);
|
||||||
|
|
||||||
|
/* Let's just keep it connected for now. */
|
||||||
|
// printf("Bettola Server: Client disconnected.\n");
|
||||||
|
// client_socket->close();
|
||||||
|
// delete client_socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
server_socket.close(); /* Shouldn't reach here. */
|
||||||
|
printf("=== Bettola Server: Shutting Down ===\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user