#include "Network.h" #include "btBulletDynamicsCommon.h" #include #include #include #include namespace gln { Network::Network(Game *game) : game(game), client_peer(0) { } void Network::initialize(const std::string &hostname) { if (enet_initialize() != 0) { throw "failed to initialize enet"; } if (hostname.empty()) { std::cout << "[Network] Start server." << std::endl; ENetAddress address; address.host = ENET_HOST_ANY; address.port = 1234; host = enet_host_create(&address, 32, 2, 0, 0); if (host == NULL) { throw "An error occurred while trying to create an ENet server host."; } } else { std::cout << "[Network] Start client." << std::endl; ENetAddress address; ENetEvent event; host = enet_host_create(NULL, 1, 2, 57600 / 8, 14400 / 8); if (host == NULL) { throw "An error occurred while trying to create an ENet client host."; } std::cout << "[Network] connect to " << hostname << std::endl; enet_address_set_host(&address, hostname.c_str()); address.port = 1234; /* Initiate the connection, allocating the two channels 0 and 1. */ client_peer = enet_host_connect(host, &address, 2, 0); if (client_peer == NULL) { throw "No available peers for initiating an ENet connection."; } /* Wait up to 5 seconds for the connection attempt to succeed. */ if (enet_host_service(host, &event, 2000) > 0 && event.type == ENET_EVENT_TYPE_CONNECT) { std::cout << "[Network] connected" << std::endl; } else { /* Either the 5 seconds are up or a disconnect event was */ /* received. Reset the peer in the event the 5 seconds */ /* had run out without any significant event. */ enet_peer_reset(client_peer); throw "[Network] connection failed."; } } } void Network::shutdown() { enet_host_destroy(host); enet_deinitialize(); } void Network::sendGameUpdates() { size_t i; for (i = 0; i < GAME_PLAYER_COUNT; i++) { if (game->player[i].status == 0) continue; player_update_message_t msg; //msg.msg_id = MESSAGE_PLAYER_UPDATE; msg.player_id = game->player[i].id; btVector3 v = game->player[i].body->getLinearVelocity(); btVector3 p = game->player[i].body->getWorldTransform().getOrigin(); msg.x = p.x(); msg.y = p.y(); msg.z = p.z(); msg.vx = v.x(); msg.vy = v.y(); msg.vz = v.z(); msg.points = game->player[i].points; YAML::Emitter em; em << msg; ENetPacket * packet = enet_packet_create((uint8_t *) em.c_str(), em.size(), 0); enet_host_broadcast(host, 0, packet); } for (i = 0; i < game->teams.size(); i++) { team_update_message_t msg; msg.team_id = game->teams[i].id; msg.points = game->teams[i].points; msg.wins = game->teams[i].wins; YAML::Emitter em; em << msg; ENetPacket * packet = enet_packet_create((uint8_t *) em.c_str(), em.size(), 0); enet_host_broadcast(host, 0, packet); } enet_host_flush(host); } template T *_message_cast(void *p, size_t l) { if (sizeof(T) != l) { std::cout << sizeof(T) << std::endl; std::cout << l << std::endl; throw(typeid(T).name()); } return (T *) p; } #define PARSE_START(msg) void operator >>(const YAML::Node& node, msg& m) { #define PARSE_FIELD(name) node[#name] >> m.name; #define PARSE_END() } #define EMIT_START(msg) YAML::Emitter& operator <<(YAML::Emitter& out, const msg& m) { out << YAML::BeginMap; out << YAML::Key << "id" << YAML::Value << msg::id; #define EMIT_FIELD(name) out << YAML::Key << #name << YAML::Value << m.name; #define EMIT_END() out << YAML::EndMap;return out;} // player_spawn_message_t EMIT_START(player_spawn_message_t) EMIT_FIELD(team_id)EMIT_FIELD(player_id) EMIT_END() PARSE_START(player_spawn_message_t) PARSE_FIELD(team_id) PARSE_FIELD(player_id)PARSE_END() // player_kill_message_t EMIT_START(player_kill_message_t) EMIT_FIELD(player_id) EMIT_END() PARSE_START(player_kill_message_t) PARSE_FIELD(player_id)PARSE_END() // accept_message_t EMIT_START(accept_message_t) EMIT_FIELD(player_id) EMIT_END() PARSE_START(accept_message_t) PARSE_FIELD(player_id)PARSE_END() // player_update_message_t EMIT_START(player_update_message_t) EMIT_FIELD(player_id)EMIT_FIELD(x)EMIT_FIELD( y)EMIT_FIELD(z)EMIT_FIELD(vx)EMIT_FIELD(vy)EMIT_FIELD(vz)EMIT_FIELD( points) EMIT_END() PARSE_START(player_update_message_t) PARSE_FIELD(player_id)PARSE_FIELD(x)PARSE_FIELD( y)PARSE_FIELD(z)PARSE_FIELD(vx)PARSE_FIELD(vy)PARSE_FIELD(vz) PARSE_FIELD(points)PARSE_END() // player_accelerate_message_t EMIT_START(player_accelerate_message_t) EMIT_FIELD(player_id)EMIT_FIELD(x)EMIT_FIELD( y)EMIT_FIELD(z)EMIT_FIELD(rx)EMIT_FIELD( ry)EMIT_FIELD(rz) EMIT_END() PARSE_START(player_accelerate_message_t) PARSE_FIELD(player_id)PARSE_FIELD(x)PARSE_FIELD( y)PARSE_FIELD(rx)PARSE_FIELD( ry) PARSE_FIELD(rz)PARSE_END() // bomb_drop_meesage_t EMIT_START(bomb_drop_meesage_t) EMIT_FIELD(x)EMIT_FIELD(y)EMIT_FIELD(z)EMIT_FIELD( vx)EMIT_FIELD(vy)EMIT_FIELD(vz)EMIT_FIELD(ttl) EMIT_END() PARSE_START(bomb_drop_meesage_t) PARSE_FIELD(x)PARSE_FIELD(y)PARSE_FIELD(z)PARSE_FIELD( vx)PARSE_FIELD(vy)PARSE_FIELD(vz) PARSE_FIELD(ttl)PARSE_END() // bomb_update_meesage_t EMIT_START(bomb_update_meesage_t) EMIT_FIELD(bomb_index)EMIT_FIELD(x)EMIT_FIELD( y)EMIT_FIELD(z)EMIT_FIELD(vx)EMIT_FIELD(vy)EMIT_FIELD(vz)EMIT_FIELD( ttl) EMIT_END() PARSE_START(bomb_update_meesage_t) PARSE_FIELD(bomb_index)PARSE_FIELD(x)PARSE_FIELD( y)PARSE_FIELD(z)PARSE_FIELD(vx)PARSE_FIELD(vy)PARSE_FIELD(vz) PARSE_FIELD(ttl)PARSE_END() // point_update_mesage_t EMIT_START(point_update_mesage_t) EMIT_FIELD(point_index)EMIT_FIELD(x)EMIT_FIELD( y)EMIT_FIELD(z)EMIT_FIELD(status) EMIT_END() PARSE_START(point_update_mesage_t) PARSE_FIELD(point_index)PARSE_FIELD(x)PARSE_FIELD( y)PARSE_FIELD(z) PARSE_FIELD(status)PARSE_END() // team_update_message_t EMIT_START(team_update_message_t) EMIT_FIELD(team_id)EMIT_FIELD(points)EMIT_FIELD( wins) EMIT_END() PARSE_START(team_update_message_t) PARSE_FIELD(team_id)PARSE_FIELD(points) PARSE_FIELD(wins)PARSE_END() void Network::dispatch(enet_uint8 *data, size_t length) { std::istringstream istr; istr.rdbuf()->pubsetbuf((char *) data, length); YAML::Parser parser; parser.Load(istr); YAML::Node doc; parser.GetNextDocument(doc); int msd_id; doc["id"] >> msd_id; switch (msd_id) { case MESSAGE_PLAYER_SPAWN: { player_spawn_message_t m; doc >> m; Team *team = game->getTeam(m.team_id); player_t *player = game->spawnPlayerWithId(team, m.player_id); break; } case MESSAGE_PLAYER_KILL: { player_kill_message_t m; doc >> m; player_t *player = game->getPlayer(m.player_id); player->status = 0; player->team = 0; break; } case MESSAGE_ACCEPT: { accept_message_t m; doc >> m; game->local_player = game->getPlayer(m.player_id); std::cout << "[Network] accpeted player " << m.player_id << std::endl; break; } case MESSAGE_PLAYER_UPDATE: { player_update_message_t m; doc >> m; player_t *player = game->getPlayer(m.player_id); #if 1 btVector3 v = player->body->getLinearVelocity(); btVector3 p = player->body->getWorldTransform().getOrigin(); btVector3 v1(m.vx, m.vy, m.vz); btVector3 p1(m.x, m.y, m.z); player->body->activate(true); player->body->setLinearVelocity(v1); btVector3 dp = p1 - p; if (dp.length2() > 5.) player->body->getWorldTransform().setOrigin(p1); else player->body->getWorldTransform().setOrigin(p + dp / 10.); //player->body->applyCentralForce((v1 -v)/10.0); #endif #if 0 std::cout << " v:" << um->vx << " " << um->vy << " " << um -> vz << std::endl; std::cout << " p:" << um->x << " " << um->y << " " << um -> z << std::endl; double threshold = 0.1; double dx = um->x - player->x; double dy = um->y - player->y; double dz = um->z - player->z; std::cout << " d:" << dx << " " << dy << " " << dz << std::endl; if (fabs(dx) < threshold) { player->x += 0.1 * dx; } else { player->x = um->x; } if (fabs(dy) < threshold) { player->y += 0.1 * dy; } else { player->y = um->y; } if (fabs(dz) < threshold) { player->z += 0.1 * dz; } else { player->z = um->z; } player->vx = um->vx; player->vy = um->vy; player->vz = um->vz; #endif player->points = m.points; break; } case MESSAGE_POINT_UPDATE: { point_update_mesage_t m; doc >> m; point_t *p = &game->point[m.point_index]; p->status = m.status; p->x = m.x; p->y = m.y; p->z = m.z; break; } case MESSAGE_PLAYER_ACCELERATE: { player_accelerate_message_t m; doc >> m; player_t *player = game->getPlayer(m.player_id); btVector3 v = player->body->getLinearVelocity(); v += btVector3(m.x, m.y, m.z); player->body->setLinearVelocity(v); player->body->activate(true); //player->body->applyCentralImpulse(btVector3(m.x, m.y, m.z)); break; } case MESSAGE_BOMB_DROP: { bomb_drop_meesage_t m; doc >> m; bomb_t *bomb = game->spawn_bomb(); if (bomb == NULL) return; bomb->x = m.x + m.vx * 0.0001; bomb->y = m.y + m.vy * 0.0001; bomb->z = m.z + m.vz * 0.0001; bomb->vx = m.vx; bomb->vy = m.vy; bomb->vz = m.vz; bomb->ttl = m.ttl; if (client_peer == NULL) { ENetPacket * packet = enet_packet_create(data, length, ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(host, 0, packet); } break; } case MESSAGE_TEAM_UPDATE: { team_update_message_t m; doc >> m; Team *team = game->getTeam(m.team_id); if (team == NULL) return; team->points = m.points; team->wins = m.wins; break; } }; } void Network::service(uint32_t timeout) { ENetEvent event; if (host == 0) throw "not connected"; /* Wait up to 1000 milliseconds for an event. */ while (enet_host_service(host, &event, timeout) > 0) { switch (event.type) { case ENET_EVENT_TYPE_CONNECT: { char hostname[256]; enet_address_get_host(&event.peer->address, hostname, sizeof(hostname)); std::cout << "[Network] A new client connected from " << hostname << std::endl; // bring new client up to date size_t i; for (i = 0; i < GAME_PLAYER_COUNT; i++) { if (game->player[i].status == 0) continue; // send player spawn message player_spawn_message_t spwan_msg; spwan_msg.team_id = game->player[i].team->id; spwan_msg.player_id = game->player[i].id; YAML::Emitter em; em << spwan_msg; ENetPacket * packet = enet_packet_create((uint8_t *) em.c_str(), em.size(), ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, packet); } for (i = 0; i < GAME_POINT_COUNT; i++) { if (game->point[i].status == 0) continue; point_update_mesage_t msg; msg.point_index = i; msg.status = game->point[i].status; msg.x = game->point[i].x; msg.y = game->point[i].y; msg.z = game->point[i].z; YAML::Emitter em; em << msg; ENetPacket * packet = enet_packet_create((uint8_t *) em.c_str(), em.size(), ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, packet); } Team *team = game->team_with_least_players(); player_t *player = game->spawnPlayer(team); event.peer->data = player; { // send player spawn message player_spawn_message_t spwan_msg; spwan_msg.team_id = team->id; spwan_msg.player_id = player->id; YAML::Emitter em; em << spwan_msg; ENetPacket * packet = enet_packet_create((uint8_t *) em.c_str(), em.size(), ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(host, 0, packet); } { // send team and player accept_message_t msg; msg.player_id = player->id; YAML::Emitter em; em << msg; ENetPacket * packet = enet_packet_create((uint8_t *) em.c_str(), em.size(), ENET_PACKET_FLAG_RELIABLE); enet_peer_send(event.peer, 0, packet); } // send state } break; case ENET_EVENT_TYPE_RECEIVE: { dispatch(event.packet->data, event.packet->dataLength); enet_packet_destroy(event.packet); break; } case ENET_EVENT_TYPE_DISCONNECT: { /* Reset the peer's client information. */ player_t *player = (player_t *) event.peer->data; player->status = 0; player->team = 0; #if 0 // send player spawn message player_kill_message_t msg; msg.msg_id = MESSAGE_PLAYER_KILL; msg.player_id = player->id; ENetPacket * packet = enet_packet_create(&msg, sizeof(msg), ENET_PACKET_FLAG_RELIABLE); enet_host_broadcast(host, 0, packet); #endif } event.peer->data = NULL; break; } } } //void Network::sendMessage(message_t &m) { // if (client_peer) { // msgpack::sbuffer b; // msgpack::packer packer(b); // m.pack(packer); // ENetPacket * packet = enet_packet_create(b.data(), b.size(), // ENET_PACKET_FLAG_RELIABLE); // enet_peer_send(client_peer, 0, packet); // } else { // // dispatch // } //} void Network::send(const YAML::Emitter &em) { sendMessage((uint8_t *) em.c_str(), em.size()); } void Network::sendMessage(uint8_t *data, size_t length) { if (client_peer) { ENetPacket * packet = enet_packet_create(data, length, ENET_PACKET_FLAG_RELIABLE); enet_peer_send(client_peer, 0, packet); } else { dispatch(data, length); } } } // namespace grln