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 si8 bonus; //usees enum type Ebonus
ui8 color; //from 0 - ui8 color; //from 0 -
ui8 handicap;//0-no, 1-mild, 2-severe ui8 handicap;//0-no, 1-mild, 2-severe
std::string name; 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) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & castle; h & castle;
@ -51,15 +52,15 @@ struct PlayerSettings
struct StartInfo 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 ui8 difficulty; //0=easy; 4=impossible
std::map<int, PlayerSettings> playerInfos; //color indexed std::map<int, PlayerSettings> playerInfos; //color indexed
ui8 turnTime; //in minutes, 0=unlimited ui8 turnTime; //in minutes, 0=unlimited
std::string mapname; std::string mapname;
ui8 whichMapInCampaign; //used only for mode 2 ui8 whichMapInCampaign; //used only for mode CAMPAIGN
ui8 choosenCampaignBonus; //used only for mode 2 ui8 choosenCampaignBonus; //used only for mode CAMPAIGN
PlayerSettings & getIthPlayersSettings(int no) PlayerSettings & getIthPlayersSettings(int no)
{ {
if(playerInfos.find(no) != playerInfos.end()) if(playerInfos.find(no) != playerInfos.end())
@ -68,10 +69,10 @@ struct StartInfo
throw std::string("Cannot find info about player"); 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) 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 &it->second;
return NULL; return NULL;
@ -87,6 +88,11 @@ struct StartInfo
h & whichMapInCampaign; h & whichMapInCampaign;
h & choosenCampaignBonus; h & choosenCampaignBonus;
} }
StartInfo()
{
mode = INVALID;
}
}; };

View File

@ -670,4 +670,9 @@ void CSlider::keyPressed(const SDL_KeyboardEvent & key)
} }
moveTo(moveDest); 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, 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 int Value=0, bool Horizontal=true, int style = 0); //style 0 - brown, 1 - blue
~CSlider(); ~CSlider();
void moveToMax();
}; };
#endif // __ADVENTUREMAPBUTTON_H__ #endif // __ADVENTUREMAPBUTTON_H__

View File

