added basic multiplayer capabilities
This commit is contained in:
parent
35f11b1cee
commit
8a8b84b4da
13 changed files with 641 additions and 27 deletions
4
Makefile
4
Makefile
|
@ -5,10 +5,10 @@ ifndef CXXFLAGS
|
|||
CXXFLAGS=-g -O2 -Wall
|
||||
endif
|
||||
SDL_CFLAGS=`sdl-config --cflags` `freetype-config --cflags`
|
||||
SDL_LIBS=`sdl-config --libs` `freetype-config --libs` -lSDL_mixer -lSDL_ttf -lSDL_image
|
||||
SDL_LIBS=`sdl-config --libs` `freetype-config --libs` -lSDL_mixer -lSDL_ttf -lSDL_image -lSDL_net
|
||||
LIBS=${SDL_LIBS} -lstdc++
|
||||
INCLUDES=-I. -Isrc -Isrc/tools -Isrc/widgets
|
||||
OBJS=src/actions.o src/ai.o src/ai_attack.o src/ai_move.o src/config.o src/dialogs.o src/display.o src/filesystem.o src/font.o src/game.o src/game_config.o src/game_events.o src/gamestatus.o src/hotkeys.o src/intro.o src/key.o src/language.o src/log.o src/map.o src/menu.o src/multiplayer.o src/pathfind.o src/playlevel.o src/playturn.o src/preferences.o src/replay.o src/sdl_utils.o src/sound.o src/team.o src/terrain.o src/tooltips.o src/unit.o src/unit_types.o src/video.o src/widgets/button.o src/widgets/slider.o src/widgets/textbox.o
|
||||
OBJS=src/actions.o src/ai.o src/ai_attack.o src/ai_move.o src/config.o src/dialogs.o src/display.o src/filesystem.o src/font.o src/game.o src/game_config.o src/game_events.o src/gamestatus.o src/hotkeys.o src/intro.o src/key.o src/language.o src/log.o src/map.o src/menu.o src/multiplayer.o src/network.o src/pathfind.o src/playlevel.o src/playturn.o src/preferences.o src/replay.o src/sdl_utils.o src/sound.o src/team.o src/terrain.o src/tooltips.o src/unit.o src/unit_types.o src/video.o src/widgets/button.o src/widgets/slider.o src/widgets/textbox.o
|
||||
|
||||
MAKE_TRANS_OBJS=src/tools/make_translation.o src/config.o src/filesystem.o src/log.o
|
||||
MERGE_TRANS_OBJS=src/tools/merge_translations.o src/config.o src/filesystem.o src/log.o
|
||||
|
|
|
@ -124,6 +124,13 @@ start_game="Start Game!"
|
|||
|
||||
human_controlled="Human"
|
||||
ai_controlled="AI"
|
||||
network_controlled="Network Player"
|
||||
remote_host="Choose host to connect to"
|
||||
connection_failed="Could not connect to the remote host"
|
||||
connection_timeout="Connection timed out"
|
||||
|
||||
host_game="Host Multiplayer Game"
|
||||
join_game="Join Networked Multiplayer Game"
|
||||
|
||||
quit_message="Do you really want to quit?"
|
||||
|
||||
|
|
16
src/game.cpp
16
src/game.cpp
|
@ -26,6 +26,7 @@
|
|||
#include "language.hpp"
|
||||
#include "menu.hpp"
|
||||
#include "multiplayer.hpp"
|
||||
#include "network.hpp"
|
||||
#include "pathfind.hpp"
|
||||
#include "playlevel.hpp"
|
||||
#include "preferences.hpp"
|
||||
|
@ -364,8 +365,19 @@ int play_game(int argc, char** argv)
|
|||
state.campaign_type = "multiplayer";
|
||||
state.scenario = 0;
|
||||
|
||||
std::vector<std::string> host_or_join;
|
||||
host_or_join.push_back(string_table["host_game"]);
|
||||
host_or_join.push_back(string_table["join_game"]);
|
||||
|
||||
const int res = gui::show_dialog(disp,NULL,"","",gui::MESSAGE,
|
||||
&host_or_join);
|
||||
|
||||
try {
|
||||
play_multiplayer(disp,units_data,game_config,state);
|
||||
if(res == 0) {
|
||||
play_multiplayer(disp,units_data,game_config,state);
|
||||
} else if(res == 1) {
|
||||
play_multiplayer_client(disp,units_data,game_config,state);
|
||||
}
|
||||
} catch(gamestatus::load_game_failed& e) {
|
||||
std::cerr << "error loading the game: " << e.message
|
||||
<< "\n";
|
||||
|
@ -374,6 +386,8 @@ int play_game(int argc, char** argv)
|
|||
std::cerr << "error while playing the game: "
|
||||
<< e.message << "\n";
|
||||
return 0;
|
||||
} catch(network::error& e) {
|
||||
gui::show_dialog(disp,NULL,"",e.message,gui::OK_ONLY);
|
||||
}
|
||||
|
||||
continue;
|
||||
|
|
|
@ -10,9 +10,12 @@
|
|||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "language.hpp"
|
||||
#include "log.hpp"
|
||||
#include "menu.hpp"
|
||||
#include "multiplayer.hpp"
|
||||
#include "network.hpp"
|
||||
#include "playlevel.hpp"
|
||||
#include "replay.hpp"
|
||||
|
||||
|
@ -21,6 +24,203 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
bool accept_network_connections(config& players)
|
||||
{
|
||||
log_scope("accept network connections");
|
||||
typedef std::map<config*,network::connection> positions_map;
|
||||
positions_map positions;
|
||||
|
||||
std::vector<config*>& sides = players.children["side"];
|
||||
for(std::vector<config*>::const_iterator i = sides.begin();
|
||||
i != sides.end(); ++i) {
|
||||
if((*i)->values["controller"] == "network") {
|
||||
positions[*i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if(positions.empty())
|
||||
return true;
|
||||
|
||||
std::cerr << "waiting for connections...\n";
|
||||
for(;;) {
|
||||
network::connection sock = network::accept_connection();
|
||||
if(sock) {
|
||||
std::cerr << "Received connection\n";
|
||||
network::send_data(players,sock);
|
||||
}
|
||||
|
||||
config cfg;
|
||||
sock = network::receive_data(cfg);
|
||||
if(sock) {
|
||||
const int side_taken = atoi(cfg.values["side"].c_str())-1;
|
||||
if(side_taken >= 0 && side_taken < int(sides.size())) {
|
||||
positions_map::iterator pos = positions.find(sides[side_taken]);
|
||||
if(pos != positions.end()) {
|
||||
if(!pos->second) {
|
||||
std::cerr << "client has taken a valid position\n";
|
||||
|
||||
//broadcast to everyone the new game status
|
||||
pos->first->values["taken"] = "yes";
|
||||
positions[sides[side_taken]] = sock;
|
||||
network::send_data(players);
|
||||
|
||||
std::cerr << "sent player data\n";
|
||||
|
||||
//send a reply telling the client they have secured
|
||||
//the side they asked for
|
||||
std::stringstream side;
|
||||
side << (side_taken+1);
|
||||
config reply;
|
||||
reply.values["side_secured"] = side.str();
|
||||
std::cerr << "going to send data...\n";
|
||||
network::send_data(reply,sock);
|
||||
|
||||
//see if all positions are now filled
|
||||
bool unclaimed = false;
|
||||
for(positions_map::const_iterator p = positions.begin();
|
||||
p != positions.end(); ++p) {
|
||||
if(!p->second) {
|
||||
unclaimed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!unclaimed) {
|
||||
std::cerr << "starting game now...\n";
|
||||
config start_game;
|
||||
start_game.children["start_game"].
|
||||
push_back(new config());
|
||||
network::send_data(start_game);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
config response;
|
||||
response.values["failed"] = "yes";
|
||||
network::send_data(response,sock);
|
||||
}
|
||||
} else {
|
||||
std::cerr << "tried to take illegal side: " << side_taken
|
||||
<< "\n";
|
||||
}
|
||||
} else {
|
||||
std::cerr << "tried to take unknown side: " << side_taken
|
||||
<< "\n";
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "pump\n";
|
||||
pump_events();
|
||||
SDL_Delay(50);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void play_multiplayer_client(display& disp, game_data& units_data, config& cfg,
|
||||
game_state& state)
|
||||
{
|
||||
const network::manager net_manager;
|
||||
|
||||
std::string host;
|
||||
const int res = gui::show_dialog(disp,NULL,"","",
|
||||
gui::OK_CANCEL,NULL,NULL,
|
||||
string_table["remote_host"] + ": ",&host);
|
||||
if(res != 0 || host.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
network::connection sock;
|
||||
|
||||
sock = network::connect(host);
|
||||
config sides;
|
||||
|
||||
network::connection data_res = network::receive_data(sides,0,3000);
|
||||
|
||||
if(!data_res) {
|
||||
throw network::error(string_table["connection_timeout"]);
|
||||
}
|
||||
|
||||
std::map<int,int> choice_map;
|
||||
std::vector<std::string> choices;
|
||||
|
||||
std::vector<config*>& sides_list = sides.children["side"];
|
||||
for(std::vector<config*>::iterator s = sides_list.begin();
|
||||
s != sides_list.end(); ++s) {
|
||||
if((*s)->values["controller"] == "network" &&
|
||||
(*s)->values["taken"] != "yes") {
|
||||
choice_map[choices.size()] = 1 + s - sides_list.begin();
|
||||
choices.push_back((*s)->values["name"] + " - " +
|
||||
(*s)->values["type"]);
|
||||
}
|
||||
}
|
||||
|
||||
const int choice = gui::show_dialog(disp,NULL,"","Choose side:",
|
||||
gui::OK_CANCEL,&choices);
|
||||
if(choice < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int team_num = choice_map[choice];
|
||||
|
||||
//send our choice of team to the server
|
||||
{
|
||||
config response;
|
||||
std::stringstream stream;
|
||||
stream << team_num;
|
||||
response.values["side"] = stream.str();
|
||||
network::send_data(response);
|
||||
}
|
||||
|
||||
bool got_side = false;
|
||||
|
||||
for(;;) {
|
||||
config reply;
|
||||
data_res = network::receive_data(reply,0,100);
|
||||
if(data_res) {
|
||||
if(reply.values["failed"] == "yes") {
|
||||
got_side = false;
|
||||
break;
|
||||
} else if(reply.values["side_secured"].size() > 0) {
|
||||
got_side = true;
|
||||
} else if(reply.children["start_game"].empty() == false) {
|
||||
break;
|
||||
} else {
|
||||
sides = reply;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!got_side) {
|
||||
throw network::error("Choice of team unavailable.");
|
||||
}
|
||||
|
||||
//we want to make the network/human players look right from our
|
||||
//perspective
|
||||
{
|
||||
std::vector<config*>& sides_list = sides.children["side"];
|
||||
for(std::vector<config*>::iterator side = sides_list.begin();
|
||||
side != sides_list.end(); ++side) {
|
||||
string_map& values = (*side)->values;
|
||||
if(team_num-1 == side - sides_list.begin())
|
||||
values["controller"] = "human";
|
||||
else
|
||||
values["controller"] = "network";
|
||||
}
|
||||
}
|
||||
|
||||
std::cerr << "starting game\n";
|
||||
|
||||
state.starting_pos = sides;
|
||||
|
||||
recorder.set_save_info(state);
|
||||
|
||||
std::vector<config*> story;
|
||||
play_level(units_data,cfg,&sides,disp.video(),state,story);
|
||||
recorder.clear();
|
||||
}
|
||||
|
||||
void play_multiplayer(display& disp, game_data& units_data, config& cfg,
|
||||
game_state& state)
|
||||
{
|
||||
|
@ -66,10 +266,16 @@ void play_multiplayer(display& disp, game_data& units_data, config& cfg,
|
|||
sd != sides.end(); ++sd) {
|
||||
std::stringstream details;
|
||||
details << (*sd)->values["side"] << ","
|
||||
<< (*sd)->values["name"] << ","
|
||||
<< ((*sd)->values["controller"] == "human" ?
|
||||
string_table["human_controlled"] :
|
||||
string_table["ai_controlled"]);
|
||||
<< (*sd)->values["name"] << ",";
|
||||
|
||||
const std::string& controller = (*sd)->values["controller"];
|
||||
if(controller == "human")
|
||||
details << string_table["human_controlled"];
|
||||
else if(controller == "network")
|
||||
details << string_table["network_controlled"];
|
||||
else
|
||||
details << string_table["ai_controlled"];
|
||||
|
||||
sides_list.push_back(details.str());
|
||||
}
|
||||
|
||||
|
@ -81,13 +287,21 @@ void play_multiplayer(display& disp, game_data& units_data, config& cfg,
|
|||
if(size_t(res) < sides.size()) {
|
||||
std::vector<std::string> choices;
|
||||
|
||||
for(int n = 0; n != 2; ++n) {
|
||||
for(int n = 0; n != 3; ++n) {
|
||||
for(std::vector<config*>::iterator i = possible_sides.begin();
|
||||
i != possible_sides.end(); ++i) {
|
||||
std::stringstream choice;
|
||||
choice << (*i)->values["name"] << " - "
|
||||
<< (n == 0 ? string_table["human_controlled"] :
|
||||
string_table["ai_controlled"]);
|
||||
choice << (*i)->values["name"] << " - ";
|
||||
switch(n) {
|
||||
case 0: choice << string_table["human_controlled"];
|
||||
break;
|
||||
case 1: choice << string_table["ai_controlled"];
|
||||
break;
|
||||
case 2: choice << string_table["network_controlled"];
|
||||
break;
|
||||
default: assert(false);
|
||||
}
|
||||
|
||||
choices.push_back(choice.str());
|
||||
}
|
||||
}
|
||||
|
@ -96,10 +310,18 @@ void play_multiplayer(display& disp, game_data& units_data, config& cfg,
|
|||
string_table["choose_side"],
|
||||
gui::MESSAGE,&choices);
|
||||
if(result >= 0) {
|
||||
sides[res]->values["controller"] =
|
||||
(result >= int(choices.size())/2) ? "ai" : "human";
|
||||
if(result >= int(choices.size())/2)
|
||||
result -= choices.size()/2;
|
||||
std::string controller = "network";
|
||||
if(result < int(choices.size())/3) {
|
||||
controller = "human";
|
||||
} else if(result < int(choices.size()/3)*2) {
|
||||
controller = "ai";
|
||||
result -= choices.size()/3;
|
||||
} else {
|
||||
controller = "network";
|
||||
result -= (choices.size()/3)*2;
|
||||
}
|
||||
|
||||
sides[res]->values["controller"] = controller;
|
||||
|
||||
assert(result < int(possible_sides.size()));
|
||||
|
||||
|
@ -112,6 +334,13 @@ void play_multiplayer(display& disp, game_data& units_data, config& cfg,
|
|||
}
|
||||
}
|
||||
|
||||
const network::manager net_manager;
|
||||
const network::server_manager server_man;
|
||||
|
||||
const bool network_state = accept_network_connections(level);
|
||||
if(network_state == false)
|
||||
return;
|
||||
|
||||
state.starting_pos = level;
|
||||
|
||||
recorder.set_save_info(state);
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
#include "unit_types.hpp"
|
||||
#include "video.hpp"
|
||||
|
||||
void play_multiplayer_client(display& disp, game_data& units_data,
|
||||
config& cfg, game_state& state);
|
||||
|
||||
void play_multiplayer(display& disp, game_data& units_data,
|
||||
config& cfg, game_state& state);
|
||||
|
||||
|
|
230
src/network.cpp
Normal file
230
src/network.cpp
Normal file
|
@ -0,0 +1,230 @@
|
|||
#include "log.hpp"
|
||||
#include "network.hpp"
|
||||
|
||||
#include "SDL_net.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
SDLNet_SocketSet socket_set = SDLNet_AllocSocketSet(64);
|
||||
typedef std::vector<network::connection> sockets_list;
|
||||
sockets_list sockets;
|
||||
|
||||
std::map<network::connection,std::string> received_data;
|
||||
|
||||
TCPsocket server_socket;
|
||||
|
||||
}
|
||||
|
||||
namespace network {
|
||||
|
||||
manager::manager()
|
||||
{
|
||||
if(SDLNet_Init() == -1) {
|
||||
throw error(SDL_GetError());
|
||||
}
|
||||
}
|
||||
|
||||
manager::~manager()
|
||||
{
|
||||
disconnect();
|
||||
SDLNet_FreeSocketSet(socket_set);
|
||||
SDLNet_Quit();
|
||||
}
|
||||
|
||||
server_manager::server_manager(int port, bool create_server)
|
||||
{
|
||||
if(create_server) {
|
||||
server_socket = connect("",port);
|
||||
std::cerr << "server socket initialized: " << server_socket << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
server_manager::~server_manager()
|
||||
{
|
||||
if(server_socket) {
|
||||
SDLNet_TCP_Close(server_socket);
|
||||
server_socket = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t nconnections()
|
||||
{
|
||||
return sockets.size();
|
||||
}
|
||||
|
||||
connection connect(const std::string& host, int port)
|
||||
{
|
||||
char* const hostname = host.empty() ? NULL:const_cast<char*>(host.c_str());
|
||||
IPaddress ip;
|
||||
if(SDLNet_ResolveHost(&ip,hostname,port) == -1) {
|
||||
throw error(SDLNet_GetError());
|
||||
}
|
||||
|
||||
TCPsocket sock = SDLNet_TCP_Open(&ip);
|
||||
if(!sock) {
|
||||
throw error(SDLNet_GetError());
|
||||
}
|
||||
|
||||
//if this is not a server socket, then add it to the list
|
||||
//of sockets we listen to
|
||||
if(hostname != NULL) {
|
||||
const int res = SDLNet_TCP_AddSocket(socket_set,sock);
|
||||
if(res == -1) {
|
||||
SDLNet_TCP_Close(sock);
|
||||
throw network::error("Could not add socket to socket set");
|
||||
}
|
||||
|
||||
assert(sock != server_socket);
|
||||
sockets.push_back(sock);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
connection accept_connection()
|
||||
{
|
||||
assert(server_socket);
|
||||
const connection sock = SDLNet_TCP_Accept(server_socket);
|
||||
if(sock) {
|
||||
const int res = SDLNet_TCP_AddSocket(socket_set,sock);
|
||||
if(res == -1) {
|
||||
SDLNet_TCP_Close(sock);
|
||||
throw network::error("Could not add socket to socket set");
|
||||
}
|
||||
|
||||
assert(sock != server_socket);
|
||||
sockets.push_back(sock);
|
||||
std::cerr << "new socket: " << sock << "\n";
|
||||
std::cerr << "server socket: " << server_socket << "\n";
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
void disconnect(connection s)
|
||||
{
|
||||
if(!s) {
|
||||
while(sockets.empty() == false) {
|
||||
disconnect(sockets.back());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
received_data.erase(s);
|
||||
|
||||
const sockets_list::iterator i = std::find(sockets.begin(),sockets.end(),s);
|
||||
if(i != sockets.end()) {
|
||||
sockets.erase(i);
|
||||
SDLNet_TCP_DelSocket(socket_set,s);
|
||||
SDLNet_TCP_Close(s);
|
||||
}
|
||||
}
|
||||
|
||||
connection receive_data(config& cfg, connection connection_num, int timeout)
|
||||
{
|
||||
if(sockets.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int starting_ticks = SDL_GetTicks();
|
||||
|
||||
const int res = SDLNet_CheckSockets(socket_set,timeout);
|
||||
if(res <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(sockets_list::const_iterator i = sockets.begin();
|
||||
i != sockets.end(); ++i) {
|
||||
if(SDLNet_SocketReady(*i)) {
|
||||
std::string buffer;
|
||||
|
||||
for(;;) {
|
||||
char c;
|
||||
const int len = SDLNet_TCP_Recv(*i,&c,1);
|
||||
if(len == 0) {
|
||||
break;
|
||||
} else if(len < 0) {
|
||||
disconnect(*i);
|
||||
throw error(std::string("error receiving data: ") +
|
||||
SDLNet_GetError());
|
||||
}
|
||||
|
||||
buffer.resize(buffer.size()+1);
|
||||
buffer[buffer.size()-1] = c;
|
||||
|
||||
if(c == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(buffer == "") {
|
||||
disconnect(*i);
|
||||
throw error("error receiving data");
|
||||
}
|
||||
|
||||
std::cerr << "received: " << buffer << "\n";
|
||||
|
||||
if(buffer[buffer.size()-1] != 0) {
|
||||
received_data[*i] += buffer;
|
||||
const int ticks_taken = SDL_GetTicks() - starting_ticks;
|
||||
if(ticks_taken < timeout) {
|
||||
return receive_data(cfg,connection_num,timeout-ticks_taken);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
const std::map<connection,std::string>::iterator it =
|
||||
received_data.find(*i);
|
||||
if(it != received_data.end()) {
|
||||
buffer = it->second + buffer;
|
||||
received_data.erase(it);
|
||||
}
|
||||
|
||||
cfg.read(buffer);
|
||||
return *i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void send_data(config& cfg, connection connection_num)
|
||||
{
|
||||
log_scope("sending data");
|
||||
if(!connection_num) {
|
||||
std::cerr << "sockets: " << sockets.size() << "\n";
|
||||
for(sockets_list::const_iterator i = sockets.begin();
|
||||
i != sockets.end(); ++i) {
|
||||
std::cerr << "server socket: " << server_socket << "\n";
|
||||
std::cerr << "current socket: " << *i << "\n";
|
||||
assert(*i && *i != server_socket);
|
||||
send_data(cfg,*i);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::cerr << "a\n";
|
||||
|
||||
assert(connection_num != server_socket);
|
||||
|
||||
std::string value = cfg.write();
|
||||
std::cerr << "sending " << (value.size()+1) << " bytes\n";
|
||||
const int res = SDLNet_TCP_Send(connection_num,
|
||||
const_cast<char*>(value.c_str()),
|
||||
value.size()+1);
|
||||
|
||||
if(res < int(value.size()+1)) {
|
||||
std::cerr << "sending data failed: " << res << "/" << value.size() << ": " << SDL_GetError() << "\n";
|
||||
disconnect(connection_num);
|
||||
throw error("Could not send data over socket");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
42
src/network.hpp
Normal file
42
src/network.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef NETWORK_HPP_INCLUDED
|
||||
#define NETWORK_HPP_INCLUDED
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
#include "SDL_net.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace network {
|
||||
|
||||
struct manager {
|
||||
manager();
|
||||
~manager();
|
||||
};
|
||||
|
||||
struct server_manager {
|
||||
server_manager(int port=15000, bool create_server=true);
|
||||
~server_manager();
|
||||
};
|
||||
|
||||
typedef TCPsocket connection;
|
||||
|
||||
size_t nconnections();
|
||||
|
||||
connection connect(const std::string& host, int port=15000);
|
||||
connection accept_connection();
|
||||
void disconnect(connection connection_num=0);
|
||||
|
||||
connection receive_data(config& cfg, connection connection_num=0, int tout=0);
|
||||
|
||||
void send_data(config& cfg, connection connection_num=0);
|
||||
|
||||
struct error
|
||||
{
|
||||
error(const std::string& msg) : message(msg) {}
|
||||
std::string message;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -13,6 +13,7 @@
|
|||
#include "game_events.hpp"
|
||||
#include "intro.hpp"
|
||||
#include "language.hpp"
|
||||
#include "network.hpp"
|
||||
#include "playlevel.hpp"
|
||||
#include "playturn.hpp"
|
||||
#include "preferences.hpp"
|
||||
|
@ -162,22 +163,64 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config,
|
|||
dialogs::show_objectives(gui,*level);
|
||||
}
|
||||
|
||||
play_turn(gameinfo,state_of_game,status,terrain_config,
|
||||
level, video, key, gui, events_manager, map,
|
||||
teams, player_number, units);
|
||||
try {
|
||||
play_turn(gameinfo,state_of_game,status,terrain_config,
|
||||
level, video, key, gui, events_manager, map,
|
||||
teams, player_number, units);
|
||||
} catch(end_level_exception& e) {
|
||||
if(network::nconnections() > 0) {
|
||||
config cfg;
|
||||
cfg.children["turn"].push_back(
|
||||
new config(recorder.get_last_turn(1)));
|
||||
network::send_data(cfg);
|
||||
}
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
if(game_config::debug)
|
||||
display::clear_debug_highlights();
|
||||
|
||||
} else if(!replaying) {
|
||||
if(network::nconnections() > 0) {
|
||||
config cfg;
|
||||
cfg.children["turn"].push_back(
|
||||
new config(recorder.get_last_turn(2)));
|
||||
network::send_data(cfg);
|
||||
}
|
||||
|
||||
} else if(!replaying && team_it->is_ai()) {
|
||||
ai::do_move(gui,map,gameinfo,units,teams,
|
||||
player_number,status);
|
||||
|
||||
if(network::nconnections() > 0) {
|
||||
config cfg;
|
||||
cfg.children["turn"].push_back(
|
||||
new config(recorder.get_last_turn(2)));
|
||||
network::send_data(cfg);
|
||||
}
|
||||
|
||||
gui.invalidate_unit();
|
||||
gui.invalidate_game_status();
|
||||
gui.invalidate_all();
|
||||
gui.draw();
|
||||
SDL_Delay(1000);
|
||||
SDL_Delay(500);
|
||||
} else if(!replaying && team_it->is_network()) {
|
||||
config cfg;
|
||||
for(;;) {
|
||||
network::connection res=network::receive_data(cfg,0,50);
|
||||
if(res && cfg.children["turn"].empty() == false) {
|
||||
break;
|
||||
}
|
||||
|
||||
pump_events();
|
||||
}
|
||||
|
||||
std::cerr << "replay: '" << cfg.children["turn"].front()->write() << "'\n";
|
||||
replay replay_obj(*cfg.children["turn"].front());
|
||||
replay_obj.start_replay();
|
||||
do_replay(gui,map,gameinfo,units,teams,
|
||||
player_number,status,state_of_game,&replay_obj);
|
||||
|
||||
}
|
||||
|
||||
for(unit_map::iterator uit = units.begin();
|
||||
|
|
|
@ -181,6 +181,32 @@ void replay::end_turn()
|
|||
cmd->children["end_turn"].push_back(new config());
|
||||
}
|
||||
|
||||
config replay::get_last_turn(int num_turns)
|
||||
{
|
||||
log_scope("get_last_turn\n");
|
||||
//back up from the end, finding the position we're looking for
|
||||
std::vector<config*>& cmd = commands();
|
||||
std::vector<config*>::reverse_iterator i;
|
||||
for(i = cmd.rbegin(); i != cmd.rend() && num_turns > 0; ++i) {
|
||||
if((*i)->children["end_turn"].size() > 0) {
|
||||
--num_turns;
|
||||
}
|
||||
}
|
||||
|
||||
//if we reached an end turn, we have to move one step forward so
|
||||
//that we don't include the end turn
|
||||
const int adjust = num_turns == 0 ? 1 : 0;
|
||||
|
||||
config res;
|
||||
|
||||
for(std::vector<config*>::const_iterator fi = i.base()+adjust;
|
||||
fi != cmd.end(); ++fi) {
|
||||
res.children["command"].push_back(new config(**fi));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void replay::undo()
|
||||
{
|
||||
std::vector<config*>& cmd = commands();
|
||||
|
@ -263,8 +289,10 @@ bool replay::empty()
|
|||
bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
||||
std::map<gamemap::location,unit>& units,
|
||||
std::vector<team>& teams, int team_num, const gamestatus& state,
|
||||
game_state& state_of_game)
|
||||
game_state& state_of_game, replay* obj)
|
||||
{
|
||||
replay& recorder = obj != NULL ? *obj : recorder;
|
||||
|
||||
update_locker lock_update(disp,recorder.skipping());
|
||||
|
||||
//a list of units that have promoted from the last attack
|
||||
|
|
|
@ -42,6 +42,8 @@ public:
|
|||
void choose_option(int index);
|
||||
void end_turn();
|
||||
|
||||
config get_last_turn(int num_turns=1);
|
||||
|
||||
void undo();
|
||||
|
||||
int get_random();
|
||||
|
@ -80,6 +82,6 @@ extern replay recorder;
|
|||
bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
||||
std::map<gamemap::location,unit>& units,
|
||||
std::vector<team>& teams, int team_num, const gamestatus& state,
|
||||
game_state& state_of_game);
|
||||
game_state& state_of_game, replay* obj=NULL);
|
||||
|
||||
#endif
|
||||
|
|
18
src/team.cpp
18
src/team.cpp
|
@ -43,9 +43,11 @@ team::team_info::team_info(config& cfg)
|
|||
}
|
||||
|
||||
if(cfg.values["controller"] == "human")
|
||||
human = true;
|
||||
controller = HUMAN;
|
||||
else if(cfg.values["controller"] == "network")
|
||||
controller = NETWORK;
|
||||
else
|
||||
human = false;
|
||||
controller = AI;
|
||||
|
||||
const std::string& scouts_val = cfg.values["villages_per_scout"];
|
||||
if(scouts_val.empty()) {
|
||||
|
@ -171,7 +173,17 @@ double team::aggression() const
|
|||
|
||||
bool team::is_human() const
|
||||
{
|
||||
return info_.human;
|
||||
return info_.controller == team_info::HUMAN;
|
||||
}
|
||||
|
||||
bool team::is_ai() const
|
||||
{
|
||||
return info_.controller == team_info::AI;
|
||||
}
|
||||
|
||||
bool team::is_network() const
|
||||
{
|
||||
return info_.controller == team_info::NETWORK;
|
||||
}
|
||||
|
||||
double team::leader_value() const
|
||||
|
|
|
@ -40,7 +40,9 @@ public:
|
|||
std::vector<std::string> recruitment_pattern;
|
||||
double aggression;
|
||||
std::vector<int> enemies;
|
||||
bool human;
|
||||
|
||||
enum CONTROLLER { HUMAN, AI, NETWORK };
|
||||
CONTROLLER controller;
|
||||
|
||||
int villages_per_scout;
|
||||
double leader_value, village_value;
|
||||
|
@ -69,6 +71,8 @@ public:
|
|||
double aggression() const;
|
||||
|
||||
bool is_human() const;
|
||||
bool is_network() const;
|
||||
bool is_ai() const;
|
||||
|
||||
double leader_value() const;
|
||||
double village_value() const;
|
||||
|
|
|
@ -29,7 +29,7 @@ struct tooltip
|
|||
std::string message;
|
||||
};
|
||||
|
||||
static const int font_size = 12;
|
||||
static const int font_size = 14;
|
||||
|
||||
std::vector<tooltip> tips;
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue