Multiplayer framework in place (can now connect multiple clients and view them in game)

This commit is contained in:
xethm55 2012-09-24 20:31:51 -04:00
parent 3e178b4d97
commit abd731f3ca
9 changed files with 308 additions and 146 deletions

View File

@ -258,17 +258,22 @@ void Client::redraw()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
double dir_x = cos(m_player->direction);
double dir_y = sin(m_player->direction);
double dir_x = cos((*m_players)[current_player].direction);
double dir_y = sin((*m_players)[current_player].direction);
m_modelview.load_identity();
m_modelview.look_at(
m_player->x - dir_x * 25, m_player->y - dir_y * 25, 30,
m_player->x, m_player->y, 20,
(*m_players)[current_player].x - dir_x * 25, (*m_players)[current_player].y - dir_y * 25, 30,
(*m_players)[current_player].x, (*m_players)[current_player].y, 20,
0, 0, 1);
// TODO: call draw_player() for each networked player
draw_player(m_player);
draw_map();
for(std::map<sf::Uint8, Player>::iterator piter = m_players->begin(); piter != m_players->end(); piter++)
{
draw_player(piter->second);
}
draw_map();
draw_sky();
draw_lava();
@ -277,12 +282,12 @@ void Client::redraw()
m_window->display();
}
void Client::draw_player(refptr<Player> player)
void Client::draw_player(Player player)
{
m_obj_program.use();
m_modelview.push();
m_modelview.translate(player->x, player->y, 4);
m_modelview.rotate(player->direction * 180.0 / M_PI, 0, 0, 1);
m_modelview.translate(player.x, player.y, 4);
m_modelview.rotate(player.direction * 180.0 / M_PI, 0, 0, 1);
m_modelview.scale(2, 2, 2);
m_tank_obj.bindBuffers();
glEnableVertexAttribArray(0);
@ -398,8 +403,8 @@ void Client::draw_overlay()
proj.ortho(-span, span, -span, span, -1, 1);
proj.to_uniform(m_overlay_program.uniform("projection"));
GLMatrix modelview;
modelview.rotate(90 - m_player->direction * 180 / M_PI, 0, 0, 1);
modelview.translate(-m_player->x, -m_player->y, 0);
modelview.rotate(90 - (*m_players)[current_player].direction * 180 / M_PI, 0, 0, 1);
modelview.translate(-(*m_players)[current_player].x, -(*m_players)[current_player].y, 0);
m_overlay_hex_attributes.bind();
m_overlay_hex_indices.bind();
glEnableVertexAttribArray(0);
@ -452,8 +457,8 @@ void Client::draw_sky()
m_sky_attributes.bind();
m_projection.to_uniform(m_sky_program.uniform("projection"));
m_modelview.push();
m_modelview.translate(m_player->x, m_player->y, 0);
m_modelview.rotate(m_player->direction * 180.0 / M_PI, 0, 0, 1);
m_modelview.translate((*m_players)[current_player].x, (*m_players)[current_player].y, 0);
m_modelview.rotate((*m_players)[current_player].direction * 180.0 / M_PI, 0, 0, 1);
m_modelview.to_uniform(m_sky_program.uniform("modelview"));
m_modelview.pop();
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,

View File

@ -7,6 +7,9 @@ Client::Client()
m_net_client = new Network();
m_net_client->Create(59243, "127.0.0.1"); // Just connect to local host for now - testing
client_has_focus = true;
m_players = new std::map<sf::Uint8, Player>;
m_players->clear();
current_player = 0;
}
Client::~Client()
@ -15,16 +18,15 @@ Client::~Client()
}
void Client::run(bool fullscreen, int width, int height)
void Client::run(bool fullscreen, int width, int height, std::string pname)
{
current_player_name = pname;
if (!create_window(fullscreen, width, height))
return;
m_player = new Player();
m_player->x = 0;
m_player->y = 0;
m_player->direction = M_PI_2;
m_clock.restart();
sf::Mouse::setPosition(sf::Vector2i(m_width / 2, m_height / 2), *m_window);
double last_time = 0.0;
while (m_window->isOpen())
{
@ -64,8 +66,11 @@ void Client::run(bool fullscreen, int width, int height)
}
update(elapsed_time);
redraw();
last_time = current_time;
if(m_players->size() > 0)
{
redraw();
}
last_time = current_time;
// temporary for now. otherwise this thread consumed way too processing
sf::sleep(sf::seconds(0.005)); // 5 milli-seconds
@ -74,81 +79,136 @@ void Client::run(bool fullscreen, int width, int height)
void Client::update(double elapsed_time)
{
static sf::Uint8 w_pressed_prev = KEY_NOT_PRESSED;
static sf::Uint8 a_pressed_prev = KEY_NOT_PRESSED;
static sf::Uint8 s_pressed_prev = KEY_NOT_PRESSED;
static sf::Uint8 d_pressed_prev = KEY_NOT_PRESSED;
static sf::Int32 rel_mouse_movement_prev = 0;
static bool registered_player = false;
sf::Packet client_packet;
m_net_client->Receive();
client_packet.clear();
// Handle all received data (only really want the latest)
while(m_net_client->getData(client_packet))
{
// Update player position as calculated from the server.
client_packet >> m_player->direction;
client_packet >> m_player->x;
client_packet >> m_player->y;
sf::Uint8 packet_type;
client_packet >> packet_type;
switch(packet_type)
{
case PLAYER_CONNECT:
{
Player p;
sf::Uint8 pindex;
std::string name = "";
client_packet >> pindex;
client_packet >> name;
if(name == current_player_name)
{
current_player = pindex;
}
p.name = name;
(*m_players)[pindex] = p;
break;
}
case PLAYER_UPDATE:
{
sf::Uint8 player_index;
// Update player position as calculated from the server.
client_packet >> player_index;
if(m_players->end() != m_players->find(player_index))
{
client_packet >> (*m_players)[player_index].direction;
client_packet >> (*m_players)[player_index].x;
client_packet >> (*m_players)[player_index].y;
}
break;
}
case PLAYER_DISCONNECT:
{
// This will remove the player once the disconnect algorithm is implemented
break;
}
case PLAYER_DEATH:
{
// This will set a death flag in the player struct.
break;
}
default :
{
// Eat the packet
break;
}
}
}
// For now, we are going to do a very crude shove data into
// packet from keyboard and mouse events.
// TODO: Clean this up and make it more robust
client_packet.clear();
if(m_players->size() > 0)
{
sf::Uint8 w_pressed = KEY_NOT_PRESSED;
sf::Uint8 a_pressed = KEY_NOT_PRESSED;
sf::Uint8 s_pressed = KEY_NOT_PRESSED;
sf::Uint8 d_pressed = KEY_NOT_PRESSED;
sf::Int32 rel_mouse_movement = 0;
sf::Uint8 w_pressed = KEY_NOT_PRESSED;
sf::Uint8 a_pressed = KEY_NOT_PRESSED;
sf::Uint8 s_pressed = KEY_NOT_PRESSED;
sf::Uint8 d_pressed = KEY_NOT_PRESSED;
sf::Int32 rel_mouse_movement = 0;
// This is a fix so that the mouse will not move outside the window and
// cause the user to click on another program.
// Note: Does not work well with fast movement.
if(client_has_focus)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
a_pressed = KEY_PRESSED;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
d_pressed = KEY_PRESSED;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
w_pressed = KEY_PRESSED;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
{
s_pressed = KEY_PRESSED;
}
rel_mouse_movement = sf::Mouse::getPosition(*m_window).x - m_width / 2;
sf::Mouse::setPosition(sf::Vector2i(m_width / 2, m_height / 2), *m_window);
}
// This is a fix so that the mouse will not move outside the window and
// cause the user to click on another program.
// Note: Does not work well with fast movement.
if(client_has_focus)
{
if (sf::Keyboard::isKeyPressed(sf::Keyboard::A))
{
a_pressed = KEY_PRESSED;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::D))
{
d_pressed = KEY_PRESSED;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::W))
{
w_pressed = KEY_PRESSED;
}
if (sf::Keyboard::isKeyPressed(sf::Keyboard::S))
{
s_pressed = KEY_PRESSED;
}
rel_mouse_movement = sf::Mouse::getPosition(*m_window).x - m_width / 2;
sf::Mouse::setPosition(sf::Vector2i(m_width / 2, m_height / 2), *m_window);
}
// Send an update to the server if something has changed
if(((*m_players)[current_player].w_pressed != w_pressed) ||
((*m_players)[current_player].a_pressed != a_pressed) ||
((*m_players)[current_player].s_pressed != s_pressed) ||
((*m_players)[current_player].d_pressed != d_pressed) ||
((*m_players)[current_player].rel_mouse_movement != rel_mouse_movement))
{
sf::Uint8 packet_type = PLAYER_UPDATE;
client_packet.clear();
client_packet << packet_type;
client_packet << current_player;
client_packet << w_pressed;
client_packet << a_pressed;
client_packet << s_pressed;
client_packet << d_pressed;
client_packet << rel_mouse_movement;
// Send an update to the server if something has changed
if((w_pressed_prev != w_pressed) ||
(a_pressed_prev != a_pressed) ||
(s_pressed_prev != s_pressed) ||
(d_pressed_prev != d_pressed) ||
(rel_mouse_movement_prev != rel_mouse_movement))
{
client_packet << w_pressed;
client_packet << a_pressed;
client_packet << s_pressed;
client_packet << d_pressed;
client_packet << rel_mouse_movement;
m_net_client->sendData(client_packet);
m_net_client->sendData(client_packet);
w_pressed_prev = w_pressed;
a_pressed_prev = a_pressed;
s_pressed_prev = s_pressed;
d_pressed_prev = d_pressed;
rel_mouse_movement_prev = rel_mouse_movement;
}
(*m_players)[current_player].w_pressed = w_pressed;
(*m_players)[current_player].a_pressed = a_pressed;
(*m_players)[current_player].s_pressed = s_pressed;
(*m_players)[current_player].d_pressed = d_pressed;
(*m_players)[current_player].rel_mouse_movement = rel_mouse_movement;
}
}
else if(!registered_player)
{
sf::Uint8 packet_type = PLAYER_CONNECT;
client_packet.clear();
client_packet << packet_type;
client_packet << current_player;
client_packet << current_player_name;
m_net_client->sendData(client_packet);
registered_player = true;
}
m_net_client->Transmit();
}

