外观
适用范围
本文记录的是社区服务端项目接口,不是 Candy Rufus Games 官方原版客户端的公共 API。代码需要在对应源码分支的 SERVER 构建中使用;接口名称和行为可能随社区项目版本变化。
本文档以当前源码为准,整理服务端插件开发时常用的生命周期、加载规则、命令接口、事件接口与注册方式。所有服务端插件代码需要在 SERVER 构建中使用,常见命名空间包括:
csharp
using Game.Server;
using Game.Server.Event;
using Game.Server.PlayerEvent;
using Game.Server.Terminal;
using Engine;插件加载规则
插件基类为 Game.Server.ServerPlugin。
csharp
public abstract class ServerPlugin
{
public abstract int Version { get; }
public abstract string Name { get; }
public abstract void Initialize();
public abstract void Load();
public abstract void Save();
public virtual void Update(float dt) { }
}- 外部插件 DLL 放在服务端插件目录
Plugins下,由ServerManager.LoadPluginsDll()扫描。 - DLL 中所有继承
ServerPlugin且非抽象的类会被实例化。 - DLL 中所有继承
AbstractProcessCmd且非抽象的命令类会自动注册到CmdManager。 - 内置插件和外部插件都会执行
Initialize();世界加载后执行Load(),保存时执行Save(),服务端每帧执行Update(dt)。 - 事件订阅建议放在
Initialize(),避免Load()多次执行导致重复订阅。 - 插件配置建议存到
Storage.GetSystemPath("app:/Configs")或当前世界目录,避免写入程序目录失败。
最小插件示例
csharp
using Game.Server;
namespace MyPlugin;
public class HelloPlugin : ServerPlugin
{
public override int Version => 10000; // 1.0.0
public override string Name => "示例插件";
public override void Initialize()
{
}
public override void Load()
{
}
public override void Save()
{
}
public override void Update(float dt)
{
}
}事件注册约定
大多数事件都有对应的 XxxEventManager.AddObject(this) 和 RemoveObject(this):
csharp
public override void Initialize()
{
MessageEventManager.AddObject(this);
PlayerEnterGameEventManager.AddObject(this);
}返回值约定:
- 多数
bool返回值表示是否允许原行为继续执行。 - 返回
false通常表示拦截、取消或禁止该行为。 - 管理器一般会遍历所有处理器,只要任意处理器返回
false,最终结果就是false。 FirstLevel多数接口暂未完整实现排序,建议默认返回0。
消息与告示牌接口
IMessageEventHandle
命名空间:Game.Server.Event
该接口保持旧插件兼容,告示牌相关插件应继续实现此接口。
csharp
public interface IMessageEventHandle
{
byte FirstLevel { get; }
void ReceiveMessage(
string playerName,
NetNode netNode,
Client From,
string message,
byte messageType,
out bool External);
bool EditSignMessage(Point3 point, ComponentPlayer componentPlayer);
}说明:
ReceiveMessage在服务端收到聊天消息时触发。External用于标记外部消息显示样式;旧插件可继续使用。EditSignMessage在玩家请求编辑告示牌时触发,返回false可阻止打开编辑界面。- 当前版本为了兼容旧插件,
IMessageEventHandle不再使用ref string message,也不再把告示牌接口改成void。
示例:
csharp
public class SignGuardPlugin : ServerPlugin, IMessageEventHandle
{
public override int Version => 10000;
public override string Name => "告示牌保护";
public byte FirstLevel => 0;
public override void Initialize()
{
MessageEventManager.AddObject(this);
}
public bool EditSignMessage(Point3 point, ComponentPlayer componentPlayer)
{
return componentPlayer != null;
}
public void ReceiveMessage(string playerName, NetNode netNode, Client From, string message, byte messageType, out bool External)
{
External = false;
}
public override void Load() { }
public override void Save() { }
}IMutableMessageEventHandle
命名空间:Game.Server.Event
新插件如果需要修改玩家聊天内容,可以额外实现该扩展接口。它不会替代 IMessageEventHandle。
csharp
public interface IMutableMessageEventHandle
{
void ReceiveMessage(
string playerName,
NetNode netNode,
Client From,
ref string message,
byte messageType);
}示例:
csharp
public class WordReplacePlugin : ServerPlugin, IMessageEventHandle, IMutableMessageEventHandle
{
public override int Version => 10000;
public override string Name => "聊天替换";
public byte FirstLevel => 0;
public override void Initialize()
{
MessageEventManager.AddObject(this);
}
public void ReceiveMessage(string playerName, NetNode netNode, Client From, string message, byte messageType, out bool External)
{
External = false;
}
public void ReceiveMessage(string playerName, NetNode netNode, Client From, ref string message, byte messageType)
{
message = message.Replace("bad", "***");
}
public bool EditSignMessage(Point3 point, ComponentPlayer componentPlayer) => true;
public override void Load() { }
public override void Save() { }
}玩家连接与验证接口
IBanEventHandle
命名空间:Game.Server
csharp
public interface IBanEventHandle
{
byte FirstLevel { get; }
bool IsBan(
string nickname,
string id,
string password,
string ip,
NetNode netNode,
Client client,
out bool UseExternalPassword);
bool IsBanIp(string ip, NetNode netNode, ConnectionRequest request);
}IsBan返回true表示拒绝玩家进入。UseExternalPassword可告知后续密码验证走外部逻辑。IsBanIp返回true表示按 IP 拒绝连接。
注册:
csharp
BanEventManager.AddObject(this);IPasswordValidationEventHandle
命名空间:Game.Server.Event
csharp
public interface IPasswordValidationEventHandle
{
byte FirstLevel { get; }
bool ValidatePassword(
string inputPassword,
string serverPassword,
bool useExternalPassword,
string nickname,
string communityAccountId,
string ipAddress,
NetNode netNode,
Client client,
out string errorMessage);
}- 用于自定义密码验证。
- 返回值表示验证是否通过。
errorMessage会作为失败提示。
注册:
csharp
PasswordValidationEventManager.AddObject(this);玩家生命周期接口
IPlayerEnterGameEventHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerEnterGameEventHandle
{
byte FirstLevel { get; }
void PlayerEnter(PlayerData playerData);
void PlayerLeave(PlayerData playerData);
}PlayerEnter在玩家进入服务器后触发。PlayerLeave在玩家离开服务器前触发。
注册:
csharp
PlayerEnterGameEventManager.AddObject(this);IPlayerIntoPlayingHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerIntoPlayingHandle
{
byte FirstLevel { get; }
void PlayerIntoPlayingEvent(ComponentPlayer componentPlayer);
}- 玩家实体进入正式 Playing 状态时触发。
注册:
csharp
PlayerIntoPlayingEventManager.AddObject(this);玩家行为接口
IPlayerBreakAndPlaceHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerBreakAndPlaceHandle
{
byte FirstLevel { get; }
bool PlayerPlaceEvent(ComponentPlayer componentPlayer, Point3 point, int placeBlockValue);
bool PlayerBreakEvent(ComponentPlayer componentPlayer, Point3 point, int digBlockValue, int toolLevel);
}- 返回
false可阻止放置或破坏方块。
注册:
csharp
PlayerBreakAndPlaceBlockEventManager.AddObject(this);IPlayerInteractEventHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerInteractEventHandle
{
byte FirstLevel { get; }
bool Interact(ComponentPlayer componentPlayer, CellFace cellFace);
bool Use(ComponentPlayer componentPlayer, object raycast, int activeBlockValue);
bool Hit(ComponentPlayer componentPlayer, ComponentBody componentBody, Vector3 hitPoint, Vector3 hitDirection);
bool Aim(ComponentPlayer componentPlayer, int activeBlockValue, Ray3 aim, AimState state);
}- 与方块交互、使用物品、击打、瞄准相关。
- 返回
false可拦截对应行为。
注册:
csharp
PlayerInteractEventManager.AddObject(this);IPlayerNetInteractEventHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerNetInteractEventHandle
{
byte FirstLevel { get; }
bool PlayerNetInteractEvent(
ComponentPlayer player,
ComponentPlayer.InteractEvent m_interactEvent,
Ray3 m_netInteractRay,
TerrainRaycastResult? m_netInteractRaycast);
}- 在接收到玩家网络交互包时触发,比部分本地交互接口更靠近网络层。
- 返回
false可阻止该网络交互。
注册:
csharp
PlayerNetInteractEventManager.AddObject(this);IPlayerMoveHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerMoveHandle
{
byte FirstLevel { get; }
bool PlayerMoveEvent(ComponentPlayer componentPlayer, Vector3 position);
}- 玩家移动时触发。
- 返回
false可阻止移动。
注册:
csharp
PlayerMoveEventManager.AddObject(this);IPlayerPositionSetHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerPositionSetHandle
{
byte FirstLevel { get; }
bool OnPlayerPositionSet(ComponentPlayer componentPlayer);
}- 玩家位置被强制设置时触发。
- 处理器会按
FirstLevel从小到大排序。 - 返回
false可阻止位置设置。
注册:
csharp
PlayerPositionSetEventManager.AddObject(this);背包与容器接口
IPlayerInventoryHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerInventoryHandle
{
byte FirstLevel { get; }
bool PlayerDrop(ComponentPlayer componentPlayer);
bool PlayerDrapDrop(ComponentPlayer componentPlayer, Vector3 worldPos, InventoryDragData inventoryDragData, int count);
bool PlayerHandleMoveItem(ComponentPlayer componentPlayer, IInventory sourceInventory, int sourceSlotIndex, IInventory targetInventory, int targetSlotIndex, int count);
bool PlayerHandleDragDrop(ComponentPlayer componentPlayer, IInventory sourceInventory, int sourceSlotIndex, DragMode dragMode, IInventory targetInventory, int targetSlotIndex, bool processingOnly);
}- 拦截玩家丢弃、拖拽丢弃、移动物品、拖拽物品。
- 返回
false可阻止对应背包操作。
注册:
csharp
PlayerInventoryEventManager.AddObject(this);IPlayerInventoryOpenHandle
命名空间:Game.Server.PlayerEvent
csharp
public interface IPlayerInventoryOpenHandle
{
byte FirstLevel { get; }
bool PlayerOpenInventoryEvent(PlayerData playerData, IInventory inventory);
bool PlayerOpenPointInventoryEvent(PlayerData playerData, Point3 point, IInventory inventory);
}- 打开容器界面时触发。
PlayerOpenPointInventoryEvent会额外提供容器位置。- 返回
false可阻止打开。
注册:
csharp
PlayerInventoryOpenEventManager.AddObject(this);方块、地形与环境接口
IBlockChangeEventHandle
命名空间:Game.Server.Event
csharp
public interface IBlockChangeEventHandle
{
byte FirstLevel { get; }
bool ChangeCell(int x, int y, int z, int oldValue, int newValue, ComponentMiner componentMiner);
void OnTerrainContentsGenerated(TerrainUpdater terrainUpdater, TerrainChunk chunk);
}ChangeCell在方块值变化时触发,返回false可阻止变化。OnTerrainContentsGenerated在地形内容生成后触发,可用于调整区块内容。
注册:
csharp
BlockChangeEventManager.AddObject(this);IExplodeEventHandle
命名空间:Game.Server.Event
csharp
public interface IExplodeEventHandle
{
byte FirstLevel { get; }
void Explode(int x, int y, int z, ref float pressure, bool isIncendiary, bool noExplosionSound, PlayerData miner);
}- 爆炸时触发。
- 可修改
pressure,设置为0通常可取消爆炸破坏效果。
注册:
csharp
ExplodeEventManager.AddObject(this);IFireEventHandle
命名空间:Game.Server.Event
csharp
public interface IFireEventHandle
{
byte FirstLevel { get; }
bool Fire(Ray3 ray, ComponentMiner componentMiner);
bool FireTerrain(CellFace cellFace, ComponentMiner componentMiner);
bool OnFireGeneration(int x, int y, int z, ref float spreadability);
}Fire:火柴向实体等目标点火时触发。FireTerrain:火柴向地形点火时触发。OnFireGeneration:火方块/火实体生成时触发,可修改spreadability。- 返回
false可阻止对应点火或火焰生成。
注册:
csharp
FireEventManager.AddObject(this);生物接口
ICreatureHealthEventHandle
命名空间:Game.Server.Event
csharp
public interface ICreatureHealthEventHandle
{
byte FirstLevel { get; }
bool Heal(ComponentHealth componentHealth, float amount);
bool Injure(ComponentHealth componentHealth, float amount, ComponentCreature attacker, string cause);
}- 生物治疗或受伤时触发。
- 返回
false可阻止治疗或伤害。
注册:
csharp
CreatureHealthEventManager.AddObject(this);ICreatureSpawnEventHandle
命名空间:Game.Server.Event
csharp
public interface ICreatureSpawnEventHandle
{
byte FirstLevel { get; }
bool Update(SubsystemCreatureSpawn subsystemCreatureSpawn, float dt);
void OnEntityAdded(SubsystemCreatureSpawn subsystemCreatureSpawn, Entity entity);
void OnEntityRemoved(SubsystemCreatureSpawn subsystemCreatureSpawn, Entity entity);
void OnPlayerSpawned(PlayerData playerData, Entity playerEntity, Vector3 position);
void InitCreatureTypes(SubsystemCreatureSpawn subsystemCreatureSpawn, List<SubsystemCreatureSpawn.CreatureType> creatureTypes);
}- 可参与生物刷新、实体添加移除、玩家出生、生物类型初始化。
Update返回false可阻止原刷新更新逻辑继续执行。
注册:
csharp
CreatureSpawnEventManager.AddObject(this);服务器命令接口
命令类继承 Game.Server.AbstractProcessCmd。外部 DLL 中非抽象命令类会自动注册。
csharp
public abstract class AbstractProcessCmd
{
public abstract string Cmd { get; }
public abstract string Introduce { get; }
public abstract int AuthLevel { get; }
public abstract DisplayType Display { get; }
public abstract void ProcessCmd();
public virtual IEnumerable<string> GetSuggestions(string[] args);
}DisplayType:
All:所有人都可以在帮助中看到。Authority:权限达到后可见。NoDisplay:不在帮助中显示。
命令示例:
csharp
public class CmdHello : AbstractProcessCmd
{
public override string Cmd => "hello";
public override string Introduce => "/hello - 测试命令";
public override int AuthLevel => 0;
public override DisplayType Display => DisplayType.All;
public override void ProcessCmd()
{
SendMessage("Hello", "插件命令执行成功");
}
}终端提示符与状态栏接口
这些接口用于增强服务端终端显示,只有增强终端模式支持;普通终端下会使用 NullPromptManager / NullStatusManager 降级。
IPromptProvider
命名空间:Game.Server.Terminal
csharp
public interface IPromptProvider
{
int Priority { get; }
IEnumerable<ConsolePart> GetPromptParts(TerminalContextForPart ctx);
float RefreshInterval => 10f;
}注册:
csharp
TerminalManager.RegisterPrompt(new MyPromptProvider());IStatusProvider
命名空间:Game.Server.Terminal
csharp
public interface IStatusProvider
{
int Priority { get; }
IEnumerable<ConsolePart> GetStatusParts(TerminalContextForPart ctx);
float RefreshInterval => 10f;
bool IsFullLine => false;
}注册:
csharp
TerminalManager.RegisterStatus(new MyStatusProvider());插件工程建议
外部插件项目通常引用:
xml
<ProjectReference Include="..\..\EngineServer\EngineServer.csproj" />
<ProjectReference Include="..\..\EntitySystemServer\EntitySystemServer.csproj" />
<ProjectReference Include="..\..\SurvivalcraftServer\SurvivalcraftServer.csproj" />建议:
- 插件 DLL 放到服务端
Plugins目录。 - 插件类和命令类都必须是
public且非抽象类。 - 订阅事件放在
Initialize()。 - 配置读取放在
Load(),保存放在Save()。 Update(dt)中避免阻塞 IO、网络请求和长耗时计算。- 需要世界路径时,可参考箱子锁插件按当前世界目录保存配置。
- 不要在日志中打印敏感 token、密码、玩家隐私信息。
当前兼容性说明
IMessageEventHandle已恢复旧签名,旧告示牌/聊天插件优先兼容。- 新的聊天内容修改能力通过
IMutableMessageEventHandle扩展实现。 - 若插件提示找不到接口,优先检查引用的服务端程序集版本,以及是否添加正确命名空间:
csharp
using Game.Server.Event;
using Game.Server.PlayerEvent;