From da2aaf1247c87f0e46ee6db5252aca48a87119bb Mon Sep 17 00:00:00 2001 From: DrPerkyLegit <116128211+DrPerkyLegit@users.noreply.github.com> Date: Sun, 29 Mar 2026 13:56:24 -0400 Subject: [PATCH] Add PlayerPreLoginEvent (#8) * PlayerPreLoginEvent, comments for more events * basic plugin events * plugin failed to load event * add docs --------- Co-authored-by: sylvessa <225480449+sylvessa@users.noreply.github.com> --- Minecraft.Client/PendingConnection.cpp | 55 +++++++++++++++++++ Minecraft.Client/PlayerConnection.cpp | 9 +++ .../Event/Player/PlayerPreLoginEvent.cs | 40 ++++++++++++++ .../Event/Server/PluginDisableEvent.cs | 11 ++++ .../Event/Server/PluginEnableEvent.cs | 11 ++++ .../Event/Server/PluginEvent.cs | 16 ++++++ .../Event/Server/PluginLoadFailedEvent.cs | 18 ++++++ .../Event/Server/ServerEvent.cs | 11 ++++ .../FourKitHost.Events.cs | 25 +++++++++ Minecraft.Server.FourKit/PluginLoader.cs | 28 ++++++++-- Minecraft.Server/FourKitBridge.cpp | 20 +++++++ Minecraft.Server/FourKitBridge.h | 1 + 12 files changed, 241 insertions(+), 4 deletions(-) create mode 100644 Minecraft.Server.FourKit/Event/Player/PlayerPreLoginEvent.cs create mode 100644 Minecraft.Server.FourKit/Event/Server/PluginDisableEvent.cs create mode 100644 Minecraft.Server.FourKit/Event/Server/PluginEnableEvent.cs create mode 100644 Minecraft.Server.FourKit/Event/Server/PluginEvent.cs create mode 100644 Minecraft.Server.FourKit/Event/Server/PluginLoadFailedEvent.cs create mode 100644 Minecraft.Server.FourKit/Event/Server/ServerEvent.cs diff --git a/Minecraft.Client/PendingConnection.cpp b/Minecraft.Client/PendingConnection.cpp index f24086c1..b651c8ad 100644 --- a/Minecraft.Client/PendingConnection.cpp +++ b/Minecraft.Client/PendingConnection.cpp @@ -18,7 +18,10 @@ #include "..\Minecraft.Server\ServerLogManager.h" #include "..\Minecraft.Server\Access\Access.h" #include "..\Minecraft.World\Socket.h" +#include +#include #endif + // #ifdef __PS3__ // #include "PS3\Network\NetworkPlayerSony.h" // #endif @@ -111,6 +114,58 @@ void PendingConnection::handlePreLogin(shared_ptr packet) } return; } + +#if defined(_WINDOWS64) && defined(MINECRAFT_SERVER_BUILD) + std::string connectionIp = ""; + int connectionPort = 0; + + if (!connection || !connection->getSocket()) { + goto handlePreLoginEND; + } + + unsigned char smallId = connection->getSocket()->getSmallId(); + if (smallId == 0) { + goto handlePreLoginEND; + } + + if (!ServerRuntime::ServerLogManager::TryGetConnectionRemoteIp(smallId, &connectionIp)) + { + SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId); + if (sock != INVALID_SOCKET) + { + sockaddr_in addr; + int addrLen = sizeof(addr); + if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) + { + char ipBuf[64] = {}; + if (inet_ntop(AF_INET, &addr.sin_addr, ipBuf, sizeof(ipBuf))) + { + connectionIp = ipBuf; + connectionPort = (int)ntohs(addr.sin_port); + } + } + } + if (connectionIp.empty()) { + goto handlePreLoginEND; + } + } else { + SOCKET sock = WinsockNetLayer::GetSocketForSmallId(smallId); + if (sock != INVALID_SOCKET) + { + sockaddr_in addr; + int addrLen = sizeof(addr); + if (getpeername(sock, (sockaddr*)&addr, &addrLen) == 0) + connectionPort = (int)ntohs(addr.sin_port); + } + } + + if (FourKitBridge::FirePlayerPreLogin(packet->loginKey, connectionIp, connectionPort)) { + disconnect(DisconnectPacket::eDisconnect_EndOfStream); //idk what to use here, eventually it should be set by the event + return; + } +#endif + +handlePreLoginEND: // printf("Server: handlePreLogin\n"); name = packet->loginKey; // 4J Stu - Change from the login packet as we know better on client end during the pre-login packet sendPreLoginResponse(); diff --git a/Minecraft.Client/PlayerConnection.cpp b/Minecraft.Client/PlayerConnection.cpp index e4dcac28..39ad92ef 100644 --- a/Minecraft.Client/PlayerConnection.cpp +++ b/Minecraft.Client/PlayerConnection.cpp @@ -1437,10 +1437,12 @@ void PlayerConnection::handleClientCommand(shared_ptr packe void PlayerConnection::handleRespawn(shared_ptr packet) { + //todo: fire respawn event } void PlayerConnection::handleContainerClose(shared_ptr packet) { + //todo: fire container close event player->doCloseContainer(); } @@ -1819,6 +1821,7 @@ bool PlayerConnection::isServerPacketListener() void PlayerConnection::handlePlayerAbilities(shared_ptr playerAbilitiesPacket) { + //todo: fire abilities change event player->abilities.flying = playerAbilitiesPacket->isFlying() && player->abilities.mayfly; } @@ -1937,6 +1940,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr custo Slot *slot = beaconMenu->getSlot(0); if (slot->hasItem()) { + //todo: beacon powered event? slot->remove(1); shared_ptr beacon = beaconMenu->getBeacon(); beacon->setPrimaryPower(primary); @@ -1950,6 +1954,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr custo AnvilMenu *menu = dynamic_cast( player->containerMenu); if (menu) { + //todo: anvel item rename event? if (customPayloadPacket->data.data == nullptr || customPayloadPacket->data.length < 1) { menu->setItemName(L""); @@ -1977,6 +1982,7 @@ bool PlayerConnection::isDisconnected() void PlayerConnection::handleDebugOptions(shared_ptr packet) { + //todo: debug options event? #ifdef _DEBUG // Player player = dynamic_pointer_cast( player->shared_from_this() ); player->SetDebugOptions(packet->m_uiVal); @@ -2063,6 +2069,8 @@ void PlayerConnection::handleCraftItem(shared_ptr packet) } } + //todo: fire item crafted event? + // 4J Stu - Fix for #13119 - We should add the item after we remove the ingredients if(player->inventory->add(pTempItemInst)==false ) { @@ -2137,6 +2145,7 @@ void PlayerConnection::handleTradeItem(shared_ptr packet) int buyBMatches = player->inventory->countMatches(buyBItem); if( (buyAItem != nullptr && buyAMatches >= buyAItem->count) && (buyBItem == nullptr || buyBMatches >= buyBItem->count) ) { + //todo: fire trade event? menu->getMerchant()->notifyTrade(activeRecipe); // Remove the items we are purchasing with diff --git a/Minecraft.Server.FourKit/Event/Player/PlayerPreLoginEvent.cs b/Minecraft.Server.FourKit/Event/Player/PlayerPreLoginEvent.cs new file mode 100644 index 00000000..a3015b1e --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Player/PlayerPreLoginEvent.cs @@ -0,0 +1,40 @@ +namespace Minecraft.Server.FourKit.Event.Player; + +using Minecraft.Server.FourKit.Net; + +/// +/// Stores details for players attempting to log in. +/// +public class PlayerPreLoginEvent : Event, Cancellable +{ + private string name; + private InetSocketAddress ipAddress; //bukkit uses InetAddress but we expose port also + private bool _cancelled; + + + internal PlayerPreLoginEvent(string name, InetSocketAddress ipAddress) : base() + { + this.name = name; + this.ipAddress = ipAddress; + } + + + /// + /// Gets the player's name. + /// + /// The player's name. + public string getName() => name; + + + /// + /// Gets the player IP address. + /// + /// The IP address. + public InetSocketAddress getAddress() => ipAddress; + + /// + public bool isCancelled() => _cancelled; + + /// + public void setCancelled(bool cancel) => _cancelled = cancel; +} diff --git a/Minecraft.Server.FourKit/Event/Server/PluginDisableEvent.cs b/Minecraft.Server.FourKit/Event/Server/PluginDisableEvent.cs new file mode 100644 index 00000000..6ef8ad0d --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Server/PluginDisableEvent.cs @@ -0,0 +1,11 @@ +namespace Minecraft.Server.FourKit.Event.Server; + +using Minecraft.Server.FourKit.Plugin; + +public class PluginDisableEvent : PluginEvent +{ + + internal PluginDisableEvent(ServerPlugin plugin) : base(plugin) + { + } +} diff --git a/Minecraft.Server.FourKit/Event/Server/PluginEnableEvent.cs b/Minecraft.Server.FourKit/Event/Server/PluginEnableEvent.cs new file mode 100644 index 00000000..9218bcc8 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Server/PluginEnableEvent.cs @@ -0,0 +1,11 @@ +namespace Minecraft.Server.FourKit.Event.Server; + +using Minecraft.Server.FourKit.Plugin; + +public class PluginEnableEvent : PluginEvent +{ + + internal PluginEnableEvent(ServerPlugin plugin) : base(plugin) + { + } +} diff --git a/Minecraft.Server.FourKit/Event/Server/PluginEvent.cs b/Minecraft.Server.FourKit/Event/Server/PluginEvent.cs new file mode 100644 index 00000000..ebc49294 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Server/PluginEvent.cs @@ -0,0 +1,16 @@ +namespace Minecraft.Server.FourKit.Event.Server; + +using Minecraft.Server.FourKit.Plugin; + +public abstract class PluginEvent : ServerEvent +{ + private readonly ServerPlugin _plugin; + + internal protected PluginEvent(ServerPlugin plugin) : base() + { + _plugin = plugin; + } + + /// Returns the plugin involved in this event. + public ServerPlugin getPlugin() => _plugin; +} diff --git a/Minecraft.Server.FourKit/Event/Server/PluginLoadFailedEvent.cs b/Minecraft.Server.FourKit/Event/Server/PluginLoadFailedEvent.cs new file mode 100644 index 00000000..a15ad97f --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Server/PluginLoadFailedEvent.cs @@ -0,0 +1,18 @@ +namespace Minecraft.Server.FourKit.Event.Server; + +using Minecraft.Server.FourKit.Plugin; + +public class PluginLoadFailedEvent : ServerEvent +{ + private readonly string _fileName; + private readonly string _message; + internal PluginLoadFailedEvent(string fileName, string message) : base() + { + _fileName = fileName; + _message = message; + } + + public string getFileName() => _fileName; + + public string getMessage() => _message; +} diff --git a/Minecraft.Server.FourKit/Event/Server/ServerEvent.cs b/Minecraft.Server.FourKit/Event/Server/ServerEvent.cs new file mode 100644 index 00000000..e3b437d8 --- /dev/null +++ b/Minecraft.Server.FourKit/Event/Server/ServerEvent.cs @@ -0,0 +1,11 @@ +namespace Minecraft.Server.FourKit.Event.Server; + +using Minecraft.Server.FourKit.Plugin; + +public abstract class ServerEvent : Event +{ + + internal protected ServerEvent() : base() + { + } +} diff --git a/Minecraft.Server.FourKit/FourKitHost.Events.cs b/Minecraft.Server.FourKit/FourKitHost.Events.cs index a0dd4fb2..85ecc193 100644 --- a/Minecraft.Server.FourKit/FourKitHost.Events.cs +++ b/Minecraft.Server.FourKit/FourKitHost.Events.cs @@ -7,11 +7,36 @@ using Minecraft.Server.FourKit.Event.Entity; using Minecraft.Server.FourKit.Event.Player; using Minecraft.Server.FourKit.Event.Inventory; using Minecraft.Server.FourKit.Inventory; +using Minecraft.Server.FourKit.Net; namespace Minecraft.Server.FourKit; public static partial class FourKitHost { + [UnmanagedCallersOnly] + public static int FirePlayerPreLogin(IntPtr namePtr, int nameByteLen, IntPtr ipPtr, int ipByteLen, int port) + { + try + { + string name = nameByteLen > 0 + ? Marshal.PtrToStringUTF8(namePtr, nameByteLen) ?? string.Empty + : string.Empty; + + string ipStr = ipByteLen > 0 + ? Marshal.PtrToStringUTF8(ipPtr, ipByteLen) ?? string.Empty + : string.Empty; + + var evt = new PlayerPreLoginEvent(name, new InetSocketAddress(new InetAddress(ipStr), port)); + FourKit.FireEvent(evt); + return evt.isCancelled() ? 1 : 0; + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"FirePlayerJoin error: {ex}"); + return 0; + } + } + [UnmanagedCallersOnly] public static void FirePlayerJoin(int entityId, IntPtr namePtr, int nameByteLen, IntPtr uuidPtr, int uuidByteLen) { diff --git a/Minecraft.Server.FourKit/PluginLoader.cs b/Minecraft.Server.FourKit/PluginLoader.cs index afad29b2..506e36b7 100644 --- a/Minecraft.Server.FourKit/PluginLoader.cs +++ b/Minecraft.Server.FourKit/PluginLoader.cs @@ -1,3 +1,4 @@ +using Minecraft.Server.FourKit.Event.Server; using Minecraft.Server.FourKit.Plugin; using System.Reflection; @@ -44,15 +45,30 @@ internal sealed class PluginLoader if (mainDll != null) { - try { LoadPluginAssembly(mainDll); } - catch (Exception ex) { ServerLog.Error("fourkit", $"Failed to load {Path.GetFileName(mainDll)}: {ex.Message}"); } + try + { + LoadPluginAssembly(mainDll); + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"Failed to load {Path.GetFileName(mainDll)}: {ex.Message}"); + FourKit.FireEvent(new PluginLoadFailedEvent(mainDll, ex.Message)); + } } else { foreach (var dll in allDlls) { - try { LoadPluginAssembly(dll); } - catch (Exception ex) { ServerLog.Error("fourkit", $"Failed to load {Path.GetFileName(dll)}: {ex.Message}"); } + try + { + LoadPluginAssembly(dll); + } + catch (Exception ex) + { + ServerLog.Error("fourkit", $"Failed to load {Path.GetFileName(dll)}: {ex.Message}"); + FourKit.FireEvent(new PluginLoadFailedEvent(dll, ex.Message)); + + } } } } @@ -108,6 +124,8 @@ internal sealed class PluginLoader InvokePluginMethod(plugin, "onEnable", "OnEnable"); string pName = GetPluginString(plugin, "name", "getName", "GetName", plugin.GetType().Name); ServerLog.Info("fourkit", $"Enabled: {pName}"); + + FourKit.FireEvent(new PluginEnableEvent(plugin)); } catch (Exception ex) { @@ -126,6 +144,8 @@ internal sealed class PluginLoader InvokePluginMethod(_plugins[i], "onDisable", "OnDisable"); string pName = GetPluginString(_plugins[i], "name", "getName", "GetName", _plugins[i].GetType().Name); ServerLog.Info("fourkit", $"Disabled: {pName}"); + + FourKit.FireEvent(new PluginDisableEvent(_plugins[i])); } catch (Exception ex) { diff --git a/Minecraft.Server/FourKitBridge.cpp b/Minecraft.Server/FourKitBridge.cpp index fe15b058..7e909451 100644 --- a/Minecraft.Server/FourKitBridge.cpp +++ b/Minecraft.Server/FourKitBridge.cpp @@ -17,6 +17,7 @@ namespace FourKitBridge { typedef void(__stdcall *fn_initialize)(); +typedef int(__stdcall *fn_fire_player_prelogin)(const char* nameUtf8, int nameByteLen, const char* ipUtf8, int ipByteLen, int port); typedef void(__stdcall *fn_fire_player_join)(int entityId, const char *nameUtf8, int nameByteLen, const char *uuidUtf8, int uuidByteLen); typedef void(__stdcall *fn_fire_player_quit)(int entityId); typedef int(__stdcall *fn_fire_player_kick)(int entityId, int disconnectReason, @@ -100,6 +101,7 @@ struct OpenContainerInfo static std::unordered_map s_openContainerInfo; static fn_initialize s_managedInit = nullptr; +static fn_fire_player_prelogin s_managedFirePreLogin = nullptr; static fn_fire_player_join s_managedFireJoin = nullptr; static fn_update_entity_id s_managedUpdateEntityId = nullptr; static fn_fire_player_quit s_managedFireQuit = nullptr; @@ -165,6 +167,7 @@ void Initialize() struct { const wchar_t *name; void **target; } entries[] = { {L"Initialize", (void **)&s_managedInit}, + {L"FirePlayerPreLogin", (void **)&s_managedFirePreLogin}, {L"FirePlayerJoin", (void **)&s_managedFireJoin}, {L"FirePlayerQuit", (void **)&s_managedFireQuit}, {L"FirePlayerKick", (void **)&s_managedFireKick}, @@ -305,6 +308,23 @@ void Shutdown() LogInfo("fourkit", "FourKit shut down."); } +bool FirePlayerPreLogin(const std::wstring& name, const std::string& ip, int port) { + if (!s_initialized || !s_managedFirePreLogin) + { + return true; + } + + std::string nameUtf8 = ServerRuntime::StringUtils::WideToUtf8(name); + int canceled = s_managedFirePreLogin( + nameUtf8.empty() ? "" : nameUtf8.data(), (int)nameUtf8.size(), + ip.empty() ? "" : ip.data(), (int)ip.size(), + port); + + LogDebugf("fourkit", "Fired PlayerPreLogin: %s", nameUtf8.data()); + + return canceled != 0; +} + void FirePlayerJoin(int entityId, const std::wstring &name, const std::wstring &uuid) { if (!s_initialized || !s_managedFireJoin) diff --git a/Minecraft.Server/FourKitBridge.h b/Minecraft.Server/FourKitBridge.h index 3025c413..78013009 100644 --- a/Minecraft.Server/FourKitBridge.h +++ b/Minecraft.Server/FourKitBridge.h @@ -5,6 +5,7 @@ namespace FourKitBridge { void Initialize(); void Shutdown(); + bool FirePlayerPreLogin(const std::wstring& name, const std::string& ip, int port); void FirePlayerJoin(int entityId, const std::wstring &name, const std::wstring &uuid); bool FirePlayerQuit(int entityId); bool FirePlayerKick(int entityId, int disconnectReason,