diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-07-16 17:22:39 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-07-16 17:22:39 -0300 |
| commit | 6d498fe17dab48cfd8fc8a4da62a573b799e10ba (patch) | |
| tree | 1c8d55bd907b35abf014385dbedb90b2ae98f96a | |
| parent | e65dc561acab7a9e49f59777f16cb040c574c64d (diff) | |
| download | game-6d498fe17dab48cfd8fc8a4da62a573b799e10ba.tar.gz game-6d498fe17dab48cfd8fc8a4da62a573b799e10ba.zip | |
fix most init and parsing bugs + critical bug with dynamic strings
With an appropriate query manager, the server now starts up and
seems to work but won't properly handle connections until we fix
the problem with threads and signals described in `TODO.md`.
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | src/communication.cc | 32 | ||||
| -rw-r--r-- | src/containers.hh | 4 | ||||
| -rw-r--r-- | src/crmain.cc | 11 | ||||
| -rw-r--r-- | src/crnonpl.cc | 31 | ||||
| -rw-r--r-- | src/crplayer.cc | 5 | ||||
| -rw-r--r-- | src/crypto.cc | 15 | ||||
| -rw-r--r-- | src/crypto.hh | 4 | ||||
| -rw-r--r-- | src/houses.cc | 40 | ||||
| -rw-r--r-- | src/map.cc | 34 | ||||
| -rw-r--r-- | src/moveuse.cc | 12 | ||||
| -rw-r--r-- | src/objects.cc | 4 | ||||
| -rw-r--r-- | src/operate.cc | 34 | ||||
| -rw-r--r-- | src/query.cc | 4 | ||||
| -rw-r--r-- | src/strings.cc | 11 | ||||
| -rw-r--r-- | src/threads.cc | 24 | ||||
| -rw-r--r-- | src/writer.cc | 12 |
17 files changed, 165 insertions, 114 deletions
@@ -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){ @@ -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){ |
