Sfoglia il codice sorgente

New, experimental crashhandler for windows (creates minidumps).
Several minor fixes and improvements.

Michał W. Urbańczyk 16 anni fa
parent
commit
78afb07f52

+ 107 - 9
CConsoleHandler.cpp

@@ -3,7 +3,17 @@
 #include "CConsoleHandler.h"
 #include <boost/function.hpp>
 #include <boost/thread.hpp>
+#include <iomanip>
 
+/*
+* CConsoleHandler.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
 
 #ifndef _WIN32
 	typedef std::string TColor;
@@ -17,6 +27,8 @@
 	#define CONSOLE_GRAY "\x1b[0;40;39m"
 #else
 	#include <windows.h>
+	#include <dbghelp.h>
+	#pragma comment(lib, "dbghelp.lib")
 
 	typedef WORD TColor;
 	#define _kill_thread(a) TerminateThread(a,0)
@@ -33,16 +45,101 @@
 
 TColor defColor;
 
+#ifdef _WIN32
+
+void printWinError()
+{
+	//Get error code
+	int error = GetLastError();
+	if(!error)
+	{
+		tlog0 << "No Win error information set.\n";
+		return;
+	}
+	tlog1 << "Error " << error << " encountered:\n";
+
+	//Get error description
+	char* pTemp = NULL;
+	FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM,
+		NULL, error,  MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), (LPSTR)&pTemp, 1, NULL);
+	tlog1 << pTemp << std::endl;
+	LocalFree( pTemp );
+}
+
+const char* exceptionName(DWORD exc)
+{
+#define EXC_CASE(EXC)	case EXCEPTION_##EXC : return "EXCEPTION_" #EXC
+	switch (exc)
+	{
+		EXC_CASE(ACCESS_VIOLATION);
+		EXC_CASE(DATATYPE_MISALIGNMENT);
+		EXC_CASE(BREAKPOINT);
+		EXC_CASE(SINGLE_STEP);
+		EXC_CASE(ARRAY_BOUNDS_EXCEEDED);
+		EXC_CASE(FLT_DENORMAL_OPERAND);
+		EXC_CASE(FLT_DIVIDE_BY_ZERO);
+		EXC_CASE(FLT_INEXACT_RESULT);
+		EXC_CASE(FLT_INVALID_OPERATION);
+		EXC_CASE(FLT_OVERFLOW);
+		EXC_CASE(FLT_STACK_CHECK);
+		EXC_CASE(FLT_UNDERFLOW);
+		EXC_CASE(INT_DIVIDE_BY_ZERO);
+		EXC_CASE(INT_OVERFLOW);
+		EXC_CASE(PRIV_INSTRUCTION);
+		EXC_CASE(IN_PAGE_ERROR);
+		EXC_CASE(ILLEGAL_INSTRUCTION);
+		EXC_CASE(NONCONTINUABLE_EXCEPTION);
+		EXC_CASE(STACK_OVERFLOW);
+		EXC_CASE(INVALID_DISPOSITION);
+		EXC_CASE(GUARD_PAGE);
+		EXC_CASE(INVALID_HANDLE);
+	default:
+		return "UNKNOWN EXCEPTION";
+	}
+#undef EXC_CASE
+}
+
+
+
+LONG WINAPI onUnhandledException(EXCEPTION_POINTERS* exception)
+{
+	tlog1 << "Disaster happened.\n";
+
+	PEXCEPTION_RECORD einfo = exception->ExceptionRecord;
+	tlog1 << "Reason: 0x" << std::hex << einfo->ExceptionCode << " - " << exceptionName(einfo->ExceptionCode);
+	tlog1 << " at " << std::setfill('0') << std::setw(4) << exception->ContextRecord->SegCs << ":" << (void*)einfo->ExceptionAddress << std::endl;;
+
+	if (einfo->ExceptionCode == EXCEPTION_ACCESS_VIOLATION)
+	{
+		tlog1 << "Attempt to " << (einfo->ExceptionInformation[0] == 1 ? "write to " : "read from ") 
+			<< "0x" <<  std::setw(8) << (void*)einfo->ExceptionInformation[1] << std::endl;;
+	}
+	const DWORD threadId = ::GetCurrentThreadId();
+	tlog1 << "Thread ID: " << threadId << " [" << std::dec << std::setw(0) << threadId << "]\n";
+
+	//exception info to be placed in the dump
+	MINIDUMP_EXCEPTION_INFORMATION meinfo = {threadId, exception, TRUE};
+
+	//create file where dump will be placed
+	char *mname = NULL;
+	char buffer[MAX_PATH + 1];
+	HMODULE hModule = NULL;	
+	GetModuleFileNameA(hModule, buffer, MAX_PATH);
+	mname = strrchr(buffer, '\\');
+	if (mname != 0)
+		mname++;
+	else
+		mname = buffer;
+
+	strcat(mname, "_crashinfo.dmp");
+	HANDLE dfile = CreateFileA(mname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
+	tlog1 << "Crash info will be put in " << dfile << std::endl;
+	MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), dfile, MiniDumpWithDataSegs, &meinfo, 0, 0);
+	MessageBoxA(0, "VCMI has crashed. We are sorry. File with information about encountered problem has been created.", "VCMI Crashhandler", MB_OK | MB_ICONERROR);
+	return EXCEPTION_EXECUTE_HANDLER;
+}
+#endif
 
-/*
- * CConsoleHandler.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
 
 void CConsoleHandler::setColor(int level)
 {
@@ -100,6 +197,7 @@ CConsoleHandler::CConsoleHandler()
 	CONSOLE_SCREEN_BUFFER_INFO csbi;
 	GetConsoleScreenBufferInfo(handleOut,&csbi);
 	defColor = csbi.wAttributes;
+	SetUnhandledExceptionFilter(onUnhandledException);
 #else
 	defColor = "\x1b[0m";
 #endif

+ 8 - 1
client/CMT.cpp

@@ -57,7 +57,8 @@
  *
  */
 
