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>
This commit is contained in:
DrPerkyLegit 2026-03-29 13:56:24 -04:00 committed by GitHub
parent 33e0ecac56
commit da2aaf1247
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 241 additions and 4 deletions

View file

@ -18,7 +18,10 @@
#include "..\Minecraft.Server\ServerLogManager.h"
#include "..\Minecraft.Server\Access\Access.h"
#include "..\Minecraft.World\Socket.h"
#include <FourKitBridge.h>
#include <Windows64/Network/WinsockNetLayer.h>
#endif
// #ifdef __PS3__
// #include "PS3\Network\NetworkPlayerSony.h"
// #endif
@ -111,6 +114,58 @@ void PendingConnection::handlePreLogin(shared_ptr<PreLoginPacket> 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();

View file

@ -1437,10 +1437,12 @@ void PlayerConnection::handleClientCommand(shared_ptr<ClientCommandPacket> packe
void PlayerConnection::handleRespawn(shared_ptr<RespawnPacket> packet)
{
//todo: fire respawn event
}
void PlayerConnection::handleContainerClose(shared_ptr<ContainerClosePacket> packet)
{
//todo: fire container close event
player->doCloseContainer();
}
@ -1819,6 +1821,7 @@ bool PlayerConnection::isServerPacketListener()
void PlayerConnection::handlePlayerAbilities(shared_ptr<PlayerAbilitiesPacket> playerAbilitiesPacket)
{
//todo: fire abilities change event
player->abilities.flying = playerAbilitiesPacket->isFlying() && player->abilities.mayfly;
}
@ -1937,6 +1940,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> custo
Slot *slot = beaconMenu->getSlot(0);
if (slot->hasItem())
{
//todo: beacon powered event?
slot->remove(1);
shared_ptr<BeaconTileEntity> beacon = beaconMenu->getBeacon();
beacon->setPrimaryPower(primary);
@ -1950,6 +1954,7 @@ void PlayerConnection::handleCustomPayload(shared_ptr<CustomPayloadPacket> custo
AnvilMenu *menu = dynamic_cast<AnvilMenu *>( 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<DebugOptionsPacket> packet)
{
//todo: debug options event?
#ifdef _DEBUG
// Player player = dynamic_pointer_cast<Player>( player->shared_from_this() );
player->SetDebugOptions(packet->m_uiVal);
@ -2063,6 +2069,8 @@ void PlayerConnection::handleCraftItem(shared_ptr<CraftItemPacket> 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<TradeItemPacket> 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

View file

@ -0,0 +1,40 @@
namespace Minecraft.Server.FourKit.Event.Player;
using Minecraft.Server.FourKit.Net;
/// <summary>
/// Stores details for players attempting to log in.
/// </summary>
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;
}
/// <summary>
/// Gets the player's name.
/// </summary>
/// <returns>The player's name.</returns>
public string getName() => name;
/// <summary>
/// Gets the player IP address.
/// </summary>
/// <returns>The IP address.</returns>
public InetSocketAddress getAddress() => ipAddress;
/// <inheritdoc/>
public bool isCancelled() => _cancelled;
/// <inheritdoc/>
public void setCancelled(bool cancel) => _cancelled = cancel;
}

View file

@ -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)
{
}
}

View file

@ -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)
{
}
}

View file

@ -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;
}
/// <summary>Returns the plugin involved in this event.</summary>
public ServerPlugin getPlugin() => _plugin;
}

View file

@ -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;
}

View file

@ -0,0 +1,11 @@
namespace Minecraft.Server.FourKit.Event.Server;
using Minecraft.Server.FourKit.Plugin;
public abstract class ServerEvent : Event
{
internal protected ServerEvent() : base()
{
}
}

View file

@ -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)
{

View file

@ -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)
{

View file

@ -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<int, OpenContainerInfo> 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)

View file

@ -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,