Commit Graph

10 Commits

Author SHA1 Message Date
Aaron Kimbrell
4399daad4c fix(login): select the last played character in a stable list
The character list selected index 0 and was ordered by last login, so
the characters swapped places on the selection screen after each
session. Live kept them in the order they were made and selected the
one with the latest last login (a new character counts as logged in
when it is made): 2014 captures show e.g. a new fourth character
appended and selected (index 3), and after the next login the list in
the same order with index 0 for the character played last.

Characters are now sorted by ID (creation order) and the one with the
latest last login is selected.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:31:04 -05:00
Aaron Kimbrell
796fd1b941 refactor: chat packets as structs
Every packet of the chat service is now an LUBitStream struct in dNet/ChatPackets.h
(docs/PacketArchitecture.md, PR 13), and nothing in the chat server or the chat side of
the world server reads or writes a packet by hand any more.

- World <-> chat: LoginSessionNotify, UnexpectedDisconnect, GMLevelUpdate, GMMute,
  Announcement (GM announce), CreateTeam, TeamUpdate (TEAM_GET_STATUS to worlds),
  AchievementNotify, ShowAllRequest, FindPlayerRequest.
- Client -> world -> chat (friends, ignore list, teams, general and private chat):
  GetFriendsList, AddFriendRequest, AddFriendResponse, RemoveFriend, GetIgnoreList,
  AddIgnore, RemoveIgnore, GeneralChatMessage, PrivateChatMessage, TeamInvite,
  TeamInviteResponse, TeamLeave, TeamKick, TeamSetLeader, TeamSetLoot, TeamGetStatus.
  The 77 bytes the handlers skipped are named fields now (the sender block the client
  fills in); the 4 unused bytes after the player ID are kept as `unknown`.
- What the client receives. Chat service (ChatPackets::Client): GeneralChatMessage
  (replaces SendChatMessage; SendSystemMessage stays as a helper built on it),
  PrivateChatMessage. Client service (ClientPackets, as structs go in the file of the
  ServiceType in their header): SendCannedText (replaces SendMessageFail), GetFriendsListResponse, AddFriendRequest,
  AddFriendResponse, RemoveFriendResponse, UpdateFriendNotify, WhoResponse,
  ShowAllResponse, Get/Add/RemoveIgnoreResponse, TeamInvite, TeamInviteInitialResponse,
  and the team game messages chat writes (TeamInviteConfirm, TeamGetStatusResponse,
  TeamSetLeader, TeamAddPlayer, TeamRemovePlayer, TeamSetOffWorldFlag) as TeamGameMsg
  structs, since chat doesn't link dGame's NetGameMsg.
- WORLD_ROUTE_PACKET is ChatPackets::WorldRoutePacket (its own file, dNet/WorldRoutePacket.h,
  since it carries packets of other services): the target and the inner packet.
  ChatPacketHandler::SendRouted replaces the per-function route headers and
  SendRoutedMsg.

Dispatch: dNet/PacketDispatcher.h is a dispatch map from packet ID to (struct, handler
function); packets that fail to Deserialize are logged and dropped. The chat server's
switch and the world's HandlePacketChat switch are now maps. The handlers take the
structs; their logic is unchanged. World code sends to chat with ChatServerLink::Send
(dGame) instead of writing to Game::chatServer by hand. eChatChannel,
eChatMessageResponseCode and eAddIgnoreResponse moved to dCommon/dEnums so the structs
can use them.

