1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Early changes towards MP support.

It's possible to connect several clients (running on localhost) and enter MP pregame. The actual MP game still not playable. (though it can be started)
This commit is contained in:
Michał W. Urbańczyk 2010-10-24 11:35:14 +00:00
parent aa131bbf15
commit daeb3af67a
24 changed files with 1694 additions and 616 deletions

View File

@ -26,8 +26,9 @@ struct PlayerSettings
si8 bonus; //usees enum type Ebonus
ui8 color; //from 0 -
ui8 handicap;//0-no, 1-mild, 2-severe
std::string name;
ui8 human;
ui8 human; //0 - AI, non-0 serves as player id
template <typename Handler> void serialize(Handler &h, const int version)
{
h & castle;
@ -51,15 +52,15 @@ struct PlayerSettings
struct StartInfo
{
enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN};
enum EMode {NEW_GAME, LOAD_GAME, CAMPAIGN, INVALID = 255};
ui8 mode; //0 - new game; 1 - load game; 2 - campaign
ui8 mode; //uses EMode enum
ui8 difficulty; //0=easy; 4=impossible
std::map<int, PlayerSettings> playerInfos; //color indexed
ui8 turnTime; //in minutes, 0=unlimited
std::string mapname;
ui8 whichMapInCampaign; //used only for mode 2
ui8 choosenCampaignBonus; //used only for mode 2
ui8 whichMapInCampaign; //used only for mode CAMPAIGN
ui8 choosenCampaignBonus; //used only for mode CAMPAIGN
PlayerSettings & getIthPlayersSettings(int no)
{
if(playerInfos.find(no) != playerInfos.end())
@ -68,10 +69,10 @@ struct StartInfo
throw std::string("Cannot find info about player");
}
PlayerSettings *getPlayersSettings(const std::string &name)
PlayerSettings *getPlayersSettings(const ui8 nameID)
{
for(std::map<int, PlayerSettings>::iterator it=playerInfos.begin(); it != playerInfos.end(); ++it)
if(it->second.name == name)
if(it->second.human == nameID)
return &it->second;
return NULL;
@ -87,6 +88,11 @@ struct StartInfo
h & whichMapInCampaign;
h & choosenCampaignBonus;
}
StartInfo()
{
mode = INVALID;
}
};

View File

@ -670,4 +670,9 @@ void CSlider::keyPressed(const SDL_KeyboardEvent & key)
}
moveTo(moveDest);
}
void CSlider::moveToMax()
{
moveTo(amount);
}

View File

@ -148,6 +148,7 @@ public:
CSlider(int x, int y, int totalw, boost::function<void(int)> Moved, int Capacity, int Amount,
int Value=0, bool Horizontal=true, int style = 0); //style 0 - brown, 1 - blue
~CSlider();
void moveToMax();
};
#endif // __ADVENTUREMAPBUTTON_H__

View File

