Skip to content

Commit

Permalink
some more stuff bewore working on better enet and more unittests
Browse files Browse the repository at this point in the history
  • Loading branch information
UtoECat committed Jan 21, 2024
1 parent 0ab5ed1 commit 81a5384
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 0 deletions.
266 changes: 266 additions & 0 deletions engine/network.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
/*
* This file is a part of Pixelbox - Infinite 2D sandbox game
* Enet wrapper!
* Copyright (C) 2023-2024 UtoECat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once
#include <base.hpp>
#include <stdexcept>
#include <string>
#include <functional>
#include "external/enet.h"
#include <string.h>

/**
* @description ENet Wrapper
*/
namespace pb {

struct ProtocolInfo {
const char* ip;
unsigned short port;
int nchannels;
int nconnections;
public:
inline ENetAddress getAddress() {
ENetAddress addr;
addr.port = port;

if (ip)
enet_address_set_host(&addr, ip);
else addr.host = ENET_HOST_ANY;
return addr;
}
};

static void HostDefaultConfig(ENetHost* host) {
// TODO Adjust to better values!
host->maximumPacketSize = 1024 * 32;
host->maximumWaitingData = 1024 * 128;
}

class ENetClient : public Default {
ENetHost* host;
ENetPeer* server = nullptr;
ProtocolInfo pinfo;
public:

ENetClient(ProtocolInfo info) {
pinfo = info;
auto addr = info.getAddress();
host = enet_host_create(NULL, info.nconnections, info.nchannels,
0, 0);
if (!host) {
throw std::runtime_error("Can't create client!");
}
HostDefaultConfig(host);
}

void disconnect() {
if (server) {
enet_peer_reset(server);
server = nullptr;
}
}

const char* errmsg = nullptr;
bool service(ENetEvent& ev, int timeout = 1) {
return enet_host_service(host, &ev, timeout) > 0;
}

bool connect() {
if (server) disconnect();
auto addr = pinfo.getAddress();
server = enet_host_connect(host, &addr, pinfo.nchannels, 0);

if (!server) {
errmsg = "can't create peer!";
return false;
}
// process events
ENetEvent event;
if (enet_host_service(host, &event, 5000) > 0 &&
event.type == ENET_EVENT_TYPE_CONNECT) {
return true;
}

errmsg = "Can't connect to server!";
enet_peer_reset(server);
server = nullptr;
return false;
}

~ENetClient() {
disconnect();
enet_host_destroy(host);
}

/* must be called after event is processeed */
bool is_connected() {
return server != nullptr;
}

void releaseEvent(ENetEvent& ev) {
if (ev.type == ENET_EVENT_TYPE_RECEIVE && ev.packet) {
enet_packet_destroy(ev.packet);
}
}

/** @warning channel starts FROM ZERO!!!! */
void send(uint8_t channel, ENetPacket* data) {
if (server)
enet_peer_send(server, channel, data);
}

void flush() {
enet_host_flush(host);
}
};

class ConnectionData {
public:
virtual ~ConnectionData() {};
};

class ENetConnection {
ENetPeer* peer;
public:
ENetConnection(ENetPeer* p) {
peer = p;
}
~ENetConnection() {} // nothing
public:
void disconnect() {
enet_peer_disconnect_later(peer, 0);
}

void reset() { // forcefully disconnect
release_data();
enet_peer_reset(peer);
}

bool send(uint8_t channel, ENetPacket* data) {
return enet_peer_send(peer, channel, data) == 0;
}

ConnectionData* data() {
return (ConnectionData*)enet_peer_get_data(peer);
}

void set_data(ConnectionData* dat) {
return enet_peer_set_data(peer, dat);
}

void release_data() {
delete data();
set_data(nullptr);
}
};

class ENetServer : public Default {
ENetHost* host;
public:

ENetServer(ProtocolInfo info) {
ENetAddress addr = info.getAddress();
host = enet_host_create(&addr, info.nconnections, info.nchannels,
0, 0);
if (!host) {
throw std::runtime_error("Can't create server!");
}
HostDefaultConfig(host);
}

bool service(ENetEvent& ev, int timeout = 1) {
bool stat = enet_host_service(host, &ev, timeout) > 0;
if (stat && ev.type == ENET_EVENT_TYPE_CONNECT) {
enet_peer_set_data(ev.peer, nullptr); // we rely on this behaviour!
}
return stat;
}

~ENetServer() {
enet_host_destroy(host);
}

bool is_valid() {
return host != nullptr;
}

void releaseEvent(ENetEvent& ev) {
if (ev.type == ENET_EVENT_TYPE_RECEIVE && ev.packet) {
enet_packet_destroy(ev.packet);
}
}

void broadcast(uint8_t channel, ENetPacket* data) {
if (host)
enet_host_broadcast(host, channel, data);
}

void foreach(std::function<void(ENetConnection)> cb) {
if (!cb) return;
ENetPeer *currentPeer;
for (currentPeer = host->peers;
currentPeer < &host->peers[host->peerCount]; ++currentPeer) {
cb(currentPeer);
}
}

void flush() {
enet_host_flush(host);
}

/* shutdown sequence. Behaviour after this is not specified! */
void shutdown() {
// send disconnect
foreach([](ENetConnection c) {
c.disconnect();
});
enet_host_flush(host);

// queue
ENetEvent ev;
while (service(ev, 200)) {
switch (ev.type) {
case ENET_EVENT_TYPE_DISCONNECT:
case ENET_EVENT_TYPE_DISCONNECT_TIMEOUT: {
ENetConnection con(ev.peer);
con.release_data();
}; break;
case ENET_EVENT_TYPE_CONNECT:
enet_peer_disconnect_now(ev.peer, 0);
break;
case ENET_EVENT_TYPE_RECEIVE:
enet_packet_destroy(ev.packet);
break;
default:
break;
};
}

// force disconnect and free all data
foreach([](ENetConnection c) {
c.reset();
});

// now we can safely call destructor!
}
};

};

1 change: 1 addition & 0 deletions game/main_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "tools/tools.hpp"
#include "game/world_view.hpp"
#include "engine/network.hpp"

namespace pb {

Expand Down
1 change: 1 addition & 0 deletions game/main_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace pb {

// used in main() function only anyway...
void main_server(std::vector<const char*> argv) {

return;
}
};
45 changes: 45 additions & 0 deletions game/server.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* This file is a part of Pixelbox - Infinite 2D sandbox game
* Server setup logic
* Copyright (C) 2023-2024 UtoECat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

#include <iostream>
#include <memory>
#include "engine/network.hpp"
#include "base.hpp"
#include "engine/database.hpp"

namespace pb {

class GamerServerBase {
protected:
std::unique_ptr<ENetServer> server;
Database database;
public:
GamerServerBase(const char* db_file, int port, int max_cli = 1) {
// create server
server = std::make_unique<ENetServer>(ProtocolInfo(NULL, port, 5, max_cli));
if (!db_file) {
std::cerr << "No file specified - in memory database is used for testing" << std::endl;
db_file = ":memory:";
}
database.open(db_file);
db::world_settings(database); // configure PRAGMA's for world storade db
}
};

};

0 comments on commit 81a5384

Please sign in to comment.