Compare commits

...

10 Commits

Author SHA1 Message Date
fusion32
386fa9b807 minor tweak to script recursion depth checks 2026-06-20 21:50:45 -03:00
fusion32
a8d26b6ce3 fix player state desync on login (#59) 2026-06-01 01:54:15 -03:00
fusion32
e2ba7cd296 fix issues and warnings when compiling with glibc < 2.32 (#57, #58)
This also fixes some inconsistencies such as mixing both strerror
and strerrordesc_np, and possible formatting issues.
2026-05-28 23:56:29 -03:00
fusion32
d1c4dce3be minor fix to monster targeting logic 2026-05-25 18:19:39 -03:00
fusion32
54c2169ec9 fix issues with TMonster constructor + Search functions + UH formula
The issue with the TMonster constructor was obvious, but the others
were slightly different from the original.
2026-05-09 12:40:41 -03:00
fusion32
7beaa081de fix issue with CUMULATIVE + THROW loot (#56) 2026-05-05 21:54:32 -03:00
fusion32
a9f70ed1f1 small change to monster talking
Monsters will prefix their lines with "#Y" when they're supposed
to yell. The minotaur mage from the leaked files uses a "#W" prefix
which is either a mistake or some removed feature? Either way, it
wouldn't be a problem with the original binary because it would only
check for a leading "#" before removing a three character prefix.
2026-04-16 07:34:02 -03:00
fusion32
f9beda0832 missing NEWLINE on non-patched fields + fix accept error on shutdown 2026-03-30 15:54:52 -03:00
fusion32
995f50a984 fix issue with whisper "pspsps" condition 2026-03-25 17:53:42 -03:00
fusion32
370842756a support for proxy headers + minor tweaks 2026-03-14 16:07:56 -03:00
26 changed files with 247 additions and 99 deletions

View File

@ -4,7 +4,7 @@ OUTPUTEXE = game
CC = g++ CC = g++
CFLAGS = -m64 -fno-strict-aliasing -pedantic -Wall -Wextra -Wno-deprecated-declarations -Wno-unused-parameter -Wno-format-truncation -std=c++11 -pthread -DOS_LINUX=1 -DARCH_X64=1 CFLAGS = -m64 -fno-strict-aliasing -pedantic -Wall -Wextra -Wno-deprecated-declarations -Wno-unused-parameter -Wno-format-truncation -std=c++11 -pthread -DOS_LINUX=1 -DARCH_X64=1
LFLAGS = -Wl,-t -lcrypto LFLAGS = -Wl,-t -lrt -lcrypto
DEBUG ?= 0 DEBUG ?= 0
ifneq ($(DEBUG), 0) ifneq ($(DEBUG), 0)

View File

@ -18,10 +18,22 @@ The server uses a few Linux specific features so it will **ONLY** compile on Lin
make # build in release mode make # build in release mode
make DEBUG=1 # build in debug mode make DEBUG=1 # build in debug mode
make clean # remove `build` directory make clean # remove `build` directory
make clean && make # full rebuild in release mode (recommended) make -B # full rebuild in release mode (recommended)
make clean && make DEBUG=1 # full rebuild in debug mode (recommended) make -B DEBUG=1 # full rebuild in debug mode (recommended)
``` ```
### Extra Features (Advanced)
There are also some extra features that can be enabled by appending the following definitions to the `CFLAGS` variable in your `Makefile`. They're completely optional but can be useful in specific scenarios.
- `-DTIBIA772=1`
- Modify the protocol to match Tibia 7.72. It's not a 1:1 relation so 7.7 clients won't be able to connect.
- `-DALLOW_LOCAL_PROXY=1`
- Allow **local** connections to send a proxy header. Ideally we'd have a list of trusted proxies to prevent clients from spoofing their addresses. In reality, it is almost always simpler to have a **local** relay. This is also the only way to get a multi-route proxy without having to drastically change the way connections are handled.
- `-DBIND_ACCEPTOR_TO_GAME_ADDRESS=1`
- Make the acceptor socket bind to the interface specified by the game address, rather than all available network interfaces. This is the same address stored in the database and may cause startup issues if no such interface exists, or connection issues if the address is not publicly reachable.
## Running ## Running
This repository contains only the source code for the game server. After the first decompilation pass, it was clear the server would need a few supporting services. They're fairly simple but each one will have a separate *README* file with a short description on how to compile and run them. This repository contains only the source code for the game server. After the first decompilation pass, it was clear the server would need a few supporting services. They're fairly simple but each one will have a separate *README* file with a short description on how to compile and run them.
- [Query Manager](https://github.com/fusion32/tibia-querymanager) - [Query Manager](https://github.com/fusion32/tibia-querymanager)

View File

@ -182,6 +182,8 @@ void error(const char *Text, ...) ATTR_PRINTF(1, 2);
void print(int Level, const char *Text, ...) ATTR_PRINTF(2, 3); void print(int Level, const char *Text, ...) ATTR_PRINTF(2, 3);
int random(int Min, int Max); int random(int Min, int Max);
bool FileExists(const char *FileName); bool FileExists(const char *FileName);
const char *GetSignalDescription(int SigNr);
const char *GetErrorDescription(int SigNr);
bool isSpace(int c); bool isSpace(int c);
bool isAlpha(int c); bool isAlpha(int c);

View File

@ -652,7 +652,7 @@ bool CallGameThread(TConnection *Connection){
Connection->WaitingForACK = true; Connection->WaitingForACK = true;
if(tgkill(GetGameProcessID(), GetGameThreadID(), SIGUSR1) == -1){ if(tgkill(GetGameProcessID(), GetGameThreadID(), SIGUSR1) == -1){
error("CallGameThread: Can't send SIGUSR1 to thread %d/%d: (%d) %s\n", error("CallGameThread: Can't send SIGUSR1 to thread %d/%d: (%d) %s\n",
GetGameProcessID(), GetGameThreadID(), errno, strerrordesc_np(errno)); GetGameProcessID(), GetGameThreadID(), errno, GetErrorDescription(errno));
SendLoginMessage(Connection, LOGIN_MESSAGE_ERROR, SendLoginMessage(Connection, LOGIN_MESSAGE_ERROR,
"The server is not online.\nPlease try again later.", -1); "The server is not online.\nPlease try again later.", -1);
return false; return false;
@ -1130,7 +1130,12 @@ bool ReceiveCommand(TConnection *Connection){
} }
while(!Connection->WaitingForACK){ while(!Connection->WaitingForACK){
#if ALLOW_LOCAL_PROXY
uint8 Help[32];
#else
uint8 Help[2]; uint8 Help[2];
#endif
int BytesRead = ReadFromSocket(Connection, Help, 2); int BytesRead = ReadFromSocket(Connection, Help, 2);
if(BytesRead == 0){ if(BytesRead == 0){
// NOTE(fusion): Peer has closed the connection and there was no // NOTE(fusion): Peer has closed the connection and there was no
@ -1153,6 +1158,62 @@ bool ReceiveCommand(TConnection *Connection){
return false; return false;
} }
#if ALLOW_LOCAL_PROXY
if(Connection->State == CONNECTION_CONNECTED){
uint8_t *SourceAddr = NULL;
if(Help[0] == 0x0D && Help[1] == 0x0A){
// NOTE(fusion): HAProxy-V2 header.
static const uint8_t HAProxyV2Signature[] = {
0x0D, 0x0A, 0x0D, 0x0A, 0x00, 0x0D, 0x0A, 0x51,
0x55, 0x49, 0x54, 0x0A,
};
if(ReadFromSocket(Connection, Help + 2, 26) != 26){
error("Unable to read proxy header from %s (Socket=%d)\n",
Connection->IPAddress, Connection->GetSocket());
return false;
}
if(memcmp(Help, HAProxyV2Signature, 12) != 0){
error("Invalid proxy header from %s (Socket=%d)\n",
Connection->IPAddress, Connection->GetSocket());
return false;
}
SourceAddr = Help + 16;
}else if(Help[0] == 0xD9 && Help[1] == 0xFF){
// NOTE(fusion): Our custom proxy header.
if(ReadFromSocket(Connection, Help + 2, 4) != 4){
error("Unable to read proxy header from %s (Socket=%d)\n",
Connection->IPAddress, Connection->GetSocket());
return false;
}
SourceAddr = Help + 2;
}
if(SourceAddr != NULL){
// NOTE(fusion): Ideally we'd have a list of trusted proxies to
// prevent clients from spoofing their addresses. In reality, it
// is almost always simpler to have a localhost "receiver". This
// is also the only way to get a multi-route proxy without having
// to drastically change the way connections are handled.
if(strcmp(Connection->IPAddress, "127.0.0.1") != 0){
error("Received proxy header (%02X, %02X) from unknown host %s\n",
Help[0], Help[1], Connection->IPAddress);
return false;
}
// NOTE(fusion): Update connection IP-Address and resume reading.
snprintf(Connection->IPAddress, sizeof(Connection->IPAddress),
"%d.%d.%d.%d",
(int)SourceAddr[0], (int)SourceAddr[1],
(int)SourceAddr[2], (int)SourceAddr[3]);
continue;
}
}
#endif
// TODO(fusion): Size is encoded as a little endian uint16. We should // TODO(fusion): Size is encoded as a little endian uint16. We should
// have a few helper functions to assist with buffer reading. // have a few helper functions to assist with buffer reading.
int Size = ((uint16)Help[0] | ((uint16)Help[1] << 8)); int Size = ((uint16)Help[0] | ((uint16)Help[1] << 8));
@ -1428,21 +1489,21 @@ bool OpenSocket(void){
Linger.l_linger = 0; Linger.l_linger = 0;
if(setsockopt(TCPSocket, SOL_SOCKET, SO_LINGER, &Linger, sizeof(Linger)) == -1){ if(setsockopt(TCPSocket, SOL_SOCKET, SO_LINGER, &Linger, sizeof(Linger)) == -1){
error("LaunchServer: Failed to set SO_LINGER=(0, 0): (%d) %s.\n", error("LaunchServer: Failed to set SO_LINGER=(0, 0): (%d) %s.\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
return false; return false;
} }
int ReuseAddr = 1; int ReuseAddr = 1;
if(setsockopt(TCPSocket, SOL_SOCKET, SO_REUSEADDR, &ReuseAddr, sizeof(ReuseAddr)) == -1){ if(setsockopt(TCPSocket, SOL_SOCKET, SO_REUSEADDR, &ReuseAddr, sizeof(ReuseAddr)) == -1){
error("LaunchServer: Failed to set SO_REUSEADDR=1: (%d) %s.\n", error("LaunchServer: Failed to set SO_REUSEADDR=1: (%d) %s.\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
return false; return false;
} }
int NoDelay = 1; int NoDelay = 1;
if(setsockopt(TCPSocket, IPPROTO_TCP, TCP_NODELAY, &NoDelay, sizeof(NoDelay)) == -1){ if(setsockopt(TCPSocket, IPPROTO_TCP, TCP_NODELAY, &NoDelay, sizeof(NoDelay)) == -1){
error("LaunchServer: Failed to set TCP_NODELAY=1: (%d) %s.\n", error("LaunchServer: Failed to set TCP_NODELAY=1: (%d) %s.\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
return false; return false;
} }
@ -1456,7 +1517,7 @@ bool OpenSocket(void){
#endif #endif
if(bind(TCPSocket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress)) == -1){ if(bind(TCPSocket, (struct sockaddr*)&ServerAddress, sizeof(ServerAddress)) == -1){
error("LaunchServer: Failed to bind to acceptor to %s:%d: (%d) %s.\n", error("LaunchServer: Failed to bind to acceptor to %s:%d: (%d) %s.\n",
inet_ntoa(ServerAddress.sin_addr), GamePort, errno, strerrordesc_np(errno)); inet_ntoa(ServerAddress.sin_addr), GamePort, errno, GetErrorDescription(errno));
return false; return false;
} }
@ -1471,9 +1532,13 @@ bool OpenSocket(void){
int AcceptorThreadLoop(void *Unused){ int AcceptorThreadLoop(void *Unused){
AcceptorThreadID = gettid(); AcceptorThreadID = gettid();
print(1, "Warte auf Clients...\n"); print(1, "Warte auf Clients...\n");
while(GameRunning()){ while(true){
int Socket = accept(TCPSocket, NULL, NULL); int Socket = accept(TCPSocket, NULL, NULL);
if(Socket == -1){ if(Socket == -1){
if(!GameRunning()){
break;
}
error("AcceptorThreadLoop: Fehler %d beim Accept.\n", errno); error("AcceptorThreadLoop: Fehler %d beim Accept.\n", errno);
continue; continue;
} }

View File

@ -105,7 +105,7 @@ bool TConnection::SetLoginTimer(int Timeout){
SigEvent.sigev_notify_thread_id = this->ThreadID; SigEvent.sigev_notify_thread_id = this->ThreadID;
if(timer_create(CLOCK_MONOTONIC, &SigEvent, &this->LoginTimer) == -1){ if(timer_create(CLOCK_MONOTONIC, &SigEvent, &this->LoginTimer) == -1){
error("TConnection::SetLoginTimer: Failed to create timer: (%d) %s\n", error("TConnection::SetLoginTimer: Failed to create timer: (%d) %s\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
return false; return false;
} }
@ -113,7 +113,7 @@ bool TConnection::SetLoginTimer(int Timeout){
TimerSpec.it_value.tv_sec = Timeout; TimerSpec.it_value.tv_sec = Timeout;
if(timer_settime(this->LoginTimer, 0, &TimerSpec, NULL) == -1){ if(timer_settime(this->LoginTimer, 0, &TimerSpec, NULL) == -1){
error("TConnection::SetLoginTimer: Failed to start timer: (%d) %s\n", error("TConnection::SetLoginTimer: Failed to start timer: (%d) %s\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
return false; return false;
} }
@ -133,7 +133,7 @@ void TConnection::StopLoginTimer(void){
if(timer_delete(this->LoginTimer) == -1){ if(timer_delete(this->LoginTimer) == -1){
error("TConnection::StopLoginTimer: Failed to delete timer: (%d) %s\n", error("TConnection::StopLoginTimer: Failed to delete timer: (%d) %s\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
} }
this->LoginTimer = 0; this->LoginTimer = 0;

View File

@ -857,6 +857,7 @@ struct TPlayer: TCreature {
void SetQuestValue(int QuestNr, int Value); void SetQuestValue(int QuestNr, int Value);
void CheckOutfit(void); void CheckOutfit(void);
void CheckState(void); void CheckState(void);
void SyncState(void);
void AddBuddy(const char *Name); void AddBuddy(const char *Name);
void RemoveBuddy(uint32 CharacterID); void RemoveBuddy(uint32 CharacterID);
void SendBuddies(void); void SendBuddies(void);

View File

@ -1016,7 +1016,7 @@ void TCreature::ToDoGo(int DestX, int DestY, int DestZ, bool MustReach, int MaxS
// NOTE(fusion): The number of steps between two points is the same as the // NOTE(fusion): The number of steps between two points is the same as the
// their manhattan distance, if we exclude diagonal movement. We can skip // their manhattan distance, if we exclude diagonal movement. We can skip
// the path finder if we know we're step away from the destination. // the path finder if we know we're one step away from the destination.
if(DistanceX + DistanceY == 1){ if(DistanceX + DistanceY == 1){
TToDoEntry TD = {}; TToDoEntry TD = {};
TD.Code = TDGo; TD.Code = TDGo;

View File

@ -2082,7 +2082,7 @@ void ProcessMonsterRaids(void){
|| ItemType.getFlag(WEAROUT) || ItemType.getFlag(WEAROUT)
|| ItemType.getFlag(EXPIRE) || ItemType.getFlag(EXPIRE)
|| ItemType.getFlag(EXPIRESTOP)){ || ItemType.getFlag(EXPIRESTOP)){
Item = Create(Bag, ItemType, 0); Item = Create(Bag, ItemType, Amount);
}else{ }else{
Item = CreateAtCreature(Creature->ID, ItemType, Amount); Item = CreateAtCreature(Creature->ID, ItemType, Amount);
} }

View File

@ -1760,10 +1760,18 @@ void TNPC::IdleStimulus(void){
case 3: DestY += 1; break; case 3: DestY += 1; break;
} }
// NOTE(fusion): TNPC::MovePossible in particular doesn't throw
// exceptions so we don't need a try-catch block here like in the
// case of TMonster::IdleStimulus, nor does the original function
// has it. That said, it's still better to be safe.
try{
if(this->MovePossible(DestX, DestY, DestZ, true, false)){ if(this->MovePossible(DestX, DestY, DestZ, true, false)){
DestFound = true; DestFound = true;
break; break;
} }
}catch(RESULT r){
// no-op
}
} }
if(DestFound){ if(DestFound){
@ -1976,8 +1984,8 @@ TMonster::TMonster(int Race, int x, int y, int z, int Home, uint32 MasterID) :
this->starty = y; this->starty = y;
this->startz = z; this->startz = z;
this->posx = x; this->posx = x;
this->posx = y; this->posy = y;
this->posx = z; this->posz = z;
this->State = IDLE; this->State = IDLE;
this->Home = Home; this->Home = Home;
this->Master = MasterID; this->Master = MasterID;
@ -2050,7 +2058,7 @@ TMonster::TMonster(int Race, int x, int y, int z, int Home, uint32 MasterID) :
|| ItemType.getFlag(WEAROUT) || ItemType.getFlag(WEAROUT)
|| ItemType.getFlag(EXPIRE) || ItemType.getFlag(EXPIRE)
|| ItemType.getFlag(EXPIRESTOP)){ || ItemType.getFlag(EXPIRESTOP)){
Item = Create(Bag, ItemType, 0); Item = Create(Bag, ItemType, Amount);
}else{ }else{
Item = CreateAtCreature(this->ID, ItemType, Amount); Item = CreateAtCreature(this->ID, ItemType, Amount);
} }
@ -2384,13 +2392,14 @@ void TMonster::IdleStimulus(void){
int TalkNr = random(1, RaceData[this->Race].Talks); int TalkNr = random(1, RaceData[this->Race].Talks);
const char *Text = GetDynamicString(*RaceData[this->Race].Talk.at(TalkNr)); const char *Text = GetDynamicString(*RaceData[this->Race].Talk.at(TalkNr));
if(Text != 0 && Text[0] != 0){ if(Text != 0 && Text[0] != 0){
// TODO(fusion): We were only checking for a `#` but it could be // NOTE(fusion): The original would only check for a leading '#' before
// problematic for any two character nul terminated string, since // advancing 3 characters. This would be a problem for any 2 character
// adding 3 would make it point out of bounds. Poor string management // string since we'd end up pointing beyond the null terminator.
// is a recurring theme.
int Mode = TALK_ANIMAL_LOW; int Mode = TALK_ANIMAL_LOW;
if(Text[0] == '#' && (Text[1] == 'y' || Text[1] == 'Y') && Text[2] == ' '){ if(Text[0] == '#' && Text[1] != 0 && Text[2] == ' '){
if(Text[1] == 'y' || Text[1] == 'Y'){
Mode = TALK_ANIMAL_LOUD; Mode = TALK_ANIMAL_LOUD;
}
Text += 3; Text += 3;
} }
@ -2450,7 +2459,7 @@ void TMonster::IdleStimulus(void){
int DistanceX = std::abs(Target->posx - this->posx); int DistanceX = std::abs(Target->posx - this->posx);
int DistanceY = std::abs(Target->posy - this->posy); int DistanceY = std::abs(Target->posy - this->posy);
if((Target->posz != this->posz || DistanceX > 10 || DistanceY > 10) if((Target->posz != this->posz || DistanceX > 10 || DistanceY > 10)
||(Target->Type == PLAYER && CheckRight(Target->ID, IGNORED_BY_MONSTERS)) || (Target->Type == PLAYER && CheckRight(Target->ID, IGNORED_BY_MONSTERS))
|| (Target->IsInvisible() && !RaceData[this->Race].SeeInvisible) || (Target->IsInvisible() && !RaceData[this->Race].SeeInvisible)
|| IsProtectionZone(Target->posx, Target->posy, Target->posz) || IsProtectionZone(Target->posx, Target->posy, Target->posz)
|| IsHouse(Target->posx, Target->posy, Target->posz) || IsHouse(Target->posx, Target->posy, Target->posz)
@ -2475,7 +2484,7 @@ void TMonster::IdleStimulus(void){
// NOTE(fusion): We're looking for the creature that maximizes // NOTE(fusion): We're looking for the creature that maximizes
// the strategy parameter. // the strategy parameter.
int TieBreaker = random(0, 99); int TieBreaker = random(0, 99);
if(StrategyParam >= BestStrategyParam if(StrategyParam > BestStrategyParam
||(StrategyParam == BestStrategyParam && TieBreaker > BestTieBreaker)){ ||(StrategyParam == BestStrategyParam && TieBreaker > BestTieBreaker)){
this->Target = TargetID; this->Target = TargetID;
BestTieBreaker = TieBreaker; BestTieBreaker = TieBreaker;
@ -2702,7 +2711,7 @@ void TMonster::IdleStimulus(void){
// TODO(fusion): We're doing something similar to `TCombat::CanToDoAttack` // TODO(fusion): We're doing something similar to `TCombat::CanToDoAttack`
// with added random steps around the attacking position. This function is // with added random steps around the attacking position. This function is
// too convoluted and we should definitely split it into smaller ones. I // too convoluted and we should definitely split it into smaller ones. I
// don't have a problem with large functions but this it too much. // don't have a problem with large functions but this is too much.
// NOTE(fusion): Max distance. // NOTE(fusion): Max distance.
int Distance = std::max<int>( int Distance = std::max<int>(
@ -2777,6 +2786,8 @@ void TMonster::IdleStimulus(void){
if(this->State == ATTACKING || this->State == PANIC){ if(this->State == ATTACKING || this->State == PANIC){
this->Rotate(Target); this->Rotate(Target);
// TODO(fusion): This condition should be always true because we're
// already running on the ELSE branch of `this->Master == this->Target`.
if(this->Master != this->Target){ if(this->Master != this->Target){
this->ToDoAttack(); this->ToDoAttack();
}else{ }else{
@ -2832,6 +2843,9 @@ void TMonster::IdleStimulus(void){
this->ToDoWait(1000); this->ToDoWait(1000);
this->ToDoStart(); this->ToDoStart();
}catch(RESULT r){ }catch(RESULT r){
// TODO(fusion): This shouldn't be possible? Otherwise the creature
// might become stuck since we're not calling ToDoStart or ToDoYield.
// Probably just fallback to ToDoWait(1000)?
error("TMonster::IdleStimulus: Exception %d.\n", r); error("TMonster::IdleStimulus: Exception %d.\n", r);
} }
}else{ }else{
@ -2936,7 +2950,7 @@ bool TMonster::KickCreature(TCreature *Creature){
print(3, "%s verschiebt %s.\n", this->Name, Creature->Name); print(3, "%s verschiebt %s.\n", this->Name, Creature->Name);
// TODO(fusion): Declare these here so they can be used within the catch // NOTE(fusion): Declare these here so they can be used within the catch
// block to print out what's on the last position we tried to move the // block to print out what's on the last position we tried to move the
// creature to. // creature to.
int DestX = Creature->posx; int DestX = Creature->posx;

View File

@ -205,7 +205,7 @@ TPlayer::TPlayer(TConnection *Connection, uint32 CharacterID):
SendAmbiente(Connection); SendAmbiente(Connection);
AnnounceChangedCreature(CharacterID, CREATURE_LIGHT_CHANGED); AnnounceChangedCreature(CharacterID, CREATURE_LIGHT_CHANGED);
SendPlayerSkills(Connection); SendPlayerSkills(Connection);
this->CheckState(); this->SyncState();
this->SendBuddies(); this->SendBuddies();
if(PlayerData->LastLoginTime != 0){ if(PlayerData->LastLoginTime != 0){
@ -750,7 +750,6 @@ void TPlayer::TakeOver(TConnection *Connection){
strcpy(this->Guild, PlayerData->Guild); strcpy(this->Guild, PlayerData->Guild);
strcpy(this->Rank, PlayerData->Rank); strcpy(this->Rank, PlayerData->Rank);
strcpy(this->Title, PlayerData->Title); strcpy(this->Title, PlayerData->Title);
this->OldState = 0;
this->CheckOutfit(); this->CheckOutfit();
this->ClearRequest(); this->ClearRequest();
@ -770,7 +769,7 @@ void TPlayer::TakeOver(TConnection *Connection){
SendAmbiente(Connection); SendAmbiente(Connection);
SendPlayerData(Connection); SendPlayerData(Connection);
SendPlayerSkills(Connection); SendPlayerSkills(Connection);
this->CheckState(); this->SyncState();
this->SendBuddies(); this->SendBuddies();
} }
@ -1254,6 +1253,16 @@ void TPlayer::CheckState(void){
} }
} }
void TPlayer::SyncState(void){
// NOTE(fusion): This is only called after a new connection is established,
// when we know that the current client state is ZERO but `OldState` may not.
// It'll typically be the case in `TPlayer::TakeOver` but can also happen
// in the player constructor if `TPlayer::CheckState` is called from within
// any helper function before `TConnection::EnterGame` (see `BeginSendData`).
this->OldState = 0;
this->CheckState();
}
void TPlayer::AddBuddy(const char *Name){ void TPlayer::AddBuddy(const char *Name){
if(Name == NULL){ if(Name == NULL){
error("TPlayer::AddBuddy: Name ist NULL.\n"); error("TPlayer::AddBuddy: Name ist NULL.\n");
@ -2845,7 +2854,7 @@ void AttachPlayerPoolSlot(TPlayerData *Slot, bool DontWait){
} }
PlayerDataPoolMutex.up(); PlayerDataPoolMutex.up();
DelayThread(0,100); DelayThread(0, 100);
} }
} }

View File

@ -107,9 +107,6 @@ void TSkill::Load(int Act, int Max, int Min, int DAct, int MDAct,
TCreature *Master = this->Master; TCreature *Master = this->Master;
if(Master && Cycle != 0){ if(Master && Cycle != 0){
// NOTE(fusion): It seems we had `TSkillBase::SetTimer` inlined here.
// For whatever reason I hadn't noticed the error message referencing
// it, LOL.
Master->SetTimer(this->SkNr, Cycle, Count, MaxCount, FactorPercent); Master->SetTimer(this->SkNr, Cycle, Count, MaxCount, FactorPercent);
} }
} }

View File

@ -798,8 +798,8 @@ static Object CreateTempDepot(void){
return TempDepot; return TempDepot;
} }
// NOTE(fusion): This is used inside house processing functions to empty the depot // NOTE(fusion): This is used by house processing functions to clear the temporary
// container after processing a player's depot. // depot container after processing player depots.
static void DeleteContainerObjects(Object Con){ static void DeleteContainerObjects(Object Con){
Object Obj = GetFirstContainerObject(Con); Object Obj = GetFirstContainerObject(Con);
while(Obj != NONE){ while(Obj != NONE){

View File

@ -768,19 +768,14 @@ bool SearchFreeField(int *x, int *y, int *z, int Distance, uint16 HouseID, bool
int FieldY = *y + OffsetY; int FieldY = *y + OffsetY;
int FieldZ = *z; int FieldZ = *z;
// TODO(fusion): This is probably some form of the `TCreature::MovePossible` bool MovePossible = false;
// function inlined.
bool MovePossible;
if(Jump){ if(Jump){
MovePossible = JumpPossible(FieldX, FieldY, FieldZ, true); MovePossible = JumpPossible(FieldX, FieldY, FieldZ, true);
}else{ }else{
MovePossible = CoordinateFlag(FieldX, FieldY, FieldZ, BANK) MovePossible = CoordinateFlag(FieldX, FieldY, FieldZ, BANK)
&& !CoordinateFlag(FieldX, FieldY, FieldZ, UNPASS); && !CoordinateFlag(FieldX, FieldY, FieldZ, UNPASS)
&& (!CoordinateFlag(FieldX, FieldY, FieldZ, AVOID)
// TODO(fusion): This one I'm not so sure. || CoordinateFlag(FieldX, FieldY, FieldZ, BED));
if(MovePossible && CoordinateFlag(FieldX, FieldY, FieldZ, AVOID)){
MovePossible = CoordinateFlag(FieldX, FieldY, FieldZ, BED);
}
} }
if(MovePossible){ if(MovePossible){
@ -792,11 +787,9 @@ bool SearchFreeField(int *x, int *y, int *z, int Distance, uint16 HouseID, bool
} }
} }
// NOTE(fusion): We're spiraling out from the initial coordinate. The
// NOTE(fusion): We're spiraling out from the initial coordinate. // original function used direction values different from the ones used
// TODO(fusion): This function used directions different from the ones // by creatures and defined in `enums.hh` so I changed it for consistency.
// used by creatures and defined in `enums.hh` so I made it use them
// instead, LOL.
if(CurrentDirection == DIRECTION_NORTH){ if(CurrentDirection == DIRECTION_NORTH){
OffsetY -= 1; OffsetY -= 1;
if(OffsetY <= -CurrentDistance){ if(OffsetY <= -CurrentDistance){
@ -979,9 +972,8 @@ bool SearchSpawnField(int *x, int *y, int *z, int Distance, bool Player){
if(ObjType.getFlag(UNMOVE)){ if(ObjType.getFlag(UNMOVE)){
ExpansionPossible = false; ExpansionPossible = false;
LoginPossible = LoginPossible && !Player; LoginPossible = LoginPossible && !Player;
}else{
LoginBad = true;
} }
LoginBad = true;
} }
Obj = Obj.getNextObject(); Obj = Obj.getNextObject();

View File

@ -4119,7 +4119,7 @@ void UseMagicItem(uint32 CreatureID, Object Obj, Object Dest){
throw NOCREATURE; throw NOCREATURE;
} }
int Amount = ComputeDamage(Actor, SpellNr, 250, 30); int Amount = ComputeDamage(Actor, SpellNr, 250, 0);
Heal(Target, -1, 0, Amount); // -1 ? Heal(Target, -1, 0, Amount); // -1 ?
break; break;
} }

View File

@ -53,7 +53,8 @@ static void SigBlock(int SigNr){
sigaddset(&Set, SigNr); sigaddset(&Set, SigNr);
if(sigprocmask(SIG_BLOCK, &Set, NULL) == -1){ if(sigprocmask(SIG_BLOCK, &Set, NULL) == -1){
error("SigBlock: Failed to block signal %d (%s): (%d) %s\n", error("SigBlock: Failed to block signal %d (%s): (%d) %s\n",
SigNr, sigdescr_np(SigNr), errno, strerrordesc_np(errno)); SigNr, GetSignalDescription(SigNr),
errno, GetErrorDescription(errno));
} }
} }
@ -63,18 +64,18 @@ static void SigWaitAny(void){
sigsuspend(&Set); sigsuspend(&Set);
} }
static void SigHupHandler(int signr){ static void SigHupHandler(int SigNr){
// no-op (?) // no-op (?)
} }
static void SigAbortHandler(int signr){ static void SigAbortHandler(int SigNr){
print(1, "SigAbortHandler: schalte Writer-Thread ab.\n"); print(1, "SigAbortHandler: schalte Writer-Thread ab.\n");
AbortWriter(); AbortWriter();
} }
static void DefaultHandler(int signr){ static void DefaultHandler(int SigNr){
print(1, "DefaultHandler: Beende Game-Server (SigNr. %d: %s).\n", print(1, "DefaultHandler: Beende Game-Server (SigNr. %d: %s).\n",
signr, sigdescr_np(signr)); SigNr, GetSignalDescription(SigNr));
SigHandler(SIGINT, SIG_IGN); SigHandler(SIGINT, SIG_IGN);
SigHandler(SIGQUIT, SIG_IGN); SigHandler(SIGQUIT, SIG_IGN);
@ -83,8 +84,8 @@ static void DefaultHandler(int signr){
SigHandler(SIGXFSZ, SIG_IGN); SigHandler(SIGXFSZ, SIG_IGN);
SigHandler(SIGPWR, SIG_IGN); SigHandler(SIGPWR, SIG_IGN);
SaveMapOn = (signr == SIGQUIT) || (signr == SIGTERM) || (signr == SIGPWR); SaveMapOn = (SigNr == SIGQUIT) || (SigNr == SIGTERM) || (SigNr == SIGPWR);
if(signr == SIGTERM){ if(SigNr == SIGTERM){
int Hour, Minute; int Hour, Minute;
GetRealTime(&Hour, &Minute); GetRealTime(&Hour, &Minute);
RebootTime = (Hour * 60 + Minute + 6) % 1440; RebootTime = (Hour * 60 + Minute + 6) % 1440;
@ -98,8 +99,8 @@ static void DefaultHandler(int signr){
#if 0 #if 0
// TODO(fusion): This function was exported in the binary but not referenced anywhere. // TODO(fusion): This function was exported in the binary but not referenced anywhere.
static void ErrorHandler(int signr){ static void ErrorHandler(int SigNr){
error("ErrorHandler: SigNr. %d: %s\n", signr, sigdescr_np(signr)); error("ErrorHandler: SigNr. %d: %s\n", SigNr, GetSignalDescription(SigNr));
EndGame(); EndGame();
LogoutAllPlayers(); LogoutAllPlayers();
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -151,7 +152,7 @@ static void InitTime(void){
SigEvent.sigev_notify_thread_id = gettid(); SigEvent.sigev_notify_thread_id = gettid();
if(timer_create(CLOCK_MONOTONIC, &SigEvent, &BeatTimer) == -1){ if(timer_create(CLOCK_MONOTONIC, &SigEvent, &BeatTimer) == -1){
error("InitTime: Failed to create beat timer: (%d) %s\n", error("InitTime: Failed to create beat timer: (%d) %s\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
throw "cannot create beat timer"; throw "cannot create beat timer";
} }
@ -161,7 +162,7 @@ static void InitTime(void){
TimerSpec.it_value = TimerSpec.it_interval; TimerSpec.it_value = TimerSpec.it_interval;
if(timer_settime(BeatTimer, 0, &TimerSpec, NULL) == -1){ if(timer_settime(BeatTimer, 0, &TimerSpec, NULL) == -1){
error("InitTime: Failed to start beat timer: (%d) %s\n", error("InitTime: Failed to start beat timer: (%d) %s\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
throw "cannot start beat timer"; throw "cannot start beat timer";
} }
} }
@ -169,7 +170,7 @@ static void InitTime(void){
static void ExitTime(void){ static void ExitTime(void){
if(timer_delete(BeatTimer) == -1){ if(timer_delete(BeatTimer) == -1){
error("ExitTime: Failed to delete beat timer: (%d) %s\n", error("ExitTime: Failed to delete beat timer: (%d) %s\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
} }
SigHandler(SIGALRM, SIG_IGN); SigHandler(SIGALRM, SIG_IGN);
@ -448,7 +449,7 @@ static void AdvanceGame(int Delay){
SendAll(); SendAll();
} }
static void SigUsr1Handler(int signr){ static void SigUsr1Handler(int SigNr){
SigUsr1Counter += 1; SigUsr1Counter += 1;
} }
@ -508,7 +509,12 @@ static bool DaemonInit(bool NoFork){
} }
umask(0177); umask(0177);
chdir(SAVEPATH);
// NOTE(fusion): The original binary would change directories to `SAVEPATH`
// here, but because this function is called very early at startup, no config
// values would have been loaded, leaving `SAVEPATH` empty and causing this
// next call to `chdir` to always fail with ENOENT.
//chdir(SAVEPATH);
int OpenMax = sysconf(_SC_OPEN_MAX); int OpenMax = sysconf(_SC_OPEN_MAX);
if(OpenMax < 0){ if(OpenMax < 0){

View File

@ -1569,6 +1569,10 @@ void PatchSector(int SectorX, int SectorY, int SectorZ, bool FullSector,
} }
if(IN.Token == BYTES){ if(IN.Token == BYTES){
if(OffsetX != -1 && OffsetY != -1 && !FieldPatched[OffsetX][OffsetY]){
OUT.writeLn();
}
uint8 *SectorOffset = IN.getBytesequence(); uint8 *SectorOffset = IN.getBytesequence();
OffsetX = (int)SectorOffset[0]; OffsetX = (int)SectorOffset[0];
OffsetY = (int)SectorOffset[1]; OffsetY = (int)SectorOffset[1];
@ -1634,6 +1638,10 @@ void PatchSector(int SectorX, int SectorY, int SectorZ, bool FullSector,
IN.error("unknown map flag"); IN.error("unknown map flag");
} }
} }
if(OffsetX != -1 && OffsetY != -1 && !FieldPatched[OffsetX][OffsetY]){
OUT.writeLn();
}
} }
// NOTE(fusion): Step 4. // NOTE(fusion): Step 4.
@ -1705,7 +1713,7 @@ void PatchSector(int SectorX, int SectorY, int SectorZ, bool FullSector,
if(rename(FileNameBak, FileName) != 0){ if(rename(FileNameBak, FileName) != 0){
int ErrCode = errno; int ErrCode = errno;
error("PatchSector: Fehler %d beim Umbenennen von %s.\n", ErrCode, FileNameBak); error("PatchSector: Fehler %d beim Umbenennen von %s.\n", ErrCode, FileNameBak);
error("# Fehler %d: %s.\n", ErrCode, strerror(ErrCode)); error("# Fehler %d: %s.\n", ErrCode, GetErrorDescription(ErrCode));
throw "cannot patch ORIGMAP"; throw "cannot patch ORIGMAP";
} }
} }

View File

@ -2367,7 +2367,7 @@ void Talk(uint32 CreatureID, int Mode, const char *Addressee, const char *Text,
continue; continue;
} }
if(DistanceX > 1 && DistanceY > 1){ if(DistanceX > 1 || DistanceY > 1){
SendTalk(Spectator->Connection, 0, Creature->Name, Mode, SendTalk(Spectator->Connection, 0, Creature->Name, Mode,
Creature->posx, Creature->posy, Creature->posz, Creature->posx, Creature->posy, Creature->posz,
"pspsps"); "pspsps");
@ -2406,7 +2406,7 @@ void Talk(uint32 CreatureID, int Mode, const char *Addressee, const char *Text,
TConnection *Connection = GetFirstConnection(); TConnection *Connection = GetFirstConnection();
while(Connection != NULL){ while(Connection != NULL){
if(Connection->Live()){ if(Connection->Live()){
SendMessage(Connection, TALK_ADMIN_MESSAGE, Text); SendMessage(Connection, TALK_ADMIN_MESSAGE, "%s", Text);
} }
Connection = GetNextConnection(); Connection = GetNextConnection();

View File

@ -95,7 +95,7 @@ void TQueryManagerConnection::connect(void){
int SocketFlags = fcntl(this->Socket, F_GETFL); int SocketFlags = fcntl(this->Socket, F_GETFL);
if(SocketFlags == -1 || fcntl(this->Socket, F_SETFL, (SocketFlags | O_NONBLOCK)) == -1){ if(SocketFlags == -1 || fcntl(this->Socket, F_SETFL, (SocketFlags | O_NONBLOCK)) == -1){
error("TQueryManagerConnection::connect: Failed to set socket as non-blocking: (%d) %s\n", error("TQueryManagerConnection::connect: Failed to set socket as non-blocking: (%d) %s\n",
errno, strerrordesc_np(errno)); errno, GetErrorDescription(errno));
this->disconnect(); this->disconnect();
continue; continue;
} }

View File

@ -92,7 +92,7 @@ TReadScriptFile::~TReadScriptFile(void){
void TReadScriptFile::open(const char *FileName){ void TReadScriptFile::open(const char *FileName){
int Depth = this->RecursionDepth + 1; int Depth = this->RecursionDepth + 1;
if((Depth + 1) >= NARRAY(this->File)){ if(Depth >= NARRAY(this->File)){
::error("TReadScriptFile::open: Rekursionstiefe zu groß.\n"); ::error("TReadScriptFile::open: Rekursionstiefe zu groß.\n");
throw "Recursion depth too high"; throw "Recursion depth too high";
} }
@ -115,7 +115,7 @@ void TReadScriptFile::open(const char *FileName){
if(this->File[Depth] == NULL){ if(this->File[Depth] == NULL){
int ErrCode = errno; int ErrCode = errno;
::error("TReadScriptFile::open: Kann Datei %s nicht öffnen.\n", this->Filename[Depth]); ::error("TReadScriptFile::open: Kann Datei %s nicht öffnen.\n", this->Filename[Depth]);
::error("Fehler %d: %s.\n", ErrCode, strerror(ErrCode)); ::error("Fehler %d: %s.\n", ErrCode, GetErrorDescription(ErrCode));
throw "Cannot open script-file"; throw "Cannot open script-file";
} }
@ -139,7 +139,7 @@ void TReadScriptFile::close(void){
void TReadScriptFile::error(const char *Text){ void TReadScriptFile::error(const char *Text){
int Depth = this->RecursionDepth; int Depth = this->RecursionDepth;
ASSERT(Depth >= 0 && Depth <= NARRAY(this->File)); ASSERT(Depth >= 0 && Depth < NARRAY(this->File));
const char *Filename = this->Filename[Depth]; const char *Filename = this->Filename[Depth];
if(const char *Slash = findLast(this->Filename[Depth], '/')){ if(const char *Slash = findLast(this->Filename[Depth], '/')){
@ -582,7 +582,7 @@ void TWriteScriptFile::open(const char *FileName){
if(this->File == NULL){ if(this->File == NULL){
int ErrCode = errno; int ErrCode = errno;
::error("TWriteScriptFile: Kann Datei %s nicht anlegen.\n", FileName); ::error("TWriteScriptFile: Kann Datei %s nicht anlegen.\n", FileName);
::error("Fehler %d: %s.\n", ErrCode, strerror(ErrCode)); ::error("Fehler %d: %s.\n", ErrCode, GetErrorDescription(ErrCode));
throw "Cannot create script-file"; throw "Cannot create script-file";
} }
@ -751,7 +751,7 @@ void TReadBinaryFile::close(void){
int ErrCode = errno; int ErrCode = errno;
::error("TReadBinaryFile::close: Fehler beim Schließen der Datei.\n"); ::error("TReadBinaryFile::close: Fehler beim Schließen der Datei.\n");
::error("# Datei: %s, Fehlercode: %d (%s)\n", ::error("# Datei: %s, Fehlercode: %d (%s)\n",
this->Filename, ErrCode, strerror(ErrCode)); this->Filename, ErrCode, GetErrorDescription(ErrCode));
} }
this->File = NULL; this->File = NULL;
} }
@ -811,7 +811,7 @@ uint8 TReadBinaryFile::readByte(void){
int Position = this->getPosition(); int Position = this->getPosition();
::error("TReadBinaryFile::readByte: Fehler beim Lesen eines Bytes\n"); ::error("TReadBinaryFile::readByte: Fehler beim Lesen eines Bytes\n");
::error("# Datei: %s, Position: %d, Rückgabewert: %d, Fehlercode: %d (%s)\n", ::error("# Datei: %s, Position: %d, Rückgabewert: %d, Fehlercode: %d (%s)\n",
this->Filename, Position, Result, ErrCode, strerror(ErrCode)); this->Filename, Position, Result, ErrCode, GetErrorDescription(ErrCode));
// NOTE(fusion): Close file and make a backup, possibly for further inspection. // NOTE(fusion): Close file and make a backup, possibly for further inspection.
if(fclose(this->File) != 0){ if(fclose(this->File) != 0){
@ -832,7 +832,7 @@ void TReadBinaryFile::readBytes(uint8 *Buffer, int Count){
int Position = this->getPosition(); int Position = this->getPosition();
::error("TReadBinaryFile::readBytes: Fehler beim Lesen von %d Bytes\n", Count); ::error("TReadBinaryFile::readBytes: Fehler beim Lesen von %d Bytes\n", Count);
::error("# Datei: %s, Position %d, Rückgabewert: %d, Fehlercode: %d (%s)\n", ::error("# Datei: %s, Position %d, Rückgabewert: %d, Fehlercode: %d (%s)\n",
this->Filename, Position, Result, ErrCode, strerror(ErrCode)); this->Filename, Position, Result, ErrCode, GetErrorDescription(ErrCode));
// NOTE(fusion): Close file and make a backup, possibly for further inspection. // NOTE(fusion): Close file and make a backup, possibly for further inspection.
if(fclose(this->File) != 0){ if(fclose(this->File) != 0){
@ -885,7 +885,7 @@ void TWriteBinaryFile::open(const char *FileName){
if(this->File == NULL){ if(this->File == NULL){
int ErrCode = errno; int ErrCode = errno;
::error("TWriteBinaryFile::open: Kann Datei %s nicht anlegen.\n", FileName); ::error("TWriteBinaryFile::open: Kann Datei %s nicht anlegen.\n", FileName);
::error("Fehler %d: %s.\n", ErrCode, strerror(ErrCode)); ::error("Fehler %d: %s.\n", ErrCode, GetErrorDescription(ErrCode));
snprintf(ErrorString, sizeof(ErrorString), snprintf(ErrorString, sizeof(ErrorString),
"Cannot create file %s.", FileName); "Cannot create file %s.", FileName);
@ -902,7 +902,7 @@ void TWriteBinaryFile::close(void){
int ErrCode = errno; int ErrCode = errno;
::error("TWriteBinaryFile::close: Fehler beim Schließen der Datei.\n"); ::error("TWriteBinaryFile::close: Fehler beim Schließen der Datei.\n");
::error("# Datei: %s, Fehlercode: %d (%s)\n", ::error("# Datei: %s, Fehlercode: %d (%s)\n",
this->Filename, ErrCode, strerror(ErrCode)); this->Filename, ErrCode, GetErrorDescription(ErrCode));
} }
this->File = NULL; this->File = NULL;
} }
@ -928,7 +928,7 @@ void TWriteBinaryFile::writeByte(uint8 Byte){
int ErrCode = errno; int ErrCode = errno;
::error("TWriteBinaryFile::writeByte: Fehler beim Schreiben eines Bytes\n"); ::error("TWriteBinaryFile::writeByte: Fehler beim Schreiben eines Bytes\n");
::error("# Datei: %s, Rückgabewert: %d, Fehlercode: %d (%s)\n", ::error("# Datei: %s, Rückgabewert: %d, Fehlercode: %d (%s)\n",
this->Filename, Result, ErrCode, strerror(ErrCode)); this->Filename, Result, ErrCode, GetErrorDescription(ErrCode));
// NOTE(fusion): Close file and make a backup, possibly for further inspection. // NOTE(fusion): Close file and make a backup, possibly for further inspection.
if(fclose(this->File) != 0){ if(fclose(this->File) != 0){
@ -947,7 +947,7 @@ void TWriteBinaryFile::writeBytes(const uint8 *Buffer, int Count){
int ErrCode = errno; int ErrCode = errno;
::error("TWriteBinaryFile::writeBytes: Fehler beim Schreiben von %d Bytes\n", Count); ::error("TWriteBinaryFile::writeBytes: Fehler beim Schreiben von %d Bytes\n", Count);
::error("# Datei: %s, Rückgabewert: %d, Fehlercode: %d (%s)\n", ::error("# Datei: %s, Rückgabewert: %d, Fehlercode: %d (%s)\n",
this->Filename, Result, ErrCode, strerror(ErrCode)); this->Filename, Result, ErrCode, GetErrorDescription(ErrCode));
// NOTE(fusion): Close file and make a backup, possibly for further inspection. // NOTE(fusion): Close file and make a backup, possibly for further inspection.
if(fclose(this->File) != 0){ if(fclose(this->File) != 0){

View File

@ -349,7 +349,7 @@ void SendResult(TConnection *Connection, RESULT r){
} }
if(Message != NULL){ if(Message != NULL){
SendMessage(Connection, TALK_FAILURE_MESSAGE, Message); SendMessage(Connection, TALK_FAILURE_MESSAGE, "%s", Message);
if(r == ENTERPROTECTIONZONE || r == NOTINVITED || r == MOVENOTPOSSIBLE){ if(r == ENTERPROTECTIONZONE || r == NOTINVITED || r == MOVENOTPOSSIBLE){
SendSnapback(Connection); SendSnapback(Connection);
} }
@ -1721,7 +1721,7 @@ void BroadcastMessage(int Mode, const char *Text, ...){
TConnection *Connection = GetFirstConnection(); TConnection *Connection = GetFirstConnection();
while(Connection != NULL){ while(Connection != NULL){
if(Connection->Live()){ if(Connection->Live()){
SendMessage(Connection, Mode, Message); SendMessage(Connection, Mode, "%s", Message);
} }
Connection = GetNextConnection(); Connection = GetNextConnection();
} }

View File

@ -126,7 +126,7 @@ static void ErrorHandler(const char *Text){
if(SHM != NULL){ if(SHM != NULL){
SHM->Errors += 1; SHM->Errors += 1;
if(SHM->Errors <= 0x8000){ if(SHM->Errors <= 0x8000){
Log("error", Text); Log("error", "%s", Text);
if(SHM->Errors == 0x8000){ if(SHM->Errors == 0x8000){
Log("error", "Zu viele Fehler. Keine weitere Protokollierung.\n"); Log("error", "Zu viele Fehler. Keine weitere Protokollierung.\n");
} }

View File

@ -146,10 +146,10 @@ Semaphore::~Semaphore(void){
int ErrorCode; int ErrorCode;
if((ErrorCode = pthread_mutex_destroy(&this->mutex)) != 0){ if((ErrorCode = pthread_mutex_destroy(&this->mutex)) != 0){
error("Semaphore::~Semaphore: Kann Mutex nicht freigeben: (%d) %s.\n", error("Semaphore::~Semaphore: Kann Mutex nicht freigeben: (%d) %s.\n",
ErrorCode, strerrordesc_np(ErrorCode)); ErrorCode, GetErrorDescription(ErrorCode));
}else if((ErrorCode = pthread_cond_destroy(&this->condition)) != 0){ }else if((ErrorCode = pthread_cond_destroy(&this->condition)) != 0){
error("Semaphore::~Semaphore: Kann Wartebedingung nicht freigeben: (%d) %s.\n", error("Semaphore::~Semaphore: Kann Wartebedingung nicht freigeben: (%d) %s.\n",
ErrorCode, strerrordesc_np(ErrorCode)); ErrorCode, GetErrorDescription(ErrorCode));
} }
} }

View File

@ -1,5 +1,6 @@
#include "common.hh" #include "common.hh"
#include <signal.h>
#include <sys/stat.h> #include <sys/stat.h>
static TErrorFunction *ErrorFunction; static TErrorFunction *ErrorFunction;
@ -95,6 +96,46 @@ bool FileExists(const char *FileName){
return Result; return Result;
} }
#if !defined(_GNU_SOURCE) || (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 32))
# if defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE)
static const char *sigdescr_np(int SigNr){
const char *Description = NULL;
if(SigNr >= 0 && SigNr < NSIG){
Description = sys_siglist[SigNr];
}
return Description;
}
static const char *strerrordesc_np(int ErrCode){
const char *Description = NULL;
if(ErrCode >= 0 && ErrCode < sys_nerr){
Description = sys_errlist[ErrCode];
}
return Description;
}
# else // defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE)
#warning "Current LIBC/GLIBC version doesn't expose error/signal description tables."
static const char *sigdescr_np(int SigNr){ return "No signal description"; }
static const char *strerrordesc_np(int ErrCode){ return "No error description"; }
# endif // defined(_BSD_SOURCE) || defined(_DEFAULT_SOURCE)
#endif //!defined(_GNU_SOURCE) || (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 32))
const char *GetSignalDescription(int SigNr){
const char *Description = sigdescr_np(SigNr);
if(Description == NULL){
Description = "Invalid signal";
}
return Description;
}
const char *GetErrorDescription(int ErrCode){
const char *Description = strerrordesc_np(ErrCode);
if(Description == NULL){
Description = "Invalid error";
}
return Description;
}
// String Utility // String Utility
// ============================================================================= // =============================================================================
bool isSpace(int c){ bool isSpace(int c){

View File

@ -921,7 +921,7 @@ void ProcessBroadcastReply(TBroadcastReplyData *Data){
return; return;
} }
BroadcastMessage(TALK_STATUS_MESSAGE, Data->Message); BroadcastMessage(TALK_STATUS_MESSAGE, "%s", Data->Message);
delete Data; delete Data;
} }

View File

@ -19,7 +19,7 @@ RestartSec=10
# After a SIGTERM, the server will issue shutdown warnings for 5 minutes and # After a SIGTERM, the server will issue shutdown warnings for 5 minutes and
# take another minute or two to save everything and exit. Not waiting for this # take another minute or two to save everything and exit. Not waiting for this
# process to complete would cause data to not be properly saved. # process to complete would cause data to not be properly saved.
TimeoutStopSec=600 TimeoutStopSec=900
LimitCORE=infinity LimitCORE=infinity
StandardOutput=journal StandardOutput=journal
StandardError=journal StandardError=journal

View File

@ -30,6 +30,7 @@ var (
releaseOptions = []string{"-O2"} releaseOptions = []string{"-O2"}
linkerOptions = []string{ linkerOptions = []string{
"-Wl,-t", "-Wl,-t",
"-lrt",
"-lcrypto", "-lcrypto",
} }
) )
@ -80,9 +81,9 @@ func main() {
// DEBUG SWITCH // DEBUG SWITCH
fmt.Fprint(&output, "DEBUG ?= 0\n") fmt.Fprint(&output, "DEBUG ?= 0\n")
fmt.Fprint(&output, "ifneq ($(DEBUG), 0)\n") fmt.Fprint(&output, "ifneq ($(DEBUG), 0)\n")
fmt.Fprintf(&output, "\tCFLAGS += %v\n", strings.Join(debugOptions, " ")) fmt.Fprintf(&output, " CFLAGS += %v\n", strings.Join(debugOptions, " "))
fmt.Fprint(&output, "else\n") fmt.Fprint(&output, "else\n")
fmt.Fprintf(&output, "\tCFLAGS += %v\n", strings.Join(releaseOptions, " ")) fmt.Fprintf(&output, " CFLAGS += %v\n", strings.Join(releaseOptions, " "))
fmt.Fprint(&output, "endif\n\n") fmt.Fprint(&output, "endif\n\n")
// HEADERS // HEADERS