diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-05-25 22:37:57 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-05-25 22:37:57 -0300 |
| commit | ad8213f35523cbb07f418ae275af448a47cc0288 (patch) | |
| tree | 4d77b8ff2b44461734b5a2cbaad5402b4a94736f | |
| parent | 5f883a80175a4cc9abda9d647a6a0d73bda84878 (diff) | |
| download | game-ad8213f35523cbb07f418ae275af448a47cc0288.tar.gz game-ad8213f35523cbb07f418ae275af448a47cc0288.zip | |
linux Makefile + fix most compilation problems
I wanted to see if the compiler had any problems with the code
so far and added a few stub definitions so that each file would
properly compile. We still fail when linking but we're able to to
find and fix compile time errors.
| -rw-r--r-- | Makefile | 75 | ||||
| -rw-r--r-- | build.bat | 4 | ||||
| -rw-r--r-- | reference/enums.hh | 8 | ||||
| -rw-r--r-- | src/common.hh | 22 | ||||
| -rw-r--r-- | src/config.cc | 13 | ||||
| -rw-r--r-- | src/containers.hh | 8 | ||||
| -rw-r--r-- | src/crcombat.cc | 2 | ||||
| -rw-r--r-- | src/creature.cc | 5 | ||||
| -rw-r--r-- | src/creature.hh | 5 | ||||
| -rw-r--r-- | src/enums.hh | 8 | ||||
| -rw-r--r-- | src/main.cc | 21 | ||||
| -rw-r--r-- | src/map.cc | 999 | ||||
| -rw-r--r-- | src/map.hh | 43 | ||||
| -rw-r--r-- | src/monster.hh | 2 | ||||
| -rw-r--r-- | src/objects.cc | 42 | ||||
| -rw-r--r-- | src/objects.hh | 1 | ||||
| -rw-r--r-- | src/player.cc | 2 | ||||
| -rw-r--r-- | src/player.hh | 1 | ||||
| -rw-r--r-- | src/script.cc | 29 | ||||
| -rw-r--r-- | src/script.hh | 26 | ||||
| -rw-r--r-- | src/shm.cc | 18 | ||||
| -rw-r--r-- | src/skill.cc | 10 | ||||
| -rw-r--r-- | src/stubs.hh | 64 | ||||
| -rw-r--r-- | src/thread.cc | 2 | ||||
| -rw-r--r-- | src/thread.hh | 2 | ||||
| -rw-r--r-- | src/time.cc | 8 | ||||
| -rw-r--r-- | src/util.cc | 17 |
27 files changed, 902 insertions, 535 deletions
diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..18037c1 --- /dev/null +++ b/Makefile @@ -0,0 +1,75 @@ +SRCDIR = src +BUILDDIR = build +OUTPUTEXE = game + +CC = g++ +CFLAGS = -m64 -fno-strict-aliasing -Wall -Wextra -Wno-unused-parameter -Wno-format-truncation -std=c++11 -DOS_LINUX=1 -DARCH_X64=1 -D_CRT_SECURE_NO_WARNINGS=1 +LFLAGS = -Wl,-t + +DEBUG ?= 0 +ifneq ($(DEBUG), 0) + CFLAGS += -g -O0 +else + CFLAGS += -O2 +endif + +HEADERS = $(SRCDIR)/common.hh $(SRCDIR)/config.hh $(SRCDIR)/connection.hh $(SRCDIR)/containers.hh $(SRCDIR)/creature.hh $(SRCDIR)/enums.hh $(SRCDIR)/map.hh $(SRCDIR)/monster.hh $(SRCDIR)/objects.hh $(SRCDIR)/player.hh $(SRCDIR)/script.hh $(SRCDIR)/skill.hh $(SRCDIR)/thread.hh + +$(BUILDDIR)/$(OUTPUTEXE): $(BUILDDIR)/config.obj $(BUILDDIR)/crcombat.obj $(BUILDDIR)/creature.obj $(BUILDDIR)/main.obj $(BUILDDIR)/map.obj $(BUILDDIR)/objects.obj $(BUILDDIR)/player.obj $(BUILDDIR)/script.obj $(BUILDDIR)/shm.obj $(BUILDDIR)/skill.obj $(BUILDDIR)/thread.obj $(BUILDDIR)/time.obj $(BUILDDIR)/util.obj + $(CC) $(CFLAGS) $(LFLAGS) -o $@ $^ + +$(BUILDDIR)/config.obj: $(SRCDIR)/config.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/crcombat.obj: $(SRCDIR)/crcombat.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/creature.obj: $(SRCDIR)/creature.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/main.obj: $(SRCDIR)/main.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/map.obj: $(SRCDIR)/map.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/objects.obj: $(SRCDIR)/objects.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/player.obj: $(SRCDIR)/player.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/script.obj: $(SRCDIR)/script.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/shm.obj: $(SRCDIR)/shm.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/skill.obj: $(SRCDIR)/skill.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/thread.obj: $(SRCDIR)/thread.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/time.obj: $(SRCDIR)/time.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +$(BUILDDIR)/util.obj: $(SRCDIR)/util.cc $(HEADERS) + @mkdir -p $(@D) + $(CC) -c $(CFLAGS) -o $@ $< + +.PHONY: clean +clean: + @rm -r $(BUILDDIR) @@ -11,8 +11,8 @@ set OUTPUTPDB=out.pdb set INSTALLDIR=..\bin @REM set SRC="../src/creature.cc" "../src/main.cc" "../src/player.cc" "../src/skill.cc" -set SRC="../src/script.cc" "../src/util.cc" -cl -W3 -WX -Zi -we4244 -we4456 -we4457 -wd4996 -std:c++14 -utf-8 -MP8 -DBUILD_DEBUG=1 -DARCH_X64=1 -D_CRT_SECURE_NO_WARNINGS=1 -Fe:"%OUTPUTEXE%" -Fd:"%OUTPUTPDB%" %* %SRC% /link -subsystem:console -incremental:no -opt:ref -dynamicbase "ws2_32.lib" +set SRC="../src/map.cc" "../src/script.cc" "../src/util.cc" +cl -W3 -WX -Zi -we4244 -we4456 -we4457 -wd4996 -std:c++14 -EHsc -utf-8 -MP8 -DBUILD_DEBUG=1 -DARCH_X64=1 -D_CRT_SECURE_NO_WARNINGS=1 -Fe:"%OUTPUTEXE%" -Fd:"%OUTPUTPDB%" %* %SRC% /link -subsystem:console -incremental:no -opt:ref -dynamicbase "ws2_32.lib" if errorlevel 1 exit /b errorlevel diff --git a/reference/enums.hh b/reference/enums.hh index 5e045f3..6a84666 100644 --- a/reference/enums.hh +++ b/reference/enums.hh @@ -186,11 +186,3 @@ enum SITUATION: int { BUSY=3, VANISH=4 }; - -enum BloodType: int { - BT_BLOOD=0, - BT_SLIME=1, - BT_BONES=2, - BT_FIRE=3, - BT_ENERGY=4 -}; diff --git a/src/common.hh b/src/common.hh index 219ff22..e113e41 100644 --- a/src/common.hh +++ b/src/common.hh @@ -19,7 +19,7 @@ typedef uintptr_t uintptr; typedef size_t usize; #define STATIC_ASSERT(expr) static_assert((expr), "static assertion failed: " #expr) -#define NARRAY(arr) (sizeof(arr) / sizeof(arr[0])) +#define NARRAY(arr) (int)(sizeof(arr) / sizeof(arr[0])) #define ISPOW2(x) ((x) != 0 && ((x) & ((x) - 1)) == 0) #define KB(x) ((usize)(x) << 10) #define MB(x) ((usize)(x) << 20) @@ -80,10 +80,9 @@ STATIC_ASSERT(sizeof(int) == 4); // be too difficult either. Overall this design is outdated and should be reviewed. // Nevertheless, we should focus on getting it working as intended, on the target // platform (which is Linux 32-bits) before attempting to refine it. -//STATIC_ASSERT(OS_LINUX); -//#include <sys/types.h> -typedef int pid_t; -// +STATIC_ASSERT(OS_LINUX); +#include <errno.h> +#include <unistd.h> // shm.cc // ============================================================================= @@ -120,7 +119,6 @@ void ExitSHMExtern(void); // ============================================================================= extern uint32 RoundNr; extern uint32 ServerMilliseconds; - void GetRealTime(int *Hour, int *Minute); void GetTime(int *Hour, int *Minute); void GetDate(int *Year, int *Cycle, int *Day); @@ -134,8 +132,8 @@ typedef void TErrorFunction(const char *Text); typedef void TPrintFunction(int Level, const char *Text); void SetErrorFunction(TErrorFunction *Function); void SetPrintFunction(TPrintFunction *Function); -void error(char *Text, ...) ATTR_PRINTF(1, 2); -void print(int Level, char *Text, ...) ATTR_PRINTF(1, 2); +void error(const char *Text, ...) ATTR_PRINTF(1, 2); +void print(int Level, const char *Text, ...) ATTR_PRINTF(2, 3); bool isSpace(int c); bool isAlpha(int c); @@ -145,7 +143,7 @@ int toLower(int c); int toUpper(int c); char *strLower(char *s); char *strUpper(char *s); -int stricmp(const char *s1, const char *s2, int Pos); +int stricmp(const char *s1, const char *s2, int Max = INT_MAX); char *findFirst(char *s, char c); char *findLast(char *s, char c); @@ -166,8 +164,6 @@ struct TReadBuffer: TReadStream { // REGULAR FUNCTIONS // ========================================================================= TReadBuffer(uint8 *Data, int Size); - TReadBuffer(const TWriteBuffer &WriteBuffer) - : TReadBuffer(WriteBuffer.Data, WriteBuffer.Size) {} // VIRTUAL FUNCTIONS // ========================================================================= @@ -207,7 +203,7 @@ struct TWriteBuffer: TWriteStream { void writeByte(uint8 Byte) override; void writeWord(uint16 Word) override; void writeQuad(uint32 Quad) override; - void writeBytes(uint8 *Buffer, int Count) override; + void writeBytes(const uint8 *Buffer, int Count) override; // DATA // ========================================================================= @@ -227,7 +223,7 @@ struct TDynamicWriteBuffer: TWriteBuffer { void writeByte(uint8 Byte) override; void writeWord(uint16 Word) override; void writeQuad(uint32 Quad) override; - void writeBytes(uint8 *Buffer, int Count) override; + void writeBytes(const uint8 *Buffer, int Count) override; // TODO(fusion): Appended virtual functions. These are not in the base class // VTABLE which can be problematic if we intend to use polymorphism, although diff --git a/src/config.cc b/src/config.cc index e3e3c85..d08b791 100644 --- a/src/config.cc +++ b/src/config.cc @@ -1,4 +1,7 @@ #include "config.hh" +#include "script.hh" + +#include "stubs.hh" char BINPATH[4096]; char DATAPATH[4096]; @@ -254,16 +257,16 @@ void ReadConfig(void){ }else if(strcmp(Identifier, "querymanager") == 0){ Script.readSymbol('{'); do{ - if(NumberOfQueryManager >= NARRAY(QUERY_MANAGER)){ + if(NumberOfQueryManagers >= NARRAY(QUERY_MANAGER)){ Script.error("Cannot handle more query managers"); } Script.readSymbol('('); - strcpy(QUERY_MANAGER[NumberOfQueryManager].Host, Script.readString()); + strcpy(QUERY_MANAGER[NumberOfQueryManagers].Host, Script.readString()); Script.readSymbol(','); - QUERY_MANAGER[NumberOfQueryManager].Port = Script.readNumber(); + QUERY_MANAGER[NumberOfQueryManagers].Port = Script.readNumber(); Script.readSymbol(','); - strcpy(QUERY_MANAGER[NumberOfQueryManager].Password, Script.readString()); - DisguisePassword(QUERY_MANAGER[NumberOfQueryManager].Password, PasswordKey); + strcpy(QUERY_MANAGER[NumberOfQueryManagers].Password, Script.readString()); + DisguisePassword(QUERY_MANAGER[NumberOfQueryManagers].Password, PasswordKey); Script.readSymbol(')'); NumberOfQueryManagers += 1; }while(Script.readSpecial() != '}'); diff --git a/src/containers.hh b/src/containers.hh index 8000d3c..d79946c 100644 --- a/src/containers.hh +++ b/src/containers.hh @@ -75,7 +75,7 @@ struct vector{ increment = this->space; } - T *entry = new[this->space + increment]; + T *entry = new T[this->space + increment]; for(int i = this->min; i <= this->max; i += 1){ int old_index = i - this->start; int new_index = old_index; @@ -207,7 +207,7 @@ struct priority_queue{ // priority_queue<uint32, uint32> ToDoQueue(5000, 1000); // priority_queue<uint32, TAttackWave*> AttackWaveQueue(100, 100); -typedef<typename T> +template<typename T> struct matrix{ // REGULAR FUNCTIONS // ========================================================================= @@ -264,7 +264,7 @@ struct matrix{ T *entry; }; -typedef<typename T> +template<typename T> struct matrix3d{ // REGULAR FUNCTIONS // ========================================================================= @@ -298,7 +298,7 @@ struct matrix3d{ } matrix3d(int xmin, int xmax, int ymin, int ymax, int zmin, int zmax, T init) - : matrix(xmin, xmax, ymin, ymax, zmin, zmax) { + : matrix3d(xmin, xmax, ymin, ymax, zmin, zmax) { int count = this->dx * this->dy * this->dz; for(int i = 0; i < count; i += 1){ this->entry[i] = init; diff --git a/src/crcombat.cc b/src/crcombat.cc index 6b0df86..854ffe2 100644 --- a/src/crcombat.cc +++ b/src/crcombat.cc @@ -10,7 +10,7 @@ TCombat::TCombat(void){ this->ChaseMode = 0; this->SecureMode = 1; this->AttackDest = 0; - this->Following; + this->Following = false; this->Shield = NONE; this->Close = NONE; this->Missile = NONE; diff --git a/src/creature.cc b/src/creature.cc index e2cb5ce..549543e 100644 --- a/src/creature.cc +++ b/src/creature.cc @@ -1,13 +1,14 @@ #include "creature.hh" - #include "enums.hh" +#include "stubs.hh" + TCreature::TCreature(void) : TSkillBase(), Combat(), ToDoList(0, 20, 10) { - this->Master = this; + this->Combat.Master = this; this->ID = 0; this->NextHashEntry = NULL; this->NextChainCreature = 0; diff --git a/src/creature.hh b/src/creature.hh index a6f7678..423ad8c 100644 --- a/src/creature.hh +++ b/src/creature.hh @@ -4,6 +4,7 @@ #include "common.hh" #include "connection.hh" #include "containers.hh" +#include "map.hh" #include "skill.hh" struct TCreature; @@ -18,6 +19,7 @@ struct TCombat{ // REGULAR FUNCTIONS // ========================================================================= TCombat(void); + void CheckCombatValues(void); // DATA // ========================================================================= @@ -108,7 +110,8 @@ struct TOutfit{ struct TCreature: TSkillBase { // REGULAR FUNCTIONS // ========================================================================= - // TODO + TCreature(void); + int Damage(TCreature *Attacker, int Damage, int DamageType); // VIRTUAL FUNCTIONS // ========================================================================= diff --git a/src/enums.hh b/src/enums.hh index 5124936..ea0d87a 100644 --- a/src/enums.hh +++ b/src/enums.hh @@ -6,6 +6,14 @@ // TODO(fusion): Probably cleanup these names? Prefix values? Its crazy that // there are no collision problems (possibly yet). +enum BloodType: int { + BT_BLOOD = 0, + BT_SLIME = 1, + BT_BONES = 2, + BT_FIRE = 3, + BT_ENERGY = 4, +}; + enum CreatureType: int { PLAYER = 0, MONSTER = 1, diff --git a/src/main.cc b/src/main.cc index 736f2f2..e0cf156 100644 --- a/src/main.cc +++ b/src/main.cc @@ -2,8 +2,12 @@ #include "config.hh" #include "map.hh" +#include "stubs.hh" + #include <signal.h> +#include <sys/stat.h> #include <sys/time.h> +#include <fstream> static bool BeADaemon = false; static bool Reboot = false; @@ -62,6 +66,7 @@ static void DefaultHandler(int signr){ SaveMapOn = (signr == SIGQUIT) || (signr == SIGTERM) || (signr == SIGPWR); if(signr == SIGTERM){ + int Hour, Minute; GetRealTime(&Hour, &Minute); RebootTime = (Hour * 60 + Minute + 6) % 1440; CloseGame(); @@ -114,7 +119,7 @@ static void SigAlarmHandler(int signr){ SigAlarmCounter += 1; struct itimerval new_timer = {}; - strict itimerval old_timer = {}; + struct itimerval old_timer = {}; new_timer.it_value.tv_usec = Beat * 1000; setitimer(ITIMER_REAL, &new_timer, &old_timer); } @@ -122,12 +127,12 @@ static void SigAlarmHandler(int signr){ static void InitTime(void){ SigAlarmCounter = 0; handler(SIGALRM, SigAlarmHandler); - SigAlarmHandler(SIGALARM); + SigAlarmHandler(SIGALRM); } static void ExitTime(void){ struct itimerval new_timer = {}; - strict itimerval old_timer = {}; + struct itimerval old_timer = {}; setitimer(ITIMER_REAL, &new_timer, &old_timer); handler(SIGALRM, SIG_IGN); } @@ -138,7 +143,7 @@ static void UnlockGame(void){ strcpy(FileName, SAVEPATH); strcat(FileName, "/game.pid"); - std::ifstream InputFile(Filename, std::ios_base::in); + std::ifstream InputFile(FileName, std::ios_base::in); if(!InputFile.fail()){ int Pid; InputFile >> Pid; @@ -156,7 +161,7 @@ static void LockGame(void){ strcat(FileName, "/game.pid"); { - std::ifstream InputFile(Filename, std::ios_base::in); + std::ifstream InputFile(FileName, std::ios_base::in); if(!InputFile.fail()){ int Pid; InputFile >> Pid; @@ -200,7 +205,7 @@ void LoadWorldConfig(void){ WorldType = NORMAL; RebootTime = 6 * 60; // minutes strcpy(GameAddress, "127.0.0.1"); // I KNOW - Port = 7171; + GamePort = 7171; MaxPlayers = 1000; PremiumPlayerBuffer = 100; MaxNewbies = 200; @@ -292,7 +297,7 @@ static void AdvanceGame(int Delay){ if(CreatureTimeCounter >= 1750){ CreatureTimeCounter -= 1000; - ProcessCreature(); + ProcessCreatures(); } if(CronTimeCounter >= 1500){ @@ -315,7 +320,7 @@ static void AdvanceGame(int Delay){ ProcessMonsterhomes(); ProcessMonsterRaids(); ProcessCommunicationControl(); - ProcessReaderThreadReplies(RefreshSector,SendMails); + ProcessReaderThreadReplies(RefreshSector, SendMails); ProcessWriterThreadReplies(); ProcessCommand(); @@ -1,5 +1,12 @@ #include "map.hh" #include "containers.hh" +#include "config.hh" +#include "enums.hh" +#include "script.hh" + +#include "stubs.hh" + +#include <dirent.h> // NOTE(fusion): This is used by hash table entries and sectors to tell whether // they're currently loaded or swapped out to disk. @@ -57,25 +64,387 @@ static int CronEntries; static vector<TDepotInfo> DepotInfo(0, 4, 5); static int Depots; -static vector<TMask> Mark(0, 4, 5); +static vector<TMark> Mark(0, 4, 5); static int Marks; static TDynamicWriteBuffer HelpBuffer(0x10000); -// Map Init/Exit and Helpers +// Object // ============================================================================= -static void SwapObject(TWriteBinaryFile *File, Object Obj, uint32 FileNumber){ +bool Object::exists(void){ + if(*this == NONE){ + return false; + } + + uint32 EntryIndex = this->ObjectID & HashTableMask; + if(HashTableType[EntryIndex] == STATUS_SWAPPED){ + UnswapSector((uint32)((uintptr)HashTableData[EntryIndex])); + } + + return HashTableType[EntryIndex] == STATUS_LOADED + && HashTableData[EntryIndex]->ObjectID == this->ObjectID; +} + +ObjectType Object::getObjectType(void){ + return AccessObject(*this)->Type; +} + +void Object::setObjectType(ObjectType Type){ + AccessObject(*this)->Type = Type; +} + +Object Object::getNextObject(void){ + return AccessObject(*this)->NextObject; +} + +void Object::setNextObject(Object NextObject){ + AccessObject(*this)->NextObject = NextObject; +} + +Object Object::getContainer(void){ + return AccessObject(*this)->Container; +} + +void Object::setContainer(Object Container){ + AccessObject(*this)->Container = Container; +} + +uint32 Object::getCreatureID(void){ + // TODO(fusion): We call `AccessObject` once in `getObjectType` then again + // after checking the TypeID, when we could call it once to check both type + // and access `Attributes[1]`. + + if(!this->getObjectType().isCreatureContainer()){ + error("Object::getCreatureID: Objekt ist keine Kreatur.\n"); + return 0; + } + + return AccessObject(*this)->Attributes[1]; +} + +uint32 Object::getAttribute(INSTANCEATTRIBUTE Attribute){ + ObjectType ObjType = this->getObjectType(); + int AttributeOffset = ObjType.getAttributeOffset(Attribute); + if(AttributeOffset == -1){ + error("Object::getAttribute: Flag für Attribut %d bei Objekttyp %d nicht gesetzt.\n", + Attribute, ObjType.TypeID); + return 0; + } + + if(AttributeOffset < 0 || AttributeOffset >= NARRAY(TObject::Attributes)){ + error("Object::getAttribute: Ungültiger Offset %d für Attribut %d bei Objekttyp %d.\n", + AttributeOffset, Attribute, ObjType.TypeID); + return 0; + } + + return AccessObject(*this)->Attributes[AttributeOffset]; +} + +void Object::setAttribute(INSTANCEATTRIBUTE Attribute, uint32 Value){ + ObjectType ObjType = this->getObjectType(); + int AttributeOffset = ObjType.getAttributeOffset(Attribute); + if(AttributeOffset == -1){ + error("Object::setAttribute: Flag für Attribut %d bei Objekttyp %d nicht gesetzt.\n", + Attribute, ObjType.TypeID); + return; + } + + if(AttributeOffset < 0 || AttributeOffset >= NARRAY(TObject::Attributes)){ + error("Object::setAttribute: Ungültiger Offset %d für Attribut %d bei Objekttyp %d.\n", + AttributeOffset, Attribute, ObjType.TypeID); + return; + } + + if(Value == 0){ + if(Attribute == AMOUNT || Attribute == POOLLIQUIDTYPE || Attribute == CHARGES){ + Value = 1; + }else if(Attribute == REMAININGUSES){ + Value = ObjType.getAttribute(TOTALUSES); + } + } + + AccessObject(*this)->Attributes[AttributeOffset] = Value; +} + +// Map Management +// ============================================================================= +static void ReadMapConfig(void){ + OBCount = 0xA0000; + SectorXMin = 1000; + SectorXMax = 1015; + SectorYMin = 1000; + SectorYMax = 1015; + SectorZMin = 0; + SectorZMax = 15; + RefreshedCylinders = 1; + NewbieStartPositionX = 0; + NewbieStartPositionY = 0; + NewbieStartPositionZ = 0; + VeteranStartPositionX = 0; + VeteranStartPositionY = 0; + VeteranStartPositionZ = 0; + HashTableSize = 0x100000; + Marks = 0; + Depots = 0; + + char FileName[4096]; + snprintf(FileName, sizeof(FileName), "%s/map.dat", DATAPATH); + + TReadScriptFile Script; + Script.open(FileName); + while(true){ + Script.nextToken(); + if(Script.Token == ENDOFFILE){ + Script.close(); + break; + } + + char Identifier[MAX_IDENT_LENGTH]; + strcpy(Identifier, Script.readIdentifier()); + Script.readSymbol('='); + + if(strcmp(Identifier, "sectorxmin") == 0){ + SectorXMin = Script.readNumber(); + }else if(strcmp(Identifier, "sectorxmax") == 0){ + SectorXMax = Script.readNumber(); + }else if(strcmp(Identifier, "sectorymin") == 0){ + SectorYMin = Script.readNumber(); + }else if(strcmp(Identifier, "sectorymax") == 0){ + SectorYMax = Script.readNumber(); + }else if(strcmp(Identifier, "sectorzmin") == 0){ + SectorZMin = Script.readNumber(); + }else if(strcmp(Identifier, "sectorzmax") == 0){ + SectorZMax = Script.readNumber(); + }else if(strcmp(Identifier, "refreshedcylinders") == 0){ + RefreshedCylinders = Script.readNumber(); + }else if(strcmp(Identifier, "objects") == 0){ + HashTableSize = (uint32)Script.readNumber(); + }else if(strcmp(Identifier, "cachesize") == 0){ + OBCount = Script.readNumber(); + }else if(strcmp(Identifier, "depot") == 0){ + int DepotIndex = 0; + TDepotInfo TempInfo = {}; + Script.readSymbol('('); + DepotIndex = Script.readNumber(); + Script.readSymbol(','); + const char *Town = Script.readString(); + if(strlen(Town) >= NARRAY(TempInfo.Town)){ + Script.error("town name too long"); + } + strcpy(TempInfo.Town, Town); + Script.readSymbol(','); + TempInfo.Size = Script.readNumber(); + Script.readSymbol(')'); + *DepotInfo.at(DepotIndex) = TempInfo; + }else if(strcmp(Identifier, "mark") == 0){ + TMark TempMark = {}; + Script.readSymbol('('); + const char *Name = Script.readString(); + if(strlen(Name) >= NARRAY(TempMark.Name)){ + Script.error("mark name too long"); + } + strcpy(TempMark.Name, Name); + Script.readSymbol(','); + Script.readCoordinate(&TempMark.x, &TempMark.y, &TempMark.z); + Script.readSymbol(')'); + *Mark.at(Marks) = TempMark; + Marks += 1; + }else if(strcmp(Identifier, "newbiestart") == 0){ + Script.readCoordinate( + &NewbieStartPositionX, + &NewbieStartPositionY, + &NewbieStartPositionZ); + }else if(strcmp(Identifier, "veteranstart") == 0){ + Script.readCoordinate( + &VeteranStartPositionX, + &VeteranStartPositionY, + &VeteranStartPositionZ); + }else{ + // TODO(fusion): + //error("Unknown map configuration key \"%s\"", Identifier); + } + } + + // NOTE(fusion): If each sector is 32 x 32 tiles and the whole world is + // 65535 x 65535 tiles, then the maximum number of sectors is 65535 / 32 + // which is the 2047 used below. We should probably define these contants. + // Notice that sectors at the edge of the XY-plane are also considered + // invalid, probably to add some buffer to avoid wrapping or other types + // of problems. + + if(SectorXMin <= 0){ + throw "illegal value for SectorXMin"; + } + + if(SectorXMax >= 2047){ + throw "illegal value for SectorXMax"; + } + + if(SectorYMin <= 0){ + throw "illegal value for SectorYMin"; + } + + if(SectorYMax >= 2047){ + throw "illegal value for SectorYMax"; + } + + if(SectorZMin < 0){ + throw "illegal value for SectorZMin"; + } + + if(SectorZMax > 15){ + throw "illegal value for SectorZMax"; + } + + if(SectorXMin > SectorXMax){ + throw "SectorXMin is greater than SectorXMax"; + } + + if(SectorYMin > SectorYMax){ + throw "SectorYMin is greater than SectorYMax"; + } + + if(SectorZMin > SectorZMax){ + throw "SectorZMin is greater than SectorZMax"; + } + + // TODO(fusion): Just align up from whatever value we got? And use `ObjectsPerBlock`? + if(OBCount % 32768 != 0){ + throw "CacheSize must be a multiple of 32768"; + } + + if(OBCount <= 0){ + throw "illegal value for CacheSize"; + } + + OBCount /= 32768; + + if(HashTableSize <= 0){ + throw "illegal value for Objects"; + } + + if(!ISPOW2(HashTableSize)){ + throw "Objects must be a power of 2"; + } + + if(NewbieStartPositionX == 0){ + throw "no start position for newbies specified"; + } + + if(VeteranStartPositionX == 0){ + throw "no start position for veterans specified"; + } +} + +static void ResizeHashTable(void){ + uint32 OldSize = HashTableSize; + uint32 NewSize = OldSize * 2; + ASSERT(ISPOW2(OldSize)); + ASSERT(NewSize > OldSize); + + // TODO(fusion): See note below. + error("FATAL ERROR in ResizeHashTable: Resizing the object hash table is" + " currently disabled. You may increase `Objects` in the map config" + " from %d to %d, to achieve the same effect.", OldSize, NewSize); + abort(); + + error("INFO: HashTabelle zu klein. Größe wird verdoppelt auf %d.\n", NewSize); + + uint32 NewMask = NewSize - 1; + TObject **NewData = (TObject**)malloc(NewSize * sizeof(TObject*)); + uint8 *NewType = (uint8*)malloc(NewSize * sizeof(uint8)); + memset(NewType, 0, NewSize * sizeof(uint8)); + + // TODO(fusion): This rehash loop doesn't make a lot of sense. It wants to + // access all existing objects but doing so would cause all sectors to be + // swapped in at some time or another. This may be bad for performance but + // the real problem is that `UnswapSector` may swap out some other sector + // whose objects were already put into `NewType` and `NewData`, causing + // multiple object ids to reference the same `TObject`. + // Looking at some of the logs included with this executable, it doesn't + // look like this function was ever called which explains why it wasn't fixed + // earlier. + // It would be possible to do this rehashing without swapping anything out, + // if we stored ObjectID somewhere (maybe `HashTableData`). Doubling the size + // of the table means there are two possible indices for each previous entry, + // and we can only determine which one to actually move it with the ObjectID. + + NewType[0] = STATUS_PERMANENT; + NewData[0] = HashTableData[0]; + for(uint32 i = 1; i < NewSize; i += 1){ + if(HashTableType[i] != STATUS_FREE){ + if(HashTableType[i] == STATUS_SWAPPED){ + UnswapSector((uint32)((uintptr)HashTableData[i])); + } + + if(HashTableType[i] == STATUS_LOADED){ + TObject *Entry = HashTableData[i]; + NewType[Entry->ObjectID & NewMask] = STATUS_LOADED; + NewData[Entry->ObjectID & NewMask] = Entry; + }else{ + error("ResizeHashTable: Fehler beim Reorganisieren der HashTabelle.\n"); + } + } + } + + free(HashTableData); + free(HashTableType); + + HashTableData = NewData; + HashTableType = NewType; + HashTableSize = NewSize; + HashTableMask = NewMask; + HashTableFree += (NewSize - OldSize); +} + +static TObject *GetFreeObjectSlot(void){ + if(FirstFreeObject == NULL){ + SwapSector(); + } + + TObject *Entry = FirstFreeObject; + if(Entry == NULL){ + error("GetFreeObjectSlot: Kein freier Platz mehr.\n"); + return NULL; + } + + // NOTE(fusion): The next object pointer was originally stored in `Entry->NextObject.ObjectID` + // which is a problem when compiling in 64 bits mode. For this reason, I've changed it to be + // stored at the beggining of `TObject`. + FirstFreeObject = *((TObject**)Entry); + + // TODO(fusion): Using `memset` here will trigger a compiler warning because `TObject` contains + // a few `Object`s and I've made them non PODs by adding a few constructors. + //memset(Entry, 0, sizeof(TObject)); + + *Entry = TObject{}; + return Entry; +} + +static void PutFreeObjectSlot(TObject *Entry){ + if(Entry == NULL){ + error("PutFreeObjectSlot: Entry ist NULL.\n"); + return; + } + + // NOTE(fusion): See note in `GetFreeObjectSlot`, just above. + *((TObject**)Entry) = FirstFreeObject; + FirstFreeObject = Entry; +} + +void SwapObject(TWriteBinaryFile *File, Object Obj, uint32 FileNumber){ ASSERT(Obj != NONE); // NOTE(fusion): Does it make sense to swap an object that isn't loaded? We // were originally calling `Object::exists` that would swap in the object's // sector if it was swapped out. We should probably have an assertion here. - if(HashTableType[Obj.ObjectID & HashTableMask] != STATUS_LOADED){ + uint32 EntryIndex = Obj.ObjectID & HashTableMask; + if(HashTableType[EntryIndex] != STATUS_LOADED){ error("SwapObject: Object doesn't exists or is not currently loaded.\n"); return; } - TObject *Entry = HashTableData[Obj.ObjectID & HashTableMask]; + TObject *Entry = HashTableData[EntryIndex]; if(Entry->ObjectID != Obj.ObjectID){ error("SwapObject: Übergebenes Objekt existiert nicht.\n"); return; @@ -93,10 +462,10 @@ static void SwapObject(TWriteBinaryFile *File, Object Obj, uint32 FileNumber){ PutFreeObjectSlot(Entry); HashTableType[EntryIndex] = STATUS_SWAPPED; - HashTableData[EntryIndex] = (TObject*)FileNumber; + HashTableData[EntryIndex] = (TObject*)((uintptr)FileNumber); } -static void SwapSector(void){ +void SwapSector(void){ static uint32 FileNumber = 0; TSector *Oldest = NULL; @@ -128,8 +497,11 @@ static void SwapSector(void){ char FileName[4096]; do{ - FileNumber += 1; // NOTE(fusion): Let it wrap naturally. - snprintf(FileName, sizeof(FileName), "%s/%010u.swp", SAVEPATH, FileNumber); + FileNumber += 1; + if(FileNumber > 99999999){ + FileNumber = 1; + } + snprintf(FileName, sizeof(FileName), "%s/%08u.swp", SAVEPATH, FileNumber); }while(FileExists(FileName)); TWriteBinaryFile File; @@ -155,9 +527,9 @@ static void SwapSector(void){ } } -static void UnswapSector(uint32 FileNumber){ +void UnswapSector(uint32 FileNumber){ char FileName[4096]; - snprintf(FileName, sizeof(FileName), "%s/%010u.swp", SAVEPATH, FileNumber); + snprintf(FileName, sizeof(FileName), "%s/%08u.swp", SAVEPATH, FileNumber); TReadBinaryFile File; try{ @@ -210,7 +582,7 @@ static void UnswapSector(uint32 FileNumber){ } } -static void DeleteSwappedSectors(void){ +void DeleteSwappedSectors(void){ DIR *SwapDir = opendir(SAVEPATH); if(SwapDir == NULL){ error("DeleteSwappedSectors: Unterverzeichnis %s nicht gefunden\n", SAVEPATH); @@ -235,13 +607,13 @@ static void DeleteSwappedSectors(void){ closedir(SwapDir); } -static void LoadObjects(TReadScriptFile *Script, TWriteStream *Stream, bool Skip){ +void LoadObjects(TReadScriptFile *Script, TWriteStream *Stream, bool Skip){ int Depth = 1; - bool ParseAttribute = false; + bool ProcessObjects = true; Script->readSymbol('{'); Script->nextToken(); while(true){ - if(!ParseAttribute){ + if(ProcessObjects){ if(Script->Token != SPECIAL){ int TypeID = Script->getNumber(); if(!ObjectTypeExists(TypeID)){ @@ -252,7 +624,7 @@ static void LoadObjects(TReadScriptFile *Script, TWriteStream *Stream, bool Skip Stream->writeWord((uint16)TypeID); } - ParseAttribute = true; + ProcessObjects = false; }else{ char Special = Script->getSpecial(); if(Special == '}'){ @@ -284,7 +656,7 @@ static void LoadObjects(TReadScriptFile *Script, TWriteStream *Stream, bool Skip if(Attribute == CONTENT){ Script->readSymbol('{'); Depth += 1; - ParseAttribute = false; + ProcessObjects = true; }else if(Attribute == TEXTSTRING || Attribute == EDITOR){ const char *String = Script->readString(); if(!Skip){ @@ -305,13 +677,13 @@ static void LoadObjects(TReadScriptFile *Script, TWriteStream *Stream, bool Skip if(!Skip){ Stream->writeByte(0xFF); } - ParseAttribute = false; + ProcessObjects = true; } } } } -static void LoadObjects(TReadStream *Stream, Object Con){ +void LoadObjects(TReadStream *Stream, Object Con){ int Depth = 1; Object Obj = NONE; while(true){ @@ -322,6 +694,10 @@ static 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); @@ -342,11 +718,12 @@ static void LoadObjects(TReadStream *Stream, Object Con){ Obj = NONE; Depth += 1; }else if(Attribute == TEXTSTRING || Attribute == EDITOR){ - uint32 Value = AddDynamicString(Stream->readString()); - Obj.setAttribute(Attribute, Value); + char String[4096]; + Stream->readString(String, sizeof(String)); + Obj.setAttribute((INSTANCEATTRIBUTE)Attribute, AddDynamicString(String)); }else if(Attribute != REMAININGEXPIRETIME){ uint32 Value = Stream->readQuad(); - Obj.setAttribute(Attribute, Value); + Obj.setAttribute((INSTANCEATTRIBUTE)Attribute, Value); }else{ uint32 Value = Stream->readQuad(); if(Value != 0){ @@ -360,7 +737,7 @@ static void LoadObjects(TReadStream *Stream, Object Con){ } } -static void InitSector(int SectorX, int SectorY, int SectorZ){ +void InitSector(int SectorX, int SectorY, int SectorZ){ ASSERT(Sector); if(*Sector->at(SectorX, SectorY, SectorZ) != NULL){ error("InitSector: Sektor %d/%d/%d existiert schon.\n", SectorX, SectorY, SectorZ); @@ -373,9 +750,9 @@ static void InitSector(int SectorX, int SectorY, int SectorZ){ Object MapCon = CreateObject(); // NOTE(fusion): `Attributes[0]` is probably the object id of the // first object in the container. - Access(MapCon)->Attributes[1] = SectorX * 32 + X; - Access(MapCon)->Attributes[2] = SectorY * 32 + Y; - Access(MapCon)->Attributes[3] = SectorZ; + AccessObject(MapCon)->Attributes[1] = SectorX * 32 + X; + AccessObject(MapCon)->Attributes[2] = SectorY * 32 + Y; + AccessObject(MapCon)->Attributes[3] = SectorZ; NewSector->MapCon[X][Y] = MapCon; } } @@ -386,7 +763,7 @@ static void InitSector(int SectorX, int SectorY, int SectorZ){ *Sector->at(SectorX, SectorY, SectorZ) = NewSector; } -static void LoadSector(const char *FileName, int SectorX, int SectorY, int SectorZ){ +void LoadSector(const char *FileName, int SectorX, int SectorY, int SectorZ){ if(SectorX < SectorXMin || SectorXMax < SectorX || SectorY < SectorYMin || SectorYMax < SectorY || SectorZ < SectorZMin || SectorZMax < SectorZ){ @@ -447,7 +824,7 @@ static void LoadSector(const char *FileName, int SectorX, int SectorY, int Secto Script.readSymbol('='); HelpBuffer.reset(); LoadObjects(&Script, &HelpBuffer, false); - TReadBuffer ReadBuffer(HelpBuffer); + TReadBuffer ReadBuffer(HelpBuffer.Data, HelpBuffer.Position); LoadObjects(&ReadBuffer, LoadingSector->MapCon[OffsetX][OffsetY]); }else{ Script.error("unknown map flag"); @@ -460,7 +837,7 @@ static void LoadSector(const char *FileName, int SectorX, int SectorY, int Secto } } -static void LoadMap(void){ +void LoadMap(void){ DIR *MapDir = opendir(MAPPATH); if(MapDir == NULL){ error("LoadMap: Unterverzeichnis %s nicht gefunden\n", MAPPATH); @@ -470,6 +847,7 @@ static void LoadMap(void){ print(1, "Lade Karte ...\n"); ObjectCounter = 0; + int SectorCounter = 0; char FilePath[4096]; while(dirent *DirEntry = readdir(MapDir)){ if(DirEntry->d_type == DT_REG){ @@ -477,7 +855,7 @@ static void LoadMap(void){ const char *FileExt = findLast(DirEntry->d_name, '.'); if(FileExt != NULL && strcmp(FileExt, ".sec") == 0){ int SectorX, SectorY, SectorZ; - if(sscanf("%d-%d-%d.sec", &SectorX, &SectorY, &SectorZ) == 3){ + if(sscanf(DirEntry->d_name, "%d-%d-%d.sec", &SectorX, &SectorY, &SectorZ) == 3){ snprintf(FilePath, sizeof(FilePath), "%s/%s", SAVEPATH, DirEntry->d_name); LoadSector(FilePath, SectorX, SectorY, SectorZ); SectorCounter += 1; @@ -491,8 +869,145 @@ static void LoadMap(void){ print(1, "%d Objekte geladen.\n", ObjectCounter); } -//SaveObjects -//SaveObjects +void SaveObjects(Object Obj, TWriteStream *Stream, bool Stop){ + // TODO(fusion): Just use a recursive algorithm for both `SaveObjects` and + // `LoadObjects`. Regardless of performance differences, this iterative version + // is just unreadable and mostly disconnected. + int Depth = 1; + bool ProcessObjects = true; + Object Prev = NONE; + while(true){ + if(ProcessObjects){ + if(Obj != NONE){ + ObjectType ObjType = Obj.getObjectType(); + if(!ObjType.isCreatureContainer()){ + Stream->writeWord((uint16)ObjType.TypeID); + ProcessObjects = false; + }else{ + if(Stop && Depth == 1){ + break; + } + Prev = Obj; + Obj = Obj.getNextObject(); + } + }else{ + Stream->writeWord(0xFFFF); + Depth -= 1; + if(Depth <= 0){ + break; + } + + Stream->writeByte(0xFF); + ObjectCounter += 1; + if(Stop && Depth == 1){ + break; + } + + if(Prev == NONE){ + error("LastObj ist NONE (1)\n"); + } + + Prev = Prev.getContainer(); + if(Prev == NONE){ + error("LastObj ist NONE (2)\n"); + } + + Obj = Prev.getNextObject(); + } + }else{ + ASSERT(Obj != NONE); + ObjectType ObjType = Obj.getObjectType(); + for(int Attribute = 1; Attribute <= 17; Attribute += 1){ + if(ObjType.getAttributeOffset((INSTANCEATTRIBUTE)Attribute) != -1){ + uint32 Value = 0; + if(Attribute == REMAININGEXPIRETIME){ + Value = CronInfo(Obj, false); + }else{ + Value = Obj.getAttribute((INSTANCEATTRIBUTE)Attribute); + } + + if(Value != 0){ + Stream->writeByte((uint8)Attribute); + if(Attribute == TEXTSTRING || Attribute == EDITOR){ + Stream->writeString(GetDynamicString(Value)); + }else{ + Stream->writeQuad(Value); + } + } + } + } + + Object First = NONE; + if(ObjType.getAttributeOffset(CONTENT) != -1){ + First = Obj.getAttribute(CONTENT); + } + + if(First != NONE){ + Depth += 1; + Prev = NONE; + Obj = First; + Stream->writeByte((uint8)CONTENT); + }else{ + Stream->writeByte(0xFF); + ObjectCounter += 1; + if(Stop && Depth == 1){ + break; + } + + Prev = Obj; + Obj = Obj.getNextObject(); + } + + ProcessObjects = true; + } + } +} + +void SaveObjects(TReadStream *Stream, TWriteScriptFile *Script){ + int Depth = 1; + bool ProcessObjects = true; + bool FirstObject = true; + Script->writeText("{"); + while(true){ + if(ProcessObjects){ + int TypeID = (int)Stream->readWord(); + if(TypeID != 0xFFFF){ + if(!FirstObject){ + Script->writeText(", "); + } + + Script->writeNumber(TypeID); + }else{ + Depth -= 1; + Script->writeText("}"); + } + + ProcessObjects = false; + }else{ + int Attribute = (int)Stream->readByte(); + if(Attribute != 0xFF){ + Script->writeText(" "); + Script->writeText(GetInstanceAttributeName(Attribute)); + Script->writeText("="); + if(Attribute == CONTENT){ + Depth += 1; + ProcessObjects = true; + FirstObject = true; + Script->writeText("{"); + }else if(Attribute == TEXTSTRING || Attribute == EDITOR){ + char String[4096]; + Stream->readString(String, sizeof(String)); + Script->writeString(String); + }else{ + Script->writeNumber((int)Stream->readQuad()); + } + }else{ + ProcessObjects = true; + FirstObject = false; + } + } + } +} void SaveSector(char *FileName, int SectorX, int SectorY, int SectorZ){ ASSERT(Sector); @@ -561,7 +1076,7 @@ void SaveSector(char *FileName, int SectorX, int SectorY, int SectorZ){ Script.writeText("Content="); HelpBuffer.reset(); SaveObjects(First, &HelpBuffer, false); - TReadBuffer ReadBuffer(HelpBuffer); + TReadBuffer ReadBuffer(HelpBuffer.Data, HelpBuffer.Position); SaveObjects(&ReadBuffer, &Script); AttrCount += 1; } @@ -583,7 +1098,7 @@ void SaveSector(char *FileName, int SectorX, int SectorY, int SectorZ){ } } -static void SaveMap(void){ +void SaveMap(void){ // NOTE(fusion): I guess this could happen if we're already saving the map // and a signal causes `exit` to execute cleanup functions registered with // `atexit`, among which is `ExitAll` which may call `SaveMap` throught @@ -611,267 +1126,6 @@ static void SaveMap(void){ SavingMap = false; } -static void ResizeHashTable(void){ - uint32 OldSize = HashTableSize; - uint32 NewSize = OldSize * 2; - ASSERT(ISPOW2(OldSize)); - ASSERT(NewSize > OldSize); - - // TODO(fusion): See note below. - error("FATAL ERROR in ResizeHashTable: Resizing the object hash table is" - " currently disabled. You may increase `Objects` in the map config" - " from %d to %d, to achieve the same effect.", OldSize, NewSize); - abort(); - - error("INFO: HashTabelle zu klein. Größe wird verdoppelt auf %d.\n", NewSize); - - uint32 NewMask = NewSize - 1; - TObject **NewData = (TObject**)malloc(NewSize * sizeof(TObject*)); - uint8 *NewType = (uint8*)malloc(NewSize * sizeof(uint8)); - memset(NewType, 0, NewSize * sizeof(uint8)); - - // TODO(fusion): This rehash loop doesn't make a lot of sense. It wants to - // access all existing objects but doing so would cause all sectors to be - // swapped in at some time or another. This may be bad for performance but - // the real problem is that `UnswapSector` may swap out some other sector - // whose objects were already put into `NewType` and `NewData`, causing - // multiple object ids to reference the same `TObject`. - // Looking at some of the logs included with this executable, it doesn't - // look like this function was ever called which explains why it wasn't fixed - // earlier. - // It would be possible to do this rehashing without swapping anything out, - // if we stored ObjectID somewhere (maybe `HashTableData`). Doubling the size - // of the table means there are two possible indices for each previous entry, - // and we can only determine which one to actually move it with the ObjectID. - - NewType[0] = STATUS_PERMANENT; - NewData[0] = HashTableData[0]; - for(uint32 i = 1; i < NewSize; i += 1){ - if(HashTableType[i] != STATUS_FREE){ - if(HashTableType[i] == STATUS_SWAPPED){ - UnswapSector((uintptr)HashTableData[i]); - } - - if(HashTableType[i] == STATUS_LOADED){ - TObject *Entry = HashTableData[i]; - NewType[Entry->ObjectID & NewMask] = STATUS_LOADED; - NewData[Entry->ObjectID & NewMask] = Entry; - }else{ - error("ResizeHashTable: Fehler beim Reorganisieren der HashTabelle.\n"); - } - } - } - - free(HashTableData); - free(HashTableType); - - HashTableData = NewData; - HashTableType = NewType; - HashTableSize = NewSize; - HashTableMask = NewMask; - HashTableFree += (NewSize - OldSize); -} - -static TObject *GetFreeObjectSlot(void){ - if(FirstFreeObject == NULL){ - SwapSector(); - } - - TObject *Entry = FirstFreeObject; - if(Entry == NULL){ - error("GetFreeObjectSlot: Kein freier Platz mehr.\n"); - return NULL; - } - - // NOTE(fusion): The next object pointer was originally stored in `Entry->NextObject.ObjectID` - // which is a problem when compiling in 64 bits mode. For this reason, I've changed it to be - // stored at the beggining of `TObject`. - FirstFreeObject = *((TObject**)Entry); - - memset(Entry, 0, sizeof(TObject)); - - return Entry; -} - -static void PutFreeObjectSlot(TObject *Entry){ - if(Entry == NULL){ - error("PutFreeObjectSlot: Entry ist NULL.\n"); - return; - } - - // NOTE(fusion): See note in `GetFreeObjectSlot`, just above. - *((TObject**)Entry) = FirstFreeObject; - FirstFreeObject = Entry; -} - -static void ReadMapConfig(void){ - OBCount = 0xA0000; - SectorXMin = 1000; - SectorXMax = 1015; - SectorYMin = 1000; - SectorYMax = 1015; - SectorZMin = 0; - SectorZMax = 15; - RefreshedCylinders = 1; - NewbieStartPositionX = 0; - NewbieStartPositionY = 0; - NewbieStartPositionZ = 0; - VeteranStartPositionX = 0; - VeteranStartPositionY = 0; - VeteranStartPositionZ = 0; - HashTableSize = 0x100000; - Marks = 0; - Depots = 0; - - char FileName[4096]; - snprintf(FileName, sizeof(FileName), "%s/map.dat", DATAPATH); - - TReadScriptFile Script; - Script.open(FileName); - while(true){ - Script.nextToken(); - if(Script.Token == ENDOFFILE){ - Script.close(): - break; - } - - char Identifier[MAX_IDENT_LENGTH]; - strcpy(Identifier, Script.readIdentifier()); - Script.readSymbol('='); - - if(strcmp(Identifier, "sectorxmin") == 0){ - SectorXMin = Script.readNumber(); - }else if(strcmp(Identifier, "sectorxmax") == 0){ - SectorXMax = Script.readNumber(); - }else if(strcmp(Identifier, "sectorymin") == 0){ - SectorYMin = Script.readNumber(); - }else if(strcmp(Identifier, "sectorymax") == 0){ - SectorYMax = Script.readNumber(); - }else if(strcmp(Identifier, "sectorzmin") == 0){ - SectorZMin = Script.readNumber(); - }else if(strcmp(Identifier, "sectorzmax") == 0){ - SectorZMax = Script.readNumber(); - }else if(strcmp(Identifier, "refreshedcylinders") == 0){ - RefreshedCylinders = Script.readNumber(); - }else if(strcmp(Identifier, "objects") == 0){ - HashTableSize = (uint32)Script.readNumber(); - }else if(strcmp(Identifier, "cachesize") == 0){ - OBCount = Script.readNumber(); - }else if(strcmp(Identifier, "depot") == 0){ - int DepotIndex = 0; - TDepotInfo TempInfo = {}; - Script.readSymbol('('); - DepotIndex = Script.readNumber(); - Script.readSymbol(','); - const char *Town = Script.readString(); - if(strlen(Town) >= NARRAY(TempInfo.Town)){ - Script.error("town name too long"); - } - strcpy(TempInfo.Town, Town); - Script.readSymbol(','); - TempInfo.Size = Script.readNumber(); - Script.readSymbol(')'); - *DepotInfo.at(DepotIndex) = TempInfo; - }else if(strcmp(Identifier, "mark") == 0){ - TMark TempMark = {}; - Script.readSymbol('('); - const char *Name = Script.readString(); - if(strlen(Name) >= NARRAY(TempMark.Name)){ - Script.error("mark name too long"); - } - strcpy(TempMark.Name, Name); - Script.readSymbol(','); - Script.readCoordinate(&TempMark.x, &TempMark.y, &TempMark.z); - Script.readSymbol(')'); - *Mark.at(Marks) = TempMark; - Marks += 1; - }else if(strcmp(Identifier, "newbiestart") == 0){ - Script.readCoordinate( - &NewbieStartPositionX, - &NewbieStartPositionY, - &NewbieStartPositionZ); - }else if(strcmp(Identifier, "veteranstart") == 0){ - Script.readCoordinate( - &VeteranStartPositionX, - &VeteranStartPositionY, - &VeteranStartPositionZ); - }else{ - // TODO(fusion): - //error("Unknown map configuration key \"%s\"", Identifier); - } - } - - // NOTE(fusion): If each sector is 32 x 32 tiles and the whole world is - // 65535 x 65535 tiles, then the maximum number of sectors is 65535 / 32 - // which is the 2047 used below. We should probably define these contants. - // Notice that sectors at the edge of the XY-plane are also considered - // invalid, probably to add some buffer to avoid wrapping or other types - // of problems. - - if(SectorXMin <= 0){ - throw "illegal value for SectorXMin"; - } - - if(SectorXMax >= 2047){ - throw "illegal value for SectorXMax"; - } - - if(SectorYMin <= 0){ - throw "illegal value for SectorYMin"; - } - - if(SectorYMax >= 2047){ - throw "illegal value for SectorYMax"; - } - - if(SectorZMin < 0){ - throw "illegal value for SectorZMin"; - } - - if(SectorZMax > 15){ - throw "illegal value for SectorZMax"; - } - - if(SectorXMin > SectorXMax){ - throw "SectorXMin is greater than SectorXMax"; - } - - if(SectorYMin > SectorYMax){ - throw "SectorYMin is greater than SectorYMax"; - } - - if(SectorZMin > SectorZMax){ - throw "SectorZMin is greater than SectorZMax"; - } - - // TODO(fusion): Just align up from whatever value we got? And use `ObjectsPerBlock`? - if(OBCount % 32768 != 0){ - throw "CacheSize must be a multiple of 32768"; - } - - if(OBCount <= 0){ - throw "illegal value for CacheSize"; - } - - OBCount /= 32768; - - if(HashTableSize <= 0){ - throw "illegal value for Objects"; - } - - if(!ISPOW2(HashTableSize)){ - throw "Objects must be a power of 2"; - } - - if(NewbieStartPositionX == 0){ - throw "no start position for newbies specified"; - } - - if(VeteranStartPositionX == 0){ - throw "no start position for veterans specified"; - } -} - void InitMap(void){ ReadMapConfig(); @@ -951,26 +1205,9 @@ void ExitMap(bool Save){ DeleteSwappedSectors(); } -// TODO(fusion): For some reason this is not a member of `Object`? -static TObject *AccessObject(Object Obj){ - if(Obj == NONE){ - error("AccessObject: Ungültige Objektnummer Null.\n"); - return HashTableData[0]; - } - - uint32 Index = Obj.ObjectID - if(HashTableType[Index] == STATUS_SWAPPED){ - UnswapSector((uintptr)HashTableData[Index]); - } - - if(HashTableType[Index] == STATUS_LOADED && HashTableData[Index]->ObjectID == Obj.ObjectID){ - return HashTableData[Index]; - }else{ - return HashTableData[0]; - } -} - -static Object CreateObject(void){ +// Object Functions +// ============================================================================= +Object CreateObject(void){ static uint32 NextObjectID = 1; // NOTE(fusion): Load factor of 1/16. @@ -1006,105 +1243,24 @@ static Object CreateObject(void){ // DestroyObject // DeleteObject -// Object -// ============================================================================= -bool Object::exists(void){ - if(*this == NONE){ - return false; - } - - uint32 Index = this->ObjectID & HashTableMask; - if(HashTableType[Index] == STATUS_SWAPPED){ - UnswapSector(HashTableData[Index]); - } - - return HashTableType[Index] == STATUS_LOADED - && HashTableData[Index]->ObjectID == this->ObjectID; -} - -ObjectType Object::getObjectType(void){ - return AccessObject(*this)->Type; -} - -void Object::setObjectType(ObjectType Type){ - AccessObject(*this)->Type = Type; -} - -Object Object::getNextObject(void){ - return AccessObject(*this)->NextObject; -} - -void Object::setNextObject(Object NextObject){ - AccessObject(*this)->NextObject = NextObject; -} - -Object Object::getContainer(void){ - return AccessObject(*this)->Container; -} - -void Object::setContainer(Object Container){ - AccessObject(*this)->Container = Container; -} - -uint32 Object::getCreatureID(void){ - // TODO(fusion): We call `AccessObject` once in `getObjectType` then again - // after checking the TypeID, when we could call it once to check both type - // and access `Attributes[1]`. - - if(!this->getObjectType().isCreatureContainer()){ - error("Object::getCreatureID: Objekt ist keine Kreatur.\n"); - return 0; - } - - return AccessObject(*this)->Attributes[1]; -} - -uint32 Object::getAttribute(INSTANCEATTRIBUTE Attribute){ - ObjectType ObjType = this->getObjectType(); - int AttributeOffset = ObjType.getAttributeOffset(Attribute); - if(AttributeOffset == -1){ - error("Object::getAttribute: Flag für Attribut %d bei Objekttyp %d nicht gesetzt.\n", - Attribute, ObjType.TypeID); - return 0; - } - - if(AttributeOffset < 0 || AttributeOffset >= NARRAY(TObject::Attributes)){ - error("Object::getAttribute: Ungültiger Offset %d für Attribut %d bei Objekttyp %d.\n", - AttributeOffset, Attribute, ObjType.TypeID); - return 0; - } - - return AccessObject(*this)->Attributes[AttributeOffset]; -} - -void Object::setAttribute(INSTANCEATTRIBUTE Attribute, uint32 Value){ - ObjectType ObjType = this->getObjectType(); - int AttributeOffset = ObjType.getAttributeOffset(Attribute); - if(AttributeOffset == -1){ - error("Object::setAttribute: Flag für Attribut %d bei Objekttyp %d nicht gesetzt.\n", - Attribute, ObjType.TypeID); - return; +TObject *AccessObject(Object Obj){ + if(Obj == NONE){ + error("AccessObject: Ungültige Objektnummer Null.\n"); + return HashTableData[0]; } - if(AttributeOffset < 0 || AttributeOffset >= NARRAY(TObject::Attributes)){ - error("Object::setAttribute: Ungültiger Offset %d für Attribut %d bei Objekttyp %d.\n", - AttributeOffset, Attribute, ObjType.TypeID); - return; + uint32 EntryIndex = Obj.ObjectID & HashTableMask; + if(HashTableType[EntryIndex] == STATUS_SWAPPED){ + UnswapSector((uint32)((uintptr)HashTableData[EntryIndex])); } - if(Value == 0){ - if(Attribute == AMOUNT || Attribute == POOLLIQUIDTYPE || Attribute == CHARGES){ - Value = 1; - }else if(Attribute == REMAININGUSES){ - Value = ObjType.getAttribute(TOTALUSES); - } + if(HashTableType[EntryIndex] == STATUS_LOADED && HashTableData[EntryIndex]->ObjectID == Obj.ObjectID){ + return HashTableData[EntryIndex]; + }else{ + return HashTableData[0]; } - - AccessObject(*this)->Attributes[AttributeOffset] = Value; } -// Object Functions -// ============================================================================= void ChangeObject(Object Obj, ObjectType NewType){ if(!Obj.exists()){ error("ChangeObject: Übergebenes Objekt existiert nicht.\n"); @@ -1227,12 +1383,11 @@ void PlaceObject(Object Obj, Object Con, bool Append){ Object Cur(Con.getAttribute(CONTENT)); if(ConType.isMapContainer()){ // TODO(fusion): Review. The loop below was a bit rough but it seems that - // append is forced for PRIORITY_CREATURE and PRIORITY_OTHER. We should - // confirm this whenever the server is up and running. It'll show. + // append is forced for non PRIORITY_CREATURE and PRIORITY_OTHER. int ObjPriority = GetObjectPriority(Obj); Append = Append - || ObjPriority == PRIORITY_CREATURE - || ObjPriority == PRIORITY_OTHER; + || (ObjPriority != PRIORITY_CREATURE + && ObjPriority != PRIORITY_OTHER); while(Cur != NONE){ int CurPriority = GetObjectPriority(Cur); @@ -1243,16 +1398,16 @@ void PlaceObject(Object Obj, Object Con, bool Append){ // TODO(fusion): Replace item? I think the client might assert // if there are two of these objects on a single tile. const char *PriorityString = ""; - if(ObjPriority == PRIORITY_BACK){ - PriorityString == "BANK"; + if(ObjPriority == PRIORITY_BANK){ + PriorityString = "BANK"; }else if(ObjPriority == PRIORITY_BOTTOM){ - PriorityString == "BOTTOM"; + PriorityString = "BOTTOM"; }else if(ObjPriority == PRIORITY_TOP){ - PriorityString == "TOP"; + PriorityString = "TOP"; } int CoordX, CoordY, CoordZ; - GetObjectCoordinates(Con, CoordX, CoordY, CoordZ); + GetObjectCoordinates(Con, &CoordX, &CoordY, &CoordZ); ObjectType ObjType = Obj.getObjectType(); ObjectType CurType = Cur.getObjectType(); @@ -1302,7 +1457,7 @@ Object GetFirstContainerObject(Object Con){ return NONE; } - ObjectType ConType = Con.getObjectType(Con); + ObjectType ConType = Con.getObjectType(); if(!ConType.getFlag(CONTAINER) && !ConType.getFlag(CHEST)){ error("GetFirstContainerObject: Con (%d) ist kein Container.\n", ConType.TypeID); return NONE; @@ -1322,12 +1477,12 @@ Object GetContainerObject(Object Con, int Index){ return NONE; } - Object Current = GetFirstContainerObject(Con); - while(Current != NONE && Index > 0){ - Current = CurrentObject.getNextObject(); + Object Obj = GetFirstContainerObject(Con); + while(Obj != NONE && Index > 0){ + Obj = Obj.getNextObject(); } - return Current; + return Obj; } void GetObjectCoordinates(Object Obj, int *x, int *y, int *z){ @@ -1396,7 +1551,7 @@ Object GetMapContainer(int x, int y, int z){ Object GetMapContainer(Object Obj){ if(!Obj.exists()){ - error("GetMapContainer: Übergebenes Objekt existiert nicht\n") + error("GetMapContainer: Übergebenes Objekt existiert nicht\n"); return NONE; } @@ -3,12 +3,22 @@ #include "common.hh" #include "objects.hh" +#include "script.hh" struct Object { // REGULAR FUNCTIONS // ========================================================================= - Object(void) { this->ObjectID = 0; } - Object(uint32 ObjectID) { this->ObjectID = ObjectID; } + constexpr Object(void) : ObjectID(0) {} + constexpr Object(uint32 ObjectID): ObjectID(ObjectID) {} + + constexpr bool operator==(const Object &other) const { + return this->ObjectID == other.ObjectID; + } + + constexpr bool operator!=(const Object &other) const { + return this->ObjectID != other.ObjectID; + } + bool exists(void); ObjectType getObjectType(void); @@ -71,7 +81,36 @@ struct TCronEntry { int Next; }; +// NOTE(fusion): Map management functions. Most for internal use. +void SwapObject(TWriteBinaryFile *File, Object Obj, uint32 FileNumber); +void SwapSector(void); +void UnswapSector(uint32 FileNumber); +void DeleteSwappedSectors(void); +void LoadObjects(TReadScriptFile *Script, TWriteStream *Stream, bool Skip); +void LoadObjects(TReadStream *Stream, Object Con); +void InitSector(int SectorX, int SectorY, int SectorZ); +void LoadSector(const char *FileName, int SectorX, int SectorY, int SectorZ); +void LoadMap(void); +void SaveObjects(Object Obj, TWriteStream *Stream, bool Stop); +void SaveObjects(TReadStream *Stream, TWriteScriptFile *Script); +void SaveSector(char *FileName, int SectorX, int SectorY, int SectorZ); +void SaveMap(void); void InitMap(void); void ExitMap(bool Save); +// NOTE(fusion): Object related functions. +Object CreateObject(void); +TObject *AccessObject(Object Obj); +void ChangeObject(Object Obj, ObjectType NewType); +int GetObjectPriority(Object Obj); +void PlaceObject(Object Obj, Object Con, bool Append); +Object AppendObject(Object Con, ObjectType Type); +Object GetFirstContainerObject(Object Con); +Object GetContainerObject(Object Con, int Index); +void GetObjectCoordinates(Object Obj, int *x, int *y, int *z); +uint8 GetMapContainerFlags(Object Obj); +Object GetMapContainer(int x, int y, int z); +Object GetMapContainer(Object Obj); +Object GetFirstObject(int x, int y, int z); + #endif //TIBIA_MAP_HH_ diff --git a/src/monster.hh b/src/monster.hh index b9bd27e..dd246a1 100644 --- a/src/monster.hh +++ b/src/monster.hh @@ -85,4 +85,6 @@ struct TMonster: TNonplayer { }; #endif +extern TRaceData RaceData[512]; + #endif //TIBIA_MONSTER_HH_ diff --git a/src/objects.cc b/src/objects.cc index 0119088..de62aca 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -1,4 +1,5 @@ #include "objects.hh" +#include "containers.hh" static vector<TObjectType> ObjectTypes(0, 5000, 1000); static ObjectType SpecialObjects[49]; @@ -69,24 +70,24 @@ static FLAG TypeAttributeFlags[62] = { }; static FLAG InstanceAttributeFlags[18] = { - CONTAINER // CONTENT - CHEST // CHESTQUESTNUMBER - CUMULATIVE // AMOUNT - KEY // KEYNUMBER - KEYDOOR // KEYHOLENUMBER - LEVELDOOR // DOORLEVEL - QUESTDOOR // DOORQUESTNUMBER - QUESTDOOR // DOORQUESTVALUE - RUNE // CHARGES - TEXT // TEXTSTRING - TEXT // EDITOR - LIQUIDCONTAINER // CONTAINERLIQUIDTYPE - LIQUIDPOOL // POOLLIQUIDTYPE - TELEPORTABSOLUTE // ABSTELEPORTDESTINATION - MAGICFIELD // RESPONSIBLE - EXPIRE // REMAININGEXPIRETIME - EXPIRESTOP // SAVEDEXPIRETIME - WEAROUT // REMAININGUSES + CONTAINER, // CONTENT + CHEST, // CHESTQUESTNUMBER + CUMULATIVE, // AMOUNT + KEY, // KEYNUMBER + KEYDOOR, // KEYHOLENUMBER + LEVELDOOR, // DOORLEVEL + QUESTDOOR, // DOORQUESTNUMBER + QUESTDOOR, // DOORQUESTVALUE + RUNE, // CHARGES + TEXT, // TEXTSTRING + TEXT, // EDITOR + LIQUIDCONTAINER, // CONTAINERLIQUIDTYPE + LIQUIDPOOL, // POOLLIQUIDTYPE + TELEPORTABSOLUTE, // ABSTELEPORTDESTINATION + MAGICFIELD, // RESPONSIBLE + EXPIRE, // REMAININGEXPIRETIME + EXPIRESTOP, // SAVEDEXPIRETIME + WEAROUT, // REMAININGUSES }; static const char InstanceAttributeNames[18][30] = { @@ -180,6 +181,11 @@ const char *ObjectType::getDescription(void){ // Object Type Related Functions // ============================================================================= +const char *GetInstanceAttributeName(int Attribute){ + ASSERT(Attribute >= 0 && Attribute <= 17); + return InstanceAttributeNames[Attribute]; +} + int GetInstanceAttributeByName(const char *Name){ int Result = -1; for(int i = 0; i < NARRAY(InstanceAttributeNames); i += 1){ diff --git a/src/objects.hh b/src/objects.hh index 3cbc401..61a0185 100644 --- a/src/objects.hh +++ b/src/objects.hh @@ -65,6 +65,7 @@ struct TObjectType { int AttributeOffsets[18]; }; +const char *GetInstanceAttributeName(int Attribute); int GetInstanceAttributeByName(const char *Name); bool ObjectTypeExists(int TypeID); //void InitObjects(void); diff --git a/src/player.cc b/src/player.cc index a6cd4a5..0ee78f3 100644 --- a/src/player.cc +++ b/src/player.cc @@ -1,6 +1,8 @@ #include "player.hh" #include "enums.hh" +#include "stubs.hh" + void TPlayer::CheckState(void){ if(this->Connection != NULL){ uint8 State = 0; diff --git a/src/player.hh b/src/player.hh index f0c4a82..c985408 100644 --- a/src/player.hh +++ b/src/player.hh @@ -69,6 +69,7 @@ struct TPlayer: TCreature { // REGULAR FUNCTIONS // ========================================================================= void CheckState(void); + uint8 GetActiveProfession(void); // VIRTUAL FUNCTIONS // ========================================================================= diff --git a/src/script.cc b/src/script.cc index 39141f7..5606107 100644 --- a/src/script.cc +++ b/src/script.cc @@ -595,7 +595,7 @@ TWriteScriptFile::~TWriteScriptFile(void){ } } -void TWriteScriptFile::open(char *FileName){ +void TWriteScriptFile::open(const char *FileName){ if(this->File != NULL){ ::error("TWriteScriptFile::open: Altes Skript ist noch offen.\n"); if(fclose(this->File) != 0){ @@ -628,7 +628,7 @@ void TWriteScriptFile::close(void){ this->File = NULL; } -void TWriteScriptFile::error(char *Text){ +void TWriteScriptFile::error(const char *Text){ if(this->File != NULL){ if(fclose(this->File) != 0){ ::error("TWriteScriptFile::error: Fehler %d beim Schließen der Datei.\n", errno); @@ -638,7 +638,7 @@ void TWriteScriptFile::error(char *Text){ snprintf(ErrorString, sizeof(ErrorString), "error in script-file \"%s\", line %d: %s", - this->Filename, this->Line); + this->Filename, this->Line, Text); throw ErrorString; } @@ -652,7 +652,7 @@ void TWriteScriptFile::writeLn(void){ putc('\n', this->File); } -void TWriteScriptFile::writeText(char *Text){ +void TWriteScriptFile::writeText(const char *Text){ if(this->File == NULL){ ::error("TWriteScriptFile::writeText: Kein Skript zum Schreiben geöffnet.\n"); throw "Cannot write text"; @@ -679,9 +679,9 @@ void TWriteScriptFile::writeNumber(int Number){ this->writeText(s); } -void TWriteScriptFile::writeString(char *Text){ +void TWriteScriptFile::writeString(const char *Text){ if(this->File == NULL){ - ::error(); + ::error("TWriteScriptFile::writeString: Kein Skript zum Schreiben geöffnet.\n"); throw "Cannot write string"; } @@ -722,7 +722,7 @@ void TWriteScriptFile::writeCoordinate(int x ,int y ,int z){ this->writeText(s); } -void TWriteScriptFile::writeBytesequence(uint8 *Sequence, int Length){ +void TWriteScriptFile::writeBytesequence(const uint8 *Sequence, int Length){ if(this->File == NULL){ ::error("TWriteScriptFile::writeBytesequence: Kein Skript zum Schreiben geöffnet.\n"); throw "Cannot write bytesequence"; @@ -743,6 +743,7 @@ void TWriteScriptFile::writeBytesequence(uint8 *Sequence, int Length){ putc('-', this->File); } + char s[32]; snprintf(s, sizeof(s), "%u", Sequence[i]); this->writeText(s); } @@ -754,7 +755,7 @@ TReadBinaryFile::TReadBinaryFile(void){ this->File = NULL; } -void TReadBinaryFile::open(char *FileName){ +void TReadBinaryFile::open(const char *FileName){ if(this->File != NULL){ this->error("File still open"); } @@ -770,7 +771,7 @@ void TReadBinaryFile::open(char *FileName){ this->FileSize = -1; } -int TReadBinaryFile::close(void){ +void TReadBinaryFile::close(void){ // TODO(fusion): Check if file is NULL? if(fclose(this->File) != 0){ int ErrCode = errno; @@ -781,7 +782,7 @@ int TReadBinaryFile::close(void){ this->File = NULL; } -void TReadBinaryFile::error(char *Text){ +void TReadBinaryFile::error(const char *Text){ if(this->File != NULL){ if(fclose(this->File) != 0){ ::error("TReadBinaryFile::error: Fehler %d beim Schließen der Datei.\n", errno); @@ -870,7 +871,7 @@ void TReadBinaryFile::readBytes(uint8 *Buffer, int Count){ } } -bool TReadBinaryFile::eof(TReadBinaryFile *this){ +bool TReadBinaryFile::eof(void){ if(this->File == NULL){ this->error("File not open for eof check"); } @@ -901,7 +902,7 @@ TWriteBinaryFile::TWriteBinaryFile(void){ this->File = NULL; } -void TWriteBinaryFile::open(char *FileName){ +void TWriteBinaryFile::open(const char *FileName){ if(this->File != NULL){ this->error("File still open"); } @@ -932,7 +933,7 @@ void TWriteBinaryFile::close(void){ this->File = NULL; } -void TWriteBinaryFile::error(char *Text){ +void TWriteBinaryFile::error(const char *Text){ if(this->File != NULL){ if(fclose(this->File) != 0){ ::error("TWriteBinaryFile::error: Fehler %d beim Schließen der Datei.\n", errno); @@ -966,7 +967,7 @@ void TWriteBinaryFile::writeByte(uint8 Byte){ } } -void TWriteBinaryFile::writeBytes(uint8 *Buffer, int Count){ +void TWriteBinaryFile::writeBytes(const uint8 *Buffer, int Count){ int Result = (int)fwrite(Buffer, 1, Count, this->File); if(Result != Count){ int ErrCode = errno; diff --git a/src/script.hh b/src/script.hh index 0aa6a78..0361190 100644 --- a/src/script.hh +++ b/src/script.hh @@ -86,15 +86,15 @@ struct TWriteScriptFile { // ========================================================================= TWriteScriptFile(void); ~TWriteScriptFile(void); - void open(char *FileName); + void open(const char *FileName); void close(void); - void error(char *Text); + void error(const char *Text); void writeLn(void); - void writeText(char *Text); + void writeText(const char *Text); void writeNumber(int Number); - void writeString(char *Text); + void writeString(const char *Text); void writeCoordinate(int x ,int y ,int z); - void writeBytesequence(uint8 *Sequence, int Length); + void writeBytesequence(const uint8 *Sequence, int Length); // DATA // ========================================================================= @@ -107,9 +107,9 @@ struct TReadBinaryFile: TReadStream { // REGULAR FUNCTIONS // ========================================================================= TReadBinaryFile(void); - void open(char *FileName); - int close(void); - void error(char *Text); + void open(const char *FileName); + void close(void); + void error(const char *Text); int getPosition(void); int getSize(void); void seek(int Offset); @@ -118,13 +118,13 @@ struct TReadBinaryFile: TReadStream { // ========================================================================= uint8 readByte(void) override; void readBytes(uint8 *Buffer, int Count) override; - bool eof(TReadBinaryFile *this) override; + bool eof(void) override; void skip(int Count) override; // TODO(fusion): Appended virtual functions. These are not in the base class // VTABLE which can be problematic if we intend to use polymorphism, although // that doesn't seem to be case. - virtual ~TReadBinaryFile(void) override; // VTABLE[8] + virtual ~TReadBinaryFile(void); // VTABLE[8] // Duplicate destructor that also calls operator delete. // VTABLE[9] // DATA @@ -138,14 +138,14 @@ struct TWriteBinaryFile: TWriteStream { // REGULAR FUNCTIONS // ========================================================================= TWriteBinaryFile(void); - void open(char *FileName); + void open(const char *FileName); void close(void); - void error(char *Text); + void error(const char *Text); // VIRTUAL FUNCTIONS // ========================================================================= void writeByte(uint8 Byte) override; - void writeBytes(uint8 *Buffer, int Count) override; + void writeBytes(const uint8 *Buffer, int Count) override; // TODO(fusion): Appended virtual functions. These are not in the base class // VTABLE which can be problematic if we intend to use polymorphism, although @@ -1,4 +1,11 @@ #include "common.hh" +#include "config.hh" +#include "enums.hh" +#include "thread.hh" + +#include "stubs.hh" + +#include <sys/shm.h> // NOTE(fusion): This looks like an interface to external tools. Looking at the // `bin` directory this program was in, there are other programs that probably @@ -103,7 +110,7 @@ pid_t GetGameThreadPID(void){ return Pid; } -static void ErrorHandler(char *Text){ +static void ErrorHandler(const char *Text){ if(VerboseOutput){ printf("%s", Text); } @@ -119,7 +126,7 @@ static void ErrorHandler(char *Text){ } } -static void PrintHandler(int Level, char *Text){ +static void PrintHandler(int Level, const char *Text){ static Semaphore LogfileMutex(1); if(Level > DebugLevel){ @@ -249,7 +256,7 @@ void SetCommand(int Command, char *Text){ if(Text == NULL){ SHM->CommandBuffer[0] = 0; }else{ - strncpy(SHM->CommandBuffer, sizeof(SHM->CommandBuffer), Text); + strncpy(SHM->CommandBuffer, Text, sizeof(SHM->CommandBuffer)); SHM->CommandBuffer[sizeof(SHM->CommandBuffer) - 1] = 0; } } @@ -359,8 +366,9 @@ void InitSHM(bool Verbose){ // the decompiled version was also clearing SHM, probably just in case. memset(SHM, 0, sizeof(TSharedMemory)); - strncpy(SHM->PrintBuffer[0], sizeof(SHM->PrintBuffer[0]), - "SHM initialized. System printing is working!\n"); + strncpy(SHM->PrintBuffer[0], + "SHM initialized. System printing is working!\n", + sizeof(SHM->PrintBuffer[0])); SHM->PrintBuffer[0][sizeof(SHM->PrintBuffer[0]) - 1] = 0; SHM->PrintBufferPosition = 1; diff --git a/src/skill.cc b/src/skill.cc index 6ca04ef..aa69658 100644 --- a/src/skill.cc +++ b/src/skill.cc @@ -1,8 +1,11 @@ #include "skill.hh" - #include "enums.hh" #include "creature.hh" +#include "monster.hh" #include "player.hh" + +#include "stubs.hh" + #include <math.h> // TSkill REGULAR FUNCTIONS @@ -952,8 +955,7 @@ void TSkillIllusion::Event(int Range){ if(this->Get() <= 0){ Master->Outfit = Master->OrgOutfit; AnnounceChangedCreature(Master->ID, 3); - // TODO(fusion): I'm not sure this is correct. - NotifyAllCreatures(&Master->CrObject, 2, &NONE); + NotifyAllCreatures(Master->CrObject, 2, NONE); } } } @@ -1101,7 +1103,7 @@ void TSkillEnergy::Event(int Range){ // TSkillBase //============================================================================== TSkillBase::TSkillBase(void){ - STATIC_ASSERT(NARRAY(this>Skills) == NARRAY(this->TimerList)); + STATIC_ASSERT(NARRAY(this->Skills) == NARRAY(this->TimerList)); for(int i = 0; i < NARRAY(this->Skills); i += 1){ this->Skills[i] = NULL; this->TimerList[i] = NULL; diff --git a/src/stubs.hh b/src/stubs.hh new file mode 100644 index 0000000..72c898d --- /dev/null +++ b/src/stubs.hh @@ -0,0 +1,64 @@ +#ifndef TIBIA_STUBS_HH_ +#define TIBIA_STUBS_HH_ 1 + +#include "common.hh" +#include "enums.hh" +#include "connection.hh" +#include "creature.hh" +#include "map.hh" +#include "player.hh" + +// IMPORTANT(fusion): These function definitions exist to test compilation. They're +// not yet implemented and will cause the linker to fail with unresolved symbols. + +typedef void TRefreshSectorFunction(int SectorX, int SectorY, int SectorZ, TReadStream *Stream); +typedef void TSendMailsFunction(TPlayerData *PlayerData); + +extern void AbortWriter(void); +extern uint32 AddDynamicString(const char *Text); +extern void AnnounceChangedCreature(uint32 CreatureID, int Type); +extern void BroadcastMessage(int Mode, const char *Text, ...) ATTR_PRINTF(2, 3); +extern bool CheckRight(uint32 CreatureID, RIGHT Right); +extern void CleanupDynamicStrings(void); +extern void CreatePlayerList(bool Online); +extern void CronChange(Object Obj, int NewDelay); +extern void CronExpire(Object Obj, int Delay); +extern uint32 CronInfo(Object Obj, bool Delete); +extern uint32 CronStop(Object Obj); +extern void DeleteDynamicString(uint32 Number); +extern bool FileExists(const char *FileName); +extern TCreature *GetCreature(uint32 CreatureID); +extern const char *GetDynamicString(uint32 Number); +extern TConnection *GetFirstConnection(void); +extern TConnection *GetNextConnection(void); +extern bool IsProtectionZone(int x, int y, int z); +extern void Log(const char *ProtocolName, const char *Text, ...) ATTR_PRINTF(2, 3); +extern void LogoutAllPlayers(void); +extern void MoveCreatures(int Delay); +extern void NetLoadCheck(void); +extern void NetLoadSummary(void); +extern void NotifyAllCreatures(Object Obj, int Type, Object OldCon); +extern void ProcessCommunicationControl(void); +extern void ProcessConnections(void); +extern void ProcessCreatures(void); +extern void ProcessCronSystem(void); +extern void ProcessMonsterhomes(void); +extern void ProcessMonsterRaids(void); +extern void ProcessReaderThreadReplies(TRefreshSectorFunction *RefreshSector, TSendMailsFunction *SendMails); +extern void ProcessSkills(void); +extern void ProcessWriterThreadReplies(void); +extern void ReceiveData(void); +extern void RefreshCylinders(void); +extern void RefreshMap(void); +extern void RefreshSector(int SectorX, int SectorY, int SectorZ, TReadStream *Stream); +extern void SavePlayerDataOrder(void); +extern void SendAll(void); +extern void SendAmbiente(TConnection *Connection); +extern void SendMails(TPlayerData *PlayerData); +extern void SendMessage(TConnection *Connection, int Mode, const char *Text, ...) ATTR_PRINTF(3, 4); +extern void SendPlayerData(TConnection *Connection); +extern void SendPlayerSkills(TConnection *Connection); +extern void SendPlayerState(TConnection *Connection, uint8 State); +extern void WriteKillStatistics(void); + +#endif //TIBIA_STUBS_HH_ diff --git a/src/thread.cc b/src/thread.cc index 2d2d427..89dfc92 100644 --- a/src/thread.cc +++ b/src/thread.cc @@ -50,7 +50,7 @@ int JoinThread(ThreadHandle Handle){ int Result = 0; int *ResultPointer; - pthread_join((pthread_t)Handle, &ResultPointer); + pthread_join((pthread_t)Handle, (void**)&ResultPointer); if(ResultPointer != NULL){ Result = *ResultPointer; delete ResultPointer; diff --git a/src/thread.hh b/src/thread.hh index 825b330..20cfa0b 100644 --- a/src/thread.hh +++ b/src/thread.hh @@ -18,7 +18,7 @@ void DelayThread(int Seconds, int MicroSeconds); struct Semaphore { // REGULAR FUNCTIONS // ========================================================================= - Semaphore(void); + Semaphore(int Value); ~Semaphore(void); void up(void); void down(void); diff --git a/src/time.cc b/src/time.cc index 4e743ed..0003c39 100644 --- a/src/time.cc +++ b/src/time.cc @@ -1,5 +1,7 @@ #include "common.hh" +#include <time.h> + // IMPORTANT(fusion): `RoundNr` is just the number of seconds since startup which // is why `GetRoundAtTime` and `GetRoundForNextMinute` straight up uses it as // seconds. It is incremented every 1 second inside `AdvanceGame`. @@ -38,7 +40,7 @@ void GetDate(int *Year, int *Cycle, int *Day){ // NOTE(fusion): This maps each real time week to a game time year. time_t RealTime = time(NULL); struct tm LocalTime = GetLocalTimeTM(RealTime); - *Year = ((T / 86400) + 4) / 7 + *Year = (int)(((RealTime / 86400) + 4) / 7); *Cycle = LocalTime.tm_wday; *Day = LocalTime.tm_hour; } @@ -79,7 +81,7 @@ void GetAmbiente(int *Brightness, int *Color){ } uint32 GetRoundAtTime(int Hour, int Minute){ - struct tm LocalTime = GetLocalTimeTM(RealTime); + struct tm LocalTime = GetLocalTimeTM(time(NULL)); int SecondsToTime = (Hour - LocalTime.tm_hour) * 3600 + (Minute - LocalTime.tm_min) * 60 + (0 - LocalTime.tm_sec); @@ -90,7 +92,7 @@ uint32 GetRoundAtTime(int Hour, int Minute){ } uint32 GetRoundForNextMinute(void){ - struct tm LocalTime = GetLocalTimeTM(RealTime); + struct tm LocalTime = GetLocalTimeTM(time(NULL)); int SecondsToNextMinute = 60 - LocalTime.tm_sec; return SecondsToNextMinute + RoundNr + 30; } diff --git a/src/util.cc b/src/util.cc index 017e98b..418fb98 100644 --- a/src/util.cc +++ b/src/util.cc @@ -11,7 +11,7 @@ void SetPrintFunction(TPrintFunction *Function){ PrintFunction = Function; } -void error(char *Text, ...){ +void error(const char *Text, ...){ char s[1024]; va_list ap; @@ -26,7 +26,7 @@ void error(char *Text, ...){ } } -void print(int Level, char *Text, ...){ +void print(int Level, const char *Text, ...){ char s[1024]; va_list ap; @@ -109,8 +109,8 @@ char *strUpper(char *s){ return s; } -int stricmp(const char *s1, const char *s2, int Pos){ - for(int i = 0; i < Pos; i += 1){ +int stricmp(const char *s1, const char *s2, int Max /*= INT_MAX*/){ + for(int i = 0; i < Max; i += 1){ int c1 = toLower(s1[i]); int c2 = toLower(s2[i]); if(c1 > c2){ @@ -120,10 +120,11 @@ int stricmp(const char *s1, const char *s2, int Pos){ }else{ ASSERT(c1 == c2); if(c1 == 0){ - return 0; + break; } } } + return 0; } char *findFirst(char *s, char c){ @@ -179,7 +180,7 @@ void TReadStream::readString(char *Buffer, int MaxLength){ if(Length > 0){ if(MaxLength < 0 || MaxLength > Length){ - this->readBytes(Buffer, Length); + this->readBytes((uint8*)Buffer, Length); Buffer[Length] = 0; }else{ this->readBytes((uint8*)Buffer, MaxLength - 1); @@ -375,7 +376,7 @@ void TWriteBuffer::writeQuad(uint32 Quad){ this->Position += 4; } -void TWriteBuffer::writeBytes(uint8 *Buffer, int Count){ +void TWriteBuffer::writeBytes(const uint8 *Buffer, int Count){ if((this->Size - this->Position) < Count){ throw "buffer full"; } @@ -436,7 +437,7 @@ void TDynamicWriteBuffer::writeQuad(uint32 Quad){ this->Position += 4; } -void TDynamicWriteBuffer::writeBytes(uint8 *Buffer, int Count){ +void TDynamicWriteBuffer::writeBytes(const uint8 *Buffer, int Count){ while((this->Size - this->Position) < Count){ this->resizeBuffer(); } |