-std::string NAME = NAME_VER + std::string(" (client)"); //application name
+std::string NAME_AFFIX = "client";
+std::string NAME = NAME_VER + std::string(" (") + NAME_AFFIX + ')'; //application name
 SDL_Surface *screen = NULL, //main screen surface 
 	*screen2 = NULL,//and hlp surface (used to store not-active interfaces layer) 
 	*screenBuf = screen; //points to screen (if only advmapint is present) or screen2 (else) - should be used when updating controls which are not regularly redrawed
@@ -392,6 +393,12 @@ void processCommand(const std::string &message, CClient *&client)
 		}
 		tlog0<<"\rExtracting done :)\n";
 	}
+	else if(cn=="crash")
+	{
+		int *ptr = NULL;
+		*ptr = 666;
+		//disaster!
+	}
 	else if(client && client->serv && client->serv->connected) //send to server
 	{
 		PlayerMessage pm(LOCPLINT->playerID,message);

+ 1 - 1
client/CMessage.cpp

@@ -309,7 +309,7 @@ CSimpleWindow * CMessage::genWindow(std::string text, int player, int Lmar, int
 	std::vector<std::string> * brtext = breakText(text,32,true,true);
 	std::vector<std::vector<SDL_Surface*> > * txtg = drawText(brtext, fontHeight);
 	std::pair<int,int> txts = getMaxSizes(txtg, fontHeight);
-	ret->bitmap = drawBox1(txts.first+Lmar+Rmar,txts.second+Tmar+Bmar,0);
+	ret->bitmap = drawBox1(txts.first+Lmar+Rmar,txts.second+Tmar+Bmar,player);
 	ret->pos.h=ret->bitmap->h;
 	ret->pos.w=ret->bitmap->w;
 	int curh = ret->bitmap->h/2 - (fontHeight*txtg->size())/2;

+ 17 - 15
client/CPlayerInterface.cpp

@@ -278,34 +278,36 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
 	if(details.result == TryMoveHero::TELEPORTATION	||  details.start == details.end)
 		return;
 
-	//initializing objects and performing first step of move
 	int3 hp = details.start;
-	if (details.result != TryMoveHero::SUCCESS) //hero failed to move
+
+	if(makingTurn  &&  ho->tempOwner == playerID) //we are moving our hero
 	{
-		if(details.result != TryMoveHero::FAILED)
+		if (details.result != TryMoveHero::SUCCESS && details.result != TryMoveHero::FAILED) //hero didn't change tile but visit succeeded
 		{
 			adventureInt->paths.erase(ho);
 			adventureInt->terrain.currentPath = NULL;
 		}
+		else if(adventureInt->terrain.currentPath) //&& hero is moving
+		{
+			//remove one node from the path (the one we went)
+			adventureInt->terrain.currentPath->nodes.erase(adventureInt->terrain.currentPath->nodes.end()-1);
+			if(!adventureInt->terrain.currentPath->nodes.size())  //if it was the last one, remove entire path
+			{
+				adventureInt->paths.erase(ho);
+				adventureInt->terrain.currentPath = NULL;
+			}
+		}
+	}
 
+	if (details.result != TryMoveHero::SUCCESS) //hero failed to move
+	{
 		ho->isStanding = true;
 		stillMoveHero.setn(STOP_MOVE);
 		LOCPLINT->totalRedraw();
 		return;
 	}
 
-	if (adventureInt->terrain.currentPath) //&& hero is moving
-	{
-		//remove one node from the path (the one we went)
-		adventureInt->terrain.currentPath->nodes.erase(adventureInt->terrain.currentPath->nodes.end()-1);
-		if(!adventureInt->terrain.currentPath->nodes.size())  //if it was the last one, remove entire path
-		{
-			adventureInt->paths.erase(ho);
-			adventureInt->terrain.currentPath = NULL;
-		}
-	}
-
-
+	//initializing objects and performing first step of move
 	if(details.end.x+1 == details.start.x && details.end.y+1 == details.start.y) //tl
 	{
 		//ho->moveDir = 1;

+ 0 - 3
client/Graphics.cpp

@@ -361,9 +361,6 @@ void Graphics::loadHeroAnims()
 	loadHeroAnim("AB01_.DEF", rotations, &Graphics::boatAnims);
 	loadHeroAnim("AB02_.DEF", rotations, &Graphics::boatAnims);
 	loadHeroAnim("AB03_.DEF", rotations, &Graphics::boatAnims);
-	VLC->dobjinfo->gobjs[8][0]->handler = boatAnims[0];
-	VLC->dobjinfo->gobjs[8][1]->handler = boatAnims[1];
-	VLC->dobjinfo->gobjs[8][2]->handler = boatAnims[2];
 }
 
 void Graphics::loadHeroAnim( const std::string &name, const std::vector<std::pair<int,int> > &rotations, std::vector<CDefEssential *> Graphics::*dst )

+ 2 - 0
global.h

@@ -20,6 +20,8 @@ typedef boost::int8_t si8; //signed int 8 bits (1 byte)
 #endif
 
 #define NAME_VER ("VCMI 0.72d")
+extern std::string NAME; //full name
+extern std::string NAME_AFFIX; //client / server
 #define CONSOLE_LOGGING_LEVEL 5
 #define FILE_LOGGING_LEVEL 6
 

+ 3 - 0
hch/CObjectHandler.cpp

@@ -1058,6 +1058,9 @@ void CGDwelling::newTurn() const
 	if(cb->getDate(1) != 1) //not first day of week
 		return;
 
+	//town growths are handled separately
+	if(ID == TOWNI_TYPE)
+		return;
 
 	bool change = false;
 

+ 6 - 2
lib/map.cpp

@@ -909,13 +909,17 @@ void Mapa::loadHero( CGObjectInstance * &nobj, unsigned char * bufor, int &i )
 		nhi->name = readString(bufor,i);
 	if(version>AB)
 	{
-		if(readChar(bufor,i))//true if hore's experience is greater than 0
+		if(readChar(bufor,i))//true if hero's experience is greater than 0
 		{	nhi->exp = readNormalNr(bufor,i); i+=4;	}
 		else
 			nhi->exp = 0xffffffff;
 	}
 	else
-	{	nhi->exp = readNormalNr(bufor,i); i+=4;	}
+	{	
+		nhi->exp = readNormalNr(bufor,i); i+=4;	
+		if(!nhi->exp) //0 means "not set" in <=AB maps
+			nhi->exp = 0xffffffff;
+	}
 
 	bool portrait=bufor[i]; ++i;
 	if (portrait)

+ 4 - 0
mapHandler.cpp

@@ -431,6 +431,10 @@ void CMapHandler::init()
 	timeHandler th;
 	th.getDif();
 
+	CGI->dobjinfo->gobjs[8][0]->handler = graphics->boatAnims[0];
+	CGI->dobjinfo->gobjs[8][1]->handler = graphics->boatAnims[1];
+	CGI->dobjinfo->gobjs[8][2]->handler = graphics->boatAnims[2];
+
 	// Size of visible terrain.
 	mapW = conf.go()->ac.advmapW;
 	mapH = conf.go()->ac.advmapH;

+ 19 - 0
server/CGameHandler.cpp

@@ -202,6 +202,13 @@ void CGameHandler::changeSecSkill( int ID, int which, int val, bool abs/*=false*
 	sss.val = val;
 	sss.abs = abs;
 	sendAndApply(&sss);
+
+	if(which == 7) //Wisdom
+	{
+		const CGHeroInstance *h = getHero(ID);
+		if(h && h->visitedTown)
+			giveSpells(h->visitedTown, h);
+	}
 }
 
 void CGameHandler::changePrimSkill(int ID, int which, int val, bool abs)
@@ -661,6 +668,9 @@ void CGameHandler::newTurn()
 		
 		BOOST_FOREACH(CGHeroInstance *h, (*i).second.heroes)
 		{
+			if(h->visitedTown)
+				giveSpells(h->visitedTown, h);
+
 			NewTurn::Hero hth;
 			hth.id = h->id;
 			hth.move = h->maxMovePoints(gs->map->getTile(h->getPosition(false)).tertype != TerrainTile::water);
@@ -1273,6 +1283,13 @@ bool CGameHandler::moveHero( si32 hid, int3 dst, ui8 instant, ui8 asker /*= 255*
 
 	tlog5 << "Player " <<int(asker) << " wants to move hero "<< hid << " from "<< h->pos << " to " << dst << std::endl;
 	int3 hmpos = dst + int3(-1,0,0);
+
+	if(!gs->map->isInTheMap(hmpos))
+	{
+		tlog1 << "Destination tile os out of the map!\n";
+		return false;
+	}
+
 	TerrainTile t = gs->map->terrain[hmpos.x][hmpos.y][hmpos.z];
 	int cost = gs->getMovementCost(h,h->getPosition(false),CGHeroInstance::convertPosition(dst,false),h->movement);
 
@@ -2934,4 +2951,6 @@ bool CGameHandler::buildBoat( ui32 objid )
 	no.subID = 1;
 	no.pos = tile + int3(1,0,0);
 	sendAndApply(&no);
+
+	return true;
 }

+ 2 - 1
server/CVCMIServer.cpp

@@ -25,7 +25,8 @@
 #include "../lib/Interprocess.h"
 #include "../lib/VCMI_Lib.h"
 #include "CGameHandler.h"
-std::string NAME = NAME_VER + std::string(" (server)");
+std::string NAME_AFFIX = "server";
+std::string NAME = NAME_VER + std::string(" (") + NAME_AFFIX + ')'; //application name
 using namespace boost;
 using namespace boost::asio;
 using namespace boost::asio::ip;