aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--src/communication.cc32
-rw-r--r--src/containers.hh4
-rw-r--r--src/crmain.cc11
-rw-r--r--src/crnonpl.cc31
-rw-r--r--src/crplayer.cc5
-rw-r--r--src/crypto.cc15
-rw-r--r--src/crypto.hh4
-rw-r--r--src/houses.cc40
-rw-r--r--src/map.cc34
-rw-r--r--src/moveuse.cc12
-rw-r--r--src/objects.cc4
-rw-r--r--src/operate.cc34
-rw-r--r--src/query.cc4
-rw-r--r--src/strings.cc11
-rw-r--r--src/threads.cc24
-rw-r--r--src/writer.cc12
17 files changed, 165 insertions, 114 deletions
diff --git a/Makefile b/Makefile
index a8e0e51..41b11f2 100644
--- a/Makefile
+++ b/Makefile
@@ -137,5 +137,5 @@ $(BUILDDIR)/writer.obj: $(SRCDIR)/writer.cc $(HEADERS)
.PHONY: clean
clean:
- @rm -r $(BUILDDIR)
+ @rm -rf $(BUILDDIR)
diff --git a/src/communication.cc b/src/communication.cc
index a15e20a..a2c4221 100644
--- a/src/communication.cc
+++ b/src/communication.cc
@@ -23,7 +23,7 @@
#define MAX_COMMUNICATION_THREADS 1100
#define COMMUNICATION_THREAD_STACK_SIZE ((int)KB(64))
-static int TERMINALVERSION[3] = {770, 770, 770};
+static int TERMINALVERSION[] = {770, 770, 770};
static int TCPSocket;
static ThreadHandle AcceptorThread;
static pid_t AcceptorThreadPID;
@@ -710,12 +710,12 @@ TPlayerData *PerformRegistration(TConnection *Connection, char *PlayerName,
char BuddyNames[100][30]; // MAX_BUDDIES, MAX_NAME_LENGTH ?
uint8 Rights[12]; // MAX_RIGHT_BYTES ?
bool PremiumAccountActivated;
- int Status = QueryManagerConnection->loginGame(AccountID, PlayerName,
+ int LoginCode = QueryManagerConnection->loginGame(AccountID, PlayerName,
PlayerPassword, Connection->GetIPAddress(), PrivateWorld, false,
GamemasterClient, &CharacterID, &Sex, Guild, Rank, Title,
&NumberOfBuddies, BuddyIDs, BuddyNames, Rights,
&PremiumAccountActivated);
- switch(Status){
+ switch(LoginCode){
case 0:{
// NOTE(fusion): Login successful.
break;
@@ -845,6 +845,8 @@ TPlayerData *PerformRegistration(TConnection *Connection, char *PlayerName,
return NULL;
}
+ // TODO(fusion): This should probably checked beforehand or also dispatch
+ // `decrementIsOnline`.
if(AccountID == 0){
error("PerformRegistration: Spieler %s wurde noch keinem Account zugewiesen.\n", PlayerName);
SendLoginMessage(Connection, 20,
@@ -925,22 +927,20 @@ bool HandleLogin(TConnection *Connection){
char PlayerName[30];
char PlayerPassword[30];
try{
- uint8 AssymmetricData[128];
- InputBuffer.readBytes(AssymmetricData, 128);
+ uint8 AsymmetricData[128];
+ InputBuffer.readBytes(AsymmetricData, 128);
RSAMutex.down();
- try{
- PrivateKey.decrypt(AssymmetricData);
- }catch(const char *str){
+ if(!PrivateKey.decrypt(AsymmetricData)){
RSAMutex.up();
- error("HandleLogin: Fehler beim Entschlüsseln (%s).\n", str);
+ error("HandleLogin: Fehler beim Entschlüsseln.\n");
SendLoginMessage(Connection, 20,
"Login failed due to corrupt data.",-1);
return false;
}
RSAMutex.up();
- TReadBuffer ReadBuffer(AssymmetricData, 128);
- ReadBuffer.readByte();
+ TReadBuffer ReadBuffer(AsymmetricData, 128);
+ ReadBuffer.readByte(); // always zero
Connection->SymmetricKey.init(&ReadBuffer);
TerminalType = (int)ReadBuffer.readWord();
TerminalVersion = (int)ReadBuffer.readWord();
@@ -1037,7 +1037,7 @@ bool HandleLogin(TConnection *Connection){
return false;
}
- bool SlotLocked = Slot->Locked == getpid();
+ bool SlotLocked = (Slot->Locked == getpid());
// NOTE(fusion): These checks would have been already made if the player was
// in the waiting list so they'd be redundant.
@@ -1425,9 +1425,7 @@ int AcceptorThreadLoop(void *Unused){
AcceptorThreadPID = getpid();
print(1, "Warte auf Clients...\n");
while(GameRunning()){
- struct sockaddr_in RemoteAddr = {};
- socklen_t RemoteAddrLen = sizeof(RemoteAddr);
- int Socket = accept(TCPSocket, (struct sockaddr*)&RemoteAddr, &RemoteAddrLen);
+ int Socket = accept(TCPSocket, NULL, NULL);
if(Socket == -1){
error("AcceptorThreadLoop: Fehler %d beim Accept.\n", errno);
continue;
@@ -1525,7 +1523,9 @@ void InitCommunication(void){
QueryManagerConnectionPool.init();
// TODO(fusion): This is arbitrary, should probably be set in the config.
- PrivateKey.initFromFile("tibia.pem");
+ if(!PrivateKey.initFromFile("tibia.pem")){
+ throw "cannot load RSA key";
+ }
OpenSocket();
if(TCPSocket == -1){
diff --git a/src/containers.hh b/src/containers.hh
index 1ff9896..25a368f 100644
--- a/src/containers.hh
+++ b/src/containers.hh
@@ -323,9 +323,11 @@ struct matrix3d{
this->xmin = xmin;
this->ymin = ymin;
+ this->zmin = zmin;
this->dx = dx;
this->dy = dy;
- this->entry = new T[dx * dy];
+ this->dz = dz;
+ this->entry = new T[dx * dy * dz];
}
matrix3d(int xmin, int xmax, int ymin, int ymax, int zmin, int zmax, T init)
diff --git a/src/crmain.cc b/src/crmain.cc
index 96ccc92..1faee30 100644
--- a/src/crmain.cc
+++ b/src/crmain.cc
@@ -1140,7 +1140,6 @@ void AddKillStatistics(int AttackerRace, int DefenderRace){
KilledCreatures[DefenderRace] += 1;
}
- // NOTE(fusion): This seems to track how many players are killed by
if(strcmp(RaceData[DefenderRace].Name, "human") == 0){
KilledPlayers[AttackerRace] += 1;
}
@@ -1458,7 +1457,7 @@ void LoadRace(const char *FileName){
}else if(strcmp(Identifier, "flags") == 0){
Script.readSymbol('{');
do{
- const char *Flag = Script.getIdentifier();
+ const char *Flag = Script.readIdentifier();
if(strcmp(Flag, "kickboxes") == 0){
Race->KickBoxes = true;
}else if(strcmp(Flag, "kickcreatures") == 0){
@@ -1800,17 +1799,17 @@ void LoadMonsterRaid(const char *FileName, int Start,
Script.nextToken();
if(strcmp(Script.getIdentifier(), "date") == 0){
Script.readSymbol('=');
- *Date = Script.getNumber();
+ *Date = Script.readNumber();
}else if(strcmp(Script.getIdentifier(), "interval") == 0){
Script.readSymbol('=');
- *Interval = Script.getNumber();
+ *Interval = Script.readNumber();
}else{
Script.error("date or interval expected");
}
Script.nextToken();
while(Script.Token != ENDOFFILE){
- if(strcmp(Script.getIdentifier(), "delay") == 0){
+ if(strcmp(Script.getIdentifier(), "delay") != 0){
Script.error("delay expected");
}
@@ -1928,7 +1927,7 @@ void LoadMonsterRaids(void){
SecondsToReboot += 86400;
}
- char BigRaidName[4096];
+ char BigRaidName[4096] = {};
int BigRaidDuration = 0;
int BigRaidTieBreaker = -1;
diff --git a/src/crnonpl.cc b/src/crnonpl.cc
index 277e2e6..b841d18 100644
--- a/src/crnonpl.cc
+++ b/src/crnonpl.cc
@@ -303,7 +303,6 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
if(!Ok){
TBehaviourNode *Left = this->readTerm(Script);
- Script->nextToken();
if(Script->Token != SPECIAL){
Script->error("relational operator expected");
}
@@ -326,9 +325,10 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
TBehaviourNode *Right = this->readTerm(Script);
Behaviour->addCondition(BEHAVIOUR_CONDITION_EXPRESSION,
NewBehaviourNode(Operator, Right, Left));
+ }else{
+ Script->nextToken();
}
- Script->nextToken();
if(Script->Token == SPECIAL && Script->getSpecial() == ','){
Script->nextToken();
}else{
@@ -346,6 +346,7 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
while(true){
if(Script->Token == STRING){
Behaviour->addAction(BEHAVIOUR_ACTION_REPLY, Script->getString(), NULL, NULL, NULL);
+ Script->nextToken();
}else if(Script->Token == IDENTIFIER){
int Type = -1;
int Data = 0;
@@ -423,7 +424,7 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
switch(Type){
case BEHAVIOUR_ACTION_NONE:{
- // no-op
+ Script->nextToken();
break;
}
@@ -432,7 +433,6 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
Script->readSymbol('=');
Script->nextToken();
TBehaviourNode *Value = this->readTerm(Script);
-
Behaviour->addAction(Type, &Data, Value, NULL, NULL);
break;
}
@@ -444,7 +444,7 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
if(Script->Token != SPECIAL || Script->getSpecial() != ')'){
Script->error(") expected");
}
-
+ Script->nextToken();
Behaviour->addAction(Type, &Data, Param, NULL, NULL);
break;
}
@@ -452,50 +452,46 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
case BEHAVIOUR_ACTION_FUNCTION2:
case BEHAVIOUR_ACTION_SET_SKILL_TIMER:{
Script->readSymbol('(');
-
Script->nextToken();
TBehaviourNode *Param1 = this->readTerm(Script);
if(Script->Token != SPECIAL || Script->getSpecial() != ','){
Script->error(", expected");
}
-
Script->nextToken();
TBehaviourNode *Param2 = this->readTerm(Script);
if(Script->Token != SPECIAL || Script->getSpecial() != ')'){
Script->error(") expected");
}
-
+ Script->nextToken();
Behaviour->addAction(Type, &Data, Param1, Param2, NULL);
break;
}
case BEHAVIOUR_ACTION_FUNCTION0:
case BEHAVIOUR_ACTION_CHANGESTATE:{
+ Script->nextToken();
Behaviour->addAction(Type, &Data, NULL, NULL, NULL);
break;
}
case BEHAVIOUR_ACTION_FUNCTION3:{
Script->readSymbol('(');
-
Script->nextToken();
TBehaviourNode *Param1 = this->readTerm(Script);
if(Script->Token != SPECIAL || Script->getSpecial() != ','){
Script->error(", expected");
}
-
Script->nextToken();
TBehaviourNode *Param2 = this->readTerm(Script);
if(Script->Token != SPECIAL || Script->getSpecial() != ','){
Script->error(", expected");
}
-
Script->nextToken();
TBehaviourNode *Param3 = this->readTerm(Script);
if(Script->Token != SPECIAL || Script->getSpecial() != ')'){
Script->error(") expected");
}
-
+ Script->nextToken();
Behaviour->addAction(Type, &Data, Param1, Param2, Param3);
break;
}
@@ -505,16 +501,17 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
break;
}
}
+
}else if(Script->Token == SPECIAL && Script->getSpecial() == '*'){
if(this->Behaviours == 0){
Script->error("no previous pattern");
}
+ Script->nextToken();
Behaviour->addAction(BEHAVIOUR_ACTION_REPEAT, NULL, NULL, NULL, NULL);
}else{
Script->error("illegal action");
}
- Script->nextToken();
if(Script->Token == SPECIAL && Script->getSpecial() == ','){
Script->nextToken();
}else{
@@ -529,7 +526,7 @@ TBehaviourDatabase::TBehaviourDatabase(TReadScriptFile *Script) :
TBehaviourNode *TBehaviourDatabase::readValue(TReadScriptFile *Script){
TBehaviourNode *Node = NULL;
if(Script->Token == NUMBER){
- Node = NewBehaviourNode(BEHAVIOUR_NODE_NUMBER, Script->readNumber());
+ Node = NewBehaviourNode(BEHAVIOUR_NODE_NUMBER, Script->getNumber());
}else if(Script->Token == IDENTIFIER){
const char *Identifier = Script->getIdentifier();
if(strcmp(Identifier, "topic") == 0){
@@ -1597,7 +1594,7 @@ TNPC::TNPC(const char *FileName) :
this->OrgOutfit = ReadOutfit(&Script);
this->Outfit = this->OrgOutfit;
}else if(strcmp(Identifier, "home") == 0){
- Script.readCoordinate(&this->startx, &this->starty, &this->startx);
+ Script.readCoordinate(&this->startx, &this->starty, &this->startz);
this->posx = this->startx;
this->posy = this->starty;
this->posz = this->startz;
@@ -2025,7 +2022,7 @@ TMonster::TMonster(int Race, int x, int y, int z, int Home, uint32 MasterID) :
Object Bag = Create(GetBodyContainer(this->ID, INVENTORY_BAG),
GetSpecialObject(DEFAULT_CONTAINER),
0);
- for(int i = 0; i < RaceData[Race].Items; i += 1){
+ for(int i = 1; i <= RaceData[Race].Items; i += 1){
TItemData *ItemData = RaceData[Race].Item.at(i);
if(random(0, 999) > ItemData->Probability){
continue;
@@ -2381,7 +2378,7 @@ void TMonster::IdleStimulus(void){
// NOTE(fusion): TALKING.
if(RaceData[this->Race].Talks > 0 && (rand() % 50) == 0){
- int TalkNr = rand() % (RaceData[this->Race].Talks + 1);
+ int TalkNr = random(1, RaceData[this->Race].Talks);
const char *Text = GetDynamicString(*RaceData[this->Race].Talk.at(TalkNr));
if(Text != 0 && Text[0] != 0){
// TODO(fusion): We were only checking for a `#` but it could be
diff --git a/src/crplayer.cc b/src/crplayer.cc
index e5874be..dce7ad2 100644
--- a/src/crplayer.cc
+++ b/src/crplayer.cc
@@ -2314,7 +2314,7 @@ bool LoadPlayerData(TPlayerData *Slot){
}
}
- int Position = Script.readNumber();
+ int Position = Script.getNumber();
if(Position < INVENTORY_FIRST || Position > INVENTORY_LAST){
Script.error("illegal inventory position");
}
@@ -2342,7 +2342,7 @@ bool LoadPlayerData(TPlayerData *Slot){
}
}
- int DepotNr = Script.readNumber();
+ int DepotNr = Script.getNumber();
if(DepotNr < 0 || DepotNr >= NARRAY(Slot->Depot)){
Script.error("illegal depot number");
}
@@ -2361,6 +2361,7 @@ bool LoadPlayerData(TPlayerData *Slot){
Script.error("end of file expected");
}
+ Script.close();
Result = true;
}catch(const char *str){
error("LoadPlayerData: Kann Gegenstände des Spielers %u nicht laden.\n",
diff --git a/src/crypto.cc b/src/crypto.cc
index aa53dc3..1559999 100644
--- a/src/crypto.cc
+++ b/src/crypto.cc
@@ -27,16 +27,16 @@ TRSAPrivateKey::~TRSAPrivateKey(void){
}
}
-void TRSAPrivateKey::initFromFile(const char *FileName){
+bool TRSAPrivateKey::initFromFile(const char *FileName){
if(m_RSA != NULL){
error("TRSAPrivateKey::init: Key already initialized.\n");
- return;
+ return false;
}
FILE *File = fopen(FileName, "rb");
if(File == NULL){
error("TRSAPrivateKey::initFromFile: Failed to open \"%s\".\n", FileName);
- return;
+ return false;
}
m_RSA = PEM_read_RSAPrivateKey(File, NULL, NULL, NULL);
@@ -50,12 +50,14 @@ void TRSAPrivateKey::initFromFile(const char *FileName){
RSA_free(m_RSA);
m_RSA = NULL;
}
+
+ return (m_RSA != NULL);
}
-void TRSAPrivateKey::decrypt(uint8 *Data){
+bool TRSAPrivateKey::decrypt(uint8 *Data){
if(m_RSA == NULL){
error("TRSAPrivateKey::decrypt: Key not initialized.\n");
- return;
+ return false;
}
// TODO(fusion): Pass in the length of `Data` for checking.
@@ -63,7 +65,10 @@ void TRSAPrivateKey::decrypt(uint8 *Data){
if(RSA_private_decrypt(128, Data, Data, m_RSA, RSA_NO_PADDING) == -1){
DumpOpenSSLErrors("TRSAPrivateKey::decrypt", "RSA_private_decrypt");
+ return false;
}
+
+ return true;
}
// TXTEASymmetricKey
diff --git a/src/crypto.hh b/src/crypto.hh
index b649ad8..4c89bf7 100644
--- a/src/crypto.hh
+++ b/src/crypto.hh
@@ -7,8 +7,8 @@
struct TRSAPrivateKey{
TRSAPrivateKey(void);
~TRSAPrivateKey(void);
- void initFromFile(const char *FileName);
- void decrypt(uint8 *Data); // single 128 bytes block
+ bool initFromFile(const char *FileName);
+ bool decrypt(uint8 *Data); // single 128 bytes block
// DATA
// =================
diff --git a/src/houses.cc b/src/houses.cc
index 4c06419..a26ffbf 100644
--- a/src/houses.cc
+++ b/src/houses.cc
@@ -21,7 +21,27 @@ static TQueryManagerConnection *QueryManagerConnection;
static int PaymentExtension;
THouse::THouse(void) : Subowner(0, 4, 5), Guest(0, 9, 10) {
- // no-op
+ this->ID = 0;
+ this->Name[0] = 0;
+ this->Description[0] = 0;
+ this->Size = 0;
+ this->Rent = 0;
+ this->DepotNr = 0;
+ this->NoAuction = 0;
+ this->GuildHouse = 0;
+ this->ExitX = 0;
+ this->ExitY = 0;
+ this->ExitZ = 0;
+ this->CenterX = 0;
+ this->CenterY = 0;
+ this->CenterZ = 0;
+ this->OwnerID = 0;
+ this->OwnerName[30] = 0;
+ this->LastTransition = 0;
+ this->PaidUntil = 0;
+ this->Subowners = 0;
+ this->Guests = 0;
+ this->Help = 0;
}
THouse::THouse(const THouse &Other) : THouse() {
@@ -1121,7 +1141,7 @@ bool EvictFreeAccounts(void){
uint16 *HouseIDs = (uint16*)alloca(NumberOfEvictions * sizeof(uint16));
uint32 *OwnerIDs = (uint32*)alloca(NumberOfEvictions * sizeof(uint32));
int Ret = QueryManagerConnection->evictFreeAccounts(&NumberOfEvictions, HouseIDs, OwnerIDs);
- if(Ret == 0){
+ if(Ret != 0){
error("CollectRents: Kann Zahlungsdaten nicht ermitteln.\n");
return false;
}
@@ -1154,7 +1174,7 @@ bool EvictDeletedCharacters(void){
int NumberOfEvictions = Houses;
uint16 *HouseIDs = (uint16*)alloca(NumberOfEvictions * sizeof(uint16));
int Ret = QueryManagerConnection->evictDeletedCharacters(&NumberOfEvictions, HouseIDs);
- if(Ret == 0){
+ if(Ret != 0){
error("CollectRents: Kann gelöschte Charaktere nicht ermitteln.\n");
return false;
}
@@ -1193,7 +1213,7 @@ bool EvictExGuildLeaders(void){
int NumberOfEvictions;
int Ret = QueryManagerConnection->evictExGuildleaders(
NumberOfGuildHouses, &NumberOfEvictions, HouseIDs, GuildLeaders);
- if(Ret == 0){
+ if(Ret != 0){
error("CollectRents: Kann Gildenränge nicht ermitteln.\n");
return false;
}
@@ -1311,7 +1331,6 @@ void ProcessRent(void){
CollectRent();
}
-
bool StartAuctions(void){
Log("houses", "Starte neue Versteigerungen...\n");
@@ -1533,7 +1552,7 @@ void LoadHouseAreas(void){
Script.readSymbol('(');
Area->ID = (uint16)Script.readNumber();
Script.readSymbol(',');
- Script.readNumber(); // area name
+ Script.readString(); // area name
Script.readSymbol(',');
Area->SQMPrice = Script.readNumber();
Script.readSymbol(',');
@@ -1772,7 +1791,7 @@ void LoadOwners(void){
}
}
- strcpy(House->Guest.at(House->Guests)->Name, Script.readString());
+ strcpy(House->Guest.at(House->Guests)->Name, Script.getString());
House->Guests += 1;
}
@@ -1789,7 +1808,7 @@ void LoadOwners(void){
}
}
- strcpy(House->Subowner.at(House->Subowners)->Name, Script.readString());
+ strcpy(House->Subowner.at(House->Subowners)->Name, Script.getString());
House->Subowners += 1;
}
@@ -1924,9 +1943,8 @@ void SaveOwners(void){
void ProcessHouses(void){
FinishAuctions();
ProcessRent();
- if(StartAuctions()){
- UpdateHouseOwners();
- }
+ StartAuctions();
+ UpdateHouseOwners();
}
void InitHouses(void){
diff --git a/src/map.cc b/src/map.cc
index a4f3fac..78e54db 100644
--- a/src/map.cc
+++ b/src/map.cc
@@ -222,6 +222,7 @@ static void CronSet(Object Obj, uint32 Delay){
Entry->RoundNr = RoundNr + Delay;
Entry->Previous = -1;
Entry->Next = CronHashTable[Obj.ObjectID % NARRAY(CronHashTable)];
+ CronHashTable[Obj.ObjectID % NARRAY(CronHashTable)] = Position;
if(Entry->Next != -1){
CronEntry.at(Entry->Next)->Previous = Position;
}
@@ -370,7 +371,7 @@ static void ReadMapConfig(void){
}
char Identifier[MAX_IDENT_LENGTH];
- strcpy(Identifier, Script.readIdentifier());
+ strcpy(Identifier, Script.getIdentifier());
Script.readSymbol('=');
if(strcmp(Identifier, "sectorxmin") == 0){
@@ -807,6 +808,7 @@ void LoadObjects(TReadScriptFile *Script, TWriteStream *Stream, bool Skip){
}
Depth -= 1;
+ ProcessObjects = false;
if(Depth <= 0){
break;
}
@@ -868,10 +870,6 @@ void LoadObjects(TReadStream *Stream, Object Con){
ObjectType ObjType(TypeID);
if(ObjType.isBodyContainer()){
- // TODO(fusion): I couldn't find if a creature's body containers
- // are properly initialized or if we need to create them here.
- // Either way we should come back to this function to check it
- // is working properly.
Obj = GetContainerObject(Con, (TypeID - 1));
}else{
Obj = AppendObject(Con, ObjType);
@@ -895,14 +893,14 @@ void LoadObjects(TReadStream *Stream, Object Con){
char String[4096];
Stream->readString(String, sizeof(String));
Obj.setAttribute((INSTANCEATTRIBUTE)Attribute, AddDynamicString(String));
- }else if(Attribute != REMAININGEXPIRETIME){
- uint32 Value = Stream->readQuad();
- Obj.setAttribute((INSTANCEATTRIBUTE)Attribute, Value);
- }else{
+ }else if(Attribute == REMAININGEXPIRETIME){
uint32 Value = Stream->readQuad();
if(Value != 0){
CronChange(Obj, (int)Value);
}
+ }else{
+ uint32 Value = Stream->readQuad();
+ Obj.setAttribute((INSTANCEATTRIBUTE)Attribute, Value);
}
}else{
Obj = NONE;
@@ -1037,7 +1035,7 @@ void LoadMap(void){
int SectorX, SectorY, SectorZ;
if(sscanf(DirEntry->d_name, "%d-%d-%d.sec", &SectorX, &SectorY, &SectorZ) == 3){
- snprintf(FileName, sizeof(FileName), "%s/%s", SAVEPATH, DirEntry->d_name);
+ snprintf(FileName, sizeof(FileName), "%s/%s", MAPPATH, DirEntry->d_name);
LoadSector(FileName, SectorX, SectorY, SectorZ);
SectorCounter += 1;
}
@@ -1157,8 +1155,11 @@ void SaveObjects(TReadStream *Stream, TWriteScriptFile *Script){
Script->writeNumber(TypeID);
}else{
- Depth -= 1;
Script->writeText("}");
+ Depth -= 1;
+ if(Depth <= 0){
+ break;
+ }
}
ProcessObjects = false;
@@ -1924,9 +1925,8 @@ void ChangeObject(Object Obj, ObjectType NewType){
}
if(OldType.getFlag(EXPIRESTOP)){
- uint32 Value = Obj.getAttribute(SAVEDEXPIRETIME);
- if(Value != 0){
- Delay = (int)Value;
+ if(Obj.getAttribute(SAVEDEXPIRETIME) != 0){
+ Delay = (int)Obj.getAttribute(SAVEDEXPIRETIME);
}
}
@@ -1938,7 +1938,7 @@ void ChangeObject(Object Obj, ObjectType NewType){
Obj.setObjectType(NewType);
if(NewType.getFlag(CUMULATIVE)){
- if(Amount == 0){
+ if(Amount <= 0){
Amount = 1;
}
Obj.setAttribute(AMOUNT, Amount);
@@ -1952,7 +1952,7 @@ void ChangeObject(Object Obj, ObjectType NewType){
if(NewType.getFlag(LIQUIDPOOL)){
if(Obj.getAttribute(POOLLIQUIDTYPE) == 0){
- Obj.setAttribute(POOLLIQUIDTYPE, 1); // LIQUID_TYPE_NONE (?)
+ Obj.setAttribute(POOLLIQUIDTYPE, LIQUID_WATER);
}
}
@@ -2014,7 +2014,7 @@ void PlaceObject(Object Obj, Object Con, bool Append){
}
ObjectType ConType = Con.getObjectType();
- if(!ConType.getFlag(CONTAINER) || !ConType.getFlag(CHEST)){
+ if(!ConType.getFlag(CONTAINER) && !ConType.getFlag(CHEST)){
error("PlaceObject: Con (%d) ist kein Container.\n", ConType.TypeID);
return;
}
diff --git a/src/moveuse.cc b/src/moveuse.cc
index 6c83dcc..cd03bd8 100644
--- a/src/moveuse.cc
+++ b/src/moveuse.cc
@@ -2543,7 +2543,7 @@ void LoadParameters(TReadScriptFile *Script, int *Parameters, int NumberOfParame
}
case MOVEUSE_PARAMETER_RIGHT:{
- const char *Right = Script->getIdentifier();
+ const char *Right = Script->readIdentifier();
if(strcmp(Right, "premium_account") == 0){
Parameters[i] = PREMIUM_ACCOUNT;
}else if(strcmp(Right, "special_moveuse") == 0){
@@ -2968,22 +2968,24 @@ void LoadDataBase(void){
if(strcmp(Identifier, "begin") == 0){
Script.readString();
+ Script.nextToken();
continue;
}else if(strcmp(Identifier, "end") == 0){
+ Script.nextToken();
continue;
}
int EventType;
if(strcmp(Identifier, "use") == 0){
- EventType = MOVEUSE_EVENT_MULTIUSE;
+ EventType = MOVEUSE_EVENT_USE;
}else if(strcmp(Identifier, "multiuse") == 0){
EventType = MOVEUSE_EVENT_MULTIUSE;
}else if(strcmp(Identifier, "movement") == 0){
- EventType = MOVEUSE_EVENT_MULTIUSE;
+ EventType = MOVEUSE_EVENT_MOVEMENT;
}else if(strcmp(Identifier, "collision") == 0){
- EventType = MOVEUSE_EVENT_MULTIUSE;
+ EventType = MOVEUSE_EVENT_COLLISION;
}else if(strcmp(Identifier, "separation") == 0){
- EventType = MOVEUSE_EVENT_MULTIUSE;
+ EventType = MOVEUSE_EVENT_SEPARATION;
}else{
Script.error("Unknown event type");
}
diff --git a/src/objects.cc b/src/objects.cc
index 94edca4..18fe01c 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -98,8 +98,8 @@ static FLAG InstanceAttributeFlags[18] = {
};
static const char FlagNames[66][30] = {
- "Bank" // BANK
- "Clip" // CLIP
+ "Bank", // BANK
+ "Clip", // CLIP
"Bottom", // BOTTOM
"Top", // TOP
"Container", // CONTAINER
diff --git a/src/operate.cc b/src/operate.cc
index 7827d0d..14fab8a 100644
--- a/src/operate.cc
+++ b/src/operate.cc
@@ -1204,6 +1204,7 @@ Object Create(Object Con, ObjectType Type, uint32 Value){
SetObject(Obj, ObjectType(Position), 0);
}
+ Creature->CrObject = Obj;
Creature->NotifyCreate();
}
@@ -1535,7 +1536,7 @@ void Merge(uint32 CreatureID, Object Obj, Object Dest, int Count, Object Ignore)
}
void Change(Object Obj, ObjectType NewType, uint32 Value){
- if(Obj.exists()){
+ if(!Obj.exists()){
error("Change: Übergebenes Objekt existiert nicht.\n");
throw ERROR;
}
@@ -1563,7 +1564,7 @@ void Change(Object Obj, ObjectType NewType, uint32 Value){
if(NewType.getFlag(CONTAINER)){
int ObjectCount = CountObjectsInContainer(Obj);
int NewCapacity = (int)NewType.getAttribute(CAPACITY);
- if(ObjectCount < NewCapacity){
+ if(ObjectCount > NewCapacity){
error("Change: Zielobjekt %d für %d ist kleinerer Container.\n",
NewType.TypeID, OldType.TypeID);
throw EMPTYCONTAINER;
@@ -2751,18 +2752,29 @@ void ProcessCronSystem(void){
break;
}
- ObjectType ObjType = Obj.getObjectType();
- ObjectType ExpireTarget = (int)ObjType.getAttribute(EXPIRETARGET);
- if(ObjType.getFlag(CONTAINER)){
- int Remainder = 0;
- if(!ExpireTarget.isMapContainer() && ExpireTarget.getFlag(CONTAINER)){
- Remainder = (int)ExpireTarget.getAttribute(CAPACITY);
+ try{
+ ObjectType ObjType = Obj.getObjectType();
+ ObjectType ExpireTarget = (int)ObjType.getAttribute(EXPIRETARGET);
+ if(ObjType.getFlag(CONTAINER)){
+ int Remainder = 0;
+ if(!ExpireTarget.isMapContainer() && ExpireTarget.getFlag(CONTAINER)){
+ Remainder = (int)ExpireTarget.getAttribute(CAPACITY);
+ }
+
+ Empty(Obj, Remainder);
}
- Empty(Obj, Remainder);
- }
+ Change(Obj, ExpireTarget, 0);
+ }catch(RESULT r){
+ error("ProcessCronSystem: Exception %d beim Verwesen von %d.\n",
+ r, Obj.getObjectType().TypeID);
- Change(Obj, ExpireTarget, 0);
+ try{
+ Delete(Obj, -1);
+ }catch(RESULT r){
+ error("ProcessCronSystem: Kann Objekt nicht löschen - Exception %d.\n", r);
+ }
+ }
}
}
diff --git a/src/query.cc b/src/query.cc
index 5da269d..5384b79 100644
--- a/src/query.cc
+++ b/src/query.cc
@@ -846,7 +846,7 @@ int TQueryManagerConnection::reportStatement(uint32 ReporterID, const char *Play
}
}
- this->sendQuad((uint32)NonZeroStatements);
+ this->sendWord((uint16)NonZeroStatements);
for(int i = 0; i < NumberOfStatements; i += 1){
if(StatementIDs[i] != 0){
this->sendQuad(StatementIDs[i]);
@@ -1253,7 +1253,7 @@ int TQueryManagerConnection::createPlayerlist(int NumberOfPlayers, const char **
}
int TQueryManagerConnection::logKilledCreatures(int NumberOfRaces, const char **Names,
- int *KilledPlayers,int *KilledCreatures){
+ int *KilledPlayers, int *KilledCreatures){
this->prepareQuery(48);
this->sendWord((uint16)NumberOfRaces);
diff --git a/src/strings.cc b/src/strings.cc
index d0bfe70..95471d6 100644
--- a/src/strings.cc
+++ b/src/strings.cc
@@ -183,8 +183,8 @@ void CleanupDynamicStrings(void){
if(Block->EntryType[i] == DYNAMIC_STRING_DELETED){
int StringOffset = Block->StringOffset[i];
char *String = &Block->Text[StringOffset];
- int StringLen = (int)strlen(String);
- int StringEnd = StringOffset + StringLen + 1;
+ int StringSize = (int)strlen(String) + 1;
+ int StringEnd = StringOffset + StringSize;
Block->EntryType[i] = DYNAMIC_STRING_FREE;
if(StringEnd > DynamicBlockSize){
@@ -193,13 +193,14 @@ void CleanupDynamicStrings(void){
}
if(StringEnd < DynamicBlockSize){
- memmove(String, String + StringEnd, DynamicBlockSize - StringEnd);
+ memmove(String, String + StringSize, DynamicBlockSize - StringEnd);
}
for(int j = 0; j < DynamicBlockEntries; j += 1){
if(Block->EntryType[j] != DYNAMIC_STRING_FREE
- && Block->StringOffset[j] > StringOffset){
- Block->StringOffset[j] -= StringLen + 1;
+ && Block->StringOffset[j] > StringOffset){
+ ASSERT(Block->StringOffset[j] >= StringEnd);
+ Block->StringOffset[j] -= StringSize;
}
}
}
diff --git a/src/threads.cc b/src/threads.cc
index a7acb41..fbced09 100644
--- a/src/threads.cc
+++ b/src/threads.cc
@@ -132,12 +132,24 @@ Semaphore::Semaphore(int Value){
}
Semaphore::~Semaphore(void){
- if(pthread_mutex_destroy(&this->mutex) != 0){
- error("Semaphore::~Semaphore: Kann Mutex nicht freigeben.\n");
- }
-
- if(pthread_cond_destroy(&this->condition) != 0){
- error("Semaphore::~Semaphore: Kann Wartebedingung nicht freigeben.\n");
+ // IMPORTANT(fusion): Due to how initialization is rolled out, `exit` may be
+ // called after threads are spawned but before `ExitAll` is registered as an
+ // exit handler. This means such threads may still be running or left global
+ // semaphores in an inconsistent state if abruptly terminated. Either way,
+ // they are still considered "in use".
+ // In this case, calling `destroy` on either mutex or condition variable is
+ // undefined behaviour as per the manual but the actual implementation would
+ // fail on `mutex_destroy` with `EBUSY` and hang on `cond_destroy`.
+ // The temporary solution is to check the result from `mutex_destroy` before
+ // attempting to call `cond_destroy` to avoid hanging at exit.
+
+ int ErrorCode;
+ if((ErrorCode = pthread_mutex_destroy(&this->mutex)) != 0){
+ error("Semaphore::~Semaphore: Kann Mutex nicht freigeben: (%d) %s.\n",
+ ErrorCode, strerrordesc_np(ErrorCode));
+ }else if((ErrorCode = pthread_cond_destroy(&this->condition)) != 0){
+ error("Semaphore::~Semaphore: Kann Wartebedingung nicht freigeben: (%d) %s.\n",
+ ErrorCode, strerrordesc_np(ErrorCode));
}
}
diff --git a/src/writer.cc b/src/writer.cc
index dbe0cb0..093711e 100644
--- a/src/writer.cc
+++ b/src/writer.cc
@@ -146,11 +146,13 @@ void Log(const char *ProtocolName, const char *Text, ...){
if(Line[0] != 0){
int LineEnd = (int)strlen(Line);
- if(LineEnd < (int)(sizeof(Line) - 1)){
- Line[LineEnd] = '\n';
- Line[LineEnd + 1] = 0;
- }else{
- Line[LineEnd - 1] = '\n';
+ if(Line[LineEnd - 1] != '\n'){
+ if(LineEnd < (int)(sizeof(Line) - 1)){
+ Line[LineEnd] = '\n';
+ Line[LineEnd + 1] = 0;
+ }else{
+ Line[LineEnd - 1] = '\n';
+ }
}
if(ProtocolThread != INVALID_THREAD_HANDLE){