Diggler/Server.cpp
Dorian Wouters 3c0ae6f49e Init commit
2016-01-02 20:00:07 +01:00

330 lines
9.1 KiB
C++

#include "Server.hpp"
#include "Game.hpp"
#include "network/Network.hpp"
#include "network/NetHelper.hpp"
#include "VersionInfo.hpp"
#include "CaveGenerator.hpp"
#include "ChunkChangeHelper.hpp"
#include <iterator>
#include <algorithm>
#include <thread>
#include <sstream>
using std::cout;
using std::endl;
using namespace Diggler::Net;
namespace Diggler {
Player* Server::getPlayerByPeer(const Peer &peer) {
try {
return &G->players.getByPeer(peer);
} catch (const std::out_of_range &e) {
return nullptr;
}
}
Player* Server::getPlayerById(uint32 id) {
try {
return &G->players.getById(id);
} catch (const std::out_of_range &e) {
return nullptr;
}
}
Player *Server::getPlayerByName(const std::string &name) {
try {
return &G->players.getByName(name);
} catch (const std::out_of_range &e) {
return nullptr;
}
}
void Server::handlePlayerJoin(InMessage &msg, Peer &peer) {
std::string name = msg.readString();
getOutputStream() << peer.getHost() << " is joining as " << name << std::endl;
// TODO: ban list
Player *possible = getPlayerByName(name);
if (possible != nullptr) {
// TODO: use kick() method
OutMessage kick(MessageType::PlayerQuit, QuitReason::UsernameAlreadyUsed);
kick.writeString("You are \faalready\f0 playing on this server");
H.send(peer, kick, Tfer::Rel);
getOutputStream() << peer.getHost() << " tried to connect as " << name << ": name is taken" << endl;
return;
}
Player &plr = G->players.add();
plr.name = name;
plr.id = FastRand();
plr.P = peer;
// Confirm successful join
OutMessage join(MessageType::PlayerJoin);
join.writeU32(plr.id);
H.send(peer, join, Tfer::Rel);
// Send the player list
for (Player &p : G->players) {
if (p.id == plr.id)
continue; // ok, he knows he's here
OutMessage playerMsg(MessageType::PlayerJoin);
playerMsg.writeU32(p.id);
playerMsg.writeString(p.name);
H.send(peer, playerMsg, Tfer::Rel);
}
OutMessage map(MessageType::MapTransfer);
G->SC->writeMsg(map);
H.send(peer, map, Tfer::Rel);
// Broadcast player's join
OutMessage broadcast(MessageType::PlayerJoin);
broadcast.writeU32(plr.id);
broadcast.writeString(plr.name);
for (Player &p : G->players) {
if (p.id == plr.id)
continue; // dont send broadcast to the player
H.send(p.P, broadcast, Tfer::Rel);
}
getOutputStream() << plr.name << " joined from " << peer.getHost() << endl;
}
void Server::handlePlayerQuit(Peer &peer, QuitReason reason) {
Player *plrPtr = getPlayerByPeer(peer);
if (plrPtr) {
Player &plr = *plrPtr;
// Broadcast disconnection
OutMessage broadcast(MessageType::PlayerQuit, reason);
broadcast.writeU32(plr.id);
for (Player &p : G->players) {
if (p.id == plr.id)
continue; // dont send broadcast to the player
H.send(p.P, broadcast, Tfer::Rel);
}
getOutputStream() << plr.name << " disconnected" << endl;
G->players.remove(plr);
} else {
getOutputStream() << peer.getHost() << " disconnected" << endl;
}
}
void Server::handleDisconnect(Peer &peer) {
handlePlayerQuit(peer, QuitReason::Timeout);
}
void Server::handleEvent(InMessage &msg, Peer &peer) {
Player &plr = G->players.getByPeer(peer);
switch (msg.getSubtype()) {
case Net::EventType::PlayerJumpOnPad: {
OutMessage out;
NetHelper::MakeEvent(out, (Net::EventType)msg.getSubtype(), plr);
NetHelper::Broadcast(G, out);
} break;
default:
break;
}
}
void Server::handleChat(InMessage &msg, Peer &peer) {
try {
// TODO: implement codecvt_utf8<utf32> when libstdc++ supports it
Player &plr = G->players.getByPeer(peer);
std::string chatMsg = msg.readString();
getOutputStream() << plr.name << ": " << chatMsg << endl;
std::ostringstream contentFormatter;
contentFormatter << plr.name << "> " << chatMsg;
std::string content = contentFormatter.str();
OutMessage newMsg(MessageType::Chat);
newMsg.writeString(content);
NetHelper::Broadcast(G, newMsg, Tfer::Rel);
} catch (const std::out_of_range &e) {
getErrorStream() << peer.getHost() << " sent chat message but is not connected" << std::endl;
}
}
void Server::handlePlayerUpdate(InMessage &msg, Peer &peer) {
try {
Player &plr = G->players.getByPeer(peer);
switch (msg.getSubtype()) {
case PlayerUpdateType::Move: {
// Broadcast movement
OutMessage bcast(MessageType::PlayerUpdate, PlayerUpdateType::Move);
bcast.writeU32(plr.id);
glm::vec3 pos = msg.readVec3(),
vel = msg.readVec3(),
acc = msg.readVec3();
bcast.writeVec3(pos);
bcast.writeVec3(vel);
bcast.writeVec3(acc);
for (Player &p : G->players) {
if (p.id == plr.id)
continue; // dont send broadcast to the player
// TODO: confirm position to player
H.send(p.P, bcast, Tfer::Unrel);
}
} break;
default:
break;
}
} catch (std::out_of_range &e) {
// TODO: log?
return;
}
}
Server::Server(Game *G) : G(G) {
getOutputStream() << "Diggler v" << VersionString << " Server, port " << G->Port << ", "
<< std::thread::hardware_concurrency() << " HW threads supported" << endl;
try {
H.create(G->Port);
} catch (Net::Exception &e) {
getErrorStream() << "Couldn't open port " << G->Port << " for listening" << endl <<
"Make sure no other server instance is running" << endl;
throw "Server init failed";
}
#if 1
G->SC->setSize(4, 4, 4);
//for (int i=0; i < 8192; i++) G->SC->set(FastRand(CX*G->SC->getChunksX()), FastRand(CY*G->SC->getChunksY()), FastRand(CZ*G->SC->getChunksZ()), (BlockType)(FastRand((int)BlockType::LAST)));
for(int x=0;x<CX*G->SC->getChunksX();x++) for(int z=0;z<(CZ*G->SC->getChunksZ())/2;z++) G->SC->set(x, 0, z, BlockType::Dirt);
for(int x=0;x<CX*G->SC->getChunksX();x++) for(int z=0;z<(CZ*G->SC->getChunksZ())/2;z++) G->SC->set(x, 0, z+(CZ*G->SC->getChunksZ())/2, BlockType::Road);
// for(int x=0;x<CX*G->SC->getChunksX();x++) for(int y=0;y<16;y++) for(int z=0;z<CZ*G->SC->getChunksZ();z++) G->SC->set(x,y,z,BlockType::Dirt);
for(int x=0; x < (int)BlockType::LAST; x++) G->SC->set(x, 2, 0, (BlockType)(x));
G->SC->set(4, 4, 4, BlockType::Shock);
G->SC->set(4, 0, 4, BlockType::Jump);
G->SC->set(0, 1, 1, BlockType::Metal);
G->SC->set(0, 2, 1, BlockType::Metal);
G->SC->set(0, 3, 1, BlockType::Metal);
G->SC->set(1, 3, 1, BlockType::Metal);
G->SC->set(2, 3, 1, BlockType::Metal);
G->SC->set(3, 1, 1, BlockType::Metal);
G->SC->set(3, 2, 1, BlockType::Metal);
G->SC->set(3, 3, 1, BlockType::Metal);
CaveGenerator::PaintAtPoint(*(G->SC), 8, 8, 8, 1, BlockType::Dirt);
CaveGenerator::PaintAtPoint(*(G->SC), 16, 8, 8, 2, BlockType::Dirt);
CaveGenerator::PaintAtPoint(*(G->SC), 24, 8, 8, 3, BlockType::Dirt);
for(int x=0;x<CX*G->SC->getChunksX();x++) for(int z=0;z<(CZ*G->SC->getChunksZ())/2;z++) G->SC->set(x, 64, z, BlockType::Dirt);
G->SC->set(2*CX, 68, 2*CY, BlockType::Lava);
#else
G->SC->setSize(4, 4, 4);
CaveGenerator::GenerateCaveSystem(*(G->SC), true, 15);
#endif
//G->SC->save("/tmp/a");
//G->SC->load("/tmp/a");
/*{
Game *G = this->G;
std::thread make([G]{CaveGenerator::GenerateCaveSystem(*(G->SC), true, 15);});
make.detach();
}*/
}
void chunk_updater(Game *G, Superchunk *sc, Host &H) {
while (true) {
for (int x=0; x < CX; x++)
for (int y=0; y < CY; y++)
for (int z=0; z < CZ; z++) {
Chunk* c = sc->getChunk(x, y, z);
if (c)
c->updateServerPrepare();
}
for (int x=0; x < CX; x++)
for (int y=0; y < CY; y++)
for (int z=0; z < CZ; z++) {
Chunk* c = sc->getChunk(x, y, z);
if (c)
c->updateServer();
}
for (int x=0; x < CX; x++)
for (int y=0; y < CY; y++)
for (int z=0; z < CZ; z++) {
Chunk* c = sc->getChunk(x, y, z);
if (c)
c->updateServerSwap();
}
if (!G->CCH->empty()) {
OutMessage msg(MessageType::MapUpdate, G->CCH->count());
// Message subtype = update count, trickery ;)
G->CCH->flush(msg);
for (Player &p : G->players) {
H.send(p.P, msg, Tfer::Rel);
}
}
OutMessage msg(MessageType::Event, EventType::ExplosivesBlow);
msg.writeVec3(glm::vec3(0.f, 0.f, 0.f));
for (Player &p : G->players) {
H.send(p.P, msg, Tfer::Rel);
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
}
void Server::run() {
InMessage msg;
Peer peer;
std::thread upd(chunk_updater, G, G->SC.get(), std::ref(H));
while (true) {
if (H.recv(msg, peer, 100)) {
switch (msg.getType()) {
case MessageType::Connect:
getOutputStream() << peer.getHost() << " NEWCONN" << std::endl;
break;
case MessageType::Disconnect:
handleDisconnect(peer);
break;
case MessageType::PlayerJoin:
handlePlayerJoin(msg, peer);
break;
case MessageType::PlayerQuit:
handlePlayerQuit(peer);
break;
case MessageType::Chat:
handleChat(msg, peer);
break;
case MessageType::PlayerUpdate:
handlePlayerUpdate(msg, peer);
break;
case MessageType::Event:
handleEvent(msg, peer);
break;
default:
break;
}
}
}
}
bool Server::isPlayerOnline(const std::string &playername) const {
for (const Player &p : G->players) {
if (p.name == playername)
return true;
}
return false;
}
void Server::kick(Player& p, Net::QuitReason r, const std::string& message) {
OutMessage msg(MessageType::PlayerQuit, r);
msg.writeU32(p.id);
msg.writeString(message);
H.send(p.P, msg, Tfer::Rel);
p.P.disconnect();
}
Server::~Server() {
}
}