-
-
Notifications
You must be signed in to change notification settings - Fork 30
Piping
Piping is a system provided by ServerMod to interact with other plugins without requiring dependencies. This allows for smoother integration by resolving plugin conflictions, as well as extended functionality by hooking to plugin events, calling your own events, or calling a specific plugin's methods.
All but events are calculated and set after the plugin's Register()
method. From there, all a plugin has to do is use it like a normal variable, property, or method with a small amount of wrapping.
Events are currently calculated during run-time and exceptions thrown by event hooks will be caught and reported. Be careful about this, as this means that passing invalid parameters can cause exceptions which look like other plugins' faults.
All exports
- do not have to be public.
All imports
- have types that are backwards names of the exports (e.g.
PipeField
export: swappingPipe
andField
makesFieldPipe
import). - have a generic type for convenience purposes. These generics also support implicit casting, so in some cases the generics can be used just like their original type.
- do not care about if the field is readonly (the field that the
PipeLink
marks).
Good practices include:
- keeping same name as the pipe export.
- using objects that do not require any additional dependencies (the whole point of pipes is to make plugins less dependent but keep integration support).
Allows for other plugins to get/set a field marked by this attribute. Settability is determined by whether or not the field is readonly, however it can be overridden in the constructor.
// Gettable and settable fields.
[PipeField]
public float scp049InfectMultiplier;
[PipeField(false)]
public bool scp079CanOpen106;
// Gettable but not settable fields.
[PipeField]
public readonly float scp650BloodSodiumMultiplier;
[PipeField(true)]
public Role exitTeleportsToScp106;
[PipeLink("plugin.id", "scp049InfectMultiplier")]
private readonly PipeField<float> scp049InfectMultiplier;
[PipeLink("plugin.id", "scp650BloodSodiumMultiplier")]
private readonly PipeField scp650BloodSodiumMultiplier;
...
public override void OnEnable()
{
// All of the pipe imports are from the same plugin, so if one pipe is null, all are.
if (scp049InfectMultiplier != null)
{
// Because the field is a generic type, implicit casting can be used. Be careful when using reference types with implicit casting, as a != null would check the value of the pipe and not the pipe itself.
if (scp049InfectMultiplier < 2f)
{
scp049InfectMultiplier.Value = 2f;
}
// A .Value and a cast must be used because it is not a generic type.
Info("The value of " + nameof(scp650BloodSodiumMultiplier) + " is: " + (float) scp650BloodSodiumMultiplier.Value);
// If this check was not here, an exception would be thrown from trying to set a readonly field.
if (!scp650BloodSodiumMultiplier.Readonly)
{
scp650BloodSodiumMultiplier.Value = 1000f;
}
}
}
Allows for other plugins to get/set a property marked by this attribute. Gettability/settability is determined by whether or not a getter/setter is public, however it can be overridden in the constructor.
// Gettable and settable properties.
[PipeProperty]
public float Scp1730Duration { get; set; }
[PipeProperty(true, true)]
public bool Scp173IsOverrated { get; set; };
// Gettable but not settable properties.
[PipeProperty]
public float Scp939DefaultSpeed { get; private set; }
[PipeProperty(true, false)] // You should only use these if you the export is a non-public member.
public Role GodRole { get; set; }
// Settable but not gettable properties.
[PipeProperty]
public int DidntReadTheSmodWiki { private get; set; }
[PipeProperty(true, false)] // You should only use these if you the export is a non-public member.
public string DisplayText { get; set; }
[PipeLink("plugin.id", "Scp1730Duration")]
private readonly PipeProperty<float> Scp1730Duration;
[PipeLink("plugin.id", "Scp939DefaultSpeed")]
private readonly PipeProperty Scp939DefaultSpeed;
[PipeLink("plugin.id", "DidntReadTheSmodWiki")]
private readonly PipeProperty<int> DidntReadTheSmodWiki;
...
public override void OnEnable()
{
// All of the pipe imports are from the same plugin, so if one pipe is null, all are.
if (Scp1730Duration != null)
{
Scp1730Duration.Value *= 2f;
// Again, .Value and a cast must be used because it is not a generic type.
Info("The value of " + nameof(Scp939DefaultSpeed) + " is: " + (float) Scp939DefaultSpeed.Value);
// If this check was not here, an exception would be thrown from trying to set a get-only field.
if (!Scp939DefaultSpeed.Settable)
{
Scp939DefaultSpeed.Value = 1000f; // Nyoom
}
while (true)
{
DidntReadTheSmodWiki.Value++;
if (DidntReadTheSmodWiki.Gettable)
{
Info(DidntReadTheSmodWiki + " people haven't read the ServerMod wiki.")
}
}
}
}
Allows for other plugins to call a method marked by this attribute. There is no parameter type restrictions during compile time, so exceptions may at run-time occur due to invalid parameter count or type.
[PipeMethod]
public float CalculatePlayerNuisanceFactor(Player player)
{
float factor;
switch (player.TeamRole.Role)
{
case Role.FACILITY_GUARD:
factor = 2f;
break;
case Role.SCIENTIST:
factor = 1.5f;
break;
default:
factor = 1f;
break;
}
switch (player.GetHeldItem().ItemType)
{
case ItemType.COM15:
case ItemType.USP:
case ItemType.MP7:
case ItemType.P90:
case ItemType.E11_RIFLE:
case ItemType.LOGICER:
factor *= 2f;
break;
case ItemType.FLASHBANG:
factor *= 4f;
break;
}
return factor;
}
[PipeLink("plugin.id", "CalculatePlayerNuisanceFactor")]
private readonly MethodPipe<float> CalculatePlayerNuisanceFactor;
...
public override void OnEnable()
{
// If the plugin (and therefore the export pipe) exists.
if (CalculatePlayerNuisanceFactor != null)
{
Player worst = Server.GetPlayers()
.OrderByDescending(x => CalculatePlayerNuisanceFactor.Invoke(x))
.FirstOrDefault();
if (worst != null)
{
Info("Greatest nuisance: " + worst.Name);
}
}
}
Allows for other plugins to batch-call methods marked by the attribute and labeled with the proper event name.
The return value of an event is not used. If you need a value, pass a 1 element array that other plugins can set.
It's good practice to keep methods private
in order to discourage other plugins from invoking whitelisted events or your plugin event specifically.
[PipeEvent("courtney.example.plugin.Echo", // The event ID. The event ID consists of: plugin ID + "." + event name
"courtney.example.plugin" // More plugin IDs can be added here to whitelist plugins to run specific events.
)]
private void OnEcho(string text) => Info("Example plugin says: " + text);
[PipeEvent("plugin.id.CacheCheck")]
private void OnCacheCheck(string[] hashedIds, bool[] downloadFull)
{
if (downloadFull[0]) return;
foreach (string hash in File.ReadAllLines("customhashes.txt"))
{
if (!hashedIds.Contains(hash))
{
downloadFull[0] = true;
break;
}
}
}
public void CheckCache()
{
byte[] data = File.ReadAllBytes("hashes.txt");
string allText = Encoding.UTF8.GetString(data);
string[] hashes = string.Split('\n');
// Single length array allows for get/set behavior on a parameter, much like a ref would. However, in, out, and ref is not supported.
bool[] result = new[] {data.Length == ValidHashFileSize};
InvokeEvent("CacheCheck", hashes, result);
if (result[0])
{
hashes = DownloadAllHashes();
}
Hashes = hashes;
}
- Installation
- Config
- Server Name Variables
- Commands
-
- Admin Events
- Enviroment Events
-
Player Events
- OnPlayerHurt
- OnPlayerDie
- OnPlayerPickupItem
- OnPlayerDropItem
- OnPlayerJoin
- OnNicknameSet
- OnAssignTeam
- OnSetRole
- OnCheckEscape
- OnSpawn
- OnDoorAccess
- OnIntercom
- OnIntercomCooldownCheck
- OnPocketDimensionExit
- OnPocketDimensionEnter
- OnPocketDimensionDie
- OnThrowGrenade
- OnPlayerInfected
- OnSpawnRagdoll
- OnLure
- OnContain106
- OnMedkitUse
- OnShoot
- On106CreatePortal
- On106Teleport
- OnElevatorUse
- OnHandcuff
- OnGeneratorUnlock
- OnGeneratorAccess
- OnGeneratorInsertTablet
- OnGeneratorEjectTablet
- On079Door
- On079Lock
- On079Elevator
- On079TeslaGate
- On079AddExp
- On079LevelUp
- On079UnlockDoors
- On079CameraTeleport
- On079ElevatorTeleport
- On079StartSpeaker
- On079StopSpeaker
- On079Lockdown
- Server Round Events
- Team Events