#include "Server.hh"
#include "Protocol.hh"
#include "../Config.hh"
#include "../Engine.hh"
#include "../Logger.hh"
// XXX: move
#include "Message.hh"
#include <cassert>
NetworkServer::NetworkServer (GameState &state, const NetworkEndpoint &listen_addr) :
state(state), netsession(NETWORK_MAGIC_ID), controller(netsession, NETCHAN_CORE) {
// connect slots
slots.connect(netsession.sig_node_connected(), this, &NetworkServer::on_node_connected);
// and then we listen
netsession.listen(listen_addr);
Engine::log(INFO, "net.server") << "Listening on interface: " << listen_addr;
}
void NetworkServer::on_node_connected (NetworkNode *node) {
Engine::log(INFO, "net.server") << "Client connected, sending terrain data: " << node->getRemoteAddress();
// send the terrain data
send_terrain_data(*node);
// create the player object (it logs it)
NetworkServerPlayer *player = new NetworkServerPlayer(*this, *node);
// add to players
players.push_back(player);
}
void NetworkServer::send_terrain_data (NetworkNode &node) {
Terrain &terrain = state.world.terrain;
// dimensions?
PixelCoordinate map = terrain.getDimensions();
// translate to a byte array
size_t terrain_size = map.x * map.y;
// get terrain buffer
const uint8_t *terrain_buf = terrain.getTerrainBuffer();
// allocate our packet...
BigNetworkPacket pkt (
// NetworkChannel header
NETWORK_SESSION_HEADER_SIZE
// our own header
+ 2 * sizeof(uint32_t)
// compressed terrain buffer
+ NetworkPacketOutput::write_compressed_size(terrain_size)
);
// write netsession header
netsession.write_packet_header(pkt, NETCHAN_TERRAIN_ARRAY);
// write terrain dimensions
pkt.write_uint32(map.x);
pkt.write_uint32(map.y);
// write compressed terrain data
pkt.write_compressed(terrain_buf, terrain_size);
// send
node.send_raw(pkt, true);
}
void NetworkServer::handle_disconnect (NetworkServerPlayer *player) {
// remove from list
players.remove(player);
}
/*
* NetworkServerObject
*/
NetworkServerObject::NetworkServerObject (NetworkServer &server) :
NetworkObject_Server(server.controller), server(server)
{
}
/*
* NetworkServerPlayer
*/
NetworkServerPlayer::NetworkServerPlayer (NetworkServer &server, NetworkNode &node) :
Player(server.state, Vector(PLAYER_INITIAL_X, PLAYER_INITIAL_Y), true), NetworkServerObject(server), node(node)
{
// messages
slots.connect(node.sig_disconnected(), this, &NetworkServerPlayer::on_disconnected);
slots.connect(this->sig_message(NETMSG_CLIENT_INPUT), this, &NetworkServerPlayer::on_input);
// the initial NETMSG_PLAYER_HELLO
NetworkMessage hello_msg(*this, NETMSG_SERVER_HELLO);
hello_msg.write_vector(getPosition());
node.send(hello_msg);
// sync other players
for (std::list<NetworkServerPlayer*>::iterator it = server.players.begin(); it != server.players.end(); it++) {
NetworkServerPlayer *player = *it;
// player is not in players list yet
assert(player != this);
// send info
player->send_info(node);
}
// sync projectiles
for (std::list<Projectile*>::iterator it = server.state.projectiles.begin(); it != server.state.projectiles.end(); it++) {
NetworkServerProjectile *proj = dynamic_cast<NetworkServerProjectile*>(*it);
// all projectiles should be under the control of the server
assert(proj);
// send info
proj->send_info(node);
}
// broadcast NETMSG_PLAYER_JOIN to all clients except current
NetworkMessage join_msg(*this, NETMSG_PLAYER_JOIN);
join_msg.write_vector(getPosition());
server.netsession.all_nodes_except(node).send(join_msg);
Engine::log(INFO, "net.server") << "Player joined: " << this << " from " << node.getRemoteAddress();
}
void NetworkServerPlayer::handleDig (Vector position, float radius) {
NetworkMessage msg(*this, NETMSG_PLAYER_DIG);
Engine::log(DEBUG, "server_player.handle_dig") << "position=" << position << ", radius=" << radius;
// write packet
msg.write_vector(position);
msg.write_float32(radius);
// broadcast packet
server.netsession.all_nodes().send(msg);
// and carry out the actual dig on the server as well
Player::handleDig(position, radius);
}
void NetworkServerPlayer::handleFireWeapon (Weapon *weapon, Vector position, Vector velocity) {
Engine::log(DEBUG, "server_player.fire_weapon") << "weapon='" << weapon->getName() << "', position=" << position << ", velocity=" << velocity;
// create new NetworkServerProjectile object
new NetworkServerProjectile(server, this, position, velocity, weapon);
// as handleFireWeapon does
weaponFired(weapon);
}
void NetworkServerPlayer::handleChangeWeapon (unsigned int weaponIndex) {
NetworkMessage msg(*this, NETMSG_PLAYER_WEAPON_CHANGE);
Engine::log(DEBUG, "server_player.change_weapon") << "weaponIndex=" << weaponIndex;
// write packet
msg.write_uint8(weaponIndex);
// XXX: only tell the client itself?
server.netsession.all_nodes().send(msg);
// pass through
Player::handleChangeWeapon(weaponIndex);
}
void NetworkServerPlayer::handleRopeState (RopeState state) {
Engine::log(DEBUG, "server_player.rope_state") << "state=" << rope.getState() << ", position=" << rope.getPosition() << ", velocity=" << rope.getVelocity() << ", length=" << rope.getLength() << ", pivotPlayer=" << rope.getPivotPlayer();
// the NetworkGroup to send to
NetworkGroup all = server.netsession.all_nodes();
// send new rope state
send_rope_state(all, state);
}
void NetworkServerPlayer::handleRopeLength (float length) {
NetworkMessage msg(*this, NETMSG_PLAYER_ROPE_LENGTH);
// write packet
msg.write_float32(length);
// send packet
server.netsession.all_nodes().send(msg);
}
void NetworkServerPlayer::spawn (Vector position) {
NetworkMessage msg(*this, NETMSG_PLAYER_SPAWN);
// write packet
msg.write_vector(position);
Engine::log(DEBUG, "server_player.spawn") << this << ": position=" << position;
// send packet
server.netsession.all_nodes().send(msg);
// super
Player::spawn(position);
}
void NetworkServerPlayer::die (bool start_timer) {
NetworkMessage msg(*this, NETMSG_PLAYER_DIE);
Engine::log(DEBUG, "server_player.die") << this;
// send packet
server.netsession.all_nodes().send(msg);
// super
Player::die(start_timer);
}
void NetworkServerPlayer::send_info (NetworkNode &node) {
// we send a NETMSG_PLAYER_INFO message for the player object
NetworkMessage player_msg(*this, NETMSG_PLAYER_INFO);
// write packet
player_msg.write_vector(getPosition());
// send message to client
node.send(player_msg);
// then send rope state, unless folded
if (rope.getState() != ROPE_FOLDED)
send_rope_state(node, rope.getState());
}
void NetworkServerPlayer::send_rope_state (NetworkTarget &target, RopeState state) {
switch (state) {
case ROPE_FLYING: {
NetworkMessage msg(*this, NETMSG_PLAYER_ROPE_THROW);
msg.write_vector(rope.getPosition());
msg.write_vector(rope.getVelocity());
msg.write_float32(rope.getLength());
target.send(msg);
} break;
case ROPE_FIXED: {
NetworkMessage msg(*this, NETMSG_PLAYER_ROPE_FIXED);
Player *player_base = rope.getPivotPlayer();
NetworkServerPlayer *player = NULL;
if (player_base != NULL && (player = dynamic_cast<NetworkServerPlayer*>(player_base)) == NULL)
throw Error("NetworkServerPlayer::handleRopeState: rope's pivotPlayer is not a NetworkServerPlayer");
msg.write_vector(rope.getPosition());
msg.write_float32(rope.getLength());
msg.write_object(player); // may be NULL
target.send(msg);
} break;
case ROPE_FOLDED: {
NetworkMessage msg(*this, NETMSG_PLAYER_ROPE_RELEASED);
target.send(msg);
} break;
}
}
void NetworkServerPlayer::on_disconnected (void) {
NetworkMessage msg(*this, NETMSG_PLAYER_QUIT);
Engine::log(INFO, "net.server") << "Player disconnected: " << this;
// remove from server
server.handle_disconnect(this);
// tell other clients
server.netsession.all_nodes().send(msg);
// XXX: free
// delete this;
}
void NetworkServerPlayer::on_input (NetworkNode *src, NetworkPacketInput &pkt) {
// sanity-check, other players shouldn't move
if (src != &node) {
Engine::log(WARN, "server_player.on_input") << "packet from wrong src=" << src << ", node=" << &node;
return;
}
// read packet
PlayerInput input = pkt.read_uint16();
TimeMS dt = pkt.read_uint32();
// Engine::log(INFO, "server_player.on_input") << "player=" << obj << ", old_pos=" << position << ", input=" << input;
// apply input
handleInput(input, dt);
// send position update
send_position_update();
}
void NetworkServerPlayer::send_position_update (void) {
NetworkMessage msg(*this, NETMSG_PLAYER_POSITION);
int flags =
(inAir ? NETWORK_PHYSICS_INAIR : 0) |
(facing == FACING_RIGHT ? NETWORK_PHYSICS_FACE_RIGHT : 0);
msg.write_vector(getPosition());
msg.write_vector(getVelocity());
msg.write_uint8(flags);
msg.write_float32(aim);
// Engine::log(INFO, "server_player.send_position_update") << "obj=" << obj << " -> " << position << "+" << velocity << " [" << flags << "]";
server.netsession.all_nodes().send(msg);
}
/*
* NetworkServerProjectile
*/
NetworkServerProjectile::NetworkServerProjectile (NetworkServer &server, NetworkServerPlayer *player, Vector position,
Vector velocity, Weapon *weapon) :
Projectile(player, position, velocity, weapon, true), NetworkServerObject(server)
{
NetworkMessage msg(*this, NETMSG_PROJECTILE_PLAYER_FIRED);
// write out packet
msg.write_object(player);
msg.write_vector(getPosition());
msg.write_vector(getVelocity());
msg.write_uint8(weapon->getID());
// send to given target
server.netsession.all_nodes().send(msg);
}
void NetworkServerProjectile::onDestroy (Vector position, bool removeGround) {
NetworkMessage msg(*this, NETMSG_PROJECTILE_DESTROY);
Engine::log(DEBUG, "server_projectile.destroy") << this << "position=" << position << ", removeGround=" << removeGround;
// write packet
msg.write_vector(position);
msg.write_uint8(removeGround ? NETWORK_PROJECTILE_REMOVE_GROUND : 0);
// broadcast packet
server.netsession.all_nodes().send(msg);
// XXX: leak obj, not yet implemented: obj.destory();
// pass on to super
Projectile::onDestroy(position, removeGround);
}
void NetworkServerProjectile::onHitPlayer (Player *player_ptr) {
NetworkMessage msg(*this, NETMSG_PROJECTILE_HIT_PLAYER);
NetworkServerPlayer *player = dynamic_cast<NetworkServerPlayer*>(player_ptr);
if (player == NULL)
throw Error("NetworkServerProjectile::onHitPlayer called with non-NetworkServerPlayer player");
Engine::log(DEBUG, "server_projectile.hit_player") << this << ": player=" << player;
// write packet
msg.write_object(player);
// send packet
server.netsession.all_nodes().send(msg);
// super
Projectile::onHitPlayer(player_ptr);
}
void NetworkServerProjectile::send_info (NetworkNode &node) {
// get our NetworkServerPlayer via getOwner()
const NetworkServerPlayer *player = dynamic_cast<const NetworkServerPlayer*>(getOwner());
// as should players
assert(player);
// send info
NetworkMessage msg(*this, NETMSG_PROJECTILE_INFO);
// write out packet
msg.write_object(player);
msg.write_vector(getPosition());
msg.write_vector(getVelocity());
msg.write_uint8(getWeapon()->getID());
// send to given target
node.send(msg);
}