@ -110,7 +110,7 @@ void init()
tlog0 << "\tInitializing minors: " << pomtime.getDif() << std::endl; tlog0 << "\tInitializing minors: " << pomtime.getDif() << std::endl;
{ {
//read system options //read system options
CLoadFile settings(GVCMIDirs.UserPath + "/config/sysopts.bin", false); CLoadFile settings(GVCMIDirs.UserPath + "/config/sysopts.bin", 727);
if(settings.sfile) if(settings.sfile)
{ {
settings >> GDefaultOptions; settings >> GDefaultOptions;
@ -282,6 +282,17 @@ int main(int argc, char** argv)
return 0; 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) void processCommand(const std::string &message)
{ {
std::istringstream readed; std::istringstream readed;
@ -452,6 +463,16 @@ void processCommand(const std::string &message)
{ {
LOCPLINT->showingDialog->setn(false); 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 else if(client && client->serv && client->serv->connected) //send to server
{ {
PlayerMessage pm(LOCPLINT->playerID,message); 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) //plays intro, ends when intro is over or button has been pressed (handles events)
void playIntro() void playIntro()
{ {
@ -623,7 +643,7 @@ static void listenForEvents()
} }
} }
void startGame(StartInfo * options) void startGame(StartInfo * options, CConnection *serv/* = NULL*/)
{ {
GH.curInt =NULL; GH.curInt =NULL;
if(gOnlyAI) if(gOnlyAI)
@ -655,7 +675,7 @@ void startGame(StartInfo * options)
{ {
case StartInfo::NEW_GAME: case StartInfo::NEW_GAME:
case StartInfo::CAMPAIGN: case StartInfo::CAMPAIGN:
client->newGame(NULL, options); client->newGame(serv, options);
break; break;
case StartInfo::LOAD_GAME: case StartInfo::LOAD_GAME:
std::string fname = options->mapname; std::string fname = options->mapname;

View File

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

View File

@ -76,6 +76,7 @@ namespace boost
struct SystemOptions struct SystemOptions
{ {
std::string playerName;
ui8 heroMoveSpeed;/*, enemyMoveSpeed*/ //speed of player's hero movement ui8 heroMoveSpeed;/*, enemyMoveSpeed*/ //speed of player's hero movement
ui8 mapScrollingSpeed; //map scrolling speed ui8 mapScrollingSpeed; //map scrolling speed
@ -94,13 +95,14 @@ struct SystemOptions
void setMapScrollingSpeed(int newSpeed); //set the member above void setMapScrollingSpeed(int newSpeed); //set the member above
void setMusicVolume(int newVolume); void setMusicVolume(int newVolume);
void setSoundVolume(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 settingsChanged(); //updates file with "default" settings for next running of application
void apply(); void apply();
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & playerName;
h & heroMoveSpeed & mapScrollingSpeed & musicVolume & soundVolume; h & heroMoveSpeed & mapScrollingSpeed & musicVolume & soundVolume;
h & printCellBorders & printStackRange & animSpeed & printMouseShadow & showQueue; 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 "../StartInfo.h"
#include "GUIBase.h" #include "GUIBase.h"
#include "FunctionList.h" #include "FunctionList.h"
#include "../lib/CMapInfo.h"
/* /*
* CPreGame.h, part of VCMI engine * CPreGame.h, part of VCMI engine
@ -26,27 +27,10 @@ class CGStatusBar;
class CTextBox; class CTextBox;
class CCampaignState; class CCampaignState;
class CConnection; class CConnection;
class CPackForSelectionScreen;
class PlayerInfo;
class CMapInfo namespace boost{ class thread; class recursive_mutex;}
{
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();
};
enum ESortBy{_playerAm, _size, _format, _name, _viccon, _loscon, _numOfMaps}; //_numOfMaps is for campaigns enum ESortBy{_playerAm, _size, _format, _name, _viccon, _loscon, _numOfMaps}; //_numOfMaps is for campaigns
@ -66,7 +50,7 @@ public:
}; };
enum EMultiMode { enum EMultiMode {
SINGLE_PLAYER = 0, HOT_SEAT, MULTI_PLAYER SINGLE_PLAYER = 0, MULTI_HOT_SEAT, MULTI_NETWORK_HOST, MULTI_NETWORK_GUEST
}; };
CPicture *bgAd; CPicture *bgAd;
@ -94,6 +78,10 @@ public:
CTextInput *inputBox; CTextInput *inputBox;
CChatBox(const Rect &rect); CChatBox(const Rect &rect);
void keyPressed(const SDL_KeyboardEvent & key);
void addNewMessage(const std::string &text);
}; };
class InfoCard : public CIntObject class InfoCard : public CIntObject
@ -102,9 +90,11 @@ class InfoCard : public CIntObject
public: public:
CMenuScreen::EState type; CMenuScreen::EState type;
bool network;
bool chatOn; //if chat is shown, then description is hidden bool chatOn; //if chat is shown, then description is hidden
CTextBox *mapDescription; CTextBox *mapDescription;
CChatBox *chat; CChatBox *chat;
CPicture *playerListBg;
CHighlightableButtonsGroup *difficulty; CHighlightableButtonsGroup *difficulty;
CDefHandler *sizes, *sFlags;; CDefHandler *sizes, *sFlags;;
@ -115,7 +105,7 @@ public:
void showTeamsPopup(); void showTeamsPopup();
void toggleChat(); void toggleChat();
void setChat(bool activateChat); void setChat(bool activateChat);
InfoCard(CMenuScreen::EState Type, bool network = false); InfoCard(bool Network = false);
~InfoCard(); ~InfoCard();
}; };
@ -159,7 +149,7 @@ public:
void clickLeft(tribool down, bool previousState); void clickLeft(tribool down, bool previousState);
void keyPressed(const SDL_KeyboardEvent & key); void keyPressed(const SDL_KeyboardEvent & key);
void onDoubleClick(); 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(); ~SelectionTab();
}; };
@ -183,6 +173,7 @@ public:
struct PlayerOptionsEntry : public CIntObject struct PlayerOptionsEntry : public CIntObject
{ {
PlayerInfo &pi;
PlayerSettings &s; PlayerSettings &s;
CPicture *bg; CPicture *bg;
AdventureMapButton *btns[6]; //left and right for town, hero, bonus AdventureMapButton *btns[6]; //left and right for town, hero, bonus
@ -190,18 +181,25 @@ public:
SelectedBox *town; SelectedBox *town;
SelectedBox *hero; SelectedBox *hero;
SelectedBox *bonus; SelectedBox *bonus;
bool fixedHero;
enum {HUMAN_OR_CPU, HUMAN, CPU} whoCanPlay; enum {HUMAN_OR_CPU, HUMAN, CPU} whoCanPlay;
PlayerOptionsEntry(OptionsTab *owner, PlayerSettings &S); PlayerOptionsEntry(OptionsTab *owner, PlayerSettings &S);
void selectButtons(bool onlyHero = true); //hides unavailable buttons void selectButtons(bool onlyHero = true); //hides unavailable buttons
void showAll(SDL_Surface * to); void showAll(SDL_Surface * to);
}; };
CMenuScreen::EState type;
CSlider *turnDuration; CSlider *turnDuration;
std::set<int> usedHeroes; 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 std::map<int, PlayerOptionsEntry *> entries; //indexed by color
void nextCastle(int player, int dir); //dir == -1 or +1 void nextCastle(int player, int dir); //dir == -1 or +1
@ -210,8 +208,8 @@ public:
void setTurnLength(int npos); void setTurnLength(int npos);
void flagPressed(int player); void flagPressed(int player);
void changeSelection(const CMapHeader *to); void recreate();
OptionsTab(CMenuScreen::EState Type/*, StartInfo &Opts*/); OptionsTab();
~OptionsTab(); ~OptionsTab();
void showAll(SDL_Surface * to); void showAll(SDL_Surface * to);
@ -220,7 +218,32 @@ public:
bool canUseThisHero( int ID ); 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: public:
CPicture *bg; //general bg image CPicture *bg; //general bg image
@ -229,25 +252,33 @@ public:
AdventureMapButton *start, *back; AdventureMapButton *start, *back;
SelectionTab *sel; SelectionTab *sel;
CMenuScreen::EState type; //new/save/load#Game
const CMapInfo *current;
StartInfo sInfo;
CIntObject *curTab; 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 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(); ~CSelectionScreen();
void toggleTab(CIntObject *tab); void toggleTab(CIntObject *tab);
void changeSelection(const CMapInfo *to); void changeSelection(const CMapInfo *to);
static void updateStartInfo( const CMapInfo * to, StartInfo & sInfo, const CMapHeader * mapHeader );
void startCampaign(); void startCampaign();
void startGame(); void startGame();
void difficultyChange(int to); void difficultyChange(int to);
void toggleChat();
void handleConnection(); 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 class CSavingScreen : public CSelectionScreen
@ -260,7 +291,8 @@ public:
~CSavingScreen(); ~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: public:
AdventureMapButton *back; AdventureMapButton *back;
@ -386,10 +418,8 @@ public:
CGPreGame(); CGPreGame();
~CGPreGame(); ~CGPreGame();
void update(); void update();
void run();
void openSel(CMenuScreen::EState type, CMenuScreen::EMultiMode multi = CMenuScreen::SINGLE_PLAYER); void openSel(CMenuScreen::EState type, CMenuScreen::EMultiMode multi = CMenuScreen::SINGLE_PLAYER);
void resetPlayerNames();
void loadGraphics(); void loadGraphics();
void disposeGraphics(); void disposeGraphics();
}; };

View File

@ -309,37 +309,57 @@ int CClient::getSelectedHero()
void CClient::newGame( CConnection *con, StartInfo *si ) void CClient::newGame( CConnection *con, StartInfo *si )
{ {
enum {SINGLE, HOST, GUEST} networkMode = SINGLE;
std::set<ui8> myPlayers;
if (con == NULL) if (con == NULL)
{ {
CServerHandler sh; 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; timeHandler tmh;
CGI->state = new CGameState(); CGI->state = new CGameState();
tlog0 <<"\tGamestate: "<<tmh.getDif()<<std::endl; tlog0 <<"\tGamestate: "<<tmh.getDif()<<std::endl;
serv = con; CConnection &c(*serv);
CConnection &c(*con);
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
ui8 pom8;
c << ui8(2) << ui8(1); //new game; one client if(networkMode == SINGLE)
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)
{ {
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; ui32 seed, sum;
delete si;
c >> si >> sum >> seed; c >> si >> sum >> seed;
tlog0 <<"\tSending/Getting info to/from the server: "<<tmh.getDif()<<std::endl; tlog0 <<"\tSending/Getting info to/from the server: "<<tmh.getDif()<<std::endl;
tlog0 << "\tUsing random seed: "<<seed << 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 it != gs->scenarioOps->playerInfos.end(); ++it)//initializing interfaces for players
{ {
ui8 color = it->first; ui8 color = it->first;
if(!vstd::contains(myPlayers, color))
continue;
CCallback *cb = new CCallback(gs,color,this); CCallback *cb = new CCallback(gs,color,this);
if(!it->second.human) if(!it->second.human)
{ {
@ -524,27 +547,15 @@ CConnection * CServerHandler::connectToServer()
waitForServer(); waitForServer();
th.update(); th.update();
CConnection *ret = NULL; CConnection *ret = justConnectToServer(conf.cc.server, port);
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);
}
}
if(verbose) if(verbose)
tlog0<<"\tConnecting to the server: "<<th.getDif()<<std::endl; tlog0<<"\tConnecting to the server: "<<th.getDif()<<std::endl;
return ret; return ret;
} }
CServerHandler::CServerHandler() CServerHandler::CServerHandler(bool runServer /*= false*/)
{ {
serverThread = NULL; serverThread = NULL;
shared = NULL; shared = NULL;
@ -569,3 +580,24 @@ void CServerHandler::callServer()
std::system(comm.c_str()); std::system(comm.c_str());
tlog0 << "Server finished\n"; 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 bool verbose; //whether to print log msgs
std::string port; //port number in text form std::string port; //port number in text form
//functions setting up local server
void startServer(); //creates a thread with callServer void startServer(); //creates a thread with callServer
void waitForServer(); //waits till server is ready void waitForServer(); //waits till server is ready
CConnection * connectToServer(); //connects to server 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(); ~CServerHandler();
}; };

View File

@ -1669,7 +1669,7 @@ void CRecruitmentWindow::close()
} }
void CRecruitmentWindow::Max() void CRecruitmentWindow::Max()
{ {
slider->moveTo(slider->amount); slider->moveToMax();
} }
void CRecruitmentWindow::Buy() void CRecruitmentWindow::Buy()
{ {
@ -3172,7 +3172,7 @@ CMarketplaceWindow::~CMarketplaceWindow()
void CMarketplaceWindow::setMax() void CMarketplaceWindow::setMax()
{ {
slider->moveTo(slider->amount); slider->moveToMax();
} }
void CMarketplaceWindow::makeDeal() 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 = new CSlider(231,481,137,0,0,0);
slider->moved = boost::bind(&CAltarWindow::sliderMoved,this,_1); 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); sacrificedUnits.resize(ARMY_SIZE, 0);
sacrificeAll = new AdventureMapButton(CGI->generaltexth->zelp[579],boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTARMY.DEF"); 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; focus = false;
pos += Pos; pos += Pos;
captureAllKeys = true;
OBJ_CONSTRUCTION; OBJ_CONSTRUCTION;
bg = new CPicture(bgName, bgOffset.x, bgOffset.y); bg = new CPicture(bgName, bgOffset.x, bgOffset.y);
used = LCLICK | KEYBOARD; used = LCLICK | KEYBOARD;
@ -6639,10 +6640,14 @@ CTextInput::CTextInput(const Rect &Pos, SDL_Surface *srf)
{ {
focus = false; focus = false;
pos += Pos; pos += Pos;
captureAllKeys = true;
OBJ_CONSTRUCTION; OBJ_CONSTRUCTION;
bg = new CPicture(Pos, 0, true); bg = new CPicture(Pos, 0, true);
Rect hlp = Pos; 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.w = bg->pos.w;
pos.h = bg->pos.h; pos.h = bg->pos.h;
bg->pos = pos; bg->pos = pos;

View File

@ -359,7 +359,7 @@ public:
void setText(const std::string &nText, bool callCb = false); 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, 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(); ~CTextInput();
void showAll(SDL_Surface * to); void showAll(SDL_Surface * to);
void clickLeft(tribool down, bool previousState); void clickLeft(tribool down, bool previousState);

View File

@ -41,12 +41,12 @@
void SetResources::applyCl( CClient *cl ) void SetResources::applyCl( CClient *cl )
{ {
cl->playerint[player]->receivedResource(-1,-1); INTERFACE_CALL_IF_PRESENT(player,receivedResource,-1,-1);
} }
void SetResource::applyCl( CClient *cl ) void SetResource::applyCl( CClient *cl )
{ {
cl->playerint[player]->receivedResource(resid,val); INTERFACE_CALL_IF_PRESENT(player,receivedResource,resid,val);
} }
void SetPrimSkill::applyCl( CClient *cl ) void SetPrimSkill::applyCl( CClient *cl )
@ -698,7 +698,7 @@ void ShowInInfobox::applyCl(CClient *cl)
{ {
SComponent sc(c); SComponent sc(c);
text.toString(sc.description); 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); 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)) #define read_unaligned_u32(p) (* reinterpret_cast<const Uint32 *>(p))
#endif #endif
//for explicit overrides
#ifdef _MSC_VER
#define OVERRIDE override
#else
#define OVERRIDE //is there any working counterpart?
#endif
#endif // __GLOBAL_H__ #endif // __GLOBAL_H__

View File

@ -28,6 +28,7 @@
#include "RegisterTypes.cpp" #include "RegisterTypes.cpp"
#include <algorithm> #include <algorithm>
#include <numeric> #include <numeric>
#include "CMapInfo.h"
boost::rand48 ran; boost::rand48 ran;
class CGObjectInstance; class CGObjectInstance;
@ -1057,14 +1058,15 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, int player, co
int CGameState::pickHero(int owner) int CGameState::pickHero(int owner)
{ {
int h=-1; 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; return h;
int f = scenarioOps->getIthPlayersSettings(owner).castle; int f = ps.castle;
int i=0; int i=0;
do //try to find free hero of our faction do //try to find free hero of our faction
{ {
i++; 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); } 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 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 mx;
delete map; delete map;
delete curB; delete curB;
//delete scenarioOps; delete scenarioOps;
delete applierGs; delete applierGs;
delete objCaller; delete objCaller;
@ -1503,7 +1505,7 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
day = 0; day = 0;
seed = Seed; seed = Seed;
ran.seed((boost::int32_t)seed); ran.seed((boost::int32_t)seed);
scenarioOps = si; scenarioOps = new StartInfo(*si);
loadTownDInfos(); loadTownDInfos();
//pick grail location //pick grail location
@ -1628,9 +1630,9 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
map->objects.push_back(nnn); map->objects.push_back(nnn);
map->addBlockVisTiles(nnn); map->addBlockVisTiles(nnn);
//give campaign bonus //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 = 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; 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 //give start resource bonus in case of campaign
if (si->mode == StartInfo::CAMPAIGN) if (scenarioOps->mode == StartInfo::CAMPAIGN)
{ {
CScenarioTravel::STravelBonus chosenBonus = 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 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) for (int b=0; b<people.size(); ++b)
{ {
std::vector<int> res; //resources we will give 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; 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 = 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 if (chosenBonus.isBonusForHero() && chosenBonus.info1 != 0xFFFE) //exclude generated heroes
{ {
//find human player //find human player
@ -1856,7 +1858,7 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
maxB = b; 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 else //specific hero
{ {
@ -1864,7 +1866,7 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
{ {
if (heroes[b]->subID == chosenBonus.info1) 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; 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) for(std::map<ui8, PlayerState>::iterator k=players.begin(); k!=players.end(); ++k)
{ {
//starting bonus //starting bonus
if(si->playerInfos[k->first].bonus==PlayerSettings::brandom) if(scenarioOps->playerInfos[k->first].bonus==PlayerSettings::brandom)
si->playerInfos[k->first].bonus = ran()%3; scenarioOps->playerInfos[k->first].bonus = ran()%3;
switch(si->playerInfos[k->first].bonus) switch(scenarioOps->playerInfos[k->first].bonus)
{ {
case PlayerSettings::bgold: case PlayerSettings::bgold:
k->second.resources[6] += 500 + (ran()%6)*100; k->second.resources[6] += 500 + (ran()%6)*100;
break; break;
case PlayerSettings::bresource: 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) if(res == 127)
{ {
k->second.resources[0] += 5 + ran()%6; k->second.resources[0] += 5 + ran()%6;
@ -2035,10 +2037,10 @@ void CGameState::init( StartInfo * si, ui32 checksum, int Seed )
} }
//campaign bonuses for towns //campaign bonuses for towns
if (si->mode == StartInfo::CAMPAIGN) if (scenarioOps->mode == StartInfo::CAMPAIGN)
{ {
CScenarioTravel::STravelBonus chosenBonus = 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) if (chosenBonus.type == 2)
{ {

View File

@ -71,6 +71,11 @@ void CConnection::init()
tlog0 << "Established connection with "<<pom<<std::endl; tlog0 << "Established connection with "<<pom<<std::endl;
wmx = new boost::mutex; wmx = new boost::mutex;
rmx = 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) 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) CConnection::~CConnection(void)
{ {
if(handler)
handler->join();
delete handler;
close(); close();
delete io_service; delete io_service;
delete wmx; 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) :sfile(NULL)
{ {
registerTypes(*this); registerTypes(*this);
openNextFile(fname, requireLatest); openNextFile(fname, minimalVersion);
} }
CLoadFile::~CLoadFile() CLoadFile::~CLoadFile()
@ -304,7 +314,7 @@ void CLoadFile::close()
sfile = NULL; sfile = NULL;
} }
void CLoadFile::openNextFile(const std::string &fname, bool requireLatest) void CLoadFile::openNextFile(const std::string &fname, int minimalVersion)
{ {
fName = fname; fName = fname;
sfile = new std::ifstream(fname.c_str(),std::ios::binary); 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)) 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; delete sfile;
sfile = NULL; sfile = NULL;
return; return;
} }
*this >> myVersion; *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; delete sfile;
sfile = NULL; sfile = NULL;
} }
@ -371,6 +381,11 @@ ui16 CTypeList::getTypeID( const std::type_info *type )
return 0; 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() CSerializer::~CSerializer()
{ {

View File

@ -23,7 +23,7 @@
#include <boost/mpl/identity.hpp> #include <boost/mpl/identity.hpp>
#include <boost/any.hpp> #include <boost/any.hpp>
const ui32 version = 726; const ui32 version = 727;
class CConnection; class CConnection;
class CGObjectInstance; class CGObjectInstance;
class CGameState; class CGameState;
@ -824,12 +824,12 @@ public:
std::string fName; std::string fName;
std::ifstream *sfile; std::ifstream *sfile;
CLoadFile(const std::string &fname, bool requireLatest = true); CLoadFile(const std::string &fname, int minimalVersion = version);
~CLoadFile(); ~CLoadFile();
int read(const void * data, unsigned size); int read(const void * data, unsigned size);
void close(); void close();
void openNextFile(const std::string &fname, bool requireLatest); void openNextFile(const std::string &fname, int minimalVersion);
void reportState(CLogger &out); void reportState(CLogger &out);
}; };
@ -853,6 +853,12 @@ public:
boost::asio::io_service *io_service; boost::asio::io_service *io_service;
std::string name; //who uses this connection 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(std::string host, std::string port, std::string Name);
CConnection(TAcceptor * acceptor, boost::asio::io_service *Io_service, 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 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) 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> template<typename T>
class CApplier class CApplier
{ {

View File

@ -24,6 +24,7 @@ class CConnection;
class CCampaignState; class CCampaignState;
class CArtifact; class CArtifact;
class CSelectionScreen; class CSelectionScreen;
class CMapInfo;
struct CPack struct CPack
{ {
@ -1626,7 +1627,13 @@ struct CPackForSelectionScreen : public CPack
void apply(CSelectionScreen *selScreen){}; //that functions are implemented in CPreGame.cpp 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; 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__ #endif //__NETPACKS_H__

View File

@ -181,6 +181,15 @@ template<typename Serializer> DLL_EXPORT
void registerTypes4(Serializer &s) void registerTypes4(Serializer &s)
{ {
s.template registerType<ChatMessage>(); 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 template<typename Serializer> DLL_EXPORT

View File

@ -101,6 +101,31 @@ struct DLL_EXPORT PlayerInfo
mainHeroPortrait(0), hasMainTown(0), generateHeroAtMainTown(0), mainHeroPortrait(0), hasMainTown(0), generateHeroAtMainTown(0),
team(255), generateHero(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) template <typename Handler> void serialize(Handler &h, const int version)
{ {
h & p7 & p8 & p9 & canHumanPlay & canComputerPlay & AITactic & allowedFactions & isFactionRandom & 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; using namespace boost::posix_time;
BOOST_FOREACH(CConnection *cc, conns) BOOST_FOREACH(CConnection *cc, conns)
{//init conn. {//init conn.
ui8 quantity, pom; ui32 quantity;
ui8 pom;
//ui32 seed; //ui32 seed;
if(!resume) 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 (*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++) for(int i=0;i<quantity;i++)
{ {
(*cc) >> pom; //read player color (*cc) >> pom; //read player color
tlog0 << (int)pom << " ";
{ {
boost::unique_lock<boost::recursive_mutex> lock(gsm); boost::unique_lock<boost::recursive_mutex> lock(gsm);
connections[pom] = cc; connections[pom] = cc;
} }
} }
tlog0 << std::endl;
} }
for(std::set<CConnection*>::iterator i = conns.begin(); i!=conns.end();i++) 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(SetResources * info);
void sendAndApply(NewStructures * info); void sendAndApply(NewStructures * info);
void run(bool resume, const StartInfo *si = NULL); void run(bool resume);
void newTurn(); void newTurn();
void handleAfterAttackCasting( const BattleAttack & bat ); void handleAfterAttackCasting( const BattleAttack & bat );
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, ui32 artID); bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, ui32 artID);

View File

@ -28,6 +28,10 @@
#include "../lib/VCMI_Lib.h" #include "../lib/VCMI_Lib.h"
#include "../lib/VCMIDirs.h" #include "../lib/VCMIDirs.h"
#include "CGameHandler.h" #include "CGameHandler.h"
#include <boost/thread.hpp>
#include <boost/foreach.hpp>
#include "../lib/CMapInfo.h"
std::string NAME_AFFIX = "server"; std::string NAME_AFFIX = "server";
std::string NAME = NAME_VER + std::string(" (") + NAME_AFFIX + ')'; //application name std::string NAME = NAME_VER + std::string(" (") + NAME_AFFIX + ')'; //application name
using namespace boost; using namespace boost;
@ -53,8 +57,256 @@ static void vaccept(tcp::acceptor *ac, tcp::socket *s, boost::system::error_code
ac->accept(*s,*error); 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() 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; tlog4 << "CVCMIServer created!" <<std::endl;
} }
@ -64,64 +316,75 @@ CVCMIServer::~CVCMIServer()
//delete acceptor; //delete acceptor;
} }
void CVCMIServer::newGame(CConnection *c) CGameHandler * CVCMIServer::initGhFromHostingConnection(CConnection &c)
{ {
CGameHandler gh; CGameHandler *gh = new CGameHandler();
boost::system::error_code error; StartInfo si;
StartInfo *si = new StartInfo; c >> si; //get start options
ui8 clients;
*c >> clients; //how many clients should be connected - TODO: support more than one
*c >> *si; //get start options
int problem; int problem;
#ifdef _MSC_VER #ifdef _MSC_VER
FILE *f; FILE *f;
problem = fopen_s(&f,si->mapname.c_str(),"r"); problem = fopen_s(&f,si.mapname.c_str(),"r");
#else #else
FILE * f = fopen(si->mapname.c_str(),"r"); FILE * f = fopen(si.mapname.c_str(),"r");
problem = !f; problem = !f;
#endif #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! c << ui8(problem); //WRONG!
return; return NULL;
} }
else else
{ {
if(f) if(f)
fclose(f); fclose(f);
*c << ui8(0); //OK! c << ui8(0); //OK!
} }
StartInfo startInfoCpy = *si; gh->init(&si,std::clock());
gh.init(si,rand()); c.addStdVecItems(gh->gs);
c->addStdVecItems(gh.gs); gh->conns.insert(&c);
CConnection* cc; //tcp::socket * ss; return gh;
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);
} }
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() void CVCMIServer::start()
{ {
ServerReady *sr = NULL; ServerReady *sr = NULL;
@ -147,6 +410,7 @@ void CVCMIServer::start()
boost::thread acc(boost::bind(vaccept,acceptor,s,&error)); boost::thread acc(boost::bind(vaccept,acceptor,s,&error));
sr->setToTrueAndNotify(); sr->setToTrueAndNotify();
delete mr; delete mr;
acc.join(); acc.join();
if (error) if (error)
{ {
@ -154,39 +418,44 @@ void CVCMIServer::start()
return; return;
} }
tlog0<<"We've accepted someone... " << std::endl; tlog0<<"We've accepted someone... " << std::endl;
CConnection *connection = new CConnection(s,NAME); firstConnection = new CConnection(s,NAME);
tlog0<<"Got connection!" << std::endl; tlog0<<"Got connection!" << std::endl;
while(!end2) while(!end2)
{ {
ui8 mode; ui8 mode;
*connection >> mode; *firstConnection >> mode;
switch (mode) switch (mode)
{ {
case 0: case 0:
connection->socket->close(); firstConnection->close();
exit(0); exit(0);
break; break;
case 1: case 1:
connection->socket->close(); firstConnection->close();
return; return;
break; break;
case 2: case 2:
newGame(connection); newGame();
break; break;
case 3: case 3:
loadGame(connection); loadGame();
break;
case 4:
newPregame();
break; break;
} }
} }
} }
void CVCMIServer::loadGame( CConnection *c ) void CVCMIServer::loadGame()
{ {
CConnection &c = *firstConnection;
std::string fname; std::string fname;
CGameHandler gh; CGameHandler gh;
boost::system::error_code error; boost::system::error_code error;
ui8 clients; 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; ui32 ver;
@ -202,7 +471,7 @@ void CVCMIServer::loadGame( CConnection *c )
tlog0 <<"Reading handlers"<<std::endl; tlog0 <<"Reading handlers"<<std::endl;
lf >> (gh.gs); lf >> (gh.gs);
c->addStdVecItems(gh.gs); c.addStdVecItems(gh.gs);
tlog0 <<"Reading gamestate"<<std::endl; tlog0 <<"Reading gamestate"<<std::endl;
} }
@ -211,14 +480,14 @@ void CVCMIServer::loadGame( CConnection *c )
lf >> gh; lf >> gh;
} }
*c << ui8(0); c << ui8(0);
CConnection* cc; //tcp::socket * ss; CConnection* cc; //tcp::socket * ss;
for(int i=0; i<clients; i++) for(int i=0; i<clients; i++)
{ {
if(!i) if(!i)
{ {
cc=c; cc = &c;
} }
else else
{ {

View File

@ -2,6 +2,7 @@
#define __CVCMISERVER_H__ #define __CVCMISERVER_H__
#include "../global.h" #include "../global.h"
#include <set> #include <set>
#include <boost/thread/recursive_mutex.hpp>
/* /*
* CVCMIServer.h, part of VCMI engine * CVCMIServer.h, part of VCMI engine
@ -13,34 +14,90 @@
* *
*/ */
class CMapInfo;
class CConnection; class CConnection;
class CPackForSelectionScreen;
class CGameHandler;
namespace boost namespace boost
{ {
namespace asio namespace asio
{ {
class io_service;
namespace ip namespace ip
{ {
class tcp; 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> class socket_acceptor_service;
template <typename Protocol,typename SocketAcceptorService> template <typename Protocol,typename SocketAcceptorService>
class basic_socket_acceptor; 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 class CVCMIServer
{ {
boost::asio::io_service *io; boost::asio::io_service *io;
boost::asio::basic_socket_acceptor<boost::asio::ip::tcp, boost::asio::socket_acceptor_service<boost::asio::ip::tcp> > * acceptor; TAcceptor * acceptor;
std::map<int,CConnection*> connections;
std::set<CConnection*> conns; CConnection *firstConnection;
public: public:
CVCMIServer(); //c-tor CVCMIServer(); //c-tor
~CVCMIServer(); //d-tor ~CVCMIServer(); //d-tor
void setUpConnection(CConnection *c, std::string mapname, si32 checksum);
void newGame(CConnection *c);
void loadGame(CConnection *c);
void start(); 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__ #endif // __CVCMISERVER_H__