Getting Started with CSharp (Server-Side)

From Onset Developer Wiki
Revision as of 17:43, 17 May 2021 by DasDarki (talk | contribs) (Created page with "This article explains how and where you should start if you are either completely new to the Onset API or if you are coming from Lua. Let's be clear: this article does not exp...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

This article explains how and where you should start if you are either completely new to the Onset API or if you are coming from Lua. Let's be clear: this article does not explain how to program with C# and a good knowledge base is also required to work with the C# API, especially related to object-oriented programming.

Preparation

Requirements

  • A DedicatedServer for testing as well as integrating the libraries for programming should be prepared
  • .NET 5 (or newer) framework installed
  • An IDE (your choice; recommended VisualStudio or JetBrains Rider) installed
  • Minimum basic knowledge, recommended advanced knowledge of C#
  • A created project for a class library for .NET 5

Add the Onset Library as Reference

Currently, installing and adding the reference is not yet supported through NUGET. Until then, the installation must be done by manually adding the DLL in the server folder. Just go into your Server-Folder and locate the dotnet folder in there. Now go into that folder and open the runtime folder in that folder. Now the path should be something like this:

~OnsetServer/dotnet/runtime

Now add the following two DLL files to your project as reference:

  • Onset.Server.dll
  • Onset.Common.dll

Ideas and Concepts

This part of the article explains some ideas and concepts that were created before the actual API was programmed. These things are important to know in order to fully understand the API and the structure of it itself. For the actual programming with the API, however, this knowledge is not required.

Everything is an Entity

Actually, the concept of "Everything is an Entity" is not a new concept and certainly not C# exclusive, but due to the abstraction of the API (see Abstraction and OOP) this is a very important concept for C#. All things in the world that are not the world itself are entities, so players, doors, vehicles, other objects, pickups, 3D texts, and NPCs. Each of these objects has its own ID - which is not the same after a server restart - and functions that change this entity are executed with the ID.

Abstraction and OOP

Onset's Lua API is very simple in design, and that's fine for beginners and in general. But when it comes to more complex projects, the Lua API and Lua in general quickly comes to its limits, at least in relation to the neatness. Because the big problem of the Lua API: Each entity function requires entity IDs. In itself, this is good and important, but how do you manage them without it becoming complete chaos. For smaller projects this is still relatively easy. For massive projects it means a lot of work. The C# API was primarily created with the concept of object-oriented programming and abstracts the Lua API around just such objects. This creates a much more natural and realistic feel when you lay hands on development with Onset. In addition, the concept of "Everything is an Entity" comes into play here again. Each entity has its own class and each ID is assigned a C# object. Managing these objects is much easier. In addition, it saves a lot of code, because the functions of the entities can be called directly by this object, and thus the ID can be omitted from these functions.

JIT-Compiling and Pre-Compiled

The Onset API has two modes built in: JIT compilation as well as loading pre-compiled DLLs. JIT compilation is always active by default, unless you tell the server to load a pre-compiled DLL. If sandbox mode is enabled in the server, JIT compilation is forced and a kind of sandbox is also loaded that prohibits certain namespaces. "Es gibt programmiertechnische jedoch keinen Unterschied zwischen JIT-Kompilierung oder pre-compiled DLLs."

JIT-Compiling

JIT (Just in Time) compilation allows you to simply mark your classes as server files and let the server do the rest. The class files are automatically compiled and loaded at the end. This would allow scripts to be loaded and shared more easily and transparently. However, loading the server takes a little longer because the files have to be compiled first, of course.

Pre-Compiled

Using pre-compiled DLLs has some advantages. The biggest advantage is that this method allows you to use external libraries from NUGET or other sources. For server owners: when using this mode, please make sure that the things you install are legitimate and harmless. You can decompile the DLLs, but of course you have to do it yourself. To use the pre-compiled mode, you just have to go into the package.json of your project and add a line to the JSON object:

"use_csharp_assemblies": true

With that you can just add DLL files to your server files list in your package.json.

Main Class and Modules

One problem and weakness that C# brings with it is that files cannot simply contain code that is executed when initialized the way scripts can. This creates the problem that it needs a class and in this class a method which can then execute code. To counteract this problem, it needs a main class that follows a certain structure. This one main class is called when initializing, starting and stopping the package and executes the code that the Lua Script just so have in it. However, in order to maintain order, the C# API offers a Module API, which loads other classes with only one line of code to it, whereby parts from the main class can be outsourced.

Create a new Package

First you have to create a main class which must extend the Onset.Server.Package class. The package class forces you to override the OnStart() method which gets called when the package is getting started. The OnStop() method can be overridden optioanlly. The method is getting called when the package stops.

using Onset.Server;

public class Main : Package
{
    public override void OnStart()
    {
        // -- Put the Start Logic here -- \\
    }

    public override void OnStop()
    {
        // The overriding of the OnStop method is optional
        // and must be done manually
        
        // -- Put the Stop Logic here -- \\
    }
}

This is the absolute minimum needed for the package to be loaded and started. Now when the package gets started, the runtime calls the OnStart method.

Events

A very important part of the API are the events. Unlike the Lua API, this one also relies on OOP. Each event has its own object. In the object are then the parameters that the event brings with it. Cancelable events like the ClientConnectionRequest event also have a property called Cancel, which can be set to true to cancel the execution of the event and the subsequent actions. Objects make it easier to deal with events by eliminating the need for you to know what parameters the event brings with it. You only need to know the name of the object. You can use the auto-completion of your IDE for this. All events are located in the namespace Onset.Server.Events.*. In addition, the class names use the names of the events in Lua, but without the "On" and with an "Event" after the name. To listen to an event you just need to declare a method, the first and only parameter must be the wanted event and you need to mark the event as EventListener by adding the attribute.

[EventListener]
public void OnClientRequest(ClientConnectionRequestEvent e) 
{
    // -- Put Event Logic here -- \\
}

The main class as well as the module classes are automatically registered for event listening. If you use another class you need to register it manually with the following methods (these methods must be executed with an instance of Package because only there the EventManager is accessible):

// Registers all non-static methods in the class of the given object
EventManager.Register(new SomeEvents());
// Registers all static methods in the given class
EventManager.Register<SomeEvents>();