diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/communication.cc | 3 | ||||
| -rw-r--r-- | src/communication.hh | 3 | ||||
| -rw-r--r-- | src/connections.cc | 3 | ||||
| -rw-r--r-- | src/cract.cc | 1 | ||||
| -rw-r--r-- | src/crmain.cc | 3 | ||||
| -rw-r--r-- | src/crnonpl.cc | 4 | ||||
| -rw-r--r-- | src/crplayer.cc | 2 | ||||
| -rw-r--r-- | src/houses.cc | 5 | ||||
| -rw-r--r-- | src/houses.hh | 6 | ||||
| -rw-r--r-- | src/info.cc | 3 | ||||
| -rw-r--r-- | src/magic.cc | 4 | ||||
| -rw-r--r-- | src/magic.hh | 1 | ||||
| -rw-r--r-- | src/main.cc | 12 | ||||
| -rw-r--r-- | src/map.cc | 1 | ||||
| -rw-r--r-- | src/moveuse.cc | 3 | ||||
| -rw-r--r-- | src/operate.cc | 2 | ||||
| -rw-r--r-- | src/operate.hh | 10 | ||||
| -rw-r--r-- | src/query.hh | 29 | ||||
| -rw-r--r-- | src/receiving.cc | 4 | ||||
| -rw-r--r-- | src/sending.cc | 4 | ||||
| -rw-r--r-- | src/shm.cc | 3 | ||||
| -rw-r--r-- | src/stubs.hh | 36 | ||||
| -rw-r--r-- | src/writer.cc | 1045 | ||||
| -rw-r--r-- | src/writer.hh | 155 |
24 files changed, 1271 insertions, 71 deletions
diff --git a/src/communication.cc b/src/communication.cc index 799cd42..99c891a 100644 --- a/src/communication.cc +++ b/src/communication.cc @@ -5,8 +5,7 @@ #include "crypto.hh" #include "query.hh" #include "threads.hh" - -#include "stubs.hh" +#include "writer.hh" #include <arpa/inet.h> #include <fcntl.h> diff --git a/src/communication.hh b/src/communication.hh index f466b9e..8d2574b 100644 --- a/src/communication.hh +++ b/src/communication.hh @@ -3,7 +3,8 @@ #include "common.hh" #include "connections.hh" -#include "cr.hh" + +struct TPlayerData; struct TWaitinglistEntry { TWaitinglistEntry *Next; diff --git a/src/connections.cc b/src/connections.cc index 7cf70c7..cef9fa6 100644 --- a/src/connections.cc +++ b/src/connections.cc @@ -2,8 +2,7 @@ #include "cr.hh" #include "info.hh" #include "threads.hh" - -#include "stubs.hh" +#include "writer.hh" #include <arpa/inet.h> #include <netinet/in.h> diff --git a/src/cract.cc b/src/cract.cc index 15fdf66..a7bb2fe 100644 --- a/src/cract.cc +++ b/src/cract.cc @@ -1,5 +1,6 @@ #include "cr.hh" #include "config.hh" +#include "houses.hh" #include "info.hh" #include "operate.hh" diff --git a/src/crmain.cc b/src/crmain.cc index c70acb2..96ccc92 100644 --- a/src/crmain.cc +++ b/src/crmain.cc @@ -4,8 +4,7 @@ #include "enums.hh" #include "info.hh" #include "operate.hh" - -#include "stubs.hh" +#include "writer.hh" #include <dirent.h> diff --git a/src/crnonpl.cc b/src/crnonpl.cc index 29a5d11..277e2e6 100644 --- a/src/crnonpl.cc +++ b/src/crnonpl.cc @@ -2,9 +2,9 @@ #include "config.hh" #include "containers.hh" #include "info.hh" +#include "magic.hh" #include "operate.hh" - -#include "stubs.hh" +#include "writer.hh" #include <dirent.h> diff --git a/src/crplayer.cc b/src/crplayer.cc index 9978527..b8e1926 100644 --- a/src/crplayer.cc +++ b/src/crplayer.cc @@ -1,10 +1,12 @@ #include "cr.hh" #include "config.hh" +#include "houses.hh" #include "info.hh" #include "moveuse.hh" #include "operate.hh" #include "query.hh" #include "threads.hh" +#include "writer.hh" #include "stubs.hh" diff --git a/src/houses.cc b/src/houses.cc index 87f4556..16bc0ee 100644 --- a/src/houses.cc +++ b/src/houses.cc @@ -4,8 +4,7 @@ #include "info.hh" #include "moveuse.hh" #include "query.hh" - -#include "stubs.hh" +#include "writer.hh" static vector<THelpDepot> HelpDepot(0, 9, 10); static int HelpDepots; @@ -1904,6 +1903,6 @@ void InitHouses(void){ delete QueryManagerConnection; } -void SaveHouses(void){ +void ExitHouses(void){ SaveOwners(); } diff --git a/src/houses.hh b/src/houses.hh index 9326e65..8402bf2 100644 --- a/src/houses.hh +++ b/src/houses.hh @@ -3,11 +3,13 @@ #include "common.hh" #include "containers.hh" -#include "cr.hh" #include "map.hh" #define MAX_HOUSE_GUEST_NAME 60 +struct TPlayer; +struct TPlayerData; + struct THelpDepot { uint32 CharacterID; Object Box; @@ -99,6 +101,6 @@ void LoadOwners(void); void SaveOwners(void); void ProcessHouses(void); void InitHouses(void); -void SaveHouses(void); +void ExitHouses(void); #endif //TIBIA_HOUSES_HH_ diff --git a/src/info.cc b/src/info.cc index fa01ecb..08433f2 100644 --- a/src/info.cc +++ b/src/info.cc @@ -1,7 +1,6 @@ #include "info.hh" #include "cr.hh" - -#include "stubs.hh" +#include "magic.hh" const char *GetLiquidName(int LiquidType){ const char *LiquidName; diff --git a/src/magic.cc b/src/magic.cc index 1282e5d..ca7aaec 100644 --- a/src/magic.cc +++ b/src/magic.cc @@ -1,9 +1,9 @@ #include "magic.hh" #include "config.hh" +#include "houses.hh" #include "info.hh" #include "operate.hh" - -#include "stubs.hh" +#include "writer.hh" #include <fstream> #include <sstream> diff --git a/src/magic.hh b/src/magic.hh index ff14c48..30570b0 100644 --- a/src/magic.hh +++ b/src/magic.hh @@ -3,6 +3,7 @@ #include "common.hh" #include "cr.hh" +#include "map.hh" enum : int { FIELD_TYPE_FIRE = 1, diff --git a/src/main.cc b/src/main.cc index 4b52ab2..c64838a 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1,6 +1,7 @@ #include "common.hh" #include "communication.hh" #include "config.hh" +#include "houses.hh" #include "info.hh" #include "map.hh" #include "magic.hh" @@ -8,6 +9,7 @@ #include "objects.hh" #include "operate.hh" #include "query.hh" +#include "writer.hh" #include "stubs.hh" @@ -217,13 +219,13 @@ static void InitAll(void){ LoadWorldConfig(); InitSHM(!BeADaemon); LockGame(); - //InitLog("game"); + InitLog("game"); srand(time(NULL)); InitSignalHandler(); InitConnections(); InitCommunication(); InitStrings(); - //InitWriter(); + InitWriter(); //InitReader(); InitObjects(); InitMap(); @@ -231,7 +233,7 @@ static void InitAll(void){ InitMoveUse(); InitMagic(); InitCr(); - //InitHouses(); + InitHouses(); InitTime(); ApplyPatches(); }catch(const char *str){ @@ -249,11 +251,11 @@ static void ExitAll(void){ ExitMagic(); ExitMoveUse(); ExitInfo(); - //ExitHouses(); + ExitHouses(); ExitMap(SaveMapOn); ExitObjects(); //ExitReader(); - //ExitWriter(); + ExitWriter(); ExitStrings(); ExitCommunication(); ExitConnections(); @@ -2,6 +2,7 @@ #include "containers.hh" #include "config.hh" #include "enums.hh" +#include "houses.hh" #include "script.hh" #include "stubs.hh" diff --git a/src/moveuse.cc b/src/moveuse.cc index 9cdcd95..3db2016 100644 --- a/src/moveuse.cc +++ b/src/moveuse.cc @@ -1,7 +1,10 @@ #include "moveuse.hh" #include "config.hh" +#include "houses.hh" #include "info.hh" +#include "magic.hh" #include "operate.hh" +#include "writer.hh" #include "stubs.hh" diff --git a/src/operate.cc b/src/operate.cc index 8123ba0..a9644c2 100644 --- a/src/operate.cc +++ b/src/operate.cc @@ -1,6 +1,8 @@ #include "operate.hh" #include "config.hh" +#include "houses.hh" #include "info.hh" +#include "magic.hh" #include "moveuse.hh" #include "stubs.hh" diff --git a/src/operate.hh b/src/operate.hh index 85cbf9f..6d55416 100644 --- a/src/operate.hh +++ b/src/operate.hh @@ -97,12 +97,12 @@ struct TListener { }; struct TReportedStatement { - uint32 StatementID; + uint32 StatementID; int TimeStamp; - uint32 CharacterID; - int Mode; - int Channel; - char Text[256]; + uint32 CharacterID; + int Mode; + int Channel; + char Text[256]; }; void AnnounceMovingCreature(uint32 CreatureID, Object Con); diff --git a/src/query.hh b/src/query.hh index 0db66cc..a69413a 100644 --- a/src/query.hh +++ b/src/query.hh @@ -18,7 +18,34 @@ struct TQueryManagerConnection{ bool GamemasterRequired, uint32 *CharacterID, int *Sex, char *Guild, char *Rank, char *Title, int *NumberOfBuddies, uint32 *BuddyIDs, char (*BuddyNames)[30], uint8 *Rights, bool *PremiumAccountActivated); - void decrementIsOnline(uint32 CharacterID); + int logoutGame(uint32 CharacterID, int Level, const char *Profession, + const char *Residence, time_t LastLoginTime, int TutorActivities); + int createPlayerlist(int NumberOfPlayers, const char **Names, int *Levels, + const char (*Professions)[30], bool *NewRecord); + int decrementIsOnline(uint32 CharacterID); + int clearIsOnline(int *NumberOfAffectedPlayers); + int addBuddy(uint32 AccountID, uint32 BuddyID); + int removeBuddy(uint32 AccountID, uint32 BuddyID); + + int logKilledCreatures(int NumberOfRaces, const char **Names, + int *KilledPlayers,int *KilledCreatures); + + int logCharacterDeath(uint32 CharacterID, int Level, uint32 OffenderID, + const char *Remark, bool Unjustified, time_t Time); + + int setNotation(uint32 GamemasterID, const char *PlayerName, const char *IPAddress, + const char *Reason, const char *Comment, uint32 *BanishmentID); + int setNamelock(uint32 GamemasterID, const char *PlayerName, const char *IPAddress, + const char *Reason, const char *Comment); + int banishAccount(uint32 GamemasterID, const char *PlayerName, const char *IPAddress, + const char *Reason, const char *Comment, bool *FinalWarning, int *Days, + uint32 *BanishmentID); + int reportStatement(uint32 ReporterID, const char *PlayerName, const char *Reason, + const char *Comment, uint32 BanishmentID, uint32 StatementID, int NumberOfStatements, + uint32 *StatementIDs, int *TimeStamps, uint32 *CharacterIDs, char (*Channels)[30], + char (*Texts)[256]); + int banishIPAddress(uint32 GamemasterID, const char *PlayerName, const char *IPAddress, + const char *Reason, const char *Comment); int insertHouses(int NumberOfHouses, uint16 *HouseIDs, char **Names, int *Rents, char **Descriptions, int *Sizes, int *PositionsX, diff --git a/src/receiving.cc b/src/receiving.cc index 1c6ee00..147f085 100644 --- a/src/receiving.cc +++ b/src/receiving.cc @@ -1,7 +1,7 @@ #include "connections.hh" +#include "houses.hh" #include "info.hh" - -#include "stubs.hh" +#include "writer.hh" #include <signal.h> diff --git a/src/sending.cc b/src/sending.cc index 14551da..970439f 100644 --- a/src/sending.cc +++ b/src/sending.cc @@ -2,9 +2,9 @@ #include "config.hh" #include "cr.hh" #include "info.hh" +#include "magic.hh" #include "map.hh" - -#include "stubs.hh" +#include "writer.hh" #include <signal.h> @@ -2,8 +2,7 @@ #include "config.hh" #include "enums.hh" #include "threads.hh" - -#include "stubs.hh" +#include "writer.hh" #include <sys/shm.h> diff --git a/src/stubs.hh b/src/stubs.hh index 191c733..4e974d7 100644 --- a/src/stubs.hh +++ b/src/stubs.hh @@ -3,11 +3,8 @@ #include "common.hh" #include "enums.hh" -#include "connections.hh" #include "cr.hh" -#include "magic.hh" #include "map.hh" -#include "operate.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. @@ -15,45 +12,12 @@ typedef void TRefreshSectorFunction(int SectorX, int SectorY, int SectorZ, const uint8 *Data, int Size); typedef void TSendMailsFunction(TPlayerData *PlayerData); -extern void AbortWriter(void); -extern void AddBuddyOrder(TCreature *Creature, uint32 CharacterID); -extern void RemoveBuddyOrder(TCreature *Creature, uint32 BuddyID); -extern void ChangeGuests(uint16 HouseID, TPlayer *Player, const char *Text); -extern void ChangeSubowners(uint16 HouseID, TPlayer *Player, const char *Text); -extern void ChangeNameDoor(Object Door, TPlayer *Player, const char *Text); -extern void CharacterDeathOrder(TCreature *Creature, int OldLevel, - uint32 Offender, const char *Remark, bool Unjustified); -extern void CleanHouseField(int x, int y, int z); -extern void DecrementIsOnlineOrder(uint32 CharacterID); -extern void GetExitPosition(uint16 HouseID, int *x, int *y, int *z); -extern int GetOrderBufferSpace(void); -extern const char *GetHouseName(uint16 HouseID); -extern const char *GetHouseOwner(uint16 HouseID); -extern void InitLog(const char *ProtocolName); -extern bool IsInvited(uint16 HouseID, TPlayer *Player, int TimeStamp); -extern bool IsOwner(uint16 HouseID, TPlayer *Player); -extern void KickGuest(uint16 HouseID, TPlayer *Host, TPlayer *Guest); -extern void KillStatisticsOrder(int NumberOfRaces, const char *RaceNames, int *KilledPlayers, int *KilledCreatures); extern void LoadCharacterOrder(uint32 CharacterID); extern void LoadSectorOrder(int SectorX, int SectorY, int SectorZ); -extern void Log(const char *ProtocolName, const char *Text, ...) ATTR_PRINTF(2, 3); -extern void LogoutOrder(TPlayer *Player); -extern bool MayOpenDoor(Object Door, TPlayer *Player); -extern void PrepareHouseCleanup(void); -extern void FinishHouseCleanup(void); -extern void PlayerlistOrder(int NumberOfPlayers, char *PlayerNames, int *PlayerLevels, int *PlayerProfessions); extern void ProcessReaderThreadReplies(TRefreshSectorFunction *RefreshSector, TSendMailsFunction *SendMails); -extern void ProcessWriterThreadReplies(void); -extern void PunishmentOrder(TCreature *Creature, const char *Name, const char *IPAddress, - int Reason, int Action, const char *Comment, int NumberOfStatements, - vector<TReportedStatement> *ReportedStatements, uint32 StatementID, - bool IPBanishment); extern void SavePlayerData(TPlayerData *Slot); extern bool LoadPlayerData(TPlayerData *Slot); extern bool PlayerDataExists(uint32 CharacterID); extern void SavePlayerDataOrder(void); -extern void ShowGuestList(uint16 HouseID, TPlayer *Player, char *Buffer); -extern void ShowSubownerList(uint16 HouseID, TPlayer *Player, char *Buffer); -extern void ShowNameDoor(Object Door, TPlayer *Player, char *Buffer); #endif //TIBIA_STUBS_HH_ diff --git a/src/writer.cc b/src/writer.cc new file mode 100644 index 0000000..1b84e98 --- /dev/null +++ b/src/writer.cc @@ -0,0 +1,1045 @@ +#include "writer.hh" +#include "config.hh" +#include "cr.hh" +#include "info.hh" +#include "query.hh" +#include "threads.hh" + +static ThreadHandle ProtocolThread; +static ThreadHandle WriterThread; + +static TProtocolThreadOrder ProtocolBuffer[1000]; +static int ProtocolPointerWrite; +static int ProtocolPointerRead; +static Semaphore ProtocolMutex(1); +static Semaphore ProtocolBufferEmpty(1000); +static Semaphore ProtocolBufferFull(0); + +static TWriterThreadOrder OrderBuffer[2000]; +static int OrderPointerWrite; +static int OrderPointerRead; +static Semaphore OrderBufferEmpty(2000); +static Semaphore OrderBufferFull(0); + +static TWriterThreadReply ReplyBuffer[100]; +static int ReplyPointerWrite; +static int ReplyPointerRead; + +static TQueryManagerConnection *QueryManagerConnection; + +// Protocol Orders +// ============================================================================= +void InitProtocol(void){ + ProtocolPointerWrite = 0; + ProtocolPointerRead = 0; +} + +void InsertProtocolOrder(const char *ProtocolName, const char *Text){ + if(ProtocolName == NULL){ + error("InsertProtocolOrder: Protokoll-Name nicht angegeben.\n"); + return; + } + + if(Text == NULL){ + error("InsertProtocolOrder: Text nicht angegeben.\n"); + return; + } + + int Orders = (ProtocolPointerWrite - ProtocolPointerRead); + if(Orders >= NARRAY(ProtocolBuffer) && strcmp(ProtocolName, "error") != 0){ + error("InsertProtocolOrder: Protokoll-Puffer ist voll => Vergrößern.\n"); + } + + ProtocolMutex.down(); + ProtocolBufferEmpty.down(); + int WritePos = ProtocolPointerWrite % NARRAY(ProtocolBuffer); + strcpy(ProtocolBuffer[WritePos].ProtocolName, ProtocolName); + strcpy(ProtocolBuffer[WritePos].Text, Text); + ProtocolPointerWrite += 1; + ProtocolBufferFull.up(); + ProtocolMutex.up(); +} + +void GetProtocolOrder(TProtocolThreadOrder *Order){ + ProtocolBufferFull.down(); + int ReadPos = ProtocolPointerRead % NARRAY(ProtocolBuffer); + *Order = ProtocolBuffer[ReadPos]; + ProtocolPointerRead += 1; + ProtocolBufferEmpty.up(); +} + +void WriteProtocol(const char *ProtocolName, const char *Text){ + char FileName[4096]; + snprintf(FileName, sizeof(FileName), "%s/%s.log", LOGPATH, ProtocolName); + + FILE *File = fopen(FileName, "at"); + if(File != NULL){ + fprintf(File, "%s", Text); + if(fclose(File) != 0){ + error("WriteProtocol: Fehler %d beim Schließen der Datei.\n", errno); + } + } +} + +int ProtocolThreadLoop(void *Unused){ + TProtocolThreadOrder Order = {}; + while(true){ + GetProtocolOrder(&Order); + if(Order.ProtocolName[0] == 0){ + break; + } + + WriteProtocol(Order.ProtocolName, Order.Text); + } + return 0; +} + +void InitLog(const char *ProtocolName){ + if(ProtocolName == NULL){ + error("InitLog: Protokoll-Name nicht angegeben.\n"); + return; + } + + char FileName[4096]; + snprintf(FileName, sizeof(FileName), "%s/%s.log", LOGPATH, ProtocolName); + + FILE *File = fopen(FileName, "at"); + if(File ==NULL){ + error("InitLog: Kann Protokoll %s nicht anlegen.\n", ProtocolName); + return; + } + + // NOTE(fusion): `ctime` will already add a new line character. + time_t Time = time(NULL); + fprintf(File, "-------------------------------------------------------------------------------\n"); + fprintf(File, "Tibia - Graphical Multi-User-Dungeon\n"); + fprintf(File, "%s.log - gestartet %s", ProtocolName, ctime(&Time)); + fclose(File); +} + +void Log(const char *ProtocolName, const char *Text, ...){ + if(ProtocolName == NULL || ProtocolName[0] == 0){ + error("Log: Protokoll-Name nicht angegeben.\n"); + return; + } + + bool WriteDate = (strcmp(ProtocolName, "bugreport") == 0) + || (strcmp(ProtocolName, "client-error") == 0) + || (strcmp(ProtocolName, "load") == 0); + + char Output[256]; + va_list ap; + va_start(ap, Text); + vsnprintf(Output, sizeof(Output), Text, ap); + va_end(ap); + + char Line[256]; + if(WriteDate){ + print(2, "%s.log: %s", ProtocolName, Output); + struct tm LocalTime = GetLocalTimeTM(time(NULL)); + snprintf(Line, sizeof(Line), "%02d.%02d.%04d %02d:%02d:%02d (%u): %s", + LocalTime.tm_mday, LocalTime.tm_mon + 1, LocalTime.tm_year + 1900, + LocalTime.tm_hour, LocalTime.tm_min, LocalTime.tm_sec, RoundNr, Output); + }else{ + strcpy(Line, Output); + } + + if(Line[0] != 0){ + int LineEnd = (int)strlen(Line); + if(LineEnd < (int)(sizeof(Line) - 1)){ + Line[LineEnd] = '\n'; + Line[LineEnd + 1] = 0; + }else{ + Line[LineEnd - 1] = '\n'; + } + + if(ProtocolThread != INVALID_THREAD_HANDLE){ + InsertProtocolOrder(ProtocolName, Line); + }else{ + WriteProtocol(ProtocolName, Line); + } + } +} + +// Writer Orders +// ============================================================================= +void InitWriterBuffers(void){ + OrderPointerWrite = 0; + ReplyPointerWrite = 0; + OrderPointerRead = 0; + ReplyPointerRead = 0; +} + +int GetOrderBufferSpace(void){ + int Result = INT_MAX; + if(WriterThread != INVALID_THREAD_HANDLE){ + int Orders = (OrderPointerWrite - OrderPointerRead); + Result = NARRAY(OrderBuffer) - Orders; + } + return Result; +} + +void InsertOrder(TWriterThreadOrderType OrderType, const void *Data){ + if(WriterThread != INVALID_THREAD_HANDLE){ + int Orders = (OrderPointerWrite - OrderPointerRead); + if(Orders >= NARRAY(OrderBuffer)){ + error("InsertOrder (Writer): Order-Puffer ist voll => Vergrößern.\n"); + } + + OrderBufferEmpty.down(); + int WritePos = OrderPointerWrite % NARRAY(OrderBuffer); + OrderBuffer[WritePos].OrderType = OrderType; + OrderBuffer[WritePos].Data = Data; + OrderPointerWrite += 1; + OrderBufferFull.up(); + } +} + +void GetOrder(TWriterThreadOrder *Order){ + OrderBufferFull.down(); + int ReadPos = OrderPointerRead % NARRAY(OrderBuffer); + *Order = OrderBuffer[ReadPos]; + OrderPointerRead += 1; + OrderBufferEmpty.up(); +} + +void TerminateWriterOrder(void){ + InsertOrder(ORDER_TERMINATE, NULL); +} + +void LogoutOrder(TPlayer *Player){ + if(Player == NULL){ + error("LogoutOrder: Übergebener Spieler existiert nicht.\n"); + return; + } + + TLogoutOrderData *Data = new TLogoutOrderData; + Data->CharacterID = Player->ID; + Data->Level = Player->Skills[SKILL_LEVEL]->Get(); + Data->Profession = Player->GetActiveProfession(); + if(Player->PlayerData == NULL){ + error("LogoutOrder: PlayerData ist NULL.\n"); + Data->LastLoginTime = 0; + }else{ + Data->LastLoginTime = Player->PlayerData->LastLoginTime; + } + Data->TutorActivities = Player->TutorActivities; + + // TODO(fusion): LOL. + if(Player->startx == 32097){ + strcpy(Data->Residence, "Rookgaard"); + }else if(Player->startx == 32360){ + strcpy(Data->Residence, "Carlin"); + }else if(Player->startx == 32369){ + strcpy(Data->Residence, "Thais"); + }else if(Player->startx == 32595){ + strcpy(Data->Residence, "Port Hope"); + }else if(Player->startx == 32649){ + strcpy(Data->Residence, "Kazordoon"); + }else if(Player->startx == 32732){ + strcpy(Data->Residence, "Ab'Dendriel"); + }else if(Player->startx == 32957){ + strcpy(Data->Residence, "Venore"); + }else if(Player->startx == 33194){ + strcpy(Data->Residence, "Ankrahmun"); + }else if(Player->startx == 33213){ + strcpy(Data->Residence, "Darashia"); + }else if(Player->startx == 33217){ + strcpy(Data->Residence, "Edron"); + }else{ + error("LogoutOrder: Unbekannte Startkoordinate [%d,%d,%d] bei Spieler %s.\n", + Player->startx, Player->starty, Player->startz, Player->Name); + strcpy(Data->Residence, "Unknown"); + } + + InsertOrder(ORDER_LOGOUT, Data); +} + +void PlayerlistOrder(int NumberOfPlayers, const char *PlayerNames, + int *PlayerLevels, int *PlayerProfessions){ + if(PlayerNames == NULL){ + error("PlayerlistOrder: PlayerNames ist NULL.\n"); + return; + } + + if(PlayerLevels == NULL){ + error("PlayerlistOrder: PlayerLevels ist NULL.\n"); + return; + } + + if(PlayerProfessions == NULL){ + error("PlayerlistOrder: PlayerProfessions ist NULL.\n"); + return; + } + + TPlayerlistOrderData *Data = new TPlayerlistOrderData; + Data->NumberOfPlayers = NumberOfPlayers; + Data->PlayerNames = PlayerNames; + Data->PlayerLevels = PlayerLevels; + Data->PlayerProfessions = PlayerProfessions; + + InsertOrder(ORDER_PLAYERLIST, Data); +} + +void KillStatisticsOrder(int NumberOfRaces, const char *RaceNames, + int *KilledPlayers, int *KilledCreatures){ + if(RaceNames == NULL){ + error("KillStatisticsOrder: RaceNames ist NULL.\n"); + return; + } + + if(KilledPlayers == NULL){ + error("KillStatisticsOrder: KilledPlayers ist NULL.\n"); + return; + } + + if(KilledCreatures == NULL){ + error("KillStatisticsOrder: KilledCreatures ist NULL.\n"); + return; + } + + TKillStatisticsOrderData *Data = new TKillStatisticsOrderData; + Data->NumberOfRaces = NumberOfRaces; + Data->RaceNames = RaceNames; + Data->KilledPlayers = KilledPlayers; + Data->KilledCreatures = KilledCreatures; + + InsertOrder(ORDER_KILLSTATISTICS, Data); +} + +void PunishmentOrder(TCreature *Gamemaster, const char *Name, const char *IPAddress, + int Reason, int Action, const char *Comment, int NumberOfStatements, + vector<TReportedStatement> *ReportedStatements, uint32 StatementID, + bool IPBanishment){ + if(Name == NULL){ + error("PunishmentOrder: Name ist NULL.\n"); + return; + } + + if(Comment == NULL){ + error("PunishmentOrder: Comment ist NULL.\n"); + return; + } + + TPunishmentOrderData *Data = new TPunishmentOrderData; + if(Gamemaster != NULL){ + Data->GamemasterID = Gamemaster->ID; + strcpy(Data->GamemasterName, Gamemaster->Name); + }else{ + Data->GamemasterID = 0; + strcpy(Data->GamemasterName, "automatic"); + } + strcpy(Data->CriminalName, Name); + strcpy(Data->CriminalIPAddress, (IPAddress != NULL ? IPAddress : "")); + Data->Reason = Reason; + Data->Action = Action; + strcpy(Data->Comment, Comment); + Data->NumberOfStatements = NumberOfStatements; + Data->ReportedStatements = ReportedStatements; + Data->StatementID = StatementID; + Data->IPBanishment = IPBanishment; + + InsertOrder(ORDER_PUNISHMENT, Data); +} + +void CharacterDeathOrder(TCreature *Creature, int OldLevel, + uint32 OffenderID, const char *Remark, bool Unjustified){ + if(Creature == NULL){ + error("CharacterDeathOrder: cr ist NULL.\n"); + return; + } + + if(Remark == NULL){ + error("CharacterDeathOrder: Remark ist NULL.\n"); + return; + } + + TCharacterDeathOrderData *Data = new TCharacterDeathOrderData; + Data->CharacterID = Creature->ID; + Data->Level = OldLevel; + Data->Offender = OffenderID; + strcpy(Data->Remark, Remark); + Data->Unjustified = Unjustified; + Data->Time = time(NULL); + + InsertOrder(ORDER_CHARACTERDEATH, Data); +} + +void AddBuddyOrder(TCreature *Creature, uint32 BuddyID){ + if(Creature == NULL){ + error("AddBuddyOrder: cr ist NULL.\n"); + return; + } + + if(Creature->Type != PLAYER){ + error("AddBuddyOrder: Kreatur ist kein Spieler.\n"); + return; + } + + TBuddyOrderData *Data = new TBuddyOrderData; + Data->AccountID = ((TPlayer*)Creature)->AccountID; + Data->Buddy = BuddyID; + + InsertOrder(ORDER_ADDBUDDY, Data); +} + +void RemoveBuddyOrder(TCreature *Creature, uint32 BuddyID){ + if(Creature == NULL){ + error("RemoveBuddyOrder: cr ist NULL.\n"); + return; + } + + if(Creature->Type != PLAYER){ + error("RemoveBuddyOrder: Kreatur ist kein Spieler.\n"); + return; + } + + TBuddyOrderData *Data = new TBuddyOrderData; + Data->AccountID = ((TPlayer*)Creature)->AccountID; + Data->Buddy = BuddyID; + + InsertOrder(ORDER_REMOVEBUDDY, Data); +} + +void DecrementIsOnlineOrder(uint32 CharacterID){ + void *Data = (void*)((uintptr)CharacterID); + InsertOrder(ORDER_DECREMENTISONLINE, Data); +} + +void SavePlayerDataOrder(void){ + InsertOrder(ORDER_SAVEPLAYERDATA, NULL); +} + +void ProcessLogoutOrder(TLogoutOrderData *Data){ + if(Data == NULL){ + error("ProcessLogoutOrder: Keine Daten übergeben.\n"); + return; + } + + if(Data->TutorActivities > 0){ + print(3, "%d Tutor-Punkte für Spieler %u.\n", + Data->TutorActivities, Data->CharacterID); + } + + char ProfessionName[30]; + GetProfessionName(ProfessionName, Data->Profession, false, true); + int Ret = QueryManagerConnection->logoutGame(Data->CharacterID, Data->Level, + ProfessionName, Data->Residence, Data->LastLoginTime, Data->TutorActivities); + if(Ret != 0){ + error("ProcessLogoutOrder: Logout für Spieler %u fehlgeschlagen.\n", + Data->CharacterID); + } + + delete Data; +} + +void ProcessPlayerlistOrder(TPlayerlistOrderData *Data){ + if(Data == NULL){ + error("ProcessPlayerlistOrder: Keine Daten übergeben.\n"); + return; + } + + bool NewRecord = false; + if(Data->NumberOfPlayers <= 0){ + int Ret = QueryManagerConnection->createPlayerlist(Data->NumberOfPlayers, + NULL, NULL, NULL, &NewRecord); + if(Ret != 0){ + error("ProcessPlayerlistOrder: Anfrage fehlgeschlagen (1).\n"); + } + }else{ + const char **Names = (const char**)alloca(Data->NumberOfPlayers * sizeof(const char*)); + int *Levels = (int*)alloca(Data->NumberOfPlayers * sizeof(int)); + char (*Professions)[30] = (char(*)[30])alloca(Data->NumberOfPlayers * 30); + for(int PlayerNr = 0; PlayerNr < Data->NumberOfPlayers; PlayerNr += 1){ + Names[PlayerNr] = &Data->PlayerNames[PlayerNr * 30]; + Levels[PlayerNr] = Data->PlayerLevels[PlayerNr]; + GetProfessionName(Professions[PlayerNr], + Data->PlayerProfessions[PlayerNr], false, true); + } + + int Ret = QueryManagerConnection->createPlayerlist(Data->NumberOfPlayers, + Names, Levels, Professions, &NewRecord); + if(Ret != 0){ + error("ProcessPlayerlistOrder: Anfrage fehlgeschlagen (2).\n"); + } + } + + if(NewRecord){ + BroadcastReply("New record: %d players are logged in.", Data->NumberOfPlayers); + } + + delete[] Data->PlayerNames; + delete[] Data->PlayerLevels; + delete[] Data->PlayerProfessions; + delete Data; +} + +void ProcessKillStatisticsOrder(TKillStatisticsOrderData *Data){ + if(Data == NULL){ + error("ProcessKillStatisticsOrder: Keine Daten übergeben.\n"); + return; + } + + const char **RaceNames = (const char**)alloca(Data->NumberOfRaces * sizeof(const char*)); + int *KilledPlayers = (int*)alloca(Data->NumberOfRaces * sizeof(int)); + int *KilledCreatures = (int*)alloca(Data->NumberOfRaces * sizeof(int)); + for(int RaceNr = 0; RaceNr < Data->NumberOfRaces; RaceNr += 1){ + RaceNames[RaceNr] = &Data->RaceNames[RaceNr * 30]; + KilledPlayers[RaceNr] = Data->KilledPlayers[RaceNr]; + KilledCreatures[RaceNr] = Data->KilledCreatures[RaceNr]; + } + + int Ret = QueryManagerConnection->logKilledCreatures(Data->NumberOfRaces, + RaceNames, KilledPlayers, KilledCreatures); + if(Ret != 0){ + error("ProcessKillStatisticsOrder: Anfrage fehlgeschlagen.\n"); + } + + delete[] Data->RaceNames; + delete[] Data->KilledPlayers; + delete[] Data->KilledCreatures; + delete Data; +} + +static const char *GetStatementOutputChannel(int Mode, int Channel){ + const char *Result; + switch(Mode){ + case TALK_SAY: Result = "Say"; break; + case TALK_WHISPER: Result = "Whisper"; break; + case TALK_YELL: Result = "Yell"; break; + case TALK_PRIVATE_MESSAGE: Result = "Private Message"; break; + case TALK_GAMEMASTER_REQUEST: Result = "Gamemaster Request"; break; + case TALK_GAMEMASTER_ANSWER: Result = "Gamemaster Answer"; break; + case TALK_PLAYER_ANSWER: Result = "Player Answer"; break; + case TALK_GAMEMASTER_BROADCAST: Result = "Broadcast"; break; + case TALK_GAMEMASTER_MESSAGE: Result = "Private Message"; break; + + case TALK_CHANNEL_CALL: + case TALK_GAMEMASTER_CHANNELCALL: + case TALK_HIGHLIGHT_CHANNELCALL:{ + switch(Channel){ + case CHANNEL_GUILD: Result = "Guild Channel"; break; + case CHANNEL_GAMEMASTER: Result = "Gamemaster Channel"; break; + case CHANNEL_TUTOR: Result = "Tutor Channel"; break; + case CHANNEL_GAMECHAT: Result = "Game Chat"; break; + case CHANNEL_TRADE: Result = "Trade Channel"; break; + case CHANNEL_RLCHAT: Result = "Reallife Chat"; break; + case CHANNEL_HELP: Result = "Help Channel"; break; + default: Result = "Private Chat Channel"; break; + } + break; + } + + default:{ + error("ProcessPunishmentOrder: Ungültiger Modus %d.\n", Mode); + Result = "Unknown"; + break; + } + } + + return Result; +} + +void ProcessPunishmentOrder(TPunishmentOrderData *Data){ + // TODO(fusion): I feel this it too complex for handling a simple banishment system. + if(Data == NULL){ + error("ProcessPunishmentOrder: Keine Daten übergeben.\n"); + return; + } + + bool Ok = true; + const char *Reason = GetBanishmentReason(Data->Reason); + uint32 BanishmentID = 0; + + if(Ok && Data->Action == 0){ + int Ret = QueryManagerConnection->setNotation(Data->GamemasterID, + Data->CriminalName, Data->CriminalIPAddress, Reason, + Data->Comment, &BanishmentID); + switch(Ret){ + case 0:{ + DirectReply(Data->GamemasterID, "Notation for player %s inserted.", Data->CriminalName); + Log("banish", "%s notiert zu %s: %s.\n", Data->GamemasterName, Data->CriminalName, Data->Comment); + break; + } + + case 1:{ + DirectReply(Data->GamemasterID, "A player with this name does not exist. Perhaps he/she has been renamed?"); + Ok = false; + break; + } + + case 2:{ + DirectReply(Data->GamemasterID, "You may not report a god or gamemaster."); + Ok = false; + break; + } + } + } + + if(Ok && (Data->Action == 1 || Data->Action == 3 || Data->Action == 5)){ + int Ret = QueryManagerConnection->setNamelock(Data->GamemasterID, + Data->CriminalName, Data->CriminalIPAddress, Reason, + Data->Comment); + switch(Ret){ + case 0:{ + DirectReply(Data->GamemasterID, "Player %s reported for renaming.", Data->CriminalName); + Log("banish", "%s meldet %s zur Namensänderung.\n", Data->GamemasterName, Data->CriminalName); + break; + } + + case 3:{ + DirectReply(Data->GamemasterID, "This player has already been reported."); + break; + } + + case 1:{ + DirectReply(Data->GamemasterID, "A player with this name does not exist. Perhaps he/she has already been renamed?"); + Ok = false; + break; + } + + case 2: + case 4:{ + DirectReply(Data->GamemasterID, "This name has already been approved."); + Ok = false; + break; + } + } + } + + if(Ok && (Data->Action == 2 || Data->Action == 3 || Data->Action == 4 || Data->Action == 5)){ + int Days; + bool FinalWarning = (Data->Action == 4 || Data->Action == 5); + int Ret = QueryManagerConnection->banishAccount(Data->GamemasterID, + Data->CriminalName, Data->CriminalIPAddress, Reason, + Data->Comment, &FinalWarning, &Days, &BanishmentID); + switch(Ret){ + case 0:{ + if(Days == -1){ + DirectReply(Data->GamemasterID, "Account of player %s banished infinitely.", Data->CriminalName); + }else if(FinalWarning){ + DirectReply(Data->GamemasterID, "Account of player %s banished for %d days with final warning.", Data->CriminalName, Days); + }else{ + DirectReply(Data->GamemasterID, "Account of player %s banished for %d days.", Data->CriminalName, Days); + } + LogoutReply(Data->CriminalName); + Log("banish", "%s verbannt Account von Spieler %s.\n", Data->GamemasterName, Data->CriminalName); + break; + } + + case 3:{ + DirectReply(Data->GamemasterID, "Player %s has already been banished.", Data->CriminalName); + LogoutReply(Data->CriminalName); + break; + } + + case 1:{ + DirectReply(Data->GamemasterID, "A player with this name does not exist. Perhaps he/she has been renamed?"); + Ok = false; + break; + } + + case 2:{ + DirectReply(Data->GamemasterID, "You may not report a god or gamemaster."); + Ok = false; + break; + } + } + } + + if(Ok && Data->StatementID != 0){ + if(Data->NumberOfStatements > 0 && Data->ReportedStatements != NULL){ + uint32 *StatementIDs = (uint32*)alloca(Data->NumberOfStatements * sizeof(uint32)); + int *TimeStamps = (int*)alloca(Data->NumberOfStatements * sizeof(int)); + uint32 *CharacterIDs = (uint32*)alloca(Data->NumberOfStatements * sizeof(uint32)); + char (*Channels)[30] = (char(*)[30])alloca(Data->NumberOfStatements * 30); + char (*Texts)[256] = (char(*)[256])alloca(Data->NumberOfStatements * 256); + for(int StatementNr = 0; StatementNr < Data->NumberOfStatements; StatementNr += 1){ + TReportedStatement *Statement = Data->ReportedStatements->at(StatementNr); + StatementIDs[StatementNr] = Statement->StatementID; + TimeStamps[StatementNr] = Statement->TimeStamp; + CharacterIDs[StatementNr] = Statement->CharacterID; + strcpy(Channels[StatementNr], GetStatementOutputChannel(Statement->Mode, Statement->Channel)); + strcpy(Texts[StatementNr], Statement->Text); + } + + int Ret = QueryManagerConnection->reportStatement(Data->GamemasterID, + Data->CriminalName, Reason, Data->Comment, BanishmentID, + Data->StatementID, Data->NumberOfStatements, StatementIDs, + TimeStamps, CharacterIDs, Channels, Texts); + switch(Ret){ + case 0:{ + if(Data->Action == 6){ + DirectReply(Data->GamemasterID, "Statement of %s reported.", Data->CriminalName); + } + Log("banish", "%s meldet Äußerung von Spieler %s.\n", Data->GamemasterName, Data->CriminalName); + break; + } + + case 1:{ + if(Data->Action == 6){ + DirectReply(Data->GamemasterID, "A player with this name does not exist. Perhaps he/she has been renamed?"); + } + Ok = false; + break; + } + + case 2:{ + if(Data->Action == 6){ + DirectReply(Data->GamemasterID, "Statement has already been reported."); + } + Ok = false; + break; + } + } + }else{ + error("ProcessPunishmentOrder: Statements existieren nicht.\n"); + } + } + + if(Ok && Data->IPBanishment){ + int Ret = QueryManagerConnection->banishIPAddress(Data->GamemasterID, + Data->CriminalName, Data->CriminalIPAddress, Reason, Data->Comment); + switch(Ret){ + case 0:{ + DirectReply(Data->GamemasterID, "IP address of %s banished.", Data->CriminalName); + LogoutReply(Data->CriminalName); + Log("banish", "%s sperrt die IP-Adresse von %s.\n", Data->GamemasterName, Data->CriminalName); + break; + } + + case 1:{ + DirectReply(Data->GamemasterID, "A player with this name does not exist. Perhaps he/she has been renamed?"); + Ok = false; + break; + } + + case 2:{ + DirectReply(Data->GamemasterID, "You may not report a god or gamemaster."); + Ok = false; + break; + } + } + } + + delete Data->ReportedStatements; + delete Data; +} + +void ProcessCharacterDeathOrder(TCharacterDeathOrderData *Data){ + if(Data == NULL){ + error("ProcessCharacterDeathOrder: Keine Daten übergeben.\n"); + return; + } + + int Ret = QueryManagerConnection->logCharacterDeath(Data->CharacterID, + Data->Level, Data->Offender, Data->Remark, Data->Unjustified, + Data->Time); + if(Ret != 0){ + error("ProcessCharacterDeathOrder: Protokollierung fehlgeschlagen.\n"); + } + + delete Data; +} + +void ProcessAddBuddyOrder(TBuddyOrderData *Data){ + if(Data == NULL){ + error("ProcessAddBuddyOrder: Keine Daten übergeben.\n"); + return; + } + + int Ret = QueryManagerConnection->addBuddy(Data->AccountID, Data->Buddy); + if(Ret != 0){ + error("ProcessAddBuddyOrder: Aufnahme fehlgeschlagen.\n"); + } + + delete Data; +} + +void ProcessRemoveBuddyOrder(TBuddyOrderData *Data){ + if(Data == NULL){ + error("ProcessRemoveBuddyOrder: Keine Daten übergeben.\n"); + return; + } + + int Ret = QueryManagerConnection->removeBuddy(Data->AccountID, Data->Buddy); + if(Ret != 0){ + error("ProcessRemoveBuddyOrder: Entfernen fehlgeschlagen.\n"); + } + + delete Data; +} + +void ProcessDecrementIsOnlineOrder(uint32 CharacterID){ + int Ret = QueryManagerConnection->decrementIsOnline(CharacterID); + if(Ret != 0){ + error("ProcessDecrementIsOnlineOrder: Verringerung fehlgeschlagen.\n"); + } +} + +int WriterThreadLoop(void *Unused){ + TWriterThreadOrder Order = {}; + while(true){ + GetOrder(&Order); + if(Order.OrderType == ORDER_TERMINATE){ + break; + } + + switch(Order.OrderType){ + case ORDER_LOGOUT:{ + ProcessLogoutOrder((TLogoutOrderData*)Order.Data); + break; + } + + case ORDER_PLAYERLIST:{ + ProcessPlayerlistOrder((TPlayerlistOrderData*)Order.Data); + break; + } + + case ORDER_KILLSTATISTICS:{ + ProcessKillStatisticsOrder((TKillStatisticsOrderData*)Order.Data); + break; + } + + case ORDER_PUNISHMENT:{ + ProcessPunishmentOrder((TPunishmentOrderData*)Order.Data); + break; + } + + case ORDER_CHARACTERDEATH:{ + ProcessCharacterDeathOrder((TCharacterDeathOrderData*)Order.Data); + break; + } + + case ORDER_ADDBUDDY:{ + ProcessAddBuddyOrder((TBuddyOrderData*)Order.Data); + break; + } + + case ORDER_REMOVEBUDDY:{ + ProcessRemoveBuddyOrder((TBuddyOrderData*)Order.Data); + break; + } + + case ORDER_DECREMENTISONLINE:{ + uint32 CharacterID = (uint32)((uintptr)Order.Data); + ProcessDecrementIsOnlineOrder(CharacterID); + break; + } + + case ORDER_SAVEPLAYERDATA:{ + SavePlayerPoolSlots(); + break; + } + + default:{ + error("WriterThreadLoop: Unbekanntes Kommando %d.\n", Order.OrderType); + break; + } + } + } + + return 0; +} + +// Writer Replies +// ============================================================================= +void InsertReply(TWriterThreadReplyType ReplyType, const void *Data){ + int Replies = (ReplyPointerWrite - ReplyPointerRead); + if(Replies >= NARRAY(ReplyBuffer)){ + error("InsertReply (Writer): Puffer ist voll; Rückmeldung wird verworfen.\n"); + return; + } + + int WritePos = ReplyPointerWrite % NARRAY(ReplyBuffer); + ReplyBuffer[WritePos].ReplyType = ReplyType; + ReplyBuffer[WritePos].Data = Data; + ReplyPointerWrite += 1; +} + +void BroadcastReply(const char *Text, ...){ + if(Text == NULL){ + error("BroadcastReply: Kein Text angegeben.\n"); + return; + } + + TBroadcastReplyData *Data = new TBroadcastReplyData; + + va_list ap; + va_start(ap, Text); + vsnprintf(Data->Message, sizeof(Data->Message), Text, ap); + va_end(ap); + + InsertReply(REPLY_BROADCAST, Data); +} + +void DirectReply(uint32 CharacterID, const char *Text, ...){ + if(CharacterID == 0){ + return; + } + + if(Text == NULL){ + error("SendDirectReply: Kein Text angegeben.\n"); + return; + } + + TDirectReplyData *Data = new TDirectReplyData; + Data->CharacterID = CharacterID; + + va_list ap; + va_start(ap, Text); + vsnprintf(Data->Message, sizeof(Data->Message), Text, ap); + va_end(ap); + + InsertReply(REPLY_DIRECT, Data); +} + +void LogoutReply(const char *PlayerName){ + if(PlayerName == NULL){ + return; + } + + // TODO(fusion): Probably some string dup function? + char *Data = new char[strlen(PlayerName) + 1]; + strcpy(Data, PlayerName); + InsertReply(REPLY_LOGOUT, Data); +} + +bool GetReply(TWriterThreadReply *Reply){ + bool Result = (ReplyPointerRead < ReplyPointerWrite); + if(Result){ + *Reply = ReplyBuffer[ReplyPointerRead % NARRAY(ReplyBuffer)]; + ReplyPointerRead += 1; + } + return Result; +} + +void ProcessBroadcastReply(TBroadcastReplyData *Data){ + if(Data == NULL){ + error("ProcessBroadcastReply: Keine Daten übergeben.\n"); + return; + } + + BroadcastMessage(TALK_STATUS_MESSAGE, Data->Message); + + delete Data; +} + +void ProcessDirectReply(TDirectReplyData *Data){ + if(Data == NULL){ + error("ProcessDirectReply: Keine Daten übergeben.\n"); + return; + } + + TPlayer *Player = GetPlayer(Data->CharacterID); + if(Player != NULL){ + SendMessage(Player->Connection, TALK_INFO_MESSAGE, "%s", Data->Message); + } + + delete Data; +} + +void ProcessLogoutReply(const char *Name){ + if(Name == NULL){ + error("ProcessLogoutReply: Keine Daten übergeben.\n"); + return; + } + + TPlayer *Player = GetPlayer(Name); + if(Player != NULL){ + GraphicalEffect(Player->CrObject, EFFECT_MAGIC_GREEN); + Player->StartLogout(true, true); + } + + delete[] Name; +} + +void ProcessWriterThreadReplies(void){ + TWriterThreadReply Reply = {}; + while(GetReply(&Reply)){ + switch(Reply.ReplyType){ + case REPLY_BROADCAST:{ + ProcessBroadcastReply((TBroadcastReplyData*)Reply.Data); + break; + } + + case REPLY_DIRECT:{ + ProcessDirectReply((TDirectReplyData*)Reply.Data); + break; + } + + case REPLY_LOGOUT:{ + ProcessLogoutReply((const char*)Reply.Data); + break; + } + + default:{ + error("ProcessWriterThreadReplies: Unbekannte Rückmeldung %d.\n", Reply.ReplyType); + break; + } + } + } +} + +// Writer Initialization +// ============================================================================= +void ClearPlayers(void){ + int NumberOfAffectedPlayers; + int Ret = QueryManagerConnection->clearIsOnline(&NumberOfAffectedPlayers); + if(Ret != 0){ + error("ClearPlayers: Kann IsOnline-Flags nicht löschen.\n"); + }else if(NumberOfAffectedPlayers != 0){ + error("ClearPlayers: %d Spieler waren als eingeloggt markiert.\n", + NumberOfAffectedPlayers); + } +} + +void InitWriter(void){ + // TODO(fusion): No idea what's this about. + int QueryBufferSize = std::max<int>(KB(16), MaxPlayers * 66 + 2); + QueryManagerConnection = new TQueryManagerConnection(QueryBufferSize); + if(!QueryManagerConnection->isConnected()){ + throw "cannot connect to query manager"; + } + + ClearPlayers(); + + InitProtocol(); + ProtocolThread = StartThread(ProtocolThreadLoop, NULL, false); + if(ProtocolThread == INVALID_THREAD_HANDLE){ + throw "cannot start protocol thread"; + } + + InitWriterBuffers(); + WriterThread = StartThread(WriterThreadLoop, NULL, false); + if(WriterThread == INVALID_THREAD_HANDLE){ + throw "cannot start writer thread"; + } +} + +void AbortWriter(void){ + // TODO(fusion): The original function was calling `pthread_cancel` with + // an argument of 0 which is probably wrong?. I feel something is missing + // here. + if(WriterThread != INVALID_THREAD_HANDLE){ + pthread_cancel(WriterThread); + WriterThread = INVALID_THREAD_HANDLE; + OrderBufferEmpty.up(); + } +} + +void ExitWriter(void){ + if(ProtocolThread != INVALID_THREAD_HANDLE){ + InsertProtocolOrder("", ""); + JoinThread(ProtocolThread); + ProtocolThread = INVALID_THREAD_HANDLE; + } + + if(WriterThread != INVALID_THREAD_HANDLE){ + TerminateWriterOrder(); + JoinThread(WriterThread); + WriterThread = INVALID_THREAD_HANDLE; + } + + delete QueryManagerConnection; +} diff --git a/src/writer.hh b/src/writer.hh new file mode 100644 index 0000000..8c87c0f --- /dev/null +++ b/src/writer.hh @@ -0,0 +1,155 @@ +#ifndef TIBIA_WRITER_HH_ +#define TIBIA_WRITER_HH_ 1 + +#include "common.hh" +#include "containers.hh" +#include "operate.hh" + +enum TWriterThreadReplyType: int { + REPLY_BROADCAST = 0, + REPLY_DIRECT = 1, + REPLY_LOGOUT = 2, +}; + +enum TWriterThreadOrderType: int { + ORDER_TERMINATE = 0, + ORDER_LOGOUT = 1, + ORDER_PLAYERLIST = 2, + ORDER_KILLSTATISTICS = 3, + ORDER_PUNISHMENT = 4, + ORDER_CHARACTERDEATH = 5, + ORDER_ADDBUDDY = 6, + ORDER_REMOVEBUDDY = 7, + ORDER_DECREMENTISONLINE = 8, + ORDER_SAVEPLAYERDATA = 9, +}; + +struct TProtocolThreadOrder{ + char ProtocolName[20]; + char Text[256]; +}; + +struct TWriterThreadReply{ + TWriterThreadReplyType ReplyType; + const void *Data; +}; + +struct TBroadcastReplyData{ + char Message[100]; +}; + +struct TDirectReplyData{ + uint32 CharacterID; + char Message[100]; +}; + +struct TWriterThreadOrder{ + TWriterThreadOrderType OrderType; + const void *Data; +}; + +struct TLogoutOrderData{ + uint32 CharacterID; + int Level; + int Profession; + time_t LastLoginTime; + int TutorActivities; + char Residence[30]; +}; + +struct TPlayerlistOrderData{ + int NumberOfPlayers; + const char *PlayerNames; + int *PlayerLevels; + int *PlayerProfessions; +}; + +struct TKillStatisticsOrderData{ + int NumberOfRaces; + const char *RaceNames; + int *KilledPlayers; + int *KilledCreatures; +}; + +struct TPunishmentOrderData{ + uint32 GamemasterID; + char GamemasterName[30]; + char CriminalName[30]; + char CriminalIPAddress[16]; + int Reason; + int Action; + char Comment[200]; + int NumberOfStatements; + vector<TReportedStatement> *ReportedStatements; + uint32 StatementID; + bool IPBanishment; +}; + +struct TCharacterDeathOrderData{ + uint32 CharacterID; + int Level; + uint32 Offender; + char Remark[30]; + bool Unjustified; + time_t Time; +}; + +struct TBuddyOrderData{ + uint32 AccountID; + uint32 Buddy; +}; + +void InitProtocol(void); +void InsertProtocolOrder(const char *ProtocolName, const char *Text); +void GetProtocolOrder(TProtocolThreadOrder *Order); +void WriteProtocol(const char *ProtocolName, const char *Text); +int ProtocolThreadLoop(void *Unused); +void InitLog(const char *ProtocolName); +void Log(const char *ProtocolName, const char *Text, ...) ATTR_PRINTF(2, 3); + +void InitWriterBuffers(void); +int GetOrderBufferSpace(void); +void InsertOrder(TWriterThreadOrderType OrderType, const void *Data); +void GetOrder(TWriterThreadOrder *Order); +void TerminateWriterOrder(void); +void LogoutOrder(TPlayer *Player); +void PlayerlistOrder(int NumberOfPlayers, const char *PlayerNames, + int *PlayerLevels, int *PlayerProfessions); +void KillStatisticsOrder(int NumberOfRaces, const char *RaceNames, + int *KilledPlayers, int *KilledCreatures); +void PunishmentOrder(TCreature *Gamemaster, const char *Name, const char *IPAddress, + int Reason, int Action, const char *Comment, int NumberOfStatements, + vector<TReportedStatement> *ReportedStatements, uint32 StatementID, + bool IPBanishment); +void CharacterDeathOrder(TCreature *Creature, int OldLevel, + uint32 OffenderID, const char *Remark, bool Unjustified); +void AddBuddyOrder(TCreature *Creature, uint32 BuddyID); +void RemoveBuddyOrder(TCreature *Creature, uint32 BuddyID); +void DecrementIsOnlineOrder(uint32 CharacterID); +void SavePlayerDataOrder(void); +void ProcessLogoutOrder(TLogoutOrderData *Data); +void ProcessPlayerlistOrder(TPlayerlistOrderData *Data); +void ProcessKillStatisticsOrder(TKillStatisticsOrderData *Data); +void ProcessPunishmentOrder(TPunishmentOrderData *Data); +void ProcessCharacterDeathOrder(TCharacterDeathOrderData *Data); +void ProcessAddBuddyOrder(TBuddyOrderData *Data); +void ProcessRemoveBuddyOrder(TBuddyOrderData *Data); +void ProcessDecrementIsOnlineOrder(uint32 CharacterID); +int WriterThreadLoop(void *Unused); + +void InsertReply(TWriterThreadReplyType ReplyType, const void *Data); +void BroadcastReply(const char *Text, ...) ATTR_PRINTF(1, 2); +void DirectReply(uint32 CharacterID, const char *Text, ...) ATTR_PRINTF(2, 3); +void LogoutReply(const char *PlayerName); +bool GetReply(TWriterThreadReply *Reply); +void ProcessBroadcastReply(TBroadcastReplyData *Data); +void ProcessDirectReply(TDirectReplyData *Data); +void ProcessLogoutReply(const char *Name); +void ProcessWriterThreadReplies(void); + +void ClearPlayers(void); +void InitWriter(void); +void AbortWriter(void); +void ExitWriter(void); + +#endif //TIBIA_WRITER_HH_ |
