Commit Graph
25 Commits
Author SHA1 Message Date
ThomasandClaude Sonnet 5 9829688a85 Add web admin panel for accounts/characters (login, GM rights, live stats)
New "webadmin" service (Flask + PyMySQL, port 8081) backing a small
server-rendered admin UI:
- Login gated on an existing game account: only accounts with
  accesslevel >= ADMIN_MIN_ACCESSLEVEL (default 900) get in. Password
  is MD5-hashed before comparison since the game client hashes it
  client-side before it ever hits the wire -- accounts.password is
  always an MD5 hex digest, confirmed against existing DB rows.
- Accounts: list/search, create, edit accesslevel/active/password.
- Characters: list/search, create (mirrors the real char-creation
  INSERT in Src/Char Server/charpackets.cpp -- only account_name,
  char_name, face, hairStyle, sex are set, everything else relies on
  the table's own defaults), edit core stats (level, zuly, attributes,
  hp/mp, stat/skill points) and isgm/isdev flags directly.

All queries are parameterized (no string-formatted SQL). No CSRF
protection or rate limiting yet -- acceptable for a small
already-behind-a-high-accesslevel-gate private server, worth
revisiting before ever exposing this port publicly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 07:46:07 +02:00
ThomasandClaude Sonnet 5 f053a06920 Fix core RNG collapsing to ~0-1 on Linux due to integer overflow
CWorldServer::RandNumber (and three inlined copies of the same pattern
in worldpackets.cpp's item-refine code) computed "range*rand()" in
integer arithmetic before dividing by (RAND_MAX + 1.0). On Windows
RAND_MAX is 32767 (15-bit), so this never overflowed. On Linux/glibc
RAND_MAX is 2147483647 (2^31-1), so "range*rand()" overflows a 32-bit
UINT for the vast majority of rand() outputs, wrapping to a small
value -- the division by ~2^31 then collapses the result to ~0 or 1
almost always, regardless of the requested range. This made every
percentage-based roll in the game (item drop selection, refine
durability/stats, and anything else using RandNumber) behave as if
called with range 1-2 instead of the real range, which is why item
drops effectively never triggered post-port. Cast to double before
the multiply so it can't overflow.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 07:24:14 +02:00
ThomasandClaude Sonnet 5 560f6a968f Rebrand welcome messages and re-enable drop-roll debug logging
Welcome message default/example changed from "Welcome to ROSE" to
"Welcome to ROSS", and the hardcoded post-login system message
replaced with a ROSS-specific credit line.

Also re-enable the codedebug SendPM lines in GetNewDrop that report
the raw randv/money/zulyrate/item/itemrate values behind each
zuly-vs-item roll (previously commented out) -- needed to diagnose
in-game why item drops are still rare/absent after the drop-rate and
item-index fixes, without guessing at STB drop-table values blind.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 07:16:01 +02:00
ThomasandClaude Sonnet 5 46e028c3ec Fix item-drop selection off-by-one and double/slow-drop config defaults
GetNewDrop's weighted item-index selection used a do-while that
increments before its first RunningTotal check, so index 0 (the first
item slot in every drop table) could never be selected -- for the 51
drop tables that only populate item slot 1 (all others zero), this
silently discarded every roll ("item at itemindex does not exist"),
so those mobs/maps never dropped an item at all. Switch to a pre-check
while loop so index 0 is reachable.

Also: battle.cpp's kill-drop loop runs GetDrop()/AddDrop() exactly
Stats->itemdroprate + Config.DROP_RATE times, not as a probability --
with the shipped drop_rate=2 every kill produced two independently
rolled drops (hence always-doubled Zuly boxes for characters with no
item-drop-rate gear). Default drop_rate to 1 so a kill yields one
roll by default; gear/GM commands that raise itemdroprate still grant
extra rolls as designed.

Also default dropdelay to 0: the killed-monster's loot became visible
to clients only after Config.DropDelay (1s) *plus* the next
VisualDelay tick (200ms), giving a ~1.2s lag before drops appeared.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 07:06:25 +02:00
ThomasandClaude Sonnet 5 25c474d286 Ignore local Windows client/data assets and .claude config
Client/, 3Ddata*, KTRose_pre-evo.zip are large local-only assets used
for end-to-end login/game testing, not part of the server source tree.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 06:37:31 +02:00
ThomasandClaude Sonnet 5 36857a2eab Add webtools static frontend and serve it via docker-compose
Adds an nginx:alpine service serving webtools/ (item/map lookup
frontend) on port 80 alongside the existing login/char/world stack.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 06:35:50 +02:00
ThomasandClaude Sonnet 5 42cf2944ba Trim trailing whitespace in charpackets.cpp
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 06:35:35 +02:00
ThomasandClaude Sonnet 5 582e29106b Fix new-account creation failing under MariaDB strict mode
accounts.lastlogin/logtime had no defaults and were NOT NULL, but the
auto-create-account INSERT never set them, so new accounts failed
under MariaDB strict mode (silently accepted under the old MySQL
5.0 target). Insert explicit empty/zero values and give lastlogin a
default; also widen it to varchar(32) to match the runtime.sql fix
already applied to the starttime column.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 06:35:31 +02:00
ThomasandClaude Sonnet 5 5f600d0e61 Fix memory leaks and add bounds checks in packet/drop handling
GetNewDrop() allocated newdrop up front but returned NULL without
freeing it on every rejection path (failed drop-rate check, missing
drop table, unknown item type, etc.), leaking on every non-drop roll.
Free it on all early-return paths, and only assign a clientid once a
drop is actually going to be returned.

CPacket::AddByte/Word/DWord/QWord/Float now bounds-check against the
buffer before writing, preventing an overflow if a packet is built
past its allocated size.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 06:35:18 +02:00
ThomasandClaude Sonnet 5 f59158a95c Fix quest array off-by-ones and add QuestID bounds checks
Several quest-loading loops used <=N over arrays sized N (Variables,
Switches, Items, EpisodeVar, JobVar, PlanetVar, UnionVar), writing one
element past the end on every character load. Also guard STB_QUEST
table lookups with a rowcount bounds check before indexing by
QuestID, which could otherwise read out of bounds for stale/invalid
quest ids.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 06:35:11 +02:00
ThomasandClaude Sonnet 5 725620e23c Fix respawn/spawn timers racing ahead under multi-threading
POSIX clock() sums CPU time across every thread in the process, unlike
Windows' clock() which tracks wall-clock milliseconds. With several
worker threads running, respawn/spawn/AI timers based on clock() raced
far ahead of real elapsed time. Switch spawn-timer bookkeeping to
time(NULL)/time_t (seconds) throughout the world server, and add a
portable clock() shim in sockets.h (backed by CLOCK_MONOTONIC,
millisecond resolution) for remaining call sites so CLOCKS_PER_SEC==1000
still holds like it did on Windows.

Also: fix MapDelay/VisualDelay/WorldDelay being passed to usleep() (us)
without converting from their configured milliseconds; fix the shutdown
timer likewise; stop leaking the temporary actor used to run an NPC's
AIP script; fix CleanDrops() skipping the element that shifts into
position after an erase.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-09 06:35:05 +02:00
92thmsandClaude Sonnet 5 1f626c6b93 Fix DB warning and a client-connection-blocking accept() busy-loop
- runtime.sql: widen starttime to varchar(32). ctime() produces 25 chars;
  MariaDB's default strict mode rejects the 20-char column instead of
  silently truncating like the original MySQL 5.0 target did.

- SocketServer.cpp/SocketBase.cpp: the accept-loop's select() used `maxfd`
  set only from `sock`, never updated for the ISC sockets (sckISC/sckISCII).
  Any fd beyond nfds is left however FD_SET last set it, since select()
  never examines it -- so those bits stayed permanently "set", and the
  server treated them as always-ready.  For the login server specifically
  this was fatal: its sckISC is created but never listen()'d (the login
  server has no inbound ISC use), so accept() on it returned EINVAL on
  every single loop iteration, spinning the login server's CPU and
  spamming the log without ever getting to the real client-facing accept()
  in a timely way. Root-caused with a real client connecting for the first
  time (this path was never exercised end-to-end before). Fixed by
  including the ISC sockets in maxfd and skipping the ISC accept branch
  entirely for the login server. Also initialize sckISC/sckISCII to
  INVALID_SOCKET in the constructor -- they were previously left
  uninitialized for server types that never touch them.

