#include <WinSock2.h> #include <ws2tcpip.h>
#include <QuickDraw.h> #include <Timer.h>
#include "Room.h" #include "Ship.h"
#include <sstream> #include <iostream>
enum ActionType { PLAYER_UPDATE, PLAYER_JOIN, PLAYER_LEAVE, ROOM_UPDATE, BROADCAST };
class NamedSocket { public: struct sockaddr_in socket; std::string id; };
class Message { public: int action; };
class RoomMessage : public Message { public: int numPlayers; // Arrays int x[10], y[10], r[10]; };
class ServerMessage : public Message { public: int numPlayers; struct sockaddr_in socket; std::string serverName; };
class PlayerMessage : public Message { public: int port; int numPlayers; std::string id; double px, py; double vx, vy; double r, fired; };
const int gamePort = 33303; struct in_addr selfIP;
double tickRate = 1 / 20;
int client(int argc, char * argv[]) { std::cout << "Loading...";
// Set up the socket. // Argument is an IP address to send packets to. Multicast allowed. int socket_d; socket_d = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
char opt = 1; setsockopt(socket_d, SOL_SOCKET, SO_BROADCAST, (char*)&opt, sizeof(opt));
struct sockaddr_in my_addr; // my address information
int myPort = gamePort + 1;
my_addr.sin_family = AF_INET; // host byte order my_addr.sin_port = htons(myPort); // short, network byte order my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
if (bind(socket_d, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { std::cerr << "Bind error: " << WSAGetLastError() << "\n"; return 1; }
// Hacky clearing of console std::cout << "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n";
// Hacky english std::string playerName; std::cout << "Khoroshiy den! Yiu have opened glorious Mother Russia Pilot Game for the Benefit of the Proletiriat.\nPlease to be choose a name: "; std::cin >> playerName;
struct sockaddr_in dest_addr;
Timer clientTimer; clientTimer.mark();
u_long iMode = 1; if (ioctlsocket(socket_d, FIONBIO, &iMode) != 0) { std::cout << "failed to set blocking mode"; }
if (argc > 1) { // Do direct connection dest_addr.sin_family = AF_INET; dest_addr.sin_addr.s_addr = inet_addr(argv[1]); dest_addr.sin_port = htons(gamePort); } else { std::vector<ServerMessage> allServers;
Message broadcast; broadcast.action = BROADCAST;
struct sockaddr_in all;
all.sin_family = AF_INET; all.sin_port = htons(gamePort); all.sin_addr.s_addr = INADDR_BROADCAST; // broadcast address memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
sendto(socket_d, (const char *)&broadcast, sizeof (broadcast), 0, (const sockaddr *)&(all), sizeof (all));
std::cout << "Searching for servers, have good wait ples.";
int timesDotted = 0; // haha sleep deprivation ;3;
while (clientTimer.interval() < 3.1) { struct sockaddr_in their_addr; // connector's address information int addr_len = sizeof(struct sockaddr); char buf[50000]; int numbytes = 50000; int n;
if ((n = recvfrom(socket_d, buf, numbytes, 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) { if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking. { std::cerr << "Recv error: " << WSAGetLastError() << "\n"; } } else { ServerMessage *response = (ServerMessage *)buf; response->socket = their_addr;
allServers.push_back(*response); }
if (clientTimer.interval() > 1 && timesDotted == 0) { std::cout << "."; timesDotted = 1; } else if (clientTimer.interval() > 2 && timesDotted == 1) { std::cout << "."; timesDotted = 2; }
if (clientTimer.interval() > 3 && allServers.size() == 0) { std::cout << "\nFailed to find any server, Chyort voz'mi."; sendto(socket_d, (const char *)&broadcast, sizeof (broadcast), 0, (const sockaddr *)&(all), sizeof (all)); timesDotted = 0; clientTimer.mark(); } }
std::cout << "\n\nCongratulation, " << playerName << "! you are on your way into training simulation.\nPlease to be choose a server:\n\n";
int i = 0; for each (ServerMessage message in allServers) { i++; std::cout << i << " - " << message.serverName << "\n"; }
int selection; std::cout << "-: "; std::cin >> selection;
dest_addr = allServers[selection-1].socket; }
// Turn blocking back on temporarily for initial server response iMode = 0; if (ioctlsocket(socket_d, FIONBIO, &iMode) != 0) { std::cout << "failed to set blocking mode"; }
QuickDraw window; View & view = (View &) window; Controller & controller = (Controller &) window;
Ship * ship = new Ship (Ship::INPLAY, playerName);
PlayerMessage thisPlayer; thisPlayer.id = playerName; thisPlayer.action = PLAYER_JOIN; ship->getPosition(thisPlayer.px, thisPlayer.py); thisPlayer.r = ship->direction; thisPlayer.fired = ship->controlfire; thisPlayer.port = myPort;
// Start timer Timer latencyTimer; latencyTimer.mark();
sendto(socket_d, (const char *)&thisPlayer, sizeof (thisPlayer), 0, (const sockaddr *)&(dest_addr), sizeof (dest_addr));
struct sockaddr_in their_addr; // connector's address information int addr_len = sizeof(struct sockaddr); char buf[50000]; int numbytes = 50000; int n;
Room model; int numPlayers;
// Receive room update if ((n = recvfrom(socket_d, buf, numbytes, 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) { if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking. { std::cerr << "Recv error: " << WSAGetLastError() << "\n"; } } else { std::cout << "Received room data, size: " << n << "\n"; RoomMessage *m = (RoomMessage *)buf; for (int i = 0; i < 10; i++) { model.obstacles.push_back(new Obstacle(m->x[i], m->y[i], m->r[i])); } numPlayers = m->numPlayers; }
model.addActor(ship);
for (int i = 0; i < numPlayers; i++) { struct sockaddr_in their_addr; // connector's address information int addr_len = sizeof(struct sockaddr); char buf[50000]; int numbytes = 50000; int n;
// Receive all players in server if ((n = recvfrom(socket_d, buf, numbytes, 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) { if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking. { std::cerr << "Recv error: " << WSAGetLastError() << "\n"; } } else { std::cout << "Received player data, size: " << n << "\n";
PlayerMessage *m = (PlayerMessage *)buf;
if (m->id != playerName) { Ship *newShip = new Ship(Ship::NETWORKED, m->id); newShip->setPosition(m->px, m->py); newShip->setVelocity(m->vx, m->vy); newShip->direction = m->r; newShip->controlfire = m->fired;
model.addActor(newShip); } } }
std::cout << "Latency of initial connection: " << latencyTimer.interval() << "\n" << "Players on server: " << numPlayers << "\n";
// Create a timer to measure the real time since the previous game cycle. Timer timer; timer.mark (); // zero the timer. double lasttime = timer.interval (); double avgdeltat = 0.0;
double scale = 1.0;
// put the socket into non-blocking mode. iMode = 1; if (ioctlsocket(socket_d, FIONBIO, &iMode) != 0) { std::cout << "failed to set blocking mode"; }
clientTimer.mark();
while (true) { // Check for updates from the server. struct sockaddr_in their_addr; // connector's address information int addr_len = sizeof(struct sockaddr); char buf[50000]; int numbytes = 50000; int n;
// Receive room update if ((n = recvfrom(socket_d, buf, numbytes, 0, (struct sockaddr *)&their_addr, &addr_len)) == -1) { if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking. { std::cerr << "Recv error: " << WSAGetLastError() << "\n"; } } else { Message *m = (Message *)buf; if (m->action == PLAYER_UPDATE) { PlayerMessage *mp = (PlayerMessage *)buf; if (mp->id != playerName) { std::vector <Actor *> actors = model.getActors(); for (unsigned int i = 0; i < actors.size();) { if (actors[i]->getType() == Actor::SHIP) { Ship *ship = (Ship *)actors[i]; if (ship->name == mp->id) { ship->setPosition(mp->px, mp->py); ship->setVelocity(mp->vx, mp->vy); ship->direction = mp->r; ship->controlfire = mp->fired; } } i++; } } } else if (m->action == PLAYER_JOIN) { PlayerMessage *mp = (PlayerMessage *)buf;
if (mp->id != playerName) { // Add the ship into the game Ship *ship = new Ship(Ship::NETWORKED, mp->id); ship->setPosition(mp->px, mp->py); ship->direction = mp->r; ship->setVelocity(mp->vx, mp->vy); ship->controlfire = mp->fired; model.addActor(ship); } } else if (m->action == PLAYER_LEAVE) { PlayerMessage *mp = (PlayerMessage *)buf; std::vector <Actor *> actors = model.getActors(); int i = 0; for (unsigned int i = 0; i < actors.size();) { if (actors[i]->getType() == Actor::SHIP) { Ship *ship = (Ship *)actors[i]; if (ship->name == mp->id) { ship->inRoom = false; } } i++; } } }
// Calculate the time since the last iteration. double currtime = timer.interval (); double deltat = currtime - lasttime;
// Run a smoothing step on the time change, to overcome some of the // limitations with timer accuracy. avgdeltat = 0.2 * deltat + 0.8 * avgdeltat; deltat = avgdeltat; lasttime = lasttime + deltat;
// Give me another week to refactor this, and then ugly hacks won't be necessary // Deadlines aren't terribly conducive to good code (look at EA and Activision QA)
// Moved input to here from Ship if (ship->mode == Ship::INPLAY) { char c = controller.lastKey(); switch (c) { case 'W': ship->controlthrust = 1.0; break; case 'A': ship->controlleft = 1.0; break; case 'D': ship->controlright = 1.0; break; case VK_SPACE: ship->controlfire = 1.0; break; default: // unknown key. ; } }
// Send to server before model update for consistency if (clientTimer.interval() > tickRate || ship->controlfire == 1.0) { clientTimer.mark();
// Send latest update to server PlayerMessage playerUpdate; playerUpdate.action = PLAYER_UPDATE; playerUpdate.id = ship->name; ship->getPosition(playerUpdate.px, playerUpdate.py); ship->getVelocity(playerUpdate.vx, playerUpdate.vy); playerUpdate.r = ship->direction; playerUpdate.fired = ship->controlfire;
sendto(socket_d, (const char *)&playerUpdate, sizeof (playerUpdate), 0, (const sockaddr *)&(dest_addr), sizeof (dest_addr)); }
// Allow the environment to update. model.update (deltat);
// Schedule a screen update event. view.clearScreen (); double offsetx = 0.0; double offsety = 0.0; (*ship).getPosition (offsetx, offsety); model.display (view, offsetx, offsety, scale);
std::ostringstream score; score << "Score: " << ship->getScore (); view.drawText (20, 20, score.str ()); view.swapBuffer ();
if (window.isDestroyed) { PlayerMessage thisPlayer; thisPlayer.id = playerName; thisPlayer.action = PLAYER_LEAVE; thisPlayer.port = myPort;
sendto(socket_d, (const char *)&thisPlayer, sizeof (thisPlayer), 0, (const sockaddr *)&(dest_addr), sizeof (dest_addr)); break; }
}
return 0; }
int server(int argc, char * argv[]) { std::cout << "Initialising...\n";
Room model(-400, 400, 100, -500);
std::string thisServerName = argv[2];
Timer servTimer; servTimer.mark();
// Create a timer to measure the real time since the previous game cycle. Timer timer; timer.mark(); // zero the timer. double lasttime = timer.interval(); double avgdeltat = 0.0;
double scale = 1.0;
WSADATA wsaData; int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
// Set up the socket. // Argument is an IP address to send packets to. Multicast allowed. int server_socket_d; server_socket_d = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in my_addr; // my address information
my_addr.sin_family = AF_INET; // host byte order my_addr.sin_port = htons(gamePort); // short, network byte order my_addr.sin_addr.s_addr = INADDR_ANY; // automatically fill with my IP memset(&(my_addr.sin_zero), '\0', 8); // zero the rest of the struct
if (bind(server_socket_d, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) == -1) { std::cerr << "Bind error: " << WSAGetLastError() << "\n"; return -1; }
std::cout << "Server running\n";
std::vector <NamedSocket> client_addr; int playersInServer = 0;
u_long iMode = 1; if (ioctlsocket(server_socket_d, FIONBIO, &iMode) != 0) { std::cout << "failed to set non blocking"; }
while (true) { // Calculate the time since the last iteration. double currtime = timer.interval(); double deltat = currtime - lasttime;
// Run a smoothing step on the time change, to overcome some of the // limitations with timer accuracy. avgdeltat = 0.2 * deltat + 0.8 * avgdeltat; deltat = avgdeltat; lasttime = lasttime + deltat;
// Allow the environment to update. model.update(deltat);
struct sockaddr_in dest_addr; // connector's address information int addr_len = sizeof(struct sockaddr); char buf[50000]; int numbytes = 50000; int n;
// Receive requests from clients. if ((n = recvfrom(server_socket_d, buf, numbytes, 0, (struct sockaddr *)&dest_addr, &addr_len)) == -1) { if (WSAGetLastError() != WSAEWOULDBLOCK) // A real problem - not just avoiding blocking. { std::cout << "Recv error: " << WSAGetLastError() << "\n"; } } else { Message *m = (Message *)buf; if (m->action == BROADCAST) { std::cout << "received broadcast from client\n";
ServerMessage response; response.action = BROADCAST; response.serverName = thisServerName;
sendto(server_socket_d, (const char *)&response, sizeof (response), 0, (const sockaddr *)&(dest_addr), sizeof (dest_addr)); } else if (m->action == PLAYER_JOIN) { PlayerMessage *mp = (PlayerMessage *)buf; std::cout << "Player joined, Name: " << mp->id << "\n"; playersInServer++;
// Add the ship into the game Ship *ship = new Ship(Ship::SERVER, mp->id); ship->setPosition(mp->px, mp->py); ship->direction = mp->r; ship->setVelocity(mp->vx, mp->vy); model.addActor(ship);
NamedSocket newSock; newSock.id = mp->id; newSock.socket = dest_addr;
client_addr.push_back(newSock);
RoomMessage roomUpdate; roomUpdate.action = ROOM_UPDATE; roomUpdate.numPlayers = playersInServer; int i = 0; for each (Obstacle *b in model.obstacles) { roomUpdate.x[i] = b->x1; roomUpdate.y[i] = b->y1; roomUpdate.r[i] = b->radius; i++; }
sendto(server_socket_d, (const char *)&roomUpdate, sizeof (roomUpdate), 0, (const sockaddr *)&(dest_addr), sizeof (dest_addr));
std::vector <Actor *> actors = model.getActors(); for (unsigned int i = 0; i < actors.size();) { if (actors[i]->getType() == Actor::SHIP) { Ship *ship = (Ship *)actors[i];
PlayerMessage playerUpdate; playerUpdate.action = PLAYER_JOIN; playerUpdate.id = ship->name; ship->getPosition(playerUpdate.px, playerUpdate.py); ship->getVelocity(playerUpdate.vx, playerUpdate.vy); playerUpdate.r = ship->direction; playerUpdate.fired = ship->controlfire;
sendto(server_socket_d, (const char *)&playerUpdate, sizeof (playerUpdate), 0, (const sockaddr *)&(dest_addr), sizeof (dest_addr)); } i++; }
for each (NamedSocket addr in client_addr) { PlayerMessage playerUpdate; playerUpdate.action = PLAYER_JOIN; playerUpdate.numPlayers = playersInServer; playerUpdate.id = ship->name; ship->getPosition(playerUpdate.px, playerUpdate.py); ship->getVelocity(playerUpdate.vx, playerUpdate.vy); playerUpdate.r = ship->direction; playerUpdate.fired = ship->controlfire;
sendto(server_socket_d, (const char *)&playerUpdate, sizeof (playerUpdate), 0, (const sockaddr *)&(addr.socket), sizeof (addr.socket)); } } else if (m->action == PLAYER_LEAVE) { PlayerMessage *mp = (PlayerMessage *)buf; std::cout << "Player left, Name: " << mp->id << "\n";
// Tell all clients about leave PlayerMessage playerUpdate; playerUpdate.action = PLAYER_LEAVE; playerUpdate.numPlayers = playersInServer; playerUpdate.id = mp->id;
int i = 0; for each (NamedSocket addr in client_addr) { sendto(server_socket_d, (const char *)&playerUpdate, sizeof (playerUpdate), 0, (const sockaddr *)&(addr.socket), sizeof (addr.socket));
// remove the socket from the array if (addr.id == mp->id) { client_addr.erase(client_addr.begin() + i); break; }
i++; }
// remove the ship from the game std::vector <Actor *> actors = model.getActors(); for (unsigned int i = 0; i < actors.size();) { if (actors[i]->getType() == Actor::SHIP) { Ship *ship = (Ship *)actors[i]; if (ship->name == mp->id) { ship->inRoom = false; playersInServer--; } } i++; } } // Receive updates from players else if (m->action == PLAYER_UPDATE) { PlayerMessage *mp = (PlayerMessage *)buf; std::vector <Actor *> actors = model.getActors(); for (unsigned int i = 0; i < actors.size();) { if (actors[i]->getType() == Actor::SHIP) { Ship *ship = (Ship *)actors[i]; if (ship->name == mp->id) { ship->setPosition(mp->px, mp->py); ship->setVelocity(mp->vx, mp->vy); ship->direction = mp->r; ship->controlfire = mp->fired; } } i++; } } }
if (servTimer.interval() > tickRate) { servTimer.mark(); for each (NamedSocket addr in client_addr) { std::vector <Actor *> actors = model.getActors(); for (unsigned int i = 0; i < actors.size();) { if (actors[i]->getType() == Actor::SHIP) { Ship *ship = (Ship *)actors[i];
PlayerMessage playerUpdate; playerUpdate.action = PLAYER_UPDATE; playerUpdate.numPlayers = playersInServer; playerUpdate.id = ship->name; ship->getPosition(playerUpdate.px, playerUpdate.py); ship->getVelocity(playerUpdate.vx, playerUpdate.vy); playerUpdate.r = ship->direction; playerUpdate.fired = ship->controlfire;
sendto(server_socket_d, (const char *)&playerUpdate, sizeof (playerUpdate), 0, (const sockaddr *)&(addr.socket), sizeof (addr.socket)); } i++; } }
std::vector <Actor *> actors = model.getActors(); for (unsigned int i = 0; i < actors.size();) { if (actors[i]->getType() == Actor::SHIP) { Ship *ship = (Ship *)actors[i];
// Turn off the fire control after sending to all players ship->controlfire = 0.0; } i++; }
} }
return 0; }
int main(int argc, char * argv[]) {
WSADATA wsaData; int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (argc > 2) { if ((std::string) argv[1] == "server") { // run server server(argc, argv); } else { // run client with args client(argc, argv); } } else { // run client client(argc, argv); }
WSACleanup(); return 0; }