Bläddra i källkod

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

Michał W. Urbańczyk 16 år sedan
förälder
incheckning
78afb07f52
11 ändrade filer med 169 tillägg och 32 borttagningar
  1. 107 9
      CConsoleHandler.cpp
  2. 8 1
      client/CMT.cpp
  3. 1 1
      client/CMessage.cpp
  4. 17 15
      client/CPlayerInterface.cpp
  5. 0 3
      client/Graphics.cpp
  6. 2 0
      global.h
  7. 3 0
      hch/CObjectHandler.cpp
  8. 6 2
      lib/map.cpp
  9. 4 0
      mapHandler.cpp
  10. 19 0
      server/CGameHandler.cpp
  11. 2 1
      server/CVCMIServer.cpp

+ 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;