View File

@ -18,14 +18,14 @@ class Client
public:
Client();
~Client();
void run(bool fullscreen, int width, int height);
void run(bool fullscreen, int width, int height, std::string pname);
protected:
bool create_window(bool fullscreen, int width, int height);
bool initgl();
void resize_window(int width, int height);
void update(double elapsed_time);
void redraw();
void draw_player(refptr<Player> player);
void draw_player(Player player);
void draw_map();
void draw_overlay();
void draw_sky();
@ -34,7 +34,9 @@ class Client
refptr<sf::Window> m_window;
sf::Clock m_clock;
Map m_map;
refptr<Player> m_player;
sf::Uint8 current_player;
std::string current_player_name;
refptr< std::map<sf::Uint8, Player> > m_players;
int m_width;
int m_height;
GLProgram m_obj_program;

View File

@ -8,12 +8,14 @@ int main(int argc, char *argv[])
bool fullscreen = false;
int width = 1200;
int height = 900;
std::string player_name = "Player";
struct option longopts[] = {
{"fullscreen", no_argument, NULL, 'f'},
{"height", required_argument, NULL, 'h'},
{"width", required_argument, NULL, 'w'},
{NULL, 0, NULL, 0}
{"name", required_argument, NULL, 'n'},
{NULL, 0, NULL, 0}
};
for (;;)
{
@ -31,12 +33,15 @@ int main(int argc, char *argv[])
case 'w':
width = atoi(optarg);
break;
case 'n':
player_name = std::string(optarg);
break;
}
}
Client client;
client.run(fullscreen, width, height);
client.run(fullscreen, width, height, player_name);
return 0;
}

