Modding:Moddable Mod: Difference between revisions

From Vintage Story Wiki
(Created page with "This article requires you a setup development environment. If you don't have one, read the tutorial at 'Setting up your Development Environment'. For brevity, this article pre...")
 
m (expanded draft)
Line 1: Line 1:
- THIS IS CURRENTLY A DRAFT -
This article requires you a setup development environment. If you don't have one, read the tutorial
This article requires you a setup development environment. If you don't have one, read the tutorial
at 'Setting up your Development Environment'. For brevity, this article presupposes some familiarity with
at 'Setting up your Development Environment'. For brevity, this article presupposes some familiarity with
VintageStory code modding and how to package a mod (https://wiki.vintagestory.at/index.php?title=Modding:Mod_Packaging).  
VintageStory code modding and how to package a mod (https://wiki.vintagestory.at/index.php?title=Modding:Mod_Packaging).  
If you are looking to create your first mod, start here =Link to first code mod sample=.
If you are looking to create your first mod, start here =Link to first code mod sample=.
[Contents]


== Introduction ==
== Introduction ==
Line 24: Line 26:
Note that, excluding the use of refleciton, which is outside the scope of this article,
Note that, excluding the use of refleciton, which is outside the scope of this article,
<b>only compiled mods will be able to interact with eachother</b>. If you do not compile the code of your mods
<b>only compiled mods will be able to interact with eachother</b>. If you do not compile the code of your mods
into .dll files, they will not be able to resolve eachother at runtime.
into .dll files, they will not be able to resolve references to eachother at runtime.


== InfoMod & SpawnMod ==
== TipMod & SpawnMod ==


The two mods we will be creating are named "InfoMod" and "SpawnMod".
The two mods we will be creating are named "TipMod" and "SpawnMod".


"InfoMod" will store descriptions of other mods. It will provide a function for mods to add their descriptions through,
"TipMod" will store useful gameplay tips and periodically print them in chat. It will also provide a method which other mods
and will also register a command that prints the stored descriptions in chat.
can use to add their own tips.


"SpawnMod" will register a command which teleports the player to his set or default spawnpoint. It will also
"SpawnMod" will register a command which teleports the player to his set or default spawnpoint. It will also
access "InfoMod" and add it's description.
access "TipMod" and add it's own tip describing the command.


== Preperation ==
== Preperation ==
- THIS IS CURRENTLY A DRAFT -
We start by setting up two empty mods, "TipMod" and "SpawnMod".
We start by setting up two empty mods, "InfoMod" and "SpawnMod".
In each mod, we add a new *.cs source file - we will be naming these files TipMod.cs and SpawnMod.cs respectively.
In each mod, we add a new *.cs source file - we will be naming these files InfoMod.cs and SpawnMod.cs respectively.
In our .cs files, we add the necessary using statements, create classes named after our mods and have them inherit ModSystem,
In our .cs files, we add the necessary using statements, create classes named after our mods and have them inherit ModSystem,
the base for any VintageStory code mod. In both our mods we override the StartServerSide method to access the API.
the base for any VintageStory code mod. In both our mods we override the StartServerSide method to access the API. We also make TipMod public, so it's accessible by other mods.


- img of folders -
- img of folders -


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
// Inside InfoMod.cs
// In TipMod.cs
using System;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
Line 55: Line 56:
using Vintagestory.API.Server;
using Vintagestory.API.Server;


namespace infomod.src
namespace tipmod.src
{
{
     class InfoMod : ModSystem
     public class TipMod : ModSystem
     {
     {
         public override void StartServerSide(ICoreServerAPI api)
         public override void StartServerSide(ICoreServerAPI api)
Line 68: Line 69:


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
// Inside SpawnMod.cs
// In SpawnMod.cs
using System;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
Line 88: Line 89:
}
}
</syntaxhighlight>
</syntaxhighlight>
This concludes setting up the basis of our mods. Lastly, in InfoMod, we create an additional file called ModDescription.cs,  
This concludes setting up the basis of our mods. Lastly, in TipMod, we create an additional file called Tip.cs,  
where we will define the data structure for storing mod descriptions.
where we will define the data structure for tips.


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
// Inside ModDescription.cs
// In Tip.cs
using System;
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Text;
using System.Threading.Tasks;
using System.Threading.Tasks;


namespace infomod.src
namespace tipmod.src
{
{
     class ModDescription
     public class Tip
     {
     {
         public ModDescription(string name, string author, string description)
        string author;
        string tip;
 
         public Tip(string author, string tip)
         {
         {
            this.name = name;
             this.author = author;
             this.author = author;
             this.description = description;
             this.tip = tip;
         }
         }


         public string Get()
         public override string ToString() {
        {
             return "Tip by \"" + author + "\": " + tip;  
             return name + " by \"" + author + "\": " + description;
         }
         }
        private string name;
        private string author;
        private string description;
     }
     }
}
}
</syntaxhighlight>
</syntaxhighlight>
We are now ready to start adding the functionality of our mods. We will start with InfoMod, as that will be our
We are now ready to start adding the functionality of our mods. We will start with TipMod, as that will be our
moddable mod.
moddable mod.


== Setting up InfoMod ==
== Setting up TipMod ==


For InfoMod, we do the following
For TipMod, we do the following
* Define a List to store ModDescriptions
* Define a List that stores tips
* Register a command for printing ModDescriptions
* Register a timed event that prints a random tip in chat
* Define a method for adding new ModDescriptions
* Define a public method for adding new Tips
* Publicize our InfoMod class and the method for adding new ModDescriptions, so that it can be called by other mods


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
     public class InfoMod : ModSystem
     public class TipMod : ModSystem
     {
     {
         List<ModDescription> modDescriptions = new List<ModDescription>();
        ICoreServerAPI api;
 
         List<Tip> tips = new List<Tip>();
        int tipInterval = 1000;


         public override void StartServerSide(ICoreServerAPI api)
         public override void StartServerSide(ICoreServerAPI api)
         {
         {
             base.StartServerSide(api);
             base.StartServerSide(api);
            this.api = api;


             api.RegisterCommand("mods", "Print descriptions of server mods", "", CmdMods);
             api.Event.Timer(OnInterval, tipInterval);
         }
         }


         private void CmdMods(IServerPlayer player, int groupId, CmdArgs args)
         private void OnInterval()
         {
         {
             string message = "";
             int tipCount = tips.Count();


             bool noModsDescribed = modDescriptions.Count() == 0;
             // Select a random number in the range of [0-1]
             if (noModsDescribed)
            double random = api.World.Rand.NextDouble();
             {
             // Make the number relative to the size of our tips list
                message += "There are no mod descriptions listed";
             int randomTip = (int)Math.Floor(random * tipCount);
            }
            else
            {
                foreach (ModDescription desc in modDescriptions)
                {
                    message += desc.Get() + "\n";
                }
            }


             player.SendMessage(groupId, message, EnumChatType.OwnMessage);
             Tip tip = tips[randomTip];
 
            api.SendMessageToGroup(0, tip.ToString(), EnumChatType.AllGroups);
         }
         }


         public void AddModDescription(string name, string author, string description)
         public void AddTip(Tip tip)
         {
         {
             modDescriptions.Add(new ModDescription(name, author, description));
             tips.Add(tip);
         }
         }
     }
     }
Line 176: Line 173:


We can now compile our mod, add it to our VintageStory mod folder, and test it ingame. If the /commandhere prints out a message, we are ready
We can now compile our mod, add it to our VintageStory mod folder, and test it ingame. If the /commandhere prints out a message, we are ready
to setup SpawnMod and have it interact with InfoMod.
to setup SpawnMod and have it interact with TipMod.
 
== Setting up SpawnMod ==
== Setting up SpawnMod ==


Line 188: Line 184:
Next, we do the following
Next, we do the following
* Register a command for teleporting the player to spawn
* Register a command for teleporting the player to spawn
* Access the ModLoader and retrieve InfoMod
* Add the using statement for InfoMod
* Access the ModLoader and retrieve InfoMod by using it's type
* Call the method provided by InfoMod to describe our mod
* Call the method provided by InfoMod to describe our mod



Revision as of 12:08, 5 September 2020

- THIS IS CURRENTLY A DRAFT -

This article requires you a setup development environment. If you don't have one, read the tutorial at 'Setting up your Development Environment'. For brevity, this article presupposes some familiarity with VintageStory code modding and how to package a mod (https://wiki.vintagestory.at/index.php?title=Modding:Mod_Packaging). If you are looking to create your first mod, start here =Link to first code mod sample=.

Introduction

VintageStory makes it possible to retrieve all loaded mods through the API. This makes it possible for code mods to interact with eachother and use eachother's interfaces.

The loading and retrieving of mods is done through the =ModLoader=, which is accessible via the API. We can retrieve both the full =Mod= with all it's useful data, as well as it's contained ModSystems.

This is superbly useful in multiple scenarios, for example -

  • Splitting up a large mod into a modpack, where individual mod parts can communicate with eachother, but don't break if parts of the pack are taken out.
  • Utility mods that centralize some functionality for other mods or provide an interface for some common action, such as the creative mode gui tool for growing trees, wherein you can register new tree types.

In this article, we will be creating two mods. Our first mod will provide an interface for our second mod to interact with. We will demonstrate the use of the =ModLoader= and also explain how to reference an external mod and have that reference resolve at game runtime. We'll also take note of execution order to make sure our moddable mod is loaded by the ModLoader before we try to access it.

Note that, excluding the use of refleciton, which is outside the scope of this article, only compiled mods will be able to interact with eachother. If you do not compile the code of your mods into .dll files, they will not be able to resolve references to eachother at runtime.

TipMod & SpawnMod

The two mods we will be creating are named "TipMod" and "SpawnMod".

"TipMod" will store useful gameplay tips and periodically print them in chat. It will also provide a method which other mods can use to add their own tips.

"SpawnMod" will register a command which teleports the player to his set or default spawnpoint. It will also access "TipMod" and add it's own tip describing the command.

Preperation

We start by setting up two empty mods, "TipMod" and "SpawnMod". In each mod, we add a new *.cs source file - we will be naming these files TipMod.cs and SpawnMod.cs respectively. In our .cs files, we add the necessary using statements, create classes named after our mods and have them inherit ModSystem, the base for any VintageStory code mod. In both our mods we override the StartServerSide method to access the API. We also make TipMod public, so it's accessible by other mods.

- img of folders -

// In TipMod.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Vintagestory.API.Common;
using Vintagestory.API.Server;

namespace tipmod.src
{
    public class TipMod : ModSystem
    {
        public override void StartServerSide(ICoreServerAPI api)
        {
            base.StartServerSide(api);
        }
    }
}
// In SpawnMod.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Vintagestory.API.Common;
using Vintagestory.API.Server;

namespace spawnmod.src
{
    class SpawnMod : ModSystem
    {
        public override void StartServerSide(ICoreServerAPI api)
        {
            base.StartServerSide(api);
        }
    }
}

This concludes setting up the basis of our mods. Lastly, in TipMod, we create an additional file called Tip.cs, where we will define the data structure for tips.

// In Tip.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace tipmod.src
{
    public class Tip
    {
        string author;
        string tip;

        public Tip(string author, string tip)
        {
            this.author = author;
            this.tip = tip;
        }

        public override string ToString() {
            return "Tip by \"" + author + "\": " + tip; 
        }
    }
}

We are now ready to start adding the functionality of our mods. We will start with TipMod, as that will be our moddable mod.

Setting up TipMod

For TipMod, we do the following

  • Define a List that stores tips
  • Register a timed event that prints a random tip in chat
  • Define a public method for adding new Tips
    public class TipMod : ModSystem
    {
        ICoreServerAPI api;

        List<Tip> tips = new List<Tip>();
        int tipInterval = 1000;

        public override void StartServerSide(ICoreServerAPI api)
        {
            base.StartServerSide(api);
            this.api = api;

            api.Event.Timer(OnInterval, tipInterval);
        }

        private void OnInterval()
        {
            int tipCount = tips.Count();

            // Select a random number in the range of [0-1]
            double random = api.World.Rand.NextDouble();
            // Make the number relative to the size of our tips list
            int randomTip = (int)Math.Floor(random * tipCount);

            Tip tip = tips[randomTip];

            api.SendMessageToGroup(0, tip.ToString(), EnumChatType.AllGroups);
        }

        public void AddTip(Tip tip)
        {
            tips.Add(tip);
        }
    }

Remember, both classes and methods which other mods will interact with have to be declared public. In C#, classes and methods are not public by default. You can read more about this here (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers)

We can now compile our mod, add it to our VintageStory mod folder, and test it ingame. If the /commandhere prints out a message, we are ready to setup SpawnMod and have it interact with TipMod.

Setting up SpawnMod

Before we get started, we have to add a reference to ModInfo. You can either reference the uncompiled project, or the compiled .dll file. If you are using Visual Studio, in your Solution Explorer, directly under your project, right-click "References" and select "Add Reference".

- img -

Next, we do the following

  • Register a command for teleporting the player to spawn
  • Add the using statement for InfoMod
  • Access the ModLoader and retrieve InfoMod by using it's type
  • Call the method provided by InfoMod to describe our mod

Let's compile our mod and try it ingame. ? does only the includable need to be compiled ? If we call /spawn and are teleported to spawn, our mod has been added successfuly. Next, we can try calling /commandhere to see if SpawnMod successfuly described itself to InfoMod.

- img -

Great! Next, we can add some code to make sure SpawnMod runs without InfoMod, if the user of our mod has not deemed it necessary to add.

Making SpawnMod not depend on InfoMod

add throw statement here

Conclusion

All done. This is how our code should look.

- code -

Distribution