Modding:Moddable Mod: Difference between revisions

From Vintage Story Wiki
mNo edit summary
mNo edit summary
Line 137: Line 137:


         List<Tip> tips = new List<Tip>();
         List<Tip> tips = new List<Tip>();
         int tipInterval = 1000;
         double tipInterval = 10;
 
         public override void StartServerSide(ICoreServerAPI api)
         public override void StartServerSide(ICoreServerAPI api)
         {
         {
Line 150: Line 149:
         {
         {
             int tipCount = tips.Count();
             int tipCount = tips.Count();
            // Stop if there are no tips
            if (tipCount == 0) return;


             // Select a random number in the range of [0-1]
             // Select a random number in the range of [0-1]
Line 158: Line 160:
             Tip tip = tips[randomTip];
             Tip tip = tips[randomTip];


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


Line 167: Line 169:
     }
     }
</syntaxhighlight>
</syntaxhighlight>
For testing purposes, we set the interval to be very short (10 seconds). Feel free to change this accordingly.


Remember, both <b>classes and methods which other mods will interact with have to be declared public.</b>
Remember, both <b>classes and methods which other mods will interact with have to be declared public.</b>
In C#, classes and methods are not public by default.
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)
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
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
Line 176: Line 178:
== Setting up SpawnMod ==
== Setting up SpawnMod ==


Before we get started, we have to add a reference to TipMod. You can either reference the uncompiled project,
Before we start interacting with TipMod, let's setup SpawnMod. This involves registering a simple command which teleports the player to spawn.
<syntaxhighlight lang="c#">
    class SpawnMod : ModSystem
    {
        public override void StartServerSide(ICoreServerAPI api)
        {
            base.StartServerSide(api);
 
            api.RegisterCommand("spawn", "Teleport to spawn", "", OnCmdSpawn);
        }
 
        private void OnCmdSpawn(IServerPlayer player, int groupId, CmdArgs args)
        {
            player.Entity.TeleportTo(player.SpawnPosition);
        }
    }
</syntaxhighlight>
SpawnMod is ready to be tested. When added to the mods folder, the command /spawn should now teleport us to spawn.
We've setup the base of our mods, now let's make SpawnMod interact with TipMod.
 
== Mods interacting ==
Before we get started, we have to add a reference to TipMod. You can either reference the project itself,
or the compiled .dll file. If you are using Visual Studio, in your Solution Explorer, directly under your 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".
right-click "References" and select "Add Reference".
Line 183: Line 206:


Next, we do the following
Next, we do the following
* Register a command for teleporting to spawn
* Add a using statement for tipmod.src
* Add the using statement for TipMod
* Retrieve TipMod through the ModLoader by passing it's type
* Access the ModLoader and retrieve TipMod
* Call the method provided by TipMod and add a variety of tips
* Call the method provided by TipMod to add our own tip


<syntaxhighlight lang="c#">
<syntaxhighlight lang="c#">
    class SpawnMod : ModSystem
    {
         public override void StartServerSide(ICoreServerAPI api)
         public override void StartServerSide(ICoreServerAPI api)
         {
         {
Line 199: Line 219:
             TipMod tipMod = api.ModLoader.GetModSystem<TipMod>();
             TipMod tipMod = api.ModLoader.GetModSystem<TipMod>();
             tipMod.AddTip(new Tip("codemeister32", "To quickly return to spawn, type /spawn"));
             tipMod.AddTip(new Tip("codemeister32", "To quickly return to spawn, type /spawn"));
            tipMod.AddTip(new Tip("codemeister32", "Can't find your way home? Type /spawn"));
            tipMod.AddTip(new Tip("codemeister32", "Being chased by wolves? Quick, type /spawn"));
         }
         }
</syntaxhighlight>


        private void OnCmdSpawn(IServerPlayer player, int groupId, CmdArgs args)
We're now ready to test our mods. After placing both our compiled mods in the mods folder, we should be seeing a random tip pop up in the chat every 10 seconds. If no tips are showing up, check the log files for any errors. Don't forget that both our mods have to be compiled and present in the mod folder for them to work.
        {
            player.Entity.TeleportTo(player.SpawnPosition);
        }
    }
</syntaxhighlight>


Let's compile our mod and try it ingame.
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 TipMod.


- img -
- img -
Line 218: Line 233:




== Making SpawnMod not depend on InfoMod ==
== Making SpawnMod not depend on TipMod ==


add throw statement here
add throw statement here

Revision as of 16:18, 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>();
        double tipInterval = 10;
        public override void StartServerSide(ICoreServerAPI api)
        {
            base.StartServerSide(api);
            this.api = api;

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

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

            // Stop if there are no tips
            if (tipCount == 0) return;

            // 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(GlobalConstants.GeneralChatGroup, tip.ToString(), EnumChatType.AllGroups);
        }

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

For testing purposes, we set the interval to be very short (10 seconds). Feel free to change this accordingly.

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 start interacting with TipMod, let's setup SpawnMod. This involves registering a simple command which teleports the player to spawn.

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

            api.RegisterCommand("spawn", "Teleport to spawn", "", OnCmdSpawn);
        }

        private void OnCmdSpawn(IServerPlayer player, int groupId, CmdArgs args)
        {
            player.Entity.TeleportTo(player.SpawnPosition);
        }
    }

SpawnMod is ready to be tested. When added to the mods folder, the command /spawn should now teleport us to spawn. We've setup the base of our mods, now let's make SpawnMod interact with TipMod.

Mods interacting

Before we get started, we have to add a reference to TipMod. You can either reference the project itself, 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

  • Add a using statement for tipmod.src
  • Retrieve TipMod through the ModLoader by passing it's type
  • Call the method provided by TipMod and add a variety of tips
        public override void StartServerSide(ICoreServerAPI api)
        {
            base.StartServerSide(api);

            api.RegisterCommand("spawn", "Teleport to spawn", "", OnCmdSpawn);

            TipMod tipMod = api.ModLoader.GetModSystem<TipMod>();
            tipMod.AddTip(new Tip("codemeister32", "To quickly return to spawn, type /spawn"));
            tipMod.AddTip(new Tip("codemeister32", "Can't find your way home? Type /spawn"));
            tipMod.AddTip(new Tip("codemeister32", "Being chased by wolves? Quick, type /spawn"));
        }

We're now ready to test our mods. After placing both our compiled mods in the mods folder, we should be seeing a random tip pop up in the chat every 10 seconds. If no tips are showing up, check the log files for any errors. Don't forget that both our mods have to be compiled and present in the mod folder for them to work.


- img -

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


Making SpawnMod not depend on TipMod

add throw statement here

Conclusion

All done. This is how our code should look.

- code -

Distribution