Prechádzať zdrojové kódy

#1153 and #1395 should be fixed. Terrible, terrible, terrible.
Fixed crash in battles, when attempted to access <0 screen coordinates.

Michał W. Urbańczyk 12 rokov pred
rodič
commit
385f4ab7f5

+ 8 - 0
CCallback.cpp

@@ -351,9 +351,17 @@ void CCallback::castSpell(const CGHeroInstance *hero, SpellID spellID, const int
 void CCallback::unregisterMyInterface()
 {
 	ASSERT_IF_CALLED_WITH_PLAYER
+	auto playerInt = cl->playerint[*player];
+
 	cl->playerint.erase(*player);
 	cl->battleints.erase(*player);
 	//TODO? should callback be disabled as well?
+
+	//Disabled interface will be deleted very soon, so switch LOCPLINT to next player available
+	if(playerInt == LOCPLINT  &&  cl->playerint.size())
+	{
+		LOCPLINT = dynamic_cast<CPlayerInterface*>(cl->playerint.begin()->second.get());
+	}
 }
 
 void CCallback::validatePaths()

+ 2 - 1
client/CAdvmapInterface.cpp

@@ -649,7 +649,8 @@ void CAdvMapInt::deactivate()
 		townList.deactivate();
 		terrain.deactivate();
 		infoBar.deactivate();
-		LOCPLINT->cingconsole->deactivate();
+		if(LOCPLINT)
+			LOCPLINT->cingconsole->deactivate();
 	}
 }
 void CAdvMapInt::showAll(SDL_Surface * to)

+ 32 - 26
client/CPlayerInterface.cpp

@@ -129,21 +129,14 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player)
 
 CPlayerInterface::~CPlayerInterface()
 {
-	howManyPeople--;
+	logGlobal->traceStream() << "\tHuman player interface for player " << playerID << " being destructed";
+	//howManyPeople--;
 	//delete pim;
 	//vstd::clear_pointer(pim);
 	delete showingDialog;
-	if(adventureInt)
-	{
-		if(adventureInt->active & CIntObject::KEYBOARD)
-			adventureInt->deactivateKeyboard();
-		delete adventureInt;
-		adventureInt = nullptr;
-	}
-
 	delete cingconsole;
-
-	LOCPLINT = nullptr;
+	if(LOCPLINT == this)
+		LOCPLINT = nullptr;
 }
 void CPlayerInterface::init(shared_ptr<CCallback> CB)
 {
@@ -381,6 +374,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
+	LOG_TRACE_PARAMS(logGlobal, "Hero %s killed handler for player %s", hero->name % playerID);
 
 	const CArmedInstance *newSelection = nullptr;
 	if (makingTurn)
@@ -405,9 +399,9 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 		paths.erase(hero);
 
 	adventureInt->heroList.update(hero);
-	if (makingTurn)
+	if (makingTurn && newSelection)
 		adventureInt->select(newSelection, true);
-	else
+	else if(adventureInt->selection == hero)
 		adventureInt->selection = nullptr;
 }
 
@@ -619,14 +613,11 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
 		cb->registerBattleInterface(autofightingAI);
 	}
 	
-	waitForAllDialogs();
-
-	if(isAutoFightOn)
-		GH.topInt()->deactivate();
-
+	//Don't wait for dialogs when we are non-active hot-seat player
+	if(LOCPLINT == this)
+		waitForAllDialogs();
+	
 	BATTLE_EVENT_POSSIBLE_RETURN;
-
-	GH.pushInt(battleInt);
 }
 
 
@@ -1569,6 +1560,10 @@ void CPlayerInterface::update()
 	if(terminate_cond.get())
 		return;
 
+	//While mutexes were locked away we may be have stopped being the active interface
+	if(LOCPLINT != this)
+		return;
+
 	//make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request
 	boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
 
