// // Created by aurailus on 09/01/19. // #include #include #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((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(event.peer->data); if (client->hasPlayer) { handlePlayerPacket(*client, p); } else { bool done = config.handlePacket(*client, p); if (done) { std::shared_ptr 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(p.type) << ") recieved." << Log::endl; break; } case PacketType::PLAYER_INFO: { client.setPos(p.d.read()); client.setPitch(p.d.read()); client.setYaw(p.d.read()); 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(); unsigned int block = p.d.read(); 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(); auto face = static_cast(p.d.read()); world.blockPlace(Target(pos, face), client); break; } case PacketType::BLOCK_INTERACT: { glm::ivec3 pos = p.d.read(); auto face = static_cast(p.d.read()); world.blockInteract(Target(pos, face), client); break; } case PacketType::BLOCK_PLACE_OR_INTERACT: { glm::ivec3 pos = p.d.read(); auto face = static_cast(p.d.read()); world.blockPlaceOrInteract(Target(pos, face), client); break; } case PacketType::INV_WATCH: { std::string source = p.d.read(); std::string list = p.d.read(); // 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 list = p.d.read(); // 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(); std::string source = p.d.read(); std::string list = p.d.read(); unsigned short ind = p.d.read(); // 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(); }