View File

@ -1,10 +1,18 @@
#include "Player.h"
#include <math.h>
Player::Player()
{
x = 0.0;
y = 0.0;
direction = 0.0;
direction = M_PI_2;
hover = 1.0;
name = "";
w_pressed = KEY_NOT_PRESSED;
a_pressed = KEY_NOT_PRESSED;
s_pressed = KEY_NOT_PRESSED;
d_pressed = KEY_NOT_PRESSED;
rel_mouse_movement = 0;
updated = false;
}

View File

@ -3,6 +3,8 @@
#define PLAYER_H
#include <string>
#include "Types.h"
#include "SFML/Config.hpp"
class Player
{
@ -12,6 +14,12 @@ class Player
double y;
double direction; /* 0 = East, M_PI_2 = North, M_PI = West, ... */
double hover;
sf::Uint8 w_pressed;
sf::Uint8 a_pressed;
sf::Uint8 s_pressed;
sf::Uint8 d_pressed;
sf::Int32 rel_mouse_movement;
bool updated;
Player();
};

View File

@ -4,6 +4,9 @@
#define KEY_PRESSED 0xA5u
#define KEY_NOT_PRESSED 0x5Au
#define PLAYER_CONNECT 0x1Au
#define PLAYER_DISCONNECT 0x2Bu
#define PLAYER_DEATH 0x3Cu
#define PLAYER_UPDATE 0x4Du
#endif

