aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-05-25 22:37:57 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-05-25 22:37:57 -0300
commitad8213f35523cbb07f418ae275af448a47cc0288 (patch)
tree4d77b8ff2b44461734b5a2cbaad5402b4a94736f
parent5f883a80175a4cc9abda9d647a6a0d73bda84878 (diff)
downloadgame-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--Makefile75
-rw-r--r--build.bat4
-rw-r--r--reference/enums.hh8
-rw-r--r--src/common.hh22
-rw-r--r--src/config.cc13
-rw-r--r--src/containers.hh8
-rw-r--r--src/crcombat.cc2
-rw-r--r--src/creature.cc5
-rw-r--r--src/creature.hh5
-rw-r--r--src/enums.hh8
-rw-r--r--src/main.cc21
-rw-r--r--src/map.cc999
-rw-r--r--src/map.hh43
-rw-r--r--src/monster.hh2
-rw-r--r--src/objects.cc42
-rw-r--r--src/objects.hh1
-rw-r--r--src/player.cc2
-rw-r--r--src/player.hh1
-rw-r--r--src/script.cc29
-rw-r--r--src/script.hh26
-rw-r--r--src/shm.cc18
-rw-r--r--src/skill.cc10
-rw-r--r--src/stubs.hh64
-rw-r--r--src/thread.cc2
-rw-r--r--src/thread.hh2
-rw-r--r--src/time.cc8
-rw-r--r--src/util.cc17
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)
diff --git a/build.bat b/build.bat
index ccee552..a0e501f 100644
--- a/build.bat
+++ b/build.bat
@@ -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();
diff --git a/src/map.cc b/src/map.cc
index 0d3f69b..20dae79 100644
--- a/src/map.cc
+++ b/src/map.cc
@@ -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;
}
diff --git a/src/map.hh b/src/map.hh
index 590fb41..3c92fff 100644
--- a/src/map.hh
+++ b/src/map.hh
@@ -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
diff --git a/src/shm.cc b/src/shm.cc
index c26638f..c68ce1f 100644
--- a/src/shm.cc
+++ b/src/shm.cc
@@ -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();
}