Modding:Moddable Mod: Difference between revisions

From Vintage Story Wiki
m (expanded draft)
mNo edit summary
Line 176: Line 176:
== Setting up SpawnMod ==
== Setting up SpawnMod ==


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


Next, we do the following
Next, we do the following
* Register a command for teleporting the player to spawn
* Register a command for teleporting to spawn
* Add the using statement for InfoMod
* Add the using statement for TipMod
* Access the ModLoader and retrieve InfoMod by using it's type
* Access the ModLoader and retrieve TipMod
* Call the method provided by InfoMod to describe our mod
* 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)
        {
            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"));
        }
        private void OnCmdSpawn(IServerPlayer player, int groupId, CmdArgs args)
        {
            player.Entity.TeleportTo(player.SpawnPosition);
        }
    }
</syntaxhighlight>
</syntaxhighlight>


Let's compile our mod and try it ingame. ? does only the includable need to be compiled ?
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,
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.
we can try calling /commandhere to see if SpawnMod successfuly described itself to TipMod.


- img -
- img -


Great! Next, we can add some code to make sure SpawnMod runs without InfoMod, if the user of our mod  
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.
has not deemed it necessary to add.


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

Revision as of 12:22, 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 TipMod. 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 to spawn
  • Add the using statement for TipMod
  • Access the ModLoader and retrieve TipMod
  • Call the method provided by TipMod to add our own tip
    class SpawnMod : ModSystem
    {
        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"));
        }

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

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 -

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 InfoMod

add throw statement here

Conclusion

All done. This is how our code should look.

- code -

Distribution