View File

@ -6,10 +6,8 @@ Server::Server(sf::Uint16 port)
{
m_net_server = new Network();
m_net_server->Create(port, sf::IpAddress::None);
m_player = new Player();
m_player->x = 0;
m_player->y = 0;
m_player->direction = M_PI_2;
m_players = new std::map<sf::Uint8, Player>;
m_players->clear();
}
Server::~Server()
@ -40,65 +38,137 @@ void Server::update( double elapsed_time )
static Player player_prev;
const double move_speed = 75.0;
sf::Packet server_packet;
static sf::Uint8 w_pressed = KEY_NOT_PRESSED;
static sf::Uint8 a_pressed = KEY_NOT_PRESSED;
static sf::Uint8 s_pressed = KEY_NOT_PRESSED;
static sf::Uint8 d_pressed = KEY_NOT_PRESSED;
static sf::Int32 rel_mouse_movement = 0;
m_net_server->Receive();
// Handle all received data (only really want the latest)
while(m_net_server->getData(server_packet))
{
server_packet >> w_pressed;
server_packet >> a_pressed;
server_packet >> s_pressed;
server_packet >> d_pressed;
server_packet >> rel_mouse_movement;
sf::Uint8 ptype;
// Get packet type
server_packet >> ptype;
switch(ptype)
{
case PLAYER_CONNECT:
{
Player p;
std::string pname;
sf::Uint8 pindex;
server_packet >> pindex;
server_packet >> pname;
// When a player connects, we need to associate
// that player with a new ID. find first unused id
// player zero means a player does not exist.
if(pindex == 0)
{
for(pindex = 1u; pindex < 255u; pindex++ )
{
if(m_players->end() == m_players->find(pindex))
{
break;
}
}
p.name = pname;
(*m_players)[pindex] = p;
// Send a response to the newly added player
server_packet.clear();
server_packet << ptype;
server_packet << pindex;
server_packet << p.name;
m_net_server->sendData(server_packet, true);
}
break;
}
case PLAYER_UPDATE:
{
// Need to determine the correct player id
// then update the stored contents.
sf::Uint8 pindex;
server_packet >> pindex;
if(m_players->end() != m_players->find(pindex))
{
server_packet >> (*m_players)[pindex].w_pressed;
server_packet >> (*m_players)[pindex].a_pressed;
server_packet >> (*m_players)[pindex].s_pressed;
server_packet >> (*m_players)[pindex].d_pressed;
server_packet >> (*m_players)[pindex].rel_mouse_movement;
}
break;
}
case PLAYER_DISCONNECT:
{
// This completely removes the player from the game
// Deletes member from the player list
break;
}
case PLAYER_DEATH:
{
// This just forces the player to dissapper from the
// playing field.
break;
}
default:
{
// Just eat the packet
break;
}
}
}
if (KEY_PRESSED == a_pressed)
{
double direction = m_player->direction + M_PI_2;
m_player->x += cos(direction) * move_speed * elapsed_time;
m_player->y += sin(direction) * move_speed * elapsed_time;
}
if (KEY_PRESSED == d_pressed)
{
double direction = m_player->direction - M_PI_2;
m_player->x += cos(direction) * move_speed * elapsed_time;
m_player->y += sin(direction) * move_speed * elapsed_time;
}
if (KEY_PRESSED == w_pressed)
{
double direction = m_player->direction;
m_player->x += cos(direction) * move_speed * elapsed_time;
m_player->y += sin(direction) * move_speed * elapsed_time;
}
if (KEY_PRESSED == s_pressed)
{
double direction = m_player->direction + M_PI;
m_player->x += cos(direction) * move_speed * elapsed_time;
m_player->y += sin(direction) * move_speed * elapsed_time;
}
m_player->direction -= M_PI * 0.5 * rel_mouse_movement / 1000;
for(std::map<sf::Uint8, Player>::iterator piter = m_players->begin(); piter != m_players->end(); piter++)
{
sf::Uint8 pindex = piter->first;
double direction = (*m_players)[pindex].direction;
direction -= M_PI * 0.5 * (*m_players)[pindex].rel_mouse_movement / 1000;
if(direction != (*m_players)[pindex].direction)
{
(*m_players)[pindex].direction = direction;
(*m_players)[pindex].updated = true;
}
if (KEY_PRESSED == (*m_players)[pindex].a_pressed)
{
direction = (*m_players)[pindex].direction + M_PI_2;
(*m_players)[pindex].x += cos(direction) * move_speed * elapsed_time;
(*m_players)[pindex].y += sin(direction) * move_speed * elapsed_time;
(*m_players)[pindex].updated = true;
}
if (KEY_PRESSED == (*m_players)[pindex].d_pressed)
{
direction = (*m_players)[pindex].direction - M_PI_2;
(*m_players)[pindex].x += cos(direction) * move_speed * elapsed_time;
(*m_players)[pindex].y += sin(direction) * move_speed * elapsed_time;
(*m_players)[pindex].updated = true;
}
if (KEY_PRESSED == (*m_players)[pindex].w_pressed)
{
direction = (*m_players)[pindex].direction;
(*m_players)[pindex].x += cos(direction) * move_speed * elapsed_time;
(*m_players)[pindex].y += sin(direction) * move_speed * elapsed_time;
(*m_players)[pindex].updated = true;
}
if (KEY_PRESSED == (*m_players)[pindex].s_pressed)
{
direction = (*m_players)[pindex].direction + M_PI;
(*m_players)[pindex].x += cos(direction) * move_speed * elapsed_time;
(*m_players)[pindex].y += sin(direction) * move_speed * elapsed_time;
(*m_players)[pindex].updated = true;
}
server_packet.clear();
// Send the player update if there were changes
if((player_prev.direction != m_player->direction) ||
(player_prev.x != m_player->x) ||
(player_prev.y != m_player->y))
{
server_packet << m_player->direction;
server_packet << m_player->x;
server_packet << m_player->y;
m_net_server->sendData(server_packet);
player_prev.direction = m_player->direction;
player_prev.x = m_player->x;
player_prev.y = m_player->y;
}
server_packet.clear();
// Send the player update if there were changes
if((*m_players)[pindex].updated)
{
sf::Uint8 ptype = PLAYER_UPDATE;
server_packet << ptype;
server_packet << pindex;
server_packet << (*m_players)[pindex].direction;
server_packet << (*m_players)[pindex].x;
server_packet << (*m_players)[pindex].y;
m_net_server->sendData(server_packet);
(*m_players)[pindex].updated = false;
}
}
m_net_server->Transmit();
}

View File

@ -1,6 +1,7 @@
#include "Network.h"
#include "Player.h"
#include "refptr.h"
#include "SFML/Config.hpp"
class Server{
public:
@ -11,6 +12,6 @@ class Server{
protected:
void update(double elapsed_time);
refptr<Network> m_net_server;
refptr<Player> m_player;
refptr< std::map<sf::Uint8, Player> > m_players;
sf::Clock m_clock;
};