More networking code implementation

Move connection code from Client.cpp to ServerConnection.cpp
GameScene connects to the server and sets player position based on it.
Server 'authenticates' client from (currently unused) token before giving it data.
Packets now support encoding floats and strings.
Server now acknowledges port argument.
ServerPlayer class for keeping track of client Player entities.
master
aurailus 2019-01-11 23:16:10 -08:00
parent 0639c3b655
commit d50cc1c072
14 changed files with 303 additions and 88 deletions

View File

@ -89,10 +89,9 @@ add_executable(zeus
zeus/game/MenuScene.h
zeus/server/Server.cpp
zeus/server/Server.h
zeus/client/ClientPacket.cpp
zeus/server/ClientConnection.cpp
zeus/server/ClientConnection.h
zeus/server/Packet.cpp
zeus/server/Packet.h)
zeus/server/Packet.h zeus/server/ServerPlayer.cpp zeus/server/ServerPlayer.h zeus/client/ServerConnection.cpp zeus/client/ServerConnection.h)
target_link_libraries(zeus ${OPENGL_gl_LIBRARY} glfw libGLEW.so pthread lua dl)

View File

@ -46,58 +46,6 @@ void Client::start(char* path) {
Scene* m = new MenuScene(state);
sceneManager.setScene(m);
//Try to connect to a server
try {
asio::io_context io_context;
udp::resolver resolver(io_context);
udp::endpoint receiver_endpoint = *resolver.resolve(udp::v4(), "127.0.0.1", "12346").begin();
udp::socket socket(io_context);
socket.open(udp::v4());
int attempts = 0;
bool connected = false;
while (!connected) {
if (attempts > 5) {
break;
}
Packet p(Packet::HANDSHAKE);
p.addInt(attempts);
auto send_buf = p.serialize();
socket.send_to(asio::buffer(send_buf, send_buf.size()), receiver_endpoint);
attempts++;
std::this_thread::sleep_for(std::chrono::milliseconds(200));
if (socket.available() > 0) {
std::cout << "Connected" << std::endl;
connected = true;
}
else {
std::cout << "Failed to connect..." << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds((attempts < 3 ? 50 : 700)));
}
}
if (!connected) {
std::cout << "Connection timed out!" << std::endl;
return;
}
char recv_buf[20];
udp::endpoint sender_endpoint; //Populated by the next line
size_t len = socket.receive_from(asio::buffer(recv_buf), sender_endpoint);
std::cout.write(recv_buf, len);
std::cout << "Done " << len << std::endl;
}
catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
while (!renderer->getWindow()->getShouldClose()) loop();
}

View File

@ -0,0 +1,86 @@
//
// Created by aurailus on 11/01/19.
//
#include "ServerConnection.h"
ServerConnection::ServerConnection(std::string address, int port) {
this->address = std::move(address);
this->port = port;
this->connected = false;
}
ServerConfig* ServerConnection::connect() {
try {
asio::ip::udp::resolver resolver(io_context);
remote_endpoint = *resolver.resolve(asio::ip::udp::v4(), address, std::to_string(port)).begin();
asio::ip::udp::socket socket(io_context);
socket.open(asio::ip::udp::v4());
int attempts = 0;
bool handshook = false;
Timer t("Connection time");
while (t.elapsedNs() < 10L*1000000L*1000L) {
if (!handshook) {
Packet p;
p = Packet(Packet::HANDSHAKE);
p.addInt(attempts++);
auto data = p.serialize();
socket.send_to(asio::buffer(data, data.size()), remote_endpoint);
std::cout << "Sent handshake." << std::endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(200));
size_t pendingSize = socket.available();
if (pendingSize > 0) {
std::vector<Packet::PacketByte> recv_buf((unsigned long)pendingSize);
auto remote_endpoint = new asio::ip::udp::endpoint();
socket.receive_from(asio::buffer(recv_buf, pendingSize), *remote_endpoint);
auto packet = Packet::deserialize(recv_buf);
if (packet.type == Packet::HANDSHAKE) {
std::cout << "Handshake received." << std::endl;
t = Timer("Authenticate time");
handshook = true;
Packet p;
p = Packet(Packet::AUTHTOKEN);
p.addString("Hello! I'm a string!!!");
auto data = p.serialize();
socket.send_to(asio::buffer(data, data.size()), *remote_endpoint);
}
else if (packet.type == Packet::PLAYRINFO) {
std::cout << "Player info received." << std::endl;
connected = true;
float x = Packet::decodeFloat(&packet.data[0]);
float y = Packet::decodeFloat(&packet.data[4]);
float z = Packet::decodeFloat(&packet.data[8]);
return new ServerConfig {
.playerPos = glm::vec3(x, y, z)
};
}
}
}
if (!connected) {
std::cout << "Connection timed out!" << std::endl;
return nullptr;
}
}
catch (std::exception &e) {
std::cout << e.what() << std::endl;
}
}
ServerConnection::~ServerConnection() {
}

