90
edits
mNo edit summary |
mNo edit summary |
||
Line 2: | Line 2: | ||
- THIS IS CURRENTLY A DRAFT - | - THIS IS CURRENTLY A DRAFT - | ||
This article requires | This article requires 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 | VintageStory code modding, primarily how to package a mod (https://wiki.vintagestory.at/index.php?title=Modding:Mod_Packaging) and familiarity with what a ModSystem is. | ||
If you are looking to create your first mod, | If you are looking to create your first mod, we recommend starting here =Link to first code mod sample=. | ||
== Introduction == | == Introduction == | ||
VintageStory makes | VintageStory makes all loaded mods available through the API. | ||
This makes it possible for code mods to interact with eachother and | This makes it possible for code mods to interact with eachother and access eachother's public properties. | ||
The loading and retrieving of mods is done through the =ModLoader=, which is accessible via the API. | The loading and retrieving of mods is done through the =ModLoader=, which is accessible via the API. | ||
Line 16: | Line 16: | ||
This is superbly useful in multiple scenarios, for example - | This is superbly useful in multiple scenarios, for example - | ||
* Splitting up a large mod into a modpack | * Splitting up a large mod into smaller logical parts that communicate - a modpack. | ||
* | * Extendable mods, for example - the creative tool for growing trees, which lets you register new tree types. | ||
In this article, we will be creating two mods. Our first mod will provide | In this article, we will be creating two mods. Our first mod will provide a method 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 | 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 | 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. | we try to access it. | ||
Note that, excluding the use of | Note that, excluding the use of reflection or the use of an intermediary library, which is outside the scope of this article, | ||
<b>only compiled mods will be able to | <b>only compiled mods will be able to resolve eachother's references</b>. | ||
== TipMod & SpawnMod == | == TipMod & SpawnMod == | ||
Line 36: | Line 35: | ||
"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 "TipMod" and add it's own | access "TipMod" and add it's own tips about the command. | ||
== Preperation == | == Preperation == | ||
We start by setting up two empty mods, "TipMod" and "SpawnMod". | 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 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 | In our .cs files, we do the following. | ||
the base for any VintageStory code mod. | * Add the necessary using directives. | ||
* Create a class named after the mod it's contained it. | |||
* Have our classes inherit ModSystem - the base for any VintageStory code mod. | |||
* Override the StartServerSide method to access the API. | |||
* Make the TipMod class public so that it's accessible by other mods. | |||
- img of folders - | - img of folders - | ||
Line 122: | Line 125: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
We are now ready to start adding the functionality of our mods. We will start with TipMod, 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 | ||
point of interaction. | |||
== Setting up TipMod == | == Setting up TipMod == | ||
For TipMod, we do the following | For TipMod, we do the following. | ||
* Define a List that stores tips | * Store the API object in a variable so that we can access it throughout our class. | ||
* | * Define a List that stores tips. | ||
* Define a public method for adding new Tips | * Create a method that selects a random tip and prints it in chat for all players, printing a default message if there are no existing tips. | ||
* Register that method to be called at set intervals by the timer, which is available in the event API. | |||
* Define a public method for adding new Tips. | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
// In TipMod.cs | |||
public class TipMod : ModSystem | public class TipMod : ModSystem | ||
{ | { | ||
Line 149: | Line 155: | ||
{ | { | ||
int tipCount = tips.Count(); | int tipCount = tips.Count(); | ||
string message = "There aren't any listed tips"; | |||
// | if (tipCount > 0) | ||
{ | |||
// 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]; | |||
message = tip.ToString(); | |||
} | |||
api.SendMessageToGroup(GlobalConstants.GeneralChatGroup, | |||
api.SendMessageToGroup(GlobalConstants.GeneralChatGroup, message, EnumChatType.AllGroups); | |||
} | } | ||
Line 174: | Line 184: | ||
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) | 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 | We can now compile our mod, add it to our VintageStory mod folder and test it ingame. If the there are occasional chat messages | ||
to | claiming that no tips are listed, our mod is working. We are ready to move on to the next step - setting up SpawnMod and then having it interact with TipMod. | ||
== Setting up SpawnMod == | == Setting up SpawnMod == | ||
Let's first setup our mods functionality, We register a command which teleports the player to spawn. Conveniently the Player object holds it's entity, which defines a method for teleporting itself to a location. | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
class SpawnMod : ModSystem | class SpawnMod : ModSystem | ||
Line 195: | Line 204: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
SpawnMod is ready | SpawnMod is ready, let's test it. 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. | We've setup the base of our mods, now let's make SpawnMod interact with TipMod. | ||
== Mods interacting == | == Mods interacting == | ||
Before we get started, we have to add a reference to TipMod | Before we get started, we have to add a reference in SpawnMod to TipMod. 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". | - img - | ||
Here we locate either the TipMod project or the compiled .dll file and add it. | |||
After adding the reference, make sure it is not copying the file to the output folder when compiling - | |||
having multiple .dll files with ModSystems in them will break your mod. | |||
- img - | - img - | ||
Now we're ready to have SpawnMod add tips. Let's go ahead and do the following. | |||
* Add a using | * Add a using directive for tipmod.src. | ||
* Retrieve TipMod through the ModLoader by passing it's type | * Retrieve TipMod through the ModLoader by passing it's type. | ||
* Call the method provided by TipMod and add a variety of tips | * Call the method provided by TipMod and add a variety of tips. | ||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 217: | Line 230: | ||
api.RegisterCommand("spawn", "Teleport to spawn", "", OnCmdSpawn); | api.RegisterCommand("spawn", "Teleport to spawn", "", OnCmdSpawn); | ||
using tipmod.src; | |||
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")); | ||
Line 224: | Line 238: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
== Conclusion == | |||
Following the steps in this article, this is what our resulting code looks like, spread across two mods. | |||
<syntaxhighlight lang="c#"> | |||
// 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.Config; | |||
using Vintagestory.API.Server; | |||
namespace tipmod.src | |||
{ | |||
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(); | |||
string message = "There aren't any listed tips"; | |||
- | if (tipCount > 0) | ||
{ | |||
// 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]; | |||
message = tip.ToString(); | |||
} | |||
api.SendMessageToGroup(GlobalConstants.GeneralChatGroup, message, EnumChatType.AllGroups); | |||
} | |||
public void AddTip(Tip tip) | |||
{ | |||
tips.Add(tip); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<syntaxhighlight lang="c#"> | |||
// 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; | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
<syntaxhighlight lang="c#"> | |||
// 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; | |||
using tipmod.src; | |||
namespace spawnmod.src | |||
{ | |||
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")); | |||
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")); | |||
} | |||
private void OnCmdSpawn(IServerPlayer player, int groupId, CmdArgs args) | |||
{ | |||
player.Entity.TeleportTo(player.SpawnPosition); | |||
} | |||
} | |||
} | |||
</syntaxhighlight> | |||
We're now ready to test our mods to see if they're interacting successfully. After placing both our compiled mods in the mods folder, we should see a random tip pop up in the chat every 10 seconds. If so, well done! You've successfully followed the article and have created a moddable mod. | |||
- img - | |||
Congratulations! | |||
== Troubleshooting == | |||
- | If you've ran into problems setting up either mod, check the error logs located at VintagestoryData/Logs in server-main.txt. | ||
Possible causes for either mod not working are as follows. | |||
* It's a .cs mod and not a .dll (compiled) mod. | |||
* It's missing modinfo.json. | |||
* Your version of VintageStory is outdated. | |||
* You have multiple .dll files containing ModSystems in your mod folder. | |||
* The referenced mod is not present in the mod folder. | |||
== Distribution == | == Distribution == | ||
Here are the mod versions: | |||
-Mod Download here- |
edits