Merge branch 'feature/plugin-api' of github.com:sylvessa/MinecraftConsoles into feature/plugin-api

This commit is contained in:
sylvessa 2026-05-06 17:37:05 -05:00
commit 8e66b2c19e
13 changed files with 570 additions and 5 deletions

View file

@ -63,6 +63,10 @@
#include "Durango\Network\NetworkPlayerDurango.h" #include "Durango\Network\NetworkPlayerDurango.h"
#endif #endif
#if defined(MINECRAFT_SERVER_BUILD)
#include "..\Minecraft.Server\FourKitNatives.h"
#endif
#define DEBUG_SERVER_DONT_SPAWN_MOBS 0 #define DEBUG_SERVER_DONT_SPAWN_MOBS 0
//4J Added //4J Added
@ -2208,6 +2212,12 @@ void MinecraftServer::tick()
connection->tick(); connection->tick();
PIXEndNamedEvent(); PIXEndNamedEvent();
#if defined(MINECRAFT_SERVER_BUILD)
PIXBeginNamedEvent(0, "FourKit Scheduler tick");
FourKitBridge::ServerTickCallback(tickCount);
PIXEndNamedEvent();
#endif
// 4J - removed // 4J - removed
#if 0 #if 0
for (size_t i = 0; i < tickables.size(); i++) { for (size_t i = 0; i < tickables.size(); i++) {

View file

@ -5,6 +5,7 @@ using Minecraft.Server.FourKit.Entity;
using Minecraft.Server.FourKit.Event; using Minecraft.Server.FourKit.Event;
using Minecraft.Server.FourKit.Inventory; using Minecraft.Server.FourKit.Inventory;
using Minecraft.Server.FourKit.Plugin; using Minecraft.Server.FourKit.Plugin;
using Minecraft.Server.FourKit.Scheduler;
/// <summary> /// <summary>
/// The main entry point for the FourKit plugin API. /// The main entry point for the FourKit plugin API.
@ -388,4 +389,6 @@ public static class FourKit
/// </summary> /// </summary>
/// <param name="plugin">Plugin to disable.</param> /// <param name="plugin">Plugin to disable.</param>
public static void disablePlugin(ServerPlugin plugin) => FourKitHost.s_loader?.DisablePlugin(plugin); public static void disablePlugin(ServerPlugin plugin) => FourKitHost.s_loader?.DisablePlugin(plugin);
public static FourKitScheduler getScheduler() => FourKitHost.getScheduler();
} }

View file