@ -110,7 +110,7 @@ void init()
tlog0 << "\tInitializing minors: " << pomtime.getDif() << std::endl;
{
//read system options
CLoadFile settings(GVCMIDirs.UserPath + "/config/sysopts.bin", false);
CLoadFile settings(GVCMIDirs.UserPath + "/config/sysopts.bin", 727);
if(settings.sfile)
{
settings >> GDefaultOptions;
@ -282,6 +282,17 @@ int main(int argc, char** argv)
return 0;
}
void printInfoAboutIntObject(const CIntObject *obj, int level)
{
int tabs = level;
while(tabs--) tlog4 << '\t';
tlog4 << typeid(*obj).name() << " *** " << (obj->active ? "" : "not ") << "active\n";
BOOST_FOREACH(const CIntObject *child, obj->children)
printInfoAboutIntObject(child, level+1);
}
void processCommand(const std::string &message)
{
std::istringstream readed;
@ -452,6 +463,16 @@ void processCommand(const std::string &message)
{
LOCPLINT->showingDialog->setn(false);
}
else if(cn == "gui")
{
BOOST_FOREACH(const IShowActivable *child, GH.listInt)
{
if(const CIntObject *obj = dynamic_cast<const CIntObject *>(child))
printInfoAboutIntObject(obj, 0);
else
tlog4 << typeid(*obj).name() << std::endl;
}
}
else if(client && client->serv && client->serv->connected) //send to server
{
PlayerMessage pm(LOCPLINT->playerID,message);
@ -459,7 +480,6 @@ void processCommand(const std::string &message)
}
}
//plays intro, ends when intro is over or button has been pressed (handles events)
void playIntro()
{
@ -623,7 +643,7 @@ static void listenForEvents()
}
}
void startGame(StartInfo * options)
void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
{
GH.curInt =NULL;
if(gOnlyAI)
@ -655,7 +675,7 @@ void startGame(StartInfo * options)
{
case StartInfo::NEW_GAME:
case StartInfo::CAMPAIGN:
client->newGame(NULL, options);
client->newGame(serv, options);
break;
case StartInfo::LOAD_GAME:
std::string fname = options->mapname;

View File

@ -1917,6 +1917,14 @@ SystemOptions::SystemOptions()
animSpeed = 2;
printMouseShadow = true;
showQueue = true;
playerName = "Player";
}
void SystemOptions::setPlayerName(const std::string &newPlayerName)
{
playerName = newPlayerName;
settingsChanged();
}
void CPlayerInterface::eraseCurrentPathOf( const CGHeroInstance * ho, bool checkForExistanceOfPath /*= true */ )

View File

@ -76,6 +76,7 @@ namespace boost
struct SystemOptions
{
std::string playerName;
ui8 heroMoveSpeed;/*, enemyMoveSpeed*/ //speed of player's hero movement
ui8 mapScrollingSpeed; //map scrolling speed
@ -94,13 +95,14 @@ struct SystemOptions
void setMapScrollingSpeed(int newSpeed); //set the member above
void setMusicVolume(int newVolume);
void setSoundVolume(int newVolume);
void setPlayerName(const std::string &newPlayerName);
void settingsChanged(); //updates file with "default" settings for next running of application
void apply();
template <typename Handler> void serialize(Handler &h, const int version)
{
h & playerName;
h & heroMoveSpeed & mapScrollingSpeed & musicVolume & soundVolume;
h & printCellBorders & printStackRange & animSpeed & printMouseShadow & showQueue;
}
};

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@
#include "../StartInfo.h"
#include "GUIBase.h"
#include "FunctionList.h"
#include "../lib/CMapInfo.h"
/*
* CPreGame.h, part of VCMI engine
@ -26,27 +27,10 @@ class CGStatusBar;
class CTextBox;
class CCampaignState;
class CConnection;
class CPackForSelectionScreen;
class PlayerInfo;
class CMapInfo
{
public:
CMapHeader * mapHeader; //may be NULL if campaign
CCampaignHeader * campaignHeader; //may be NULL if scenario
StartInfo *scenarioOpts; //options with which scenario has been started (used only with saved games)
std::string filename;
bool lodCmpgn; //tells if this campaign is located in Lod file
std::string date;
int playerAmnt, //players in map
humenPlayers; //players ALLOWED to be controlled by human
int actualHumanPlayers; // >1 if multiplayer game
CMapInfo(bool map = true);
~CMapInfo();
//CMapInfo(const std::string &fname, const unsigned char *map);
void setHeader(CMapHeader *header);
void mapInit(const std::string &fname, const unsigned char *map);
void campaignInit();
void countPlayers();
};
namespace boost{ class thread; class recursive_mutex;}
enum ESortBy{_playerAm, _size, _format, _name, _viccon, _loscon, _numOfMaps}; //_numOfMaps is for campaigns
@ -66,7 +50,7 @@ public:
};
enum EMultiMode {
SINGLE_PLAYER = 0, HOT_SEAT, MULTI_PLAYER
SINGLE_PLAYER = 0, MULTI_HOT_SEAT, MULTI_NETWORK_HOST, MULTI_NETWORK_GUEST
};
CPicture *bgAd;
@ -94,6 +78,10 @@ public:
CTextInput *inputBox;
CChatBox(const Rect &rect);
void keyPressed(const SDL_KeyboardEvent & key);
void addNewMessage(const std::string &text);
};
class InfoCard : public CIntObject
@ -102,9 +90,11 @@ class InfoCard : public CIntObject
public:
CMenuScreen::EState type;
bool network;
bool chatOn; //if chat is shown, then description is hidden
CTextBox *mapDescription;
CChatBox *chat;
CPicture *playerListBg;
CHighlightableButtonsGroup *difficulty;
CDefHandler *sizes, *sFlags;;
@ -115,7 +105,7 @@ public:
void showTeamsPopup();
void toggleChat();
void setChat(bool activateChat);
InfoCard(CMenuScreen::EState Type, bool network = false);
InfoCard(bool Network = false);
~InfoCard();
};
@ -159,7 +149,7 @@ public:
void clickLeft(tribool down, bool previousState);
void keyPressed(const SDL_KeyboardEvent & key);
void onDoubleClick();
SelectionTab(CMenuScreen::EState Type, const boost::function<void(CMapInfo *)> &OnSelect, bool MultiPlayer=false);
SelectionTab(CMenuScreen::EState Type, const boost::function<void(CMapInfo *)> &OnSelect, CMenuScreen::EMultiMode MultiPlayer = CMenuScreen::SINGLE_PLAYER);
~SelectionTab();
};
@ -183,6 +173,7 @@ public:
struct PlayerOptionsEntry : public CIntObject
{
PlayerInfo &pi;
PlayerSettings &s;
CPicture *bg;
AdventureMapButton *btns[6]; //left and right for town, hero, bonus
@ -190,18 +181,25 @@ public:
SelectedBox *town;
SelectedBox *hero;
SelectedBox *bonus;
bool fixedHero;
enum {HUMAN_OR_CPU, HUMAN, CPU} whoCanPlay;
PlayerOptionsEntry(OptionsTab *owner, PlayerSettings &S);
void selectButtons(bool onlyHero = true); //hides unavailable buttons
void showAll(SDL_Surface * to);
};
CMenuScreen::EState type;
CSlider *turnDuration;
std::set<int> usedHeroes;
struct PlayerToRestore
{
int color, id;
void reset() { color = id = -1; }
PlayerToRestore(){ reset(); }
} playerToRestore;
std::map<int, PlayerOptionsEntry *> entries; //indexed by color
void nextCastle(int player, int dir); //dir == -1 or +1
@ -210,8 +208,8 @@ public:
void setTurnLength(int npos);
void flagPressed(int player);
void changeSelection(const CMapHeader *to);
OptionsTab(CMenuScreen::EState Type/*, StartInfo &Opts*/);
void recreate();
OptionsTab();
~OptionsTab();
void showAll(SDL_Surface * to);
@ -220,7 +218,32 @@ public:
bool canUseThisHero( int ID );
};
class CSelectionScreen : public CIntObject
class ISelectionScreenInfo
{
public:
CMenuScreen::EMultiMode multiPlayer;
CMenuScreen::EState screenType; //new/save/load#Game
const CMapInfo *current;
StartInfo sInfo;
std::map<ui32, std::string> playerNames; // id of player <-> player name; 0 is reserved as ID of AI "players"
ISelectionScreenInfo(const std::map<ui32, std::string> *Names = NULL);
virtual ~ISelectionScreenInfo();
virtual void update(){};
virtual void propagateOptions() {};
virtual void postRequest(ui8 what, ui8 dir) {};
virtual void postChatMessage(const std::string &txt){};
void setPlayer(PlayerSettings &pset, unsigned player);
void updateStartInfo( std::string filename, StartInfo & sInfo, const CMapHeader * mapHeader );
int getIdOfFirstUnallocatedPlayer(); //returns 0 if none
bool isGuest() const;
bool isHost() const;
};
class CSelectionScreen : public CIntObject, public ISelectionScreenInfo
{
public:
CPicture *bg; //general bg image
@ -229,25 +252,33 @@ public:
AdventureMapButton *start, *back;
SelectionTab *sel;
CMenuScreen::EState type; //new/save/load#Game
const CMapInfo *current;
StartInfo sInfo;
CIntObject *curTab;
CMenuScreen::EMultiMode multiPlayer;
boost::thread *serverHandlingThread;
boost::recursive_mutex *mx;
std::list<CPackForSelectionScreen *> upcomingPacks; //protected by mx
CConnection *serv; //connection to server, used in MP mode
bool ongoingClosing;
ui8 myNameID; //used when networking - otherwise all player are "mine"
CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer = CMenuScreen::SINGLE_PLAYER);
CSelectionScreen(CMenuScreen::EState Type, CMenuScreen::EMultiMode MultiPlayer = CMenuScreen::SINGLE_PLAYER, const std::map<ui32, std::string> *Names = NULL);
~CSelectionScreen();
void toggleTab(CIntObject *tab);
void changeSelection(const CMapInfo *to);
static void updateStartInfo( const CMapInfo * to, StartInfo & sInfo, const CMapHeader * mapHeader );
void startCampaign();
void startGame();
void difficultyChange(int to);
void toggleChat();
void handleConnection();
void processPacks();
void setSInfo(const StartInfo &si);
void update() OVERRIDE;
void propagateOptions() OVERRIDE;
void postRequest(ui8 what, ui8 dir) OVERRIDE;
void postChatMessage(const std::string &txt) OVERRIDE;
void propagateNames();
};
class CSavingScreen : public CSelectionScreen
@ -260,7 +291,8 @@ public:
~CSavingScreen();
};
class CScenarioInfo : public CIntObject
//scenario information screen shown during the game (thus not really a "pre-game" but fits here anyway)
class CScenarioInfo : public CIntObject, public ISelectionScreenInfo
{
public:
AdventureMapButton *back;
@ -386,10 +418,8 @@ public:
CGPreGame();
~CGPreGame();
void update();
void run();
void openSel(CMenuScreen::EState type, CMenuScreen::EMultiMode multi = CMenuScreen::SINGLE_PLAYER);
void resetPlayerNames();
void loadGraphics();
void disposeGraphics();
};