- tools/rose_login_test.cpp: protocol-level login smoke test. Links
  directly against Src/Common/rosecrypt.cpp (the server's own encryption
  code) rather than reimplementing the crypto, so there's no risk of a
  second, subtly-wrong implementation. Documents the wire handshake
  (0x703 encryption request -> 0x7FF -> 0x708 login, password[32]+username[16]
  payload) for future reference.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 07:12:52 +00:00
92thmsandClaude Sonnet 5 764fcb3b3f Port server to Linux and add Docker Compose deployment
Rebuilds (not Wine) the login/char/world servers natively for Linux via
CMake, and ships a single Docker image plus docker-compose stack (MariaDB +
3 servers) with all settings configurable via .env.

Linux portability:
- Src/Common/compat.h shims MSVC-only CRT calls (fopen_s, _strdup,
  strncpy_s, sprintf_s, SYSTEMTIME/GetLocalTime, WSAGetLastError, __cdecl)
  and adds a case-insensitive/backslash-tolerant path resolver for the
  Windows-authored STB/AIP data file paths.
- Fixed case-sensitive header includes and filenames (charserver.h vs
  CharServer.h, worldserver.h vs WorldServer.h, ../common vs ../Common).
- rose_inet_addr() resolves hostnames (not just literal IPs) for the
  server-to-server links, so docker-compose service names work.