@@ -2067,8 +2062,6 @@ void CPlayerInterface::finishMovement( const TryMoveHero &details, const int3 &h
 void CPlayerInterface::gameOver(PlayerColor player, bool victory )
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	if(LOCPLINT != this)
-		return;
 
 	if(player == playerID)
 	{
@@ -2076,17 +2069,30 @@ void CPlayerInterface::gameOver(PlayerColor player, bool victory )
 			showInfoDialog(CGI->generaltexth->allTexts[95]);
 // 		else
 // 			showInfoDialog("Placeholder message: you won!");
-
-		makingTurn = true;
-		waitForAllDialogs(); //wait till all dialogs are displayed and closed
-		makingTurn = false;
+		if(LOCPLINT == this)
+		{
+			GH.curInt = this; //waiting for dialogs requires this to get events
+			waitForAllDialogs(); //wait till all dialogs are displayed and closed
+		}
 
 		howManyPeople--;
 		if(!howManyPeople //all human players eliminated
 			&& cb->getStartInfo()->mode != StartInfo::CAMPAIGN) //campaigns are handled in proposeNextMission
 		{
+			if(adventureInt)
+			{
+				terminate_cond.setn(true);
+				adventureInt->deactivate();
+				if(GH.topInt() == adventureInt)
+					GH.popInt(adventureInt);
+				delete adventureInt;
+				adventureInt = nullptr;
+			}
 			requestReturningToMainMenu();
 		}
+		if(GH.curInt == this)
+			GH.curInt = nullptr;
+
 		cb->unregisterMyInterface(); //we already won/lost, nothing else matters
 	}
 

+ 3 - 2
client/Client.cpp

@@ -190,7 +190,6 @@ void CClient::endGame( bool closeConnection /*= true*/ )
     logNetwork->infoStream() << "Closed connection.";
 
 	GH.curInt = nullptr;
-	LOCPLINT->terminate_cond.setn(true);
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
         logNetwork->infoStream() << "Ending current game!";
@@ -606,9 +605,11 @@ void CClient::battleStarted(const BattleInfo * info)
 	if(!gNoGUI && (!!att || !!def || gs->scenarioOps->mode == StartInfo::DUEL))
 	{
 		boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
-		new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
+		auto bi = new CBattleInterface(leftSide.armyObject, rightSide.armyObject, leftSide.hero, rightSide.hero,
 			Rect((screen->w - 800)/2, 
 			     (screen->h - 600)/2, 800, 600), att, def);
+
+		GH.pushInt(bi);
 	}
 
 	auto callBattleStart = [&](PlayerColor color, ui8 side){

+ 1 - 1
client/battle/CCreatureAnimation.cpp

@@ -401,7 +401,7 @@ ui8 * CCreatureAnimation::getPixelAddr(SDL_Surface * dest, int X, int Y) const
 template<int bpp>
 inline void CCreatureAnimation::putPixelAt(SDL_Surface * dest, int X, int Y, size_t index, const std::array<SDL_Color, 8> & special) const
 {
-	if ( X < pos.x + pos.w && Y < pos.y + pos.h)
+	if ( X < pos.x + pos.w && Y < pos.y + pos.h && X >= 0 && Y >= 0)
 		putPixel<bpp>(getPixelAddr(dest, X, Y), palette[index], index, special);
 }
 

+ 7 - 1
server/CGameHandler.cpp

@@ -5098,7 +5098,7 @@ void CGameHandler::checkLossVictory( PlayerColor player )
 			if(*i  &&  (*i)->tempOwner == player)
 				setOwner(*i,PlayerColor::NEUTRAL);
 		}
-
+		
 		//eliminating one player may cause victory of another:
 		winLoseHandle(GameConstants::ALL_PLAYERS & ~(1<<player.getNum()));
 	}
@@ -5135,6 +5135,12 @@ void CGameHandler::checkLossVictory( PlayerColor player )
 			sendAndApply(&ucs);
 		}
 	}
+
+	//If player making turn has lost his turn must be over as well
+	if(gs->getPlayer(gs->currentPlayer)->status != EPlayerStatus::INGAME)
+	{
+		states.setFlag(gs->currentPlayer, &PlayerStatus::makingTurn, false);
+	}
 }
 
 void CGameHandler::getLossVicMessage( PlayerColor player, si8 standard, bool victory, InfoWindow &out ) const