View File

@ -309,37 +309,57 @@ int CClient::getSelectedHero()
void CClient::newGame( CConnection *con, StartInfo *si )
{
enum {SINGLE, HOST, GUEST} networkMode = SINGLE;
std::set<ui8> myPlayers;
if (con == NULL)
{
CServerHandler sh;
con = sh.connectToServer();
serv = sh.connectToServer();
}
else
{
serv = con;
networkMode = (con->connectionID == 1) ? HOST : GUEST;
}
for(std::map<int, PlayerSettings>::iterator it =si->playerInfos.begin();
it != si->playerInfos.end(); ++it)
{
if(networkMode == SINGLE //single - one client has all player
|| networkMode != SINGLE && serv->connectionID == it->second.human //multi - client has only "its players"
|| networkMode == HOST && it->second.human == false) //multi - host has all AI players
{
myPlayers.insert(ui8(it->first)); //add player
}
}
if(networkMode != GUEST)
myPlayers.insert(255); //neutral
timeHandler tmh;
CGI->state = new CGameState();
tlog0 <<"\tGamestate: "<<tmh.getDif()<<std::endl;
serv = con;
CConnection &c(*con);
CConnection &c(*serv);
////////////////////////////////////////////////////
ui8 pom8;
c << ui8(2) << ui8(1); //new game; one client
c << *si;
c >> pom8;
if(pom8)
throw "Server cannot open the map!";
else
tlog0 << "Server opened map properly.\n";
c << ui8(si->playerInfos.size()+1); //number of players + neutral
for(std::map<int, PlayerSettings>::iterator it =si->playerInfos.begin();
it != si->playerInfos.end(); ++it)
if(networkMode == SINGLE)
{
c << ui8(it->first); //players
ui8 pom8;
c << ui8(2) << ui8(1); //new game; one client
c << *si;
c >> pom8;
if(pom8)
throw "Server cannot open the map!";
else
tlog0 << "Server opened map properly.\n";
}
c << ui8(255); // neutrals
c << myPlayers;
ui32 seed, sum;
delete si;
c >> si >> sum >> seed;
tlog0 <<"\tSending/Getting info to/from the server: "<<tmh.getDif()<<std::endl;
tlog0 << "\tUsing random seed: "<<seed << std::endl;
@ -362,6 +382,9 @@ void CClient::newGame( CConnection *con, StartInfo *si )
it != gs->scenarioOps->playerInfos.end(); ++it)//initializing interfaces for players
{
ui8 color = it->first;
if(!vstd::contains(myPlayers, color))
continue;
CCallback *cb = new CCallback(gs,color,this);
if(!it->second.human)
{
@ -524,27 +547,15 @@ CConnection * CServerHandler::connectToServer()
waitForServer();
th.update();
CConnection *ret = NULL;
while(!ret)
{
try
{
tlog0 << "Establishing connection...\n";
ret = new CConnection(conf.cc.server, port, NAME);
}
catch(...)
{
tlog1 << "\nCannot establish connection! Retrying within 2 seconds" <<std::endl;
SDL_Delay(2000);
}
}
CConnection *ret = justConnectToServer(conf.cc.server, port);
if(verbose)
tlog0<<"\tConnecting to the server: "<<th.getDif()<<std::endl;
return ret;
}
CServerHandler::CServerHandler()
CServerHandler::CServerHandler(bool runServer /*= false*/)
{
serverThread = NULL;
shared = NULL;
@ -569,3 +580,24 @@ void CServerHandler::callServer()
std::system(comm.c_str());
tlog0 << "Server finished\n";
}
CConnection * CServerHandler::justConnectToServer(const std::string &host, const std::string &port)
{
CConnection *ret = NULL;
while(!ret)
{
try
{
tlog0 << "Establishing connection...\n";
ret = new CConnection( host.size() ? host : conf.cc.server,
port.size() ? port : boost::lexical_cast<std::string>(conf.cc.port),
NAME);
}
catch(...)
{
tlog1 << "\nCannot establish connection! Retrying within 2 seconds" << std::endl;
SDL_Delay(2000);
}
}
return ret;
}

View File

@ -43,11 +43,15 @@ public:
bool verbose; //whether to print log msgs
std::string port; //port number in text form
//functions setting up local server
void startServer(); //creates a thread with callServer
void waitForServer(); //waits till server is ready
CConnection * connectToServer(); //connects to server
CServerHandler();
//////////////////////////////////////////////////////////////////////////
static CConnection * justConnectToServer(const std::string &host = "", const std::string &port = ""); //connects to given host without taking any other actions (like setting up server)
CServerHandler(bool runServer = false);
~CServerHandler();
};

View File

