Skip to content

DevGuide

Adam Szerszenowicz edited this page May 31, 2025 · 8 revisions

Developer guide

This part of documentation covers common usage scenarios for extension developers, conventions for interface implementations and inner SLCS mechanisms.

⛔ Permissions resolver

Permissions resolver is used by SLCS plugin to check player permissions when needed. The scripts loader is responsible for initialization and utilization of permissions resolver.

🗳️ Built-in implementations

SLCommandScript.Core.Permissions namespace provides built-in implementations:

🎮 VanillaPermissionsResolver

Uses in-game permissions system for permissions validation. This is the implementation the plugin always defaults to, when no other implementation is provided.

🔌 PluginPermissionsResolver

introduced in version 2.0.0

Uses Lab API permission providers system for permissions validation.

⚙️ Custom implementations

Custom permissions resolver can be made by creating a class implementing SLCommandScript.Core.Permissions.IPermissionsResolver interface. Guidelines for members implementation are located below:

public bool CheckPermission(ICommandSender? sender, string? permission, out string? message)

Performs the permissions check operation. Case-insensitivity is a recommended approach for better consistency with other language elements.

Params

  • sender The command sender whose permissions are checked.
  • permission The name of permission to check.
  • message Is used to provide an error message if something unexpected happened. Should be set to null when no issues occurred, other values cause tokenization error regardless of the result.

Return

Should return true when command sender passed permission check and false otherwise.

🔎 Scripts loader

Scripts loader is responsible for managing scripts at runtime and the used scripting language pipeline. Plugin configuration allows you to choose which scripts loader should be loaded with the plugin. The default provided implementation works on server's local file system exactly as described in this documentation and applies standard unmodified SLCS language pipeline. Using a custom scripts loader implementation can change the way how scripts are managed on the server and how SLCS interpreter behaves. If you want to you can use a custom scripts loader to replace SLCS with a completely different scripting language.

🗳️ Built-in implementations

There are no built-in scripts loader implementations. The default version is provided as a separate dependency which can be safely removed when other implementation is provided.

⚙️ Custom implementations

Custom scripts loader can be made by creating a class implementing SLCommandScript.Core.IScriptsLoader interface. Guidelines for members implementation are located below:

Tip

SLCommandScript.Core.Commands.CommandsUtils and SLCommandScript.Core.Reflection.CustomTypesUtils static classes provide functions which can be handy when making new scripts loader implementation.

public string LoaderName { get; }

Provides the loader's displayable name.

public string LoaderVersion { get; }

Provides the loader's displayable version number.

public string LoaderAuthor { get; }

Provides the loader's author information.

public void InitScriptsLoader(Plugin? plugin, ScriptsLoaderConfig? loaderConfig)

introduced in version 2.0.0

Use this method to initialize scripts loading and language pipeline.

Params

  • plugin Reference to SLCS plugin object. Can be used to retrieve plugin file paths and other additional plugin details.
  • loaderConfig Stores currently loaded configuration. The behavior of the loader should be adjusted accordibgly to values contained inside.
public void Dispose()

Should cleanup resources and disable all language pipeline and scripts features. Consider utilizing proper Dispose Pattern when implementing this method.

🗝️ Legacy implementations

public void InitScriptsLoader(object? plugin, PluginHandler? handler, ScriptsLoaderConfig? loaderConfig)

used before version 2.0.0

Use this method to initialize scripts loading and language pipeline.

Params

  • plugin Reference to SLCS plugin object. Can be used to register event handlers.
  • handler Reference to SLCS plugin handler. Can be used to retrieve plugin file paths and other additional plugin details.
  • loaderConfig Stores currently loaded configuration. The behavior of the loader should be adjusted accordibgly to values contained inside.

🔁 Iterable objects

Iterable objects are used by foreach and forrandom loop directives. They provide the elements to iterate over and optionally they can also provide addtional data that loop's inner expression can use via variables. The plugin provides a variety of iterable objects by default but it also has support for custom iterable objects.

➡️ Providers

Iterable object provider is a function that takes no arguments and returns a valid iterable object instance. They are stored in a globally accessible dictionary with case insensitive keys within SLCommandScript.Core.Iterables.IterablesUtils static class. You can add new iterable object provider or replace an existing one by setting a dictionary entry with appropriate function.

using SLCommandScript.Core.Iterables;

IterablesUtils.Providers["<your_iterable_name"] = () => new EnumIterable<MyEnum>(false);

You can also remove an iterable object provider by removing the corresponding entry from the dictionary.

using SLCommandScript.Core.Iterables;

IterablesUtils.Providers.Remove("<your_iterable_name");

Your custom provider can return any object implementing SLCommandScript.Core.Iterables.IIterable interface. You can either create your own custom implementation or use one of available built-in implementations.

Warning

Setting an iterable object provider to null or to a function that returns null will result in a script parsing error when a script tries to use it.

🗳️ Built-in implementations

SLCommandScript.Core.Iterables namespace provides built-in implementations:

🪹 EmptyIterable

Special iterable object used to represent collections with no elements. It's recommended to use EmptyIterable.Instance to retrieve an instance instead of creating new objects.

📄 EnumIterable

A generic interface implementation that can accept any enum type and iterate over its values. Can be configured to ignore None values if needed.

⛓️ ListIterable

Most common interface implementation that can iterate over any System.Collections.IEnumerable<T> object. Requires a mapper function to retrieve variables from iterated objects.

1️⃣ SingleItemIterable

Simulates an iterable object with a single element. Requires a mapper function to retrieve variables from iterated object.

⚙️ Custom implementations

Custom iterable object can be made by creating a class implementing SLCommandScript.Core.Iterables.IIterable interface. Guidelines for members implementation are located below:

Tip

SLCommandScript.Core.Iterables.IterablesUtils static class provides functions which can be handy when making new iterable object implementation.

public bool IsAtEnd { get; }

Tells whether or not the end of the collection/iteration was reached.

public int Count { get; }

Returns amount of contained elements.

public bool LoadNext(IDictionary<string, string?>? targetVars);

Is responsible for performing next iteration step and loading new property values into provided dictionary.

Params

  • targetVars Dictionary to insert properties into.

Return

Should return true when iteration can be continued and false otherwise.

public void Randomize();

Should randomize contained elements.

public void Randomize(int amount);

Should randomize contained elements and limit their amount.

Params

  • amount Amount of random elements to select from iterable object, negative values disable the limit, zero disables randomization.
public void Randomize(float amount);

Should randomize contained elements and limit their amount.

Params

  • amount Percentage of random elements to select from iterable object, negative values disable the limit, zero disables randomization.
public void Reset();

Should reset the iteration process.

🛤️ Default language pipeline

Every script is processed by few components. The steps described below are repeated for every script expression until the end of the script is reached or an error occurs.

flowchart LR
loader[Scripts Loader]-- Script + Arguments -->Lexer
Lexer-- Tokens -->Parser
Parser-- Exression -->Interpreter
Loading
  1. An instance of SLCommandScript.Core.Language.Lexer is processing received script text into a list of tokens. An inner lexer instance is used for injection of script arguments values into produced tokens. Certain guards which can prevent expression execution are also evaluated during this stage.
  2. SLCommandScript.Core.Language.Parser parses received tokens list into a valid expression. This component is responsible for majority of script validation and most of the parsing errors are originating from this step.
  3. SLCommandScript.Core.Language.Interpreter takes the parsed expression and executes it accordingly.