diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-08-15 14:53:56 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-08-15 14:53:56 -0300 |
| commit | 7ca3cfca6e683a318eedb907e4ba9c257d117b33 (patch) | |
| tree | dd584a0f6732382b749ed26526fc3db0acc31931 | |
| parent | 20ca1cf66123c110cc14aa452a840e5f62cd74ff (diff) | |
| download | querymanager-7ca3cfca6e683a318eedb907e4ba9c257d117b33.tar.gz querymanager-7ca3cfca6e683a318eedb907e4ba9c257d117b33.zip | |
wrapping up for a public release
| -rw-r--r-- | LICENSE.txt | 24 | ||||
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | README.md | 15 | ||||
| -rw-r--r-- | config.cfg | 6 | ||||
| -rw-r--r-- | sql/init.sql | 103 | ||||
| -rw-r--r-- | sql/schema.sql | 114 | ||||
| -rw-r--r-- | src/database.cc | 2 | ||||
| -rw-r--r-- | src/querymanager.cc | 2 | ||||
| -rw-r--r-- | tibia-querymanager.service | 21 | ||||
| -rw-r--r-- | tools/pwdhash.go | 33 |
10 files changed, 213 insertions, 109 deletions
diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c32dd18 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to <https://unlicense.org/>
\ No newline at end of file @@ -10,7 +10,7 @@ LFLAGS = -Wl,-t DEBUG ?= 0 ifneq ($(DEBUG), 0) - CFLAGS += -g -O0 + CFLAGS += -g -Og else CFLAGS += -O2 endif diff --git a/README.md b/README.md new file mode 100644 index 0000000..170cbb4 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Tibia 7.7 Query Manager +This is a simple query manager designed to support [Tibia Game Server](https://github.com/fusion32/tibia-game), [Tibia Login Server](https://github.com/fusion32/tibia-login), and [Tibia Web Server](https://github.com/fusion32/tibia-web). It uses the SQLite3 3.50.2 amalgamation for a database backend so we can completely avoid having to spin up yet another service for a separate database system, effectively turning the query manager into the database itself. This design choice was made with a single game server in mind. The networking protocol is NOT encrypted at all and won't accept remote connections. For a fully multi-world distributed infrastructure, a distributed database system like PostgreSQL and MySQL/MariaDB should be considered. + +## Compiling +Even though there are no Linux specific features being used, it will currently only compile on Linux. It should be simple enough to support compiling on Windows but I don't think it would add any value, considering the game server needs to run on Linux and they need to be both on the same machine. The makefile is very simple and there are **ZERO** dependencies required. You can add the `-j N` switch to make it compile across N processes. +``` +make # build in release mode +make DEBUG=1 # build in debug mode +make clean # remove `build` directory +``` + +## Running +The query manager will automatically manage the database schema based on files in `sql/` (see `sql/README.txt`), but won't automatically insert any initial data (see `sql/init.sql`). It does have a few configuration options that are loaded from `config.cfg` but the defaults should work for most use cases. + +It is recommended that the query manager is setup as a service. There is a *systemd* configuration file (`tibia-querymanager.service`) in the repository that may be used for that purpose. The process is very similar to the one described in the [Game Server](https://github.com/fusion32/tibia-game) so I won't repeat myself here. @@ -8,8 +8,8 @@ HostNameExpireTime = 30m # Connection Config UpdateRate = 20 -QueryManagerPort = 7174 +QueryManagerPort = 7173 QueryManagerPassword = "a6glaf0c" -MaxConnections = 50 -MaxConnectionIdleTime = 60s +MaxConnections = 25 +MaxConnectionIdleTime = 5m MaxConnectionPacketSize = 1M diff --git a/sql/init.sql b/sql/init.sql new file mode 100644 index 0000000..589c4e3 --- /dev/null +++ b/sql/init.sql @@ -0,0 +1,103 @@ +-- NOTE(fusion): The query manager WON'T automatically run this but the game +-- server still requires at least the world config to be able to boot up. It +-- is probably a good idea to keep this separated from `schema.sql` and then +-- running it with `sqlite3 -echo tibia.db < sql/init.sql`, although it is +-- not mandatory. +-- Because this isn't automatically managed, all queries are wrapped in a +-- transaction to avoid partial writes in case of errors. +--============================================================================== +BEGIN; + +INSERT INTO Worlds (WorldID, Name, Type, RebootTime, Host, Port, MaxPlayers, + PremiumPlayerBuffer, MaxNewbies, PremiumNewbieBuffer) + VALUES (1, 'Zanera', 0, 5, 'localhost', 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, 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'); + +COMMIT; diff --git a/sql/schema.sql b/sql/schema.sql index 18fabd9..677d778 100644 --- a/sql/schema.sql +++ b/sql/schema.sql @@ -54,6 +54,16 @@ CREATE INDEX IF NOT EXISTS CharactersAccountIndex CREATE INDEX IF NOT EXISTS CharactersGuildIndex ON Characters(Guild, Rank); +/* +-- TODO(fusion): Have group rights instead of adding individual rights to characters? +ALTER TABLE Characters ADD GroupID INTEGER NOT NULL; +CREATE TABLE IF NOT EXISTS CharacterRights ( + GroupID INTEGER NOT NULL, + Right TEXT NOT NULL COLLATE NOCASE, + PRIMARY KEY(GroupID, Right) +); +*/ + CREATE TABLE IF NOT EXISTS CharacterRights ( CharacterID INTEGER NOT NULL, Right TEXT NOT NULL COLLATE NOCASE, @@ -262,107 +272,3 @@ CREATE TABLE IF NOT EXISTS OnlineCharacters ( Profession TEXT NOT NULL, PRIMARY KEY (WorldID, Name) ); - --- REMOVE(fusion): Testing Data. ---============================================================================== -INSERT INTO Worlds (WorldID, Name, Type, RebootTime, Host, Port, MaxPlayers, - PremiumPlayerBuffer, MaxNewbies, PremiumNewbieBuffer) - VALUES (1, 'Zanera', 0, 5, 'localhost', 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, 111111, 'Gamemaster', 1), (1, 2, 111111, 'Player', 1); - -/* --- TODO(fusion): Have group rights instead of adding individual rights to characters? -ALTER TABLE Characters ADD GroupID INTEGER NOT NULL; -CREATE TABLE IF NOT EXISTS CharacterRights ( - GroupID INTEGER NOT NULL, - Right TEXT NOT NULL COLLATE NOCASE, - PRIMARY KEY(GroupID, Right) -); -*/ - -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/database.cc b/src/database.cc index f79bd82..0bd86a2 100644 --- a/src/database.cc +++ b/src/database.cc @@ -2042,7 +2042,7 @@ bool MergeKillStatistics(int WorldID, int NumStats, TKillStatistics *Stats){ "INSERT INTO KillStatistics (WorldID, RaceName, TimesKilled, PlayersKilled)" " VALUES (?1, ?2, ?3, ?4)" " ON CONFLICT DO UPDATE SET TimesKilled = TimesKilled + Excluded.TimesKilled," - " PlayersKilled = PlayersKilled + Excluded.TimesKilled"); + " PlayersKilled = PlayersKilled + Excluded.PlayersKilled"); if(Stmt == NULL){ LOG_ERR("Failed to prepare query"); return false; diff --git a/src/querymanager.cc b/src/querymanager.cc index 6f4cfc9..4fbc465 100644 --- a/src/querymanager.cc +++ b/src/querymanager.cc @@ -44,6 +44,7 @@ void LogAdd(const char *Prefix, const char *Format, ...){ LocalTime.tm_year + 1900, LocalTime.tm_mon + 1, LocalTime.tm_mday, LocalTime.tm_hour, LocalTime.tm_min, LocalTime.tm_sec, Prefix, Entry); + fflush(stdout); } } @@ -63,6 +64,7 @@ void LogAddVerbose(const char *Prefix, const char *Function, LocalTime.tm_year + 1900, LocalTime.tm_mon + 1, LocalTime.tm_mday, LocalTime.tm_hour, LocalTime.tm_min, LocalTime.tm_sec, Prefix, Function, Entry); + fflush(stdout); } } diff --git a/tibia-querymanager.service b/tibia-querymanager.service new file mode 100644 index 0000000..a196d0a --- /dev/null +++ b/tibia-querymanager.service @@ -0,0 +1,21 @@ +# Basic SYSTEMD service file for the Tibia Query Manager + +[Unit] +Description=Tibia Query Manager +After=network.target + +[Install] +WantedBy=multi-user.target + +[Service] +Type=simple +User=tibia-querymanager +Group=tibia-querymanager +ExecStart=/opt/tibia/querymanager/querymanager +WorkingDirectory=/opt/tibia/querymanager/ +Restart=always +RestartSec=10 +LimitCORE=infinity +StandardOutput=journal +StandardError=journal +SyslogIdentifier=%n diff --git a/tools/pwdhash.go b/tools/pwdhash.go new file mode 100644 index 0000000..e66f72f --- /dev/null +++ b/tools/pwdhash.go @@ -0,0 +1,33 @@ +package main + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/hex" + "fmt" + "os" +) + +func main() { + if len(os.Args) <= 1 { + fmt.Printf("usage: pwdhash PASSWORD\n") + return + } + + fmt.Printf("password = \"%v\"\n", os.Args[1]) + + var salt [32]byte + if n, err := rand.Read(salt[:]); err != nil || n != 32 { + fmt.Printf("Failed to generate salt: %v\n", err) + } + + secret := sha256.Sum256([]byte(os.Args[1])) + for i := 0; i < 32; i += 1 { + secret[i] ^= salt[i] + } + + pwdhash := sha256.Sum256(secret[:]) + + fmt.Printf("pwdhash = %v\n", hex.EncodeToString(pwdhash[:])) + fmt.Printf("salt = %v\n", hex.EncodeToString(salt[:])) +} |
