aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-07-22 22:22:01 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-07-22 22:22:01 -0300
commit82b82dca7d621bf4530416c7c5e9fc69874eecd3 (patch)
tree92c6bfd20e9a926854ac40cd380c6c37cc7e5ab3
parent29f6d814a276af83d9fb98a398ac8d22d8b6e9c1 (diff)
downloadquerymanager-82b82dca7d621bf4530416c7c5e9fc69874eecd3.tar.gz
querymanager-82b82dca7d621bf4530416c7c5e9fc69874eecd3.zip
improvements and bug fixes
- Add support for SIGINT and SIGTERM signals for graceful termination. - Properly check premium account status with `EVICT_FREE_ACCOUNTS` query. - Properly check if character is already online before failing game login with "too many characters" error. - Overall cleanup.
-rw-r--r--config.cfg8
-rw-r--r--sql/schema.sql88
-rw-r--r--src/connections.cc45
-rw-r--r--src/database.cc46
-rw-r--r--src/querymanager.cc61
-rw-r--r--src/querymanager.hh23
6 files changed, 213 insertions, 58 deletions
diff --git a/config.cfg b/config.cfg
index 725b0e7..70e1f29 100644
--- a/config.cfg
+++ b/config.cfg
@@ -2,10 +2,10 @@
DatabaseFile = "tibia.db"
MaxCachedStatements = 100
-# Connection
-Password = "a6glaf0c"
-Port = 7174
+# Connections
+UpdateRate = 20
+QueryManagerPort = 7174
+QueryManagerPassword = "a6glaf0c"
MaxConnections = 50
MaxConnectionIdleTime = 60s
MaxConnectionPacketSize = 1M
-UpdateRate = 20
diff --git a/sql/schema.sql b/sql/schema.sql
index 5427ea6..c8a78ec 100644
--- a/sql/schema.sql
+++ b/sql/schema.sql
@@ -5,7 +5,7 @@ CREATE TABLE IF NOT EXISTS Worlds (
Name TEXT NOT NULL COLLATE NOCASE,
Type INTEGER NOT NULL,
RebootTime INTEGER NOT NULL,
- Address INTEGER NOT NULL,
+ IPAddress INTEGER NOT NULL,
Port INTEGER NOT NULL,
MaxPlayers INTEGER NOT NULL,
PremiumPlayerBuffer INTEGER NOT NULL,
@@ -264,12 +264,94 @@ CREATE TABLE IF NOT EXISTS OnlineCharacters (
-- REMOVE(fusion): Testing Data.
--==============================================================================
-INSERT INTO Worlds (WorldID, Name, Type, RebootTime, Address, Port, MaxPlayers,
+INSERT INTO Worlds (WorldID, Name, Type, RebootTime, IPAddress, Port, MaxPlayers,
PremiumPlayerBuffer, MaxNewbies, PremiumNewbieBuffer)
VALUES (1, 'Zanera', 0, 5, 0x7F000001, 7172, 1000, 100, 300, 100);
+-- 111111/tibia
INSERT INTO Accounts (AccountID, Email, Auth)
VALUES (111111, '@tibia', X'206699cbc2fae1683118c873d746aa376049cb5923ef0980298bb7acbba527ec9e765668f7a338dffea34acf61a20efb654c1e9c62d35148dba2aeeef8dc7788');
INSERT INTO Characters (WorldID, CharacterID, AccountID, Name, Sex)
- VALUES (1, 1, 1, 'Player', 1);
+ VALUES (1, 1, 111111, 'Gamemaster', 1), (1, 2, 111111, 'Player', 1);
+
+INSERT INTO CharacterRights (CharacterID, Right)
+ VALUES
+ (1, 'NOTATION'),
+ (1, 'NAMELOCK'),
+ (1, 'STATEMENT_REPORT'),
+ (1, 'BANISHMENT'),
+ (1, 'FINAL_WARNING'),
+ (1, 'IP_BANISHMENT'),
+ (1, 'KICK'),
+ (1, 'HOME_TELEPORT'),
+ (1, 'GAMEMASTER_BROADCAST'),
+ (1, 'ANONYMOUS_BROADCAST'),
+ (1, 'NO_BANISHMENT'),
+ (1, 'ALLOW_MULTICLIENT'),
+ (1, 'LOG_COMMUNICATION'),
+ (1, 'READ_GAMEMASTER_CHANNEL'),
+ (1, 'READ_TUTOR_CHANNEL'),
+ (1, 'HIGHLIGHT_HELP_CHANNEL'),
+ (1, 'SEND_BUGREPORTS'),
+ (1, 'NAME_INSULTING'),
+ (1, 'NAME_SENTENCE'),
+ (1, 'NAME_NONSENSICAL_LETTERS'),
+ (1, 'NAME_BADLY_FORMATTED'),
+ (1, 'NAME_NO_PERSON'),
+ (1, 'NAME_CELEBRITY'),
+ (1, 'NAME_COUNTRY'),
+ (1, 'NAME_FAKE_IDENTITY'),
+ (1, 'NAME_FAKE_POSITION'),
+ (1, 'STATEMENT_INSULTING'),
+ (1, 'STATEMENT_SPAMMING'),
+ (1, 'STATEMENT_ADVERT_OFFTOPIC'),
+ (1, 'STATEMENT_ADVERT_MONEY'),
+ (1, 'STATEMENT_NON_ENGLISH'),
+ (1, 'STATEMENT_CHANNEL_OFFTOPIC'),
+ (1, 'STATEMENT_VIOLATION_INCITING'),
+ (1, 'CHEATING_BUG_ABUSE'),
+ (1, 'CHEATING_GAME_WEAKNESS'),
+ (1, 'CHEATING_MACRO_USE'),
+ (1, 'CHEATING_MODIFIED_CLIENT'),
+ (1, 'CHEATING_HACKING'),
+ (1, 'CHEATING_MULTI_CLIENT'),
+ (1, 'CHEATING_ACCOUNT_TRADING'),
+ (1, 'CHEATING_ACCOUNT_SHARING'),
+ (1, 'GAMEMASTER_THREATENING'),
+ (1, 'GAMEMASTER_PRETENDING'),
+ (1, 'GAMEMASTER_INFLUENCE'),
+ (1, 'GAMEMASTER_FALSE_REPORTS'),
+ (1, 'KILLING_EXCESSIVE_UNJUSTIFIED'),
+ (1, 'DESTRUCTIVE_BEHAVIOUR'),
+ (1, 'SPOILING_AUCTION'),
+ (1, 'INVALID_PAYMENT'),
+ (1, 'TELEPORT_TO_CHARACTER'),
+ (1, 'TELEPORT_TO_MARK'),
+ (1, 'TELEPORT_VERTICAL'),
+ (1, 'TELEPORT_TO_COORDINATE'),
+ (1, 'LEVITATE'),
+ (1, 'SPECIAL_MOVEUSE'),
+ (1, 'MODIFY_GOSTRENGTH'),
+ (1, 'SHOW_COORDINATE'),
+ (1, 'RETRIEVE'),
+ (1, 'ENTER_HOUSES'),
+ (1, 'OPEN_NAMEDOORS'),
+ (1, 'INVULNERABLE'),
+ (1, 'UNLIMITED_MANA'),
+ (1, 'KEEP_INVENTORY'),
+ (1, 'ALL_SPELLS'),
+ (1, 'UNLIMITED_CAPACITY'),
+ (1, 'ATTACK_EVERYWHERE'),
+ (1, 'NO_LOGOUT_BLOCK'),
+ (1, 'GAMEMASTER_OUTFIT'),
+ (1, 'ILLUMINATE'),
+ (1, 'CHANGE_PROFESSION'),
+ (1, 'IGNORED_BY_MONSTERS'),
+ (1, 'SHOW_KEYHOLE_NUMBERS'),
+ (1, 'CREATE_OBJECTS'),
+ (1, 'CREATE_MONEY'),
+ (1, 'CREATE_MONSTERS'),
+ (1, 'CHANGE_SKILLS'),
+ (1, 'CLEANUP_FIELDS'),
+ (1, 'NO_STATISTICS');
diff --git a/src/connections.cc b/src/connections.cc
index e78c48e..7d573ca 100644
--- a/src/connections.cc
+++ b/src/connections.cc
@@ -84,11 +84,11 @@ int ListenerAccept(int Listener, uint32 *OutAddr, uint16 *OutPort){
// IMPORTANT(fusion): It should be impossible to spoof the loopback
// address so this comparison should be safe. We're also binding the
// listening socket to the loopback address which should prevent any
- // other address to show up here.
+ // remote addresses from showing up here.
uint32 Addr = ntohl(SocketAddr.sin_addr.s_addr);
uint16 Port = ntohs(SocketAddr.sin_port);
if(Addr != INADDR_LOOPBACK){
- LOG_ERR("Rejecting remote connection from %08X.", Addr);
+ LOG_ERR("Rejecting remote connection from %08X:%d.", Addr, Port);
close(Socket);
continue;
}
@@ -317,8 +317,8 @@ void ProcessConnections(void){
}
if(AssignConnection(Socket, Addr, Port) == NULL){
- LOG_ERR("Rejecting connection from %08X due to max number of"
- " connections being reached (%d)", Addr, g_MaxConnections);
+ LOG_ERR("Rejecting connection %08X:%d due to max number of"
+ " connections being reached (%d)", Addr, Port, g_MaxConnections);
close(Socket);
}
}
@@ -364,12 +364,12 @@ bool InitConnections(void){
ASSERT(g_Listener == -1);
ASSERT(g_Connections == NULL);
- LOG("Listening port: %d", g_Port);
+ LOG("Query manager port: %d", g_QueryManagerPort);
LOG("Max connections: %d", g_MaxConnections);
- LOG("Max connection idle time: %d ms", g_MaxConnectionIdleTime);
+ LOG("Max connection idle time: %dms", g_MaxConnectionIdleTime);
LOG("Max connection packet size: %d", g_MaxConnectionPacketSize);
- g_Listener = ListenerBind((uint16)g_Port);
+ g_Listener = ListenerBind((uint16)g_QueryManagerPort);
if(g_Listener == -1){
LOG_ERR("Failed to bind listener");
return false;
@@ -491,7 +491,7 @@ void ProcessLoginQuery(TConnection *Connection, TReadBuffer *Buffer){
// TODO(fusion): Probably just disconnect on failed login attempt? Implement
// write then disconnect?
- if(!StringEq(g_Password, Password)){
+ if(!StringEq(g_QueryManagerPassword, Password)){
LOG_WARN("Invalid login attempt from %s", Connection->RemoteAddress);
SendQueryStatusFailed(Connection);
return;
@@ -501,15 +501,22 @@ void ProcessLoginQuery(TConnection *Connection, TReadBuffer *Buffer){
if(ApplicationType == APPLICATION_TYPE_GAME){
WorldID = GetWorldID(LoginData);
if(WorldID == 0){
- LOG_WARN("Unknown world name \"%s\"", LoginData);
+ LOG_WARN("Rejecting connection %s from unknown game server \"%s\"",
+ Connection->RemoteAddress, LoginData);
SendQueryStatusFailed(Connection);
return;
}
-
- LOG("Connection %s AUTHORIZED to world \"%s\" (%d)",
+ LOG("Connection %s AUTHORIZED to game server \"%s\" (%d)",
Connection->RemoteAddress, LoginData, WorldID);
+ }else if(ApplicationType == APPLICATION_TYPE_LOGIN){
+ LOG("Connection %s AUTHORIZED to login server", Connection->RemoteAddress);
+ }else if(ApplicationType == APPLICATION_TYPE_WEB){
+ LOG("Connection %s AUTHORIZED to web server", Connection->RemoteAddress);
}else{
- LOG("Connection %s AUTHORIZED", Connection->RemoteAddress);
+ LOG_WARN("Rejecting connection %s from unknown application type %d",
+ Connection->RemoteAddress, ApplicationType);
+ SendQueryStatusFailed(Connection);
+ return;
}
Connection->Authorized = true;
@@ -735,8 +742,10 @@ static int LoginGameTransaction(int WorldID, int AccountID, const char *Characte
return 12;
}
+ // TODO(fusion): Probably merge these into a single query?
if(!GetCharacterRight(Character->CharacterID, "ALLOW_MULTICLIENT")
- && GetAccountOnlineCharacters(Account.AccountID) > 0){
+ && GetAccountOnlineCharacters(Account.AccountID) > 0
+ && !IsCharacterOnline(Character->CharacterID)){
return 13;
}
@@ -759,9 +768,15 @@ static int LoginGameTransaction(int WorldID, int AccountID, const char *Characte
return -1;
}
+ Account.PremiumDays += Account.PendingPremiumDays;
+ Account.PendingPremiumDays = 0;
*PremiumAccountActivated = true;
}
+ if(Account.PremiumDays > 0){
+ Rights->Push(TCharacterRight{"PREMIUM_ACCOUNT"});
+ }
+
if(!IncrementIsOnline(WorldID, Character->CharacterID)){
return -1;
}
@@ -843,6 +858,8 @@ void ProcessLoginGameQuery(TConnection *Connection, TReadBuffer *Buffer){
WriteBuffer.WriteString(Rights[i].Name);
}
+ WriteBuffer.WriteFlag(PremiumAccountActivated);
+
SendResponse(Connection, &WriteBuffer);
}
@@ -1763,7 +1780,7 @@ void ProcessLoadWorldConfigQuery(TConnection *Connection, TReadBuffer *Buffer){
TWriteBuffer WriteBuffer = PrepareResponse(Connection, QUERY_STATUS_OK);
WriteBuffer.Write8((uint8)WorldConfig.Type);
WriteBuffer.Write8((uint8)WorldConfig.RebootTime);
- WriteBuffer.Write32BE((uint32)WorldConfig.Address);
+ WriteBuffer.Write32BE((uint32)WorldConfig.IPAddress);
WriteBuffer.Write16((uint16)WorldConfig.Port);
WriteBuffer.Write16((uint16)WorldConfig.MaxPlayers);
WriteBuffer.Write16((uint16)WorldConfig.PremiumPlayerBuffer);
diff --git a/src/database.cc b/src/database.cc
index cd32f88..3b1c51e 100644
--- a/src/database.cc
+++ b/src/database.cc
@@ -53,7 +53,7 @@ sqlite3_stmt *PrepareQuery(const char *Text){
if(Stmt == NULL){
if(sqlite3_prepare_v3(g_Database, Text, -1,
SQLITE_PREPARE_PERSISTENT, &Stmt, NULL) != SQLITE_OK){
- LOG_ERR("Failed to prepary query: %s", sqlite3_errmsg(g_Database));
+ LOG_ERR("Failed to prepare query: %s", sqlite3_errmsg(g_Database));
return NULL;
}
@@ -169,7 +169,7 @@ int GetWorldID(const char *WorldName){
bool GetWorldConfig(int WorldID, TWorldConfig *WorldConfig){
ASSERT(WorldConfig != NULL);
sqlite3_stmt *Stmt = PrepareQuery(
- "SELECT Type, RebootTime, Address, Port, MaxPlayers,"
+ "SELECT Type, RebootTime, IPAddress, Port, MaxPlayers,"
" PremiumPlayerBuffer, MaxNewbies, PremiumNewbieBuffer"
" FROM Worlds WHERE WorldID = ?1");
if(Stmt == NULL){
@@ -189,7 +189,7 @@ bool GetWorldConfig(int WorldID, TWorldConfig *WorldConfig){
WorldConfig->Type = sqlite3_column_int(Stmt, 0);
WorldConfig->RebootTime = sqlite3_column_int(Stmt, 1);
- WorldConfig->Address = sqlite3_column_int(Stmt, 2);
+ WorldConfig->IPAddress = sqlite3_column_int(Stmt, 2);
WorldConfig->Port = sqlite3_column_int(Stmt, 3);
WorldConfig->MaxPlayers = sqlite3_column_int(Stmt, 4);
WorldConfig->PremiumPlayerBuffer = sqlite3_column_int(Stmt, 5);
@@ -256,6 +256,33 @@ int GetAccountOnlineCharacters(int AccountID){
return sqlite3_column_int(Stmt, 0);
}
+bool IsCharacterOnline(int CharacterID){
+ sqlite3_stmt *Stmt = PrepareQuery(
+ "SELECT IsOnline FROM Characters WHERE CharacterID = ?1");
+ if(Stmt == NULL){
+ LOG_ERR("Failed to prepare query");
+ return false;
+ }
+
+ if(sqlite3_bind_int(Stmt, 1, CharacterID) != SQLITE_OK){
+ LOG_ERR("Failed to bind CharacterID: %s", sqlite3_errmsg(g_Database));
+ return false;
+ }
+
+ int ErrorCode = sqlite3_step(Stmt);
+ if(ErrorCode != SQLITE_ROW && ErrorCode != SQLITE_DONE){
+ LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database));
+ return false;
+ }
+
+ bool Result = false;
+ if(ErrorCode == SQLITE_ROW){
+ Result = (sqlite3_column_int(Stmt, 0) != 0);
+ }
+
+ return Result;
+}
+
bool ActivatePendingPremiumDays(int AccountID){
sqlite3_stmt *Stmt = PrepareQuery(
"UPDATE Accounts"
@@ -470,7 +497,7 @@ bool GetGuildLeaderStatus(int WorldID, int CharacterID){
}
bool IncrementIsOnline(int WorldID, int CharacterID){
- // NOTE(fusion): Same as `DecrementIsOnline`
+ // NOTE(fusion): Same as `DecrementIsOnline`.
sqlite3_stmt *Stmt = PrepareQuery(
"UPDATE Characters SET IsOnline = IsOnline + 1"
" WHERE WorldID = ?1 AND CharacterID = ?2");
@@ -553,7 +580,7 @@ bool LogoutCharacter(int WorldID, int CharacterID, int Level,
" Profession = ?4,"
" Residence = ?5,"
" LastLoginTime = ?6,"
- " TutorActivities = ?7"
+ " TutorActivities = ?7,"
" IsOnline = IsOnline - 1"
" WHERE WorldID = ?1 AND CharacterID = ?2");
if(Stmt == NULL){
@@ -767,7 +794,7 @@ bool GetWorldInvitation(int WorldID, int CharacterID){
bool InsertLoginAttempt(int AccountID, int IPAddress, bool Failed){
sqlite3_stmt *Stmt = PrepareQuery(
"INSERT INTO LoginAttempts (AccountID, IPAddress, Timestamp, Failed)"
- " VALUES (?1, ?2, UNIXEPOCH(), ?4)");
+ " VALUES (?1, ?2, UNIXEPOCH(), ?3)");
if(Stmt == NULL){
LOG_ERR("Failed to prepare query");
return false;
@@ -916,9 +943,10 @@ bool GetFreeAccountEvictions(int WorldID, DynamicArray<THouseEviction> *Eviction
sqlite3_stmt *Stmt = PrepareQuery(
"SELECT O.HouseID, O.OwnerID"
" FROM HouseOwners AS O"
- " LEFT JOIN CharacterRights AS R"
- " ON R.CharacterID = O.OwnerID AND R.Right = 'PREMIUM_ACCOUNT'"
- " WHERE O.WorldID = ?1 AND R.CharacterID IS NULL");
+ " LEFT JOIN Characters AS C ON C.CharacterID = O.OwnerID"
+ " LEFT JOIN Accounts AS A ON A.AccountID = C.AccountID"
+ " WHERE O.WorldID = ?1"
+ " AND (A.PremiumEnd IS NULL OR A.PremiumEnd < UNIXEPOCH())");
if(Stmt == NULL){
LOG_ERR("Failed to prepare query");
return false;
diff --git a/src/querymanager.cc b/src/querymanager.cc
index 26d2855..5086032 100644
--- a/src/querymanager.cc
+++ b/src/querymanager.cc
@@ -1,19 +1,28 @@
#include "querymanager.hh"
+// TODO(fusion): Support windows eventually?
+#if OS_LINUX
+# include <errno.h>
+# include <signal.h>
+#else
+# error "Operating system not currently supported."
+#endif
+
+// Shutdown Signal
+int g_ShutdownSignal = 0;
+
// Time
int g_MonotonicTimeMS = 0;
-// Database CONFIG
+// Config
char g_DatabaseFile[1024] = "tibia.db";
int g_MaxCachedStatements = 100;
-
-// Connection CONFIG
-char g_Password[30] = "";
-int g_Port = 7174;
+int g_UpdateRate = 20;
+int g_QueryManagerPort = 7174;
+char g_QueryManagerPassword[30] = "";
int g_MaxConnections = 50;
int g_MaxConnectionIdleTime = 60000;
int g_MaxConnectionPacketSize = (int)MB(1);
-int g_UpdateRate = 20;
void LogAdd(const char *Prefix, const char *Format, ...){
char Entry[4096];
@@ -331,18 +340,18 @@ bool ReadConfig(const char *FileName){
ReadStringConfig(g_DatabaseFile, (int)sizeof(g_DatabaseFile), Val);
}else if(StringEqCI(Key, "MaxCachedStatements")){
ReadIntegerConfig(&g_MaxCachedStatements, Val);
- }else if(StringEqCI(Key, "Password")){
- ReadStringConfig(g_Password, (int)sizeof(g_Password), Val);
- }else if(StringEqCI(Key, "Port")){
- ReadIntegerConfig(&g_Port, Val);
+ }else if(StringEqCI(Key, "UpdateRate")){
+ ReadIntegerConfig(&g_UpdateRate, Val);
+ }else if(StringEqCI(Key, "QueryManagerPort")){
+ ReadIntegerConfig(&g_QueryManagerPort, Val);
+ }else if(StringEqCI(Key, "QueryManagerPassword")){
+ ReadStringConfig(g_QueryManagerPassword, (int)sizeof(g_QueryManagerPassword), Val);
}else if(StringEqCI(Key, "MaxConnections")){
ReadIntegerConfig(&g_MaxConnections, Val);
}else if(StringEqCI(Key, "MaxConnectionIdleTime")){
ReadDurationConfig(&g_MaxConnectionIdleTime, Val);
}else if(StringEqCI(Key, "MaxConnectionPacketSize")){
ReadSizeConfig(&g_MaxConnectionPacketSize, Val);
- }else if(StringEqCI(Key, "UpdateRate")){
- ReadIntegerConfig(&g_UpdateRate, Val);
}else{
LOG_WARN("Unknown config \"%s\"", Key);
}
@@ -352,10 +361,33 @@ bool ReadConfig(const char *FileName){
return true;
}
+static bool SigHandler(int SigNr, sighandler_t Handler){
+ struct sigaction Action = {};
+ Action.sa_handler = Handler;
+ sigfillset(&Action.sa_mask);
+ if(sigaction(SigNr, &Action, NULL) == -1){
+ LOG_ERR("Failed to change handler for signal %d (%s): (%d) %s",
+ SigNr, sigdescr_np(SigNr), errno, strerrordesc_np(errno));
+ return false;
+ }
+ return true;
+}
+
+static void ShutdownHandler(int SigNr){
+ g_ShutdownSignal = SigNr;
+}
+
int main(int argc, const char **argv){
(void)argc;
(void)argv;
+ g_ShutdownSignal = 0;
+ if(!SigHandler(SIGPIPE, SIG_IGN)
+ || !SigHandler(SIGINT, ShutdownHandler)
+ || !SigHandler(SIGTERM, ShutdownHandler)){
+ return EXIT_FAILURE;
+ }
+
int64 StartTime = GetClockMonotonicMS();
g_MonotonicTimeMS = 0;
@@ -376,7 +408,7 @@ int main(int argc, const char **argv){
LOG("Running at %d updates per second...", g_UpdateRate);
int64 UpdateInterval = 1000 / (int64)g_UpdateRate;
- while(true){
+ while(g_ShutdownSignal == 0){
int64 UpdateStart = GetClockMonotonicMS();
g_MonotonicTimeMS = (int)(UpdateStart - StartTime);
ProcessConnections();
@@ -387,5 +419,8 @@ int main(int argc, const char **argv){
}
}
+ LOG("Received signal %d (%s), shutting down...",
+ g_ShutdownSignal, sigdescr_np(g_ShutdownSignal));
+
return EXIT_SUCCESS;
}
diff --git a/src/querymanager.hh b/src/querymanager.hh
index 9b358ce..f0245d4 100644
--- a/src/querymanager.hh
+++ b/src/querymanager.hh
@@ -78,17 +78,15 @@ typedef size_t usize;
// Time
extern int g_MonotonicTimeMS;
-// Database CONFIG
+// Config
extern char g_DatabaseFile[1024];
extern int g_MaxCachedStatements;
-
-// Connection CONFIG
-extern char g_Password[30];
-extern int g_Port;
+extern int g_UpdateRate;
+extern int g_QueryManagerPort;
+extern char g_QueryManagerPassword[30];
extern int g_MaxConnections;
extern int g_MaxConnectionIdleTime;
extern int g_MaxConnectionPacketSize;
-extern int g_UpdateRate;
void LogAdd(const char *Prefix, const char *Format, ...) ATTR_PRINTF(2, 3);
void LogAddVerbose(const char *Prefix, const char *Function,
@@ -530,14 +528,8 @@ public:
//==============================================================================
enum : int {
APPLICATION_TYPE_GAME = 1,
-
- // TODO(fusion): There is definitely more application types that we don't
- // know about. From looking at the config loader on the server application,
- // we could perhaps infer a few, although we're gonna be limited to our own
- // usage cases (GAME and WEB).
- //APPLICATION_TYPE_WEB
- //APPLICATION_TYPE_FORUM
- //APPLICATION_TYPE_ADMIN
+ APPLICATION_TYPE_LOGIN = 2,
+ APPLICATION_TYPE_WEB = 3,
};
enum : int {
@@ -695,7 +687,7 @@ void ProcessConnectionQuery(TConnection *Connection);
struct TWorldConfig{
int Type;
int RebootTime;
- int Address;
+ int IPAddress;
int Port;
int MaxPlayers;
int PremiumPlayerBuffer;
@@ -834,6 +826,7 @@ int GetWorldID(const char *WorldName);
bool GetWorldConfig(int WorldID, TWorldConfig *WorldConfig);
bool GetAccountData(int AccountID, TAccountData *Account);
int GetAccountOnlineCharacters(int AccountID);
+bool IsCharacterOnline(int CharacterID);
bool ActivatePendingPremiumDays(int AccountID);
bool GetCharacterList(int AccountID, DynamicArray<TCharacterLoginData> *Characters);
int GetCharacterID(int WorldID, const char *CharacterName);