View File

@ -0,0 +1,37 @@
//
// Created by aurailus on 11/01/19.
//
#ifndef ZEUS_SERVERCONNECTION_H
#define ZEUS_SERVERCONNECTION_H
#include <string>
#include <vec3.hpp>
#include <asio.hpp>
#include "../engine/Timer.h"
#include "../server/Packet.h"
struct ServerConfig {
glm::vec3 playerPos;
};
class ServerConnection {
public:
ServerConnection(std::string address, int port);
ServerConfig* connect();
~ServerConnection();
private:
bool connected;
asio::io_context io_context;
asio::ip::udp::endpoint remote_endpoint;
std::string address;
int port;
};
#endif //ZEUS_SERVERCONNECTION_H

View File

@ -7,6 +7,9 @@
#include "../lua_api/l_register_blockmodel.h"
GameScene::GameScene(ClientState* state) : Scene(state) {
server = new ServerConnection("127.0.0.1", 12345);
auto info = server->connect();
textureAtlas = new TextureAtlas("../tex");
blockAtlas = new BlockAtlas(textureAtlas);
@ -33,7 +36,7 @@ GameScene::GameScene(ClientState* state) : Scene(state) {
player = new Player();
player->create(world, state->renderer->getCamera());
player->setPos(glm::vec3(16, 24, 0));
player->setPos(info->playerPos);
world->genNewChunk(glm::vec3(0, 0, 0));

View File

@ -19,6 +19,7 @@
#include "../engine/Ray.h"
#include "../client/Scene.h"
#include "../server/Server.h"
#include "../client/ServerConnection.h"
class GameScene : public Scene {
public:
@ -30,18 +31,13 @@ public:
void cleanup() override;
public:
ServerConnection* server;
Player* player;
//The World object represents the physical game region that is played in. It handles chunk updates and entities.
World* world;
//The texture atlas makes a single patched texture from an asset folder.
TextureAtlas* textureAtlas;
//The block atlas holds block definitions and models.
BlockAtlas* blockAtlas;
//Entities
//Entities to be drawn with world shaders
std::vector<Entity*> entities;
//GUI Related things

View File

@ -8,6 +8,7 @@ ClientConnection::ClientConnection() = default;
ClientConnection::ClientConnection(asio::ip::udp::endpoint* endpoint) {
this->endpoint = endpoint;
this->authenticated = false;
}
ClientConnection::~ClientConnection() {

View File

@ -14,6 +14,7 @@ public:
explicit ClientConnection(asio::ip::udp::endpoint* endpoint);
asio::ip::udp::endpoint* endpoint;
bool authenticated;
long lastAliveTime;
~ClientConnection();

View File

@ -65,6 +65,31 @@ void Packet::encodeInt(std::vector<Packet::PacketByte> &target, int num) {
}
#pragma clang diagnostic pop
void Packet::encodeFloat(std::vector<Packet::PacketByte> &target, float num) {
typedef union {
float a;
unsigned char b[4];
} float_union;
float_union fl {num};
target.push_back(fl.b[0]);
target.push_back(fl.b[1]);
target.push_back(fl.b[2]);
target.push_back(fl.b[3]);
}
float Packet::decodeFloat(Packet::PacketByte *floatStart) {
typedef union {
float a;
unsigned char b[4];
} float_union;
float_union fl { .b = {*floatStart, *(floatStart+1), *(floatStart+2), *(floatStart+3)} };
return fl.a;
}
int Packet::decodeInt(PacketByte* intStart) {
int num = 0;
for (int i = 0; i < 4; i++) {
@ -84,4 +109,26 @@ void Packet::addIntegers(std::vector<int> &integers) {
void Packet::addInt(int integer) {
encodeInt(this->data, integer);
this->length = data.size();
}
void Packet::addFloats(std::vector<float> &floats) {
for (float f : floats) {
encodeFloat(this->data, f);
}
this->length = data.size();
}
void Packet::addFloat(float floatVal) {
encodeFloat(this->data, floatVal);
this->length = data.size();
}
void Packet::addString(std::string *string) {
addInt((int)string->length());
std::copy(string->begin(), string->end(), std::back_inserter(data));
this->length = data.size();
}
void Packet::addString(std::string string) {
addString(&string);
}

View File

@ -6,6 +6,8 @@
#define ZEUS_PACKET_H
#include <vector>
#include <string>
#include <asio.hpp>
class Packet {
public:
@ -17,9 +19,13 @@ public:
void addIntegers(std::vector<int> &integers);
void addInt(int integer);
void addFloats(std::vector<float> &floats);
void addFloat(float floatVal);
void addString(std::string *string);
void addString(std::string string);
unsigned long length;
PacketType type;
unsigned long length = 0;
PacketType type = UNDEFINED;
std::vector<PacketByte> data;
~Packet() = default;
@ -27,12 +33,17 @@ public:
static Packet deserialize(std::vector<PacketByte> data);
std::vector<Packet::PacketByte> serialize();
static void encodeInt(std::vector<PacketByte> &target, int num);
static int decodeInt(PacketByte* intStart);
static void encodeInt(std::vector<PacketByte> &target, int num);
static int decodeInt(PacketByte* intStart);
static void encodeFloat(std::vector<PacketByte> &target, float num);
static float decodeFloat(PacketByte* floatStart);
public:
const static PacketType HANDSHAKE = 0;
const static PacketType REQCHUNKS = 1;
const static PacketType KEEPALIVE = 2;
const static PacketType UNDEFINED = 0;
const static PacketType HANDSHAKE = 1;
const static PacketType AUTHTOKEN = 2;
const static PacketType PLAYRINFO = 2;
// const static PacketType REQCHUNKS = 2;
// const static PacketType KEEPALIVE = 3;
};

View File

@ -3,17 +3,17 @@
//
#include "Server.h"
#include "../game/world/Player.h"
Server::Server() = default;
Server::Server(int port) {
//TODO: Use the port in the server initializer
this->port = port;
alive = true;
}
void Server::start() {
server_socket = new asio::ip::udp::socket(io_context, udp::endpoint(udp::v4(), 12346));
server_socket = new asio::ip::udp::socket(io_context, udp::endpoint(udp::v4(), port));
while (alive) loop();
}
@ -37,28 +37,72 @@ void Server::loop() {
std::this_thread::sleep_for(std::chrono::nanoseconds(sleep_for));
}
void Server::handlePacket(Packet &packet, udp::endpoint* endpoint) {
std::string identifier = endpoint->address().to_string() + ":" + std::to_string(endpoint->port());
std::string Server::createIdentifier(udp::endpoint *endpoint) {
return endpoint->address().to_string() + ":" + std::to_string(endpoint->port());
}
if (packet.type == Packet::HANDSHAKE) {
if (connections.count(identifier) == 0) {
std::cout << "Added " << identifier << " to client connections." << std::endl;
auto conn = new ClientConnection(endpoint);
connections.insert(std::pair<std::string, ClientConnection*>(identifier, conn));
}
else {
std::cout << identifier << " is already added to client connections." << std::endl;
}
return;
}
if (connections.count(identifier) == 0) {
std::cout << "Non-handshake packet from identifier " << identifier << "." << std::endl;
void Server::handlePacket(Packet &packet, udp::endpoint* endpoint) {
auto identifier = createIdentifier(endpoint);
if (connections.count(identifier) == 0 || !connections[identifier]->authenticated) {
handleAuthPacket(identifier, packet, endpoint);
return;
}
std::cout << packet.type << ", " << Packet::decodeInt(&packet.data[0]) << std::endl;
}
void Server::handleAuthPacket(std::string& identifier, Packet &packet, udp::endpoint* endpoint) {
if (connections.count(identifier) == 0) {
if (packet.type == Packet::HANDSHAKE) {
addConnection(identifier, endpoint);
Packet p;
p = Packet(Packet::HANDSHAKE);
auto data = p.serialize();
server_socket->send_to(asio::buffer(data, data.size()), *endpoint);
return;
}
}
else {
if (packet.type == Packet::AUTHTOKEN) {
int strLen = Packet::decodeInt(&packet.data[0]);
std::string token(packet.data.begin() + 4, packet.data.begin() + 4 + strLen);
//TODO: Validate this token somehow
std::cout << "Token: " << token << std::endl;
createPlayer(connections[identifier]);
return;
}
}
}
void Server::addConnection(std::string &identifier, udp::endpoint *endpoint) {
std::cout << "[INFO] Recieved handshake from new client " << identifier << std::endl;
auto conn = new ClientConnection(endpoint);
connections.insert(std::pair<std::string, ClientConnection*>(identifier, conn));
}
void Server::createPlayer(ClientConnection *connection) {
auto player = new ServerPlayer(connection, glm::vec3(0, 64, 0));
players.insert(std::pair<std::string, ServerPlayer*>("USERNAME", player));
Packet p;
p = Packet(Packet::PLAYRINFO);
p.addFloat(player->pos.x);
p.addFloat(player->pos.y);
p.addFloat(player->pos.z);
auto data = p.serialize();
server_socket->send_to(asio::buffer(data, data.size()), *connection->endpoint);
}
void Server::cleanup() {
alive = false;
@ -67,4 +111,4 @@ void Server::cleanup() {
Server::~Server() {
cleanup();
}
}

View File

@ -10,6 +10,7 @@
#include "../engine/Timer.h"
#include "ClientConnection.h"
#include "Packet.h"
#include "ServerPlayer.h"
#include <iostream>
#include <asio.hpp>
@ -27,9 +28,18 @@ private:
void loop();
void cleanup();
void handlePacket(Packet& packet, udp::endpoint* endpoint);
std::string createIdentifier(udp::endpoint* endpoint);
void handlePacket(Packet& packet, udp::endpoint* endpoint);
void handleAuthPacket(std::string& identifier, Packet& packet, udp::endpoint* endpoint);
void addConnection(std::string& identifier, udp::endpoint* endpoint);
void createPlayer(ClientConnection* connection);
//string is IP:Port
std::map<std::string, ClientConnection*> connections;
//string is username
std::map<std::string, ServerPlayer*> players;
int port;
bool alive;

View File

@ -0,0 +1,10 @@
//
// Created by aurailus on 11/01/19.
//
#include "ServerPlayer.h"
ServerPlayer::ServerPlayer(ClientConnection *connection, glm::vec3 pos) {
this->pos = pos;
this->connection = connection;
}

View File

@ -0,0 +1,22 @@
//
// Created by aurailus on 11/01/19.
//
#ifndef ZEUS_SERVERPLAYER_H
#define ZEUS_SERVERPLAYER_H
#include <vec3.hpp>
#include "ClientConnection.h"
class ServerPlayer {
public:
ServerPlayer() = default;
ServerPlayer(ClientConnection* connection, glm::vec3 pos);
ClientConnection* connection;
glm::vec3 pos;
};
#endif //ZEUS_SERVERPLAYER_H