230 lines
8.4 KiB
C++
230 lines
8.4 KiB
C++
//
|
|
// Created by aurailus on 09/01/19.
|
|
//
|
|
|
|
#include <thread>
|
|
#include <iostream>
|
|
|
|
#include "Server.h"
|
|
|
|
#include "../Serializer.h"
|
|
#include "../../util/Timer.h"
|
|
#include "../PacketChannel.h"
|
|
#include "../../world/Target.h"
|
|
#include "../../def/item/BlockDef.h"
|
|
#include "../../lua/usertype/ServerLuaPlayer.h"
|
|
|
|
Server::Server(unsigned short port, const std::string& subgame) :
|
|
seed(69),
|
|
port(port),
|
|
config(defs),
|
|
defs(subgame, seed),
|
|
clientList(defs),
|
|
handler(port, 32),
|
|
refs(*defs.defs, &clientList),
|
|
world(seed, defs, clientList) {
|
|
|
|
defs.init(world);
|
|
world.init("world");
|
|
config.init();
|
|
|
|
std::cout << Log::info << "Server started successfully, listening for clients." << Log::endl;
|
|
while (alive) update();
|
|
}
|
|
|
|
void Server::update() {
|
|
const static long interval_ns = static_cast<long>((1000 / 60.f) * 1000000L);
|
|
Timer loop("");
|
|
|
|
world.update(0);
|
|
defs.update(deltaTime, clientList);
|
|
refs.update();
|
|
|
|
ENetEvent event;
|
|
while (handler.update(&event) && loop.elapsedNs() < interval_ns) {
|
|
switch (event.type) {
|
|
default:
|
|
case ENET_EVENT_TYPE_NONE: {
|
|
std::cout << "Unknown packet type: " << event.type << std::endl;
|
|
break;
|
|
}
|
|
case ENET_EVENT_TYPE_CONNECT: {
|
|
clientList.handleConnect(event, refs);
|
|
break;
|
|
}
|
|
case ENET_EVENT_TYPE_DISCONNECT: {
|
|
clientList.handleDisconnect(event);
|
|
break;
|
|
}
|
|
case ENET_EVENT_TYPE_RECEIVE: {
|
|
PacketView p(event.packet);
|
|
ServerClient* client = static_cast<ServerClient*>(event.peer->data);
|
|
|
|
if (client->hasPlayer) {
|
|
handlePlayerPacket(*client, p);
|
|
}
|
|
else {
|
|
bool done = config.handlePacket(*client, p);
|
|
if (done) {
|
|
std::shared_ptr<ServerClient> clientShared = nullptr;
|
|
for (auto& sClient : clientList.clients) {
|
|
if (sClient->cid == client->cid) {
|
|
clientShared = sClient;
|
|
break;
|
|
}
|
|
}
|
|
if (!clientShared) break;
|
|
clientList.createPlayer(clientShared);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto& cid : playersUpdated) {
|
|
auto client = clientList.getClient(cid);
|
|
if (client == nullptr) continue;
|
|
|
|
Packet r(PacketType::PLAYER_INFO, false);
|
|
r.data = Serializer()
|
|
.append(client->cid)
|
|
.append(client->getPos())
|
|
.append(client->getPitch())
|
|
.append(client->getYaw())
|
|
.data;
|
|
|
|
for (auto& iter : clientList.clients)
|
|
if (iter->cid != cid && glm::distance(client->getPos(), iter->getPos()) < 200)
|
|
r.sendTo(iter->peer, PacketChannel::ENTITY);
|
|
}
|
|
playersUpdated.clear();
|
|
|
|
long sleep_for = interval_ns - loop.elapsedNs();
|
|
if (sleep_for > 0) std::this_thread::sleep_for(std::chrono::nanoseconds(sleep_for));
|
|
|
|
deltaTime = loop.elapsedNs() / 1000000.f / 1000.f;
|
|
elapsedSeconds += deltaTime;
|
|
}
|
|
|
|
void Server::handlePlayerPacket(ServerClient& client, PacketView& p) {
|
|
switch (p.type) {
|
|
default: {
|
|
std::cout << Log::err << "Invalid packet type (" << static_cast<int>(p.type) << ") recieved." << Log::endl;
|
|
break; }
|
|
|
|
case PacketType::PLAYER_INFO: {
|
|
client.setPos(p.d.read<glm::vec3>());
|
|
client.setPitch(p.d.read<float>());
|
|
client.setYaw(p.d.read<float>());
|
|
|
|
playersUpdated.emplace(client.cid);
|
|
break; }
|
|
|
|
case PacketType::THIS_PLAYER_INFO: {
|
|
client.handleAssertion(p.d);
|
|
break; }
|
|
|
|
case PacketType::BLOCK_SET: {
|
|
glm::ivec3 pos = p.d.read<glm::ivec3>();
|
|
unsigned int block = p.d.read<unsigned int>();
|
|
|
|
unsigned int worldBlock = (block == DefinitionAtlas::AIR ? world.getBlock(pos) : 0);
|
|
|
|
if (block == DefinitionAtlas::AIR) {
|
|
auto& def = defs.defs->blockFromId(worldBlock);
|
|
if (def.callbacks.count(Callback::BREAK)) defs.lua->safe_function(def.callbacks[Callback::BREAK], pos, ServerLuaPlayer(client));
|
|
defs.lua->safe_function(defs.lua->core["trigger"], "break", pos, ServerLuaPlayer(client));
|
|
}
|
|
else {
|
|
auto& def = defs.defs->blockFromId(block);
|
|
if (def.callbacks.count(Callback::PLACE)) defs.lua->safe_function(def.callbacks[Callback::PLACE], pos, ServerLuaPlayer(client));
|
|
defs.lua->safe_function(defs.lua->core["trigger"], "place", pos, ServerLuaPlayer(client));
|
|
}
|
|
|
|
world.setBlock(pos, block);
|
|
|
|
if (block == DefinitionAtlas::AIR) {
|
|
auto& def = defs.defs->blockFromId(worldBlock);
|
|
if (def.callbacks.count(Callback::AFTER_BREAK))
|
|
defs.lua->safe_function(def.callbacks[Callback::AFTER_BREAK], pos, ServerLuaPlayer(client));
|
|
defs.lua->safe_function(defs.lua->core["trigger"], "after_break", pos, ServerLuaPlayer(client));
|
|
}
|
|
else {
|
|
auto& def = defs.defs->blockFromId(block);
|
|
if (def.callbacks.count(Callback::AFTER_PLACE))
|
|
defs.lua->safe_function(def.callbacks[Callback::AFTER_PLACE], pos, ServerLuaPlayer(client));
|
|
defs.lua->safe_function(defs.lua->core["trigger"], "after_place", pos, ServerLuaPlayer(client));
|
|
}
|
|
break; }
|
|
|
|
case PacketType::BLOCK_PLACE: {
|
|
glm::ivec3 pos = p.d.read<glm::ivec3>();
|
|
auto face = static_cast<EVec>(p.d.read<unsigned short>());
|
|
world.blockPlace(Target(pos, face), client);
|
|
break; }
|
|
|
|
case PacketType::BLOCK_INTERACT: {
|
|
glm::ivec3 pos = p.d.read<glm::ivec3>();
|
|
auto face = static_cast<EVec>(p.d.read<unsigned short>());
|
|
world.blockInteract(Target(pos, face), client);
|
|
break; }
|
|
|
|
case PacketType::BLOCK_PLACE_OR_INTERACT: {
|
|
glm::ivec3 pos = p.d.read<glm::ivec3>();
|
|
auto face = static_cast<EVec>(p.d.read<unsigned short>());
|
|
world.blockPlaceOrInteract(Target(pos, face), client);
|
|
break; }
|
|
|
|
case PacketType::INV_WATCH: {
|
|
std::string source = p.d.read<std::string>();
|
|
std::string list = p.d.read<std::string>();
|
|
|
|
// TODO: When inventory saving / loading is implemented there will need to be a cross-save identifier.
|
|
if (source == "current_player") source = "player:" + std::to_string(client.cid);
|
|
|
|
bool exists = refs.addWatcher(source, list, client.cid);
|
|
if (!exists) Serializer().append(source).append(list)
|
|
.packet(PacketType::INV_INVALID).sendTo(client.peer, PacketChannel::INVENTORY);
|
|
break; }
|
|
|
|
case PacketType::INV_UNWATCH: {
|
|
std::string source = p.d.read<std::string>();
|
|
std::string list = p.d.read<std::string>();
|
|
|
|
// TODO: When inventory saving / loading is implemented there will need to be a cross-save identifier.
|
|
if (source == "current_player") source = "player:" + std::to_string(client.cid);
|
|
|
|
bool exists = refs.removeWatcher(source, list, client.cid);
|
|
if (!exists) {
|
|
Serializer().append(source).append(list)
|
|
.packet(PacketType::INV_INVALID).sendTo(client.peer, PacketChannel::INVENTORY);
|
|
break;
|
|
}
|
|
|
|
break; }
|
|
|
|
case PacketType::INV_INTERACT: {
|
|
unsigned short type = p.d.read<unsigned short>();
|
|
|
|
std::string source = p.d.read<std::string>();
|
|
std::string list = p.d.read<std::string>();
|
|
unsigned short ind = p.d.read<unsigned short>();
|
|
|
|
// TODO: When inventory saving / loading is implemented there will need to be a cross-save identifier.
|
|
if (source == "current_player") source = "player:" + std::to_string(client.cid);
|
|
|
|
if (type == 0) refs.primaryInteract(source, list, ind, client.cid);
|
|
else refs.secondaryInteract(source, list, ind, client.cid);
|
|
|
|
break; }
|
|
}
|
|
}
|
|
|
|
void Server::cleanup() {
|
|
alive = false;
|
|
}
|
|
|
|
Server::~Server() {
|
|
cleanup();
|
|
} |