@ -1,9 +1,24 @@
using Minecraft.Server.FourKit.Scheduler;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace Minecraft.Server.FourKit; namespace Minecraft.Server.FourKit;
public static partial class FourKitHost public static partial class FourKitHost
{ {
[UnmanagedCallersOnly]
public static void SetSchedulerCallbacks(IntPtr add, IntPtr remove)
{
try
{
NativeBridge.SetSchedulerCallbacks(add, remove);
}
catch (Exception ex)
{
ServerLog.Error("fourkit", $"SetSchedulerCallbacks error: {ex}");
}
}
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static void SetNativeCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity) public static void SetNativeCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity)
{ {

View file

@ -190,6 +190,19 @@ public static partial class FourKitHost
} }
} }
[UnmanagedCallersOnly]
public static void FireSchedulerCallback(int currentTick) {
try
{
FourKit.getScheduler().update(currentTick);
}
catch (Exception ex)
{
ServerLog.Error("fourkit", $"FireSchedulerCallback error: {ex}");
return;
}
}
[UnmanagedCallersOnly] [UnmanagedCallersOnly]
public static int FirePlayerMove(int entityId, public static int FirePlayerMove(int entityId,
double fromX, double fromY, double fromZ, double fromX, double fromY, double fromZ,

View file

@ -1,13 +1,15 @@
using System.Runtime.InteropServices; using Minecraft.Server.FourKit.Entity;
using Minecraft.Server.FourKit.Entity;
using Minecraft.Server.FourKit.Event.Inventory; using Minecraft.Server.FourKit.Event.Inventory;
using Minecraft.Server.FourKit.Inventory; using Minecraft.Server.FourKit.Inventory;
using Minecraft.Server.FourKit.Scheduler;
using System.Runtime.InteropServices;
namespace Minecraft.Server.FourKit; namespace Minecraft.Server.FourKit;
public static partial class FourKitHost public static partial class FourKitHost
{ {
internal static PluginLoader? s_loader; internal static PluginLoader? s_loader;
internal static FourKitScheduler? s_scheduler;
public static IReadOnlyList<Plugin.ServerPlugin> getLoadedPlugins() => s_loader?.Plugins ?? []; public static IReadOnlyList<Plugin.ServerPlugin> getLoadedPlugins() => s_loader?.Plugins ?? [];
@ -16,6 +18,9 @@ public static partial class FourKitHost
{ {
try try
{ {
ServerLog.Info("fourkit", "Initializing Scheduler...");
s_scheduler = new FourKitScheduler();
ServerLog.Info("fourkit", "Initializing plugin system..."); ServerLog.Info("fourkit", "Initializing plugin system...");
string pluginsDir = Path.Combine(AppContext.BaseDirectory, "plugins"); string pluginsDir = Path.Combine(AppContext.BaseDirectory, "plugins");
@ -47,6 +52,17 @@ public static partial class FourKitHost
} }
} }
internal static FourKitScheduler getScheduler()
{
if (s_scheduler == null)
{
ServerLog.Warn("fourkit", "Scheduler accessed before initialization. Initializing now...");
s_scheduler = new FourKitScheduler();
}
return s_scheduler;
}
private static Guid ParseOrHashGuid(string s) private static Guid ParseOrHashGuid(string s)
{ {

View file

@ -236,6 +236,13 @@ internal static class NativeBridge
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeSetBiomeIdDelegate(int dimId, int x, int z, int biomeId); internal delegate void NativeSetBiomeIdDelegate(int dimId, int x, int z, int biomeId);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeAddSchedulerDelegate(int taskid, int startDelay, int runCooldown);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
internal delegate void NativeRemoveSchedulerDelegate(int taskid);
internal static NativeDamageDelegate? DamagePlayer; internal static NativeDamageDelegate? DamagePlayer;
internal static NativeSetHealthDelegate? SetPlayerHealth; internal static NativeSetHealthDelegate? SetPlayerHealth;
internal static NativeTeleportDelegate? TeleportPlayer; internal static NativeTeleportDelegate? TeleportPlayer;
@ -315,6 +322,15 @@ internal static class NativeBridge
internal static NativeGetBiomeIdDelegate? GetBiomeId; internal static NativeGetBiomeIdDelegate? GetBiomeId;
internal static NativeSetBiomeIdDelegate? SetBiomeId; internal static NativeSetBiomeIdDelegate? SetBiomeId;
internal static NativeAddSchedulerDelegate? AddScheduler;
internal static NativeRemoveSchedulerDelegate? RemoveScheduler;
internal static void SetSchedulerCallbacks(IntPtr addScheduler, IntPtr removeScheduler)
{
AddScheduler = Marshal.GetDelegateForFunctionPointer<NativeAddSchedulerDelegate>(addScheduler);
RemoveScheduler = Marshal.GetDelegateForFunctionPointer<NativeRemoveSchedulerDelegate>(removeScheduler);
}
internal static void SetCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity) internal static void SetCallbacks(IntPtr damage, IntPtr setHealth, IntPtr teleport, IntPtr setGameMode, IntPtr broadcastMessage, IntPtr setFallDistance, IntPtr getPlayerSnapshot, IntPtr sendMessage, IntPtr setWalkSpeed, IntPtr teleportEntity)
{ {
DamagePlayer = Marshal.GetDelegateForFunctionPointer<NativeDamageDelegate>(damage); DamagePlayer = Marshal.GetDelegateForFunctionPointer<NativeDamageDelegate>(damage);

View file

@ -0,0 +1,245 @@
using Minecraft.Server.FourKit.Plugin;
using System;
using System.Collections.Generic;
using System.Text;
namespace Minecraft.Server.FourKit.Scheduler
{
public class FourKitScheduler
{
private Dictionary<int, FourKitTask> _taskInstanceMap = new Dictionary<int, FourKitTask>();
private Dictionary<int, Action> _taskActionMap = new Dictionary<int, Action>();
//temp task based on about intmax / 3, leaves 715m (715827882) ids for real tasks and 1.4b(1431655765) for temp tasks
//this is done to avoid needing to cleanup existing task ids, could be handled
private int _nextTempTaskId = 715827882;
private int _nextTaskId = 0;
private int _lastTick = -1;
/// <summary>
/// Removes all tasks from the scheduler.
/// </summary>
public void cancelAllTasks()
{
foreach (var task in _taskInstanceMap.Values)
{
NativeBridge.RemoveScheduler?.Invoke(task.getTaskId());
}
_taskInstanceMap.Clear();
_taskActionMap.Clear();
_nextTempTaskId = 715827882;
_nextTaskId = 0;
}
/// <summary>
/// Removes task from scheduler.
/// </summary>
/// <param name="taskId">Id number of task to be removed.</param>
public void cancelTask(int taskId)
{
if (_taskInstanceMap.ContainsKey(taskId))
{
NativeBridge.RemoveScheduler?.Invoke(taskId);
_taskInstanceMap.Remove(taskId);
_taskActionMap.Remove(taskId);
}
}
/// <summary>
/// Removes all tasks associated with a particular plugin from the scheduler.
/// </summary>
/// <param name="plugin">Owner of tasks to be removed.</param>
public void cancelTasks(ServerPlugin plugin)
{
List<int> tasksToRemove = new List<int>();
foreach (var task in _taskInstanceMap.Values)
{
if (task.getOwner() == plugin)
{
NativeBridge.RemoveScheduler?.Invoke(task.getTaskId());
tasksToRemove.Add(task.getTaskId());
}
}
foreach (var taskId in tasksToRemove)
{
_taskInstanceMap.Remove(taskId);
_taskActionMap.Remove(taskId);
}
}
/// <summary>
/// Returns a list of all pending tasks. The ordering of the tasks is not related to their order of execution.
/// </summary>
/// <returns>Active workers.</returns>
public List<FourKitTask> getPendingTasks()
{
return new List<FourKitTask>(_taskInstanceMap.Values);
}
/// <summary>
/// Check if the task currently running.
///
/// A repeating task might not be running currently, but will be running in the future.
/// A task that has finished, and does not repeat, will not be running ever again.
///
/// Explicitly, a task is running if there exists a thread for it, and that thread is alive.
/// </summary>
/// <param name="taskId">The task to check.</param>
/// <returns>If the task is currently running.</returns>
public bool isCurrentlyRunning(int taskId)
{
if (_taskInstanceMap.ContainsKey(taskId))
{
return _taskInstanceMap[taskId].startedRunning;
}
return false;
}
/// <summary>
/// Check if the task queued to be run later.
///
/// If a repeating task is currently running, it might not be queued now but could be in the future.
/// A task that is not queued, and not running, will not be queued again.
/// </summary>
/// <param name="taskId">The task to check.</param>
/// <returns>If the task is queued to be run.</returns>
public bool isQueued(int taskId)
{
if (_taskInstanceMap.ContainsKey(taskId))
{
return !_taskInstanceMap[taskId].startedRunning;
}
return false;
}
/// <summary>
/// Returns a task that will run on the next server tick.
/// </summary>
/// <param name="plugin">The reference to the plugin scheduling task.</param>
/// <param name="task">The task to be run.</param>
/// <returns>A task that contains the id number.</returns>
/// <exception cref="ArgumentNullException">If plugin is null.</exception>
/// <exception cref="ArgumentNullException">If task is null.</exception>
public FourKitTask runTask(ServerPlugin plugin, Action task)
{
FourKitTask fourKitTask = new FourKitTask(plugin, _nextTempTaskId++, 0, -1);
startTask(fourKitTask, task);
return fourKitTask;
}
/// <summary>
/// Returns a task that will run after the specified number of server ticks.
/// </summary>
/// <param name="plugin">The reference to the plugin scheduling task.</param>
/// <param name="task">The task to be run.</param>
/// <param name="delay">The ticks to wait before running the task.</param>
/// <returns>A task that contains the id number.</returns>
/// <exception cref="ArgumentNullException">If plugin is null.</exception>
/// <exception cref="ArgumentNullException">If task is null.</exception>
public FourKitTask runTaskLater(ServerPlugin plugin, Action task, int delay)
{
FourKitTask fourKitTask = new FourKitTask(plugin, _nextTempTaskId++, delay, -1);
startTask(fourKitTask, task);
return fourKitTask;
}
/// <summary>
/// Returns a task that will repeatedly run until cancelled, starting after the specified number of server ticks.
/// </summary>
/// <param name="plugin">The reference to the plugin scheduling task.</param>
/// <param name="task">The task to be run.</param>
/// <param name="delay">The ticks to wait before running the task.</param>
/// <param name="period">The ticks to wait between runs.</param>
/// <returns>A task that contains the id number.</returns>
/// <exception cref="ArgumentNullException">If plugin is null.</exception>
/// <exception cref="ArgumentNullException">If task is null.</exception>
public FourKitTask runTaskTimer(ServerPlugin plugin, Action task, int delay, int period)
{
FourKitTask fourKitTask = new FourKitTask(plugin, _nextTempTaskId++, delay, period);
startTask(fourKitTask, task);
return fourKitTask;
}
/// <summary>
/// Starts tracking a task instance and registers it with the native scheduler bridge.
/// </summary>
/// <param name="task">The task instance to schedule.</param>
/// <param name="action">The callback action executed when the task runs.</param>
internal void startTask(FourKitTask task, Action action)
{
task.startedRunning = true;
_taskInstanceMap[task.getTaskId()] = task;
_taskActionMap[task.getTaskId()] = action;
NativeBridge.AddScheduler?.Invoke(task.getTaskId(), task.startDelay, task.runCooldown);
}
/// <summary>
/// Updates scheduled tasks for the current server tick and runs due task callbacks.
/// </summary>
/// <param name="currentTick">The current server tick.</param>
internal void update(int currentTick)
{
if (_lastTick == -1) _lastTick = currentTick;
List<int> tasksToRemove = new List<int>();
foreach (var task in _taskInstanceMap.Values)
{
if (!task.shouldRun)
{
tasksToRemove.Add(task.getTaskId());
continue;
}
if (task.startDelay > 0)
{
task.startDelay -= (currentTick - _lastTick);
if (task.startDelay <= 0)
{
task.startDelay = 0;
_taskActionMap[task.getTaskId()]?.Invoke();
}
continue;
}
if (task.runCooldown == -1)
{
task.lastRunTick = currentTick;
_taskActionMap[task.getTaskId()]?.Invoke();
tasksToRemove.Add(task.getTaskId());
}
else
{
int lastTaskTick = task.lastRunTick;
if (lastTaskTick == -1 || (lastTaskTick + task.runCooldown) <= currentTick)
{
task.lastRunTick = currentTick;
_taskActionMap[task.getTaskId()]?.Invoke();
}
}
}
foreach (var taskId in tasksToRemove)
{
_taskInstanceMap.Remove(taskId);
_taskActionMap.Remove(taskId);
}
}
}
}

View file

@ -0,0 +1,52 @@
using Minecraft.Server.FourKit.Plugin;
using System;
using System.Collections.Generic;
using System.Text;
namespace Minecraft.Server.FourKit.Scheduler
{
public class FourKitTask
{
private ServerPlugin owner;
private int taskId;
internal bool shouldRun = true;
internal int startDelay;
internal int runCooldown;
internal int lastRunTick = -1;
internal bool startedRunning = false;
/// <summary>
/// Initializes a new task instance with owner, task id, start delay, and run cooldown.
/// </summary>
/// <param name="owner">The plugin that owns this task.</param>
/// <param name="taskId">Task id number.</param>
/// <param name="startDelay">Delay in server ticks before executing the task.</param>
/// <param name="runCooldown">Period in server ticks between runs, or -1 for one-shot tasks.</param>
internal FourKitTask(ServerPlugin owner, int taskId, int startDelay, int runCooldown)
{
this.owner = owner;
this.taskId = taskId;
this.startDelay = startDelay;
this.runCooldown = runCooldown;
}
/// <summary>
/// Will attempt to cancel this task.
/// </summary>
public void cancel() { shouldRun = false; }
/// <summary>
/// Returns the Plugin that owns this task.
/// </summary>
/// <returns>The Plugin that owns the task.</returns>
public ServerPlugin getOwner() { return owner; }
/// <summary>
/// Returns the taskId for the task.
/// </summary>
/// <returns>Task id number.</returns>
public int getTaskId() { return taskId; }
}
}

View file

@ -0,0 +1,98 @@
@page scheduler-programming Scheduler Programming
@section introduction Introduction
Usually when we code in FourKit, everything is ran linearly. Despite this you may wish to schedule some code to be executed at a later point of time.
Do NOT use the built in C# Thread system, it is not safe for FourKit.
@section getting_started Getting Started
To get started we either need the FourKitScheduler instance. You can get this from the server instance.
```csharp
FourKitScheduler scheduler = FourKit.getScheduler();
```
When scheduling a task, you will also need to pass your main plugin class instance. Here is an example of how it can be done:
```csharp
public class ExampleOne
{
// you can also use a getter
public static ExampleOne Instance { get; private set; }
public void OnEnable()
{
Instance = this;
}
}
// then
public class Other
{
private readonly ExampleOne plugin = ExampleOne.Instance;
}
```
@section scheduling_delayed_task Scheduling a Delayed Task
The scheduling itself is based on ticks, the Minecraft time unit. 1 tick = 0.05 seconds or 1 second = 20 ticks.
Let's say you want to schedule a task to run 30 seconds later which broadcasts a message:
```csharp
FourKitScheduler scheduler = FourKit.getScheduler();
scheduler.runTaskLater(plugin, () => {
FourKit.broadcastMessage("Mooooo!");
}, 20 * 30 /*<-- the delay */);
```
@section scheduling_repeating_task Scheduling a Repeating Task
Repeating tasks are tasks that can reschedule themselves.
Let's say you want to schedule a task to run 10 seconds later then after that it should repeat itself a finite amount of times with an interval of 5 seconds between each consecutive run:
```csharp
FourKitScheduler scheduler = FourKit.getScheduler();
scheduler.runTaskTimer(plugin, () => {
FourKit.broadcastMessage("Mooooo!");
}, 20 * 10 /*<-- the initial delay */, 20 * 5 /*<-- the interval */);
```
@section run_task_next_tick Running a Task on the Next Tick
Sometimes we just want to run some code on the next tick:
```csharp
FourKitScheduler scheduler = FourKit.getScheduler();
scheduler.runTask(plugin, () => {
FourKit.broadcastMessage("Mooooo again!");
});
```
@section canceling_tasks Canceling Tasks
Sometimes we want to just cancel a task!
```csharp
FourKitScheduler scheduler = FourKit.getScheduler();
// Cancel outside
FourKitTask task = scheduler.runTaskLater(plugin, () => {
FourKit.broadcastMessage("Mooooo again!");
}, 20 * 60);
// Cancel inside
scheduler.runTaskTimer(plugin, () => {
if (FourKit.getOnlinePlayers().Count == 0) {
task.cancel(); // <--
return;
}
FourKit.broadcastMessage("Mooooo again!");
}, 0, 20 * 60);
// then
task.cancel(); // <--
```

View file

@ -30,6 +30,8 @@ typedef int(__stdcall *fn_fire_player_move)(int entityId,
double fromX, double fromY, double fromZ, double fromX, double fromY, double fromZ,
double toX, double toY, double toZ, double toX, double toY, double toZ,
double *outCoords); double *outCoords);
typedef void(__stdcall* fn_set_scheduler_callbacks)(void* add, void* remove);
typedef void(__stdcall* fn_fire_scheduler_callback)(int currentTick);
typedef void(__stdcall *fn_set_native_callbacks)(void *damage, void *setHealth, void *teleport, void *setGameMode, void *broadcastMessage, void *setFallDistance, void *getPlayerSnapshot, void *sendMessage, void *setWalkSpeed, void *teleportEntity); typedef void(__stdcall *fn_set_native_callbacks)(void *damage, void *setHealth, void *teleport, void *setGameMode, void *broadcastMessage, void *setFallDistance, void *getPlayerSnapshot, void *sendMessage, void *setWalkSpeed, void *teleportEntity);
typedef void(__stdcall *fn_set_world_callbacks)(void *getTileId, void *getTileData, void *setTile, void *setTileData, void *breakBlock, void *getHighestBlockY, void *getWorldInfo, void *setWorldTime, void *setWeather, void *createExplosion, void *strikeLightning, void *setSpawnLocation, void *dropItem); typedef void(__stdcall *fn_set_world_callbacks)(void *getTileId, void *getTileData, void *setTile, void *setTileData, void *breakBlock, void *getHighestBlockY, void *getWorldInfo, void *setWorldTime, void *setWeather, void *createExplosion, void *strikeLightning, void *setSpawnLocation, void *dropItem);
typedef void(__stdcall *fn_update_entity_id)(int oldEntityId, int newEntityId); typedef void(__stdcall *fn_update_entity_id)(int oldEntityId, int newEntityId);
@ -128,6 +130,8 @@ static fn_update_entity_id s_managedUpdateEntityId = nullptr;
static fn_fire_player_quit s_managedFireQuit = nullptr; static fn_fire_player_quit s_managedFireQuit = nullptr;
static fn_fire_player_kick s_managedFireKick = nullptr; static fn_fire_player_kick s_managedFireKick = nullptr;
static fn_fire_player_move s_managedFireMove = nullptr; static fn_fire_player_move s_managedFireMove = nullptr;
static fn_set_scheduler_callbacks s_managedSetSchedulerCallbacks = nullptr;
static fn_fire_scheduler_callback s_managedFireSchedulerCallback = nullptr;
static fn_set_native_callbacks s_managedSetCallbacks = nullptr; static fn_set_native_callbacks s_managedSetCallbacks = nullptr;
static fn_set_world_callbacks s_managedSetWorldCallbacks = nullptr; static fn_set_world_callbacks s_managedSetWorldCallbacks = nullptr;
static fn_fire_structure_grow s_managedFireStructureGrow = nullptr; static fn_fire_structure_grow s_managedFireStructureGrow = nullptr;
@ -211,6 +215,8 @@ void Initialize()
{L"FirePlayerQuit", (void **)&s_managedFireQuit}, {L"FirePlayerQuit", (void **)&s_managedFireQuit},
{L"FirePlayerKick", (void **)&s_managedFireKick}, {L"FirePlayerKick", (void **)&s_managedFireKick},
{L"FirePlayerMove", (void **)&s_managedFireMove}, {L"FirePlayerMove", (void **)&s_managedFireMove},
{L"SetSchedulerCallbacks", (void**)&s_managedSetSchedulerCallbacks},
{L"FireSchedulerCallback", (void**)&s_managedFireSchedulerCallback},
{L"SetNativeCallbacks", (void **)&s_managedSetCallbacks}, {L"SetNativeCallbacks", (void **)&s_managedSetCallbacks},
{L"SetWorldCallbacks", (void **)&s_managedSetWorldCallbacks}, {L"SetWorldCallbacks", (void **)&s_managedSetWorldCallbacks},
{L"UpdatePlayerEntityId", (void **)&s_managedUpdateEntityId}, {L"UpdatePlayerEntityId", (void **)&s_managedUpdateEntityId},
@ -270,9 +276,11 @@ void Initialize()
return; return;
} }
s_initialized = true;
s_managedInit(); s_managedSetSchedulerCallbacks(
(void*)&NativeAddScheduler,
(void*)&NativeRemoveScheduler
);
s_managedSetCallbacks( s_managedSetCallbacks(
(void *)&NativeDamagePlayer, (void *)&NativeDamagePlayer,
@ -375,6 +383,10 @@ void Initialize()
(void *)&NativeGetWorldEntities, (void *)&NativeGetWorldEntities,
(void *)&NativeGetChunkEntities); (void *)&NativeGetChunkEntities);
//we should init after setting callbacks so we have access inside the plugins on enable
s_initialized = true;
s_managedInit();
LogInfo("fourkit", "FourKit initialized successfully."); LogInfo("fourkit", "FourKit initialized successfully.");
} }
@ -515,6 +527,16 @@ void UpdatePlayerEntityId(int oldEntityId, int newEntityId)
LogDebugf("fourkit", "UpdatePlayerEntityId: %d -> %d", oldEntityId, newEntityId); LogDebugf("fourkit", "UpdatePlayerEntityId: %d -> %d", oldEntityId, newEntityId);
} }
void FireSchedulerCallback(int currentTick)
{
if (!s_initialized || !s_managedFireSchedulerCallback)
{
return;
}
s_managedFireSchedulerCallback(currentTick);
}
bool FirePlayerMove(int entityId, bool FirePlayerMove(int entityId,
double fromX, double fromY, double fromZ, double fromX, double fromY, double fromZ,
double toX, double toY, double toZ, double toX, double toY, double toZ,

View file

@ -12,6 +12,7 @@ namespace FourKitBridge
bool FirePlayerQuit(int entityId); bool FirePlayerQuit(int entityId);
bool FirePlayerKick(int entityId, int disconnectReason, bool FirePlayerKick(int entityId, int disconnectReason,
std::wstring &outLeaveMessage); std::wstring &outLeaveMessage);
void FireSchedulerCallback(int currentTick);
bool FirePlayerMove(int entityId, bool FirePlayerMove(int entityId,
double fromX, double fromY, double fromZ, double fromX, double fromY, double fromZ,
double toX, double toY, double toZ, double toX, double toY, double toZ,

View file

@ -47,6 +47,7 @@
#include "Common\NetworkUtils.h" #include "Common\NetworkUtils.h"
#include "ServerLogManager.h" #include "ServerLogManager.h"
#include "../Minecraft.World/ItemInstance.cpp" #include "../Minecraft.World/ItemInstance.cpp"
#include <mutex>
namespace namespace
{ {
@ -104,12 +105,79 @@ class VirtualContainer : public SimpleContainer
return m_containerType; return m_containerType;
} }
}; };
class NativeFourKitTask;
static int64_t STATIC_lastTick = -1;
static std::unordered_map<int, std::shared_ptr<NativeFourKitTask>> _taskCache;
static std::mutex _taskMutex;
class NativeFourKitTask {
public:
int startDelay;
int runCooldown;
int lastRunTick;
NativeFourKitTask(int _startDelay, int _runCooldown) : startDelay(_startDelay), runCooldown(_runCooldown), lastRunTick(-1) {};
};
} }
namespace FourKitBridge namespace FourKitBridge
{ {
void __cdecl NativeDamagePlayer(int entityId, float amount)
void __cdecl NativeAddScheduler(int taskid, int startDelay, int runCooldown)
{
std::lock_guard<std::mutex> g(_taskMutex);
_taskCache.emplace(taskid, std::make_shared<NativeFourKitTask>(startDelay, runCooldown));
}
void __cdecl NativeRemoveScheduler(int taskid)
{
std::lock_guard<std::mutex> g(_taskMutex);
auto it = _taskCache.find(taskid);
if (it != _taskCache.end()) {
_taskCache.erase(it);
}
}
void ServerTickCallback(int currentTick)
{
bool callManagedFunction = false;
if (STATIC_lastTick == -1) {
callManagedFunction = true;
STATIC_lastTick = currentTick;
}
{
std::lock_guard<std::mutex> g(_taskMutex);
for (const auto& [taskid, task] : _taskCache)
{
NativeFourKitTask* taskPointer = task.get();
if (taskPointer->startDelay > 0) {
taskPointer->startDelay -= (currentTick - STATIC_lastTick);
if (taskPointer->startDelay <= 0) {
callManagedFunction = true; //make c# update the tasks so its not queued anymore but now its running
taskPointer->startDelay = 0; //ensure it stays 0
}
continue;
}
int lastTaskTick = taskPointer->lastRunTick;
if (lastTaskTick == -1 || (lastTaskTick + taskPointer->runCooldown) <= currentTick) {
callManagedFunction = true;
taskPointer->lastRunTick = currentTick;
}
}
}
if (callManagedFunction) FireSchedulerCallback(currentTick);
STATIC_lastTick = currentTick;
}
void __cdecl NativeDamagePlayer(int entityId, float amount)
{ {
auto player = FindPlayer(entityId); auto player = FindPlayer(entityId);
if (player) if (player)

View file

@ -3,6 +3,12 @@
namespace FourKitBridge namespace FourKitBridge
{ {
//scheduler
void __cdecl NativeAddScheduler(int taskid, int startDelay, int runCooldown);
void __cdecl NativeRemoveScheduler(int taskid);
void ServerTickCallback(int currentTick);
// core // core
void __cdecl NativeDamagePlayer(int entityId, float amount); void __cdecl NativeDamagePlayer(int entityId, float amount);
void __cdecl NativeSetPlayerHealth(int entityId, float health); void __cdecl NativeSetPlayerHealth(int entityId, float health);