Fixed three real bugs that only crashed on 64-bit Linux (silently tolerated
on 32-bit Windows):
- CIntStb::LoadSTB had an inverted fopen success/failure check, so STB game
  data (zones, items, etc.) was never actually loaded.
- GenerateCryptTables allocated its pointer table with sizeof(unsigned)
  instead of sizeof(unsigned*) -- a 1x vs 2x-size heap overflow on 64-bit.
- Crypto bit-twiddling relies throughout on signed-overflow-as-wraparound,
  which is UB in C++; GCC's -O3 exploited it and miscompiled
  GenerateCryptTables (Release-only crash). Fixed via -fwrapv.
Plus smaller fixes: off-by-one array bounds (inventory/storage/sell data),
SO_REUSEADDR on server sockets (containers restarting in the same netns hit
TIME_WAIT otherwise), and a va_list reused across multiple vprintf/vfprintf
calls in Log() that garbled every formatted log line on the Linux ABI.

Docker:
- CMakeLists.txt builds all three servers + shared Common code.
- Dockerfile: multi-stage build, one runtime image for all three servers,
  server role selected via `command: [login|char|world]`.
- docker-compose.yml: MariaDB (auto-imports database sql files/ on first
  run) + login/char/world (single channel), config rendered from
  docker/conf/*.conf.tmpl via envsubst at container start.
- .env.example documents every setting (ports, server names, rates,
  inter-server passwords, PUBLIC_HOST for internet-facing deployments).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-08 06:32:39 +00:00
purpleyouko a320bbe470 Modified packet logging to include server name and save both in and out to the same file 2025-12-26 14:28:59 -06:00
purpleyouko 4a149c8621 added some files that were apparently missing 2025-09-12 13:26:48 -05:00
purpleyouko a5d1f662b1 bit of clean up 2025-08-28 12:53:07 -05:00
purpleyouko 8fd034ef97 added stb editor 2025-08-11 11:12:14 -05:00
purpleyouko cfdc19df45 added source folders 2025-08-11 11:09:09 -05:00
purpleyouko f23521f720 added database files 2025-08-11 11:05:24 -05:00
purpleyouko 6b82755825 added servers folder 2025-08-11 11:04:46 -05:00
purpleyouko 26d2931118 added worldmap.cpp 2025-08-11 11:03:59 -05:00
purpleyouko d4969dcbbc added loginclient.cpp 2025-08-11 11:03:30 -05:00
purpleyouko 943dd7b729 added charserver folder and vcxproj files 2025-08-11 11:02:41 -05:00
purpleyouko 51ea1fbacb added clanmark folder 2025-08-11 11:01:21 -05:00
purpleyouko 4cd053de0b added Binary folder and 3DData/AI/AAJU-AQUAGUARD.AIP 2025-08-11 10:58:30 -05:00