中文 | English
This document describes the implementation for maintainers and contributors.
velocity_forget_me/
├── __init__.py # MCDR entrypoint, config loading, on_info listener
├── processor.py # event ordering, sessions, and record removal
└── utils/
├── config.py # Serializable config and field validation
├── connection.py # Velocity connection-log parsing
├── paths.py # MCDR working_directory path resolution
└── uuid.py # PlayerDB UUID lookup
The plugin handles MCDR's mcdr.general_info event through on_info():
- Only
info.is_from_servermessages are accepted. info.contentis passed toConnectionEventParser.- The configured regex must provide named
player,server, andactiongroups. - A successfully parsed event receives a sequence number and is passed to
ConnectionProcessor.process().
process() is decorated with MCDR's @new_thread() and runs in a daemon thread. Network requests and file operations do not block MCDR's info-processing thread.
JoinSession represents one player's connection session on one backend server. The session key is:
(player_name.casefold(), server_name)Therefore these are independent sessions:
(Player, lobby)
(Player, survival)
A connection or disconnect on one server does not overwrite, end, or modify the other server's session.
When the same player connects to the same server again, the generation for that (player, server) pair is incremented and the current session is replaced. The generation is used to verify that asynchronous processing still targets the current session.
A session stores:
- player name;
- server name;
- generation;
- connection time;
- UUID;
- disconnect time;
- whether removal has already been attempted.
on_info() calls reserve() before submitting the background task. Each processing thread waits until its sequence number is current, preserving the order in which MCDR received the log events.
A connection event only creates or replaces the corresponding session. It does not immediately query the UUID or remove a record.
A disconnect event looks up the session using (player, disconnect_server). If there is no matching session, it is ignored.
All of the following must hold before a record can be removed:
- A session exists for the same player and server.
- The disconnect time is not earlier than the connection time.
- The connection-to-disconnect duration does not exceed
disconnect_window_seconds. - The session is still the current generation for that
(player, server)pair. - The UUID is available from the session, cache, or PlayerDB.
- The RememberMe file content equals both the connection server and disconnect server.
The record path is:
<record_directory>/<uuid>.txt
Path.unlink() is called only after all checks pass.
A failed removal is never logged as successful. A missing file uses DEBUG, read/delete errors use ERROR, and a server-name mismatch uses WARNING.
There is currently no independent timer that removes sessions which never receive a disconnect event:
- when a matching disconnect arrives after the time window, the session is removed but the RememberMe file is retained;
- when a matching disconnect is eligible, the checks run and the session is then removed;
- when UUID lookup fails, the current session is removed and the record is retained;
- unloading or reloading the plugin calls
close(), which clears all in-memory sessions; - if no disconnect event ever arrives, the session remains until one of those cleanup points.
PluginConfig inherits from MCDR's Serializable. The entrypoint loads it with:
server.load_config_simple(
target_class=PluginConfig,
failure_policy='raise',
)Field defaults are supplied by Serializable class attributes. validate_attribute() validates the path, numeric values, regex, and UUID API URL during deserialization. After loading, the record directory is resolved against MCDR's working_directory and passed separately to the processor; the original config string is not rewritten.
- Prefer changing
connection_regexin the configuration when the proxy log format changes; keep the three named capture groups. - Changes to session logic must consider
_sessions,_generations, and_is_current_session()together. - Do not move PlayerDB lookup back into
on_info(); it must remain inside the@new_thread()processor. - Keep the server-name checks before removal so one server's disconnect cannot remove another server's RememberMe record.
- The plugin supports file-based RememberMe only; do not assume the storage structure of
VelocityRememberServeror LuckPerms.
Compile all source files:
python -m py_compile velocity_forget_me/__init__.py velocity_forget_me/processor.py velocity_forget_me/utils/__init__.py velocity_forget_me/utils/config.py velocity_forget_me/utils/connection.py velocity_forget_me/utils/paths.py velocity_forget_me/utils/uuid.pyPack with MCDR's native command:
python -m mcdreforged pack