Verified: tests/dGameTests/dNetTests/Legacy/ChatPacketsLegacy.h is a verbatim copy of
the old senders and readers; ChatPacketsTests (25 tests) sends a grid of inputs through
both and requires identical bits, bytes and destination, checks the old readers read
what the structs write, round trips every struct, checks truncated packets are refused,
and has hand written golden packets for general chat, canned text, GM mute and a routed
team game message. Breaking one field width (UpdateFriendNotify) and the TeamAddPlayer
zone flag made the tests fail. No wire bytes changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:50 -05:00
Aaron Kimbrell
0c2727ad3c refactor: building game messages and blueprint packets as structs
Converts build mode, arranging, modular build and brick by brick
messages to NetGameMsgs in BuildingMessages.{h,cpp}: StartArrangingWithItem,
FinishArrangingWithItem, ModularBuildEnd and SetBuildModeConfirmed
(sent), and StartBuildingWithItem, DoneArrangingWithItem,
ModularBuildFinish, ModularBuildMoveAndEquip, ModularBuildConvertModel,
SetBuildMode, BuildModeSet, UnUseBBBModel, BBBLoadItemRequest and
BBBSaveRequest (received, registered in GameMessageHandler's map with
their handlers' logic kept). The BLUEPRINT_SAVE_RESPONSE and
BLUEPRINT_LOAD_RESPONSE_ITEMID client packets become the LUBitStream
structs ClientPackets::BlueprintSaveResponse and BlueprintLoadItemResponse,
also used by WorldServer's level load. The never-called
SendBBBSaveResponse is gone.

SetBuildModeConfirmed keeps writing modeValue's and startPos's default
flags as always set, as DLU did. BuildModeSet and UnUseBBBModel now read
their whole client layout (DLU read only the first fields).

No wire change and no change in recipients. Verified byte for byte
against a frozen verbatim copy of the old functions and inline packet
writes over an input grid, received messages compared with the old
handlers' read sequences with every truncated payload rejected, hand
computed golden bytes, round trips and a mutation check. Layouts
confirmed against the 1.10.64 client in Ghidra.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:49 -05:00
Aaron Kimbrell
b489ee59f8 refactor: world packets as structs
WorldPackets now holds what a client sends a world server, one struct per
packet with Serialize/Deserialize: Validation, CharacterListRequest,
CharacterCreateRequest, CharacterLoginRequest, GameMessage,
CharacterDeleteRequest, CharacterRenameRequest, LevelLoadComplete,
PositionUpdate, MailPacket, RoutePacket, StringCheck, GeneralChatMessage,
HandleFunness, UIHelpTop5. WorldServer's switch became a dispatch map of
handlers (each overrides Handle in WorldServer.cpp, logic unchanged:
dashboard hooks, LoadPlayer, chat logging, migration checks, message
inspector capture all still run); a packet that does not deserialize is
logged and dropped. UserManager's create/delete/rename take the structs.

The answers are ClientPackets (the CLIENT service): LoadStaticZone,
CharacterListResponse, CharacterCreateResponse, CharacterRenameResponse,
DeleteCharacterResponse, TransferToWorld, ServerStates, CreateCharacter,
ChatModerationString, MakeGMResponse, HTTPMonitorInfoResponse and
DebugOutput, built at the call sites (world server, UserManager, slash
commands, dashboard actions, components, migration). The world -> chat
forward of a routed packet is ChatPackets::RoutedFromClient. All
WorldPackets::Send* functions, HTTPMonitorInfo and the ClientPackets parse
functions are gone. The architecture doc now states the file rule: a
packet lives in the file of the ServiceType in its header.

No wire change. Verified against frozen verbatim copies of the old code
(tests/dGameTests/dNetTests/Legacy/WorldPacketsLegacy.h): every response
is sent through the old function and the struct over grids of inputs
(all enum values, strings of every length class, IDs, >64 moderation
segments, big XML) and must match byte for byte; every request is read
by the old code and the struct and must give the same values; plus
golden bytes, round trips, truncation checks and a field width mutation
that made the tests fail. Only differences: malformed requests are
dropped instead of handled with partial values, and LevelLoadComplete now
reads the zone ID the client sends after it (lu_packets and captures show
the 1.10.64 client always sends it; DLU ignored it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:49 -05:00
Aaron Kimbrell
34f43c3fef feat(auth): stamp each login step as it happens
The login response's stamps were a fixed list: START and CLIENT_OS at the
start, an ERROR or two, and IM_LOGIN_START / WORLD_COMMUNICATION_FINISH
appended to every response whatever happened. Now a Stamps list (new
dNet/Stamps.{h,cpp}: eStamps, Stamp and Stamps with Serialize /
Deserialize in the login response's layout, so server messages can carry
it too) is created when the login request arrives, and each step adds its
own stamp, with the time, where it happens: the request read (value: the
client's OS), the account lookup (found or not), the password check,
failed checks, the closed server and play key checks, database writes,
asking master for a world, master's answer, handing the session key to
master, and sending the player on. The response serializes whatever was
stamped; the auth server logs the list like the client does.

What the client does with stamps (1.10.64): PacketHandler_MSG_CLIENT_
LOGIN_RESPONSE @ 00b32f90 reads them (LoginResponse::ReadStamps @ 005ed3c0,
count = (size - 4) / 16) and only logs each with its name from
StampLookup @ 017e6e88 (the 39 eStamps names) and its time relative to the
first and previous stamp. Documented in Stamps.h.

Behaviour change: on success, master gets the session key just before the
client gets the response instead of just after, so the handoff can be
stamped (and master knows the key before the client can reach a world).
The client-facing layout is unchanged; tests pin the stamp bytes, check
the steps of a failed login in order, and round trip the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:48 -05:00
Aaron Kimbrell
70f1c2b19e refactor: common and auth packets as structs
Adds CommonPackets (ServiceType::COMMON): ClientVersionConfirm (the
client's handshake, with the handler that logs it and answers),
ServerVersionConfirm, DisconnectNotify and GeneralNotify (layout from the
1.10.64 client's PacketHandler_MSG_SERVER_GENERAL_NOTIFY; DLU does not send
it yet). AuthPackets::LoginRequest reads the login and keeps the old login
logic in its Handle; ClientPackets::LoginResponse (with the Stamp list)
replaces the hand written response, and AuthPackets::SendLoginResponse
fills it from the settings as before. dServer::Disconnect writes a
DisconnectNotify. AuthServer's if-chain and WorldServer's COMMON case
become CommonPackets::Handle / AuthPackets::Handle, dispatch maps that
read the struct, drop and log it if it does not deserialize, then Handle.
HandleHandshake and SendHandshake are gone.

No wire change. Verified against a frozen verbatim copy of the old
functions (tests/dGameTests/dNetTests/Legacy): the old handshake and login
handlers and the new dispatchers are fed the same packets and must send
identical bytes to the same address (same RNG seed for the session key),
over grids of versions, service types, ports, response codes, error
messages, IPs and stamp lists; plus hand computed golden bytes, round
trips, truncation checks, and a deliberate field width mutation that made
the tests fail. The only behaviour difference: truncated handshake and
login packets are now dropped instead of handled with whatever was read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:30:48 -05:00
David Markowitz
b683413a60 Remove deps from dNet (#1401) 2024-01-07 08:05:57 +01:00
Aaron Kimbrell
78d8c57752 feat: make the help menu top 5 issues work and configurable (#1293)
* feat: make make the help menu questions work

* address feedback

* typo

* update defaults

* fix and address feedback

* newline
2023-11-15 19:30:46 -06:00
aronwk-aaron
19e77a38d8 format codebase 2022-07-28 08:39:57 -05:00
Unknown
0545adfac3 Public release of the DLU server code!
Have fun!
2021-12-05 18:54:36 +01:00