@ -1669,7 +1669,7 @@ void CRecruitmentWindow::close()
}
void CRecruitmentWindow::Max()
{
slider->moveTo(slider->amount);
slider->moveToMax();
}
void CRecruitmentWindow::Buy()
{
@ -3172,7 +3172,7 @@ CMarketplaceWindow::~CMarketplaceWindow()
void CMarketplaceWindow::setMax()
{
slider->moveTo(slider->amount);
slider->moveToMax();
}
void CMarketplaceWindow::makeDeal()
@ -3461,7 +3461,7 @@ CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*=
slider = new CSlider(231,481,137,0,0,0);
slider->moved = boost::bind(&CAltarWindow::sliderMoved,this,_1);
max = new AdventureMapButton(CGI->generaltexth->zelp[578],boost::bind(&CSlider::moveTo, slider, boost::ref(slider->amount)),147,520,"IRCBTNS.DEF");
max = new AdventureMapButton(CGI->generaltexth->zelp[578],boost::bind(&CSlider::moveToMax, slider),147,520,"IRCBTNS.DEF");
sacrificedUnits.resize(ARMY_SIZE, 0);
sacrificeAll = new AdventureMapButton(CGI->generaltexth->zelp[579],boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTARMY.DEF");
@ -6629,6 +6629,7 @@ CTextInput::CTextInput( const Rect &Pos, const Point &bgOffset, const std::strin
{
focus = false;
pos += Pos;
captureAllKeys = true;
OBJ_CONSTRUCTION;
bg = new CPicture(bgName, bgOffset.x, bgOffset.y);
used = LCLICK | KEYBOARD;
@ -6639,10 +6640,14 @@ CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
{
focus = false;
pos += Pos;
captureAllKeys = true;
OBJ_CONSTRUCTION;
bg = new CPicture(Pos, 0, true);
Rect hlp = Pos;
CSDL_Ext::blitSurface(srf, &hlp, *bg, NULL);
if(srf)
CSDL_Ext::blitSurface(srf, &hlp, *bg, NULL);
else
SDL_FillRect(*bg, NULL, 0);
pos.w = bg->pos.w;
pos.h = bg->pos.h;
bg->pos = pos;

View File

@ -359,7 +359,7 @@ public:
void setText(const std::string &nText, bool callCb = false);
CTextInput(const Rect &Pos, const Point &bgOffset, const std::string &bgName, const CFunctionList<void(const std::string &)> &CB);
CTextInput(const Rect &Pos, SDL_Surface *srf);
CTextInput(const Rect &Pos, SDL_Surface *srf = NULL);
~CTextInput();
void showAll(SDL_Surface * to);
void clickLeft(tribool down, bool previousState);

View File

@ -41,12 +41,12 @@
void SetResources::applyCl( CClient *cl )
{
cl->playerint[player]->receivedResource(-1,-1);
INTERFACE_CALL_IF_PRESENT(player,receivedResource,-1,-1);
}
void SetResource::applyCl( CClient *cl )
{
cl->playerint[player]->receivedResource(resid,val);
INTERFACE_CALL_IF_PRESENT(player,receivedResource,resid,val);
}
void SetPrimSkill::applyCl( CClient *cl )
@ -698,7 +698,7 @@ void ShowInInfobox::applyCl(CClient *cl)
{
SComponent sc(c);
text.toString(sc.description);
if(cl->playerint[player]->human)
if(vstd::contains(cl->playerint, player) && cl->playerint[player]->human)
{
static_cast<CPlayerInterface*>(cl->playerint[player])->showComp(sc);
}

View File

@ -412,4 +412,11 @@ static inline ui32 read_unaligned_u32(const void *p)
#define read_unaligned_u32(p) (* reinterpret_cast<const Uint32 *>(p))
#endif
//for explicit overrides
#ifdef _MSC_VER
#define OVERRIDE override
#else
#define OVERRIDE //is there any working counterpart?
#endif
#endif // __GLOBAL_H__

View File

@ -28,6 +28,7 @@
#include "RegisterTypes.cpp"
#include <algorithm>
#include <numeric>
#include "CMapInfo.h"
boost::rand48 ran;
class CGObjectInstance;
@ -1057,14 +1058,15 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, co
int CGameState::pickHero(int owner)
{
int h=-1;
if(!map->getHero(h = scenarioOps->getIthPlayersSettings(owner).hero,0) && h>=0) //we haven't used selected hero
const PlayerSettings &ps = scenarioOps->getIthPlayersSettings(owner);
if(!map->getHero(h = ps.hero,0) && h>=0) //we haven't used selected hero
return h;
int f = scenarioOps->getIthPlayersSettings(owner).castle;
int f = ps.castle;
int i=0;
do //try to find free hero of our faction
{
i++;
h = scenarioOps->getIthPlayersSettings(owner).castle*HEROES_PER_TYPE*2+(ran()%(HEROES_PER_TYPE*2));//->scenarioOps->playerInfos[pru].hero = VLC->
h = ps.castle*HEROES_PER_TYPE*2+(ran()%(HEROES_PER_TYPE*2));//->scenarioOps->playerInfos[pru].hero = VLC->
} while( map->getHero(h) && i<175);
if(i>174) //probably no free heroes - there's no point in further search, we'll take first free
{
@ -1368,7 +1370,7 @@ CGameState::~CGameState()
delete mx;
delete map;
delete curB;
//delete scenarioOps;
delete scenarioOps;
delete applierGs;
delete objCaller;
@ -1503,7 +1505,7 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
day = 0;
seed = Seed;
ran.seed((boost::int32_t)seed);
scenarioOps = si;
scenarioOps = new StartInfo(*si);
loadTownDInfos();
//pick grail location
@ -1628,9 +1630,9 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
map->objects.push_back(nnn);
map->addBlockVisTiles(nnn);
//give campaign bonus
if (si->mode == StartInfo::CAMPAIGN && getPlayer(nnn->tempOwner)->human)
if (scenarioOps->mode == StartInfo::CAMPAIGN && getPlayer(nnn->tempOwner)->human)
{
HLP::giveCampaignBonusToHero(nnn, si, campaign->camp->scenarios[si->whichMapInCampaign].travelOptions);
HLP::giveCampaignBonusToHero(nnn, scenarioOps, campaign->camp->scenarios[scenarioOps->whichMapInCampaign].travelOptions);
}
}
}
@ -1641,7 +1643,7 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
{
CScenarioTravel::STravelBonus bonus =
campaign->camp->scenarios[si->whichMapInCampaign].travelOptions.bonusesToChoose[si->choosenCampaignBonus];
campaign->camp->scenarios[scenarioOps->whichMapInCampaign].travelOptions.bonusesToChoose[scenarioOps->choosenCampaignBonus];
std::vector<CGHeroInstance *> Xheroes;
@ -1743,13 +1745,13 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
}
//give start resource bonus in case of campaign
if (si->mode == StartInfo::CAMPAIGN)
if (scenarioOps->mode == StartInfo::CAMPAIGN)
{
CScenarioTravel::STravelBonus chosenBonus =
campaign->camp->scenarios[si->whichMapInCampaign].travelOptions.bonusesToChoose[si->choosenCampaignBonus];
campaign->camp->scenarios[scenarioOps->whichMapInCampaign].travelOptions.bonusesToChoose[scenarioOps->choosenCampaignBonus];
if(chosenBonus.type == 7) //resource
{
std::vector<const PlayerSettings *> people = HLP::getHumanPlayerInfo(si); //players we will give resource bonus
std::vector<const PlayerSettings *> people = HLP::getHumanPlayerInfo(scenarioOps); //players we will give resource bonus
for (int b=0; b<people.size(); ++b)
{
std::vector<int> res; //resources we will give
@ -1827,11 +1829,11 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
hpool.pavailable[map->disposedHeroes[i].ID] = map->disposedHeroes[i].players;
}
if (si->mode == StartInfo::CAMPAIGN) //give campaign bonuses for specific / best hero
if (scenarioOps->mode == StartInfo::CAMPAIGN) //give campaign bonuses for specific / best hero
{
CScenarioTravel::STravelBonus chosenBonus =
campaign->camp->scenarios[si->whichMapInCampaign].travelOptions.bonusesToChoose[si->choosenCampaignBonus];
campaign->camp->scenarios[scenarioOps->whichMapInCampaign].travelOptions.bonusesToChoose[scenarioOps->choosenCampaignBonus];
if (chosenBonus.isBonusForHero() && chosenBonus.info1 != 0xFFFE) //exclude generated heroes
{
//find human player
@ -1856,7 +1858,7 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
maxB = b;
}
}
HLP::giveCampaignBonusToHero(heroes[maxB], si, campaign->camp->scenarios[si->whichMapInCampaign].travelOptions);
HLP::giveCampaignBonusToHero(heroes[maxB], scenarioOps, campaign->camp->scenarios[scenarioOps->whichMapInCampaign].travelOptions);
}
else //specific hero
{
@ -1864,7 +1866,7 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
{
if (heroes[b]->subID == chosenBonus.info1)
{
HLP::giveCampaignBonusToHero(heroes[b], si, campaign->camp->scenarios[si->whichMapInCampaign].travelOptions);
HLP::giveCampaignBonusToHero(heroes[b], scenarioOps, campaign->camp->scenarios[scenarioOps->whichMapInCampaign].travelOptions);
break;
}
}
@ -1904,16 +1906,16 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
for(std::map<ui8, PlayerState>::iterator k=players.begin(); k!=players.end(); ++k)
{
//starting bonus
if(si->playerInfos[k->first].bonus==PlayerSettings::brandom)
si->playerInfos[k->first].bonus = ran()%3;
switch(si->playerInfos[k->first].bonus)
if(scenarioOps->playerInfos[k->first].bonus==PlayerSettings::brandom)
scenarioOps->playerInfos[k->first].bonus = ran()%3;
switch(scenarioOps->playerInfos[k->first].bonus)
{
case PlayerSettings::bgold:
k->second.resources[6] += 500 + (ran()%6)*100;
break;
case PlayerSettings::bresource:
{
int res = VLC->townh->towns[si->playerInfos[k->first].castle].primaryRes;
int res = VLC->townh->towns[scenarioOps->playerInfos[k->first].castle].primaryRes;
if(res == 127)
{
k->second.resources[0] += 5 + ran()%6;
@ -2035,10 +2037,10 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
}
//campaign bonuses for towns
if (si->mode == StartInfo::CAMPAIGN)
if (scenarioOps->mode == StartInfo::CAMPAIGN)
{
CScenarioTravel::STravelBonus chosenBonus =
campaign->camp->scenarios[si->whichMapInCampaign].travelOptions.bonusesToChoose[si->choosenCampaignBonus];
campaign->camp->scenarios[scenarioOps->whichMapInCampaign].travelOptions.bonusesToChoose[scenarioOps->choosenCampaignBonus];
if (chosenBonus.type == 2)
{

View File

@ -71,6 +71,11 @@ void CConnection::init()
tlog0 << "Established connection with "<<pom<<std::endl;
wmx = new boost::mutex;
rmx = new boost::mutex;
handler = NULL;
receivedStop = sendStop = false;
static int cid = 1;
connectionID = cid++;
}
CConnection::CConnection(std::string host, std::string port, std::string Name)
@ -180,6 +185,11 @@ int CConnection::read(void * data, unsigned size)
}
CConnection::~CConnection(void)
{
if(handler)
handler->join();
delete handler;
close();
delete io_service;
delete wmx;
@ -280,11 +290,11 @@ void CSaveFile::reportState(CLogger &out)
}
}
CLoadFile::CLoadFile( const std::string &fname, bool requireLatest )
CLoadFile::CLoadFile(const std::string &fname, int minimalVersion /*= version*/)
:sfile(NULL)
{
registerTypes(*this);
openNextFile(fname, requireLatest);
openNextFile(fname, minimalVersion);
}
CLoadFile::~CLoadFile()
@ -304,7 +314,7 @@ void CLoadFile::close()
sfile = NULL;
}
void CLoadFile::openNextFile(const std::string &fname, bool requireLatest)
void CLoadFile::openNextFile(const std::string &fname, int minimalVersion)
{
fName = fname;
sfile = new std::ifstream(fname.c_str(),std::ios::binary);
@ -320,16 +330,16 @@ void CLoadFile::openNextFile(const std::string &fname, bool requireLatest)
if(std::memcmp(buffer,"VCMI",4))
{
tlog1 << "Error: not a VCMI save! (file " << fname << " )\n";
tlog1 << "Error: not a VCMI file! ( " << fname << " )\n";
delete sfile;
sfile = NULL;
return;
}
*this >> myVersion;
if(myVersion != version && requireLatest)
if(myVersion < minimalVersion)
{
tlog1 << "Error: Not supported save format! (file " << fname << " )\n";
tlog1 << "Error: Old file format! (file " << fname << " )\n";
delete sfile;
sfile = NULL;
}
@ -371,6 +381,11 @@ ui16 CTypeList::getTypeID( const std::type_info *type )
return 0;
}
std::ostream & operator<<(std::ostream &str, const CConnection &cpc)
{
return str << "Connection with " << cpc.name << " (ID: " << cpc.connectionID << /*", " << (cpc.host ? "host" : "guest") <<*/ ")";
}
CSerializer::~CSerializer()
{

View File

@ -23,7 +23,7 @@
#include <boost/mpl/identity.hpp>
#include <boost/any.hpp>
const ui32 version = 726;
const ui32 version = 727;
class CConnection;
class CGObjectInstance;
class CGameState;
@ -824,12 +824,12 @@ public:
std::string fName;
std::ifstream *sfile;
CLoadFile(const std::string &fname, bool requireLatest = true);
CLoadFile(const std::string &fname, int minimalVersion = version);
~CLoadFile();
int read(const void * data, unsigned size);
void close();
void openNextFile(const std::string &fname, bool requireLatest);
void openNextFile(const std::string &fname, int minimalVersion);
void reportState(CLogger &out);
};
@ -853,6 +853,12 @@ public:
boost::asio::io_service *io_service;
std::string name; //who uses this connection
int connectionID;
CConnection *c;
boost::thread *handler;
bool receivedStop, sendStop;
CConnection(std::string host, std::string port, std::string Name);
CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, std::string Name);
CConnection(TSocket * Socket, std::string Name); //use immediately after accepting connection into socket
@ -868,6 +874,8 @@ public:
CPack *retreivePack(); //gets from server next pack (allocates it with new)
};
DLL_EXPORT std::ostream &operator<<(std::ostream &str, const CConnection &cpc);
template<typename T>
class CApplier
{

View File

@ -24,6 +24,7 @@ class CConnection;
class CCampaignState;
class CArtifact;
class CSelectionScreen;
class CMapInfo;
struct CPack
{
@ -1626,7 +1627,13 @@ struct CPackForSelectionScreen : public CPack
void apply(CSelectionScreen *selScreen){}; //that functions are implemented in CPreGame.cpp
};
struct ChatMessage : public CPackForSelectionScreen
class CPregamePackToPropagate : public CPackForSelectionScreen
{};
class CPregamePackToHost : public CPackForSelectionScreen
{};
struct ChatMessage : public CPregamePackToPropagate
{
std::string playerName, message;
@ -1637,4 +1644,155 @@ struct ChatMessage : public CPackForSelectionScreen
}
};
struct QuitMenuWithoutStarting : public CPregamePackToPropagate
{
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
template <typename Handler> void serialize(Handler &h, const int version)
{}
};
struct PlayerJoined : public CPregamePackToHost
{
std::string playerName;
ui8 connectionID;
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
template <typename Handler> void serialize(Handler &h, const int version)
{
h & playerName & connectionID;
}
};
struct SelectMap : public CPregamePackToPropagate
{
const CMapInfo *mapInfo;
bool free;
SelectMap(const CMapInfo &src)
{
mapInfo = &src;
free = false;
}
SelectMap()
{
mapInfo = NULL;
free = true;
}
~SelectMap()
{
if(free)
delete mapInfo;
}
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
template <typename Handler> void serialize(Handler &h, const int version)
{
h & mapInfo;
}
};
struct UpdateStartOptions : public CPregamePackToPropagate
{
StartInfo *options;
bool free;
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
UpdateStartOptions(StartInfo &src)
{
options = &src;
free = false;
}
UpdateStartOptions()
{
options = NULL;
free = true;
}
~UpdateStartOptions()
{
if(free)
delete options;
}
template <typename Handler> void serialize(Handler &h, const int version)
{
h & options;
}
};
struct PregameGuiAction : public CPregamePackToPropagate
{
enum {NO_TAB, OPEN_OPTIONS, OPEN_SCENARIO_LIST};
ui8 action;
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
template <typename Handler> void serialize(Handler &h, const int version)
{
h & action;
}
};
struct RequestOptionsChange : public CPregamePackToHost
{
enum {TOWN, HERO, BONUS};
ui8 what;
si8 direction; //-1 or +1
ui8 playerID;
RequestOptionsChange(ui8 What, si8 Dir, ui8 Player)
:what(What), direction(Dir), playerID(Player)
{}
RequestOptionsChange(){}
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
template <typename Handler> void serialize(Handler &h, const int version)
{
h & what & direction & playerID;
}
};
struct PlayerLeft : public CPregamePackToPropagate
{
ui8 playerID;
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
template <typename Handler> void serialize(Handler &h, const int version)
{
h & playerID;
}
};
struct PlayersNames : public CPregamePackToPropagate
{
public:
std::map<ui32, std::string> playerNames;
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
template <typename Handler> void serialize(Handler &h, const int version)
{
h & playerNames;
}
};
struct StartWithCurrentSettings : public CPregamePackToPropagate
{
public:
void apply(CSelectionScreen *selScreen); //that functions are implemented in CPreGame.cpp
template <typename Handler> void serialize(Handler &h, const int version)
{
//h & playerNames;
}
};
#endif //__NETPACKS_H__

View File

@ -181,6 +181,15 @@ template<typename Serializer> DLL_EXPORT
void registerTypes4(Serializer &s)
{
s.template registerType<ChatMessage>();
s.template registerType<QuitMenuWithoutStarting>();
s.template registerType<PlayerJoined>();
s.template registerType<SelectMap>();
s.template registerType<UpdateStartOptions>();
s.template registerType<PregameGuiAction>();
s.template registerType<RequestOptionsChange>();
s.template registerType<PlayerLeft>();
s.template registerType<PlayersNames>();
s.template registerType<StartWithCurrentSettings>();
}
template<typename Serializer> DLL_EXPORT

View File

@ -101,6 +101,31 @@ struct DLL_EXPORT PlayerInfo
mainHeroPortrait(0), hasMainTown(0), generateHeroAtMainTown(0),
team(255), generateHero(0) {};
si8 defaultCastle() const
{
si8 ret = -2;
for (int j = 0; j < F_NUMBER && ret != -1; j++) //we start with none and find matching faction. if more than one, then set to random
{
if((1 << j) & allowedFactions)
{
if (ret >= 0) //we've already assigned a castle and another one is possible -> set random and let player choose
ret = -1; //breaks
if (ret == -2) //first available castle - pick
ret = j;
}
}
return ret;
}
si8 defaultHero(bool isMapRoE = false) const
{
if ((generateHeroAtMainTown || isMapRoE) && hasMainTown //we will generate hero in front of main town
|| p8) //random hero
return -1;
else
return -2;
}
template <typename Handler> void serialize(Handler &h, const int version)
{
h & p7 & p8 & p9 & canHumanPlay & canComputerPlay & AITactic & allowedFactions & isFactionRandom &

View File

@ -1321,25 +1321,30 @@ void CGameHandler::newTurn()
}
}
}
void CGameHandler::run(bool resume, const StartInfo *si /*= NULL*/)
void CGameHandler::run(bool resume)
{
using namespace boost::posix_time;
BOOST_FOREACH(CConnection *cc, conns)
{//init conn.
ui8 quantity, pom;
ui32 quantity;
ui8 pom;
//ui32 seed;
if(!resume)
(*cc) << si << gs->map->checksum << gs->seed; // gs->scenarioOps
(*cc) << gs->scenarioOps << gs->map->checksum << gs->seed; // gs->scenarioOps
(*cc) >> quantity; //how many players will be handled at that client
tlog0 << "Connection " << cc->connectionID << " will handle " << quantity << " player: ";
for(int i=0;i<quantity;i++)
{
(*cc) >> pom; //read player color
tlog0 << (int)pom << " ";
{
boost::unique_lock<boost::recursive_mutex> lock(gsm);
connections[pom] = cc;
}
}
tlog0 << std::endl;
}
for(std::set<CConnection*>::iterator i = conns.begin(); i!=conns.end();i++)

View File

@ -219,7 +219,7 @@ public:
void sendAndApply(SetResources * info);
void sendAndApply(NewStructures * info);
void run(bool resume, const StartInfo *si = NULL);
void run(bool resume);
void newTurn();
void handleAfterAttackCasting( const BattleAttack & bat );
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, ui32 artID);

View File

@ -28,6 +28,10 @@
#include "../lib/VCMI_Lib.h"
#include "../lib/VCMIDirs.h"
#include "CGameHandler.h"
#include <boost/thread.hpp>
#include <boost/foreach.hpp>
#include "../lib/CMapInfo.h"
std::string NAME_AFFIX = "server";
std::string NAME = NAME_VER + std::string(" (") + NAME_AFFIX + ')'; //application name
using namespace boost;
@ -53,8 +57,256 @@ static void vaccept(tcp::acceptor *ac, tcp::socket *s, boost::system::error_code
ac->accept(*s,*error);
}
CPregameServer::CPregameServer(CConnection *Host, TAcceptor *Acceptor /*= NULL*/)
: host(Host), state(RUNNING), acceptor(Acceptor), upcomingConnection(NULL), curmap(NULL), listeningThreads(0),
curStartInfo(NULL)
{
initConnection(host);
}
void CPregameServer::handleConnection(CConnection *cpc)
{
try
{
while(!cpc->receivedStop)
{
CPackForSelectionScreen *cpfs = NULL;
*cpc >> cpfs;
tlog0 << "Got package to announce " << typeid(*cpfs).name() << " from " << *cpc << std::endl;
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
bool quitting = dynamic_cast<QuitMenuWithoutStarting*>(cpfs),
startingGame = dynamic_cast<StartWithCurrentSettings*>(cpfs);
if(quitting || startingGame) //host leaves main menu or wants to start game -> we end
{
cpc->receivedStop = true;
if(!cpc->sendStop)
sendPack(cpc, *cpfs);
if(cpc == host)
toAnnounce.push_back(cpfs);
}
else
toAnnounce.push_back(cpfs);
if(startingGame)
{
//wait for sending thread to announce start
mx.unlock();
while(state == RUNNING) boost::this_thread::sleep(boost::posix_time::milliseconds(50));
mx.lock();
}
}
}
catch (const std::exception& e)
{
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
tlog0 << *cpc << " dies... \nWhat happened: " << e.what() << std::endl;
}
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
if(state != ENDING_AND_STARTING_GAME)
{
connections -= cpc;
//notify other players about leaving
PlayerLeft *pl = new PlayerLeft();
pl->playerID = cpc->connectionID;
announceTxt(cpc->name + " left the game");
toAnnounce.push_back(pl);
if(!connections.size())
{
tlog0 << "Last connection lost, server will close itself...\n";
boost::this_thread::sleep(boost::posix_time::seconds(2)); //we should never be hasty when networking
state = ENDING_WITHOUT_START;
}
}
tlog0 << "Thread listening for " << *cpc << " ended\n";
listeningThreads--;
delNull(cpc->handler);
}
void CPregameServer::run()
{
startListeningThread(host);
start_async_accept();
while(state == RUNNING)
{
{
boost::unique_lock<boost::recursive_mutex> myLock(mx);
while(toAnnounce.size())
{
processPack(toAnnounce.front());
toAnnounce.pop_front();
}
// //we end sending thread if we ordered all our connections to stop
// ending = true;
// BOOST_FOREACH(CPregameConnection *pc, connections)
// if(!pc->sendStop)
// ending = false;
if(state != RUNNING)
{
tlog0 << "Stopping listening for connections...\n";
acceptor->close();
}
if(acceptor)
{
acceptor->io_service().reset();
acceptor->io_service().poll();
}
} //frees lock
boost::this_thread::sleep(boost::posix_time::milliseconds(50));
}
tlog0 << "Thread handling connections ended\n";
if(state == ENDING_AND_STARTING_GAME)
{
tlog0 << "Waiting for listening thread to finish...\n";
while(listeningThreads) boost::this_thread::sleep(boost::posix_time::milliseconds(50));
tlog0 << "Preparing new game\n";
}
}
CPregameServer::~CPregameServer()
{
delete acceptor;
delete upcomingConnection;
BOOST_FOREACH(CPackForSelectionScreen *pack, toAnnounce)
delete pack;
toAnnounce.clear();
//TODO pregameconnections
}
void CPregameServer::connectionAccepted(const boost::system::error_code& ec)
{
if(ec)
{
tlog0 << "Something wrong during accepting: " << ec.message() << std::endl;
return;
}
tlog0 << "We got a new connection! :)\n";
CConnection *pc = new CConnection(upcomingConnection, NAME);
initConnection(pc);
upcomingConnection = NULL;
*pc << (ui8)pc->connectionID << curmap;
startListeningThread(pc);
announceTxt(pc->name + " joins the game");
PlayerJoined *pj = new PlayerJoined();
pj->playerName = pc->name;
pj->connectionID = pc->connectionID;
toAnnounce.push_back(pj);
start_async_accept();
}
void CPregameServer::start_async_accept()
{
assert(!upcomingConnection);
assert(acceptor);
upcomingConnection = new TSocket(acceptor->io_service());
acceptor->async_accept(*upcomingConnection, boost::bind(&CPregameServer::connectionAccepted, this, boost::asio::placeholders::error));
}
void CPregameServer::announceTxt(const std::string &txt, const std::string &playerName /*= "system"*/)
{
tlog0 << playerName << " says: " << txt << std::endl;
ChatMessage cm;
cm.playerName = playerName;
cm.message = txt;
boost::unique_lock<boost::recursive_mutex> queueLock(mx);
toAnnounce.push_front(new ChatMessage(cm));
}
void CPregameServer::announcePack(const CPackForSelectionScreen &pack)
{
BOOST_FOREACH(CConnection *pc, connections)
sendPack(pc, pack);
}
void CPregameServer::sendPack(CConnection * pc, const CPackForSelectionScreen & pack)
{
if(!pc->sendStop)
{
tlog0 << "\tSending pack of type " << typeid(pack).name() << " to " << *pc << std::endl;
*pc << &pack;
}
if(dynamic_cast<const QuitMenuWithoutStarting*>(&pack))
{
pc->sendStop = true;
}
else if(dynamic_cast<const StartWithCurrentSettings*>(&pack))
{
pc->sendStop = true;
}
}
void CPregameServer::processPack(CPackForSelectionScreen * pack)
{
if(dynamic_cast<CPregamePackToHost*>(pack))
{
sendPack(host, *pack);
}
else if(SelectMap *sm = dynamic_cast<SelectMap*>(pack))
{
delNull(curmap);
curmap = sm->mapInfo;
sm->free = false;
announcePack(*pack);
}
else if(UpdateStartOptions *uso = dynamic_cast<UpdateStartOptions*>(pack))
{
delNull(curStartInfo);
curStartInfo = uso->options;
uso->free = false;
announcePack(*pack);
}
else if(dynamic_cast<const StartWithCurrentSettings*>(pack))
{
state = ENDING_AND_STARTING_GAME;
announcePack(*pack);
}
else
announcePack(*pack);
delete pack;
}
void CPregameServer::initConnection(CConnection *c)
{
*c >> c->name;
connections.insert(c);
tlog0 << "Pregame connection with player " << c->name << " established!" << std::endl;
}
void CPregameServer::startListeningThread(CConnection * pc)
{
listeningThreads++;
pc->handler = new boost::thread(&CPregameServer::handleConnection, this, pc);
}
CVCMIServer::CVCMIServer()
: io(new io_service()), acceptor(new tcp::acceptor(*io, tcp::endpoint(tcp::v4(), port)))
: io(new io_service()), acceptor(new TAcceptor(*io, tcp::endpoint(tcp::v4(), port))), firstConnection(NULL)
{
tlog4 << "CVCMIServer created!" <<std::endl;
}
@ -64,64 +316,75 @@ CVCMIServer::~CVCMIServer()
//delete acceptor;
}
void CVCMIServer::newGame(CConnection *c)
CGameHandler * CVCMIServer::initGhFromHostingConnection(CConnection &c)
{
CGameHandler gh;
boost::system::error_code error;
StartInfo *si = new StartInfo;
ui8 clients;
*c >> clients; //how many clients should be connected - TODO: support more than one
*c >> *si; //get start options
CGameHandler *gh = new CGameHandler();
StartInfo si;
c >> si; //get start options
int problem;
#ifdef _MSC_VER
FILE *f;
problem = fopen_s(&f,si->mapname.c_str(),"r");
problem = fopen_s(&f,si.mapname.c_str(),"r");
#else
FILE * f = fopen(si->mapname.c_str(),"r");
FILE * f = fopen(si.mapname.c_str(),"r");
problem = !f;
#endif
if(problem && si->mode == StartInfo::NEW_GAME) //TODO some checking for campaigns
if(problem && si.mode == StartInfo::NEW_GAME) //TODO some checking for campaigns
{
*c << ui8(problem); //WRONG!
return;
c << ui8(problem); //WRONG!
return NULL;
}
else
{
if(f)
fclose(f);
*c << ui8(0); //OK!
c << ui8(0); //OK!
}
StartInfo startInfoCpy = *si;
gh.init(si,rand());
c->addStdVecItems(gh.gs);
gh->init(&si,std::clock());
c.addStdVecItems(gh->gs);
gh->conns.insert(&c);
CConnection* cc; //tcp::socket * ss;
for(int i=0; i<clients; i++)
{
if(!i)
{
cc=c;
}
else
{
tcp::socket * s = new tcp::socket(acceptor->io_service());
acceptor->accept(*s,error);
if(error) //retry
{
tlog3<<"Cannot establish connection - retrying..." << std::endl;
i--;
continue;
}
cc = new CConnection(s,NAME);
cc->addStdVecItems(gh.gs);
}
gh.conns.insert(cc);
}
gh.run(false, &startInfoCpy);
return gh;
}
void CVCMIServer::newGame()
{
CConnection &c = *firstConnection;
ui8 clients;
c >> clients; //how many clients should be connected
assert(clients == 1); //multi goes now by newPregame, TODO: custom lobbies
CGameHandler *gh = initGhFromHostingConnection(c);
gh->run(false);
delNull(gh);
}
void CVCMIServer::newPregame()
{
CPregameServer *cps = new CPregameServer(firstConnection, acceptor);
cps->run();
if(cps->state == CPregameServer::ENDING_WITHOUT_START)
{
delete cps;
return;
}
if(cps->state == CPregameServer::ENDING_AND_STARTING_GAME)
{
CGameHandler gh;
gh.conns = cps->connections;
gh.init(cps->curStartInfo,std::clock());
BOOST_FOREACH(CConnection *c, gh.conns)
c->addStdVecItems(gh.gs);
gh.run(false);
}
}
void CVCMIServer::start()
{
ServerReady *sr = NULL;
@ -147,6 +410,7 @@ void CVCMIServer::start()
boost::thread acc(boost::bind(vaccept,acceptor,s,&error));
sr->setToTrueAndNotify();
delete mr;
acc.join();
if (error)
{
@ -154,39 +418,44 @@ void CVCMIServer::start()
return;
}
tlog0<<"We've accepted someone... " << std::endl;
CConnection *connection = new CConnection(s,NAME);
firstConnection = new CConnection(s,NAME);
tlog0<<"Got connection!" << std::endl;
while(!end2)
{
ui8 mode;
*connection >> mode;
*firstConnection >> mode;
switch (mode)
{
case 0:
connection->socket->close();
firstConnection->close();
exit(0);
break;
case 1:
connection->socket->close();
firstConnection->close();
return;
break;
case 2:
newGame(connection);
newGame();
break;
case 3:
loadGame(connection);
loadGame();
break;
case 4:
newPregame();
break;
}
}
}
void CVCMIServer::loadGame( CConnection *c )
void CVCMIServer::loadGame()
{
CConnection &c = *firstConnection;
std::string fname;
CGameHandler gh;
boost::system::error_code error;
ui8 clients;
*c >> clients >> fname; //how many clients should be connected - TODO: support more than one
c >> clients >> fname; //how many clients should be connected - TODO: support more than one
{
ui32 ver;
@ -202,7 +471,7 @@ void CVCMIServer::loadGame( CConnection *c )
tlog0 <<"Reading handlers"<<std::endl;
lf >> (gh.gs);
c->addStdVecItems(gh.gs);
c.addStdVecItems(gh.gs);
tlog0 <<"Reading gamestate"<<std::endl;
}
@ -211,14 +480,14 @@ void CVCMIServer::loadGame( CConnection *c )
lf >> gh;
}
*c << ui8(0);
c << ui8(0);
CConnection* cc; //tcp::socket * ss;
for(int i=0; i<clients; i++)
{
if(!i)
{
cc=c;
cc = &c;
}
else
{

View File

@ -2,6 +2,7 @@
#define __CVCMISERVER_H__
#include "../global.h"
#include <set>
#include <boost/thread/recursive_mutex.hpp>
/*
* CVCMIServer.h, part of VCMI engine
@ -13,34 +14,90 @@
*
*/
class CMapInfo;
class CConnection;
class CPackForSelectionScreen;
class CGameHandler;
namespace boost
{
namespace asio
{
class io_service;
namespace ip
{
class tcp;
}
class io_service;
template <typename Protocol> class stream_socket_service;
template <typename Protocol,typename StreamSocketService>
class basic_stream_socket;
template <typename Protocol> class socket_acceptor_service;
template <typename Protocol,typename SocketAcceptorService>
class basic_socket_acceptor;
}
};
typedef boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::socket_acceptor_service<boost::asio::ip::tcp> > TAcceptor;
typedef boost::asio::basic_stream_socket < boost::asio::ip::tcp , boost::asio::stream_socket_service<boost::asio::ip::tcp> > TSocket;
class CVCMIServer
{
boost::asio::io_service *io;
boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::socket_acceptor_service<boost::asio::ip::tcp> > * acceptor;
std::map<int,CConnection*> connections;
std::set<CConnection*> conns;
TAcceptor * acceptor;
CConnection *firstConnection;
public:
CVCMIServer(); //c-tor
~CVCMIServer(); //d-tor
void setUpConnection(CConnection *c, std::string mapname, si32 checksum);
void newGame(CConnection *c);
void loadGame(CConnection *c);
void start();
CGameHandler *initGhFromHostingConnection(CConnection &c);
void newGame();
void loadGame();
void newPregame();
};
class CPregameServer
{
public:
CConnection *host;
int listeningThreads;
std::set<CConnection *> connections;
std::list<CPackForSelectionScreen*> toAnnounce;
boost::recursive_mutex mx;
//std::vector<CMapInfo> maps;
TAcceptor *acceptor;
TSocket *upcomingConnection;
const CMapInfo *curmap;
StartInfo *curStartInfo;
CPregameServer(CConnection *Host, TAcceptor *Acceptor = NULL);
~CPregameServer();
void run();
void processPack(CPackForSelectionScreen * pack);
void handleConnection(CConnection *cpc);
void connectionAccepted(const boost::system::error_code& ec);
void initConnection(CConnection *c);
void start_async_accept();
enum { INVALID, RUNNING, ENDING_WITHOUT_START, ENDING_AND_STARTING_GAME
} state;
void announceTxt(const std::string &txt, const std::string &playerName = "system");
void announcePack(const CPackForSelectionScreen &pack);
void sendPack(CConnection * pc, const CPackForSelectionScreen & pack);
void startListeningThread(CConnection * pc);
};
#endif // __CVCMISERVER_H__