--- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Adding_Block_Behavior TITLE: Modding:Adding Block Behavior --- CONTENT --- Other languages: English español русский This page is a candidate to be rewritten. This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. This page was last verified for Vintage Story version 1.19.8 . Contents 1 Introduction 1.1 Setting up 1.2 Creating the behavior 1.3 Register 1.4 Distribution 1.5 Testing 2 Advanced Behavior 2.1 Example 2.2 Parsing properties 2.3 Adding another block 3 Mod Download Introduction Block Behaviors are useful when you want different blocks to act in the same way, as you can attach one or more block behaviors to an arbitrary number of blocks. You may want to have a look at the existing block behaviors before implementing your own. In this tutorial we'll create a new Behavior that we can attach to blocks to make them movable by right clicking them. Setting up A development workspace is required. Additionally you will need the assets (blocktype, texture and lang file). You can either create your one owns or use those pre-made ones: Moving - No CS File.zip Creating the behavior So first of all we need to create the behavior itself, which is a class extending BlockBehavior class Moving : BlockBehavior { public Moving ( Block block ) : base ( block ) { } } This class provides several methods we can override. When you use Visual Studio you can find a full list of a methods by hovering with the mouse of "BlockBehavior" and pressing "F12". The method bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel, ref EnumHandling handling) looks to be ideal for our purpose. What should it do? Calculate the new position to move the block to, based on the block face the player is looking at. Check if the block can be placed at this new position. Remove the block at the old position. Place the same type of block at the new position. Skip the default logic that would otherwise place whatever item is held at the old position. public override bool OnBlockInteractStart ( IWorldAccessor world , IPlayer byPlayer , BlockSelection blockSel , ref EnumHandling handling ) { // Find the target position BlockPos pos = blockSel . Position . AddCopy ( blockSel . Face . Opposite ); // Can we place the block there? if ( world . BlockAccessor . GetBlock ( pos ). IsReplacableBy ( block )) { // Remove the block at the current position and place it at the target position world . BlockAccessor . SetBlock ( 0 , blockSel . Position ); world . BlockAccessor . SetBlock ( block . BlockId , pos ); } // Notify the game engine other block behaviors that we handled the players interaction with the block. // If we would not set the handling field the player would still be able to place blocks if he has them in hands. handling = EnumHandling . PreventDefault ; return true ; } Register In order the register the BlockBehavior we need to create a mod class, override Start(ICoreAPI) and register it with the given name: public class MovingBlocks : ModSystem { public override void Start ( ICoreAPI api ) { base . Start ( api ); api . RegisterBlockBehaviorClass ( "Moving" , typeof ( Moving )); } } Distribution In order to finish everything, open the modtools and type in pack . Now you can take the zip file and share it with other people. for VS 1.9: Moving_v1.0.0.zip for VS 1.6: Moving.zip Testing Advanced Behavior Our behavior is still rather simple, but there are a lot more possibilities. A behavior can have special properties, which can be defined by the blocktype itself. Example The behavior liquid supports some special properties as shown in this example of the water blocktype: behaviors : [ { na me : "FiniteSpreadingLiquid" , proper t ies : { spreadDelay : 150 , liquidCollisio n Sou n d : "hotmetal" , sourceReplaceme nt Code : "obsidian" , fl owi n gReplaceme nt Code : "basalt" } } ], Parsing properties In order to take care of special properties there is a method called Initialize(JsonObject) . Each blocktype creates a new instance of the behavior, so the method can be used to parse the properties. So what kind of properties could we add? push distance pull block if player is sneaking First of all, we need to override the method in our block behavior class ... public override void Initialize ( JsonObject properties ) { base . Initialize ( properties ); } Additionally we need to add two fields, one for the distance and another one if the player should pull the block while sneaking ... public int distance = 1 ; public bool pull = false ; Now we can parse the two properties like so: distance = properties [ "distance" ]. AsInt ( 1 ); pull = properties [ "pull" ]. AsBool ( false ); The next thing we need to change is the interact method itself, so that it takes care of the distance and the pull properties ... public override bool OnBlockInteractStart ( IWorldAccessor world , IPlayer byPlayer , BlockSelection blockSel , ref EnumHandling handling ) { // Find the target position BlockPos pos = blockSel . Position . AddCopy ( pull && byPlayer . WorldData . EntityControls . Sneak ? blockSel . Face : blockSel . Face . Opposite , distance ); // Can we place the block there? if ( world . BlockAccessor . GetBlock ( pos ). IsReplacableBy ( block )) { // Remove the block at the current position and place it at the target position world . BlockAccessor . SetBlock ( 0 , blockSel . Position ); world . BlockAccessor . SetBlock ( block . BlockId , pos ); } // Notify the game engine other block behaviors that we handled the players interaction with the block. // If we would not set the handling field the player would still be able to place blocks if he has them in hands. handling = EnumHandling . PreventDefault ; return true ; } Adding another block Let's create another block using this behavior, but this time we will configure some additional properties ... behaviors : [ { na me : "Moving" , proper t ies : { "distance" : 2 , "pull" : true } } ], The block will be pushed two blocks instead of one and the player can pull it by sneaking while right clicking. Mod Download for VS 1.9: AdvancedMoving_v1.0.0.zip for VS 1.6: AdvancedMoving.zip Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Advanced_Code_Tutorials TITLE: Modding:Advanced Code Tutorials --- CONTENT --- Other languages: English русский This is the landing page for the advanced code tutorials. Advanced Tutorials These tutorials are currently in progress! Please check back later! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Nothing here yet! --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Advanced_Content_Tutorials TITLE: Modding:Advanced Content Tutorials --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . This is the landing page for the advanced content tutorials. These will cover topics that require a good knowledge of how content mods work, and can often be longer than most tutorials. If you have not already done so, it is extremely important learn about the various modding concepts for content modding, especially the explanation of variants . It is also a good idea to be aware of the Debugging Content page. This will contain a list of common issues when adding content, as well as methods on how to access and read the game's log files. Advanced Tutorials There are currently no tutorials in this series, but they are being worked on! Please come back soon. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Empty --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Animation TITLE: Modding:Animation --- CONTENT --- Other languages: Deutsch English español русский This page is a candidate to be rewritten. Reason: The VSMC Tutorials are being rewritten and restructured. Thank you for the patience! This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. Below can be found modified version of an VSMC animation tutorial [1] created by Luke . This is a basic tutorial , but it is recommended that you have completed the Modeling tutorial before starting this one. Contents 1 Preparing 2 UI 3 What is a Keyframe? 4 Animation Process 5 Notes 6 See Also 7 References Preparing Vintage Story has its own model creator called VS Model Creator that allows anyone to create, texture, and animate custom shapes for use in Vintage Story mods. The newest version can be downloaded here . This tutorial uses a premade model. You can use your own or download Luke's model here (trusted file, published by game developers team member). After loading it in, go ahead and click the “Keyframe” tab to open up the animator. UI So now what are we looking at? It’s not that different from the model creator. Animation can be thought of as time + position. So we have our time values on the left, and our positional values on the right. We’ll start with the right, because it’s very similar to the model creator. So at the top right you have the hierarchy (or object selector). As you already know, you click on the various objects here to choose which one you want to edit. Next below the hierarchy we have the object editor. This is what we use to adjust position or rotation, just like in the model creator. The only difference is that by default these values are turned off. I’ll explain this later, but for now just know that this is where you manipulate the shapes. And also Stretch doesn’t do anything. Sorry. Now for the left side, we have at the top left the Animation Selector. This is where you pick which animation to work on. It’s also where you make new animations. Below that is the Timeline. You use this to fast forward, rewind, pause, etc.. Basically like a remote control for your TV. And finally we have the Keyframe Editor. Okay, so this is where I have to get a little technical to explain what keyframes are.. Bear with me. What is a Keyframe? So every animation is made up of frames. These are still images that, when the animation is playing, we view one after the other in quick succession. When the frames succeed each other fast enough (usually measured as Frame Rate or Frames per Second), it creates the illusion of motion. This is the basis of all cinema and animation. So what is a Key Frame?? These are basically the important frames that are used as a reference for the other frames. You can also think of them as the beginning and end of a given motion. The idea is, you make these particular frames look nice and detailed, and then the rest of the frames don’t have to be as detailed because they work as transitions (or inbetweens) for the keyframes. This way, you only have to work on 4 frames instead of 30, for example. If that doesn’t make sense, don’t worry about the theory. It should be clear at least in practice soon enough. Animation Process Okay! Now that that’s out of the way, let’s actually animate something. Mouse over to the Animation Selector in the top left and click the +- looking button right next to the drop down menu. This will open up your List of Animations. From here, we’re going to make a new one by pressing the giant New button at the bottom. Let’s call it “Jump”. Those are the values on the side are your animation settings. Code is the information that VS looks for. Generally, you probably want to make it the same as the animation name, except lowercase. “On Activity stopped” refers to what happens after the object in game stops their action. For example, when a sheep stops running, you can tell it to stop its animation immediately, ease out of the animation in a transition, complete the animation despite no longer running, or play the animation backward. You’ll figure out which one to use for each situation, but for now we don’t need to worry about it. “On Animation ended” refers to the animation itself instead of the object it’s attached to. So this would mean when you’ve played through all the frames. You can set it to loop, hold on the last frame, or just stop (so that it only plays the animation once). Again, we don’t need it worry about this right now, but it’s good to know. We can close out of this menu now, after making sure you have your new “Jump” animation selected. Okaay, so now we have a fresh animation to work on. Let’s make it jump. The timeline should be set to 0, and you shouldn’t have any keyframes in sight. Now we make some keyframes. To start with, select “Body” in the hierarchy, and activate Position. It will automatically set to 0, 0, 0. Which is good, because we want this to be the starting point. This also automatically creates a keyframe for “Body” at frame #0. Next, go to the timeline and drag the scrollbar to 15 (which is frame #15). Once you’ve done that, activate Position again for “Body” so that the editor knows to make a keyframe here as well. Now let’s change the Y value to 4. You’ll notice the model going up in the display. Now if hit the Play button on the timeline, you can watch your work in action. It should look like this: You’ve managed to make the Body move up and down, but now it’s time to add some rotation. Let’s give its legs some motion. It’s a pretty similar process, so you should be able to just follow along with this image: And that’s the gist of it. Now, you can use the animation selector in the top left to view the “Run” animation. It’s basically what our rabbit would look like if we kept working on it. You can look around this animation to get an idea for how to utilize position, rotation, and keyframes. I hope this has been helpful! Notes Stretch tab is no longer a thing in new versions. See Also Animation Intermediate Tutorial References ↑ https://www.vintagestory.at/forums/topic/505-vs-model-creator-basics/?tab=comments#comment-2809 Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Model Creator Basics • Tutorials • Model Animation • All VSMC Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Patching_in_Code TITLE: Modding:Asset Patching in Code --- CONTENT --- Contents 1 Attributes 1.1 Adding new attribute 1.2 Modify existing attribute 2 Behaviors 2.1 Adding behavior to collectible 2.2 Adding behavior with properties to collectible 2.3 Adding behavior with properties to block entity The JSON asset files are parsed into C# files. JSON_Patching involves patching the assets before they are parsed. Asset patching in code involves patching the assets after they are converted to C# objects. This is different from Monkey patching , which involves using new code to patch compiled code. JSON patches are tricky to get right, and patching the same change into hundreds of objects is very tedious. Patching in code works well for those cases. By applying the code patch to all assets that meet certain criteria, the patch can be more robust to the player adding more mods. Those new mods can add more assets, and as long as they meet the patch's criteria, they will automatically get patched. Most of the examples below perform the patching in the AssetsFinalize stage, before the game is fully loaded. To better understand that, it helps to understand a little about the game's start up order. The server searches the file system to find which mods to load and what their load order is. StartPre is called on the server side for every loaded mod, in their load order. The server builds the asset search path, which includes all loaded mods. Start is called on the server side for every loaded mod, in their load order. AssetsLoaded is called on the server side for every loaded mod, in their load order. Some ModSystems in the VSEssentials mod, notably ModRegistryObjectTypeLoader , parse the asset files and create C# objects. AssetsFinalize is called on the server side for every loaded mod, in their load order. This is where code patches run. StartServerSide is called on the server side for every loaded mod, in their load order. The parsed C# assets should have stopped changing by this point, so the server calls OnLoaded on all assets. The C# assets (except config assets) are sent over the network to the client. The client loads mods and calls their methods in the same order. For universal mods, they should only patch assets on the server side. Otherwise, the patch will be applied twice on the client (once by the server before sending over the assets and again on the client after receiving the patched assets). For server-only mods and client-only mods, they should patch on their side, without any special check. All examples on this page are for universal mods, and thus have the server side check. The following example comes from the VSSurvival mod and shows the server side check: public override void AssetsFinalize ( ICoreAPI api ) { // Needs to be done before assets are ready because it rewrites Behavior and CollectibleBehavior if ( api . Side == EnumAppSide . Server ) // No need to add it twice on the client { addReinforcementBehavior (); } } A downside to patching in code is that they are difficult to find in other mods. Let's say some modder gets a bug report that their new block wasn't working as expected on some server with 50 other mods. Most modders are used to looking for patches JSON patch files, but not C# files. So it may take them a long time to find out that your mod used a code patch to add a block behavior that is incompatible with their mod. Note the dumpjson mod does show the effect of code patches, and can be useful for debugging in the above situation. Code patches are applied on the server side before transferring the assets to the client, and dumpjson runs on the client side and prints the assets it received from the server. The examples below check whether the entries in the collectibles, blocks, items, etc.. arrays are null. These checks are necessary because internally VS creates an oversized array with null entries, and only resizes the array if it ends up being too small. Attributes Collectible attributes (not to confuse with itemstack attributes) can be modified using examples below: Adding new attribute This example adds the shelvable attribute to all collectibles: using Newtonsoft.Json.Linq ; using Vintagestory.API.Common ; using Vintagestory.API.Datastructures ; public class WikiExamples : ModSystem { public override void AssetsFinalize ( ICoreAPI api ) { if ( api . Side != EnumAppSide . Server ) { return ; } foreach ( CollectibleObject obj in api . World . Collectibles ) { // Make sure collectible or its code is not null if ( obj == null || obj . Code == null ) { continue ; } // Make sure attributes are not null obj . Attributes ??= new JsonObject ( new JObject ()); // Make collectible storable on shelf obj . Attributes . Token [ "shelvable" ] = JToken . FromObject ( true ); } } } Modify existing attribute In this example we retrieve integer list, modify it, then put it back: using Newtonsoft.Json.Linq ; using System.Collections.Generic ; using Vintagestory.API.Common ; public class WikiExamples : ModSystem { public override void AssetsFinalize ( ICoreAPI api ) { if ( api . Side != EnumAppSide . Server ) { return ; } foreach ( CollectibleObject obj in api . World . Collectibles ) { // Make sure collectible or its code is not null if ( obj == null || obj . Code == null ) { continue ; } // Make sure attribute exists if ( obj . Attributes != null && obj . Attributes . KeyExists ( "somelist" )) { // Retrieve list List < int > list = obj . Attributes [ "somelist" ]. AsObject < List < int >>(); // Add new value list . Add ( 1 ); // Put it back obj . Attributes . Token [ "somelist" ] = JToken . FromObject ( list ); } } } } Behaviors Behaviors of collectibles and entities can be modified too. Note that blocks have two arrays of behaviors: CollectibleBehaviors and BlockBehaviors . When the VSEssentials mod loads blocks, it adds block behaviors to both arrays , and adds collectible behaviors to only CollectibleBehaviors . So code patches that add new block behaviors should maintain this invariant and add them to both arrays. Items only have have CollectibleBehavior s. Adding behavior to collectible Example of adding behaviors to items and blocks: using Vintagestory.API.Common ; using Vintagestory.API.Util ; using Vintagestory.GameContent ; public class WikiExamples : ModSystem { public override void AssetsFinalize ( ICoreAPI api ) { if ( api . Side != EnumAppSide . Server ) { return ; } foreach ( Item item in api . World . Items ) { // Make sure item or its code is not null if ( item == null || item . Code == null ) { continue ; } // Create collectible behavior CollectibleBehaviorGroundStorable behavior = new CollectibleBehaviorGroundStorable ( item ); // Add behavior to collectible item . CollectibleBehaviors = item . CollectibleBehaviors . Append ( behavior ); } foreach ( Block block in api . World . Blocks ) { // Make sure block or its code is not null if ( block == null || block . Code == null ) { continue ; } // Create block block behavior BlockBehaviorCanIgnite blockBehavior = new BlockBehaviorCanIgnite ( block ); // Add block behavior to block block . CollectibleBehaviors = block . CollectibleBehaviors . Append ( blockBehavior ); block . BlockBehaviors = block . BlockBehaviors . Append ( blockBehavior ); } } } Adding behavior with properties to collectible Example of adding behavior with properties to collectible: using Newtonsoft.Json.Linq ; using Vintagestory.API.Common ; using Vintagestory.API.Datastructures ; using Vintagestory.API.Util ; using Vintagestory.GameContent ; public class WikiExamples : ModSystem { public override void AssetsFinalize ( ICoreAPI api ) { if ( api . Side != EnumAppSide . Server ) { return ; } foreach ( CollectibleObject obj in api . World . Collectibles ) { // Make sure collectible or its code is not null if ( obj == null || obj . Code == null ) { continue ; } // Create collectible behavior CollectibleBehaviorGroundStorable behavior = new CollectibleBehaviorGroundStorable ( obj ); // Create empty properties JsonObject properties = new JsonObject ( new JObject ()); // Fill properties properties . Token [ "layout" ] = JToken . FromObject ( "Quadrants" ); // Initialize behavior with new properties behavior . Initialize ( properties ); // Add behavior to collectible obj . CollectibleBehaviors = obj . CollectibleBehaviors . Append ( behavior ); } } } Adding behavior with properties to block entity Block entity behaviors can be added to block entities using blocks. In order for a block to have block entity behaviors, it must have a block entity class. The block entity class is set automatically when behaviors are added via JSON. However, it must be set explicitly in code patches, because they run after the VSEssentials code that sets the default value. Example of adding block entity behavior with properties to blocks: using Newtonsoft.Json.Linq ; using Vintagestory.API.Common ; using Vintagestory.API.Datastructures ; using Vintagestory.API.Util ; public class WikiExamples : ModSystem { public override void AssetsFinalize ( ICoreAPI api ) { if ( api . Side != EnumAppSide . Server ) { return ; } foreach ( Block block in api . World . Blocks ) { // Make sure collectible or its code is not null if ( block == null || block . Code == null ) { continue ; } // Create block entity behavior BlockEntityBehaviorType behavior = new BlockEntityBehaviorType () { Name = "RainCollector" }; // Create empty properties behavior . properties = new JsonObject ( new JObject ()); // Fill properties behavior . properties . Token [ "litersPerTick" ] = JToken . FromObject ( 0.1f ); // Add block entity behavior to block block . BlockEntityBehaviors = block . BlockEntityBehaviors . Append ( behavior ); if ( block . EntityClass == null ) { block . EntityClass = "Generic" ; } } } } Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_System TITLE: Modding:Asset System --- CONTENT --- Other languages: English español français русский This page was last verified for Vintage Story version 1.19.8 . Vintage Story loads most of its game content from asset JSONs. Many examples of these can be found inside the assets directory inside your Vintagestory folder . Things such as blocks, items, world generation, recipes, etc. are all loaded from assets during startup. Contents 1 Finding the assets folder 1.1 Windows 2 List of Asset Types 3 Domains 3.1 Overwriting assets Finding the assets folder All the functioning assets for Vintage Story are visible in the game's folder, allowing you to peruse them and learn to write your own. You can find these files using the following methods for each type of OS you're running Vintage Story on. If you're looking for source code (ie C# classes) your best option is to dive into the Vintage Story Github and peruse the vsapi , vssurvivalmod , vsessentialsmod and vscreativemod repositories for class references. Windows If you're running Vintage Story on Windows, and installed the game to the default location, you can navigate to this folder by typing %appdata% in desktop search bar, which will take you to your computer's roaming folder. Find the Vintagestory folder here and you'll immediately come across the assets folder, which contains assets for "creative", "game" and "survival". creative contains assets for creative mode only. game contains many universal assets, such as lang files, the player entity shape, and other essential assets to the game. survival contains the bulk of the actual content most players will come across, and contains all the resources Vintage Story uses, such as textures sounds and world generation. List of Asset Types The following are the categories of assets you can expect to use while modding for Vintage Story. Each are easily modifiable and can be used to easily add your own content or changes without having to do any advanced coding. There are many examples and tutorials on the wiki, such as those on Developing a Content Mod . For a more detailed description of each asset type, you should view the Asset Types category. Name Affects Gameplay Side Type Usage AssetCategory blocktypes true universal Defines all the blocks that are in the game config true universal Used for generic data that does not fit into the other categories. dialog false client only Contains some of the dialog layouts entities true server only Creatures and other entities itemtypes true universal Defines all the items that are in the game lang false universal Translation music false client only The games music tracks and its configuration patches true universal Used to patch game data defined in other json files worldproperties true universal Contains some commonly used lists of properties abstract true universal block true universal sounds false universal Sounds shapes false universal Contains the 3d models for all the items, blocks and creatures block false universal Shapes for blocks entity false universal Shapes for entities item false universal Shapes for items recipes true server only The crafting, knapping, smithing and clay forming recipes alloy true server only How metals can be combined to create alloys grid true server only Recipes for 3x3 grid crafting smithing true server only Recipes for smithing on the anvil worldgen true server only Contains all the configuration for world generation terrain true server only Defines how the terrain should look and with what it should be decorated with tree true server only Defines the shapes of trees shaders false client only Contains GLSL source code, that defines how the game is rendered shaderincludes false client only Contains GLSL source code, that defines how the game is rendered textures false client only Contains all the graphics of the game block false client only Block textures item false client only Item textures decal false client only Decaltextures entities false client only Entities textures environment false client only Environment textures (Sky, Moon, Sun, etc.) gui false client only Gui textures hud false client only Hud textures particle false client only Particle textures Domains Domains are used to separate mod added content from the original one. Basically a domain is a prefix for any given code (identifier for item, block, etc.) or path (textures, sounds, etc.). VintageStory itself has its own prefix game for all its assets (game, survival and creative folders). When packaging a mod you specify a domain by placing a directory inside the mod assets directory with all your mod assets inside. The name of your domain directory will be the "current domain" for all assets inside it. If no domain has been specified in an asset code the game will assume it is in the current domain, meaning you only have to add a domain prefix if you want to refer to something outside the current domain. For example, if you want to create a new block which uses the original leather texture, you would have to specify the domain (since your block has a different one). Instead of using block/leather for assets/survival/textures/block/leather.png , you would have add the prefix for the domain game:block/leather . Overwriting assets There are no limitations to the system. So you can overwrite assets from Vintagestory itself by using the game domain folder. To overwrite the bed blocktype you can put your own json file inside your mod zip archive with the following path: assets/game/blocktypes/wood/bed.json . So Vintagestory will load your json file instead of the original one. Theme packs can only override assets that do not affect game mechanics, the other mod types can override any asset. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_BlockTypes TITLE: Modding:Asset Type - BlockTypes --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 2 See Also Usage Block Type assets are used to register new types of blocks within the game. Block type assets are written in JSON. Blocks often add block behaviors . Advanced blocks may also add block entity behaviors . Asset Location New block type assets can be made by adding new JSON files in a folder called ' blocktypes ' in your mod's domain. Json Documentation All JSON properties for BlockTypes are currently documented on the JSON Reference . See Also BlockTypes on GitHub Content Tutorial Simple Block Content Tutorial Block Variants Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Entities TITLE: Modding:Asset Type - Entities --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 2 See Also Usage Entity assets are used to register new types of entities within the game. Entity assets are written in JSON. Entities often add entity behaviors . Asset Location New entity assets can be made by adding new JSON files in a folder called ' entities ' in your mod's domain. Json Documentation All JSON properties for EntityTypes are currently documented on the JSON Reference . See Also EntityTypes on GitHub Basic Entity Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_ItemTypes TITLE: Modding:Asset Type - ItemTypes --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 2 See Also Usage Item Type assets are used to register new types of items within the game. Item type assets are written in JSON. Items can also have collectible behaviors . Asset Location New item type assets can be made by creating new JSON files in a folder called ' itemtypes ' in your mod's domain. Json Documentation All JSON properties for ItemTypes are currently documented on the JSON Reference . See Also ItemTypes on GitHub Content Tutorial Simple Item Content Tutorial Item Variants Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Patches TITLE: Modding:Asset Type - Patches --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Notes 2 See Also Usage Patch assets are used to change specific parts of other JSON assets. They can be used for changing base game assets, or used for mod compatibility. Patch assets are written in JSON. Asset Location New patch assets can be made by creating new JSON files in a folder called ' patches ' in your mod's domain. Notes Although patches are written in JSON, it is recommended to use the in-built mod maker program to generate them. See Also JsonPatch on GitHub JSON Patching Compatibility With Other Mods Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Recipes TITLE: Modding:Asset Type - Recipes --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Usage Recipe assets are used to add new recipes to the game. There are a number of recipe types available. All recipe assets are written in JSON. Types Asset Type - Recipes (Alloy) - Registers new metal alloy combinations. Asset Type - Recipes (Barrel) - Adds recipes that can be made in barrels. Asset Type - Recipes (Clayforming) - Adds recipes that can be made by using clay forming. Asset Type - Recipes (Cooking) - Registers items that can be made within a cooking pot. Asset Type - Recipes (Grid) - Adds recipes that can be made using the inventory crafting grid. Asset Type - Recipes (Knapping) - Adds recipes that can be made by using flint/stone knapping. Asset Type - Recipes (Smithing) - Adds recipes that can be made by using anvil smithing. See Also RecipeLoader on GitHub Content Tutorial Simple Recipe (Grid) Content Tutorial Complex Grid Recipes Content Tutorial Further Recipes Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Recipes_(Alloy) TITLE: Modding:Asset Type - Recipes (Alloy) --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 1.3 Notes 2 See Also Usage Alloy Recipe assets are used to create new types of alloys for mixing metals. Alloy recipe assets are written in JSON. Asset Location New alloy recipe assets can be made by creating new JSON files in a folder called ' recipes/alloy ' in your mod's domain. Json Documentation All JSON properties for alloy recipes are currently documented on the JSON Reference . Notes Alloy recipes are somewhat more complex than other recipes and currently do not have a tutorial. They require items to be added to the correct worldproperties entries, and have a number of correctly configured CombustibleProperties. See Also AlloyRecipes on GitHub Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Recipes_(Barrel) TITLE: Modding:Asset Type - Recipes (Barrel) --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 1.3 Notes 2 See Also Usage Barrel Recipe assets are used to add new recipes that can be made within barrels. Barrel recipe assets are written in JSON. Asset Location New barrel recipe assets can be made by creating JSON files in a folder called ' recipes/barrel ' in your mod's domain. Json Documentation All JSON properties for barrel recipes are currently documented on the JSON Reference . Notes Barrel recipes are generally used for their ability to utilize specific amounts of liquids. See Also BarrelRecipes on GitHub Content Tutorial Further Recipes - Barrel Recipes Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Recipes_(Clayforming) TITLE: Modding:Asset Type - Recipes (Clayforming) --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 2 See Also Usage Clayforming Recipe assets are used to add new recipes that can be made with the clayforming system. Clayforming recipe assets are written in JSON. Asset Location New clayforming recipe assets can be made by creating new JSON files in a folder called ' recipes/clayforming ' in your mod's domain. Json Documentation All JSON properties for clayforming recipes are currently documented on the JSON Reference . See Also Clayforming Recipes on GitHub Content Tutorial Further Recipes - Clayforming Recipes Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Recipes_(Cooking) TITLE: Modding:Asset Type - Recipes (Cooking) --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 1.3 Notes 2 See Also Usage Cooking Recipe assets are used to add new recipes for cooking meals in cooking pots. Cooking recipe assets are written in JSON. Asset Location New cooking recipe assets can be made by creating JSON files in a folder called ' recipes/cooking ' in your mod's domain. Json Documentation All JSON properties for cooking recipes are currently documented on the JSON Reference . Notes Cooking recipes are somewhat more complex than other recipes and currently do not have a content tutorial. See Also Cooking Recipes on GitHub Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Recipes_(Grid) TITLE: Modding:Asset Type - Recipes (Grid) --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 2 See Also Usage Grid Recipe assets are used to add new recipes that can be made within the crafting grid. Grid recipe assets are written in JSON. Asset Location New grid recipe assets can be made by creating JSON files in a folder called ' recipes/grid ' in your mod's domain. Json Documentation All JSON properties for grid recipes are currently documented on the JSON Reference . See Also Grid Recipes on GitHub Content Tutorial Simple Recipe Content Tutorial Complex Grid Recipes Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Recipes_(Knapping) TITLE: Modding:Asset Type - Recipes (Knapping) --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 2 See Also Usage Knapping Recipe assets are used to add new recipes that can be made with the flint/stone knapping system. Knapping recipe assets are written in JSON. Asset Location New knapping recipe assets can be made by creating JSON files in a folder called ' recipes/knapping ' in your mod's domain. Json Documentation All JSON properties for knapping recipes are currently documented on the JSON Reference . See Also Knapping Recipes on GitHub Content Tutorial Further Recipes - Knapping Recipes Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Recipes_(Smithing) TITLE: Modding:Asset Type - Recipes (Smithing) --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Json Documentation 2 See Also Usage Smithing Recipe assets are used to add new recipes that can be made with the anvil smithing system. Smithing recipe assets are written in JSON. Asset Location New smithing recipe assets can be made by creating new JSON files in a folder called ' recipes/smithing ' in your mod's domain. Json Documentation All JSON properties for smithing recipes are currently documented on the JSON Reference . See Also Smithing Recipes on GitHub Content Tutorial Further Recipes - Smithing Recipes Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Shapes TITLE: Modding:Asset Type - Shapes --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Notes 2 See Also Usage Shape assets are used to define 3D models for other assets. Shape assets are written in JSON. Asset Location New shape assets can be made by creating JSON files in a folder called ' shapes ' in your mod's domain. Although not necessary, it is good-practice to split shapes into ' block ', 'item ', and 'entity' subfolders. Notes Although shapes are written in JSON, it is highly recommended to use the official Vintage Story Model Creator to create shape files. See Also CompositeShape on GitHub VSModelCreator Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Sounds TITLE: Modding:Asset Type - Sounds --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Usage Sound assets are audio files that can be played within the game. Sounds assets must be in .ogg format. Asset Location Sound assets can be used in game by adding .ogg files to a folder called ' sounds ' in your mod's domain. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_Textures TITLE: Modding:Asset Type - Textures --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Usage 1.1 Asset Location 1.2 Notes 2 See Also Usage Texture assets are bitmap image files that can be used in game. Texture assets must be in .png format. Icon assets are vector image files that can be used in the UI. Icon assets must be in .svg format. Asset Location Texture and icon assets can be used by adding .png or .svg files respectively to a folder called ' textures ' in your mod's domain. Notes Icon assets are usually coded within the game. Icons can currently only be added to ' textures/icons/worldmap ' without the use of a code mod. See Also CompositeTexture on GitHub - Used to load a texture through a JSON asset. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Type_-_WorldProperties TITLE: Modding:Asset Type - WorldProperties --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Usage World Property assets are used to register sets of variants for blocks, items, and entities. Some world properties are also used within the game's code. World property assets are written in JSON. Asset Location New world property assets can be made by creating JSON files in a folder called ' worldproperties ' in your mod's domain. See Also WorldProperties on GitHub Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Asset_Types_Portal TITLE: Modding:Asset Types Portal --- CONTENT --- Other languages: English русский This is the portal page for the asset types included in the game. These are often made using JSON. Each page will give a list of pages linked to each asset type. Basic Asset Types Block Types Entities Item Types Patches Recipes Alloy Barrel Clayforming Cooking Grid Knapping Smithing Shapes Sounds Textures & Icons World Properties Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Basic_Code_Tutorials TITLE: Modding:Basic Code Tutorials --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . This is the landing page for the basic code tutorials. These will cover the basic introduction of code modding, including how to create commands and complex functionality for blocks and items. Basic Tutorials These tutorials are currently in progress! Code Mod Essentials - This will give you an understanding of various code topics used within the tutorials. It will also give information on how to setup a project to use for the tutorials. Simple Block Class - This will show you how to create, register, and use a block class. Simple Item Class - This will show you how to create, register, and use an item class. Simple Commands - This will show you how to create a server-side command to use in your game. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Code Essentials • Simple Block Class • Simple Item Class • Simple Commands --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Basic_Content_Tutorials TITLE: Modding:Basic Content Tutorials --- CONTENT --- Other languages: English русский This is the landing page for the basic content tutorials. These will cover the basic introduction of modding, including the definitions of shapes, textures, lang files, blocks, items, and recipes. Basic Tutorials Content Essentials - This gives an understand of various topics used throughout the basic content mods. Simple Item Tutorial - Here you will create an item. This will introduce you to using shape, texture, and lang files. Simple Block Tutorial - Here you will create a block with different properties. You will also use texture and lang files here. Simple Recipes Tutorial - Here you will create new recipes for the above item and block. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Content Essentials • Simple Item • Simple Block • Simple Recipe --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Basic_Entity TITLE: Modding:Basic Entity --- CONTENT --- This page is outdated. Reason: This tutorial is outdated and is currently being rewritten. Please use with caution, as it does not function in recent versions. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Other languages: English español русский We highly recommend to read about domains first. This tutorial will cover the basics of adding an entity to the game using JSON files. There is a full list of all properties which can be defined inside the json file here . Contents 1 Little Figure 1.1 EntityType 1.1.1 Common 1.1.2 Client 1.1.3 Server 1.1.4 Result 1.2 Shape 1.3 Item 1.4 Testing/ Distribution Little Figure The idea is create a little passive figure, made out of wood. The modid of our mod will be figure . EntityType Common So first of all we have to create the entity type file assets/figure/entities/land/littlefigure.json . Now we go through all the properties: code : The unique identifier for your entity. A prefix of your mod id will be added automatically. Our case its figure:littlefigure . class : The class of the entity, it can be used to program special features for it. We don't need it at the moment, so we set it to EntityAgent . hitboxSize : The size of the hitbox. "hitboxSize" : { "x" : 0.4 , "y" : 0.5 }, deadHitboxSize : The size of the hitbox when the entity has died. "deadHitboxSize" : { "x" : 0.4 , "y" : 0.5 }, eyeHeight : The height of the eyes, which is 0.4 for the little figure. drops : A list of items to be dropped (can also include chances). We leave it empty for now: [] . sounds : Set the sounds for the entity "hurt" : "creature/figure-hurt" , "death" : "creature/figure-death" , "idle" : "creature/figure-idle" Client The client type has different properties for client and server. This will cover the client side (rendering, shape, texture) (please note that // comments are actually not a valid json syntax, so if you want to use this json code in your projects, you'd have to remove these first) "client" : { "renderer" : "Shape" , // How the entity will be rendered, "Shape" is the right one in almost all cases. "shape" : { "base" : "entity/land/littlefigure" }, // a path to our shape "texture" : { "base" : "game:block/wood/planks/birch1" }, // the figure should have the texture of birch wood. Therefore the prefix "game" is needed "behaviors" : [ { "code" : "repulseagents" }, { "code" : "controlledphysics" , // Adds physics to the entity "stepHeight" : 0.2 }, { "code" : "floatupwhenstuck" , // Fixes corpse get stuck in ground "onlyWhenDead" : true }, { "code" : "interpolateposition" } // Smooths out movement of entity ], "animations" : [ // Animations which have to be implemented by the shape as well { "code" : "hurt" , "animation" : "hurt" , "animationSpeed" : 2.2 , "weight" : 10 , "blendMode" : "AddAverage" }, { "code" : "die" , "animation" : "die" , "animationSpeed" : 1.25 , "weight" : 10 , "blendMode" : "AddAverage" } ] }, Server The server side handles the "brain" of the entity. "server" : { "behaviors" : [ { "code" : "repulseagents" }, { "code" : "controlledphysics" , // Adds physic interaction "stepHeight" : 0.2 }, { "code" : "health" , // Adds a health bar to the entity "currenthealth" : 4 , "maxhealth" : 4 }, { "code" : "deaddecay" , // Makes the dead entity stay for one hour "hoursToDecay" : 1 }, { "code" : "floatupwhenstuck" , // Fixes corpse get stuck in ground "onlyWhenDead" : true }, { "code" : "despawn" , // Makes the entity despawn if there is no player within 48 blocks "minPlayerDistance" : 48 , "minSeconds" : 5 }, { "code" : "emotionstates" , // Adds different emotion states "states" : [ { "code" : "fleeondamage" , // After the entity is hit it will try to flee for 10 seconds "duration" : 10 , "chance" : 0.2 , "slot" : 0 , "priority" : 1 , "accumType" : "max" } ] }, { "code" : "taskai" , // Handles what the entity does, a task will be processed one at a time "aitasks" : [ { "code" : "idle" , // the figure will stand still and think "priority" : 1.2 , "priorityForCancel" : 1.35 , "minduration" : 4000 , "maxduration" : 6000 , "chance" : 0.001 , "initialMinCoolDown" : 2000 , "initialMaxCoolDown" : 150000 , "mincooldown" : 300000 , "maxcooldown" : 10000000 , "animation" : "think" , "animationSpeed" : 1.25 }, { "code" : "wander" , // The entity will walk around "priority" : 1.0 , "movespeed" : 0.008 , "animationSpeed" : 1.6 , "animation" : "run" , "preferredLightLevel" : 15 }, { "code" : "lookaround" , // The entity will look around "priority" : 0.5 } ] } ] }, Result If we put everything together it should look like this: { "code" : "littlefigure" , "class" : "EntityAgent" , "hitboxSize" : { "x" : 0.4 , "y" : 0.5 }, "deadHitboxSize" : { "x" : 0.4 , "y" : 0.5 }, "eyeHeight" : 0.4 , "drops" : [], "client" : { "renderer" : "Shape" , "shape" : { "base" : "entity/land/littlefigure" }, "texture" : { "base" : "game:block/wood/planks/birch1" }, "behaviors" : [ { "code" : "repulseagents" }, { "code" : "controlledphysics" , "stepHeight" : 0.2 }, { "code" : "floatupwhenstuck" , "onlyWhenDead" : true }, { "code" : "interpolateposition" } ], "animations" : [ { "code" : "hurt" , "animation" : "hurt" , "animationSpeed" : 2.2 , "weight" : 10 , "blendMode" : "AddAverage" }, { "code" : "die" , "animation" : "die" , "animationSpeed" : 1.25 , "weight" : 10 , "blendMode" : "AddAverage" } ] }, "server" : { "behaviors" : [ { "code" : "repulseagents" }, { "code" : "controlledphysics" , "stepHeight" : 0.2 }, { "code" : "health" , "currenthealth" : 4 , "maxhealth" : 4 }, { "code" : "deaddecay" , "hoursToDecay" : 1 }, { "code" : "floatupwhenstuck" , "onlyWhenDead" : true }, { "code" : "despawn" , "minPlayerDistance" : 48 , "minSeconds" : 5 }, { "code" : "emotionstates" , "states" : [ { "code" : "fleeondamage" , "duration" : 10 , "chance" : 0.2 , "slot" : 0 , "priority" : 1 , "accumType" : "max" } ] }, { "code" : "taskai" , "aitasks" : [ { "code" : "idle" , "priority" : 1.2 , "priorityForCancel" : 1.35 , "minduration" : 4000 , "maxduration" : 6000 , "chance" : 0.001 , "initialMinCoolDown" : 2000 , "initialMaxCoolDown" : 150000 , "mincooldown" : 300000 , "maxcooldown" : 10000000 , "animation" : "think" , "animationSpeed" : 1.25 }, { "code" : "wander" , "priority" : 1.0 , "movespeed" : 0.008 , "animationSpeed" : 1.6 , "animation" : "run" , "preferredLightLevel" : 15 }, { "code" : "lookaround" , "priority" : 0.5 } ] } ] }, "sounds" : { "hurt" : "creature/figure-hurt" , "death" : "creature/figure-death" , "idle" : "creature/figure-idle" } } Shape If you want to know how to create the shape for the entity I suggest you checkout VS Model Creator . Item In order to spawn the entity we can create an item to do so: { "code" : "creature" , "class" : "ItemCreature" , "maxstacksize" : 64 , "variantgroups" : [ { "code" : "type" , "states" : [ "littlefigure" ] } ], "shape" : { "base" : "figure:entity/land/littlefigure" }, "texture" : { "base" : "game:block/wood/planks/birch1" }, "creativeinventory" : { "general" : [ "*" ], "items" : [ "*" ], "creatures" : [ "*" ] }, "materialDensity" : 600 , "guiTransform" : { "rotation" : { "x" : 0 , "y" : - 90 , "z" : - 180 }, "origin" : { "x" : 0.5 , "y" : 0.15 , "z" : 0.5 }, "scale" : 6 }, "fpHandTransform" : { "rotation" : { "x" : 0 , "y" : - 90 , "z" : 0 }, "origin" : { "x" : 0.5 , "y" : 0.15 , "z" : 0.5 }, "scale" : 6 }, "groundTransform" : { "translation" : { "x" : 0 , "y" : 0.15 , "z" : 0 }, "rotation" : { "x" : 0 , "y" : - 90 , "z" : 0 }, "origin" : { "x" : 0.5 , "y" : 0.15 , "z" : 0.5 }, "scale" : 6 } } Testing/ Distribution Download Figure v1.0.0 for VintageStory 1.8 Content Modding Basics Content Mods • Developing a Content Mod • Packaging & Release Tutorials Basic Basic Tutorials • 1. Content Essentials • 2. Simple Item • 3. Simple Block • 4. Simple Recipes Intermediate Intermediate Tutorials • 5. Item Variants • 6. Block Variants • 7. Complex Grid Recipes • 8. Further Recipes • 9. Simple World Generation Advanced Advanced Tutorials Other Other Tutorials • Debugging Content • Inter-Mod Compatibility Concepts Modding Concepts • Modinfo • Variants • Domains • Patching • Remapping • World Properties Moddable Assets Common Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties Uncategorized Basics Asset System • Recipes • Entities • Model Creator • Animation • VTML & Icons Worldgen WorldGen Concepts • Terrain • Ores • Trees • Json Random Generator Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Basic_Inventory_Handling TITLE: Modding:Basic Inventory Handling --- CONTENT --- Other languages: English русский This page is a candidate to be rewritten. This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. This page was last verified for Vintage Story version 1.19.8 . Overview The most important classes for inventory management are: InventoryManager: Contains multiple inventories (every player has one with different inventories). Also helps with common tasks like putting items into a valid inventory. IInventory: Represents an item inventory (for example the players hotbar). Contains multiple ItemSlots. ItemSlot: Represents a specific slot (for example the offhand slot) of an inventory. Contains the ItemStack of the slot and Methods to help transfer items to and from that slot. ItemStack: Contains the Item and amount of items (called StackSize), e.g. "block torch-up" and StackSize of 2, for two Torches. Basic Inventory Handling To get access to a players inventory, you can use the InventoryManager of the IPlayer. For example, to simply give the player a torch when joining you could use TryGiveItemStack : private ICoreServerAPI serverApi ; public override void StartServerSide ( ICoreServerAPI api ) { serverApi = api ; serverApi . Event . PlayerJoin += EventOnPlayerJoin ; } private void EventOnPlayerJoin ( IServerPlayer player ) { ItemStack torch = new ItemStack ( serverApi . World . GetBlock ( new AssetLocation ( "torch-basic-lit-up" ))); player . InventoryManager . TryGiveItemstack ( torch ); } If we want to put it directly into the offhand we can use player.Entity.LeftHandItemSlot : ItemStack torch = new ItemStack ( serverApi . World . GetBlock ( new AssetLocation ( "torch-basic-lit-up" ))); ItemSlot offhandSlot = player . Entity . LeftHandItemSlot ; if ( offhandSlot ?. Empty == true ) { offhandSlot . Itemstack = torch ; offhandSlot . MarkDirty (); //this is needed because otherwise the client does not get the update } If you need access to other inventories like e.g. the backpack inventories, you can use the InventoryManager.Inventories Property. This contains a dictionary with the name of the inventory (for example "hotbar-APlayerUID") and the corresponding inventory. Another cleaner looking way if you only need a single inventory would be the InventoryManager.GetOwnInventory() method, which appends the player uid to the classname and returns the inventory. To avoid errors when typing the classnames, check first if the classname is already in the GlobalConstants . Because backpack inventory contains the bags as well as the slots, you have to check if it is valid to put the item there. For this you can use the ItemSlot.CanHold() method. If for example you want to fill every empty slot in the backpack with single torches you could do the following: Block torchBlock = serverApi . World . GetBlock ( new AssetLocation ( "torch-basic-lit-up" )); IInventory backpack = player . InventoryManager . GetOwnInventory ( GlobalConstants . backpackInvClassName ); if ( backpack != null ) { var dummySlot = new ItemSlot ( null ) { Itemstack = new ItemStack ( torchBlock ) }; foreach ( ItemSlot bag in backpack . Where ( b => b . CanHold ( dummySlot ) && b . Empty )) { bag . Itemstack = new ItemStack ( torchBlock ); bag . MarkDirty (); } } Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Block_Classes TITLE: Modding:Block Classes --- CONTENT --- This page is outdated. Reason: A new system is being worked on to view JSON properties. Please use this page with caution, as it may not be up-to-date. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Using block classes Sometimes a block requires additional functionality that can't be accomplished with a JSON. Doing so requires a block to utilize a C# class, which uses the following format in the JSON: class: "BlockClass" A majority of all block classes can be viewed here in the Vintage Story Github for those looking to learn more about how each works. Additionally, mechanical blocks classes are stored here . All Block Classes Here is a table containing all classes used by blocks as of version 1.9.+ Class Name Example Blocks Description Github Link Block most regular blocks A standard block class that almost all blocks utilize or inherit. Block Class BlockAnvil anvil Used by the anvil Anvil Class BlockBamboo bamboo - Bamboo Class BlockBarrel barrel - Barrel Class BlockBed bed - Bed Class BlockBeehive wildbeehive - Wild Beehive Class BlockBigBerryBush smallberrybush, largeberrybush - Berry Bush Class BlockBloomery bloomerybase - Bloomery Class BlockBomb oreblastingbomb - Bomb Class BlockBowl bowl - Bowl Class BlockBucket bucket - Bucket Class BlockBunchOCandles bunchocandles - Candle Class BlockCactus saguarocactus - Cactus Class BlockCanvas canvas - Canvas Class BlockChandelier chandelier - Chandelier Class BlockChisel chiseledblock - Chiseled Class BlockClayForm clayform - Clay Form Class BlockCookedContainer pot A container that has finished cooking its contents and is ready to be taken from etc. Meal Container BlockCookingContainer pot A container that has not finished cooking its contents (if any). Meal Container BlockCrock crock - Meal Container BlockCrystal crystallargecluster, crystalsmall - Crystal Class BlockDisplayCase displaycase - Display Case Class BlockDoor door, irondoor - Door Class BlockEchoChamber echochamber - Echo Chamber Class BlockFarmland farmland - Farmland Class BlockFence fence, ironfence Used to change the shape based off of nearby attachable surfaces. Requires shape variants for every horizontal orientation. Fence Class BlockFenceGate fencegate - Fence Gate Class BlockFirepit firepit - Firepit Class BlockFirewoodPile firewoodpile - Firewood Pile Class BlockFlowerPot flowerpot, planter - Flower Pot Class BlockForge forge - Forge Class BlockFullCoating saltpeter Used to make a block which coats all flat surfaces when placed. Requires shape variants for every possible orientation. Full Coating Class BlockGenericTypedContainer chest, storagevessel, stationarybasket - Generic Container Class BlockGlowworms glowworms - Glowworms Class BlockHopper hopper - Hopper Class BlockIngotMold ingotmold - Ingot Mold Class BlockIngotPile ingotpile - Ingot Pile Class BlockKnappingSurface knappingsurface - Knapping Surface Class BlockLabeledChest chest-labeled - Labeled Chest Class BlockLantern lantern - Lantern Class BlockLava lava - Lava Class BlockLayered snow A generic class that can be layered. Requires a variant for each height. Layered Block Class BlockLayeredSlowDig charcoalpile A version of the layered class that is much slower to dig. Layered Block Class (slow dig) BlockLeaves leaves, leavesbranchy - Leaves Class BlockLocustNest cage (locustnest) - Locust Nest Class BlockLog log Only contains code for OnPickBlock method. Lava Class BlockLooseGears loosegears - Loose Gears Class BlockLooseOres looseores - Loose Ores Class BlockLooseStones loosestones - Loose Stones Class BlockLootVessel lootvessel - Loot Vessel Class BlockLupine flower-lupine - Lupine Flower Class BlockMeal bowl-meal Gives a block the ability to store meal data. Meal Container BlockMetalPartPile partpile, partpile-wall - Part Pile Class BlockMetalSpikes metalspikes (locustnest) - Metal Spikes Class BlockMeteorite meteorite - Meteorite Class BlockMushroom mushroom - Mushroom Class BlockOre ore-graded, ore-ungraded - Ore Class BlockPan pan - Pan Class BlockPeatbrick peatbrick - Peat Brick Class BlockPeatPile peatpile - Peat Pile Class BlockPlaceOnDrop meteorite - Place on Drop Class BlockPlant flower, frostedtallgrass, herb, plaintreesapling, tallfern, tallgrass - Plant Class BlockPlatePile platepile - Plate Pile Class BlockPlankPile plankpile - Plank Pile Class BlockQuern quern - Quern Class BlockRails rails - Rails Class BlockReeds reeds-free, reeds-water - Meteorite Class BlockRequireSolidGround barrelcactus - Requires Solid Ground Class BlockReeds reeds-free, reeds-water - Meteorite Class BlockSeaweed seaweed - Seaweed Class BlockShelf shelf - Shelf Class BlockSign sign - Sign Class BlockSignPost signpost - Sign Post Class BlockSimpleCoating linen, sheet (metal), wool Allows a thin, flat block to be placed on a single side of a surface. Requires directional variants for each orientation. Simple Coating Class BlockSkep skep - Skep Class BlockSmeltedContainer crucible - Smelted Container Class BlockSmeltingContainer crucible - Smelting Container Class BlockSnow snow - Snow Class BlockSoil soil - Soil Class BlockSoilDeposit clay, peat Used to generate a soil type and then generate soil beneath it to prevent gaps formation. Soil Deposit Class BlockSpawner meta - Entity Spawner Class BlockStairs All stairs Allows stair blocks to be placed in different orientations. Requires variants for each orientation. Stairs Class BlockStalagSection stalagsection - Stalag Section Class BlockStaticTranslocator statictranslocator - Static Translocator Class BlockTeleporter teleporterbase - Teleporter Class BlockThermalDifference - - Thermal Difference Class BlockToolMold toolmold - Tool Mold Class BlockTorch torch - Torch Class BlockTorchHolder torchholder - Torchholder Class BlockTrough trough-small - Trough Class BlockTroughDoubleBlock trough-large - Double Block Trough Class BlockVines wildvine - Vine Class BlockWater water - Water Class BlockWaterflowing water - Flowing Water Class BlockWaterLily waterlily - Water Lily Class BlockWaterPlant water - Water Plant Class BlockWaterfall water - Waterfall Class BlockWateringCan wateringcan - Wateringcan Class BlockWithGrassOverlay - - Grass Overlay Class Mechanical Power Block Classes This table contains all the blocks relevant to mechanical power. Class Name Example Blocks Description Github Link BlockAngledGears angledgears - Angled Gears Class BlockAxle axle - Axle Class BlockBrake brake - Brake Class BlockClutch clutch - Clutch Class BlockHelveHammer helvehammerbase - Helve Hammer Class BlockToggle toggle - Toggle Class BlockTransmission transmission - Transmission Class BlockWindmillRotor windmillrotor Controls the placement of the rotor and determines what happens when the player tries to add sails to it. Windmill Rotor Class Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Block_Entity TITLE: Modding:Block Entity --- CONTENT --- Other languages: English русский This page is a candidate to be rewritten. This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. This page was last verified for Vintage Story version 1.19.8 . Contents 1 Introduction 1.1 The Texture Flipper 1.2 The BlockEntity 1.3 Registering the Block Entity 1.4 Testing 1.5 Distribution 1.5.1 Using the new Mod Template 1.5.2 Using the (old) Modtools 2 Mod Download Introduction A block entity is a construct that you can tack onto an existing block to give it additional functionality. Whenever a block should do something on a regular interval or store extra information, such as the contents of a chest block, you need a block entity. It's highly recommend to have read the tutorial about Basic Blocks and Block Class in order to understand this tutorial properly. The Texture Flipper Let's create a block which switches its texture every 3 seconds. It should have two variants "on" and "off" . Additionally we need to define the blockentity class like so: e nt i t yClass : "tickingcounter" , You can download the assets here and place it in your mods directory. The BlockEntity Now we need to register our blockentity class and therefore we need to create a new *.cs file in our project. Let's name it Ticking.cs . First of all you need to create the blockentity class itself. Therefore you need to extend BlockEntity : public class TickingBlockEntity : BlockEntity { } This class needs to have a timer, once the timer reaches 3 seconds it should replace the current block with the different state. In order to create a timer we need to register a tick listener. Therefore we need to override Initialize(ICoreAPI) : public override void Initialize ( ICoreAPI api ) { base . Initialize ( api ); } add a counter (which should increase per tick) ... public float timer ; ... and the actual ticking method ... public void OnGameTick ( float dt ) { } To register the ticking method we can use RegisterGameTickListener in Initialize public override void Initialize ( ICoreAPI api ) { base . Initialize ( api ); RegisterGameTickListener ( OnGameTick , 50 ); } The timer itself should increment by dt, the time difference in seconds between the current tick and the previous tick. It ticks about every 50ms or less often if the game is slow. So if the timer is greater than 3, it should replace the block: public void OnGameTick ( float dt ) { timer += dt ; if ( timer >= 3 ) { Block block = Api . World . BlockAccessor . GetBlock ( Pos ); if ( block . Code . Path . EndsWith ( "-on" )) { block = Api . World . GetBlock ( block . CodeWithParts ( "off" )); } else { block = Api . World . GetBlock ( block . CodeWithParts ( "on" )); } Api . World . BlockAccessor . SetBlock ( block . BlockId , Pos ); } } Furthermore we need to save the current time: public override void ToTreeAttributes ( ITreeAttribute tree ) { base . ToTreeAttributes ( tree ); tree . SetFloat ( "timer" , timer ); } public override void FromTreeAttributes ( ITreeAttribute tree , IWorldAccessor worldForResolving ) { base . FromTreeAttributes ( tree , worldForResolving ); timer = tree . GetFloat ( "timer" ); } Registering the Block Entity Registering the blockentity class is rather simple (rather similar to registering a block class). You need a mod class and override Start(ICoreAPI) : public class Ticking : ModSystem { public override void Start ( ICoreAPI api ) { base . Start ( api ); api . RegisterBlockEntityClass ( "tickingcounter" , typeof ( TickingBlockEntity )); } } Testing Now everything is ready to run the first test: Distribution Using the new Mod Template If using the mod template setup, follow the instructions on Setting up your Development Environment to pack and distribute your mod. Using the (old) Modtools If using the modtools program, open the modtools and type in pack . Now you can take the zip file and share it with other people. It will work in the same way as ordinary mods, you can install it by copying it into the mods folder. Mod Download for VS 1.12 (Source only): GitHub for VS 1.9: Ticking_v1.0.0.zip for VS 1.6: Ticking.zip Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Block_Entity_Behaviors TITLE: Modding:Block Entity Behaviors --- CONTENT --- This page is outdated. Reason: A new system is being worked on to view JSON properties. Please use this page with caution, as it may not be up-to-date. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Using block Entity Behaviors Block entity behaviors are an expanded type of behavior that block entities can utilize. At the moment, these are mostly used by mechanical power blocks. If you're looking for regular block behaviors (which are used differently) they can be found here . The block entity behaviors are added to the entityBehaviors array of the block json file at the top level (same level as code ). Some behaviors accept a properties dictionary. The properties dictionary is optional for the majority of behaviors that have no properties. If the block does not set the entityclass field, then adding any block entities will cause it to default to "generic", thus transforming the block type into a block entity. Example: { code : "lightningrod" , ... e nt i t yBehaviors : [ { "name" : "AttractsLightning" , "properties" : { "artificialElevation" : 5 , "elevationAttractivenessMultiplier" : 2 } } ], ... } Most block entity behavior classes can be viewed at the Vintage Story Github here for those looking to learn more about each. All Block Entity Behaviors Here is a table containing all classes used by blocks as of version 1.19.8 Class Name Example Blocks Description Github Link Animatable brake, clutch - Animatable Block Entity Behavior Class AttractsLightning Burning ClutterBookshelf ClutterBookshelfWithLore ControlPointAnimatable ControlPointLampNode Door FirepitAmbient Fruiting JonasBoilerDoor JonasGasifier JonasHydraulicPump MicroblockSnowCover MPAngledGears angledgears - Angled Gears Block Entity Behavior Class MPArchimedesScrew MPAxle axle - Axle Block Entity Behavior Class MPBase - A generic mechanical power class that's used by most other behaviors. Otherwise this is not generally used in JSONS. Base Block Entity Behavior Class MPBrake brake - Brake Block Entity Behavior Class MPConsumer quern - Consumer Block Entity Behavior Class MPCreativeRotor MPLargeGear3m MPPulverizer MPToggle toggle - Block Toggle Entity Behavior Class MPTransmission transmission - Transmission Block Entity Behavior Class MPWindmillRotor windmillrotor - Windmill Block Entity Behavior Class RockRubbleFromAttributes ShapeFromAttributes SupportBeam Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Block_Entity_Classes TITLE: Modding:Block Entity Classes --- CONTENT --- This page is outdated. Reason: A new system is being worked on to view JSON properties. Please use this page with caution, as it may not be up-to-date. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Using block Entity classes May blocks that have interactive functions will require using a block entity to accomplish. When your block requires the use of a block entity you assign it using the following property format in the JSON: entityClass: "Entity" NOTE: Although they are referenced in JSONS as "Anvil", "Bed" etc. Block Entity class files themselves use the BEAnvil, BEBed naming convention. A majority of all block entities can be viewed at the Vintage Story Github here for those looking to learn more about each. Additionally, all mechanical block entities can be found here . All Block Entities Here is a table containing all block entities used as of version 1.9.+z Entity Name Example Block JSON Description Github Link Anvil anvil Controls the rendering and forging of work items on an anvil. Anvil Block Entity Class Barrel barrel - Barrel Block Entity Class Bed bed - Bed Block Entity Class Beehive wildbeehive - Beehive Block Entity Class Bellows - Unfinished Bellows Block Entity Class BerryBush smallberrybush, largeberrybush - Berry Bush Block Entity Class BlastFurnace - Unfinished Blast Furnace Block Entity Class Bloomery bloomerybase - Bloomery Block Entity Class Bomb oreblastingbomb - Bomb Block Entity Class Bucket bucket - Bucket Block Entity Class Canvas canvas - Canvas Block Entity Class CharcoalPit charcoalpit - Charcoal Pit Block Entity Class Chisel chiseledblock - Chiseled Block Entity Class Chute chute - Chute Block Entity Class ClayForm clayform - Clay Form Block Entity Class Crock crock - Crock Block Entity Class DisplayCase displaycase - Display Case Block Entity Class EchoChamber echochamber - Echo Chamber Block Entity Class Farmland farmland - Farmland Block Entity Class Fire - - Fire Block Entity Class FireWoodPile firewoodpile - Firewood Pile Block Entity Class Firepit firepit - Firepit Block Entity Class Forge forge - Forge Block Entity Class GenericContainer - - Generic Container Block Entity Class GenericTypedContainer chest, storagevessel, stationarybasket - Typed Generic Container Block Entity Class IngotMold ingotmold - Ingot Mold Block Entity Class IngotPile ingotpile - Ingot Pile Block Entity Class ItemFlow - - Flowing Item Block Entity Class KnappingSurface knappingsurface - Knapping Surface Block Entity Class LabeledChest chest-labeled - Labeled Chest Block Entity Class Lantern lantern - Lantern Block Entity Class LocustNest cage (locustnest) - Locust Nest Block Entity Class Meal bowl-meal - Meal Block Entity Class PeatPile peatpile - Peat Pile Block Entity Class PlankPile plankpile - Plank Pile Block Entity Class PlatePile platepile - Plate Pile Block Entity Class PumpkinVine vine (pumpkin) - Pumpkin Vine Block Entity Class Quern quern - Quern Block Entity Class Sapling plaintreesapling - Sapling Block Entity Class Shelf shelf - Shelf Block Entity Class Sign sign - Sign Block Entity Class SignPost signpost - Signpost Block Entity Class SmeltedContainer crucible - Smelted Container Block Entity Class Spawner meta - Spawner Block Entity Class StaticTranslocator statictranslocator - Static Translocator Block Entity Class Stove - Unfinished Stove Block Entity Class Tapestry tapestry - Tapestry Block Entity Class Teleporter teleporter - Teleporter Block Entity Class ToolMold toolmold - Tool Mold Block Entity Class ToolRack toolrack - Tool Rack Block Entity Class Torch torch (including colored version) - Torch Block Entity Class Trough trough-small, trough-large - Trough Block Entity Class WateringCan wateringcan - Watering Can Block Entity Class Mechanical Power Block Entity Classes This table contains all the block entities relevant to mechanical power. Class Name Example Blocks Description Github Link Brake brake Controls the rotation of the brake axle and events that occur when the player interacts with it. Brake Block Entity Class Clutch clutch - Clutch Block Entity Class HelveHammer helvehammerbase - Helve Hammer Class Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Block_Json_Properties TITLE: Modding:Block Json Properties --- CONTENT --- Other languages: English español русский This page is outdated. Reason: All block json properties can now be viewed on the JSON Reference The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Overview This table was once complete, but it is now missing some new properties. In the code, these come from the fields listed with the "[JsonProperty]" attribute in BlockType.cs , plus the fields from the parent classes CollectibleType.cs and RegistryObjectType.cs . The json parser is case insensitive, which is how the property names can have different capitalization in the code and in the table below. Definitions: Key - The name of the property, which should be used as it appears in the column. Value - A component of a property. Array - A list of objects or values that can be referenced in code. String - A sequence of characters that can be used as an identifier in code. Essentially a word that can be referenced and assigned to something. Generally does not use numbers. Boolean - A true or false value, essentially "on" or "off". Int - An integer, or whole number. Cannot use decimal values. Float - A decimal, specifically one that does not exceed more than 1 significant digit Object - This is a bit more complex, but essentially objects are the items, blocks and entities that can be interacted with. In most cases, when an "object" type appears it means you must use a specific item variant code, which follows the "itemcode-variant_1-variant_2-variant_n" style of naming. Property Type Default Usage Reference json Core (no byType available) code string required A unique identifier for the block. Any Block A domain prefix will be added dynamically depending on the location of the file. Every mod and VintageStory itself have a unique prefix. For example the code stone turns into game:stone . The code identifier has to be unique inside its domain. In theory there could be equal identifiers with different domain prefixes. Find out more about Domains . enabled boolean true If the block will be loaded or not. Can be used to temporarily remove the block. - variantgroups array of objects - Allows you define multiple variants of the same block. armor, ore-graded, plank, (any) bytype key: string; value: object - Allows different property values to be specified based on the variant code. allowedVariants array of strings - Used to trim unnecessary blocks generated by combined variants. crystalizedore-graded, ore-graded, ore-ungraded skipVariants array of strings - Similar to allowedVariants, but instead skips the creation of listed variants rather than assigning which are allowed. armor Specific class string "block" The block class can add special functionalities for the block. anvil, firepit, quern, skep It can be used to open guis or adding other extra functionality to the block. A complete tutorial of how to add your own class to the game can be found here . An ongoing list of block classes used in JSONS can also be found here . entityclass string - The block entity class is able to tick and to store extra data. Allows for much more advanced properties. anvil, firepit, quern, skep behaviors array of object - A behavior adds custom abilities such as falling block. gravel, lantern, log, rock, torch Behaviors are useful traits as many can be assigned to a single block. If you want to create your own custom behavior you can read Adding Block Behavior . To see all of the current behaviors in the game see All Block Behaviors and All Collectible Behaviors . entityBehaviors array of object - An entity behavior adds custom abilities to an entity assigned to a block, such as the mechanical power properties of the windmill. angledgears, axle, brake, clutch, helvehammerbase, toggle, transmission, windmillrotor Block entity behaviors are expanded capabilities that regular behaviors cannot accomplish. At the moment they are only used for Mechanical Power blocks, but if you're looking to make your own you can look through the existing files at the Vintage Story Github here . To see all of the current block entity behaviors in the game see All Block Entity Behaviors . A chest for example uses the BlockEntity to store the inventory. A tutorial of creating your own entityclass can be found here . You can also find every existing block entity and their relevant github links here . blockmaterial string - A behavior adds custom abilities such as falling block. gravel Materials are hardcoded and currently only used to determine mining speed with a specific tool. The following materials are available: Brick, Ceramic, Cloth, Fire, Glass, Gravel, Ice, Lava, Leaves, Liquid, Mantle, Metal, Other, Plant, Sand, Snow, Soil, Stone, Wood matterstate array of object "block" Determines whether the block is in a solid , a liquid , a gas or a plasma state. water Used for special collision behavior and rendering. Currently used for lava and water, which are liquids (All other blocks default to solid). Gas and plasma are not yet implemented. resistance decimal number 6 How long it takes to break this block in seconds (with a mining speed of 1). leaves, meteorite, sand Resistance values range from very low with leaves (0.5) to the average of stone (6) to the very high resistance value of meteoric iron deposits (60). requiredminingtier integer 0 Minimum required mining tier to get the drop out of the block. ore-graded, rock The following are examples of the mining tiers required for certian ores in Vintage Story: Tier Ores 1 galena and rocksalt_sylvite 2 lignite , cassiterite , sphalerite , rocksalt , sulfur and nativecopper 3 bituminouscoal , quartz_nativegold , quartz_nativesilver , lapislazuli , bismuthinite , quartz , magnetite and limonite 4 diamond and emerald 5 chromite , platinum and ilmenite climbable boolean false If true, walking against this block will make the player climb (used for ladders). ladder rainpermeable boolean false If rain can fall through this block. torch, torchholder snowcoverage boolean - Whether snow may rest on top of this block. leavesbranchy All non-solid blocks can't be covered by snow unless it's defined different: Leaves (also branchy): true , Water with particles, Lakeice: false collisionbox object box (0,0,0 -> 1,1,1) Defines a box with which the player collides with. carcass, door, fence, all slabs A half slab for example, has either a box going from 0,0,0 to 1,0.5,1 or going from 0,0.5,0 to 1,1,1, depending on whether it is a slab is down or up: collisio n boxByType : { "*-down" : { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 0.5 , z 2 : 1 }, "*-up" : { x 1 : 0 , y 1 : 0.5 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 1 } }, Collision and selection boxes are most likely equal. Also, setting the entire property to null will eliminate the collision box entirely. collisionboxes array of object - Defines multiple boxes with which the player collides with. all stairs, crate A crate for example requires multiple collision boxes: collisio n boxesByType : { "*-opened" : [ { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 0.0625 , z 2 : 1 }, { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 0.0625 }, { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 0.0625 , ro tate Y : 90 }, { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 0.0625 , ro tate Y : 180 }, { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 0.0625 , ro tate Y : 270 }, ] }, collisionSelectionBoxes array of object - Simultaneously defines multiple boxes with which the player collides with and can select. all stairs A crate for example requires multiple collision boxes: collisio n Selec t io n BoxesByType : { "*-down-*" : [ { x 1 : 0 , y 1 : 0.5 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 1 }, { x 1 : 0 , y 1 : 0 , z 1 : 0.5 , x 2 : 1 , y 2 : 0.5 , z 2 : 1 , ro tate YByType : { "*-north" : 180 , "*-east" : 90 , "*-south" : 0 , "*-west" : 270 , } } ], "*-up-*" : [ { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 0.5 , z 2 : 1 }, { x 1 : 0 , y 1 : 0.5 , z 1 : 0.5 , x 2 : 1 , y 2 : 1 , z 2 : 1 , ro tate YByType : { "*-north" : 180 , "*-east" : 90 , "*-south" : 0 , "*-west" : 270 , } } ], }, selectionbox object box (0,0,0 -> 1,1,1) Defines a box which the player's mouse pointer collides with for selection. carcass, door, fence, all slabs A half slab for example, has either a box going from 0,0,0 to 1,0.5,1 or going from 0,0.5,0 to 1,1,1, depending on whether it is a slab is down or up: selec t io n boxByType : { "*-down" : { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 0.5 , z 2 : 1 }, "*-up" : { x 1 : 0 , y 1 : 0.5 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 1 } }, Collision and selection boxes are most likely equal. selectionboxes array of object - Defines multiple boxes which the player's mouse pointer collides with for selection. carcass, door, fence, all slabs A crate for example requires multiple selection boxes: selec t io n boxesByType : { "*-opened" : [ { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 0.0625 , z 2 : 1 }, { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 0.0625 }, { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 0.0625 , ro tate Y : 90 }, { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 0.0625 , ro tate Y : 180 }, { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 1 , z 2 : 0.0625 , ro tate Y : 270 }, ] }, replaceable integer 0 A value usually between 0-9999 that indicates which blocks may be replaced with others. bigberrybush, tallgrass, water value effect (blocks) 0 ordinary blocks (stone for example) 5000 Everything equal or above will be washed away by water (such as Fruit ). 6000 Everything equal or above wwill replaced when the player tries to place a block (such as Tallgrass ). 9000 Lava 9500 Water 9999 Air fertility integer 0 Which plants can grow on top of this block. clay, gravel, sand, soil value effect 0 (rock) nothing can grow. 10 (sand) some tallgrass and small trees can be grow on it. 100 (soil) all grass and trees can grow on it. lightabsorption 0 ... 32 0 For light blocking blocks. Any value above 32 will completely block all light. full-plain (glass), water walkspeedmultiplier decimal number 1.0 Percentage walk-speed when standing on or inside this block. path, spiderweb, stonepath blocks value Spiderweb 0.25 Stonepath 1.15 dragmultiplier decimal number 1.0 Drag multiplier applied to entities standing on it (slipperiness factor). Glacierice, Lakeice blocks value Glacierice , Lakeice 0.02 drops array of object - The items that should drop from breaking this block. crops, skep Drop itself If this property does not exist the block will drop itself. No drop A firepit for example doesn't drop anything. You can do so if you specify an empty array: drops : [], Special drop You can also specify a special item/ block. Therefore you need to define an ItemStack , with the given properties: property default explanation type block Can either be block or item . code (required) - The complete code (can also include domain) of the item or block. lastdrop false If true and the quantity dropped is >=1 any subsequent drop in the list will be ignored. attributes - Tree Attributes that will be attached to the resulting itemstack. tool - If specified then given tool is required to break this block. quantity - (one) Determines the quantity of items which will be dropped. For example, the drop of a charcoalpile looks like this: drops : [ { t ype : "item" , code : "charcoal" } ], Tallgrass will only drop something if it's mined by a knife: drops : [ { t ype : "item" , code : "drygrass" , t ool : "knife" }, ], Chance drops Let's take a look at an example. This is the drop property of rock: drops : [ { t ype : "item" , code : "stone-{rock}" , qua nt i t y : { avg : 2.5 , var : 0.5 } }, ] This will drop 2-3 blocks. avg : Stands for the default drop quantity. If var is 0 or not specified it will always drop the given average. var : How much the drop rate can vary. Meaning the drop rate can be avg - var at minimum and age + var at maximum. For more information see NatFloat page. Multiple Drops Of course you can also define multiple drops at once. Sapling can drop a sapling and a stick: drops : [ { t ype : "block" , code : "sapling-{wood}" , qua nt i t y : { avg : 0.02 , var : 0 }, }, { t ype : "item" , code : "stick" , qua nt i t y : { avg : 0.02 , var : 0 }, } ], Last Drop In order to add a special drop, which (if dropped) prevents all other drops, you can use the lastDrop property: dropsByType : { "ore-quartz-*" : [ { t ype : "item" , code : "clearquartz" , qua nt i t y : { avg : 0.2 , var : 0 }, las t Drop : true }, { t ype : "item" , code : "ore-{ore}" , qua nt i t y : { avg : 1.25 , var : 0 } } ], "*" : [ { t ype : "item" , code : "ore-{ore}" , qua nt i t y : { avg : 1.25 , var : 0 } } ], } Quartz ore will drop with a 20% chance clearquartz, if not it will drop the regular ore. If lastDrop wouldn't be true it could drop both at the same time. particleproperties array of object - Particles that should spawn in regular intervals from this block. torch> The torch is a good example of how to use those particles ... par t icleProper t ies : [ { hsvaColor : [{ avg : 20 , var : 20 }, { avg : 255 , var : 50 }, { avg : 255 , var : 50 }, { avg : 255 , var : 0 }], gravi t yE ffe c t : { avg : 0 , var : 0 }, posO ffset : [ { avg : 0 , var : 0.1 }, { avg : 0 , var : 0 }, { avg : 0 , var : 0.1 }], veloci t y : [ { avg : 0 , var : 0.025 }, { avg : 0.5 , var : 0.1 }, { avg : 0 , var : 0.025 }], qua nt i t y : { avg : 0.015 }, size : { avg : 0.5 , var : 0 }, sizeEvolve : { transf orm : "quadratic" , fa c t or : -0.7 }, li fe Le n g t h : { avg : 1.5 }, glowLevel : 64 }, { hsvaColor : [{ avg : 0 , var : 0 }, { avg : 0 , var : 0 }, { avg : 40 , var : 30 }, { avg : 220 , var : 50 }], opaci t yEvolve : { transf orm : "quadratic" , fa c t or : -16 }, gravi t yE ffe c t : { avg : 0 , var : 0 }, posO ffset : [ { avg : 0 , var : 0.1 }, { avg : 0 , var : 0 }, { avg : 0 , var : 0.1 }], veloci t y : [ { avg : 0 , var : 0.025 }, { avg : 0.15 , var : 0.1 }, { avg : 0 , var : 0.025 }], qua nt i t y : { avg : 0.05 }, size : { avg : 0.25 , var : 0.05 }, sizeEvolve : { transf orm : "linear" , fa c t or : 0.5 }, par t icleModel : "Quad" } ], There is also a complete tutorial about particles , which should help you to find out what each property does. liquidlevel 0 ... 7 0 Value between 0...7 for Liquids to determine the height of the liquid. water cropprops object - Information about the block as a crop. all crops requiredNutrient Nutrient (N, P, or K) - The primary nutrient this crop uses and consumes from farmland. all crops nutrientConsumption number - The percentage value of the nutrient this plant will consume from farmland. This value should be whole, as in 50 = 50% consumption. all crops growthStages number - The number of growth stages this plant will go through before it can be harvested. all crops totalGrowthDays Number - The average number of days required for a plant to reach maturity. This value can include decimals, such as 4.5 days. all crops behaviors (crop) string - Crops can have additional functionality with special crop behaviors, currently used only by pumpkins. motherplant (pumpkin) Pumpkin number - A unique pumpkin behavior utilize only by the pumpkin motherplant block. motherplant (pumpkin) The most advanced of crops! Pumpkins utilize a special crop behavior allowing the mother plant to generate vines that will eventually produce pumpkin "fruits": cropProps : { behaviors : [{ na me : "Pumpkin" , proper t ies : { vi ne Grow t hS ta ge : 3 , vi ne Grow t hQua nt i t y : { dis t : "invexp" , avg : 2 , var : 3 } }}], requiredNu tr ie nt : "P" , nutr ie nt Co nsu mp t io n : 30 , grow t hS ta ges : 8 , t o tal Grow t hDays : 3.5 , }, We can see that the special behavior defines how many vines can be produced by one pumpkin motherplant. inside The player is inside the block. sounds key: string, value: string - The sounds played for this block during step, break, build and walk. anvil, rock, water walk An entity walks over it. inside The player is inside the block. break Breaking the block. place Placing the block. hit While mining the block. byTool Allows different sounds to be made based off the tool that's being used. We'll use the "rock" block as an example: sou n ds : { walk : "walk/stone" , byTool : { "Pickaxe" : { hi t : "block/rock-hit-pickaxe" , break : "block/rock-break-pickaxe" } } }, ambient Played from time to time if the player is close to it. ambientBlockCount The amount of blocks required to be in the vicinity of the player to play the sound at full volume. Here's a few examples of how different blocks have their own sound properties: Anvil : sou n ds : { "place" : "block/anvil" , "break" : "block/anvil" } Rails : sou n ds : { place ": " block/pla n ks ", " walk ": " walk/wood" } Water : sou n ds : { place : "block/water" , i ns ide : "walk/water" , ambie nt : "environment/creek" }, liquidCode string - An identifier for other liquids to utilize during collisions. lava, water Common creativeinventory key: string, value: string[] - In which creative inventory tabs the block should be visible in. all blocks There are several tabs to you can add your stuff. Note that general should always be included, since it should contain everything. general terrain flora construction decorative items Rock adds all of it's variantions to general, terrain and construction: crea t ivei n ve nt ory : { "general" : [ "*" ], "terrain" : [ "*" ], "construction" : [ "*" ] }, * reprents the variants which will be added. You can specify multiple and separate them with a comma. It follows the same way as the byType property. A Torch on the other hand only adds the variation up : crea t ivei n ve nt ory : { "general" : [ "*-up" ], "decorative" : [ "*-up" ] }, CreativeInventoryStacks - - If you want to add itemstacks with custom attributes to the creative inventory, add them to this list. - notCreateveInventoryStacks - - Prevents the variant from being included in the creative inventory. tapestry maxstacksize integer 64 Determines the maximum amount you can stack the block in one slot. - attackpower decimal number 0.5 The damage the deals when hitting an entity. - attackrange decimal number 1.5 The maximum distance you can hit an entity. - materialdensity integer 9999 Determines on whether an object floats on liquids or not. hay, planks, lava, water Water has a density of 1000, meaning everything below or equal will float on water. The same goes for lava which has a density of 5000. Vintage story uses real world densities for each material (where 1000 = 1 g/cm^3). To give an idea of the current range of densities, gold has a density of 19300, iron's is 7870, and a feather is 20. liquidselectable boolean false If the block can select a liquid while holding it in hand. bucket Used for buckets in order to fill it with water and to place waterlily on top of water. miningSpeed key: string, value: decimal number - The mining speed for each material. Not to be confused with resistance , which determines how long it takes to mine the block. - miningTier integer 0 Determines which blocks it can break. If the required miningtier is above the defined one there will be no drop from it. - combustibleprops object - Information about the blocks burnable states. log, wooden objects burntemperature integer - The temperature at which it burns in degrees Celsius. log, wooden objects burnduration decimal number - For how long it burns in seconds. log, wooden objects heatresistance integer 500 How many degrees celsius it can resists before it ignites (not implemented yet). log, wooden objects meltingpoint integer - How many degrees celsius it takes to smelt/transform this into another. Only used when put in a firepit and SmeltedStack is set. ingotmold, toolmold meltingduration decimal number - For how many seconds the temperature has to be above the melting point until the item is smelted. ingotmold, toolmold smokelevel decimal number 1 How much smoke this item produces when being used as fuel. - smeltedratio integer 1 How many ores are required to produce one output stack. - smeltedstack object - If set, the block/item is smeltable in a furnace and this is the resulting itemstack once the MeltingPoint has been reached for the supplied duration. ingotmold, toolmold requirescontainer boolean true If set to true, the block/item requires a smelting/cooking/baking container such as the Crucible. If false, it can be directly baked/melted without smelting/cooking/baking container. ingotmold, toolmold This property can be used to define a burning material. Plank for example can get on fire: combus t ibleProps : { bur n Tempera ture : 800 , bur n Dura t io n : 12 , }, Furthermore it can be used to define smelting processes. An example would be an ingotmold which turns into an ingotmold-burned: combus t iblePropsByType : { "ingotmold-raw" : { mel t i n gPoi nt : 600 , mel t i n gDura t io n : 30 , smel te dRa t io : 1 , smel te dS ta ck : { t ype : "block" , code : "ingotmold-burned" }, requiresCo nta i ner : false } }, nutritionprops object - Information about the blocks nutrients. - foodcategory string - Defines the type of food. It can be fruit , vegetable , protein , grain and dairy . - saturation decimal number 0 How much saturation it can restore. - health decimal number 0 How much health it can restore. - transitionableProps - - Can be used to transition an item to another item or block. redmeat, hide type - - The type of transition method to utilize. redmeat, hide Cure string - Will "cure" an item by showing percent progress until cured. hide Perish string - Will gradually reduce the saturation of a food item once it's fresh period has passed, eventually converting into the product item (usually rot). hide freshHours number (hours) - An optional "fresh" period that must pass before the transition time starts. With food, this is the period of time that saturation is not affected. bread, vegetable, redmeat transitionHours number (hours) - Number of hours before the item transitions into a different item. redmeat, hide transitionedStack object (item or block) - The item or block that the item will transition into. redmeat, hide transitionRatio number - The quantity of the item that will be consumed after the transition period. redmeat, hide Attributes attributes - - Custom Attributes associated with this item. barrel, chest, pot, skep Attributes constitute a large number of custom properties that an block can have, many of which are specific to unique blocks that rely on a C# class for additional functionality. If you wish to add your own JSON attributes to an item generally you must also have a class to utilize them. Values placed here are final and cannot be modified. For example, if you made a new type of block, say one that damages people when they get close you could define them here: a ttr ibu tes : { "touchDamage" : 0.1 , "sneakAvoidTouchDamage" : true , }, Here we have made two new attributes called "touchDamage" and "sneakAvoidTouchDamage" and have given them their own static values to be used in code. As is, these cannot do anything without the usage of C# code in a class. Regardless, we can see that this is a convenient way to create extra properties that can easily be used by other blocks that use the same class. (Container Attributes) Attributes that define additional container information. allowHeating boolean - Used by the pot block to allow it to be heated in either the output or input slot. pot bowlContents string null The item code of the contents contained in the bowl, by default this value is null since most bowls will start with nothing inside them. - canHold string (blockcode) - What blocks (liquids) a liquid container can hold. bucket capacityLitres number - The amount of litres that a liquid container can hold. bucket closeSound Path to sound - The sound that plays when a container is opened. chest, storagevessel contentBlockCode - - Used by the flowerpot to manually store the block contained in each variant, which drops when the block is broken. flowerpot, planter contentConfig - - Used by food troughs to determine the properties of each content type. trough-large, trough-small code String - Identifier used for referencing in the JSON. trough-large, trough-small content Item or Block code - The item or block placed in the trough. trough-large, trough-small foodFor Entity Code - The entity that can interact with (eat) the contents. trough-large, trough-small quantityPerFillLevel number - The number of the item or block required to fill a "portion" of the trough. trough-large, trough-small maxFillLevels number - The maximum number of portions that the trough can be filled with. trough-large, trough-small shapesPerFillLevel Array of Shape Paths - A list of shapes for each level of the trough, with first being empty, the second with one layer etc. trough-large, trough-small textureCode Path to texture - The flat texture used for the contents in the trough. trough-large, trough-small The large trough is a good example of how the contentConfig attribute is used, since it uses two different shape progressions for regular seed and dry grass: co ntent Co nf ig : [ { code : "flax" , co ntent : { t ype : "item" , code : "grain-flax" }, f oodFor : [ "pig-*" , "sheep-*" ], qua nt i t yPerFillLevel : 2 , maxFillLevels : 8 , shapesPerFillLevel : [ "block/wood/trough/large/grainfill1" , "block/wood/trough/large/grainfill1" , "block/wood/trough/large/grainfill2" , "block/wood/trough/large/grainfill2" , "block/wood/trough/large/grainfill3" , "block/wood/trough/large/grainfill3" , "block/wood/trough/large/grainfill4" , "block/wood/trough/large/grainfill4" ], te x ture Code : "contents-flax" }, { code : "drygrass" , co ntent : { t ype : "item" , code : "drygrass" }, qua nt i t yPerFillLevel : 8 , maxFillLevels : 8 , shapesPerFillLevel : [ "block/wood/trough/large/hayfill1" , "block/wood/trough/large/hayfill1" , "block/wood/trough/large/hayfill2" , "block/wood/trough/large/hayfill2" , "block/wood/trough/large/hayfill3" , "block/wood/trough/large/hayfill3" , "block/wood/trough/large/hayfill4" , "block/wood/trough/large/hayfill4" ], f oodFor : [ "pig-*" , "sheep-*" ] } ] contentItemCode Item - The id of an item stored in a container. - contentItem2BlockCode Item - Items that are converted into blocks when store in this container. bowl cookingContainerSlots Number - The number of slots a cooking container has. crucible, pot defaultTyped String - Since containers have a unique variant form called "types" this is used to determine what the default type is. chest, storagevessel dialogTitleLangCode String - Gives a container tooltip a "title" in the lang file (EG "Chest Contents"). chest, storagevessel displaycasable boolean - If true, allows a block to be placed in a display case. crystal-small eatenBlock Block Code - Used by meal blocks to determine what block to return to when the meal has been completely eaten. bowl eatenBlock Block Code - Used by meal container blocks to determine what block to return to when the meal has been emptied from it. pot fillHeight Number - Determines how high to render molten metal in a tool mold. toolmold fillQuadsByLevel Array of Coordinates - Can be used to specify quadrants to render within for tool molds that have higher levels (such as the helve hammer or anvil molds). toolmold We can look at how this is done with the Anvil mold: "fillHeight" : 10 , "fillQuadsByLevel" : [ { x 1 : 2 , z 1 : 3 , x 2 : 13 , z 2 : 13 }, { x 1 : 2 , z 1 : 3 , x 2 : 13 , z 2 : 13 }, { x 1 : 2 , z 1 : 3 , x 2 : 13 , z 2 : 13 }, { x 1 : 4 , z 1 : 6 , x 2 : 11 , z 2 : 10 }, { x 1 : 4 , z 1 : 6 , x 2 : 11 , z 2 : 10 }, { x 1 : 4 , z 1 : 6 , x 2 : 11 , z 2 : 10 }, { x 1 : 4 , z 1 : 6 , x 2 : 11 , z 2 : 10 }, { x 1 : 4 , z 1 : 5 , x 2 : 14 , z 2 : 11 }, { x 1 : 1 , z 1 : 5 , x 2 : 15 , z 2 : 11 }, { x 1 : 1 , z 1 : 5 , x 2 : 15 , z 2 : 11 }, ], As we can see, the anvil mold has 10 layers rather than one, and because it has different shapes for each layer we can redefine the space to render the molten metal per level by using two corners defined by x and z coordinates within the block. In the end, this gives a cleaner looking poured metal effect (even though it may be difficult to see what's going on in the mold). handleCookingContainerInteract boolean - If true, can be used to take food from a cooking container. crock, pot handleLiquidContainerInteract boolean - If true, can be used to hold liquids. crock, pot inFirePitProps - - Gives an item additional rendering properties when placed in a fire pit. crucible, pot transform - - If the model type shows the item, it can be transformed using this property. crucible, pot useFirepitModel - - Tell the firepit which model to use when this item is placed into it. crucible, pot Here's an example of how the firepit transformation is used by the pot and crucible: i n FirePi t Props : { transf orm : { translat io n : { x : 0 , y : 0.125 , z : 0 } }, useFirepi t Model : "Wide" } If you're familiar with the other transformation code, this is nothing unusual and works on the same principles by changing the scale, position and rotation of the model with respect to the origin of rotation. At the moment wide is the only model used with container items. The other option spit is used with cooking meat directly on the firepit. input-face Side ID - Used to determine what faces of the block can be used as an input for a hopper (set to "null" since it does not have an input face). hopper inventoryClassName String - Used to name an inventory during the loading of inventories in the world. Use "chest" for any generic containers. chest, storagevessel item-flowrate Number - Used by the chute block to determine how many items move through it per second. chute liquidcontainer boolean - If true, allows the block to hold and take liquids. bucket maxContainerSlotStackSize Number - Limits the stacksize of items placed into the container. pot maxHeatableTemp Number - The highest temperature that the container can be heated. pot, crucible mealBlockCode Block Code - Used by meal blocks to determine which variant is used when filled with a meal. bowl mealContainer boolean - Allows the block to function as a meal container (can pull from meal blocks like crocks). bowl openSound Path to Sound - Determines what sound is played when a container is opened. Generally used in tandem with closeSound . chest, storagevessel output-face Direction ID - Used by the chute block to determine which face of the block is used as an output for transferring items. chute panningDrops - - If a block can be used for panning (using the BlockPan class), these are its potential drops. pan You can add all sorts of items using the following format: { t ype : "item" , code : "nugget-nativecopper" , cha n ce : { avg : 0.15 , var : 0 } }, quantitySlots number - The number of slots a generic container has. chest, storagevessel requiredUnits number - The number of units required to fill a tool mold with molten metal. toolmold retrieveOnly boolean - If true, makes it so that a container can only be taken from and cannot be used to store new items. lootvessel servingCapacity number - The number of servings of a meal a block can hold. crock, bowl shelvable boolean - If true, allows the block to be placed on a shelf. crystal-small, seashell sitHeight number (decimal) - Used by flower pots to determine how high the incorporated block should be rendered. flowerpot, planter spoilSpeedMulByFoodCat - - Used to make food spoil slower or faster when stored in a container. storagevessel The clay storage vessel is a good example of this: spoilSpeedMulByFoodCa t : { "normal" : { "vegetable" : 0.75 , "grain" : 0.5 } }, storageType Number (Storage Flag) - The storage flag of the container, which can limit what types of items or blocks can be stored within it. storagevessel textureMapping - - A more complex method of texturing block shapes with multiple content combinations. Used primarily with the pot to texture the food contents. pot types - - A secondary method of variant categorization used by containers to help with assigning attributes. chest variantByGroup - - Used by generic containers to group types by a specific variant. "side" is used since it is a common variant of all the types. chest variantByGroupInventory - - A subtype inventory name that can be assigned to types (usually this is "null"). chest waterTightContainerProps - - This contains all the liquid properties of an block, generally determining how it is stored and used with a bucket. Keep in mind this is not generally used for blocks, as most liquids that use this property are items, even if they come from a source block . water containable boolean - If true, the liquid can be placed into liquid containers, such as barrels and buckets. water itemsPerLitre number (int) - The number of itemstack items required to make a litre of liquid. Generally this value is 1:1, but concentrated items like honey can be 4:1. water texture Path to Texture - A "block" texture given to a liquid when it's rendered in containers or other items. waterportion, honeyportion allowSpill boolean - If true, allows the player to use the Ctr + Right Click function to "spill" the liquid from a container. The whenSpilled property determines what happens if this is true. waterportion, limewater tintIndex integer 0 Tints the color of the item if it's ever drawn as a block: 0 for no tint, 1 for plant climate tint, 2 for water climate tint. - waterportion, limewater whenFilled Item or Block Code - Determines what is stored in a liquid container when interacted with. water whenSpilled - - Determines what happens when the "spill" interaction is used. Only works if the allowSpill property is set to true. waterportion, limewater action string - Code identifier that determines what happens when the liquid is spilled from a container. waterportion, limewater PlaceBlock - - Places a block at the spilled location. waterportion DropContents - - Drops an item at the location. If the item is a liquid it will disappear immediately with a "splash" particle effect. limeportion stack - - The block or item dropped when spilled. if the "PlaceBlock" action is chosen a block is placed, if "DropContents" is used an item is generated. waterportion, limewater stackByFillLevel - - Allows for different blocks or items to be placed based on the level of the liquid in the container. waterportion, limewater We'll look at an example of how this property is utilized by water block: wa ter Tigh t Co nta i ner Props : { co nta i na ble : true , i te msPerLi tre : 1 , whe n Filled : { s ta ck : { t ype : "item" , code : "waterportion" } } } As we can see, most of the watertight properties are not used by the block, that's because most of them are used by the item equivalent "waterportion". (Rendering Attributes) Attributes that define additional rendering information. ignoreTintInventory boolean - If true, does not apply texture tints to the block model as an inventory item. soil mechancialPower boolean - A special category of values reserved for mechanical power. Currently only "renderer" is used by the angled gears block to determine which combination of gears to render. angledgears pushVector vector coordinates - Allows the vector shape of a block to be altered for slopes. lava, water (Misc Attributes) Miscellaneous, unsorted attributes. allowUnstablePlacement boolean - If the block has the "unstable" behavior, this allows it to be generated in positions that it can fall from. sand, gravel beeFeed boolean - Whether or not the block is a viable source of bee feeding (IE a flower). all crops, bigberrybush, flower, smallberrybush butterflyFeed boolean - Whether or not the block will attract butterflies. all crops, bigberrybush, flower, smallberrybush blastRadius number - Used by bombs to determine the radius of the blast. oreblastingbomb blastType number, 0, 1 or 2 - Used by bombs to determine what type of blocks are broken. oreblastingbomb 0 = Destroys ores, 1 = Destroys stone, 2 = Damages only entities. canChisel boolean - If true, allows the block to be chiseled. rock, all slabs canFallSideways - - If true, allows a block to move sideways when it falls. gravel chiselShapeFromCollisionBox boolean - If true, uses the collision box as the starting point for chiseling. most stairs, all slabs convertFrom block - If a block converts to another block, this is the block that can be converted. carcass convertTo block - If a block converts to another block, this is the block that it will turn into. carcass delayGrowthBelowSunlight number - Used by the farmland block to delay growth when sunlight is below the given value. farmland drop - - Used by the toolMold block to determine what item it drops, not to be confused with the drops property. toolMold fenceConnect boolean - Can be used to prevent a solid block from connecting to fences. - grindingProps - - Gives the block a grinding recipe in a quern. ore-ungraded type object - Type of stack produced, either a block or item . - code string - Name of the item or block produced by the recipe. ore-ungraded stacksize number - The amount of the output produced after grinding. ore-ungraded growthBlockLayer string - Determines what layers of a block will grow grass, currently only uses "l1soilwithgrass", which grows grass on the first layer only. soil growthLightLevel - - Used by the soil block to determine what light level is required for it to grow grass on. soil inGameHours number - If a block converts to another block, this is the amount of time required for it to convert. carcass injureRadius number - Used by bombs to determine the radius that it will cause injury to entities. oreblastingbomb lossPerLevel - - Used in combination with delayGrowthBelowSunlight to determine how much growth progression is lost per light level. farmland mechanicalPower - - Used by the angled gear block to determine how to render gears in the different orientations. angledgears noDownVariant - - When giving a block variants based off the orientation library, this can be used to prevent a "down" variant from being used. - placeBelowBlockCode block - Used by the BlockSoilDeposit class to place specific blocks beneath it during world generation. clay, peat preventsDecay boolean - If true, prevents nearby leaves from "decaying", IE disappearing. log, soil rotatableInterval string - Gives some blocks additional rotation properties when placed (currently "22.5degnot45deg" and "22.tdeg" are the only two options). chest sleepEfficiency number (decimal) - Determines how long a player can sleep in a bed, based off a ratio of 12 hours. (ratio = #maxhours/12). - spreadGrass boolean - If true, can spread grass to other soil blocks. - stackable boolean - Used by berry bushes to determine whether they can be stacked atop each other. - tallGrassGrowChance number (decimal) - Used by the soil block to determine how often it will grow tall grass on top of it. clay, peat, soil temperature - - Used by liquids to determine the temperature of the core block. lava tempLossPerMeter number - How much temperature is lost for a liquid as it moves away from a source block. lava tickGrowthProbability number (decimal) - Used by crops to determine how often it will randomly progress its growth stage per tick. all crops toolTransforms - - Used to set default transformation on the toolrack, though all tools generally use their own. toolrack weedBlockCodes - - Used by the farmland block to determine what "weeds" can grow on it. farmland We can see here that this property allows tallgrass and horsetails to grow on farmland with varying degrees of likelihood: weedBlockCodes : [ { code : "tallgrass-veryshort" , cha n ce : 1 }, { code : "tallgrass-short" , cha n ce : 1 }, { code : "tallgrass-mediumshort" , cha n ce : 1 }, { code : "tallgrass-medium" , cha n ce : 1 }, { code : "flower-horsetail" , cha n ce : 0.2 }, ] Rendering textures key: string, value: object The texture definitions for the block as seen in the world, when dropped on the ground or held in the hand. Within a mod, to refer to a texture from the base game, prefix the path with "game:" (i.e. base: "game:path/to/texture") all blocks The dictionary contains multiple named textures. Different tessellators expect different texture names. Most tessellators accept the special "all" alias to set all textures at once. Default example (glass): te x tures : { all : { base : "block/glass" }, } Using variantgroups (rock): te x tures : { all : { base : "block/stone/rock/{rock}" }, } There are a few aliases that set multiple textures at the same time: all - sets all textures for tessellators that support the alias sides - "west", "east", "north", "south", "up", and "down" horizontals - "west", "east", "north", and "south" verticals - "up" and "down" westeast - "west" and "east" northsouth - "north" and "south" For example, hay block uses two aliases: te x tures : { horizo ntals : { base : "block/hay/{type}-side" }, ver t icals : { base : "block/hay/{type}-top" }, }, There are many options to rotate textures, combine them, and randomize the textures by block location. texturesinventory key: string, value: object The texture definitions for the block as seen in the player inventory. Overrides the textures. - shape CompositeShape - For the json drawtype, the shape definition of the block as shown in the world, dropped on the ground or held in hand. Barrel, firepit base The path to the shape json file, the base dir is assets/shapes/ . - rotatex float 0 Only 90 degree rotations are possible. - rotatey float 0 Only 90 degree rotations are possible. - rotatez float 0 Only 90 degree rotations are possible. - offsetx float 0 Offsets the shape in the X axis. - offsety float 0 Offsets the shape in the Y axis. - offsetz float 0 Offsets the shape in the Z axis. - shapeinventory CompositeShape - For the json drawtype, the shape definition of the block as shown in the players inventory. - lod0shape CompositeShape - Additional shape that is shown when the block is very close to the camera. Most blocks do not use this. leavesbranchy lod2shape CompositeShape - Alternative shape that is shown when the block is very far from the camera. Most blocks do not use this. looseboulders drawtype string "cube" Determines which block tessellator processes the block. Select JSON for being able to use custom JSON Models. Use Cube for basic cubes. - renderpass string "opaque" Determines which pass the block is drawn in. - doNotRenderAtLod2 boolean false When false, render the block at level of detail 1, which is visible at all distances within the frustum. When true, only render the block at short and medium distances. This property is only respected by some block tessellators . torch ambientocclusion boolean true If ambient occlusion will be applied to the block. - tintindex integer 0 0 for no tint, 1 for plant climate tint, 2 for water climate tint. - renderflags 0 ... 255 0 8 bits that are sent to the graphics card for each vertex of the blocks shape. The lower 3 bits are currently used for altering the vertexes z-depth to fix a bunch of z-fighting issues. - facecullmode string "default" Determines which sides of the blocks should be rendered. - default 0 Culls faces if they are opaque faces adjacent to opaque faces. - nevercull 1 Never culls any faces. - merge 2 Culls all faces that are adjacent to opaque faces and faces adjacent to blocks of the same id (Example usage: Ice blocks). - collapse 3 Culls all faces that are adjacent to opaque faces and the bottom, east or south faces adjacent to blocks of the same id. This causes to still leave one single face in between instead of 2, eliminating any z-fighting. - mergematerial 4 Same as Merge but checks for equal material (Example usage: Plain glass and all colored glass blocks). - collapsematerial 5 Same as Collapse but checks for equal material (Example usage: All leaves blocks). - liquid 6 Same as CollapseMaterial but also culls faces towards opaque blocks. - sideopaque key: string, value: boolean - Determines if given block face is fully opaque. If yes, the opposite face of the adjacent block will not be drawn for efficiency reasons. - Sides include: all; horizontals, verticals; east, west, up, down, north, south . sideao key: string, value: boolean - If AmbientOcclusion will be applied for each side. - Sides include: all; horizontals, verticals; east, west, up, down, north, south . sideEmitAo key: string, value: boolean - Defines which of the 6 block neighbours should receive AO if this block is in front of them. - Sides include: all; horizontals, verticals; east, west, up, down, north, south . sidesolid key: string, value: boolean - Determines if given block side is solid. If true, other blocks like torches can be attached to it. all stairs Sides include: all; horizontals, verticals; east, west, up, down, north, south . randomdrawoffset boolean false If true then the block will be randomly offseted by 1/3 of a block when placed. flower randomizeRotations boolean false If true then the block will be randomly rotated when placed. Note: Many random rotated blocks in one chunk will slow down block placement updates. You can increase efficieny by defining alternate shapes with random rotations instead. flower lighthsv byte array with 3 elements. See http://tyron.at/vs/vslightwheel.html for valid values - For light emitting blocks: hue, saturation and brightness value. torch, oillamp, lantern lightabsorption 0 ... 255 99 For light blocking blocks. Any value above 32 will completely block all light. - guitransform object block default Used for scaling, rotation or offseting the block when rendered in guis. - fphandtransform object block default Used for scaling, rotation or offseting the block when rendered in the first person mode hand. - tphandtransform object block default Used for scaling, rotation or offseting the block when rendered in the third person mode hand. - groundtransform object block default Used for scaling, rotation or offseting the rendered as a dropped item on the ground. - randomizeaxes string "xyz" Random texture selection - whether or not to use the Y axis during randomization (for multiblock plants). - xyz 0 - xz 1 - vertexFlags - - Applies special effects to a block, including motion shaders and other fancy renderings. crops, leaves, snow, tallgrass, waterlily glowLevel 0 ... 255 0 Causes the block to visually glow if Bloom is enabled. Basic glow level for all the blocks model elements. glacerice, lava blocks value Glacierice 8 Paperlantern 32 Ember 48 Fire , Firepit , Oillamp , Torch 64 Lava 128 grassWindwave boolean false If true, applies the grass wind wave effect to block. tallgrass, flower, fern leavesWindwave boolean false If true, applies the rustling leaves effect on a block. leaves, leavesbranchy lowContrast boolean false If true, applies a low contrast shader to a block. snow reflective boolean false If true, applies a reflective shader effect to a block. snow waterWave boolean false If true, applies the water wave effect to block. waterlily waterWave boolean false If true, applies the water wave effect to block. waterlily zOffset boolean number - Used to prevent rendering conflicts when blocks are drawn at a distance (IE z-fighting). Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Block_and_Item_Interactions TITLE: Modding:Block and Item Interactions --- CONTENT --- Other languages: English русский This page is outdated. Reason: Please note that this tutorial is candidate for a rewrite. While the particles created will work, the item used will not move due to "UsingHeldItemTransformAfter" being deprecated. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . This tutorial will introduce you into the basics of custom interactions. We will create a magic wand which will spawn particles when holding right click. Contents 1 Preparations 1.1 Adding particles 1.2 Testing 2 Mod Download Preparations I highly recommend to read about The Item Class first. Additionally you can download the assets here . All of this should be familiar to you, creating and registering the item class ... public class Magic : ModSystem { public override void Start ( ICoreAPI api ) { base . Start ( api ); api . RegisterItemClass ( "ItemMagicWand" , typeof ( ItemMagicWand )); } } public class ItemMagicWand : Item { } Adding particles Now we need to implement the interact function. First of all we need to specify that the player can "use" this tool, therefore we need to set handling to handled ... public override void OnHeldInteractStart ( ItemSlot slot , EntityAgent byEntity , BlockSelection blockSel , EntitySelection entitySel , bool firstEvent , ref EnumHandHandling handling ) { handling = EnumHandHandling . Handled ; } The method OnHeldInteractStep allows us to spawn particles per tick while the player is using the item, but it would be better to implement an animation first. Particles should spawn after the animation is done ... public override bool OnHeldInteractStep ( float secondsUsed , ItemSlot slot , EntityAgent byEntity , BlockSelection blockSel , EntitySelection entitySel ) { if ( byEntity . World is IClientWorldAccessor ) { ModelTransform tf = new ModelTransform (); tf . EnsureDefaultValues (); tf . Origin . Set ( 0 , - 1 , 0 ); tf . Rotation . Z = Math . Min ( 30 , secondsUsed * 40 ); byEntity . Controls . UsingHeldItemTransformAfter = tf ; } return true ; } Holding rightclick ... So let's start to mess around with particles, therefore we need a static particle type ... public static SimpleParticleProperties particles = new SimpleParticleProperties ( 1 , 1 , ColorUtil . ColorFromRgba ( 220 , 220 , 220 , 50 ), new Vec3d (), new Vec3d (), new Vec3f (- 0.25f , 0.1f , - 0.25f ), new Vec3f ( 0.25f , 0.1f , 0.25f ), 1.5f , - 0.075f , 0.25f , 0.25f , EnumParticleModel . Quad ); Particles should spawn once the animation is completed. This will be the case after 0.6 seconds ... if ( secondsUsed > 0.6 ) { //Spawn particles } I suggest to read the tutorial about Simple Particles first. This code will spawn particles in front of the player with a randomized color and a sinus evolving size ... Vec3d pos = byEntity . Pos . XYZ . Add ( 0 , byEntity . LocalEyePos . Y , 0 ) . Ahead ( 1f , byEntity . Pos . Pitch , byEntity . Pos . Yaw ) ; Vec3f speedVec = new Vec3d ( 0 , 0 , 0 ). Ahead ( 5 , byEntity . Pos . Pitch , byEntity . Pos . Yaw ). ToVec3f (); particles . MinVelocity = speedVec ; Random rand = new Random (); particles . Color = ColorUtil . ColorFromRgba ( rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), 255 ); particles . MinPos = pos . AddCopy (- 0.05 , - 0.05 , - 0.05 ); particles . AddPos . Set ( 0.1 , 0.1 , 0.1 ); particles . MinSize = 0.1F ; particles . SizeEvolve = EvolvingNatFloat . create ( EnumTransformFunction . SINUS , 10 ); byEntity . World . SpawnParticles ( particles ); If we put everything together the OnHeldInteractStep method will look like this ... public override bool OnHeldInteractStep ( float secondsUsed , ItemSlot slot , EntityAgent byEntity , BlockSelection blockSel , EntitySelection entitySel ) { if ( byEntity . World is IClientWorldAccessor ) { ModelTransform tf = new ModelTransform (); tf . EnsureDefaultValues (); tf . Origin . Set ( 0 , - 1 , 0 ); tf . Rotation . Z = Math . Min ( 30 , secondsUsed * 40 ); byEntity . Controls . UsingHeldItemTransformAfter = tf ; if ( secondsUsed > 0.6 ) { Vec3d pos = byEntity . Pos . XYZ . Add ( 0 , byEntity . LocalEyePos . Y , 0 ) . Ahead ( 1f , byEntity . Pos . Pitch , byEntity . Pos . Yaw ) ; Vec3f speedVec = new Vec3d ( 0 , 0 , 0 ). Ahead ( 5 , byEntity . Pos . Pitch , byEntity . Pos . Yaw ). ToVec3f (); particles . MinVelocity = speedVec ; Random rand = new Random (); particles . Color = ColorUtil . ColorFromRgba ( rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), 255 ); particles . MinPos = pos . AddCopy (- 0.05 , - 0.05 , - 0.05 ); particles . AddPos . Set ( 0.1 , 0.1 , 0.1 ); particles . MinSize = 0.1F ; particles . SizeEvolve = EvolvingNatFloat . create ( EnumTransformFunction . SINUS , 10 ); byEntity . World . SpawnParticles ( particles ); } } return true ; } Testing Now we can run our first test, doesn't it look beautiful? Mod Download Feel free to try it out yourself: Here is my version: for VS v1.9: Magicwand_vs1.9_v1.0.0.zip for VS v1.8: MagicWand.zip Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Chunk_Moddata TITLE: Modding:Chunk Moddata --- CONTENT --- Other languages: English русский This page is a candidate to be rewritten. This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. This page was last verified for Vintage Story version 1.19.8 . Before starting, you should have a development environment set up. If you don't have one already you should read the tutorial Setting up your Development Environment . Furthermore, we assume that you have a basic understanding of the C# language and Object Oriented Programming. Let's get started! Contents 1 Introduction 2 Custom Chunk Data 3 Chunk Serialization Triggers 4 Preparation 4.1 Player death and block use delegates 5 Viewing the raw mod data 6 Conclusion 7 Testing 8 Distribution Introduction Custom mod data can be attached to individual chunks . Alternatively, mods can store custom save game mod data that applies to the entire save game. Each chunk has two dictionaries where custom mod data can be attached. The shared mod data dictionary and the server mod data dictionary. Both of these dictionaries have a string key and a byte array value. Logically LiveModData is another accessor for the shared mod dictionary, but technically it is third dictionary. Every time the chunk is serialized, all of the entries in LiveModData are serialized into byte arrays and stored in the shared mod data dictionary, overriding any existing entries with the same key. Dictionary Readable on Writable on Serialized to Accessors shared mod data client and server server save game on disk client over network IWorldChunk.LiveModData IWorldChunk.SetModdata - has serialization race conditions. Use LiveModData instead. IWorldChunk.GetModdata server mod data server server save game on disk IServerChunk.GetServerModdata IServerChunk.SetServerModdata Both the server mod data dictionary and shared mod data dictionary are have string keys and byte array values. IWorldChunk.LiveModData internally uses SerializerUtil to convert rich types into byte arrays. SetServerModdata does not have such a convenience wrapper. So developers need to manually use a serializer such as SerializerUtil.Serialize to convert a rich object into byte array to give SetServerModdata . Implementation-wise any string can be used as a key in the mod data dictionaries, but the best practice is to use a string that starts with the mod identifier, which is "haunting" in this tutorial. Custom Chunk Data In this example mod we'll show you how to store custom data to chunks represented on the server side. We'll create a mod that keeps track of how many times players have died on a given chunk. Every time a player tries to sleep in a bed, the sum of total deaths on the chunk will be used to inflict damage onto the player! Spooky! Logically the count of deaths in a chunk is an integer. However, as described earlier, for serialization purposes, that int needs to be stored as a byte array on the chunk. But, an int is more convenient for processing outside serialization purposes. So we'll wrap the integer in a HauntingChunkData class. The [ProtoContract] and lets the serializer know that it is allowed to serialize the class. The [ProtoMember(1)] assigns an identifier to the Deaths field. Any previously unused identifier can be used. The identifier is used to track the field in case the field is later renamed, removed, or new fields are added. Since this is the first version of the mod, such versioning concerns do not apply. [ProtoContract] public class HauntingChunkData { [ProtoMember(1)] public int Deaths ; } Since all of the death tracking and bed tracking logic runs on the server side, the HauntingChunkData will be stored in the server mod data instead of the shared mod data. Every time a bed is used, this mod will find the chunk containing the block, then look up the number of deaths in the chunk. Each time a bed is used, it would be possible to deserialize the HauntingChunkData directly from the server mod data in the chunk. Using a bed occurs rarely enough that the deserialization cost of this approach would be acceptable. However, for pedagogical purposes, this mod uses a more CPU efficient approach: the mod adds a separate dictionary to directly look up the deaths for cached chunks. Later this tutorial will explain how to synchronize _hauntedChunks with the server mod data dictionary. private readonly ConditionalWeakTable < IServerChunk , HauntingChunkData > _hauntedChunks = new (); LiveModData contains unserialized objects. This example mod does not store the death count in LiveModData , because LiveModData serializes its entries to the shared mod data dictionary. However, for mods that use the shared mod data, the performance of directly reading from LiveModData is lower than directly reading from the server mod data, because reading from LiveModData skips the deserialization stage for already loaded chunks. However, a mod specific cached chunk dictionary is still faster, because it can skip the string hash call necessary to look up an entry in LiveModData (in exchange for a cheap IServerChunk hash call). When the game saves, we iterate through our Dictionary , convert the number of death integers into byte arrays, and attach each byte array to its corresponding chunk. Vintage Story will take care of saving that mod data in the game save file. Chunk Serialization Triggers When a chunk is marked as modified, it is eventually serialized into the save game on disk. A chunk is marked as modified in many conditions, such as when an entity is created in it, or a block is modified. However, when storing custom mod data in the chunk, the safest option is to directly marked as modified by calling IWorldChunk.MarkModified . This example mod relies on the player dying to mark the chunk as modified. Modified chunks will get serialized by the next save game event. They will get serialized sooner if the chunk is unloaded. Both of these operations run on the chunk thread (not the main thread). Care must be taken with updating the mod data dictionaries at the right time. The dictionaries should be updated right before they are serialized. If the dictionaries are updated too early before the chunk is locked, then the main thread can further modify the chunk before the serialization. Then the chunk will be in an inconsistent state. Maybe the compressed blocks will be newer than the mod data. Although for most mods, small race conditions like this would be unnoticeable. LiveModData serializes its fields at exactly the right time: on the chunk thread while the chunk is locked for serialization. There are no other events one can hook into to trigger serialization at the correct time. This is why LiveModData is strongly recommended over IWorldChunk.SetModdata . There is no equivalent to LiveModData for the server mod data. So instead this example mod stores a fake entry in LiveModData with a [ProtoBeforeSerialization] callback. It uses that callback to update the server mod data dictionary right before the chunk is serialized. [ProtoContract] public class SerializationCallback { public delegate void OnSerializationDelegate ( IServerChunk chunk ); readonly private IServerChunk _chunk ; public OnSerializationDelegate OnSerialization ; public SerializationCallback ( IServerChunk chunk ) { _chunk = chunk ; } [ProtoBeforeSerialization] private void BeforeSerialization () { OnSerialization ( _chunk ); } } ... if (! chunk . LiveModData . TryGetValue ( "haunting" , out object serializerObj ) || serializerObj is not SerializationCallback serializer ) { serializer = new ( chunk ); chunk . LiveModData [ "haunting" ] = serializer ; } serializer . OnSerialization += chunk => chunk . SetServerModdata ( "haunting" , SerializerUtil . Serialize ( chunkData )); An earlier version of haunting mod relied the IServerEventAPI.GameWorldSave event. However, that event is not called before chunks are unloaded. It is also called too early in the save game process, such that the chunk can be modified again before it is serialized. In addition to saving chunks to disk, nearby chunks are sent to clients when they first join the server, or when they walk into range of the chunk. When using shared mod data, the data may need to be sent more often by calling IWorldManagerAPI.BroadcastChunk . Note that marking a chunk as modified only triggers saving it to disk; it does not trigger sending the modified chunk to clients. Preparation Let's start by creating a new .cs file for this mod, and adding our imports and the HauntedChunks namespace to wrap our class in. Additionally, we'll declare the class HauntedChunks that will inherit from ModSystem which is the base class for mod systems. You can read more about this here . using System.Collections.Generic ; using Vintagestory.GameContent ; using Vintagestory.API.Common ; using Vintagestory.API.Server ; using Vintagestory.API.Util ; namespace HauntedChunks ; public class HauntedChunks : ModSystem { private ICoreServerAPI _serverApi ; public override void StartServerSide ( ICoreServerAPI api ) { _serverApi = api ; } } The StartServerSide method is a member of ModSystem and is called for all mods on the Server side by the game. It is given a ICoreServerAPI , which the mod will need later. So the mod overrides the method and saves it in _serverApi . hauntedChunks will be a list of hunated chunks and their haunting level. The Core Server API contains an additional API for Event registration; this object contains a list of server sided events that we can attach our delegates to, which will be invoked every time the event fires. We do this by assigning a delegate to an exposed event with the += operator. In our case, we register to two separate events, each with their own delegates that we'll define later. The purpose of each is as follows: api.Event.PlayerDeath += OnPlayerDeath; When a player dies, we want to increase haunting level on the chunk they died in by 1. api.Event.DidUseBlock += OnUseBlock; We check if the player used a bed, if so we try damaging them according to how haunted the chunk is. Note that DidUseBlock is called any time any block is right clicked. A more efficient -- but complicated -- approach would be to create a behavior that intercepts OnBlockInteractStart , and install it on just the bed block. Let us now define these event delegates! Player death and block use delegates private void OnPlayerDeath ( IServerPlayer byPlayer , DamageSource damageSource ) { IServerChunk chunk = _serverApi . WorldManager . GetChunk ( byPlayer . Entity . ServerPos . AsBlockPos ); if (! _hauntedChunks . TryGetValue ( chunk , out HauntingChunkData chunkData )) { chunkData = AddChunkToDictionary ( chunk , true ); } chunkData . Deaths ++; } The OnPlayerDeath delegate that we used to listen for player death events, will first retrieve the chunk the player died on, by calling IWorldManager.GetChunk . Once we have the chunk's instance as IServerChunk , we check to see if we're not already holding this chunk in memory by seeing if _hauntedChunks contains it. If it doesn't we'll add it with a method we'll call AddChunkToDictionary , and this method should look like this: private HauntingChunkData AddChunkToDictionary ( IServerChunk chunk , bool create ) { HauntingChunkData chunkData ; byte [] data = chunk . GetServerModdata ( "haunting" ); if ( data != null ) { chunkData = SerializerUtil . Deserialize < HauntingChunkData >( data ); } else if (! create ) { return null ; } else { chunkData = new (); } _hauntedChunks . Add ( chunk , chunkData ); if (! chunk . LiveModData . TryGetValue ( "haunting" , out object serializerObj ) || serializerObj is not SerializationCallback serializer ) { serializer = new ( chunk ); chunk . LiveModData [ "haunting" ] = serializer ; } serializer . OnSerialization += chunk => chunk . SetServerModdata ( "haunting" , SerializerUtil . Serialize ( chunkData )); return chunkData ; } _hauntedChunks acts like a cache. If the cache does not have the entry, maybe it already exists on disk. GetServerModdata is called to read the entry from disk (technically it is already in memory but not deserialized into _hauntedChunks yet). GetServerModdata returns a byte[] . If GetServerModdata returned non-null, then it deserializes the previous haunting level from disk into a HauntingChunkData object. If GetServerModdata returned null, then that means the mod has never attached a haunting level to the chunk. In this case, the create parameter indicates whether a new default HauntingChunkData should be attached to the chunk, or whether it should return null to indicate no data is attached. As described earlier, a fake entry is added to LiveModData just to get a notification when the chunk is serialized. The mod responds to that notification by calling SetServerModdata to set the server mod data for the chunk. Note that the fake entry in LiveModData will still get serialized to disk (shown later in the tutorial), but the small overhead is worth getting notified at the correct time. Hint : Holding custom data as byte[] means we don't know know the type that is stored. This warrants extra attention when using it, and we should make sure we're always storing and retrieving the same type under for a given key. Now let's handle our final delegate. private void OnUseBlock ( IServerPlayer byPlayer , BlockSelection blockSel ) { if ( _serverApi . World . BlockAccessor . GetBlock ( blockSel . Position ). GetType () != typeof ( BlockBed )) { return ; } IServerChunk chunk = _serverApi . WorldManager . GetChunk ( blockSel . Position ); if (! _hauntedChunks . TryGetValue ( chunk , out HauntingChunkData chunkData )) { chunkData = AddChunkToDictionary ( chunk , false ); } int haunting = chunkData ?. Deaths ?? 0 ; if ( haunting > 0 ) { byPlayer . Entity . ReceiveDamage ( new DamageSource () { Source = EnumDamageSource . Void , Type = EnumDamageType . BluntAttack }, haunting ); } } We begin by checking whether or not the block used is of type BlockBed , which is a block class defined in the game's Survival mod that corresponds to all beds in the base game. We find the block the player is using by using the GetBlock method of the World's BlockAccessor object which holds a lot of in-game block functionality, such as getting, removing and replacing blocks. Check out the documentation for more info. If the block being used by the player is not a bed, there's no need to go further so we return. Hint : Note that the BlockAccessor is part of the World object, which is not to be confused with the aforementioned WorldManager object. The main difference being that WorldManager is only Server sided, while World is on both Client and Server side, and contains functionality to access more general aspects of the game world and has a broader scope. We then get the chunk, the same way we got it previously by exposing GetChunk , but this time we pass it the position of the bed block. We then check if the _hauntedChunks list contains the chunk, if not then call AddChunkToDictionary to possibly add it. The second argument to AddChunkToDictionary is false, to indicate that it should only load the haunting data if it already exists in the chunk in the save file. If it does not exist, then AddChunkToDictionary returns null. The later chunkData?.Deaths ?? 0 converts a null into a 0. Finally, we check if the chunk's haunting level is above 0, and if so we damage the player by using the ReceiveDamage method of the player's Entity . This method takes two arguments. The first one is the DamageSource which we pass in as newly created instance. The Source and Type fields we populate determine how the player's entity will handle this damage. For instance, damage of type Heal will increase the entity's health points! The second argument passed is the amount of damage we want the entity to receive, in our case this number will be the haunting level of this chunk, aka the amount of player deaths that happened on the chunk. The mod is now complete! Viewing the raw mod data It is possible to extract the raw mod data from the save game to verify it was written Deserializing_protobufs_from_the_command_line has more details about how to setup the tools to view the raw data. The raw data shows that that the fake entry to the shared mod data (just called moddata in the save game) was written with an empty value. The real haunting count was written to the server mod data. $ sqlite3 test_world.vcdbs "SELECT writefile('chunk_' || position || '.binpb', data) FROM chunk;" 92 92 92 ... $ protoc --decode ServerChunk --proto_path=$HOME $HOME/schema-1.19.8.proto _hauntedChunks = new (); public override void StartServerSide ( ICoreServerAPI api ) { _serverApi = api ; api . Event . PlayerDeath += OnPlayerDeath ; api . Event . DidUseBlock += OnUseBlock ; } private void OnPlayerDeath ( IServerPlayer byPlayer , DamageSource damageSource ) { IServerChunk chunk = _serverApi . WorldManager . GetChunk ( byPlayer . Entity . ServerPos . AsBlockPos ); if (! _hauntedChunks . TryGetValue ( chunk , out HauntingChunkData chunkData )) { chunkData = AddChunkToDictionary ( chunk , true ); } chunkData . Deaths ++; } private void OnUseBlock ( IServerPlayer byPlayer , BlockSelection blockSel ) { if ( _serverApi . World . BlockAccessor . GetBlock ( blockSel . Position ). GetType () != typeof ( BlockBed )) { return ; } IServerChunk chunk = _serverApi . WorldManager . GetChunk ( blockSel . Position ); if (! _hauntedChunks . TryGetValue ( chunk , out HauntingChunkData chunkData )) { chunkData = AddChunkToDictionary ( chunk , false ); } int haunting = chunkData ?. Deaths ?? 0 ; if ( haunting > 0 ) { byPlayer . Entity . ReceiveDamage ( new DamageSource () { Source = EnumDamageSource . Void , Type = EnumDamageType . BluntAttack }, haunting ); } } private HauntingChunkData AddChunkToDictionary ( IServerChunk chunk , bool create ) { HauntingChunkData chunkData ; byte [] data = chunk . GetServerModdata ( "haunting" ); if ( data != null ) { chunkData = SerializerUtil . Deserialize < HauntingChunkData >( data ); } else if (! create ) { return null ; } else { chunkData = new (); } _hauntedChunks . Add ( chunk , chunkData ); if (! chunk . LiveModData . TryGetValue ( "haunting" , out object serializerObj ) || serializerObj is not SerializationCallback serializer ) { serializer = new ( chunk ); chunk . LiveModData [ "haunting" ] = serializer ; } serializer . OnSerialization += chunk => chunk . SetServerModdata ( "haunting" , SerializerUtil . Serialize ( chunkData )); return chunkData ; } } Testing Let's run the mod now! Once you're ingame, enter the command /kill. From now on, you will receive 1 damage every time you sleep in the chunk you died in. Distribution To distribute this mod, you may run the following command in the modtools cli pack , then copy the .zip file into your VintageStory mods folder. Here are the official versions: for VS v1.19.8: File:HauntedChunks.zip Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Code_Basics_Portal TITLE: Modding:Code Basics Portal --- CONTENT --- Other languages: English русский Modding - Code Basics Portal This is the portal for the basics of code modding. The following pages are part of this portal: Code Mods - The first step of code modding. Explains what a code mod is, and how they work. Preparing For Code Mods - Shows how to setup a development environment suitable for code modding, as well as how to install the appropriate modding templates. Creating A Code Mod - Shows how to create and test a new code mod. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Code Mods • Preparing For Code Mods • Creating A Code Mod --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Code_Guides_Portal TITLE: Modding:Code Guides Portal --- CONTENT --- Other languages: English русский This is the portal for code modding guides. These are informational guides of varying complexities. If you need information on how a certain code-modding topic works, this is the place to look! Code Guides Entity Instance Attributes EvolvingNatFloat GUIs Meal Containers Serialization Formats Server-Client Considerations TreeAttribute Data WorldGen Concepts Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Code_Mods TITLE: Modding:Code Mods --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . Code Mods are mods that use C# scripts to change mechanics within the game, using the type "code" . This page is in progress, and is used as a landing page for all code topics. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Code Mods • Preparing For Code Mods • Creating A Code Mod --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Code_Mods_Portal TITLE: Modding:Code Mods Portal --- CONTENT --- Other languages: English русский Modding - Code Mods Portal Code Modding Code Basics Tutorials Concepts & Guides What can code mods do? Code mods have very few limitations, and are an extension of content mods . Therefore, they can do everything content mods can do, plus a lot more. For examples, code mods can: Create new complex behaviors for blocks, items, and entities Create entirely new asset types Patch any existing code in the game Much, much more... What can code mods not do? As mentioned, code mods have very few limitations, but there are some. To be specific, code mods: Cannot change anything before a world is loaded Code Mod Examples The following are some examples of code mods: Primitive Survival by SpearAndFang - This mod introduces new traps, fishing systems, irrigation systems, and much more. Banners by DanaCraluminum - This mod adds flags and banners to Vintage Story, with an immense amount of combinations and customization. Exoskeletons by Maltiez - This mod adds a new armor set, where each piece requires temporal gear power and provides extra benefits. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Code_Tutorial_Essentials TITLE: Modding:Code Tutorial Essentials --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Modding Tutorials 2 Code Mods and Content Mods 3 Client/Server 4 Mod System 5 CoreAPI, CoreServerAPI, CoreClientAPI This page will give a breakdown of a number of topics and terminology used with code modding. Since some code topics can become quite complex, it is important to read through this page. This page will also introduce you to the coding tutorials and how to begin with them. Modding Tutorials All tutorials within the categorized sections (basic, intermediate, advanced, other) can be made in the same project as one another, as they are all created under the project name "VSTutorial". It is recommended to create a new mod with the same project name, and use this project when you wish to complete any of these tutorials. When writing code, it's important to keep your code well-organized and documented. Coding styles vary from person to person, however the tutorials will always prefer readability and understandability over efficiency. If there are instances of more efficient code, these may be mentioned at the time. Code Mods and Content Mods Code mods should not be thought of as separate to content mods. Most code mods are more of an expansion of content mods, aimed to add more functionality for new or existing content. Because of this, it is highly recommended that you are experienced with content mods before beginning code mods. Every code tutorial on the wiki will assume that you have completed, or are at least familiar with the basic and intermediate content tutorials. Client/Server Vintage Story loads all worlds by using a client/server system . There always exists one server per world, however there can be any number of clients. Each player that joins the world, including a singleplayer world, is classed as a client. Many aspects of code are split between the server and the client. In general, the client is used for rendering, inputs, and more precise physics calculations. The server determines world logic, most entity logic, and communication between all clients. Clients cannot directly communicate with other clients. In order to share data, the server must send this to appropriate clients. Mod System A Mod System is used as an entry point for your mod, and contains many useful functions for the execution of your mod. A single mod can have multiple mod systems, but must have at least one for any code to be executed. When creating a mod using the mod template, you will notice that a mod system is automatically generated with a number of functions. Some useful functions that your mod system can use are: Start - This is called on both the server and the client, and is where you should register blocks, items, entities, and most other things that need registering. StartServerSide - This is called only on the server side. You should register any server-side-only classes and logic here. Most commands will be registered here. StartClientSide - Similarly to StartServerSide, but only called on the client side. You should use this to register and rendering or UI logic here. ShouldLoad - This can be used to control what side your mod should be loaded on. You can use this to make client-only or server-only mods. ExecuteOrder - This is an extremely important function that alters when your mod is loaded. Check the GitHub link below for information on what is loaded in what order. There are many other functions that are more specific. You can view Mod System on GitHub for a full breakdown of this class. CoreAPI, CoreServerAPI, CoreClientAPI The CoreAPI class is extremely important, and is used throughout many different aspects of the game. The CoreServerAPI and CoreClientAPI are specific instances of CoreAPIs for server and client logic respectively. The CoreAPI is used to handle assets, commands, events, registries, world data, and many other things. You can find the following classes on GitHub. ICoreAPICommon - A set of functions and fields common to the server and client. ICoreAPI - Extends from ICoreAPICommon, contains a few more useful values. ICoreClientAPI - Extends from ICoreAPI. Contains useful functions and fields that can be used only on the client-side. ICoreServerAPI - Extends from ICoreAPI. Contains useful functions and fields that can be used only on the server-side. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Code Essentials • Simple Block Class • Simple Item Class • Simple Commands --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Code_Tutorial_Simple_Block TITLE: Modding:Code Tutorial Simple Block --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Introduction 1.1 Objective 1.2 Prerequisites 2 Assets 3 Block Class 3.1 Creating a Block Class 3.2 Explaining Block Classes 3.3 Registering the Block Class 3.4 Adding Block Class to Asset 4 Testing the Block Class 5 Trampoline Functionality 5.1 Making it Bounce 6 Finished Project 7 Conclusion 7.1 Next Steps... 8 Going Further Introduction Objective In this tutorial, you will be introduced to code modding by creating a block with custom functionality. You will create the assets for this project, find out how block classes are registered, and use a number of different functions. The block you will be creating is a trampoline, where as an entity collides with it, they will bounce. Please note that this tutorial is quite detailed since it is the first in the code tutorial series. If you are new to modding, it is highly recommended to follow each of these steps. Prerequisites It is recommended to use the same project for all newer code tutorials, with the project name "VSTutorial". If you have not yet done this, please follow the following tutorials: Preparing For Code Mods Creating A Code Mod This tutorial also assumes that you have read: Code Tutorial Essentials Assets Before you get started with any coding, it is important that you create the assets for your new trampoline block. In Visual Studio, your mod's assets are easy to access from the solution explorer. Right click on the folder called assets , and then select Open Folder in File Explorer . This will open the assets folder for your mod, which is an identical format to the assets folder in content mods. The opened folder should only contain another folder labeled vstutorial . You need to add a few asset files for the trampoline. As always, these can be downloaded from GitHub here . The downloaded zip file contains a folder called vstutorial. Copy this folder over your own, and the asset files will be added. You can verify this by going back to Visual Studio, looking at the solution explorer, and expanding the assets folder. You should now have a trampoline block type, as well as a lang and texture file. Run the game, launch a world, and check the creative menu. You should see the trampoline block. It looks fancy, but has absolutely no functionality yet. Playing Vintage Story in fullscreen mode? It is highly recommended to run the game in windowed mode when making code mods. If your code results in an error, Visual Studio will attempt to gain focus, and Vintage Story will stop responding. You can press F11 to easily switch between the fullscreen and windowed modes. Block Class Close the game and go back to Visual Studio. To add custom functionality to the trampoline block, you'll need to create a block class . A block class is a script that offers custom functionality for any specific blocktype assets. Note that each blocktype can only have a single attached block class. Creating a Block Class Before you create a new block class, you should create a new folder called 'Blocks'. It is good practice to keep your classes and other scripts in folders relevant to what they are. In the solution explorer, right click on VSTutorial (the project, not the one labelled solution ), hover over Add , and click New Folder . You can then enter a name for your folder. You need a name for your new block's class. Generally, classes in Vintage Story follow the naming convention of {Type}{Name}. So, in the example of a trampoline block class, you should call this block class "BlockTrampoline". With your folder now created, you need to create a new class. Right click the new folder, hover over Add , and select Class . This will open a menu with a lot of templates, and an input field to enter your class name. Make sure you have selected the standard Class at the top of the template list, and enter the name 'BlockTrampoline.cs'. Click Add , and your class will be created. The trampoline block's class should open automatically, but you can open it manually by double clicking its entry in the solution explorer. Explaining Block Classes Let's have a quick run down of how a block class works. Replace the entire contents of the new class with the following code: BlockTrampoline.cs //Here are the imports for this script. Most of these will add automatically. using Vintagestory.API.Common ; using Vintagestory.API.MathTools ; /* * The namespace the class will be in. This is essentially the folder the script is found in. * If you need to use the BlockTrampoline class in any other script, you will have to add 'using VSTutorial.Blocks' to that script. */ namespace VSTutorial.Blocks { /* * The class definition. Here, you define BlockTrampoline as a child of Block, which * means you can 'override' many of the functions within the general Block class. */ internal class BlockTrampoline : Block { //Any code within this 'override' function will be called when a trampoline block is placed. public override void OnBlockPlaced ( IWorldAccessor world , BlockPos blockPos , ItemStack byItemStack = null ) { //Log a message to the console. api . Logger . Event ( "Trampoline Block Placed!" ); //Perform any default logic when our block is placed. base . OnBlockPlaced ( world , blockPos , byItemStack ); } //Any code within this 'override' function will be called when a trampoline block is broken. public override void OnBlockBroken ( IWorldAccessor world , BlockPos pos , IPlayer byPlayer , float dropQuantityMultiplier = 1 ) { //Log a message to the console. api . Logger . Event ( "Trampoline Block Broken!" ); //Perform any default logic when our block is broken (e.g., dropping the block as an item.) base . OnBlockBroken ( world , pos , byPlayer , dropQuantityMultiplier ); } } } Note the code comments throughout the file. These are marked by double slashes ( // ) or any text between /* and */. These give brief explanations on what every line of this example does. For further explanation, on line 15, the BlockTrampoline class is defined as a child of the Block class, which is in turn a child of the CollectibleObject class, and then the RegistryObject class . This essentially means that any function within these three inherited classes marked as ' virtual ', ' abstract' , or ' override' can be given custom functionality. For an exhaustive list of functions and attributes that can be overriden, take a look at the API documentation for the Block and CollectibleObject methods. Why so many code comments? There is much debate about how many code comments are too many, and when/where they should be used. Although many people who are experienced with C# or other C programming languages may find this code easy to understand, this tutorial is designed for those who may not have ever used a C programming language, or even any programming language. Registering the Block Class The class for the trampoline class is created, however before it can be used it needs to be registered through the game API. When you created your mod, a mod system will have been automatically made. Look for it in your solution explorer, it should be called VSTutorialModSystem , and double click it to open it. Your mod system will likely contain three functions at this point - Start, StartServerSide, and StartClientSide. The server and client side functions are not required here, so feel free to delete them or keep them in for the future. Either is fine. In the Start function, you need to make a call to the RegisterBlockClass function in the API class. Add the following code on a new line inside of the Start function's block: api . RegisterBlockClass (); You'll notice that, currently, this results in an error. Hover over the 'RegisterBlockClass' with your mouse and it will show you a breakdown of the function. This tells you the function's parameters and a description of what it does. Not all functions in the code have descriptions attached to them, however as the modding API becomes more documented, these will hopefully become more populated. In this particular case, you need to pass in a class name, and a block type to the function. The class name argument should be fully lowercase, and should generally be your mod ID, joined with the name of the class you just made. The blockType will be the class you just made. Replace the function with the following: api . RegisterBlockClass ( Mod . Info . ModID + ".trampoline" , typeof ( BlockTrampoline )); This will register the BlockTrampoline class with the name "vstutorial.trampoline". Why include the mod id here? If you've made code mods before the introduction of these new tutorials, you probably haven't included your mod ID when registering classes. As more and more mods are being made, there are occasional collisions in regards to class names. Say, for example, two mods both add in a 'trampoline' block class. Only one instance of the trampoline class will exist, which can cause issues if they had slightly different functionality. So, including your mod ID when registering classes will ensure that these 'collisions' do not occur. Adding Block Class to Asset Before your block class will work, you need to add a new property to the blocktype JSON asset. In the solution explorer, open the file at assets/vstutorial/blocktypes/trampoline.json. You need to add the following property to the file: "class" : "vstutorial.trampoline" , Generally, this element is placed as the second property in your json file, immediately below the code property. Note that the value of this property is identical to the value we used in the RegisterBlockClass function. This is how the game links the JSON asset files to your code's registered classes. Testing the Block Class Press F5 again to run the game with the mod. Remember to set the game to windowed mode by pressing F11. Find the trampoline in the creative menu, place one, and destroy it. After doing so, take a look at the console that opened when launching the game. You should be able to see the following logs: [Client Event] Trampoline Block Placed! [Server Event] Trampoline Block Placed! [Client Event] Trampoline Block Broken! [Server Event] Trampoline Block Broken! Wait... what? The trampoline block was placed once, and broken once, so why are there two entries? Notice that each entry tells you whether it came from the client or server, and in this case, we have one of each. Due to running a singleplayer instance, there is both a client and server running at the same time. The overriden functions are being called once on the client, and once on the server. In many instances, this is exactly what is wanted. Most features are synced between the client and server, but it is important to remember that many functions get called twice in this system. There are a number of ways you can verify what 'side' is calling the function, or to limit certain code to one side. Anyway, it's clear that the trampoline class is working, so close the game and go back to the BlockTrampoline class. Trampoline Functionality You need to create the actual functionality for the trampoline. When an entity collides with this block, the entity's vertical velocity should be reversed and multiplied by a certain amount. The placed and break functions are unnecessary for this block, so you can remove them. Your script should look like the following: BlockTrampoline.cs //Here are the imports for this script. Most of these will add automatically. using Vintagestory.API.Common ; using Vintagestory.API.MathTools ; /* * The namespace the class will be in. This is essentially the folder the script is found in. * If you need to use the BlockTrampoline class in any other script, you will have to add 'using VSTutorial.Blocks' to that script. */ namespace VSTutorial.Blocks { /* * The class definition. Here, you define BlockTrampoline as a child of Block, which * means you can 'override' many of the functions within the general Block class. */ internal class BlockTrampoline : Block { } } Click on the empty space, and type 'override ' (including the space). Visual Studio will show you a scrollable list of functions that can be overriden. If the menu closes, you can simply delete the space and replace it, and the menu will return. Feel free to scroll through this menu and take a look at what is available. The functions here are all listed in the API documentation mentioned earlier. The function you need to use is called OnEntityCollide . With the mentioned menu above, type 'collide', and you should see a single result in the list. Press enter, tab, or double click on the entry, and Visual Studio will generate the function for you. Take a look at the supplied arguments for this function. You have access to the world, the entity, the block's position, the 'facing' position of the collision, the collision speed, and whether this is an impact. Making it Bounce The following points determine when an entity should bounce on the trampoline block. The entity should bounce in the moment it lands on top of the block, and not if it is standing on it already. Therefore, isImpact needs to be true . The entity should be colliding vertically. The sides of the block shouldn't push an entity away. In effect, facing.IsVertical needs to be true . So, add the following block inside the function: if ( isImpact && facing . IsVertical ) { } Now, you need to flip the motion of the entity. To do this, you can change the value at entity.Pos.Motion.Y. Add the following code inside the if block. entity . Pos . Motion . Y *= - 0.8f ; This is a quicker way of writing: entity . Pos . Motion . Y = entity . Pos . Motion . Y * - 0.8f ; Multiplying by -0.8 will result in reversing the velocity, and reducing it by 20%. Feel free to play with this value to see some different effects. The final script should look like the following: BlockTrampoline.cs //Here are the imports for this script. Most of these will add automatically. using Vintagestory.API.Common ; using Vintagestory.API.Common.Entities ; using Vintagestory.API.MathTools ; /* * The namespace the class will be in. This is essentially the folder the script is found in. * If you need to use the BlockTrampoline class in any other script, you will have to add 'using VSTutorial.Blocks' to that script. */ namespace VSTutorial.Blocks { /* * The class definition. Here, you define BlockTrampoline as a child of Block, which * means you can 'override' many of the functions within the general Block class. */ internal class BlockTrampoline : Block { public override void OnEntityCollide ( IWorldAccessor world , Entity entity , BlockPos pos , BlockFacing facing , Vec3d collideSpeed , bool isImpact ) { if ( isImpact && facing . IsVertical ) { entity . Pos . Motion . Y *= - 0.8f ; } } } } Give it a test! Falling onto your trampoline block should cause you to bounce! Finished Project You can download the entire finished project from GitHub here . This is a full project setup that includes code comments on how everything works. Conclusion Congratulations, you've created, registered and tested a new block class! Although it's been a long tutorial, you've covered many topics here that will give you a great understanding of how coding for Vintage Story works. Next Steps... If you want to test your knowledge consider doing the tasks under the Going Further section below. When you're ready, take a look at the next tutorial. This will show you how to give extra functionality to an item. Going Further Want to make some additional changes to this mod? Try and achieve the following things! Make the trampoline super bouncy. To achieve this... Replace the -0.8 value with -1.5 or lower. Although setting to -1 would technically reflect the motion perfectly, some velocity is lost on the frame the entity collides with the block. Change the functionality of the block to allow an entity to bounce on any side of the block. You should use facing.IsAxisNS and facing.IsAxisWE to determine what side the entity collides with. To achieve this... You need to replace the contents of the OnEntityCollide function with something similar to the following: if ( isImpact && facing . IsVertical ) { entity . Pos . Motion . Y *= - 0.8f ; } else if ( facing . IsAxisNS ) { entity . Pos . Motion . Z *= - 1.2f ; } else if ( facing . IsAxisWE ) { entity . Pos . Motion . X *= - 1.2f ; } Note that you will need to jump into the trampoline block to bounce off of it. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Code Essentials • Simple Block Class • Simple Item Class • Simple Commands --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Code_Tutorial_Simple_Item TITLE: Modding:Code Tutorial Simple Item --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.20.3 . Contents 1 Introduction 1.1 Objective 1.2 Prerequisites 2 Assets 3 Item Class 3.1 Creating an Item Class 3.2 Registering the Item Class 3.3 Adding Item Class to Asset 4 Testing the Item Class 5 Thorns Blade Functionality 5.1 Reflecting Damage 6 Conclusion 6.1 Next Steps... 7 Going Further Introduction Objective In this tutorial, you will be creating an item with basic custom functionality. You will find out how item classes are registered, and how to interact further with entities. The item you will be creating is a new 'thorny' sword, which will slightly hurt the player when used to attack. Prerequisites It is recommended to use the same project for all newer code tutorials, with the project name "VSTutorial". If you have not yet done this, please follow the following tutorials: Preparing For Code Mods Creating A Code Mod This tutorial also assumes that you have read: Code Tutorial Essentials It is also highly recommended to have completed previous tutorial in the code series: Simple Block Class Assets This tutorial uses the finished project from the previous tutorial. If you need to, you can download the finished version of the previous tutorial from GitHub here . As usual, you need the required assets for this tutorial. These can be downloaded from GitHub here , and should replace the entirety of your assets folder as it also contains the previous tutorial's assets. When these files are added, you should have new itemtype, shape, and texture files. Run the game, launch a world, and check the creative menu. You should be able to find the "Thorns Blade". Playing Vintage Story in fullscreen mode? It is highly recommended to run the game in windowed mode when making code mods. If your code results in an error, Visual Studio will attempt to gain focus, and Vintage Story will stop responding. You can press F11 to easily switch between the fullscreen and windowed modes. Item Class Close the game and go back to Visual Studio. To add custom functionality to the item, you'll need to create an item class . An item class is a script that offers custom functionality for any specific itemtype assets. Note that each itemtype can only have a single attached item class. Creating an item class is extremely similar to creating a block class. Creating an Item Class To keep the code organized, you should create a new folder called "Items". Right click on your project, hover over add, and select New Folder . You also need a name for the new item class. These follow the same convention as the Blocks, and most other types. In this case, our thorny blade item class, you should name this class "ItemThornsBlade". Create your new class. Right click the new folder, hover over Add , and select Class . Ensure you have the standard Class selected in the template list, enter your class name, and click create. The class you created needs to extend the 'Item' class. This will allow you to use many of the available functions for items. Change the class definition to now be: internal class ItemThornsBlade : Item Most of the time, Visual Studio will automatically add 'using' statements. However, if this errors, click on "Item", press Alt+Enter, and select the "using Vintagestory.API.Common" option. The ItemThornsBlade class now exists, but it does nothing. Time to override a function, I suppose. Inside the empty class, type "override " and a list of methods we can use will be displayed. In this instance, you will want to override the 'OnAttackingWith' function. This function is called whenever our item is used to attack an entity. Search for this in the list and double click on it to automatically add the required code sample. Your class should now look like this: ItemThornsBlade.cs using Vintagestory.API.Common ; using Vintagestory.API.Common.Entities ; namespace VSTutorial.Items { internal class ItemThornsBlade : Item { public override void OnAttackingWith ( IWorldAccessor world , Entity byEntity , Entity attackedEntity , ItemSlot itemslot ) { base . OnAttackingWith ( world , byEntity , attackedEntity , itemslot ); } } } Note that the the sample code in 'OnAttackingWith' contains a function called 'base.OnAttackingWith'. This essentially runs any default behavior for the function. If you were to remove this line then any default behavior would not happen, and, in this case, the item will not lose any durability when used for attacking. In the OnAttackingWith function, you are given access to the world, the entity who is attacking, the entity who is being attacked, and the itemslot that this item exists in. For testing purposes, add some code that outputs a message to the console when we attack an entity. You'll need to add the following code below the ' base ' line: world . Api . Logger . Event ( "Got attack with thorns blade!" ); You can access the current game API from a lot of places. This particular line accesses the API through the world, however it can also be accessed through any entity with very similar code. You should be aware that this will create a message to the console when we attack an entity with the new item. Registering the Item Class The class for the thorns blade is created, however before it can be used it needs to be registered through the game API. In your mod system, which you used in the previous tutorial, you need to register an item class for both the client and server. In the Start function, make a call to the RegisterItemClass function in the API. Add the following code on a new line inside of the Start function's block: api . RegisterItemClass (); Similarly to the previous tutorial, this will result in an error. You'll need to add some parameters to the function. Don't forget, you can hover over the function with your mouse and the required parameters will be shown. In this case, we once again need a class name, and a type. Your class name argument should be fully lowercase, and should generally be your mod ID, joined with the name of the class you just made. The itemType will be the class you just made. Replace the function with the following: api . RegisterItemClass ( Mod . Info . ModID + ".thornsblade" , typeof ( ItemThornsBlade )); This will register the ItemThornsBlade class with the name "vstutorial.thornsblade". Why include the mod id here? If you've made code mods before the introduction of these new tutorials, you probably haven't included your mod ID when registering classes. As more and more mods are being made, there are occasional collisions in regards to class names. Say, for example, two mods both add in a 'trampoline' block class. Only one instance of the trampoline class will exist, which can cause issues if they had slightly different functionality. So, including your mod ID when registering classes will ensure that these 'collisions' do not occur. Adding Item Class to Asset Before your item will work, you need to add the class to the asset file. Open the file in assets/vstutorial/itemtypes/thornsblade.json . You need to add the following property to the file: "class" : "vstutorial.thornsblade" , Generally, this element is placed as the second property in your json file, immediately below the code property. Note that the value of this property is identical to the value we used in the RegisterItemClass function. This is how the game links the JSON asset files to your code's registered classes. Testing the Item Class Press F5 again to run the game with the mod. Remember to set the game to windowed mode by pressing F11. Find the thorns blade in the creative menu, and use it to attack an entity.After doing so, take a look at the console that opened when launching the game. You should be able to see the following logs: [Client Event] Got attack with thorns blade! [Server Event] Got attack with thorns blade! Remember what's going on here? Although you only attacked the entity once, there are two entries. One of these is sent to the console by the client, and the other is sent by the server. You should start to become aware that a lot of the code you write is going to happen on both the client and server. You'll be shown ways of controlling this later. The thorns blade class and function are working, so close the game and go back to the ItemThornsBlade class. Thorns Blade Functionality You now need to add the real functionality for the thorns blade. Remove the line of code that logs the attack event, since you don't need that anymore. Your code should look like the following. Feel free to copy and paste this, as it contains some useful code comments too. ItemThornsBlade.cs using Vintagestory.API.Common ; using Vintagestory.API.Common.Entities ; namespace VSTutorial.Items { /* * As this is an item, you need to inherit the Item class. This gives access to functions within Item, and CollectibleObject. * Take a look at https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Item.html#methods and * https://apidocs.vintagestory.at/api/Vintagestory.API.Common.CollectibleObject.html#methods for all the methods that can be overriden. */ internal class ItemThornsBlade : Item { /* * This function is called whenever this item is used by an entity to attack another entity. * You have access to the world, the entity who is attacking, the entity who is being attacked, and the held item's data. */ public override void OnAttackingWith ( IWorldAccessor world , Entity byEntity , Entity attackedEntity , ItemSlot itemslot ) { base . OnAttackingWith ( world , byEntity , attackedEntity , itemslot ); } } } Note that any further code in this function can be placed above or below the 'base' call. In this case, the order does not matter. Reflecting Damage To inflict damage on an entity, there are two small steps: A DamageSource instance must be created. The damage source must be inflicted on the entity using the Entity.ReceiveDamage function. Before you can inflict damage on an entity, you must create a damage source. This includes the type of damage, and can contain information about where the damage has come from. Note that it does not contain the amount of damage. DamageSource damage = new DamageSource () { Type = EnumDamageType . PiercingAttack , SourceEntity = byEntity , KnockbackStrength = 0 }; This creates a new instance of damage source called 'damage'. Type can be any instance of EnumDamageType , but for this purpose you should use PiercingAttack. CauseEntity is the entity that caused the damage, which should be whatever is using our sword. Now the damage source is created, you need to inflict it upon the entity. The entity that used the item is 'byEntity', so you'll want to damage them: byEntity . ReceiveDamage ( damage , 0.25f ); This will inflict 0.25 points of damage onto the entity, using our 'damage' instance we just created. And that's it. When the item is used to attack, 0.25 points of damage will be inflicted on the player using the weapon. The full code should be as follows: ItemThornsBlade.cs using Vintagestory.API.Common ; using Vintagestory.API.Common.Entities ; namespace VSTutorial.Items { /* * As this is an item, you need to inherit the Item class. This gives access to functions within Item, and CollectibleObject. * Take a look at https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Item.html#methods and * https://apidocs.vintagestory.at/api/Vintagestory.API.Common.CollectibleObject.html#methods for all the methods that can be overriden. */ internal class ItemThornsBlade : Item { /* * This function is called whenever this item is used by an entity to attack another entity. * You have access to the world, the entity who is attacking, the entity who is being attacked, and the held item's data. */ public override void OnAttackingWith ( IWorldAccessor world , Entity byEntity , Entity attackedEntity , ItemSlot itemslot ) { DamageSource damage = new DamageSource () { Type = EnumDamageType . PiercingAttack , SourceEntity = byEntity , KnockbackStrength = 0 }; byEntity . ReceiveDamage ( damage , 0.25f ); base . OnAttackingWith ( world , byEntity , attackedEntity , itemslot ); } } } Go ahead and test the new sword. You'll need to be in survival mode, but you should find that hitting an entity will also inflict damage on yourself. Conclusion Congratulations, you've created, registered and tested a new item class! Next Steps... If you want to test your knowledge consider doing the tasks under the Going Further section below. When you're ready, take a look at the next tutorial. This will show you how to register and add commands into the game! Going Further Want to make some additional changes to this mod? Try and achieve the following things! Currently, the sword will damage you even if it is used on mobs that are dead. Make it inflict damage only if the entity being attacked is alive. To achieve this... Check the 'Alive' field on 'attackedEntity' is true before inflicting damage. if ( attackedEntity . Alive ) { byEntity . ReceiveDamage ( damage , 0.25f ); } Using the revive function in the Entity class, make the sword revive any dead entities hit by it. To achieve this... Check the 'alive' field is false, and then call the Revive function on 'attackedEntity'. if (! attackedEntity . Alive ) { attackedEntity . Revive (); } Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Code Essentials • Simple Block Class • Simple Item Class • Simple Commands --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Code_Tutorials_Portal TITLE: Modding:Code Tutorials Portal --- CONTENT --- Other languages: English русский Modding - Code Tutorials Portal This is the portal for the code modding tutorials. The following pages are part of this portal: Basic Code Tutorials - A set of essential tutorials for learning the basics of code modding. Intermediate Code Tutorials - A set of tutorials for learning some important aspects to code modding. Advanced Code Tutorials - A set of tutorials for learning more complex aspects of code modding. Other Code Tutorials - Miscellaneous code tutorials of varying complexity. --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Collectible_Behaviors TITLE: Modding:Collectible Behaviors --- CONTENT --- This page is outdated. Reason: A new system is being worked on to view JSON properties. Please use this page with caution, as it may not be up-to-date. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Using Collectible Behaviors Collectible behaviors can be added to items and blocks through the behaviors array in the block json file at the top level (same level as code ). Note that for blocks, this array also lists block behaviors . The properties dictionary is optional if the behavior does not have any properties. Example: { code : "wrench" , ... behaviors : [{ na me : "GroundStorable" , proper t ies : { layou t : 'WallHalves' , wallO ff Y : 1 , spri nt Key : true , selec t io n Box : { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 0.1 , z 2 : 1 }, collisio n Box : { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 0 , y 2 : 0 , z 2 : 0 }, } }], ... } All Behaviors Here is a table containing all the collectible behaviors of the base game as of 1.19.8. Block behaviors are listed separately , even though they are technically collectible behaviors too. Behavior Name Properties Used by Blocks & Items GroundStorable Layout WallOffY PlaceRemoveSound RandomizeSoundPitch StackingModel TessQuantityElements ModelItemsToStackSizeRatio StackingTextures MaxStackingHeight StackingCapacity TransferQuantity BulkTransferQuantity SprintKey UpSolid CollisionBox SelectionBox CbScaleYByLayer MaxFireable Most tools and ingots ArtPigment paintableOnBlockMaterials decorBlockCodes consumeChance charcoal limonite nugget hematite nugget chalk AnimationAuthoritative strikeSound onlyOnEntity axe blade cleaver club hammer knife spear Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Commands TITLE: Modding:Commands --- CONTENT --- Other languages: English русский This page is a candidate to be rewritten. This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. This page was last verified for Vintage Story version 1.19.8 . Ever wanted to know how to add a command to the game? If yes, this is the right place to get an answer. Preparations The idea is to add a command which makes it easier for your friends to locate you. Therefore we will spawn some particles alongside a playing sound, so others can see and hear where you are at the moment. Implementation As always, we need to create a class extending ModSystem : public class CommandMod : ModSystem { } We will be creating a server-side command, so our mod only needs to load on the server's side: public override bool ShouldLoad ( EnumAppSide side ) { return side == EnumAppSide . Server ; } Now we need to create the command itself: public override void StartServerSide ( ICoreServerAPI api ) { base . StartServerSide ( api ); AssetLocation sound = new AssetLocation ( "sounds/effect/smallexplosion" ); api . ChatCommands . Create ( "here" ) . WithDescription ( "spawns particles around the player" ) . RequiresPrivilege ( Privilege . chat ) . RequiresPlayer (); } This command can be used by any player who is allowed to send a message (by default everyone). When a player types in /here , the command will be executed. Now we the only thing missing is the actual code to spawn particles and to play the sound. public override void StartServerSide ( ICoreServerAPI api ) { base . StartServerSide ( api ); AssetLocation sound = new AssetLocation ( "sounds/effect/smallexplosion" ); api . ChatCommands . Create ( "here" ) . WithDescription ( "spawns particles around the player" ) . RequiresPrivilege ( Privilege . chat ) . RequiresPlayer () . HandleWith (( args ) => { var byEntity = args . Caller . Entity ; byEntity . World . PlaySoundAt ( sound , byEntity ); Vec3d pos = byEntity . Pos . XYZ . Add ( 0 , byEntity . LocalEyePos . Y , 0 ); Random rand = new Random (); for ( int i = 0 ; i < 100 ; i ++) { Vec3d realPos = pos . AddCopy (- 0.1 + rand . NextDouble () * 0.2 , 0 , - 0.1 + rand . NextDouble () * 0.2 ); Vec3f velocity = new Vec3f (- 0.2F + ( float ) rand . NextDouble () * 0.4F , 0.4F + ( float ) rand . NextDouble () * 2F , - 0.2F + ( float ) rand . NextDouble () * 0.4F ); byEntity . World . SpawnParticles ( 1 , ColorUtil . ToRgba ( 255 , rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 )), realPos , realPos , velocity , velocity , ( float ) rand . NextDouble () * 1 + 1 , 0.01F , 1 , EnumParticleModel . Cube ); } return TextCommandResult . Success (); }); } If you want to learn more about how to use particles you can check out this tutorial . Testing Finally, we are ready to run our first test: Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Community_Resources TITLE: Modding:Community Resources --- CONTENT --- Other languages: English русский Contents 1 Introduction 1.1 Adding to this page 2 Tools for Content Mods 3 Tools for Code Mods 4 Other Resources Introduction There are a number of resources created by the community to assist with creating mods. These are listed below. Adding to this page To add to this page, it is recommended to ask someone who has wiki-editing access. Or, create a wiki account and ask for wiki edit access in #wiki-and-translations on Discord . Don't forget to include your wiki username. Using the visual editor, insert a new table row in the appropriate category and fill in all the details. Tools for Content Mods Tool Name Author Link For VS Version... Description Achievements API Nat (Nateonus) https://mods.vintagestory.at/show/mod/9615 1.19+ Add achievements into your mods using a simple JSON format. Config lib Maltiez https://mods.vintagestory.at/configlib 1.19+ Add configuration files editable via GUI Recipe Patcher DanaCraluminum https://mods.vintagestory.at/recipepatcher 1.19+ Patches existing recipes before resolving them to replace ingredients and/or outputs. PatchDebug goxmeor https://mods.vintagestory.at/show/mod/513 1.20+ Utility to debug patches, displaying the contents of affected files before and after applying patches. Prettier VS Rhoun https://mods.vintagestory.at/prettiervs - Utility to automatically format JSON in a compact yet readable way. ExtraCode jayu https://mods.vintagestory.at/extracode 1.19+ Adds extra classes and attributes for content mods to use VTML Editor jayu https://mods.vintagestory.at/vtmleditor 1.20+ View and edit VTML lang entries in game VSContentModTemplate jayu https://mods.vintagestory.at/dotnetmodtmplts - Simple template for creating Vintage Story content mods Tools for Code Mods Tool Name Author Link For VS Version... Description VSCodeModTemplate jayu https://mods.vintagestory.at/dotnetmodtmplts - Template for Vintage Story code mods with some more advanced options Other Resources Tool Name Author Link For VS Version... Description Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Using the Modding Wiki • Getting Started • Types of Mod • Other Resources "" Community Resources • Programming Languages • Inbuilt ModMaker AnegoStudios Mod Examples • Nat's Mod Examples • Code API Docs • JSON/Content Docs • AnegoStudios GitHub --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:CompatibilityLib TITLE: Modding:CompatibilityLib --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . A simple embeded library for Vintage Story that makes it easy to add compatibility with other mods for assets. Contents 1 Easy way (overwriting assets) 1.1 Usage example 2 Mod-dependent json-patch 2.1 Usage example Easy way (overwriting assets) You just need to add your asset to assets//compatibility// . They will only be loaded if is loaded. If assets// and assets//compatibility// exists then if is loaded, the first asset will be rewritten. Usage example I have a More Variants mod ( morevariants ), I want to add a patch to support Carry Capacity ( carrycapacity ) and add recipes for Better Chests ( betterchests ). As a result, the assets will look like this: - assets - morevariants - blocktypes - patches - recipes - compatibility - carrycapacity - patches - carryable.json - betterchests - recipes - grid - copperchest.json - copperlabeledchest.json Mod-dependent json-patch You can use dependsOn[] in a JSON patch to create a mod-dependent patch. Do not use this for patches of other patches as this will lead to undefined behavior. If you want to change a patch from another mod, use the method detailed above to overwrite those assets. Usage example dependsOn[{"modid": "morerecipes"}] - loaded if the morerecipes mod is enabled dependsOn[{"modid": "morerecipes", "invert": true}] - loaded if the morerecipes mod is disabled dependsOn[{"modid": "morerecipes"}, {"modid": "captureanimals"}] - loaded if the morerecipes AND captureanimals mods are enabled dependsOn[{"modid": "morerecipes"}, {"modid": "captureanimals", "invert": true}] - loaded if the morerecipes mod is enabled AND the captureanimals mod is disabled [ { // I f you add e na bled : false t o your recipe , you ca n simply e na ble i t whe n t he desired mod is loaded "file" : "recipes/grid/best-other-fish-recipe.json" , "op" : "replace" , "path" : "/enabled" , "value" : true , "dependsOn" : [{ "modid" : "primitivesurvival" }] }, { // O t herwise , jus t disable t he recipe whe n t he mod is n o t loaded "file" : "recipes/grid/best-fish-recipe.json" , "op" : "add" , "path" : "/enabled" , "value" : false , "dependsOn" : [{ "modid" : "primitivesurvival" , "invert" : true }] }, { // Or whe n t wo mods are loaded : P "file" : "recipes/grid/best-fish-recipe-with-acorns.json" , "op" : "replace" , "path" : "/enabled" , "value" : true , "dependsOn" : [ { "modid" : "primitivesurvival" }, { "modid" : "acorns" } ] }, { // For simplici t y , you ca n pa t ch all recipes i n a f older a t o n ce wi t h * "file" : "recipes/grid/morerecipes-disable/*" , "op" : "add" , "path" : "/enabled" , "value" : false , "dependsOn" : [{ "modid" : "morerecipes" }] }, { // I n some cases you nee d t o speci f y which recipe you wa nt t o cha n ge i f t hey were crea te d i n a n array. For more see JSON Pa t chi n g wiki page "file" : "recipes/grid/best-fish-recipes.json" , "op" : "add" , "path" : "/0/enabled" , "value" : false , "dependsOn" : [{ "modid" : "primitivesurvival" }] } ] Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Debugging Content • API Updates • Modding Efficiently • Remapping Objects • Mod Compatibility • All Troubleshooting Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Basics_Portal TITLE: Modding:Content Basics Portal --- CONTENT --- Other languages: English русский Modding - Content Basics Portal This is the portal for the basics of content modding. The following pages are part of this portal: Content Mods - The first step of content modding. Explains what a content mod is, and how they work. Developing a Content Mod - Setting up a content mod, including a template and some essential information. Mod Packaging - How to package your mod, to allow other people to play it. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Content Mods • Developing a Content Mod • Packaging & Release --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Guides_Portal TITLE: Modding:Content Guides Portal --- CONTENT --- Other languages: English русский This is the portal for content modding guides. These are informational guides of varying complexities. If you need information on how a certain content-modding topic works, this is the place to look! Fundamental Content Guides Debugging Content Variants Other Content Guides Asset System Grid Recipes Guide Mod Compatibility Mod Info NatFloat Patching Assets Remapping Objects Theme Packs Tree Generation VTML WorldGen Configuration Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Mods TITLE: Modding:Content Mods --- CONTENT --- Other languages: English español français русский This page was last verified for Vintage Story version 1.19.8 . Contents 1 Content Mod Usage 1.1 Adding Content 1.2 Modifying Game Content 2 First Steps 2.1 Creating your own Content Mod 2.2 Following the Wiki Tutorials Content Mods are mods that add or change existing JSON assets within the game, using the type "content" . Content Mod Usage As stated in Getting Started , Vintage Story contains an extensive system to add and modify game content by just using JSON files. If you plan on modifying game content, it is recommended to backup your assets folder before beginning. Adding Content Nearly all of Vintage Story's assets are created in JSON files, and this is identical to how modders can add new content. By navigating the Asset System for the game, you can see thousands of in-game examples that can be edited and expanded upon. New blocks, items, entities and more can be added by simply creating new files based on these existing JSON files. Modifying Game Content To allow modders to make small modifications to existing JSON files, Vintage Story uses a detailed patching system that is built in to the game. For an extensive look on how to make small changes and patch files, see the JSON Patching modding page. First Steps Creating your own Content Mod For a content mod to work, it needs to be set up correctly. See Developing a Content Mod for information on how to create, navigate, and update a content mod. Following the Wiki Tutorials When you are familiar with how to create a content mod, consider taking a look at the basic tutorials . These will guide you through creating some simple blocks, items, and recipes, as well as giving you information on some important systems for modding. The Content Modding navigation box below shows all articles related to content modding, and will not require any C# or programming knowledge. Content mods can be quite complex at times, so it is recommended to start with the basic and intermediate tutorials before you start with making mods. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Content Mods • Developing a Content Mod • Packaging & Release --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Mods_Portal TITLE: Modding:Content Mods Portal --- CONTENT --- Other languages: English русский Modding - Content Mods Portal Content Modding Content Basics Tutorials Concepts & Guides What can content mods do? Content mods are often more simplistic than code mods, however their limitations mean they cannot add entirely new systems or mechanics into the game. So, what can you do with content mods? This list is not exhaustive, but you can: Create and modify: Blocks Items Entities Recipes Textures 3D Models ( Shapes ) World Generation Features Game Configurations Each one of these entries is known as an ' Asset Type '. Content mods allow you to create, edit, and remove assets at your will. What can content mods not do? As mentioned earlier, content mods cannot add entirely new systems or mechanics into the game. Things such as the following cannot be achieved with just content mods: Creating new AI Tasks for entities Creating new logic or systems for blocks and items Creating new types of recipe systems Changing how specific objects are rendered Creating new behaviors for entities In general, anything that is not adding onto something that already exists will require a code mod to achieve. Content Mod Examples The following are some examples of content mods: BetterRuins by NiclAss - This mod adds in new items, config settings, recipes, and most of all new structures. FOTSA:Pantherinae by TenthArchitect - This mod adds in new entities, items, shapes and textures to create a set of big cats! TerraPrety by Nephelangelo - This mod changes the world generation settings to completely redesign how the world is generated! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Basics TITLE: Modding:Content Tutorial Basics --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.20.4 . This page should give you a basic understanding of topics that may be used within the content tutorials. If there is ever a phrase or topic you are unsure about, it is recommended to check here first to see if it has an explanation. Contents 1 Shape Files 2 Texture Files 2.1 Modifying Textures 3 Lang(uage) File 4 What's Next? Shape Files A shape file is a 3d model of a block, item, or entity. They are usually stored within the shapes folder in your mod's assets folder. When referencing a shape in a json file, you can use the following example property: "shape" : { "base" : "item/simplewand" }, Note that the value automatically looks in your mod folder, unless a domain is provided. In this case for the example mod, the path to the shape is " assets/examplecontentmod/shapes/item/simplewand.json ". You do not need to provide the full path, nor do you need to provide the file type. Texture Files A texture file is a 2d image for a block, item, or entity. Textures are mapped onto shape files using UVs , allowing us to use different textures without having to create new shape files. Textures are assigned on both shape files, and the block, item or entity file that is using them ( see Modifying Textures below) . The following property exists in our shape file: "textures" : { "base" : "item/simplewand" } Note that we can use multiple textures in a single shape file: "textures" : { "head" : "item/wand-blue" , "handle" : "item/wand-handle" } In the ' textures ' block, we have a list of keys and values. The key refers to the name of the texture inside the shape file, and the value refers to the file path of the texture. In the first instance, similar to when we reference our shape file, the game will search in our mod's texture folder as no domain was given. In this case for the example mod, the path is " assets/examplecontentmod/textures/item/simplewand.png ". Note again that you do not need to provide the full path or provide the file type. Modifying Textures Textures can not only be set within a shape file, but can also be set within the block, item or entity using the shape file. If our shape only has a single texture, we can use the following property within our block/item/entity: "texture" : { "base" : "item/simplewand" } Note that the above is texture , not textures . Also note that when using a single texture, the key is always "base", regardless of the texture name in the shape file. To change multiple textures in a block/item/entity, use the following: "textures" : { "head" : { "base" : "item/wand-blue" }, "handle" : { "base" : "item/wand-handle" } }, Note that as we are changing multiple textures, we use textures . Also note that " head " and "handle " match with the textures we are changing in the shape file. Remember that since we are not specifying a domain, the game will search for these textures within the mod's assets. Lang(uage) File A lang file is a list of translations for a mod. To ensure that mods can be easily translated, the game automatically creates one or more translation keys for each asset. These keys are based on the asset code and type. An example en.json lang file may look like this: { "block-simplyshinyblock" : "(Example Mod) Simple Gold Block" , "item-simplewand" : "(Example Mod) Simple Wand" } Each 'key' matches to a string that affects how the item or block is named in game. Note that there are many available languages in Vintage Story, and these can be found in the base "assets/game/lang/languages.json" file . What's Next? Now you have an understanding of these topics, it is a good idea to move onto the first tutorial , where you will create your first item. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Content Essentials • Simple Item • Simple Block • Simple Recipe --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Block_Variants TITLE: Modding:Content Tutorial Block Variants --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.20.4 . Contents 1 Introduction 1.1 Objective 1.2 Assets 1.3 Prerequisites 2 Navigating Assets 3 Defining Variants 4 Variant-Based Textures 5 Conclusion 5.1 Next Steps... 6 Going Further Introduction Objective The variant system has an extensive amount of uses. In this tutorial, we will create a set of blocks with independent textures, using two separate variant groups. These blocks will show you how you can mix variant groups together to create a huge number of new assets. Assets Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here. This tutorial starts with the following assets: Mod Setup & Folder Structure Template advanced shiny block file Completed lang file Block texture files Prerequisites This tutorial will assume you understand the following topics: Setting up a content mod and using an IDE. The functions of shape, texture and lang files. It is recommended to have completed the following tutorials: 3. Simple Block - The simple block made in this tutorial is the basis for this tutorial. 5. Item Variants - This tutorial gives a good beginning stage for creating variants. These tutorials will also be formatted very similarly. It is recommended, but not necessary, to understand the following concept: Variants Navigating Assets Using the downloaded workspace, have a look at the mod assets that currently exist. blocktypes/advancedshinyblock.json - This blocktype file is from the finished Simple Block tutorial. lang/en.json - This already contains the entries needed for the tutorial. textures/block/shiny... - The four texture files for our blocks. Notice that we are using gold and iron textures, and both have 'damaged' states. Defining Variants All work in this tutorial will be done in the blocktypes / advancedshinyblock.json file. Open it in your IDE and you can get started. Firstly, the item's code is still set to " simplegoldblock" . Change this to " advancedshinyblock" . Before you can utilize any variant systems, you will need to define the variants for your block. To do this, use the "variantGroups" property. This is usually placed directly below the code property, but it would work anywhere in the object. For this block, we are going to create two variant groups. "variantgroups" : [ { "code" : "type" , "states" : [ "gold" , "iron" ] }, { "code" : "condition" , "states" : [ "good" , "used" ] } ], This example creates two variant groups - Named " type " and " condition ". When you use more than one variant group, the game will create block codes using every permutation of those two groups. If you were to test the mod now, you will see that there exists four block objects with the codes: advancedshinyblock-gold-good advancedshinyblock-gold-used advancedshinyblock-iron-good advancedshinyblock-iron-used Notice that the order your variant groups are defined affects the order they appear in the ID. When using variants, especially when using wildcards, this is important to remember. As you probably expected by now, these blocks do not have the appropriate textures. Variant-Based Textures As explained in the Item Variants tutorial, there are two ways of using variants - ByType and Substitution . In this tutorial, you're going to use both methods. Before continuing, it may be a good idea to refresh yourself about how both methods work in the item variants tutorial. This is the current texture property: "textures" : { "all" : { "base" : "block/shinygoldtexture" } }, This blocktype has a total of four variants - That's not many. Although you could create a byType entry for each variant code, it is a good idea to understand how you can use a mixture of the two methods. Keep in mind, similar to the item variants, there are a number of ways to achieve the final result. Feel free to experiment with other methods, variants are a complex but beautiful subject when used effectively. Using the ByType suffix, you can split the texture by the condition variant: "texturesbytype" : { "*-good" : { "all" : { "base" : "block/shinygoldtexture" } }, "*-used" : { "all" : { "base" : "block/shinygoldtexture" } } }, A quick reminder of wildcards may be in order. When using the ByType suffix, each object's resolved code will start at the first key ( "*-good") listed and go down the list. If the resolved code matches with the given string, if will then use that key's property. Using an asterisk ( * ) in a key tells the game that any string can go here. So, in this example, here is the list of matching objects: "*-good" "advancedshinyblock-gold-good", "advancedshinyblock-iron-good" "*-used" "advancedshinyblock-gold-used", "advancedshinyblock-iron-used" Although not used in this example, you can simply use an asterisk as a byType key to match everything. This is useful when you wish to specify a value for a single object, but have every other object use a different value. Why can't you use " *-gold " as a key? Remember that the order of the variant groups is important when using wildcards. In every example where wildcards have been used, it has always been to specify the final variant. The ID of, for example, "advancedshinyblock-gold-good" does not fit within the wildcard "*-gold", as "gold" is not the final part of the code. As such, you would have to use " *-gold-* ". This will accept any string before " -gold- " as well as any string after it. Anyway, back to the code. The given code sample doesn't actually specify a different texture for each type. Replace each texture property with a specific variant substitution. "texturesbytype" : { "*-good" : { "all" : { "base" : "block/shiny{type}texture" } }, "*-used" : { "all" : { "base" : "block/shiny{type}texture-damaged" } } }, As you can see, this uses the {type} to substitute "gold" or "iron" into the texture path. If you follow the code, you end up with these resolved texture paths: Block ID Resolved Texture Path advancedshinyblock-gold-good block/shinygoldtexture advancedshinyblock-gold-used block/shinygoldtexture-damaged advancedshinyblock-iron-good block/shinyirontexture advancedshinyblock-iron-used block/shinyirontexture-damaged If you are having issues getting any textures to work, it is often a good idea to create a table like this. For every ID, work out the resolved texture path, and ensure it exists. If you now test your mod, you'll see your blocks working as expected! Conclusion Congratulations, you now have four variants of a single block, all within one file! After this tutorial, you should have more of an understanding of how variants can be grouped together, and how to get the most out of them. Next Steps... If you want to test your knowledge, consider doing the tasks under the Going Further section below. Variants are pretty nifty, but they can get even better. Take a look at the next tutorial, Complex Grid Recipes for a demonstration on how to use variants inside recipes. Going Further Want to make some additional changes to this mod? Try and achieve the following things! Make the ' used ' blocks destroy faster than the ' good' blocks. To achieve this... Replace the resistance property with resistanceByType. Specify two wildcard keys, namely " *-good " and " *-used " to specify values of 3.5 and 2 respectively. Example... "resistanceByType" : { "*-good" : 3.5 , "*-used" : 2 }, Remove texturesByType, and achieve the same result using just variant substitution. (Hint: You will have to rename texture files) To achieve this... There are a number of similar methods to achieve this. Here is my method: Firstly, revert the "texturesByType" to a "textures" property. Change the base property to be " block/shiny{type}texture-{condition}". This will result in the following ID to Texture table: Block ID Resolved Texture Path advancedshinyblock-gold-good block/shinygoldtexture-good advancedshinyblock-gold-used block/shinygoldtexture-used advancedshinyblock-iron-good block/shinyirontexture-good advancedshinyblock-iron-used block/shinyirontexture-used You will now have to rename the texture files to match those in the resolved texture path column. Example... "textures" : { "all" : { "base" : "block/shiny{type}texture-{condition}" } }, Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Item Variants • Block Variants • Complex Grid Recipes • Further Recipes • Simple World Generation --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Complex_Grid_Recipes TITLE: Modding:Content Tutorial Complex Grid Recipes --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.20.4 . Contents 1 Introduction 1.1 Objective 1.2 Assets 1.3 Prerequisites 2 Navigating Assets 3 Variant-Based Recipes 4 More Tool-Usage 5 Shapeless Recipes 6 Extra-Outputs & Returned Stacks 7 Conclusion 7.1 Next Steps... 8 Going Further Introduction Objective Recipes in Vintage Story are very versatile, and the grid recipe system has a number of features that have not yet been talked about. In this tutorial, you will find out how to create more complex recipes, including those with variants, different itemstack quantities, returned itemstacks, and some other smaller features. Assets Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here. This tutorial starts with the following assets: Mod Setup & Folder Structure Simple 'Sleek Door' recipe Prerequisites This tutorial will assume you understand the following topics: Setting up a content mod and using an IDE. It is recommended to have completed the following tutorial: 4. Simple Recipes - This tutorial begins with a basic recipe. It is recommended, but not necessary, to understand the following concept: Variants Navigating Assets This tutorial is simply to create new recipes - The only file is recipes/grid/sleekdoor.json . This is a simple recipe, which inputs an oak solid door and outputs an oak sleek door. { "ingredientPattern" : "D" , "width" : 1 , "height" : 1 , "ingredients" : { "D" : { "type" : "block" , "code" : "game:door-solid-oak" } }, "output" : { "type" : "block" , "code" : "game:door-sleek-windowed-oak" } } Variant-Based Recipes The code that is given within the recipe file works to create an oak sleek door from an oak solid door. However, there exists a sleek door for every solid door, so let's go ahead and create 13 or so other recipes for the other wood types . Of course, there's a much easier way of doing that, and it uses the variant system. Firstly, let's add a wildcard into out ingredient's code to allow us to use a solid door of any type. "D" : { "type" : "block" , "code" : "game:door-solid-*" } So, the recipe now inputs any type of solid door, but will always return an oak sleek door. There needs to be a way of copying the text from the wildcard ( * ) and using it within the output. By using the " name " property in the ingredient, you can achieve exactly that. "D" : { "type" : "block" , "code" : "game:door-solid-*" , "name" : "wood" } This now marks the recipe as containing a variant called " wood ". Note that this name can be anything, it does not have to match with the variant code used when the item or block was defined. To use this in the output, you can use a variant substitution ( { } ) in the output code. "output" : { "type" : "block" , "code" : "game:door-sleek-windowed-{wood}" } Make sure that you use the variant code you defined earlier (using " name ") is the same as the substitution in your output. Important: You cannot name more than one variant per ingredient. If you test your recipe now, you'll find that you can create any sleek door from its appropriate solid door type. More Tool-Usage In the previous recipes tutorial, you used a hammer to create gold brick blocks. Add another ingredient to the sleek door recipe to make the recipe require a saw of any type. { "ingredientPattern" : "S,D" , "width" : 1 , "height" : 2 , "ingredients" : { "S" : { "type" : "item" , "code" : "game:saw-*" , "isTool" : true }, "D" : { "type" : "block" , "code" : "game:door-solid-*" , "name" : "wood" } }, "output" : { "type" : "block" , "code" : "game:door-sleek-windowed-{wood}" } } This is a fairly simple modification, it just changes the pattern, height, and adds the extra saw ingredient. The saw is marked as a tool, so it will lose a single durability point when the recipe is created. However, you may be aware that some recipes use more tool durability than others. To achieve this, you can add a "toolDurabilityCost" property to the tool ingredient. "S" : { "type" : "item" , "code" : "game:saw-*" , "isTool" : true , "toolDurabilityCost" : 10 }, If you test this, you will find that the door now requires a saw to make, which will lose 10 durability when the recipe is complete. Shapeless Recipes All the examples given so far have been shaped recipes, meaning that the ingredients must be placed in the order defined in the " ingredientPattern " property. However, there are some instances where we might now want to worry about the layout of the ingredients. To now make the sleek door recipe shapeless, add the " shapeless" property. Shapeless sleek door recipe... { "ingredientPattern" : "S,D" , "width" : 1 , "height" : 2 , "shapeless" : true , "ingredients" : { "S" : { "type" : "item" , "code" : "game:saw-*" , "isTool" : true , "toolDurabilityCost" : 10 }, "D" : { "type" : "block" , "code" : "game:door-solid-*" , "name" : "wood" } }, "output" : { "type" : "block" , "code" : "game:door-sleek-windowed-{wood}" } } Now, the saw and door can be arranged in any order in the crafting grid. Extra-Outputs & Returned Stacks In some recipes, ingredients can be turned into a different itemstack instead of being consumed. The sleek door looks like a solid door with a couple of planks removed for a window, so you should return two wooden planks to the player when they create it. To do this, you can use the " returnedStack" property on an ingredient. "D" : { "type" : "block" , "code" : "game:door-solid-*" , "name" : "wood" , "returnedStack" : { "type" : "item" , "code" : "game:plank-{wood}" , "quantity" : 2 } } Notice that you can still use variant substitutions here, as well as a quantity value. If you test your recipe now, your recipe will give you two wooden planks when the sleek door is created. Note that you have to create the door for the returnedStacks to be created. You can only have one returned stack per ingredient. Conclusion Congratulations, you now have an advanced recipe for every type of sleek door, all within one file! After this tutorial, you should have more of an understanding of how variants can be used within grid recipes, and how grid recipes have a lot more functionality. Next Steps... If you want to test your knowledge, consider doing the tasks under the Going Further section below. Grid recipes are only one type of recipe in Vintage Story. Move on to the next tutorial, Further Recipes , for information on barrel, clayforming, cooking, knapping, and smithing recipes! Going Further Want to make some additional changes to this mod? Try and achieve the following things! Create a new recipe that allows you to change any sleek door back into a solid door. What gameplay problem would arise from this, and how could you solve it? To achieve this... Copy the appropriate recipe, however make the input a sleek door with a wood variant, and the output code a solid door using the defined wood variant. The issue with this is the returned two planks when creating a sleek door - Which could result in an infinite number of wooden planks if the player kept exchanging between solid and sleek doors. To fix this, you could add another ingredient of two planks for the sleek to solid door recipe. Change the original recipe to stop the player from using a copper saw to make sleek doors. (Hint: The " skipVariants" property (which accepts an array) in an ingredient allows you to control what variants cannot be used in a wildcard.) To achieve this... Add the " skipVariants " property to the saw ingredient, with an array containing just " copper ". Note that it won't work without "name" property. "S" : { "type" : "item" , "code" : "game:saw-*" , "name" : "material" , "skipVariants" : [ "copper" ], "isTool" : true , "toolDurabilityCost" : 10 }, Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Item Variants • Block Variants • Complex Grid Recipes • Further Recipes • Simple World Generation --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Further_Recipes TITLE: Modding:Content Tutorial Further Recipes --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.20.4 . Contents 1 Introduction 1.1 Objective 1.2 Assets 1.3 Prerequisites 2 Navigating Assets 3 Barrel Recipes 3.1 Barrel Basics 3.2 Ingredients 3.2.1 Liquid Ingredients 3.2.2 Liquid Outputs 4 Clayforming Recipes 5 Knapping Recipes 6 Smithing Recipes 7 Conclusion 7.1 Next Steps... 8 Going Further Introduction Objective Vintage Story doesn't just use the grid for recipes. In this tutorial, you will learn how to create items or blocks by using barrels, clayforming, knapping and smithing. This tutorial will also give some information on alloy and cooking recipes, however these work in a somewhat more complex way to the others. Assets Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here. Note : This tutorial is split into different sections for the recipes. If you are using the setup assets, this tutorial mod will error and cannot be tested until all the recipes are complete. Sorry about that. This tutorial starts with the following assets: Mod Setup & Folder Structure Incomplete 'agedlog' Barrel Recipe Incomplete 'bricks' Clayforming Recipe Incomplete 'stone-brick' Knapping Recipe Incomplete 'metalsheets' Smithing Recipe Prerequisites This tutorial will assume you understand the following topics: Setting up a content mod and using an IDE. It is recommended to understand the following concept: Variants Navigating Assets This tutorial only contains recipe assets. You will be creating recipes for a number of objects that are already in game. Take a look at the following files: assets/recipes/barrel/agedlog.json assets/recipes/barrel/strongtannin.json assets/recipes/clayforming/bricks.json assets/recipes/knapping/stone-brick.json assets/recipes/smithing/metalsheets.json Barrel Recipes Upon opening the barrel recipe ( agedlog.json), you will see the following unfinished code: { "output" : { "type" : "block" , "code" : "game:log-placed-aged-ud" , "quantity" : 1 } } This recipe should convert any log into an aged log by using the barrel. The code given gives the output for the aged log. Barrel Basics The first thing that needs to be added to the recipe is the code property. This simply works as an identifier for the recipe. "code" : "aged-log" , Although properties can be listed in any order, they should generally be listed in a logical order - In the case of recipes, it is usually the basics, then ingredients, then the output. So, in this case, place the code property at the top of your recipe file, above the output. The next property, which is specific to barrel recipes, is the sealHours property. This is an optional property that tells the game how long the ingredient must be sealed for before the output is created. "sealHours" : 48 , If seal hours is not set, or set to 0, then the recipe will be processed as soon as the ingredients are correct. Ingredients Next, the ingredients need to be added. Above the output, you can add the following ' ingredients ' property. "ingredients" : [ { "type" : "block" , "code" : "game:log-placed-*-ud" , "name" : "wood" , "quantity" : 1 } ], This should also be fairly standard by now. The ingredient is a single log of any type, hence the wildcard. Although not used, the name property is simply for readability. The quantity can be changed here to adjust the number of logs that are needed in the barrel. Liquid Ingredients Barrel recipes can accept a maximum of two ingredients - One solid, and one optional liquid. Add the following to the ingredients array, above or below the log ingredient. { "type" : "item" , "code" : "game:waterportion" , "litres" : 1 , "consumeLitres" : 1 }, There exists a number of liquids in the game, most of which can be found in the vanilla game assets at assets/survival/itemtypes/liquid. The code given uses the waterportion liquid, which, as it sounds, is the liquid representation of water. Note that instead of quantity, this ingredient uses litres. Barrels can hold a total of 50 litres of liquid. In general, litres and consumeLitres are the same amount, however these can be different. The following is the full code for the agedlog barrel recipe. agedlog.json { "code" : "aged-log" , "sealHours" : 48 , "ingredients" : [ { "type" : "item" , "code" : "game:waterportion" , "litres" : 1 , "consumeLitres" : 1 }, { "type" : "block" , "code" : "game:log-placed-*-ud" , "name" : "wood" , "quantity" : 1 } ], "output" : { "type" : "block" , "code" : "game:log-placed-aged-ud" , "quantity" : 1 } } Liquid Outputs Liquids can also be included in the output in an identical fashion to the above example. This will automatically place the liquid output in the barrel. Note: for this to work the field "consumeLitres" must not be present. The following is the code for the strong tannin barrel recipe from oak logs. strongtannin.json { "code" : "strongtannin" , "sealHours" : 24 , "ingredients" : [ { "type" : "block" , "code" : "log-placed-oak-ud" , "quantity" : 1 }, { "type" : "item" , "code" : "weaktanninportion" , "litres" : 10 }, ], ou t pu t : { "type" : "item" , "code" : "strongtanninportion" , "litres" : 10 } } Clayforming Recipes Clayforming recipes are quite simple. If you open the bricks.json file in the clayforming folder, you will see the start of a recipe, including ingredients and output. { "ingredient" : { "type" : "item" , "code" : "game:clay-*" , "name" : "type" , "allowedVariants" : [ "blue" , "fire" , "red" ] }, "output" : { "type" : "item" , "code" : "game:rawbrick-{type}" , "stacksize" : 12 } } Clayforming recipes only have one input and one output, and luckily the input is always clay. You can specify the allowed types of clay using the allowedVariants property. When finished, this recipe will allow you to make 12 clay bricks by using clayforming. The interesting part of this recipe type (as well as some others) is the pattern property. This property is an array of string arrays, essentially offering us a 2D-text representation of the 3D voxel pattern. Sometimes, these patterns are hard to visualize. Paste the following between the ingredient and output properties. Clayforming pattern property... "pattern" : [ [ "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" ], [ "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" ] ], It's a rather bulky bit of code, but see if you can visualize how this will look in 3D. Each array (contained by [ ] ) is an individual height layer for the clayforming. Note that we use an underscore ( _ ) for an empty space, and a hash sign ( # ) for a filled voxel. Clayforming recipes must be between 16x16, and have a maximum height of 16. If a recipe is less than these dimensions, it will be automatically centered during creation. After adding the pattern, the clayforming recipe is complete! bricks.json { "ingredient" : { "type" : "item" , "code" : "game:clay-*" , "name" : "type" , "allowedVariants" : [ "blue" , "fire" , "red" ] }, "pattern" : [ [ "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" ], [ "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" , "___________" , "###_###_###" , "###_###_###" ] ], "name" : "rawbrick" , "output" : { "type" : "item" , "code" : "game:rawbrick-{type}" , "stacksize" : 12 } } Knapping Recipes Knapping recipes are like clayforming recipes, but even simpler since there's only one level of height to worry about. In the knapping folder, open the stone-brick.json file and you'll see the following code: { "ingredient" : { "type" : "item" , "code" : "game:stone-*" , "name" : "rock" , "allowedVariants" : [ "chert" , "granite" , "andesite" , "basalt" , "peridotite" ] }, "output" : { "type" : "item" , "code" : "game:stonebrick-{rock}" } } It should be evident as to what this code is doing. The ingredient for knapping recipes is always a single rock, and we're only allowing a few variants for the harder types of stone. Our output is a single stone brick, made from that rock. Similar to the clayforming recipe, you need to add a pattern property. Although knapping recipes are always one height, this is also stored as an array of arrays. Add the following property between the ingredient and output properties. "pattern" : [ [ "_#####_" , "_#####_" , "_#####_" , "_#####_" , "_#####_" , "_#####_" ] ], Similarly to the clayforming recipes, an underscore is a gap, and a hash sign is a filled voxel. In this example, the pattern is a simple 5x6 rectangle. All knapping recipes are a maximum size of 10x10. If a recipe is less than these dimensions, it will be automatically centered. In the case of the example given above, the underscores surrounding each row are actually unnecessary, and can be removed, however is sometimes helps with visualization. After adding the pattern property, the stone knapping recipe is complete! stone-brick.json { "ingredient" : { "type" : "item" , "code" : "game:stone-*" , "name" : "rock" , "allowedVariants" : [ "chert" , "granite" , "andesite" , "basalt" , "peridotite" ] }, "pattern" : [ [ "_#####_" , "_#####_" , "_#####_" , "_#####_" , "_#####_" , "_#####_" ] ], "name" : "Brick" , "output" : { "type" : "item" , "code" : "game:stonebrick-{rock}" } } Smithing Recipes Smithing recipes are, once again, similar to both clayforming and knapping recipes. If you open the metalsheets.json file in the smithing recipe folder, you will see the following code: { "ingredient" : { "type" : "item" , "code" : "game:ingot-*" , "name" : "metal" , "allowedVariants" : [ "copper" , "brass" , "tinbronze" , "bismuthbronze" , "blackbronze" , "silver" , "gold" , "iron" , "lead" , "tin" , "zinc" , "bismuth" , "chromium" , "electrum" , "platinum" , "titanium" , "molybdochalkos" , "meteoriciron" , "steel" ] }, "output" : { "type" : "block" , "code" : "game:metalsheet-{metal}-down" , "quantity" : 4 } } Smithing recipes will always start with an ingot as their ingredient, however you can specify what ingots can be used by using allowedVariants . In this example, the recipe will create 4 metal sheets (an unused block) on an anvil. You need to define a pattern for the recipe. Between the ingredient and output properties, add the following pattern: "pattern" : [ [ "###_###" , "###_###" , "###_###" , "_______" , "###_###" , "###_###" , "###_###" ] ], Smithing recipes accept patterns similar to clayforming. As always, an underscore is blank and a hash sign is a full voxel. Each array (defined inside [ ] ) counts as a single voxel layer, starting from the bottom. This example only uses a single layer. Smithing recipes can be 16x16 in width and length, and a maximum of 6 voxels high. Each ingot provides a total of 42 voxels to manipulate, so you can use this to calculate how many ingots your item will need to create. This example, although difficult to craft, can be made using a single ingot. When the pattern is added, the smithing recipe can be tested in game. Conclusion Congratulations, you have created a load of new recipes without using the crafting grid! You should know how to create barrel, clayforming, knapping, and smithing recipes now, as well as understanding how variants can be used within all types of recipes. Next Steps... If you want to test your knowledge, consider doing the tasks under the Going Further section below. With the knowledge in the completed tutorials, you should be well on your way to knowing how to create detailed and in-depth content mods! From here on, tutorials may start getting more specific. When you're ready, consider moving on to 9. Simple World Generation . Going Further Not much here this time! Perhaps try to design and make your own new recipes. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Item Variants • Block Variants • Complex Grid Recipes • Further Recipes • Simple World Generation --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Item_Variants TITLE: Modding:Content Tutorial Item Variants --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.20.4 . Contents 1 Introduction 1.1 Objective 1.2 Assets 1.3 Prerequisites 2 Navigating Assets 3 Defining Variants 4 Adding Multiple Textures 5 Variant-Based Textures 5.1 ByType 5.2 Variant Substitution 5.3 Which to use? 6 Conclusion 6.1 Next Steps... 7 Going Further Introduction Objective So far, the tutorials have only scratched the surface of creating new assets. To make your modding more efficient, you can utilize the variant system to create numerous similar objects from a single json file, and allow each object to have minor (or major) differences. In this tutorial you will create a set of items using variants, each with independent textures. The items will not have any functionality, however this tutorial should give you a good understanding of what variants are and how to use them. Assets Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here. This tutorial starts with the following assets: Mod Setup & Folder Structure Template advanced wand file Completed lang file Item shape file Item texture files Prerequisites This tutorial will assume you understand the following topics: Setting up a content mod and using an IDE. The functions of shape, texture and lang files. It is recommended to have completed the following tutorial: 2. Simple Item - The simple item made in this tutorial is the basis for this tutorial. It is recommended, but not necessary, to understand the following concept: Variants Navigating Assets Using the downloaded workspace, have a look at the mod assets that currently exist. itemtypes/advancedwand.json - This itemtype file is from the finished Simple Item tutorial. lang/en.json - This already contains the entries needed for the tutorial. shapes/item/advancedwand.json - The shape file for the new wand. If you open this file in your IDE, you should notice that this new model takes in two textures - head and handle . "textures" : { "head" : "item/wand-blue" , "handle" : "item/wand-handle" }, textures/item/wand-... - The four texture files for our wand. Notice that there exists 3 variants of colored texture, and 1 handle texture. Defining Variants All work in this tutorial will be done in the itemtypes/ advancedwand.json file. Open it in your IDE and you can get started. Firstly, the item's code is still set to " simplewand" . Change this to " advancedwand" . Before you can utilize any variant systems, you will need to define the variants for your item. To do this, use the "variantGroups" property. This is usually placed directly below the code property, but it would work anywhere in the object. "variantgroups" : [ { "code" : "wandtype" , "states" : [ "blue" , "red" , "green" ] } ], This example will create a single variant group called "wandtype ". The variant code is used for a number of purposes, so make sure it is named sensibly and clearly. After the code property, we can define a list of states in an array. As you have only defined a single variant group, each entry in state will create a single object. If you were to test the mod now, you will see that there exists three wand objects with the codes: advancedwand-blue advancedwand-red advancedwand-green However, the items do not have valid textures or shapes attached to them. Adding Multiple Textures Before adding any textures to the object, you should update the object to the new shape. Find the " shape" property and make it use " item/advancedwand" instead of the current value. Currently, your item is using a single texture. Remember that the new shape contains two textures, so you will now need to define both textures within the item. Replace the entire " texture" property (including the values and { }'s) with the following property: "textures" : { "handle" : { "base" : "item/wand-handle" }, "head" : { "base" : "item/wand-blue" } }, This code allows the use of two textures. It is important to note that the keys for this property, namely " handle" and " head" , match to the textures defined in the shape file. You may have noticed that the property is going to turn all the wand heads blue. You would be correct. Variant-Based Textures When using variants, there are two ways of adding differences between your objects. Read both methods first, and then decide which is more suitable in this case. ByType By using the ByType suffix on a property, you can specify completely different values for each variant. "texturesByType" : { "advancedwand-blue" : { "handle" : { "base" : "item/wand-handle" }, "head" : { "base" : "item/wand-blue" } }, "advancedwand-red" : { "handle" : { "base" : "item/wand-handle" }, "head" : { "base" : "item/wand-red" } }, "advancedwand-green" : { "handle" : { "base" : "item/wand-handle" }, "head" : { "base" : "item/wand-green" } } }, However, a lot of content here is duplicated as all the wands use the same handle, and have head textures that are in the same folders. The advantage of using ByType is that it is considerably versatile - If you wanted to add a completely different texture to a specific wand, you could easily achieve this by using ByType. It is also worth noting that ByType can be used on any property within an item, block, or entity, except from the main code and variant properties. Variant Substitution The other option is to substitute part of the texture path with each wands variant state. "textures" : { "handle" : { "base" : "item/wand-handle" }, "head" : { "base" : "item/wand-{wandtype}" } } By adding our variant code inside curly brackets ( { } ) in the string, the head texture automatically turns into " item/wand-blue", "item/wand-red", and "item/wand-green" for each respective variant object. Evidently, this method uses considerably less code, however it does have some drawbacks. Variant substitution can only be used in properties that accept strings, usually limiting it's functionality to textures, shapes, and sounds. As well as this, it offers less customizability of what textures you can use, as they would all have to be located in specific file paths. Which to use? Honestly, it doesn't matter which method you choose, as both achieve the same result. In a lot of cases, a mixture of using both ByTypes and substitution is the best option: you could specify a unique texture for a more unique variant, but then use substitution on all of the other variants. By using the ByType method, you could choose to have completely different handle textures, however the substitution method offers considerably neater and shorter code. If you load the game, you should be able to see your three different wands, each using a different primary color. Conclusion Congratulations, you now have three variants of a single item, all within one file! This tutorial should have given you a good understanding of what variants are and how they work, as well as the potential they can have when making content mods. Next Steps... If you want to test your knowledge, consider doing the tasks under the Going Further section below. Now that you've had a proper introduction to variants, go take a look at the Block Variants tutorial. This will give you even more experience in creating and using variants. Going Further Want to make some additional changes to this mod? Try and achieve the following things! Create a new colored wand. To achieve this... Add a new state to the wandtype variant, and create a new colored texture that matches to your new state. Don't forget, if you are using the 'byType', you will need to add a new entry for your new wand. You will also need to add a new entry in your lang file. Make some new grid recipes, one for each wand color ( Simple Recipe Tutorial ). For an extra challenge, see if you can work out how to create all three recipes from one file (Tip: arrays of recipes must be contained within square brackets ( [ ] )). To achieve this... Create three new recipes based on the simple wand, making sure each one is unique. The output code for each is the resolved item code, for example " advancedwand-blue ". Make sure your domains are correct, and check the logs for errors if you struggle with this. To create more than one recipe in a file, place a comma after the final curly bracket ( } ), and then place your next recipe inside another set of curly brackets. When all recipes have been added, place an opening square bracket ( [ ) at the very beginning of the file, and a closing square bracket ( ] ) at the very end of the file. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Item Variants • Block Variants • Complex Grid Recipes • Further Recipes • Simple World Generation --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Simple_Block TITLE: Modding:Content Tutorial Simple Block --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.20.4 . Contents 1 Introduction 1.1 Objective 1.2 Assets 1.3 Prerequisites 2 Navigating Assets 3 Creating the Block 4 Adding the Shape & Texture 5 Block Properties and Sounds 6 Renaming the Block 7 Conclusion 7.1 Next Steps... 8 Going Further Introduction Objective In this tutorial, you will create a new gold brick block! The block will be very simple, however completing this tutorial will show you how to create any number of new blocks. You will also get more practice on using textures and lang files, as well as an understanding of sound assets. Assets Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here. This tutorial starts with the following assets: Mod Setup & Folder Structure Empty block file Template lang file Block texture file Prerequisites This tutorial will assume you understand the following topics: Setting up a content mod and using an IDE. The functions of shape, texture and lang files. Although not necessary, it is recommended to have completed the following tutorial: 2. Simple Item You will notice that creating an item and creating a block use very similar methods. Navigating Assets Using the downloaded workspace, have a look at the files that currently exist. modinfo.json - The mod info file, which registers your mod with the ID 'simpleblock'. modicon.png - The mod icon file, which will be displayed on the mod manager menu. assets/simpleitem/blocktypes/simpleshinyblock.json - An empty file, this is where you shall put the code for your block. assets/simpleitem/lang/en.json - A language file, which is where you will add your translation values. assets/simpleitem/textures/block/shinygoldtexture.png - A texture file for the block. Creating the Block In order to create a block, a file must be created inside the blocktypes folder. If using the provided workspace, there already exists a block file called simpleshinyblock. Open this file in your IDE. The most important property when creating an asset is the code, also known as an ID. To begin, copy and paste this into the opened simpleshinyblock file. { "code" : "simplegoldblock" } Remember that json files are a set of keys to values. The key, in this example, is " code ", and the value is " simplegoldblock ". Note that the block file starts and ends in curly brackets ( { } ), meaning it is a single object. Now that your block file has a code, it will now be created and registered in game. However, you need a way of accessing the block. To do this, add a new property: { "code" : "simplegoldblock" , "creativeinventory" : { "general" : [ "*" ] }, } Notice that each property is seperated using a comma. The new property, " creativeinventory " tells the game what creative tab this block will be placed in. The syntax of this particular property is a little bit more complex, but don't worry about it for now. All this does is add the block into the "general" creative tab. If you were to launch Vintage Story and enable your mod, you will see the block in the creative menu! You can also place your block in the world! Obviously, this is not the correct texture for the block. Adding the Shape & Texture The texture file exists in the assets, so why isn't the item being rendered correctly? For blocks to use a certain texture, then it must specify this within its json file. Replace your current blocktype json with the following: { "code" : "simplegoldblock" , "creativeinventory" : { "general" : [ "*" ] }, "drawtype" : "Cube" , "texture" : { "base" : "block/shinygoldtexture" } } Two new properties have been added: drawtype and texture . Although drawtype is not necessary to determine the shape of the block, it is a good idea to include this property, as you may change it in the future. Setting this to Cube tells the block to render as a cube shape. Texture assigns and maps the texture to the block - Nothing special here. If you run the game again, your block will now have the appropriate texture. Block Properties and Sounds There are a couple more changes that need to be made for your block to be finished. The first thing to set is the mining properties for your block. Add a comma after the last property, and add the following properties to your json file: "blockmaterial" : "Stone" , "resistance" : 3.5 The blockmaterial property determines what tools are effective at digging this block. Setting this to Stone implies that it can be mined by pickaxes. For a complete list of block materials, view the EnumBlockMaterial section on GitHub . The resistance property determines how long a block takes to destroy, in seconds, without the use of a tool. Keep in mind that if the player uses a tool that affects the block material, the block will be destroyed faster. To see if the changes are correct, save the file and reload the world. You should notice that your block takes longer to destroy, unless mined with a pickaxe. You may also notice that your block doesn't sound very metallic - So let's fix that. Your final block json should look like the following: { "code" : "simplegoldblock" , "creativeinventory" : { "general" : [ "*" ] }, "drawtype" : "Cube" , "texture" : { "base" : "block/shinygoldtexture" }, "blockmaterial" : "Stone" , "resistance" : 3.5 , "sounds" : { "place" : "game:block/anvil" , "walk" : "game:walk/stone" } } The sounds property is rather self-explanatory, but it is important to notice that you prefix the file locations with " game: ". This is because the sound files are located within the base game, and are not added by your mod. Here, you are just assigning sounds for place and walk. The most common types of sound for blocks are place , walk , break , and hit. Renaming the Block Currently, every instance of the block is called 'simpleblock:block-simplegoldblock'. To fix this, you need to add an entry to the lang file. Open up the provided en.json language file, found in assets/simpleblock/lang. This is what the file should look like: { "" : "" } On the left is the translation key, and on the right is the translated string. In this case, the translation key is 'block -simplegoldblock' . Note that this is how the game is currently displaying the name, minus the mod domain prefix. The translated string for this should be ' Simple Gold Block' . { "block-simplegoldblock" : "Simple Gold Block" } To speed up testing, if you still have Vintage Story open, you can use the command .reload lang to reload all lang files. Hovering over your block should show that the lang file has taken effect. Conclusion Congratulations, you have now created your first block! This tutorial should have given you greater understanding of blocks, textures, sounds and language files, as well as the basics of modding for Vintage Story! Next Steps... If you want to test your knowledge, consider doing the tasks under the Going Further section below. When you're ready, try out the next tutorial: Simple Recipes . This will show you how to create recipes for the assets you have created. Going Further Want to make some additional changes to this mod? Try and achieve the following things! Change the color of the block To achieve this... Modify the shinygoldtexture.png texture to a different color. Change the block to only be mined with at least a tier 1 tool. Note that you will have to add a new " requiredMiningTier " property To achieve this... Add "requiredMiningTier":1 to the blocktype file. Change your modid and domain to something else To achieve this... Modify both the modid entry in modinfo.json , and change the name of the ' simplebloc k ' folder in ' asset s '. Rename your 'Simple Gold Block' To achieve this... Change the entry for simplegoldblock in your en.json lang file. Create another block with a different texture and properties To achieve this... Create a new block texture with a different name, and a duplicate blocktype file. In the new blocktype file, change the block code and change the texture property to your new texture path. Modify the properties to change the type of block. Don't forget to add new lang translation entries! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Content Essentials • Simple Item • Simple Block • Simple Recipe --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Simple_Item TITLE: Modding:Content Tutorial Simple Item --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.20.4 . Contents 1 Introduction 1.1 Objective 1.2 Assets 1.3 Prerequisites 2 Navigating Assets 3 Creating the Item 4 Adding the Shape & Texture 5 Positioning the Item 5.1 Using the Transform Editor 6 Renaming the Item 7 Conclusion 7.1 Next Steps... 8 Going Further Introduction Objective In this tutorial, you will create an item - A simple wand! Although this item will have no real functionality, it will demonstrate how to create assets in json, as well as how to use shapes, textures, and lang files. Assets Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here. This tutorial starts with the following assets: Mod Setup & Folder Structure Empty item file Template lang file Item shape file Item texture file Prerequisites This tutorial will assume you understand the following topics: Setting up a content mod and using an IDE. The functions of shape, texture and lang files. Navigating Assets Using the downloaded workspace, have a look at the files that currently exist. modinfo.json - The mod info file, which registers your mod with the ID 'simpleitem'. modicon.png - The mod icon file, which will be displayed on the mod manager menu. assets/simpleitem/itemtypes/simplewand.json - An empty file, this is where you shall put the code for your item. assets/simpleitem/lang/en.json - A sample language file, which is where you will add your translation values. assets/simpleitem/shapes/item/simplewand.json - A shape file for the wand. assets/simpleitem/textures/item/simplewand.png - A texture file for the wand. Creating the Item In order to create an item, a file must be created inside the itemtypes folder. If using the provided workspace, there already exists an item file called simplewand. Open this file in your IDE. The most important property when creating an asset is the code, also known as an ID. To begin, copy and paste this into the opened simplewand file. { "code" : "simplewand" } Remember that json files are a set of keys to values. The key, in this example, is " code ", and the value is " simplewand ". Note that the item file starts and ends in curly brackets ( { } ), meaning it is a single object. Now that your itemtype file has a code, it will now be created and registered in game. However, you need a way of accessing the item. To do this, add a new property: { "code" : "simplewand" , "creativeinventory" : { "general" : [ "*" ] } } Notice that each property is seperated using a comma. The new property, "creativeinventory" tells the game what creative tab this item will be placed in. The syntax of this particular property is a little bit more complex, but don't worry about it for now. All this does is add the item into the "general" creative tab. If you were to launch Vintage Story and enable your mod, you will see the item in the creative menu! Ideally, you do not want your item to be a large black box. Adding the Shape & Texture The shape and texture files exist in the assets, so why isn't the item being rendered correctly? If an asset wishes to use a certain shape or texture, then it must specify this within its json file. Replace your current itemtype json with the following: { "code" : "simplewand" , "creativeinventory" : { "general" : [ "*" ] }, "texture" : { "base" : "item/simplewand" }, "shape" : { "base" : "item/simplewand" } } Two new properties have been added - texture and shape. Remember that the shape is a 3D representation of our item, and the texture is a 2D image that is mapped onto our shape, and we should include both to ensure our item is rendered correctly. It may seem confusing that both the texture and shape values appear to be the same, but the game looks for these in different places. To find shapes, the game automatically looks in the mod's shapes folder, whereas for textures it looks in the mod's textures folder. If you run the game again, you'll notice some changes. Now, that certainly looks closer to what we wanted, but the position of the item is wrong. Positioning the Item Your item is nearly complete, however the 3d object does not have the correct transformation. A transformation, in 3d graphics, refers to an object's position, rotation, and scale. Using the Transform Editor To allow modders to easily position items and blocks correctly, Vintage Story utilises a transform editor that can be accessed in game. When in game, hold the item you wish to position and use the command '.tfedit' to open the transform editor. The transform editor allows you to select where the object is rendered, and adjust its transformation directly. The best part about this is the generated json code at the bottom of this editor, which you can copy and paste straight into your item file. It is recommend to learn how to use the transform editor, since it is an invaluable tool for transforming items and blocks. Changes made within the transform editor do not modify any files, and will therefore not save. For each item you create, it is recommended to set positions for ground, main hand, and gui. These use the properties groundTransform , tpHandTransform, and guiTransform respectively. To transform your wand correctly, add the following properties. Don't forget to add a comma after the final curly bracket on the previous shape element. "guiTransform" : { "translation" : { "x" : 0 , "y" : 0 , "z" : 0 }, "rotation" : { "x" : -89 , "y" : 41 , "z" : 33 }, "origin" : { "x" : 0.48 , "y" : 0 , "z" : 0.38 }, "scale" : 1.78 }, "groundTransform" : { "translation" : { "x" : 0 , "y" : 0 , "z" : 0 }, "rotation" : { "x" : 0 , "y" : 0 , "z" : 0 }, "origin" : { "x" : 0.5 , "y" : 0 , "z" : 0.5 }, "scale" : 3.4 }, "tpHandTransform" : { "translation" : { "x" : -0.45 , "y" : 0.03 , "z" : -0.66 }, "rotation" : { "x" : 90 , "y" : 0 , "z" : 0 }, "origin" : { "x" : 0.5 , "y" : 0 , "z" : 0.5 }, "scale" : 0.8 } To see if the changes are correct, you can simply reload the world. Hopefully, if everything worked correctly, you should find that your wand now fits better in the player's hand, as well as appearing in the gui more appropriately. There is one final change before the item is complete. Renaming the Item Currently, every instance of the item is called 'simpleitem:item-simplewand'. To fix this, you need to add an entry to the lang file. Open up the provided en.json language file, found in assets/simpleitem/lang. This sample lang file contains two premade translations, to show you how the file works. { "template-lang-1" : "Template Lang 1" , "template-lang-2" : "Template Lang 2" } On the left is the translation key, and on the right is the translated string. Add a new entry to this file by adding a comma after the second sample translation, and a new line. In this case, the translation key is ' item-simplewand' . Note that this is how the game is currently displaying the name, minus the mod domain prefix. The translated string for this should be ' Simple Wand' . { "template-lang-1" : "Template Lang 1" , "template-lang-2" : "Template Lang 2" , "item-simplewand" : "Simple Wand" } Both sample translations are not used, and can be deleted if you want to. To speed up testing, if you still have Vintage Story open, you can use the command ' .reload lang' to reload all lang files. Hovering over your item should show that our lang file has taken effect. Conclusion Congratulations, you have now created your first item! This tutorial should have given you some understanding of items, textures, shapes and language files, as well as the basics of modding for Vintage Story! Next Steps... If you want to test your knowledge, consider doing the tasks under the Going Further section below. When you're ready, try out the next tutorial: Simple Block . This will show you how to create a new block with a texture. Going Further Want to make some additional changes to this mod? Try and achieve the following things! Change the color of the wand To achieve this... Modify the simplewand.png texture to a different color. Change your modid and domain to something else To achieve this... Modify both the modid entry in modinfo.json , and change the name of the ' simpleitem ' folder in ' assets '. Rename your 'Simple Wand' To achieve this... Change the entry for simplewand in your en.json lang file. Create another wand with a different color To achieve this... Create a copy of the texture with a different name and color, and a duplicate itemtype file. In the new itemtype file, change the item code and change the texture property to your new texture path. Don't forget to add new lang translation entries! Note that you do not need to change the texture in the 'shapes' file, as the texture property in your itemtype will be used instead. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Content Essentials • Simple Item • Simple Block • Simple Recipe --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Simple_Recipe TITLE: Modding:Content Tutorial Simple Recipe --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.20.4 . Contents 1 Introduction 1.1 Objective 1.2 Assets 1.3 Prerequisites 2 Navigating Assets 3 Recipe File Structure 4 Creating the Item Recipe 4.1 Recipe File 4.2 Recipe Pattern 4.3 Recipe Ingredients 4.4 Recipe Output 4.5 Finalized Recipe 5 Creating the Block Recipe 5.1 Recipe File 5.2 Recipe Pattern 5.2.1 Understanding the IngredientPattern Property 5.3 Recipe Ingredients 5.3.1 Asset Location Wildcards 5.3.2 IsTool Property 5.4 Recipe Output 5.5 Finalized Recipe 6 Conclusion 6.1 Next Steps... 7 Going Further Introduction Objective In this tutorial, you will create two new grid recipes for our previously made item and block! Recipes are a great way of allowing players to access your new content. Assets Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here. This tutorial starts with the following assets: Mod Setup & Folder Structure Premade Blocktype file Premade Itemtype file Premade Lang file Texture & Shape Files Empty Grid Recipe Files Prerequisites This tutorial will assume you understand the following topics: Setting up a content mod and using an IDE. The functions of shape, texture and lang files. Although not necessary, it is recommended to have completed the following tutorials: 2. Simple Item The 'Simple Wand' item is included in the tutorial setup. 3. Simple Block The 'Simple Gold Block' is included in the tutorial setup. Navigating Assets Browse the downloaded workspace. You should have an understanding of most of the files that currently exist, however there are two new files: assets/simplerecipe/recipes/grid/simplewandrecipe.json - An empty json file. This will be used to create a recipe for the 'Simple Wand' item. assets/simplerecipe/recipes/grid/simpleblockrecipe.json - An empty json file. This will be used to create a recipe for the 'Simple Gold Block'. Recipe File Structure As you may have noticed, recipes are all stored within the ' recipes' folder - However there is a list of subfolders that determine the type of recipe. This tutorial uses the ' grid ' folder, which tells the game that any files stored within that folder are recipes to be made in the grid. The valid recipe types (and therefore folders) are: alloy - Allows creation of different alloyed metals in a crucible. barrel - Allows creation of objects within a barrel. clayforming - Allows creation of clay objects using the clayforming system. cooking - Allows creation of food in a cooking pot. grid - Allows creation of objects by using the crafting grid. knapping - Allows creation of stone objects using the knapping system. smithing - Allows creation of metal objects on an anvil. Creating the Item Recipe Unlike when creating items and blocks, recipes can be somewhat difficult to test before they are complete, as all the information is required before the recipe will be created. When creating recipes, it is a good idea to visualize what you are creating. This is the recipe you will be creating for this section: One clear quartz, on top of two sticks. It is, after all, a magic wand . Recipe File Open the ' simplewandrecipe.json' file inside ' recipes/grid '. To create a new grid recipe, the game needs the following information: Ingredients - This defines what objects are used in the grid to create the output. Pattern - This defines how the ingredients are arranged in the grid, as well as the width and height of the recipe. Output - This defines what object will be given to the player when the recipe is complete. You will notice that this information can be listed in any order in the json file. Recipe Pattern To begin, you need to define the recipe pattern inside your json file. { "ingredientPattern" : "Q,S,S" , "width" : 1 , "height" : 3 , These three properties will define the pattern and shape of our recipe. The ' ingredientPattern ' property defines a list of letters. Each unique letter defines a new ingredient. Note that although this uses Q for quartz and S for stick, you can pick whatever letter you want. Do not worry about the format of this property right now, as it will be discussed in the next recipe. The ' width' property defines how many columns the recipe uses. The ' height' property defines how many rows the recipe uses. Recipe Ingredients For your recipe to function correctly, each unique letter in the previous ' ingredientPattern' property needs to be registed as an ' ingredient'. Add the following code to your file, below the height property. "ingredients" : { "Q" : { "type" : "item" , "code" : "game:clearquartz" }, "S" : { "type" : "item" , "code" : "game:stick" } }, The ' ingredients' property is a little different to any properties used before in the tutorials, because it is a dictionary data type . In this case, it is simply a way of mapping each character in the pattern to an in-game object. There are two unique characters in the pattern, Q and S, so we need to add both of these to the list. When adding an ingredient, you need to include its type and code. The type is simple - either "item" or "block" depending on which one it is. The code is the item's unique identifier. A future tutorial will discuss multiple ways of finding an objects code, but for now we can use game:clearquartz and game:stick . When all the unique characters have entires, you can move on. Recipe Output The recipe output determines what object this recipe will make. Add the final section of code to your file, below the ' ingredients' section. "output" : { "type" : "item" , "code" : "simplewand" } } You may notice that the ' output ' property is defined very similarly to an ingredient, using identical ' type' and ' code' properties inside of it. In this instance, to create the simplewand item, we fill in using the item type and simplewand code. Finalized Recipe Your first recipe should now be complete! You should be able to test it by going on the "simplewand" page in the in-game handbook, or trying to create your item in survival mode. Here is the full recipe for comparison: simplewandrecipe.json { "ingredientPattern" : "Q,S,S" , "width" : 1 , "height" : 3 , "ingredients" : { "Q" : { "type" : "item" , "code" : "game:clearquartz" }, "S" : { "type" : "item" , "code" : "game:stick" } }, "output" : { "type" : "item" , "code" : "simplewand" } } Creating the Block Recipe This is the recipe you will be creating for this section: The recipe should be able to use any hammer, on top of 6 gold ingots to make the gold brick block. Recipe File Open the ' simpleblockrecipe.json' file inside ' recipes/grid '. Same as before, this grid recipe needs to define its ingredients, pattern, and output. Recipe Pattern Firstly, define the recipe pattern inside your json file. { "ingredientPattern" : "_H_,GGG,GGG" , "width" : 3 , "height" : 3 , The properties here are the same as the previous recipe, however you are now using the grid to its maximum capacity. Understanding the IngredientPattern Property The ' ingredientPattern' property is a lot easier to explain in this recipe. Each letter in the pattern determines a unique ingredient, however there are a couple of unique characters too. Each section, determined by commas, represent a row in the crafting grid. In the pattern above, the rows are: "_H_", "GGG", "GGG". Each character then represents its own space in the crafting grid. An underscore ( _ ) tells the game that this is a blank space and unused in this recipe. Look back on the previous recipe, and make sure you understand the ingredient pattern there. It is a 1 by 3 recipe, where each row is seperated by a comma. Recipe Ingredients This recipe uses two ingredients - A hammer and a gold block. Add the following code to your file, below the height property. "ingredients" : { "H" : { "type" : "item" , "code" : "game:hammer-*" , "isTool" : true }, "G" : { "type" : "item" , "code" : "game:ingot-gold" } }, The ingredients here follow the exact same format as the previous recipe, however there is a new concept to introduce. Asset Location Wildcards So far in the tutorials, you will have used a lot of asset locations without much explanation about how they work. This is a very substantial topic, and will be explained more in a future tutorial. For now, however, it is important to understand the use of wildcards within a code property. In Vintage Story, there can exist variants of each item, block, or entity. These variants are used to create a large amount of new objects that all share very similar properties, all within a single file. One such example of an item with variants is the hammer item. There exists a total of 9 hammers with near-identical functionality, however each one uses a different ' metal' variant to determine its material. Every object in the game has to have a unique code, so variants are appended to the end of the item code. This means that the full codes for all 9 hammers are: hammer-copper hammer-tinbronze hammer-bismuthbronze hammer-blackbronze hammer-gold hammer-silver hammer-iron hammer-meteoriciron hammer-steel When it comes to creating recipes, you do not want to have to create 9 seperate recipes where each one accepts a different hammer, so you can use a wildcard. A wildcard allows a modder to group all these objects together using an asterisk ( * ) in its code property. By using " hammer-* ", the game allows any object who's code begins with "hammer- " to be valid for this recipe. Note that you should still include the hyphen in the code, as using " hammer* " will result in being able to use any hammer and also any hammer head items. You will learn how to create your own object variants in future intermediate tutorials. IsTool Property There are a number of extra properties that can be included within recipes to make them slightly more complex. One such property is the ' isTool' property. Without this property, the hammer will disappear when the block is created. By marking the hammer as a tool, the recipe simple uses a single durability point, but the hammer remains. Recipe Output The final section of our recipe is the output. Add the final section of code to your file, below the ' ingredients' section. "output" : { "type" : "block" , "code" : "simplegoldblock" } } As before, we just set this to our block's code. Note that the type has been changed to block in this instance. Finalized Recipe Your second recipe should now be complete! You should be able to test it by going on the "simplegoldblock" page in the in-game handbook, or trying to create your item in survival mode. Here is the full recipe for comparison: simpleblockrecipe.json { "ingredientPattern" : "_H_,GGG,GGG" , "width" : 3 , "height" : 3 , "ingredients" : { "H" : { "type" : "item" , "code" : "game:hammer-*" , "isTool" : true }, "G" : { "type" : "item" , "code" : "game:ingot-gold" } }, "output" : { "type" : "block" , "code" : "simplegoldblock" } } Conclusion Congratulations, you have now created two recipes for your new item and block! This tutorial should have given you the ability to make simple recipes, as well as a brief understand of variants and wildcards. Next Steps... If you want to test your knowledge, consider doing the tasks under the Going Further section below. This is the final tutorial in the 'Basic Tutorial' section. Take a look at starting the intermediate content mod tutorials , where you will learn more about variants and error-checking, as well as developing complex blocks, items, entities, and many more. Going Further Want to make some additional changes to this mod? Try and achieve the following things! Gold ingots are too expensive... change the block recipe to use gold bits instead. Note that the code for gold bits is " game:metalbit-gold". To achieve this... In ' simpleblockrecipe.json', modify " game:ingot-gold " to " game:metalbit-gold ". Create another grid recipe that uses a simple wand and a single charcoal to make a diamond. The codes are " simplewand ", " game:charcoal ", and " game:gem-diamond-rough " respectively. Note that the wand should be consumed on use - Using the " onTool " property will cause your recipe to not work as the simple wand is not defined as a tool. To achieve this... Create a new charcoaltodiamond.json recipe inside recipes/grid . Your file may look similar to this: { "ingredientPattern" : "W,C" , "width" : 1 , "height" : 2 , "ingredients" : { "W" : { "type" : "item" , "code" : "simplewand" }, "C" : { "type" : "item" , "code" : "game:charcoal" } }, "output" : { "type" : "item" , "code" : "game:gem-diamond-rough" } } Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Content Essentials • Simple Item • Simple Block • Simple Recipe --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorial_Simple_Worldgen TITLE: Modding:Content Tutorial Simple Worldgen --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Introduction 1.1 Objective 1.2 Assets 1.3 Prerequisites 2 Navigating Assets 3 Block Patches 3.1 Loose Sticks 3.2 Underground Bones 3.3 Deposits Introduction In progress! Please come back soon. Objective Much of Vintage Story's world generation is modifiable and extendable through JSON. In this tutorial, you will be shown two simple ways of adding to the world generation: blockpatches and deposits . You will also learn how to modify these methods to increase or decrease the commodity of them spawning. Assets Before starting, it is recommended you download the workspace and assets for this tutorial. The completed files can also be found here. This tutorial starts with the following assets: Mod Setup & Folder Structure Incomplete 'loosesticks' blockpatches file Incomplete 'sand' deposits file Prerequisites This tutorial will assume you understand the following topics: Setting up a content mod and using an IDE. It is recommended to understand the following concept: Variants Navigating Assets This tutorial only contains world generation assets. You will be adding in new world generation rules that use existing game content. Take a look at the following files: assets/worldgen/blockpatches/loosesticks.json assets/worldgen/deposits/sand.json Block Patches Block patches refer to a type of world generation where new blocks are added into a world - essentially replacing empty air blocks. In the base game, block patches are used for berry bushes, tall grass, mushrooms, underground gears, and a lot more. Files that define blockpatches must be created in the blockpatches folder. Loose Sticks To begin with, you will be creating a JSON file that will generate a number of loose sticks on the surface. Open worldgen/blockpatches/loosesticks.json and you should see the following partially-made script: [ { "comment" : "Small groups of loose sticks everywhere" , } ] As you can see, there exists a single property. This comment property has no functionality, it is merely a way of describing what the generator achieves. Block patches all follow a very similar two-part structure. First you define what you want to generate, including the block code and the quantity, and then you define the rules of it spawning, including things such as temperature, height, fertility, as well as the ' chance' of it spawning in a chunk. So, firstly you need to define what to generate. Using the blockCodes property, you can define an array of all the potential block types. When the generation process happens, one of these blocks will be picked randomly. In this case, just add the block code for loose sticks underneath the comment property. "blockCodes" : [ "game:loosestick-free" ], Next, define a quantity. This is a NatFloat ( natural float) type - A specific type of random number generator that uses an average and a variable amount. "quantity" : { "avg" : 4 , "var" : 2 }, NatFloats can be considerably more complex - However in this example it will return a value between ( avg - var) and (avg + var). In effect, this will pick a random number between 2 and 6, and generate that number of sticks in a localized group. After you have defined what to spawn and how many of them to spawn in a single group, you can add a number of optional criteria for the block spawning. The following properties will limit loose sticks from spawning in some areas: "minTemp" : -2 , "minForest" : 0 , There are many more optional criteria that can be used. These include: MinTemp and MaxTemp MinRain and MaxRain MinForest and MaxForest MinShrub and MaxShrub (for how much shrubbery and bushes are in the current area) MinFertility and MaxFertility MinY and MaxY (for elevation, as a 0-1 decimal relative to the map height. A Y of 0 is the very bottom of the world, a Y of 1 is the world height, and a Y of around 0.43 is sea-level) After adding in the optional criteria, you need to add in a chance property: "chance" : 3 This value effects how the chance of this rule spawning per chunk. The chance can be a decimal value. For example, a chance of 0.5 means there is a 50% chance per chunk that the blockpatch will be generated. A chance of 3 means that there will be 3 instances of this generating per chunk. If you run the game with your mod, you will be able to see sticks generating most places in the surface. If you want to increase the spawn rate, increase the value of the chance property. loosesticks.json [ { "comment" : "Single loose sticks everywhere" , "blockCodes" : [ "game:loosestick-free" ], "minTemp" : -2 , "minForest" : 0 , "quantity" : { "avg" : 4 , "var" : 2 }, "chance" : 3 } ] Underground Bones There's more to be done with blockpatches. Create a new file in the blockpatches folder called " bones.json " and fill it with this code: [ { "comment" : "Lots of bones and carcasses underground" , } ] This file will be very similar to the previous one - Add in the following for the blockcodes, the quantity, and the chance. "blockCodes" : [ "game:bonyremains-cowskull-up" , "game:bonyremains-ribcage" , "game:drycarcass-humanoid1" , "game:drycarcass-humanoid2" , "game:drycarcass-tiny" , "game:drycarcass-small" , "game:drycarcass-medium" , "game:drycarcass-large" ], "quantity" : { "avg" : 1 , "var" : 0 }, "chance" : 120 , So this will add in a random carcass or bony remains. Using a quantity average of 1 with a var of 0 means this will always spawn a single carcass. There exists some further criteria for blockpatches. An additional placement property will allow you to define a more specific criteria on where the worldgen will place things. "placement" : "Underground" This particular value will allow the blocks to be placed underground. Since there is now a much larger area to work with, the chance property for this entry is considerably higher. The following values can be used for placement: ReplaceSurface OnSurface (Default) NearWater Anywhere Underground UnderWater NearSeaWater UnderSeaWater UnderTrees OnTrees OnSurfacePlusUnderTrees When this is specified, you can test the mod. You should begin seeing carcasses and other bones spawning underground. bones.json [ { "comment" : "Lots of bones and carcasses underground" , "blockCodes" : [ "game:bonyremains-cowskull-up" , "game:bonyremains-ribcage" , "game:drycarcass-humanoid1" , "game:drycarcass-humanoid2" , "game:drycarcass-tiny" , "game:drycarcass-small" , "game:drycarcass-medium" , "game:drycarcass-large" ], "quantity" : { "avg" : 1 , "var" : 0 }, "chance" : 120 , "placement" : "Underground" } ] Deposits Deposits are a little more complicated than blockpatches. This type of world generation is used to replace certain blocks with other blocks. Blocks such as ores, clay/peat, and cracked stone all spawn using a deposit generation. Deposits must be defined in the deposits folder in worldgen . Open the tutorial file at worldgen/deposits/sand.json and you'll see this code sample: [ { "code" : "sand" , } ] Sorry, this section is still being written! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Item Variants • Block Variants • Complex Grid Recipes • Further Recipes • Simple World Generation --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Content_Tutorials_Portal TITLE: Modding:Content Tutorials Portal --- CONTENT --- Other languages: English русский Modding - Content Tutorials Portal This is the portal for the content modding tutorials. The following pages are part of this portal: Basic Content Tutorials - A set of essential tutorials for learning the basics of content modding. Intermediate Content Tutorials - A set of tutorials for learning important aspects to content modding. Advanced Content Tutorials - A set of tutorials for learning more complex aspects of content modding. Other Content Tutorials - Miscellaneous content tutorials of varying complexity. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Creating_A_Code_Mod TITLE: Modding:Creating A Code Mod --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Prerequisites 1.1 Updating the Templates 1.2 Recommended Knowledge 2 Creating a New Mod 3 Project Setup 3.1 Solution Explorer 3.2 Mod Info 4 Testing your Mod 5 What's Next? Prerequisites Before creating a code mod, you need to have completed the Preparing For Code Mods tutorial. This will show you how to setup your development environment, install dotnet, and install the modding template. If you have done that, you can carry on with this tutorial. This tutorial is suitable for modders using Visual Studio. If you have chosen to use a different IDE, please refer to the older Setting up your Development Environment tutorial. Updating the Templates Before creating your mod, it is important to check for any updates to the Vintage Story template. Open Windows PowerShell or the command prompt, and enter the following command: dotnet new update This will check for updates for all templates, and install them if needed. Recommended Knowledge Before continuing, it is highly advisable to have some knowledge of Visual Studio and the C# programming language. These topics are out of the scope of this tutorial, however if you can understand the following tutorials, you should be okay: Starting with Visual Studio (YouTube) Create a .NET console application using Visual Studio (Microsoft.com) As well as these, the " C# for beginners " tutorial set on YouTube is great for learning the basics of C# and dotnet. Please keep in mind that this series uses Visual Studio Code , which is a different IDE to Visual Studio . Creating a New Mod To create a new code mod, launch Visual Studio, and select ' Create a new project '. In the create menu, select Vintage Story from the dropdown furthest to the right. This will filter the list of templates, to only show ones with the " Vintage Story " project type. Select the ' Vintage Story Basic Mod Template ' and click next. This will load a new menu asking for a project name, location, and solution name. Your project name should follow these requirements: Not contain any spaces or punctuation. Be formatted in PascalCase . Not be abbreviated (Use MyFirstMod instead of MFM ). Be unique to any other mod on the VS Mod DB. Your project name will also determine your Mod ID, however this can be altered later. Your project can be created anywhere, and it is recommended to check the " Place solution and project in the same directory" checkbox. When you have entered your details, click ' Create '. Your project will be created and Visual Studio will open. Project Setup Solution Explorer When your project loads, you should see the Solution Explorer on the right-hand-side of Visual Studio. If not, you can open the Solution Explorer by clicking View and Solution Explorer from the top Visual Studio menu bar. The Solution Explorer is a list of every file for your mod, and this is how you will create, open, and delete mod files. You can double left-click any file or folder to open it. Right clicking a file or folder will give you options to rename, delete, copy, and other file management tasks. Note that any reference to ' MyFirstMod ' will be replaced with whatever your project name is, and 'myfirstmod' will be your lowercase project name. Mod Info Double click on the modinfo.json file to open it, and you will see the following JSON code: { "type" : "code" , "modid" : "myfirstmod" , "name" : "MyFirstMod" , "authors" : [ "Unknown" ], "description" : "To be added" , "version" : "1.0.0" , "dependencies" : { "game" : "" } } This is the same modinfo file that is in content mods. As this mod is a code mod, this needs to be specified in the type property. To begin, set the name, authors, and description properties to more appropriate values. It is not recommended to change the modid property after creating a mod, as you will also have to rename the assets subfolder and any code that references your mod's domain or id. Testing your Mod To run your mod, look for the run menu immediately below the menu bar. Ensure the dropdown list on the right has your project name selected, and not " CakeBuild", and that the run button/dropdown next to that says " Client " and not " Server ". To test your mod, either press the " Client" button, or press F5. Visual Studio will build your mod, and launch Vintage Story with the mod attached. Vintage Story will load to the main menu, so select the Mod Manager menu and you should see your mod. If you can see your mod in this list, congratulations! Note that any changes made in your modinfo file will take effect here. What's Next? Now your mod has been created, it may be time to go through the Basic Code Tutorials . These will show you how to create advanced block functionality, advanced item functionality, and your very own command. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Code Mods • Preparing For Code Mods • Creating A Code Mod --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Debugging_Content TITLE: Modding:Debugging Content --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Common Issues & Solutions 1.1 My mod does not appear in the Mod Manager list. 1.2 "Unable to load mod. Check log files." in Mod Manager list. 1.3 My Block/Item/Entity is not appearing in game! 1.4 My Block/Item/Entity is not being rendered correctly. 1.5 All in-game textures look messed-up. 1.6 My Block with a custom shape is causing holes in adjacent blocks. 2 Debugging & Logs 2.1 Developer Mode 2.2 Launching the game through command line Common Issues & Solutions Some issues are a lot more common than other issues, and when it comes to content modding, it's likely that someone else has had the exact same issue you are trying to fix. My mod does not appear in the Mod Manager list. Your mod is likely in the wrong folder. Ensure your mod is placed inside the "Mods" directory of your game installation, or inside the folder shown when "Open Mods Folder" is clicked on the mod manager. "Unable to load mod. Check log files." in Mod Manager list. Your modinfo file may be formatted wrong. You may have two mods with the same modid. Your mod structure is not setup correctly. See Developing a Content Mod . The modinfo, assets, and (optionally) modicon file must be found inside the top-level folder of your mod. My Block/Item/Entity is not appearing in game! There is likely a mistake in your asset's json file. See the Debugging & Logs section below. Check you have added your asset to the correct creative menu. Or, see if the object exists by using /giveitem itemcode or /giveblock blockcode. My Block/Item/Entity is not being rendered correctly. Your texture and shape properties may not be setup correctly. The game cannot find the texture or shape specified. This could be a domain issue. If you are using a base-game texture from a mod, make sure to prefix the filepath with " game: ". All in-game textures look messed-up. Texture Atlas glitches may look similar to this. This is almost always a corrupted texture-atlas issue. Although the issue is supposedly solved from v 1.19.0 , this can still sometimes happen due to having a large number of textures. It may also be caused by using unsupported graphics cards, potentially those that have a low amount of VRAM. My Block with a custom shape is causing holes in adjacent blocks. Blocks with custom shapes may need to be defined as not opaque. Your block needs to be marked as not solid and not opaque. "sidesolid" : { "all" : false }, "sideopaque" : { "all" : false } This is due to how block occlusion works - A block's face is only rendered if the connecting face is not marked as solid. Debugging & Logs Vintage Story logs... pretty much everything. If there is a particular issue inside a json file, the logs will tell you what file it is. Developer Mode Developer mode has a fair few debugging settings when creating mods, and is particularly useful when creating mods. To enable developer mode, go into the in-game settings, and and check the " Developer Mode " box in the interface tab. This will enable a new tab in the settings window called " Dev" . For content mods, it is recommended to enable both the " Error Reporter " and "Extended Debug Info" checkboxes. When loading a world with developer mode enabled, you can see all the logs whilst your world loads. You can also view the logs folder by clicking " Open Logs Folder " at the bottom of this screen. Launching the game through command line Although Vintage Story logs everything, it does so in a number of different files. To speed up debugging, you can choose to load the game through the command line via dotnet. This will give you a color-coded console where everything is logged. To do this, you will need to create a shortcut. The shortcut can be created anywhere, but it is recommended to make it in the Vintage Story folder. To create a shortcut in Windows , right click on any space inside the folder, hover over " New" , and then " Shortcut" . When asked for the location of the item, enter the following: "C:\Program Files\dotnet\dotnet.exe" VintageStory.dll --tracelog You will also be asked to name the shortcut. I called mine "modloader", but you can name it whatever you want. Now, right click on the newly created shortcut and go in properties. In the " Start in: " input, copy and paste the folder that leads to your Vintage Story exe. Click " Apply " and " OK ", and now launch your shortcut. You should see a console launch, and shortly after, the game will launch. To check for errors, load a world. When it finishes loading, look at the console. If there were any errors or warnings during the world startup, they will be copied and displayed near the bottom of the console. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Debugging Content • API Updates • Modding Efficiently • Remapping Objects • Mod Compatibility • All Troubleshooting Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Developing_a_Content_Mod TITLE: Modding:Developing a Content Mod --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . Contents 1 Selecting an IDE 2 Template/Example Mod Setup 3 Manual Mod Setup 3.1 Mod Workspace 3.2 ModInfo.json 3.3 Modicon.png 3.4 Assets Folder 4 Mod Domains 5 Publishing a Content Mod 6 Updating a Content Mod to a new Game Version 7 What's Next? Developing a content mod can be simple, but certain files and folders need to be setup correctly. For more information on what can be achieved with a content mod, see Content Mods . Selecting an IDE When creating a content mod, you will likely be using a lot of JSON files. Although JSON is a human-readable format, it can still be beneficial to equip yourself with an Integrated Development Environment (IDE). Simply put, for the purpose of modifying JSON files, an IDE works as a fancy text editor that helps with formatting. It is recommended to select from one of the IDEs below. If you are also planning on creating code mods, it is recommended to use an IDE that is recommended for code mods. Name Free? Works on... Recommended for Code Mods Visual Studio (Recommended) Yes - Community Edition Windows Yes Visual Studio Code Yes Windows, macOS, Linux No JetBrains Rider Yes - Non-commercial use Windows, macOS, Linux No Notepad++ Yes Windows No Template/Example Mod Setup If you want to get into modding and do not wish to manually setup your folders, there are two options. The template file contains a modinfo, modicon, and folder structure for your mod. The example file is identical, however contains a number of existing assets which are used within the wiki's tutorials. Both files can be found and downloaded from GitHub . Simply unzip them into your game's mod folder. Content Mod Example/Template Manual Mod Setup If you wish to setup a content mod on your own, or want more understanding of the files, follow the instructions below. Mod Workspace To start setting up your mod, navigate to your Vintage Story install location, and enter the mods folder. Create a new folder with your mod's name - This will be where all mod-related files will be placed. It is recommended to open this folder in your selected IDE. Note that creating JSON files may require changing file extensions. If you do not know how to do this, follow these instructions . ModInfo.json For your mod to successfully load, you have to tell Vintage Story that our mod exists, as well as some details about it. To do this, create a new file called "modinfo.json" inside your mod workspace folder, either using your IDE or through your OS. Note that this cannot be created in a subfolder - It must be inside the root mod workspace folder. Open the file, and paste the following json code: { "type" : "content" , "modid" : "examplecontentmod" , "name" : "VS Wiki Example Content Mod" , "authors" : [ "Nat @ Vintage Story Wiki" ], "description" : "An example showcase of content mod additions." , "version" : "1.0.0" } This file is a very good example of how a json file is formatted, and you will notice that nearly every asset uses this file format. Json files list a set of "key": "value" entries, allowing you to change those values to fit what is desired. In this case, the following keys represent: "type": "content" - This tells Vintage Story that the mod is a content mod, and should load the provided assets. The options here are " theme ", " content ", or "code", however for this mod type you will use "content". "modid": "examplecontentmod" - This is your unique mod ID, which can be any combination of lowercase letters and numbers. "name": "..." - This is your mod's name, and displays how it should be displayed within the game. Note that this is just for display and does not affect the assets you create. "authors": [ "..." ] - This is an array of mod authors. Due to this entry using square brackets ( [ ] ) , it tells you that this value can accept multiple values, which are seperated by commas. "description": "..." - This is the mod description, which will be shown on the mod manager screen in game. "version": "1.0.0" - This is your mod's version. It follows the format of "major.minor.patch", called semantic versioning . Also note that our file starts and ends with curly brackets ( { } ). This tells us that this file contains a single object. If a json file starts with square brackets ( [ ] ), this tells us that you can include multiple objects within that single file. You can fill in the values above with your own mod info, or keep them the same. Most tutorials on the wiki will use this (or a very similar) modinfo file. This is just a basic modinfo file. For more information, and a more comprehensive list of available properties, visit the Modinfo page. Modicon.png If desired, an image file called "modicon.png" can be placed or created inside the root folder of your mod workspace. This will automatically be loaded into Vintage Story, and be displayed next to your mod on the mod manager menu. Assets Folder To actually create and modify game assets, Vintage Story searches for specific filepaths. Inside your mod workspace, create a folder called "assets" . Inside the new assets folder, create another new folder with the same name as your mod id. For example, my folder would be called "examplecontentmod" , as this is my mod id. This new folder is called a domain , and a single mod can be made up of a number of domains. Therefore, domains can actually have any name, but they must also be made from lowercase letters and numbers. This new folder is where your mod assets will be created! If a tutorial refers to your 'mod assets' folder, it is likely referring to this folder. When your mod assets folder has been created, you are officially ready to start modding! Check out the "What's Next" section to link to tutorials, and come back here when you need a reminder of how to organise your content mod. Mod Domains Each subfolder inside your mod's root assets folder dictates a mod domain. A domain works as an identifier to seperate assets from multiple mods, and must be made up of lowercase letters and numbers. For example, imagine there exists two mods which both add in a new metal called 'Natium'. Without domains, Vintage Story would not be able to isolate these items based on the code alone, so it prefixes the domain to every asset. The code for Natium in Mod A now becomes " moda:natium ", and the code for Mod B now becomes " modb:natium ". When creating content mods, you will access many different assets from inside your files, and it is important to understand how domains affect this. If you wish to reference an asset that exists in another domain, including the base game, you will need to prefix the reference with the domain ID. If you are referencing an asset in the same domain, you do not have to add a prefix to your reference. For example, let us assume that "Asset A" in the "examplecontentmod" domain wishes to access the default copper texture, which is located at ' block/metal/ingot/copper'. If you use that asset location inside Asset A, Vintage Story will change it to " examplecontentmod:block/metal/ingot/copper', which will proceed to not work due to the file not existing in that domain. To counter this, you need to prefix the asset location manually with the default game prefix, and place it as " game:block/metal/ingot/copper ". This can also be used with other mod domains to use assets from other mods. Note that all base game assets are placed under the "game" domain. Publishing a Content Mod If you want to distribute your mod for others to play, it is a good idea to pack your mod into a zip file. Most operating systems include functionality to zip a set of files, but the 7-zip program is a good alternative to this. To pack your mod on Windows, select your modinfo, modicon, and assets folder, and right click on one of the files. In the context window, hover over "Send to", and then select " Compressed (zipped) folder ". When entering a name for your zip file, most mods follow the format of " modid-version". This allows users of your mod to easily differentiate between versions of your created mod. This article gives information on how to zip a set of files on other operating systems. This file can then be uploaded for people to use on the Vintage Story ModDB . After each publish, it is recommended to increase your version number in your modinfo file. Updating a Content Mod to a new Game Version When Vintage Story updates, it is highly unlikely that your content mod will need modifying. When a new major version is released, there may be rare instances where some formatting changes can cause side-effects, however these are very rare and would mainly happen when using more complex functionality. What's Next? Now your content mod has been setup, see what's next on the Content Mod page. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Content Mods • Developing a Content Mod • Packaging & Release --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Engine_Portal TITLE: Modding:Engine Portal --- CONTENT --- Other languages: English русский Welcome to the VS Engine portal. The pages found here will give you an idea of how certain features of the game work, especially in regard to modding. They often include information relevant to both code and content mods. VS Engine Pages Client Startup Parameters - Gives a list of all startup parameters for the Vintage Story client. Server Startup Parameters - Gives a list of all startup parameters for the Vintage Story server. Understanding the VS Engine - Gives detail about a number of Vintage Story engine features, and how they work. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Client Startup Parameters • Server Startup Parameters • Understanding the VS Engine • All Engine Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Entity_Behaviors TITLE: Modding:Entity Behaviors --- CONTENT --- This page is outdated. Reason: A new system is being worked on to view JSON properties. Please use this page with caution, as it may not be up-to-date. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Using Entity Behaviors Inside the entity json file , entity behaviors can be added to the client section, server section, or both. For example, here is the controlledphysics behavior from deer.json , added to both the client and server sections. This shows the pre-1.20 style of including the behavior on both sides. Note that the properties are added directly to the behavior object, instead of in a properties object like with blocks and items. Also note that the builtin entity behavior names are all lower case, unlike block and collectible behaviors. clie nt : { ... behaviors : [ ... { code : "controlledphysics" , "stepHeightByType" : { "deer-pampas-*" : 2.1251 , "deer-pudu-*" : 2.1251 , "deer-redbrocket-*" : 2.1251 , "*" : 3.1251 } }, ... ], ... }, server : { ... behaviors : [ ... { code : "controlledphysics" , "stepHeightByType" : { "deer-pampas-*" : 2.1251 , "deer-pudu-*" : 2.1251 , "deer-redbrocket-*" : 2.1251 , "*" : 3.1251 } }, ... ], ... }, In 1.20, the behaviorConfigs section was added. For behaviors that added to both sections, the properties may be specified in the behaviorConfigs section to avoid duplicating the properties in both the client and server section. Here is the above example using behaviorConfigs . behaviorCo nf igs : { "controlledphysics" : { "stepHeightByType" : { "deer-pampas-*" : 2.1251 , "deer-pudu-*" : 2.1251 , "deer-redbrocket-*" : 2.1251 , "*" : 3.1251 } }, ... }, clie nt : { ... behaviors : [ ... { code : "controlledphysics" }, ... ], ... }, server : { ... behaviors : [ ... { code : "controlledphysics" }, ... ], ... }, All Behaviors Here is a table containing all the entity behaviors of the base game. Of course mods can, and are encouraged to add new behaviors. Behavior Name Explanation Example entities Properties Side activitydriven villager Server aimingaccuracy Adds aiming inaccuracy when sprinting, jumping, etc. player playerbot Universal antlergrowth deer animals with horns beginGrowMonth c1 c2 c3 growDurationMonths grownDurationMonths noItemDrop overrideType shedDurationMonths variants Universal armorstandinventory armorstand Universal bodytemperature player Universal boss eidolon-immobolized musicTrack Client breathe The entity will need to breathe and therefore suffocate in other blocks (for example sand). player - both sides most animals - server only Universal collectitems The entity collections items laying around on the ground. player bot racoon Server commandable erel locust-hacked mechhelper Universal controlledphysics Add physics to an entity that can move on its own. most entities stepHeight = 0.6 Universal conversable Lets the player right click the entity to open a conversation dialog. libraryresonator trader villager dialogue Universal deaddecay animals decayedBlock hoursToDecay Server despawn The entity will despawn under given circumstances. projectiles - server only animals - both sides afterDays belowLightLevel minPlayerDistance minSeconds = 30 Universal drunktyping player Client emotionstates Adds emotion states with a given chance. As an example, it can be used to make an animal "hungry" (aggressive) and "saturated" (passive). most animals most humanoids states Server extraskinnable player playerbot Universal floatupwhenstuck Float up the entity if it gets stuck in a block. item most animals onlyWhenDead = false pushVelocityMul = 1 Universal grow The entity will grow and eventually turn into one of its adults. pig-wild-piglet most baby animals hoursToGrow = 96 adultEntityCodes = [] Server harvestable Handles calculating the entity drops, updating the animal weight, adjusting the drops based on weight, and the harvest inventory. wolf harvestable animals drops duration fixedweight quantitySlots Universal health Applies custom health to the entity. Means the entity can be hurt as well. bear player most entities currenthealth = 20 maxhealth = 20 basemaxhealth = 20 Server hidewatersurface boat hideWaterElement Client hunger Adds saturation and hunger, the entity needs to eat in order to stay alive. player currentsaturation = 1200 maxsaturation = 1200 saturationlossdelay = 60 * 24 currentfruitLevel = 0 currentvegetableLevel = 0 currentgrainLevel = 0 currentproteinLevel = 0 currentdairyLevel = 0 Server idleanimations player Universal interpolateposition Interpolates entity position and rotate. Smooths out the animations. It's a visual effect, therefore not available on server side. most entities Client milkable goal sheep-bighorn-female Universal mortallywoundable elk-tamed whenBelowHealth Universal mouthinventory racoon acceptStacks Universal multiply A pack of wolves for example grows in size over time (new babies are born). When installed on the client side, it shows portions eaten info text. hostile animals - server only farm animals - both sides eatAnyway growthCapEntityCodes growthCapQuantity growthCapRange pregnancyDays = 3.0 quantityPerDay spawnEntityCode requiresNearbyEntityCode requiresNearbyEntityRange = 5 multiplyCooldownDaysMin = 6 multiplyCooldownDaysMax = 12 portionsEatenForMultiply = 3 spawnEntityCode spawnEntityCodes spawnQuantityMin = 1 spawnQuantityMax = 2 requiresFood = true Universal multiplybase chicken-hen multiplyCooldownDaysMax multiplyCooldownDaysMin portionsEatenForMultiply Universal nametag Adds a name tag to the entity which will be displayed above the entity. player playerbot trader villager renderRange = 99 selectFromRandomName showtagonlywhentargeted = false Universal openablecontainer erel mechhelper Universal passivephysics Adds physics an entity that cannot move on its own, but it still affected by things like wind and gravity. item thrownstone most projectiles waterDragFactor = 1 airDragFallingFactor = 1 groundDragFactor = 1 gravityFactor = 1 Universal passivephysicsmultibox boat boat-construction Universal pettable chicken farm animals fox wolf minGeneration Server placeblock The entity will place one of the given block codes from time to time. hen - prior to 1.15 update minHourDelay = 8 * 24 maxHourDelay = 15*24 blockCodes = [] Server playerinventory player Universal playerphysics Makes an entity controllable by a player. Because this inherits from controlledphysics, do not add both to an entity. player stepHeight = 0.6 Universal repulseagents Pushes other entities back. most entities movable = true Universal reviveondeath eidolon-immobilized trader villager maxHours minHours Server rideable elk-tamed Universal rideableaccessories boat elk-tamed Universal ropetieable elk-tamed farm animals wolf minGeneration Universal seatable boat Universal selectionboxes boat boat-construction elk-tamed Universal seraphinventory armorstand-aged playerbot Universal taskai Allows you to specify tasks. most entities aitasks Server tiredness The entity will get tired over time. When it is sufficiently tired, it may sleep in a bed. player currenttiredness = 0 Universal villagerinventory villager Both Content Modding Basics Content Mods • Developing a Content Mod • Packaging & Release Tutorials Basic Basic Tutorials • 1. Content Essentials • 2. Simple Item • 3. Simple Block • 4. Simple Recipes Intermediate Intermediate Tutorials • 5. Item Variants • 6. Block Variants • 7. Complex Grid Recipes • 8. Further Recipes • 9. Simple World Generation Advanced Advanced Tutorials Other Other Tutorials • Debugging Content • Inter-Mod Compatibility Concepts Modding Concepts • Modinfo • Variants • Domains • Patching • Remapping • World Properties Moddable Assets Common Block Types • Entities • Item Types • Patches • Recipes ( Alloy , Barrel , Clayforming , Cooking , Grid , Knapping , Smithing ) • Shapes • Sounds • Textures & Icons • World Properties Uncategorized Basics Asset System • Recipes • Entities • Model Creator • Animation • VTML & Icons Worldgen WorldGen Concepts • Terrain • Ores • Trees • Json Random Generator Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Entity_Json_Properties TITLE: Modding:Entity Json Properties --- CONTENT --- Other languages: English русский This page is outdated. Reason: All entity json properties can now be viewed on the JSON Reference The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Overview In the code, these come from the fields listed with the "[JsonProperty]" attribute in EntityType.cs , plus the fields from the parent class RegistryObjectType.cs . The json parser is case insensitive, which is how the property names can have different capitalization in the code and in the table below. Property Type Default Usage json Core (no byType available) code string required A unique identifier for the entity. A domain prefix will be added dynamically depending on the location of the file. Every mod and VintageStory itself have a unique prefix. For example the code rabbit turns into game:rabbit . The code identifier has to be unique inside its domain. In theory there could be equal identifiers with different domain prefixes. Find out more about Domains . enabled boolean true If the entity will be loaded or not. Can be used to temporarily remove the entity. variantgroups array of object - Allows you define multiple variants of the same entity. (any) bytype key: string; value: object - Allows different property values to be specified based on the variant code. allowedVariants array of strings - Used to trim unnecessary items generated by combined variants. skipVariants array of strings - Similar to allowedVariants, but instead skips the creation of listed variants rather than assigning which are allowed. Common class string "entity" The entity class can add special functionalities for the entity. Can be used to add interaction to the entity or other special functionalities. Example uses are EntityItem and EntityPlayer . habitat string "land" There is sea , land , air , and underwater hitboxsize Vec2f null Convenience alias that sets both collisionBoxSize and selectionBoxSize . collisionboxsize Vec2f { x: 0.5, y: 0.5 } The cuboid for collisions with other entities and blocks. The x value is the width and length of a square at the entity's feet. The y value is the height of the cuboid. Use .debug wireframe entity to see the box in game. selectionboxsize Vec2f collisionboxsize The cuboid for interacting with or attacking the entity. This is what would commonly be considered the entity's hitbox. The x value is the width and length of a square at the entity's feet. The y value is the height of the cuboid. Use .debug wireframe entity to see the box in game. deadboxsize Vec2f null Convenience alias that sets both deadCollisionBoxSize and deadSelectionBoxSize . collisionboxsize Vec2f { x: 0.25, y: 0.25 } The cuboid for collisions with other entities and blocks when the entity is dead. The x value is the width and length of a square at the entity's feet. The y value is the height of the cuboid. Use .debug wireframe entity to see the box in game. deadselectionboxsize Vec2f deadcollisionboxsize The cuboid for interacting with or attacking the entity's carcass. The x value is the width and length of a square at the entity's feet. The y value is the height of the cuboid. Use .debug wireframe entity to see the box in game. eyeheight decimal number 0.1 Height of the eyes, measured from the bottom of the hitbox in meters. Used for camera angle and various other things. swimmingeyeheight decimal number eyeheight Replacement value for eyeheight while the entity is swimming. weight decimal number 25 This is unused by the base game. The documentation says it is the average weight of the entity in kilograms. canclimb boolean false Whether the entity can climb on ladders or other blocks which are climbable. canclimbanywhere boolean false Whether the entity can climb on any block, doesn't matter if it's a ladder or not. For example, this is set on locusts. obsolete: falldamage boolean true Whether the entity will take fall damage. Obsolete: set FallDamageMultipler to 0 instead. falldamagemultiplier decimal number 1.0 How much this creature is affected by fall damage. Setting this to 0 causes the entity to take no fall damage. Setting this to 2.0 causes it to take double fall damage. climbtouchdistance decimal number 0.5 Distance the entity can reach a block to climb on it. rotatemodelonclimb boolean false If true the entity model will be rotated to face the block its climbing on. knockbackresistance decimal number 0.0 The higher the number is the less knockback will be applied. Useful for heavy entities. attributes key: string, value: object - Custom Attributes associated with this entity. Extra attributes added to the entity. Those are final and cannot be modified. It's a good way to keep things organized and and modifiable. These can be used by behaviors or the entity class: a ttr ibu tes : { "attackPower" : 10 }, behaviorconfigs key: string, value: object - Adds fields to both the client and server behavior properties. The dictionary is indexed by the behavior code. If a corresponding behavior is found in the client or server configs, then the properties specified in this dictionary are merged with the client/server behavior properties. Any entries in this dictionary are ignored if they do not match a client or server behavior. For example, deer.json uses this to avoid having to copy the controlledphysics properties to both the client and server behavior definition: behaviorCo nf igs : { "controlledphysics" : { "stepHeightByType" : { "deer-pampas-*" : 2.1251 , "deer-pudu-*" : 2.1251 , "deer-redbrocket-*" : 2.1251 , "*" : 3.1251 } }, ... }, clie nt : { ... behaviors : [ ... { code : "controlledphysics" }, ... ], ... }, server : { ... behaviors : [ ... { code : "controlledphysics" }, ... ], ... }, sounds key: string, value: string - Sounds of the entity, you can also add your own sounds and play them, but here is a list of default sounds used death , hurt , idle , jump , swim and eat idlesoundchance decimal number 0.3 How likely it is for the entity to play an idle sound. idlesoundrange decimal number 24 How far the idle sound played by the entity can be heard. drops array of object - The items that should drop from breaking this block. No drop By default an entity does not drop anything at all: drops : [], Drop You can also specify an item or block to drop. Therefore you need to define an ItemStack , with the given properties: Property Default Explanation type block Can either be block or item . code (required) - The complete code (can also include domain) of the item or block. lastdrop false If true and the quantity dropped is >=1 any subsequent drop in the list will be ignored. attributes - Tree Attributes that will be attached to the resulting itemstack. tool - If specified then given tool is required to break this block. quantity - (one) Determines the quantity of items which will be dropped. For example, if the entity should drop charcoalpile : drops : [ { t ype : "item" , code : "charcoal" } ], You can also specify drop special itemstack if the entity is killed by certain tool, similar to Tallgrass which only drops something if it's mined by a knife: drops : [ { t ype : "item" , code : "drygrass" , t ool : "knife" }, ], Chance drops Let's take a look at an example. This is the drop property of rock: drops : [ { t ype : "item" , code : "stone-{rock}" , qua nt i t y : { avg : 2.5 , var : 0.5 } }, ] This will drop 2-3 blocks. avg : Stands for the default drop quantity. If var is 0 or not specified it will always drop the given average. var : How much the drop rate can vary. Meaning the drop rate can be avg - var at minimum and age + var at maximum. Furthermore you can also switch between different distribution modes using the dist property. Overview - Distribution modes Name Explanation uniform Select completely random numbers within avg-var until avg+var. triangle Select random numbers with numbers near avg being the most commonly selected ones, following a triangle curve. gaussian Select random numbers with numbers near avg being the most commonly selected ones, following a gaussian curve. narrowgaussian Select random numbers with numbers near avg being the most commonly selected ones, following a narrow gaussian curve. inversegaussian Select random numbers with numbers near avg being the least commonly selected ones, following an upside down gaussian curve. narrowinversegaussian Select random numbers with numbers near avg being the least commonly selected ones, following an upside down gaussian curve. invexp Select numbers in the form of avg + var, wheras low value of var are preferred. stronginvexp Select numbers in the form of avg + var, wheras low value of var are strongly preferred. strongerinvexp Select numbers in the form of avg + var, wheras low value of var are very strongly preferred. dirac Select completely random numbers within avg-var until avg+var only ONCE and then always 0. Multiple Drops Of course you can also define multiple drops at once. A Sapling for example can drop a sapling and a stick: drops : [ { t ype : "block" , code : "sapling-{wood}" , qua nt i t y : { avg : 0.02 , var : 0 }, }, { t ype : "item" , code : "stick" , qua nt i t y : { avg : 0.02 , var : 0 }, } ], Last Drop In order to add a special drop, which (if dropped) prevents all other drops, you can use the lastDrop property: drops : [ { t ype : "item" , code : "stick" , qua nt i t y : { avg : 0.2 , var : 0 }, las t Drop : true }, { t ype : "item" , code : "sapling" , qua nt i t y : { avg : 1.25 , var : 0 } } ], The entity will either drop a stick with a chance of 20% or an average of 1.25 saplings. client: { renderer string - Name of there renderer system that draws this entity. Most entities use "shape". The valid values are: item dummy blockFalling shape playerShape echoChamber texture CompositeTexture Sets the "all" entry in textures. The "all" entry overwrites all other textures in the shape. base overlays alternates Default example (player model): te x ture : { base : "entity/humanoid/player" } Using variantgroups (rock): te x ture : { base : "entity/humanoid/player-{type}" } Overlay texutre: te x ture : { base : "entity/humanoid/player-{type}" , overlays : [ "entity/humanoid/player-{type}-overlay" ], } Random textures: te x ture : { base : "entity/humanoid/player-{type}" , al ternates : [ { base : "entity/humanoid/player-{type}2" }, { base : "entity/humanoid/player-{type}3" } ] } Random textures and overlays combined: te x ture : { base : "entity/humanoid/player-{type}" , overlays : [ "entity/humanoid/player-{type}-overlay" ], al ternates : [ { base : "entity/humanoid/player-{type}2" , overlays : [ "entity/humanoid/player-{type}2-overlay" ] }, { base : "entity/humanoid/player-{type}3" , overlays : [ "entity/humanoid/player-{type}3-overlay" ] } ] } textures key: string, value: CompositeTexture Can be used to replace specific textures of the shape. base overlays alternates Replace arrow texture of the shape, the stick texture remains untouched: te x tures : { "arrow" : { base : "entity/arrow/stone" } } shape CompositeShape - The shape of the entity. base The path to the shape json file, the base dir is assets/shapes/ . shape : { base : "entity/arrow" } glowlevel 0 ... 255 0 Causes the entity to visually glow if Bloom is enabled. size decimal number 1 Can be used to scale the entity up or down. sizegrowthfactor decimal number 0 The rate at which the entity's size grows with age - used for chicks and other small baby animals. pitchstep boolean true Makes entities pitch forward and backwards when stepping. This is usually left as the default, except for humanoid entities where most set it to false. behaviors array of object Client side entity behaviors . animations array of AnimationMetaData WIP code string The identifier of the animation. attributes key: string, value: object - Custom attributes that can be used for the animation. Valid vanilla attributes are: damageAtFrame (float) soundAtFrame (float) authorative (bool) animation string The animation code identifier to play. weight decimal number 1.0 Animations with a high weight value will be prioritized over the other animations when the animations are combined. For example if the player aims with a bow while jumping around, the aiming animation has a higher weight (for the upper body) so the jumping animation will be reduced. elementweight key: string, value: decimal number Allows you to specify a weight for each element individually. animationspeed decimal number 1.0 The speed of the animation. mulwithwalkspeed boolean false Whether the animation speed should be multiplied by the entity's current movement speed. easeinspeed decimal number 10 The ease in affects the weight of the animation, so that it starts off with weak strength (the animation only slightly changes the entity shape) and gradually goes to nearly full strength. The ease in only affects the strength of the animation, not the speed the animation plays at. With easeinspeed=1, it takes roughly 0.7 seconds for the animation to get to half strength. Setting the easeinspeed to 2 causes the ease in to go roughly twice as fast (animation strength increases faster), such that get animation gets to half strength after about 0.35 seconds. Assuming a constant frame rate, this is the exact formula for the ease in strength (assuming weight=1, blendmode=add). 1.0 - (1 - (1/framerate)*easeinstrength)^frame easeoutspeed decimal number 10 Multiplies the speed at which the animation eases out. See the easein field for an explanation of easing. weightcapfactor decimal number 0.0 This field allows the ease in and ease out to work in the Average/AddAverage blend modes. The field has no affect for the Add blend mode, where ease in and ease out work without setting this field. First, let's consider how the blending works without setting this field. Let's say animation A has weight 2 and animation B has weight 1, and they are both playing in the middle so ease in/out does not apply. Due to the weighted average, animation A will have 2/3 of its normal effect, and animation B will have 1/3 of its normal effect. Nothing wrong here. Next, let's consider what happens when animation A is eased in and animation B is playing at full strength with weight 1. Due to the ease in, animation A has weight 0.1. Now animation A gets 0.1/1.1 weight (low weight), and animation B gets 1/1.1 weight (almost full strength). Nothing wrong here either. Finally, let's consider what happens when only animation A is playing and it is getting eased in. Due to the easing, animation A only has strength 0.1. The total weight is 0.1. Animation A gets 0.1/0.1 = 1 (full strength)? That's not right; the ease in is supposed to reduce the strength of the animation. The weightcapfactor sets the minimum total weight. Let's say it is set to 1.0. Now let's revist the previous example. Animation A gets 0.1/max(1.0, 0.1) = 0.1/1.0 = 0.1. So with weightcapfactor=1.0, animation A gets properly eased in, even when it is the only animation playing. So for animations that use the Average/AddAverage modes, set weightcapfactor equal to the weight, for ease in/ease out to work properly. Note that the comment in the source code is wrong for this field as of 1.20.0-pre.4. triggeredby Specifies by what the animation is triggered. oncontrols array of strings An array of activities that start the animation. none idle move sprintmode sneakmode fly swim jump fall climb floorsitting dead break place glide mounted matchexact boolean false If set to true, all OnControls elements need to be happening simultaneously to trigger the animation. If set to false, at least one OnControls element needs to be happening to trigger the animation. defaultanim boolean false Set to true if this animation should be played whenever no other animations are playing. blendmode string Animation blend mode for all elements. add Add the animation without taking other animations into considerations. average Add the pose and average it together with all other running animations with blendmode Average or AddAverage. addaverage The comment in the source code says, "Add the animation without taking other animations into consideration, but add it's weight for averaging." However, it actually has done the same thing as Average since version 1.17. elementblendmode key: string, value: string Allows you to specify a blend mode for each element individually. Any element not in this list defaults its blend mode to the value specified in blendmode . suppressdefaultanimation boolean false When this animation plays, stop the default animation, even if the entity class set alwaysRunIdle=true (as EntityPlayer does). When alwaysRunIdle=false, setting suppressdefaultanimation is unnecessary, because all non-default animations already stop the default animation. holdeyeposaftereasein decimal number 99 In first person immersive mode, the camera position is moved with the player entity's eye position as it moves from animations. However, once any of the playing animations have an ease value above this, then the camera is held in place until the ease value is reduced. This field is ignored for other camera modes, including first person non-immersive mode. The ease value ranges from 0 to 1, where 1 means the animation is at full strength. So setting holdeyeposaftereasein to a value of 1 or greater effectively disables this hold behavior. The camera is held forall animiations if any of the running animations have an ease value above their holdeyeposaftereasein . The point of this feature is to stop the cammera from slowly moving at the end of the animation. The ease feature uses an exponential decay formula, so it never quite reaches 1. holdeyeposaftereasein prevents the camera from slowly continuing to move forever. Note that as of 1.20.0-pre.4, the official documentation is wrong for this field. clientside boolean false Usually the client starts playing animations before the server, because the client learns about which keys are pressed before the server. Then later the server syncs its list of running animations with the client. The client stops any animations not in the received list. However, if clientside=true, then the client will not stop the animation based on the list received by the server. withfpvariant boolean false If true, then for player animations, use the animation specified in fpvariant instead of this animation. In first person mode, the player's entity can use different animations than the entities for other players or for the player in 3rd person. If withfpvariant=true, then the animation code is taken from this animation metadata object, with "-fp" appended. It inherits all of the other entries from this animation metadata object. If withfpvariant=false, it then tries to look up the animation metadata with the fpEnding suffix. The suffix is "-fp" in regular first person mode, and "-ifp" in immersive first person mode. If those are not found, then it uses the regular animation. So withfpvariant=true is used to save having to copy all of the animation metadata when the first person animation needs the same metadata but a different animation code, and immersive first person and first person use the same animation code. animationsound object - Play a sound while playing the animation frame integer 0 Which animation frame to start the sound on. location asset - Which sound to play. randomizepitch boolean true Randomly change pitch of the sound each time it is played. range decimal number 32 How many blocks away the sound can be heard from. } server: { attributes key: string, value: object - Custom Attributes associated with this entity only known by the server. They will not be send to the client. Extra attributes added to the entity for server use only. Those are final and cannot be modified. It's a good way to keep things organized and and modifiable. These can be used by behaviors or the entity class: a ttr ibu tes : { "attackPower" : 10 }, behaviors array of object Server side entity behaviors . spawnConditions object - Specifies the circumstances of the entity spawn. climate object If this is set, then it will override the climate settings in runtime and worldgen. When the entity has the same climate settings at runtime and worldgen, it is recommended to use this as a convenience to avoid copy pasting the climate settings into both sections. mintemp integer -40 Minimum temperature at which the entity can spawn. maxtemp integer 40 Maximum temperature at which the entity can spawn. minrain decimal number 0 Minimum rain average at which the entity can spawn. maxrain decimal number 1 Maximum rain average at which the entity can spawn. minforest decimal number 0 Minimum forest density at which the entity can spawn. maxforest decimal number 1 Maximum forest density at which the entity can spawn. minshrubs decimal number 0 Minimum shrubs density at which the entity can spawn. maxshrubs decimal number 1 Maximum shrubs density at which the entity can spawn. minforestorshrubs decimal number 0 Minimum shrubs or forest density at which the entity can spawn. minY decimal number 0 Relative world height which the entity cannot spawn below. 0...1 is world bottom to sea level, 1...2 is sea level to world top. So the default value means that the entity can spawn all the way at the bottom of the world. maxY decimal number 2 Relative world height which the entity cannot spawn above. 0...1 is world bottom to sea level, 1...2 is sea level to world top. So the default value means that the entity can spawn all the way at the top of the world. runtime object A set of spawn conditions for chunks that have already been generated. group string The type of mob. Valid values: hostile neutral passive If this is set to hostile, the mob will be prevented from spawning in locations that prohibit hostile mobs. chance decimal number 1.0 How long it takes to make an attempt to spawn the entity: 1.0 -> 4 seconds, 0.1 -> 40 seconds. maxquantity integer 20 Stop spawning new entities of this type if there are this many or more entities of this type already in the single player world. With more than one player, the number is multipled by the spawnCapMul . maxquantitybygroup object null Stop spawning new entities of this type if there are this many or more entities that match this wildcard already in the single player world. With more than one player, the number is multipled by the spawnCapMul . maxquantity integer 0 Max number of entities that match the wildcard. code asset location string "" Wildcard to match entities in the group. mindistancetoplayer integer 18 Minimum distance the entity can spawn away from the player. minlightlevel integer 0 The minimum light level for an object to spawn. maxlightlevel integer 32 The maximum light level for an object to spawn. lightleveltype string "MaxLight" The type of light counted towards minlightlevel and maxlightlevel for spawning purposes. onlyblocklight Will get you just the block light onlysunlight Will get you just the sun light unaffected by the day/night cycle maxlight Will get you max(onlysunlight, onlyblocklight) maxtimeofdaylight Will get you max(sunlight * sunbrightness, blocklight) obsolete: groupsize object Alias for herdsize. herdsize NatFloat { avg: 1, var: 0 } Determines the size of the group. By default the size of the group is always one. companions array of asset strings - The first entity in the spawned herd is always this entity. If the herd size (randomly chosen as described by herdsize) is greater than 1, and companions is non-empty, then the remaining entities are randomly chosen from companions. If companions is empty, then the remaining spawned entities are just copies of this entity. insideblockcodes array of string [ "game:air" ] The block codes in which the entity can spawn. Entities which can spawn underwater might use [ "game:water" ] instead. requiresolidground boolean true If the entity requires a solid block below it. For example birds and fishes do not require it. tryonlysurface boolean false If false the game will also try to spawn the entity below the surface (in a cave for example). For ordinary animals this should be true so they only spawn on the surface. climatevaluemode EnumGetClimateMode string worldgenvalues Whether the rain and temperature values are referring to the worldgen values (i.e. yearly averages) or the current values at the moment of spawning. Valid values: WorldGenValues The values generate during world generation, these are loosely considered as yearly averages NowValues The values at the current calendar time ForSuppliedDateValues The values at the supplied calendar time, supplied as additional arg ForSuppliedDate_TemperatureOnly The values at the supplied calendar time, ignoring rainfall etc. ForSuppliedDate_TemperatureRainfallOnly The values at the supplied calendar time, ignoring forest cover etc. mintemp integer -40 Minimum temperature at which the entity can spawn. maxtemp integer 40 Maximum temperature at which the entity can spawn. minrain decimal number 0 Minimum rain average at which the entity can spawn. maxrain decimal number 1 Maximum rain average at which the entity can spawn. minforest decimal number 0 Minimum forest density at which the entity can spawn. maxforest decimal number 1 Maximum forest density at which the entity can spawn. minshrubs decimal number 0 Minimum shrubs density at which the entity can spawn. maxshrubs decimal number 1 Maximum shrubs density at which the entity can spawn. minforestorshrubs decimal number 0 Minimum shrubs or forest density at which the entity can spawn. minY decimal number 0 Relative world height which the entity cannot spawn below. 0...1 is world bottom to sea level, 1...2 is sea level to world top. So the default value means that the entity can spawn all the way at the bottom of the world. maxY decimal number 2 Relative world height which the entity cannot spawn above. 0...1 is world bottom to sea level, 1...2 is sea level to world top. So the default value means that the entity can spawn all the way at the top of the world. worldgen object A set of spawn conditions for when chunks are generated. group string The type of mob. Valid values: hostile neutral passive If this is set to hostile, the mob will be prevented from spawning in locations that prohibit hostile mobs. triesperchunk NatFloat zero How many tries per chunk the entity has to spawn. minlightlevel integer 0 The minimum light level for an object to spawn. maxlightlevel integer 32 The maximum light level for an object to spawn. lightleveltype string "MaxLight" The type of light counted towards minlightlevel and maxlightlevel for spawning purposes. onlyblocklight Will get you just the block light onlysunlight Will get you just the sun light unaffected by the day/night cycle maxlight Will get you max(onlysunlight, onlyblocklight) maxtimeofdaylight Will get you max(sunlight * sunbrightness, blocklight) obsolete: groupsize object Alias for herdsize. herdsize NatFloat { avg: 1, var: 0 } Determines the size of the group. By default the size of the group is always one. companions array of asset strings - The first entity in the spawned herd is always this entity. If the herd size (randomly chosen as described by herdsize) is greater than 1, and companions is non-empty, then the remaining entities are randomly chosen from companions. If companions is empty, then the remaining spawned entities are just copies of this entity. insideblockcodes array of string [ "game:air" ] The block codes in which the entity can spawn. Entities which can spawn underwater might use [ "game:water" ] instead. requiresolidground boolean true If the entity requires a solid block below it. For example birds and fishes do not require it. tryonlysurface boolean false If false the game will also try to spawn the entity below the surface (in a cave for example). For ordinary animals this should be true so they only spawn on the surface. climatevaluemode EnumGetClimateMode string worldgenvalues Whether the rain and temperature values are referring to the worldgen values (i.e. yearly averages) or the current values at the moment of spawning. Valid values: WorldGenValues The values generate during world generation, these are loosely considered as yearly averages NowValues The values at the current calendar time ForSuppliedDateValues The values at the supplied calendar time, supplied as additional arg ForSuppliedDate_TemperatureOnly The values at the supplied calendar time, ignoring rainfall etc. ForSuppliedDate_TemperatureRainfallOnly The values at the supplied calendar time, ignoring forest cover etc. mintemp integer -40 Minimum temperature at which the entity can spawn. maxtemp integer 40 Maximum temperature at which the entity can spawn. minrain decimal number 0 Minimum rain average at which the entity can spawn. maxrain decimal number 1 Maximum rain average at which the entity can spawn. minforest decimal number 0 Minimum forest density at which the entity can spawn. maxforest decimal number 1 Maximum forest density at which the entity can spawn. minshrubs decimal number 0 Minimum shrubs density at which the entity can spawn. maxshrubs decimal number 1 Maximum shrubs density at which the entity can spawn. minforestorshrubs decimal number 0 Minimum shrubs or forest density at which the entity can spawn. minY decimal number 0 Relative world height which the entity cannot spawn below. 0...1 is world bottom to sea level, 1...2 is sea level to world top. So the default value means that the entity can spawn all the way at the bottom of the world. maxY decimal number 2 Relative world height which the entity cannot spawn above. 0...1 is world bottom to sea level, 1...2 is sea level to world top. So the default value means that the entity can spawn all the way at the top of the world. } Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Entity_instance_attributes TITLE: Modding:Entity instance attributes --- CONTENT --- Entity instance attributes are key values that can be set on any entity in the world. They can only be modified with server commands and code mods. These are different from entity type attributes , which apply to entity types (a pattern used to spawn an entity into the world). Unlike entity type attributes, entity instance attributes cannot be set in the json files, because they are unique to every instance of the entity. For each entity, there are 3 kinds of entity instance attributes: WatchedAttributes the most commonly used entity instance attributes. Modifications on the server side are stored in the save game and synchronized to the clients. Modifications on the client side are not synchronized to the server, and they are lost the next time the server synchronizes the entity attributes. DebugAttributes only used if the EntityDebugMode is enabled. The base game uses this to show the active AI tasks and animations for entities. When enabled, bulk changes to these attributes on the server side are synchronized to the client, but small changes are dropped due to a bug in the synchronization code (in ServerPackets.GetBulkEntityDebugAttributesPacket in VintagestoryLib.dll). Attributes stored to the save game on the server side, but not synchronized to the client. Modifications on the client side are kept until the entity is unloaded. Contents 1 Debug commands 2 Modifying attributes in code 3 Synchronization packets 4 Listening to changes 5 Base game attributes 5.1 WatchedAttributes 5.2 Attributes Debug commands Server admins can read WatchedAttributes of the currently selected entity with this server command : /entity cmd l[] attr [attribute name] WatchedAttributes of the currently selected entity can be modified with this command: /entity cmd l[] setattr string [attribute name] [value] Modifying attributes in code The entity instance attributes are read and modified like any other TreeAttributes . An attribute can hold many different types. int , float , and string are common types. Here's an example from Entity.cs where the onHurtCounter attribute is read and set. WatchedAttributes . SetInt ( "onHurtCounter" , WatchedAttributes . GetInt ( "onHurtCounter" ) + 1 ); Synchronization packets EntityBulkAttributes showing a partial update of the WatchedAttributes for one entity and a full update for another entity When the entity is first spawned, the WatchedAttributes are sent to the client in the Entity packet, along with everything else about the entity. After that, changes to individual attributes are sent in the partialUpdates field of the BulkEntityAttributes packet. However, if 10 or more attributes are modified before the next BulkEntityAttributes packet is sent, then instead all of the entity's WatchedAttributes are sent in the fullUpdates field of the BulkEntityAttributes packet. Because the server supports partial updates, modders can generally add extra WatchedAttributes without worrying about the network cost. Listening to changes Listeners can be registered for all 3 kinds of instance attributes to get notifications when the attribute changes. Although, the client will only get notifications for server modifications if the attributes are synchronized from the server to client (basically just WatchedAttributes). Use RegisterModifiedListener to register a listener. The listener is added to the attribute for one entity (not entity type), so one needs to somehow find the entity first in order to add the listener. Listeners can be removed with the UnregisterListener . Note that listeners are registered for specific attributes, but unregistering a listener removes it from all attributes. As an example, CharacterExtraDialogs adds listeners on the client side to the hunger , stats , and bodyTemp attributes when the dialog is opened. It does this so that it can redraw the health bars in the dialog. Since the WatchedAttributes are synchronized, the dialog is redrawn on the client side when the character is hurt on the server side. When the dialog is closed, the listeners are unregistered. private void Dlg_OnClosed () { capi . World . Player . Entity . WatchedAttributes . UnregisterListener ( UpdateStatBars ); capi . World . Player . Entity . WatchedAttributes . UnregisterListener ( UpdateStats ); } private void Dlg_OnOpened () { capi . World . Player . Entity . WatchedAttributes . RegisterModifiedListener ( "hunger" , UpdateStatBars ); capi . World . Player . Entity . WatchedAttributes . RegisterModifiedListener ( "stats" , UpdateStats ); capi . World . Player . Entity . WatchedAttributes . RegisterModifiedListener ( "bodyTemp" , UpdateStats ); } Even if an attribute is set to it's current value (essentially the value is unchanged), the modification listeners are still invoked. The onHurt listeners rely on this; the listeners are triggered whenever the entity is hurt, even if the damage is the same as last time ( onHurt is set to its current value). If the modifications are sent to the client through the fullUpdates field (because 10 or more attributes were changed), then the client cannot tell which fields were really modified. So the client invokes all registered listeners, even if the field did not change. So the listeners have to be aware that they may get spurious notifications. The onHurt listener in Entity.cs uses the onHurtCounter to filter out spurious notifications (it ignores the onHurt notification if onHurtCounter was not modified). This strategy can be used for modders adding new attributes, but none of the existing attributes (except for onHurt ) have a corresponding counter attribute. WatchedAttributes . RegisterModifiedListener ( "onHurt" , () => { float damage = WatchedAttributes . GetFloat ( "onHurt" , 0 ); if ( damage == 0 ) return ; int newOnHurtCounter = WatchedAttributes . GetInt ( "onHurtCounter" ); if ( newOnHurtCounter == onHurtCounter ) return ; onHurtCounter = newOnHurtCounter ; ... }); Base game attributes Code mods can create new attributes with any name. These are the attributes from the base game. In the below list, a dot is used to signify a subattribute of a TreeAttribute. To read these attributes, one needs to read the parent attribute with GetTreeAttribute , then get the child from it; the game does not parse the dot notation shown below. Attribute names are case sensitive. WatchedAttributes entityDead : (int) 0 if the entity is alive, or 1 if the entity is dead. idleSoundChanceModifier : (float) animations : (TreeAttribute) extraInfoText : (TreeAttribute) onHurt : (float) the amount of damage applied last time the entity was hurt. Listen to this to get notifications when the entity is hurt. onHurtCounter : (int) the number of times the entity has been hurt. This is used to detect spurious notifications. onHurtDir : (float) angle that the damage came from, measured from the top down view. onFire : () grow : (TreeAttribute) textureIndex : (int) kbdirX : (double) X component of the direction that the entity is currently getting knocked back. kbdirY : (double) Y component of the direction that the entity is currently getting knocked back. kbdirZ : (double) Z component of the direction that the entity is currently getting knocked back. hunger.currentsaturation : (float) how full the player is. hunger.maxsaturation : (float) maximum fullness that the player can have. bodyTemp.bodytemp : (float) player temperature. wetness : (float) wetness in the range of 0 to 1. health.currenthealth : (float) current hit points. health.maxhealth : (float) maximum hit points. stats.walkspeed : (TreeAttribute with all entries of float type) stats.healingeffectivness : (TreeAttribute with all entries of float type) stats.hungerrate : (TreeAttribute with all entries of float type) stats.rangedWeaponsAcc : (TreeAttribute with all entries of float type) stats.rangedWeaponsSpeed : (TreeAttribute with all entries of float type) stats.rangedWeaponsSpeed : (TreeAttribute with all entries of float type) guardedEntityId : (long) the target entity that the guard entity is guarding. guardedPlayerUid : (string) the target player that the guard entity is guarding. Attributes dmgkb : (int) set to 1 if a knockback should be started on the entity's next physics tick. It is set back to 0 when the knockback is started. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:EvolvingNatFloat TITLE: Modding:EvolvingNatFloat --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Contents 1 EvolvingNatFloat 1.1 Example in code 1.2 Transform variants 1.3 Visual representation EvolvingNatFloat A number generator whose return value changes over time according to a transform and factor. Each sequential value is retrieved according to the first value and the sequence number. To use EvolvingNatFloat, add the using statement for Vintagestory.API.MathTools . Important note : The value of sequence should always satisfy sequence >= 0 Example in code using Vintagestory.API.MathTools ; public class EvolvingNatFloatMod : ModSystem { public override void StartServerSide ( ICoreServerAPI api ) { float factor = 1 ; EvolvingNatFloat evolve ; evolve = new EvolvingNatFloat ( EnumTransformFunction . CLAMPEDPOSITIVESINUS , factor ); float firstValue = 5 ; for ( float sequence = firstValue ; sequence < 100 ; sequence ++) { // Retrieve each sequential value float y = evolve . nextFloat ( firstValue , sequence ); } } } Transform variants Overview Name Explanation clampedpositivesinus Clamped positive sine wave function. cosinus Cosine wave function. identical Identical to first value. inverselinear Linear change in the direction of the first value. linearnullify Linear change towards zero, then clamp at zero. linearreduce Linear change in the opposite direction of the first value. quadratic Quadratic function. root Root function. sinus Sine wave function. smoothstep Smooth step function. Visual representation For every representation except smoothstep, the factor = 1 For every representation except smoothstep, linearnullify, the initalValue = 1 Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:GUIs TITLE: Modding:GUIs --- CONTENT --- Other languages: English español русский This page was last verified for Vintage Story version 1.19.8 . Creating good graphical user interfaces is a challenging task - sometimes even the largest of companies mess it up. In Vintage Story you currently need to formulate your GUIs in code, which requires acute spacial thinking. Hopefully someday we'll have a point&click GUI designer that takes off a lot of work. Until then, here are some of the essentials. Contents 1 Dialog Outline Modes 1.1 Mode 0: No Outlines 1.2 Mode 1: Element and Composer Outlines 1.3 Mode 2: Element Outlines 2 GUI Base Classes 2.1 Block Entity GUI 2.2 HUD 2.3 General Purpose GUI 3 ElementBounds 4 Layout 4.1 Alignment 4.2 Sizing 5 GUI Basics 5.1 ElementBounds 5.2 GuiComposer 5.2.1 Dialog / Graphics 5.2.2 Text 5.2.3 UI Control/Input 5.2.4 Tables/Grids/Inventory 5.2.5 Other 6 GuiElements 6.1 GuiElementDialogBackground 6.2 GuiElementDialogTitleBar 6.3 GuiElementInset 6.4 GuiElementStaticText 7 Example 7.1 First iteration: basic static text 7.2 Second iteration: add title bar 7.3 Final iteration: connect to hot key 8 Example - Scrolling Dialog Outline Modes The Cycle Dialog Outline Modes keybinding can be used to add outlines around GUI elements, making it much easier to understand how your code is translated into the visual GUI. Pressing the key cycles between the 3 modes. By default the key is bound to Alt + F10 , but on some Linux distros it may need to be rebound. Note that a number of Alt + F... keys are overridden by the Nvidia GeForce Experience app. You may wish to change the key bindings for these. Mode 0: No Outlines This is the default mode when the game is started. The GUI elements are shown, but no outline rectangles are drawn. Mode 1: Element and Composer Outlines In addition to the mode 2 rectangles around GuiElements (explained below), bold white rectangles are drawn around GuiComposers. Controls dialog in outline mode 1 The outer, bold white rectangle is from a GuiComposer. Mode 2: Element Outlines The bounding boxes of GuiElements are drawn as rectangles. The rectangle color is determined by the possibly overridden implementation of GuiElement.OutlineColor , which returns a color in 0xAARRGGBB format . Controls dialog in outline mode 2 Class Color GuiElementHoverText yellow - 0x80FFFF00 GuiElementItemSlotGridBase bold green - 0xFF00FF00 GuiElementClip bold red - 0xFFFF0000 GuiElement white - 0x80FFFFFF GUI Base Classes You should encapsulate every GUI Dialog into its own class, albeit a dialog may consist of multiple windows - the vanilla character dialog for example has two - one for the players wearables and one for the player stats. There's a couple of base classes to make your life a bit easier. Block Entity GUI To create a GUI that's bound to a block entity, inherit from GuiDialogBlockEntity . In your block entity code you can then create and open that gui e.g. upon player interaction (example: Quern Block Entity , Quern Block Dialog ) HUD To create a GUI element which is not interactable, inherit from HudElement . General Purpose GUI For any other uses, inerhit from the general purpose class GuiDialog , from which HudElement and GuiDialogBlockEntity also inherit from. You can override ToggleKeyCombinationCode to something like "yourAweseomeHotkeyCode" and use capi.Input.RegisterHotKey + capi.Input.SetHotKeyHandler to have your own keyboard key mapped to opening/closing your GUI (example: World Map ) ElementBounds An ElementBounds instance describes the several bounding boxes and offsets of a GuiElement, which are summarized in the diagram below. For fixed alignments, (fixedX, fixedY) is the vector from the top-left corner of the parent element's content box to the top-left corner of the child element's offset point. For auto alignments, (marginX, marginY) is used instead (technically (absMarginX, absMarginY)). After that comes another offset vector called (fixedOffsetX, fixedOffsetY) which points to the top left corner of the child padding box. Inside of the padding box is the content box. The inner width/height only contains the content. The outer width/height contains the content and padding. The fields in ElementBounds with the 'abs' prefix should be treated as internal fields. They are written by ElementBounds.CalcWorldBounds. However, these other fields may be set either directly or with helper methods. Fields Primary setters Purpose fixedX/fixedY WithFixedPosition BelowCopy RightCopy FixedUnder FixedRightOf FixedLeftOf WithFixedOffset Fixed The offset of the child padding top-left corner relative to the parent content top-left corner. These are the main fields used to place GUI elements. These fields should only be set if the corresponding x/y dimension has a fixed sizing and alignment mode. fixedWidth/fixedHeight WithFixedSize WithFixedWidth WithFixedHeight Fixed The size of the element content. These fields should only be used when the corresponding dimension is in the ElementSizing.Fixed or ElementSizing.PercentualSubstractFixed mode. fixedPaddingX/fixedPaddingY WithFixedPadding The amount of empty space to put around the element fixedMarginX/fixedMarginY WithFixedMargin These fields are never read by Vanilla VS. The corresponding absMarginX/absMarginY are internally calculated based on the alignment mode, but those calculations ignore fixedMarginX/fixedMarginY. ParentBounds/ChildBounds WithChild ForkContainingChild Tracks the parent/child relation between elements Alignment WithAlignment FixedPos Fixed Percentual The alignment of the element. If set to None the FixedX/Y Position is used. For any other value, the FixedX/Y values break the layout. For example when you used RightTop the element will always be at the right top most corner of its parent bounds. If you use RightFixed the element will be right aligned, but its Y-Position is determined by FixedY horizontalSizing/verticalSizing WithSizing The sizing method to be used, can be either Fixed (default), Percentual or FitToChildren Layout Compared to the CSS flow layout algorithm, the GuiComposer layout algorithm is very primitive. Alignment The alignment option can automatically place a child element in any of the 4 corners or 4 edges of the parent. However, the alignment algorithm does nothing to prevent two child elements from overlapping on the same edge/corner. For example, if two text boxes were added to the bottom edge of a dialog, then the two pieces of text would overlap. There are a few options to fix the conflict: Use different edges or corners for the two pieces of text. Use .WithFixedAlignmentOffset(0, -10) to move one of the children up 10 pixels (relative to the bottom alignment in this example). Give up on automatic alignment and used fixed alignment instead, where the child coordinates must be calculated exactly relative to the top-left corner of the parent content box. Typically one uses fixed alignment to layout the dialog. The functions BelowCopy and RightCopy help calculate the bounds next to the previous bounds. Sizing ElementSizing.FitToChildren tells a parent to automatically size itself so that it contains the bottom-right corner of the padding box for all of its children. Note that unlike the HTML flow layout, the VS layout does not prevent the children from overlapping. So the sizing algorithm is more or less sizing the parent to fit its largest child. GUI Basics In general, you can pretty much build your own GUI system if you want to, by just overriding OnRenderGUI and render whatever you like. There's a multitude of overridable methods to handle mouse and keyboard inputs as well, see also the GuiDialog class on Github If you want to use the vanilla GUI system, it's concept is merely a flat or hierarchical list of GUI elements placed at certain positions. The position is determined by an instance of ElementBounds . Let's have a closer look at it's properties: ElementBounds GuiComposer This is the component that builds and manages your GUI elements for you. You can create a composer via the client api: capi.Gui.CreateCompo(dialogName, bounds) . You have to supply it with a unique identifier and the overall dialog bounds. Once you have a GUIComposer instance you can chain-add elements. Dialog / Graphics .AddShadedDialogBG : Draws a pretty background and dialog border .AddDialogTitleBar : Draws a title bar with a close button and a button to move around the dialog .AddInset : Adds a darkened section with a inset border around it Text .AddStaticText : Add a static snippet of text .AddDynamicText : Add a snippet of text that can be set to other texts without the need to redraw the whole dialog .AddRichtext : Same as .AddDynamicText but allows use of VTML - a minimalist version of HTML code .AddHoverText : When the mouse cursor moves over the element boundaries, will show supplied text as a tooltip UI Control/Input .AddButton : Adds a clickable button .AddDropDown : Adds a drop down element .AddHorizontalTabs : Adds horizontally aligned tabs, like the ingame chat window has .AddVerticalScrollbar : Adds a vertical scrollbar .AddTextInput : Adds an single line editable text field .AddNumberInput : Adds an editable text field built for entering numbers .AddTextArea : Adds multiple line editable text field Tables/Grids/Inventory .AddItemSlotGrid : Create a grid displaying the contents of supplied inventory (example: GuiDialogCreatureContents ) .AddSkillItemGrid : Create a grid of custom elements (example: GuiDialogBlockEntityRecipeSelector ) Other .AddIf / .EndIf : Can be used to conditionally add certain elements (but you can also just split up your creation code for more fine grained control) .BeginClip / .EndClip : Used in combination with a scrollbar to cut away oversized content, such as in the creative inventory .AddStaticCustomDraw : Lets you define your own drawing code to be added to the GUI GuiElements To better show what is rendered by the GuiElements, the screenshots shown below were taken with dialog outlines enabled. GuiElementDialogBackground Draws a pretty background and dialog border. The background is drawn on both the padding and content boxes. Typically one uses ElementBounds.Fill (with padding optionally set) to set the background for the entire dialog. Although it is technically possible to use smaller bounds. The withTitleBar does not actually draw the title bar, but instead moves the top of the background box down GuiStyle.TitleBarHeight units to make room for the title bar. GuiComposer factory functions: .AddShadedDialogBG(ElementBounds bounds, bool withTitleBar = true, double strokeWidth = 5.0, float alpha = 0.75f) .AddDialogBG(ElementBounds bounds, bool withTitleBar = true, float alpha = 1f) AddDialogBG with a bounding box set to the entire dialog. The blank space at the top is due to the withTitleBar=true parameter. AddShadedDialogBG with a bounding box set to the entire dialog. The blank space at the top is due to the withTitleBar=true parameter. AddShadedDialogBG with a bounding box less than the entire dialog. In this case the blur from AddShadedDialogBG is offset incorrectly. AddDialogBG works correctly (not shown). GuiElementDialogTitleBar Draws a title bar with a close button and a button to move around the dialog. The drawBg option is ignored. GuiComposer factory functions: .AddDialogTitleBar(string text, Action onClose = null, CairoFont font = null, ElementBounds bounds = null) : The default null for the bounds will put the title bar in the correct place. The default title bar height is GuiStyle.TitleBarHeight . GuiElementInset Adds a darkened section with a inset border around it. GuiComposer factory functions: .AddInset(string text, ElementBounds bounds, int depth = 4, float brightness = 0.85f) Inset drawn on top of a background, with 10 units on all sides. Do not use padding in the inset bounds. Otherwise the rectangle for the darkened section is miscalculated and drawn outside of the embossed border. GuiElementStaticText Add a static snippet of text. GuiComposer factory functions: .AddStaticText(string text, CairoFont font, ElementBounds bounds, string key = null) .AddStaticText(string text, CairoFont font, EnumTextOrientation orientation, ElementBounds bounds, string key = null) .AddStaticTextAutoBoxSize(string text, CairoFont font, EnumTextOrientation orientation, ElementBounds bounds, string key = null) : adds text and resizes the bounding box such that the text fits on one line. Since the bounds are updated immediately, this may be useful for adding additional components to the right of the text. After calling AddStaticTextAutoBoxSize , one could use newbounds.FixedRightOf(textbounds) , newbounds.RightOf(textbounds) , or textbounds.RightCopy() . .AddStaticTextAutoFontSize(string text, string text, CairoFont font, ElementBounds bounds, string key = null) : attempts to shrink the font size such that the text fits in one line in bounds . The standard fonts can be obtained through static factory methods inside of CairoFont. These are commonly used: CairoFont.WhiteSmallText() CairoFont.WhiteDetailText() The text orientation would more accurately be called the text justification. It defaults to the font's justification, which is typically left. The options are: Left Right Center Justify : does the same thing as Left. The key option is used to find the static text later with GuiElementStaticText.GetStaticText(SingleComposer, key) . WhiteSmallText drawn on top of a dialog background, with 10 units of padding. AddStaticTextAutoBoxSize auto sized the text box such that the single line of text was clipped by the dialog bounds. AddStaticTextAutoFontSize attempted to shrink down the font so that it could fit on a single line. Example First iteration: basic static text private void SetupDialog () { // Auto-sized dialog at the center of the screen ElementBounds dialogBounds = ElementStdBounds . AutosizedMainDialog . WithAlignment ( EnumDialogArea . CenterMiddle ); // Just a simple 300x300 pixel box ElementBounds textBounds = ElementBounds . Fixed ( 0 , 0 , 300 , 300 ); SingleComposer = capi . Gui . CreateCompo ( "myAwesomeDialog" , dialogBounds ) . AddStaticText ( "This is a piece of text at the center of your screen - Enjoy!" , CairoFont . WhiteDetailText (), textBounds ) . Compose () ; } Some explanations: ElementStdBounds : Contains a bunch of often used element bounds configurations. See also ElementStdBounds on Github. ElementBounds.Fixed(0, 0, 300, 300) : Create a new bounds instance with fixedX/Y at 0/0 and a fixed widt/height of 300/300 pixels. CairoFont.WhiteDetailText() : Create a new font configuration based on a often used size and color, in this case white with font size 14 SingleComposer : This property is defined in the GuiDialog class. Its a getter/setter to the Composers dictionary. It contains all the "windows" of this dialog which are then rendered / handled by this GuiDialog instance Second iteration: add title bar This is of course the absolute minimum example that will only show some text. Let's add a title bar and a dialog background: private void SetupDialog () { // Auto-sized dialog at the center of the screen ElementBounds dialogBounds = ElementStdBounds . AutosizedMainDialog . WithAlignment ( EnumDialogArea . CenterMiddle ); // Just a simple 300x100 pixel box with 40 pixels top spacing for the title bar ElementBounds textBounds = ElementBounds . Fixed ( 0 , 40 , 300 , 100 ); // Background boundaries. Again, just make it fit it's child elements, then add the text as a child element ElementBounds bgBounds = ElementBounds . Fill . WithFixedPadding ( GuiStyle . ElementToDialogPadding ); bgBounds . BothSizing = ElementSizing . FitToChildren ; bgBounds . WithChildren ( textBounds ); SingleComposer = capi . Gui . CreateCompo ( "myAwesomeDialog" , dialogBounds ) . AddShadedDialogBG ( bgBounds ) . AddDialogTitleBar ( "Heck yeah!" , OnTitleBarCloseClicked ) . AddStaticText ( "This is a piece of text at the center of your screen - Enjoy!" , CairoFont . WhiteDetailText (), textBounds ) . Compose () ; } private void OnTitleBarCloseClicked () { TryClose (); } Final iteration: connect to hot key A simple standard dialog, triggered by the keyboard key 'U' public class GuiDialogAnnoyingText : GuiDialog { public override string ToggleKeyCombinationCode => "annoyingtextgui" ; public GuiDialogAnnoyingText ( ICoreClientAPI capi ) : base ( capi ) { SetupDialog (); } private void SetupDialog () { // Auto-sized dialog at the center of the screen ElementBounds dialogBounds = ElementStdBounds . AutosizedMainDialog . WithAlignment ( EnumDialogArea . CenterMiddle ); // Just a simple 300x300 pixel box ElementBounds textBounds = ElementBounds . Fixed ( 0 , 40 , 300 , 100 ); // Background boundaries. Again, just make it fit it's child elements, then add the text as a child element ElementBounds bgBounds = ElementBounds . Fill . WithFixedPadding ( GuiStyle . ElementToDialogPadding ); bgBounds . BothSizing = ElementSizing . FitToChildren ; bgBounds . WithChildren ( textBounds ); // Lastly, create the dialog SingleComposer = capi . Gui . CreateCompo ( "myAwesomeDialog" , dialogBounds ) . AddShadedDialogBG ( bgBounds ) . AddDialogTitleBar ( "Heck yeah!" , OnTitleBarCloseClicked ) . AddStaticText ( "This is a piece of text at the center of your screen - Enjoy!" , CairoFont . WhiteDetailText (), textBounds ) . Compose () ; } private void OnTitleBarCloseClicked () { TryClose (); } } public class AnnoyingTextSystem : ModSystem { ICoreClientAPI capi ; GuiDialog dialog ; public override bool ShouldLoad ( EnumAppSide forSide ) { return forSide == EnumAppSide . Client ; } public override void StartClientSide ( ICoreClientAPI api ) { base . StartClientSide ( api ); dialog = new GuiDialogAnnoyingText ( api ); capi = api ; capi . Input . RegisterHotKey ( "annoyingtextgui" , "Annoys you with annoyingly centered text" , GlKeys . U , HotkeyType . GUIOrOtherControls ); capi . Input . SetHotKeyHandler ( "annoyingtextgui" , ToggleGui ); } private bool ToggleGui ( KeyCombination comb ) { if ( dialog . IsOpened ()) dialog . TryClose (); else dialog . TryOpen (); return true ; } } Example - Scrolling A scrolling GUI requires: A clip area where the scrolling content will appear (BeginClip / EndClip) The scrollbar itself (AddVerticalScrollbar in the example below) A call to GuiElementScrollbar.SetHeights() after the GUI dialog has been composed Here is a straightforward example of a scrolling GUI. public class DemoScrollingGui : GuiDialog { public override string ToggleKeyCombinationCode => "demoscrollgui" ; public DemoScrollingGui ( ICoreClientAPI capi ) : base ( capi ) { SetupDialog (); } private void SetupDialog () { int insetWidth = 900 ; int insetHeight = 300 ; int insetDepth = 3 ; int rowHeight = 35 ; int rowCount = 40 ; // Auto-sized dialog at the center of the screen ElementBounds dialogBounds = ElementStdBounds . AutosizedMainDialog . WithAlignment ( EnumDialogArea . CenterMiddle ); // Bounds of main inset for scrolling content in the GUI ElementBounds insetBounds = ElementBounds . Fixed ( 0 , GuiStyle . TitleBarHeight , insetWidth , insetHeight ); ElementBounds scrollbarBounds = insetBounds . RightCopy (). WithFixedWidth ( 20 ); // Create child elements bounds for within the inset ElementBounds clipBounds = insetBounds . ForkContainingChild ( GuiStyle . HalfPadding , GuiStyle . HalfPadding , GuiStyle . HalfPadding , GuiStyle . HalfPadding ); ElementBounds containerBounds = insetBounds . ForkContainingChild ( GuiStyle . HalfPadding , GuiStyle . HalfPadding , GuiStyle . HalfPadding , GuiStyle . HalfPadding ); ElementBounds containerRowBounds = ElementBounds . Fixed ( 0 , 0 , insetWidth , rowHeight ); // Dialog background bounds ElementBounds bgBounds = ElementBounds . Fill . WithFixedPadding ( GuiStyle . ElementToDialogPadding ) . WithSizing ( ElementSizing . FitToChildren ) . WithChildren ( insetBounds , scrollbarBounds ); // Create the dialog SingleComposer = capi . Gui . CreateCompo ( "demoScrollGui" , dialogBounds ) . AddShadedDialogBG ( bgBounds ) . AddDialogTitleBar ( "Scroll Me!" , OnTitleBarCloseClicked ) . BeginChildElements () . AddInset ( insetBounds , insetDepth ) . BeginClip ( clipBounds ) . AddContainer ( containerBounds , "scroll-content" ) . EndClip () . AddVerticalScrollbar ( OnNewScrollbarValue , scrollbarBounds , "scrollbar" ) . EndChildElements (); // Add desired scrollable content to the container GuiElementContainer scrollArea = SingleComposer . GetContainer ( "scroll-content" ); for ( int i = 0 ; i < rowCount ; i ++) { scrollArea . Add ( new GuiElementStaticText ( capi , $"- Example Row {i+1} -" , EnumTextOrientation . Center , containerRowBounds , CairoFont . WhiteSmallishText ())); containerRowBounds = containerRowBounds . BelowCopy (); } // Compose the dialog SingleComposer . Compose (); // After composing dialog, need to set the scrolling area heights to enable scroll behavior float scrollVisibleHeight = ( float ) clipBounds . fixedHeight ; float scrollTotalHeight = rowHeight * rowCount ; SingleComposer . GetScrollbar ( "scrollbar" ). SetHeights ( scrollVisibleHeight , scrollTotalHeight ); } private void OnNewScrollbarValue ( float value ) { ElementBounds bounds = SingleComposer . GetContainer ( "scroll-content" ). Bounds ; bounds . fixedY = 5 - value ; bounds . CalcWorldBounds (); } private void OnTitleBarCloseClicked () { TryClose (); } } Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Getting_Started TITLE: Modding:Getting Started --- CONTENT --- Other languages: English español français русский Vintage story has an extensive modding system built into the game, allowing any user to make their own changes and additions as they see fit. Contents 1 What Mods can I make? 2 Types of Mods 3 Moving Forward Before you get started, it's important to understand what you would like to accomplish so you can know what to expect. What Mods can I make? The current modding system for Vintage Story is incredibly flexible and quite easy to use even if you don't know how to program. You can add fully functional blocks , items and even entities into the game without ever opening an IDE . More complex changes and systems can be made using C# programming. This is because nearly every feature of the game relies on the usage of JSONs , which are text documents that utilize a flexible, easy to read format. You can open these files with any text editor, and with just a little bit of practice you can learn how to format them to avoid errors. The best way to learn this is to study the existing JSONs that are visible to anybody who has the game installed. To do so you'll need to locate the Vintage Story Assets folder, which you can learn about at the Asset System page. In addition, here is a 12 minute video that will teach you why json is used, and the basic syntax, making it easier to understand the rest of the documentation. If you're looking to add more complex systems to Vintage story then you can accomplish this as well, but you likely won't be able to do this with JSONs alone. Vintage Story is written in C#, and it is highly suggested you learn the basics of programming and of C# in general before moving onto more complex mods that can't be accomplished with just JSONs. Types of Mods In general there are 3 main types of mods one can create for Vintage Story, with each having different levels of complexity: Theme Packs : These are mods that only affect visuals and don't add more content or change features significantly. Content Mods : These are mods that add additional content (i.e. blocks, items, mobs) to the game but don't utilize C# code and are mostly limited to JSONs. Code Mods : These are mods that add more complex features and systems that require the use of C# code to accomplish. Moving Forward If you're just starting out, it's best to begin with a content mod, as most code mods simply extend the features of blocks, items and entities made from a content mod anyway. Once you've mastered content mods you can move onto coding if you want to add some really special things to your Vintage Story experience. More advanced programmers may be tempted to jump to code mods immediately, but it is still highly suggested you familiarize yourself with content mods first since you'll almost certainly be using them in tandem with any C# code you write. The first thing to do while starting your first mod is to familiarize yourself with the Asset System . This is where most of the magic in Vintage Story is accomplished, and you'll be using these resources constantly while modding. To move on with a content mod, head over to the Content Mods page. To start with a theme pack, head over to the Theme Pack page. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Using the Modding Wiki • Getting Started • Types of Mod • Other Resources --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Grid_Recipes_Guide TITLE: Modding:Grid Recipes Guide --- CONTENT --- Other languages: English español русский This is a guide of available grid recipe features. For a follow-along tutorial, please see the simple recipes tutorial and the complex recipes tutorial . Contents 1 Advanced 1.1 Type based recipes 1.2 Converting an ingredient to another item upon crafting 1.3 Consuming more than one durability per tool 1.4 Separating recipes on the same handbook page 1.5 Restricting to a specific class 1.6 Copying attributes 1.7 Hiding recipes from 'Created by' section from handbook 1.8 Using liquid container as ingredient Advanced Type based recipes There are more complicated things you can do with recipes. This the recipe for wooden planks which are crafted out of logs: { "ingredientPattern" : "L" , "ingredients" : { "L" : { "type" : "block" , "code" : "game:log-*-ud" , "name" : "wood" } }, "width" : 1 , "height" : 1 , "output" : { "type" : "block" , "code" : "planks-{wood}-hor" , "quantity" : 4 } } Instead of having a recipe for every wood type, you can assign a name to an ingredient (in this case it is "name": "wood" ) and everything identified by * will later on replaced be for the output. Meaning {wood} will be replaced by the type of the giving log. For example if we would have a birch log block, its code would be log-birch-ud , so * would stand for birch , therefore the output will be converted from "code": "planks-{wood}-hor" to "code": "planks-birch-hor" . Converting an ingredient to another item upon crafting Sometimes you want to keep one or more of the ingredients, but convert them to a different item after crafting. For example, when crafting a honey-sulfur poultice, the player needs a bowl filled with honey, but the bowl is not consumed to craft it. Instead the bowl of honey is turned into an empty bowl. This is accomplished by adding the returnedStack property to the ingredient. This property's value needs to contain a type and code just like the standard ingredient properties. This tells the recipe which item to give the player back. Continuing with the honey-sulfur poultice example, a bowl of honey as an ingredient looks like "B": { "type": "block", "code": "bowl-honey" } , but the player would lose the bowl if the recipe were written this way. We need to add returnedStack to the ingredient's properties and indicate which item to replace it with. In this case, the player should receive an empty bowl in place of the bowl of honey "returnedStack": { "type": "block", "code": "bowl-burned" } . This property is placed alongside the type and code properties of an ingredient. Putting it all together: { "ingredientPattern" : "SBS,_L_" , "ingredients" : { "L" : { "type" : "block" , "code" : "linen-*" }, "S" : { "type" : "item" , "code" : "powderedsulfur" }, "B" : { "type" : "block" , "code" : "bowl-honey" , "returnedStack" : { "type" : "block" , "code" : "bowl-burned" } } }, "width" : 3 , "height" : 2 , "output" : { "type" : "item" , "code" : "poultice-linen-honey-sulfur" , "quantity" : 4 } } Consuming more than one durability per tool To balance durability consuming, it can be done by adding toolDurabilityCost and isTool . This is recipe for pulverizer pounder: { "ingredientPattern" : "HL_,CL_,_L_" , "ingredients" : { "H" : { "type" : "item" , "code" : "hammer-*" , isTool : true , t oolDurabili t yCos t : 10 }, "C" : { "type" : "item" , "code" : "chisel-*" , isTool : true , t oolDurabili t yCos t : 10 }, "L" : { "type" : "block" , "code" : "log-placed-*-ud" , "name" : "wood" } }, "width" : 3 , "height" : 3 , "output" : { "type" : "item" , "code" : "pounder-oak" , "quantity" : 1 } } Separating recipes on the same handbook page Sometimes amount of recipes for one item can become overwhelming, to separate important ones, it can be done by adding recipeGroup . These are recipes for wooden ladder: { "ingredientPattern" : "S_S,SSS,S_S" , "ingredients" : { "S" : { "type" : "item" , "code" : "stick" } }, "width" : 3 , "height" : 3 , "recipeGroup" : 1 , "output" : { "type" : "block" , "code" : "ladder-wood-north" , "quantity" : 3 } }, { "ingredientPattern" : "P_P,PSP,P_P" , "ingredients" : { "P" : { "type" : "item" , "code" : "plank-*" }, "S" : { "type" : "item" , "code" : "stick" } }, "width" : 3 , "height" : 3 , "output" : { "type" : "block" , "code" : "ladder-wood-north" , "quantity" : 3 } } Restricting to a specific class The recipe can be limited to a specific class . This can be done by adding requiresTrait . This is recipe for sewing kit: { "ingredientPattern" : "FFS,FF_" , "requiresTrait" : "clothier" , "ingredients" : { "F" : { "type" : "item" , "code" : "flaxtwine" }, "S" : { "type" : "item" , "code" : "stick" } }, "width" : 3 , "height" : 2 , "output" : { "type" : "item" , "code" : "sewingkit" } } Copying attributes Some recipes can require to copy attributes. This is can be done by adding copyAttributesFrom . This is recipe for labeled crate: { "ingredientPattern" : "S,C" , "ingredients" : { "S" : { "type" : "item" , "code" : "paper-parchment" }, "C" : { "type" : "block" , "code" : "crate" } }, "shapeless" : true , "copyAttributesFrom" : 'C' , "width" : 1 , "height" : 2 , "output" : { "type" : "block" , "code" : "crate" , "attributes" : { "label" : "paper-empty" } } } Hiding recipes from 'Created by' section from handbook Some recipes are better hidden, it can be done by adding showInCreatedBy . { "ingredientPattern" : "H" , "ingredients" : { "H" : { "type" : "block" , "code" : "hay-normal-ud" } }, "showInCreatedBy" : false , "width" : 1 , "height" : 1 , "output" : { "type" : "item" , "code" : "drygrass" , "quantity" : 8 } } Using liquid container as ingredient Some recipes use liquid containers, such as buckets, bowls or jugs. For single liquid container, it can be done by adding liquidContainerProps to recipe attributes. This is recipe for honey-sulfur poultice: { "ingredientPattern" : "SBS,_L_" , "ingredients" : { "L" : { "type" : "block" , "code" : "linen-*" }, "S" : { "type" : "item" , "code" : "powderedsulfur" }, "B" : { "type" : "block" , "code" : "bowl-fired" } }, "attributes" : { "liquidContainerProps" : { "requiresContent" : { "type" : "item" , "code" : "honeyportion" }, "requiresLitres" : 0.25 } }, "width" : 3 , "height" : 2 , "output" : { "type" : "item" , "code" : "poultice-linen-honey-sulfur" , "quantity" : 4 } } The above liquidContainerProps are stored in the grid attributes, which are shared for all containers in the recipe. This means that it cannot be used for recipes that need multiple liquids. Note that there is some code in BlockLiquidContainerBase to read the liquidContainerProps from the per-ingredient recipe attributes instead of the grid attributes, but the code is incomplete. Specifically, as of version 1.19.8, BlockLiquidContainerBase.OnHandbookRecipeRender still directly reads from the grid attributes. Also the ucontents cannot be used as an alternative, because the real blocks have a contents attribute instead of ucontents attribute. ucontents is not converted to contents before the recipe matching. So the recipe fails to match because the candidate liquid container has a contents attribute instead of a ucontents attribute. Also the contents cannot be directly entered in the json, because it has the ItemstackAttribute type, which is not parsed from json. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Inbuilt_ModMaker TITLE: Modding:Inbuilt ModMaker --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.20.4 . Contents 1 Create Patches (Option 1) 1.1 Downloading Vanilla Assets 1.2 Checking Shapes Folder 1.3 Analysing Changes 1.4 Mod Info 1.5 Packaging Your Mod 2 Convert Vanilla Assets to Standard JSON (Option 2) 3 Restore Vanilla Assets (Option 3) 4 Delete Temp Files (Option 4) 5 (Experimental) Create Temp Folder from Local Install (Option 5) The mod maker program is an essential modding tool included with a Vintage Story installation. To access it, navigate to your install location for the game, and open the 'ModMaker.exe' program. The mod maker currently has 5 options to choose from, however its primary use is to create patches for vanilla assets. It is recommended to have some knowledge of content mods before using the modmaker. Create Patches (Option 1) As you may be aware, much of Vintage Story is written using JSON files that are immediately accessible from installing the game. Due to this, you can make significant changes to the game just by modifying these assets. You can also directly add new JSON files to the folders that you desire to create new content. These modifications are not saved between versions, and cannot be used in multiplayer. To save your changes into a publishable mod, you can use the modmaker program. Run the ModMaker.exe program, and you're given a list of options. Select the first option by typing 1, and then pressing enter. The program will give some information, and then ask you what version you would like to use. In almost all circumstances, this version is automatically detected, and you can just press enter to continue. Downloading Vanilla Assets If you have not run the mod maker before for this version, it will now download the appropriate original assets to compare against. To do this, you will need an internet connection. Please note that if you are using a pre-release or unstable Vintage Story version, this step cannot be completed without creating a local install temp folder. See the "Create Temp Folder from Local install" step below. Checking Shapes Folder After successfully downloading the vanilla assets, the mod maker will ask if you would like to ignore the shapes folder. The shapes folder contains 55% of assets, and can take a considerable amount of time to process these. Considering that most mods do not add new shapes, it can save time by ignoring this. If you haven't made any changes to the shapes folder, enter 'y' and press enter to ignore shapes. Or, enter 'n' and press enter to look for differences in the shapes folders. Note that the mod maker will not check any non-JSON assets. This includes textures, sound files, and music files. Analysing Changes The mod maker will now begin searching for differences in the files. It analyses differences in JSON properties; so minor changes (such as additional whitespace) are ignored. While doing so, the program will inform you on the different files it notices, as well as the number of files it has analysed. Mod Info After the files have been analysed, if any changes have been found, you will be asked for some info about your mod. Mod Name - Your mod name can be pretty much anything, though it should be readable and give a good idea about what your mod does. This can include spaces, punctuation, and capital letters. Mod ID - This needs to be a unique identifier for your mod. Your mod ID should be similar to your mod name. In general, anyone who reads your mod ID should know what mod it is associated with. It needs to be all lowercase (numbers are allowed), however it cannot contain uppercase letters or punctuation. Author Name - Whatever name you wish to go by. This is just for display purposes. Packaging Your Mod When the mod info values have been entered, your mod will now be created! The program will let you know where your mod exists, which will usually be in the VintageStoryData folder. The packaged mod will be a zip file of a content mod. From here, you can either upload your mod to the Vintage Story ModDB , or unzip your mod and make further edits to it. If your mod is now complete, it may be a good idea to restore the vanilla assets. See option 3 for how to do this. Note that if you do not restore the vanilla assets, and your packaged mod is enabled, the changes you have made will actually be applied twice, which can cause unexpected behavior. Convert Vanilla Assets to Standard JSON (Option 2) Vintage Story loads all assets using JSON5 . JSON5 is significantly more leniant with formatting when compared to standard JSON, however it can often cause issues with some IDEs or text editors. To allow editing to be easier, the mod maker can convert all the vanilla assets from JSON5, into JSON. This process may take a short while, but the program will inform you on how many files have been processed. Note that this will increase the size of the assets folder from ~670MB to ~850MB, as of 1.20.3. This can cause a small increase in loading speeds, however it causes no difference in functionality to any mods. JSON5 JSON { ... crea t ivei n ve nt ory : { "general" : [ "*" ], "items" : [ "*" ] }, heldTpIdleA n ima t io n : "holdunderarm" , maxs ta cksize : 1 , combus t ibleProps : { bur n Tempera ture : 600 , bur n Dura t io n : 10 , }, ... } { ... "creativeinventory" : { "general" : [ "*" ], "items" : [ "*" ] }, "heldTpIdleAnimation" : "holdunderarm" , "maxstacksize" : 1 , "combustibleProps" : { "burnTemperature" : 600 , "burnDuration" : 10 }, ... } Restore Vanilla Assets (Option 3) After editing the assets folder, it can be important to revert the assets back to their original form. This can help reduce bugs when testing mods, and gives you a clean slate for a new modding environment. This process will delete all changes made to the assets, including new files, therefore it is recommended to backup your assets folder to not lose any changes! Any non-json assets will not be reverted, including textures, music, and sounds. Please note that if you are using a pre-release or unstable Vintage Story version, this step cannot be completed without creating a local install temp folder. See the "Create Temp Folder from Local install" step below. When prompted for a version, enter the appropriate version and press enter. If you have run option 2 beforehand, you will need to redo that process to change your assets to standard JSON. Delete Temp Files (Option 4) The program has been made more robust in recent versions, however it can still sometimes have issues. If you are having any issues with the game not finding files, or detecting files as being changed which have not been, then your first step should be to delete the temp files. This will force the program to redownload the vanilla assets, and hopefully solve any issues. (Experimental) Create Temp Folder from Local Install (Option 5) This option is required if you wish to use the mod maker with pre-release or other unstable versions. This allows you to create an assets folder based on a local installation of the game. To ensure it works correctly, you need to run this step before you modify any assets . If you have modified any assets already, you will need to create a clean install of the game, and repeat this step. The program will ask for confirmation before completing this step. When it is complete, you will be able to use the mod maker with unstable versions of the game. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Using the Modding Wiki • Getting Started • Types of Mod • Other Resources "" Community Resources • Programming Languages • Inbuilt ModMaker AnegoStudios Mod Examples • Nat's Mod Examples • Code API Docs • JSON/Content Docs • AnegoStudios GitHub --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:InheritFrom TITLE: Modding:InheritFrom --- CONTENT --- This page was last verified for Vintage Story version 1.20.0-pre.13 . 1.20.0 added the inheritFrom property for json files. This allows one json file to inherit the properties from another json file. Similar to json patching, the inheritFrom property should point to the json file that the current asset should inherit from. It may be prefixed with a game domain. It is currently only used by "roughhewnfence-fsnow.json" in the base game. Here is how it inherits from the "assets/survival/blocktypes/wood/woodtyped/roughhewnfence-free.json" file. { i n heri t From : "blocktypes/wood/woodtyped/roughhewnfence-free" , ... } The inheritFrom property is supported on items, entities, and blocks. Recursive inheritance is supported (a file can inherit from a parent, which itself inherits from a grandparent). Contents 1 Merging 1.1 No child override 1.2 Child specifies new array value 1.3 Child specifies new dictionary 2 Limitations Merging The child file's json is merged with the parent file's json. If they both contain a dictionary property, then the entries in the dictionary are merged. Otherwise if they both contain the same integer/boolean/string/array property, then the child property replaces the parent property. The following examples come from "roughhewnfence-fsnow.json" (the child), which inherits from "roughhewnfence-free.json" (the parent). No child override The parent has the class property. class : "BlockFence" , The child does not specify the class property. So the child inherits this field from the parent, for a result of: class : "BlockFence" , Child specifies new array value The parent has this array value for the variantgroups property. varia nt groups : [ { code : "wood" , s tates : [ "aged" ], loadFromProper t ies : "block/wood" }, { code : "type" , s tates : [ "empty" , "n" , "e" , "s" , "w" , "ne" , "ns" , "nw" , "es" , "ew" , "sw" , "nes" , "new" , "nsw" , "esw" , "nesw" ] }, { code : "cover" , s tates : [ "free" ] } ], The child has this replacement value: varia nt groups : [ { code : "wood" , s tates : [ "aged" ], loadFromProper t ies : "block/wood" }, { code : "type" , s tates : [ "empty" , "n" , "e" , "s" , "w" , "ne" , "ns" , "nw" , "es" , "ew" , "sw" , "nes" , "new" , "nsw" , "esw" , "nesw" ] }, { code : "cover" , s tates : [ "snow" ] } ], Because arrays are replaced, the final value is the child's override. The parent's values are not merged in. varia nt groups : [ { code : "wood" , s tates : [ "aged" ], loadFromProper t ies : "block/wood" }, { code : "type" , s tates : [ "empty" , "n" , "e" , "s" , "w" , "ne" , "ns" , "nw" , "es" , "ew" , "sw" , "nes" , "new" , "nsw" , "esw" , "nesw" ] }, { code : "cover" , s tates : [ "snow" ] } ], Child specifies new dictionary The parent has this array value for the creativeinventory property. crea t ivei n ve nt ory : { "general" : [ "roughhewnfence-*-ew-free" ], "decorative" : [ "roughhewnfence-*-ew-free" ] }, The child attempts to override it (as of 1.20.0-pre.13). crea t ivei n ve nt ory : { }, However, creativeinventory is a dictionary. So the dictionaries are merged together. The child's override ends up doing nothing. This is the merged result: crea t ivei n ve nt ory : { "general" : [ "roughhewnfence-*-ew-free" ], "decorative" : [ "roughhewnfence-*-ew-free" ] }, The only reason the "roughhewnfence-*-*-snow" blocks do not show up in the creative inventory (what the creativeinventory controls) is because the parent's wildcard does not match the child blocks. Limitations The inheritFrom processing is purely done at the JSON level. Notably it is done before the variant processing . So a JSON file can only inherit from another JSON file. It cannot inherit one registry object variant from a file. For example, "roughhewnfence-fsnow.json" cannot inherit from just the "game:roughhewnfence-aged-ew-free" block, but it does inherit from the "roughhewnfence-free.json" file that defines that variant. When inheriting from a file in a different domain (for example a mod that inherits from a vanilla JSON file), for any asset properties, the domain defaults to the child's domain, even when parsing fields inherited from the parent file. For example, let's say "customfence.json" in "mymod" inherits from "roughhewnfence-free.json". Notice that the "game:" prefix is necessary to find the JSON file, because the default domain in "mymod:blocktypes/customfence.json" is "mymod". { i n heri t From : "game:blocktypes/wood/woodtyped/roughhewnfence-free" , } "roughhewnfence-free.json" points to the shape file "block/wood/fence/roughfence/empty". When the parent parses this property, the domain defaults to "game". So it correctly finds the "game:block/clutter/fence/roughfence/empty.json" shape file ("assets/survival/shapes/block/clutter/fence/roughfence/empty.json" in the game folder). shapeby t ype : { "*-empty-free" : { base : "block/wood/fence/roughfence/empty" }, ... } However, when "customfence.json" tries to parse this, it instead tries to find "mymod:block/clutter/fence/roughfence/empty.json", which probably does not exist, and the mod's block has a red question mark. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Intermediate_Code_Tutorials TITLE: Modding:Intermediate Code Tutorials --- CONTENT --- Other languages: English русский This is the landing page for the intermediate code tutorials. These will cover some more specific aspects to code modding. Intermediate Tutorials These tutorials are currently in progress! Please check back later! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Nothing here yet! --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Intermediate_Content_Tutorials TITLE: Modding:Intermediate Content Tutorials --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . This is the landing page for the intermediate content tutorials. These will cover topics that are somewhat more complex, but will help you mod effectively and efficiently. At this point, it is also a good idea to learn about the various modding concepts for content modding, especially the explanation of variants . It is also a good idea to be aware of the Debugging Content page. This will contain a list of common issues when adding content, as well as methods on how to access and read the game's log files. Intermediate Tutorials Item Variants Block Variants Complex Grid Recipes Further Recipes Simple World Generation Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Item Variants • Block Variants • Complex Grid Recipes • Further Recipes • Simple World Generation --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Item_Json_Properties TITLE: Modding:Item Json Properties --- CONTENT --- Other languages: English русский This page is outdated. Reason: All item json properties can now be viewed on the JSON Reference The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Overview This is a mostly-complete list of available properties for items. To see an exhaustive list of properties, check out the ItemType and CollectibleType GitHub pages. Any value immediately underneath a [JsonProperty] label can be used within an itemtype file. Definitions: Key - The name of the property, which should be used as it appears in the column. Value - A component of a property. Array - A list of objects or values that can be referenced in code. String - A sequence of characters that can be used as an identifier in code. Essentially a word that can be referenced and assigned to something. Generally does not use numbers. Boolean - A true or false value, essentially "on" or "off". Int - An integer, or whole number. Cannot use decimal values. Float - A decimal, specifically one that does not exceed more than 1 significant digit Object - This is a bit more complex, but essentially objects are the items, blocks and entities that can be interacted with. In most cases, when an "object" type appears it means you must use a specific item variant code, which follows the "itemcode-variant_1-variant_2-variant_n" style of naming. Property Type Default Usage Reference json Core (no byType available) code string required A unique identifier for the item. All Items A domain prefix will be added dynamically depending on the location of the file. Every mod and VintageStory itself have a unique prefix. For example the code stone turns into game:stone . To refer to items outside your mod (IE with item patches) you would use the following format: yourmod:youritem The code identifier has to be unique inside its domain. In theory there could be equal identifiers with different domain prefixes. Find out more about Domains . enabled boolean true If the item will be loaded or not. Can be used to temporarily remove the item. - variantgroups array of objects - Allows you define multiple variants of the same item. armor, ore-graded, plank, (any) bytype key: string; value: object - Allows different property values to be specified based on the variant code. allowedVariants array of objects - Used to trim unnecessary items generated by combined variants. crystalizedore-graded, ore-graded, ore-ungraded skipVariants array of object - Similar to allowedVariants, but instead skips the creation of listed variants rather than assigning which are allowed. armor Specific class string "item" The class an item should use if it has additional C# functionalities that can't be accomplished with JSONS. axe-metal, hoe, spear It can be used to open guis or adding other extra functionality to the item. A complete tutorial of how to add your own class to the game can be found here . behaviors array of object - A behavior adds custom abilities such as ground storable. ingot, axe To see all of the current behaviors in the game see All Collectible Behaviors . durability integer 0 The maximum uses of the item. Items that reach 0 uses disappear. NOTE: The actual current durability of the item is stored as a treeAttribute, this is only a max value. pickaxe damagedby array of string - From which damage sources does the item takes durability damage. pickaxe blockbreaking 0 Mining a block. pickaxe attacking 1 Hitting an entity. sword fire 2 Currently not used. - tool string - Classifies the item as the given tool, which gives it the ability to harvest resources from certain blocks. axe-metal, pickaxe, knife, scythe, shovel Knife 0 Can harvest items from grass. knife, scythe Pickaxe 1 Can mine rock and other stone materials pickaxe Axe 2 Can chop down trees and other wood materials. axe-metal, axe-stone Sword 3 No special abilities yet. sword Shovel 4 Can dig soil, gravel and sand quickly. shovel Hammer 5 No special abilities yet. hammer Mallet 6 No special abilities yet. - Spear 7 No special abilities yet. spear Bow 8 No special abilities yet. bow Sickle 9 No special abilities yet. - Hoe 10 No special abilities yet. hoe Saw 11 No special abilities yet. saw Attributes attributes - - Custom Attributes associated with this item. armor, ingot Attributes constitute a large number of custom properties that an item can have, many of which are specific to unique items that rely on a C# class for additional functionality. If you wish to add your own JSON attributes to an item generally you must also have a class to utilize them. Values placed here are final and cannot be modified. For example, if you made a new type of weapon, say a mace that had additional weapon properties you could define them here: a ttr ibu tes : { "armorDamage" : 10 , "stunChance" : 0.1 , }, Here we have made two new attributes called "armorDamage" and "stunChance" and have given them their own static values to be used in code. As is, these cannot do anything without the usage of C# code in a class. Regardless, we can see that this is a convenient way to create extra properties that can be manipulated using variant combinations if desired. (Container Attributes) Attributes that change how items interact with containers. barrelMovetoLiquidSlot boolean - If set to true, converts an item placed into a barrel to a "liquid" state that can be used for barrel recipes (EX: how salt is used to cure meat). salt crockable boolean - If set to true, can be placed into a crock from another container (Keep in mind that doing so also turns the item into a meal, which cannot be undone). pickledvegetable displaycaseable boolean - If set to true, allows an item to be placed into a display case. ingot, ore-graded, ore-ungraded, stone forgable boolean - A bit of a misnomer, this indicates that an item can be placed into and heated in a forge, not that it can or can't be forged on an anvil. ironbloom inContainerTexture Path to texture - Pathway to the texture shown when an item is placed into a barrel. This can include a liquid or solids. waterportion, salt inFirePitProps - - Gives an item additional rendering properties when placed in a fire pit. redmeat, poultry transform - - If the model type shows the item, it can be transformed using this property. redmeat, poultry useFirepitModel - - Tell the firepit which model to use when this item is placed into it. redmeat, poultry Here's an example of how the firepit transformation is used by the different meat items: i n FirePi t Props : { transf orm : { scale : 0.85 , origi n : { x : 0.5 , y : 0.0625 , z : 0.5 }, translat io n : { x : -0.03125 , y : 0.0625 , z : 0.046875 }, ro tat io n : { x : 0 , y : 0 , z : 90 } }, useFirepi t Model : "Spit" }, If you're familiar with the other transformation code, this is nothing unusual and works on the same principles by changing the scale, position and rotation of the model with respect to the origin of rotation. At the moment spit is the only model used with single food items. The other option wide is used with the cooking pot. shelvable boolean - If true, allows the item to be placed on shelves. redmeat, stone rackable boolean - If true, allows the item to be placed on a tool rack. To look right the item also requires additional transformations using the toolrackTransform property (see rendering section). pickaxe, scythe, sword waterTightContainerProps - - This contains all the liquid properties of an item, generally determining how it is stored and used with a bucket. waterportion, limewaterportion containable boolean - If true, the liquid can be placed into liquid containers, such as barrels and buckets. waterportion, limewaterportion itemsPerLitre number (int) - The number of itemstack items required to make a litre of liquid. Generally this value is 1:1, but concentrated items like honey can be 4:1. waterportion, honeyportion texture Path to Texture - A "block" texture given to a liquid when it's rendered in containers or other items. waterportion, honeyportion allowSpill boolean - If true, allows the player to use the Ctr + Right Click function to "spill" the liquid from a container. The whenSpilled property determines what happens if this is true. waterportion, limewater tintIndex integer 0 Tints the color of the item if it's ever drawn as a block: 0 for no tint, 1 for plant climate tint, 2 for water climate tint. - waterportion, limewater whenSpilled - - Determines what happens when the "spill" interaction is used. Only works if the allowSpill property is set to true. waterportion, limewater action string - Code identifier that determines what happens when the liquid is spilled from a container. waterportion, limewater PlaceBlock - - Places a block at the spilled location. waterportion DropContents - - Drops an item at the location. If the item is a liquid it will disappear immediately with a "splash" particle effect. limeportion stack - - The block or item dropped when spilled. if the "PlaceBlock" action is chosen a block is placed, if "DropContents" is used an item is generated. waterportion, limewater stackByFillLevel - - Allows for different blocks or items to be placed based on the level of the liquid in the container. waterportion, limewater We'll look at an example of how this property is utilized by water item: wa ter Tigh t Co nta i ner Props : { co nta i na ble : true , i te msPerLi tre : 1 , te x ture : { base : "block/liquid/waterportion" }, t i nt I n dex : 2 , whe n Spilled : { ac t io n : "PlaceBlock" , s ta ck : { t ype : "block" , code : "water-still-3" }, s ta ckByFillLevel : { "10" : { t ype : "block" , code : "water-still-7" } } } } Most is self explanatory, but we can see that it has a some unique features, such as allowing a player to generate a water block when the "fill level" of the container used to spill the fluid. We can also see that the texture for it has a tintIndex of 2, which heightens the blue coloration of the water texture while in a bucket. (Equipment Attributes) Attributes primarily used with equipment and armor. attachShape Path to Shape - Assigns a shape to a bag to be shown on the player when worn in a bag slot. backpack backpack - - Gives an item bag properties, allowing it to be placed in the bag slot to increase inventory space. backpack quantitySlots number - How many additional slots the bag provides. backpack storageFlags number (storage flag ID) - Limits what type of item can be placed into the bag by assigning it a storage flag. (Need a list of storage flags) miningbag slotBgColor Color value (HEX) - Changes the background color of the additional slots provided by the bag. Requires a HEX color code, which you can find with any generic color picker miningbag clothescategory string - Assigns a wearable item to a specific inventory slot. There are 15 slots in total, including 3 armor slots and 12 decorative clothing slots. armor, upperbody, lowerbody Armor slots: armorhead, armorbody, armorlegs . Clothing slots: head, face, neck emblem, upperbodyover, upperbody, shoulder, arm, hand, lowerbody, waist, foot . defaultProtLoss - - Defines the default protection loss for incoming damage that is of a higher tier than the armor. Requires the usage of perTierRelativeProtectionLoss and perTierFlatDamageReductionLoss properties. armor disableElements Shape Element - Allows shape elements to be disabled to make better looking armor, for example removing the Seraph's ponytail with helmets. armor (plate helmet) footStepSound path to ogg file - What sound to play while the player is wearing this item. armor inCharactercreationDialog boolean - If set to true, will allow the player to select the wearable item during the character creation sequence. Lowerbody, Upperbody protectionModifiers - - Assigns protection values to an armor piece. armor protectionTier number - The value contested against the tier of incoming damage. If the damage tier is higher the effectiveness of the armor is reduced, potentially to zero if the difference is high enough. armor highDamageTierResistant boolean false If set to true this armor will reduce the protection loss of higher damage tiers by half when looping through the protection loss sequence of the damage calculation. armor flatDamageReduction number, decimal - This is the amount of damage that is initially subtracted before relative protection is applied. armor relativeProtection number, decimal - After the damage is subtracted by the flat reduction the remaining damage is reduced by a percent amount equal to this value. armor perTierFlatDamageReductionLoss array of numbers - Determines how much flat damage reduction is lost by a damage tier higher than the armor tier. Each relative difference can be customized individually. armor perTierRelativeProtectionLoss array of numbers - Determines how much relative protection is lost by a damage tier higher than the armor tier. The first value is used for high damage resistant armor, the second is for all other armors. armor The system for how these values are used is a bit complex, but here's essentially how it works: The tier of incoming damage is not just a contest of values, but is actually looped through multiple times depending on the tier value to reduce the effectiveness of an armor. For example, tier 4 damage will loop four times and reduces the armor effectiveness each loop, while tier 1 damage will loop only once. The looped value starts as 1 and as the value increases up to the damage tier it is compared to the tier of the armor. If this increasing value is greater than the armor tier the second second value is used in the perTier function, otherwise the first value is used. Additionally, if the armor has the highDamageTierResistant property set to true then every loop which is higher than the armor will have it's armor reduction halved. Lets look at an example of this using an armor with the following stats: pro te c t io n Modi f iers { pro te c t io n Tier : 2 , flat DamageReduc t io n : 1.0 , rela t ivePro te c t io n : 0.80 , perTierFla t DamageReduc t io n Loss : [ 0.03 , 0.15 ], perTierRela t ivePro te c t io n Loss : [ 0.1 , 0.2 ], highDamageTierResis tant = true }, Now lets loop through the protection values and see what the overall loss is. The damage tier is 4, so we start with 1 and end with 4: Round 1 : Damage tier = 1, armor tier = 2 - Damage tier is below the armor tier in the loop, so we use the first values (0.03 and 0.1) Round 2 : Damage Tier = 2, armor tier = 2 - Damage tier is still not higher in the loop, so once again we use the first values (0.03 and 0.1) Round 3 : Damage Tier = 3, armor tier = 2 - At last the damage tier is higher than the armor tier in the loop so we use the second values (0.15 and 0.2), but we also have the highDamageTierResistant property, so both these values are halved, resulting in (0.075 and 0.1) Round 4 : Damage tier = 4, armor tier = 2 - Same as before, resulting in (0.075 and 0.1). Finally, we add up the results of each to determine the total loss of protection: (0.21 and 0.4), which causes a 21% loss to our damage reduction and 0.4 loss to our flat damage reduction. This leaves our original protection values at 59% and 0.6, a significant loss in armor performance! If the damage was of a lower tier, such as 1, only the first round would be used, which would result in a much lower protection loss of 3% and 0.1 damage in total. statModifiers - - Gives an armor piece a modifier to certain stats. Generally these values are debuffs, but if the values were reversed they could be used as buffs for magical armor. armor healingeffectiveness number, decimal - Player healing rate (percent) modification. armor hungerrate number, decimal - Player hunger rate (percent) modification. armor rangedWeaponsAcc number, decimal - Player ranged weapon accuracy (percent) modification. armor rangedWeaponsSpeed number, decimal - Player ranged weapon draw speed (percent) modification. armor walkSpeed number, decimal - Player movement speed (percent) modification. armor wearableAttachment boolean - Designates the item as a wearable object. armor (Handbook Attributes) Attributes that give an item additional handbook details. exclude boolean - If true, removes the item from the handbook. Can be used with the "ByType" functionality to remove large amounts of variants. burnedbrick excludeFromList - - (Needs more info). - include boolean - Allows the toggling of the handbook entry. burnedbrick extraSections - - Adds a section to the handbook for this item. The title and text properties are actually code identifiers that must be filled out in the lang file of a mod, just as one would for an item or block name. text string (text id) - The textbox name that will show up in the lang file. title string (title id) - The title name that will show up in the lang file. groupBy - - Groups variants into a single handbook page, rather than making multiples. - groupedName - - Gives a name to the group of variants within the handbook (must be filled out in the lang file). - The plumb and square is a good example of how a handbook section is added to the game: ha n dbook : { i n clude : true , ex tra Sec t io ns : [ { t i tle : "plumbandsquare-handbook-help-title" , te x t : "plumbandsquare-handbook-help-text" } ] } (Misc Attributes) Uncategorized attributes used in various items. breakChanceOnImpact value, decimal - Determines how often a projectile will break when shot. Flint arrow = 0.5 (50%, iron arrow = 0.20 (20%). arrow codePrefixes array - Creates a list of block prefixes a tool can interact with using special features (such as how a scythe cuts large swathes of grass). scythe currency - - Allows an item to be traded as a universal currency. gear (rusty) value value - Assigns a value to the currency, a rusty gear is valued at 1. gear (rusty) damage value - The ranged damage of a thrown weapon (such as a spear). spear dissolveInWater boolean false If true, will make an item disappear when dropped into a water block. flour, lime, salt firepitConstructable boolean false If true, allows an item to be used to construct a firepit. firewood, bamboostakes fertilizerProps n: value, p: value, k: value - Sets the item as a fertilizer and assigns it nitrogen(N), phosphorous(P) and potassium(K) values. bonemeal, potash As an example, we can make a "universal fertilizer" that contributes a decent amount to each element: fert ilizerProps : { n : 20 , p : 20 , k : 20 }, grindingProps - - Gives the item a grinding recipe in a quern. ore-ungraded type object - Type of stack produced, either a block or item . ore-ungraded code string - Name of the item or block produced by the recipe. ore-ungraded stacksize number - The amount of the output produced after grinding. ore-ungraded health health: value - If the item is a poultice, this is the amount it will heal (requires ItemPoultice class). poultice isPlayableDisc boolean false If true, will be playable on the echo chamber block. Requires a track to be set using the musicTrack property. resonancearchive knappable boolean false Allows an item to be "knapped" into primitive toolheads. flint, stone microBlockChiseling boolean false If true, will allow the item to be used for microblock chiseling. NOTE: Microblock chiseling must also be active in the world settings. chisel metalUnits number - Assigns the units of metal the item contributes when it is smelted. NOTE: An item must be smeltable for this property to be of any use, make sure to check the combustableProps section for more info. resonancearchive musicTrack path to ogg file - Assigns a path to the ogg file of the music track associated with an item that can be played on the echo chamber block. Will only function if the isPlayableDisc property is set to true. resonancearchive nutritionPropsWhenInMeal - - Changes the nutrition of a food item when it is cooked into a meal , otherwise the default nutrition property is used. egg, vegetable satiety number - Numerical value of saturation added. egg, vegetable foodcategory string - Category it adds to in the meal: Dairy(unused), Fruit, Grain, Protein and Vegetable egg, vegetable pigment - - Allows an item to be used as a pigment for coloring. resonancearchive name string - Assigns a name to the color of the pigment. charcoal color red:value, green: value, blue: value - The RGB values of the pigment, which can be found using any generic color picker. charcoal As an example, let's say you wanted to make a valuable purple pigment from the shell of a rare rock snail: "pigment" : { "name" : "Tyrian Purple" , "color" : { "red" : 102 , "green" : 2 , "blue" : 60 } }, Here we can see that the pigment will be named "Tyrian Purple", and is made with the three RGB values (found from a Wikipedia article in this case). reinforcementStrength number - Allows an item to be used by the plumb and square tool to reinforce a block. A higher value indicates more times a block must be broken before it's removed. Example: Igneous stones = 50, Iron Ingot = 400 stone, ingot spearEntityCode Entity - Assigns an spear entity to be made when a spear is thrown. spear workableTemperature number - The temperature required for an item (an ingot) to be worked on an anvil. A value of 0 means it can be worked cold without heating. ingot Common storageFlags number (storage flag ID) - Determines the kinds of storage types the item can be put into. These values also determine what type of items can fit into specialized inventory bags. arrow, armor, upperbody, ore-graded The following are the current existing storage flags (some are unused or reserved) 1 - general 2 - backpack 4 - mining 8 - jewelcrafting 16 - alchemy 32 - agriculture 64 - currency 128 - clothes 256 - offhand 512 - arrows/ammo 1024, 2048, ... (doubling) - Reserved for mods creativeinventory key: string, value: string[] - In which creative inventory tabs the item should be visible in. Any Item There are several to which you can add content from your mod. Note that general should always be included, since it should contain everything. general terrain flora construction decorative items Rock adds all of it's variations to general, terrain and construction: crea t ivei n ve nt ory : { "general" : [ "*" ], "terrain" : [ "*" ], "construction" : [ "*" ] }, * represents the variants which will be added. You can specify multiple and separate them with a comma. It follows the same way as the byType property. However, sometimes you may only want to show a single variant of your block or item (such as one that has multiple directional variants). In this case, you can set a specific variant to show up, once again using similar syntax to the variant code. For example, a Torch only adds the variation up , since the block uses a class to determine which direction it will be placed in regardless of the variant used: crea t ivei n ve nt ory : { "general" : [ "*-up" ], "decorative" : [ "*-up" ] }, maxstacksize integer 64 Determines the maximum amount you can stack the item in one slot. hide, nugget attackpower decimal number 0.5 The damage the item deals when hitting an entity. sword, spear attackrange decimal number 1.5 The maximum distance you can hit an entity with the item. sword, spear materialdensity integer 9999 Determines on whether an object floats on liquids or not. ingot Water has a density of 1000, meaning everything below or equal will float on water. The same goes for lava which has a density of 5000. Vintage story uses real world densities for each material (where 1000 = 1 g/cm^3). To give an idea of the current range of densities, gold has a density of 19300, iron's is 7870, and a feather is 20. liquidselectable boolean false If the item can select a liquid while holding it in hand. - Used for buckets in order to fill it with water and to place waterlily on top of water. miningspeed key: string, value: decimal number - The mining speed for each block material. pickaxe, shovel Materials types are hard-coded into blocks, and include the following types: soil, gravel, sand, wood, leaves, stone, liquid, snow, ice, metal, mantle, plant, glass, ceramic, cloth, lava, brick, fire, other An item is not limited to a single material that it can mine, so if you wanted to make a tool (such as a mattock) that could mine many materials you could do the following: mi n i n gspeed : { "dirt" : 5 , "gravel" : 4 , "ice" : 7 , "metal" : 3 , "sand" : 4 , "snow" : 3 , "stone" : 6 , } miningtier integer 0 Determines which tier of blocks the item can break. If the block tier is above the one defined here nothing will be dropped from it when broken. Also determines the damage tier of a weapon, which is contested against armor tiers. pickaxe, sword combustibleprops object - Information about the items burnable states. redmeat, plank, ore-ungraded burntemperature integer - The temperature at which it burns in degrees celsius. redmeat, plank, ore-ungraded burnduration decimal number - For how long it burns in seconds. plank, ore-ungraded heatresistance integer 500 How many degrees celsius it can resists before it ignites (not implemented yet). redmeat, plank, ore-ungraded meltingpoint integer - How many degrees celsius it takes to smelt/transform this into another. Only used when put in a stove and Melted is set. redmeat, plank, ore-ungraded meltingduration decimal number - For how many seconds the temperature has to be above the melting point until the item is smelted. redmeat, plank, ore-ungraded smokelevel decimal number 1 How much smoke this item produces when being used as fuel. plank, ore-ungraded smeltedratio integer 1 How many items are required to produce one output stack. nugget, ore-graded smeltedstack object - If set, the block/item is smeltable and this is the resulting itemstack once the MeltingPoint has been reached for the supplied duration. nugget, ore-graded requirescontainer boolean true If set to true, the block/item requires a smelting/cooking/baking container such as the Crucible. If false, it can be directly baked/melted without smelting/cooking/baking container. nugget, ore-graded, lime This property can be used to define a burning material. Plank for example can get on fire: combus t ibleProps : { bur n Tempera ture : 800 , bur n Dura t io n : 12 , }, Furthermore it can be used to define smelting processes. An example would be an ingotmold which turns into an ingotmold-burned: combus t iblePropsByType : { "ingotmold-raw" : { mel t i n gPoi nt : 600 , mel t i n gDura t io n : 30 , smel te dRa t io : 1 , smel te dS ta ck : { t ype : "block" , code : "ingotmold-burned" }, requiresCo nta i ner : false } }, nutritionprops object - Defines the nutritional qualities of an item. fruit, grain, vegetable, redmeat foodcategory string - Defines the type of food. It can be fruit , vegetable , protein , grain and dairy . fruit, grain, vegetable, redmeat saturation decimal number 0 How much saturation it can restore. fruit, grain, vegetable, redmeat health decimal number 0 How much health it can restore. - transitionableProps - - Can be used to transition an item to another item or block. redmeat, hide type - - The type of transition method to utilize. redmeat, hide Cure string - Will "cure" an item by showing percent progress until cured. hide Perish string - Will gradually reduce the saturation of a food item once it's fresh period has passed, eventually converting into the product item (usually rot). hide freshHours number (hours) - An optional "fresh" period that must pass before the transition time starts. With food, this is the period of time that saturation is not affected. bread, vegetable, redmeat transitionHours number (hours) - Number of hours before the item transitions into a different item. redmeat, hide transitionedStack object (item or block) - The item or block that the item will transition into. redmeat, hide transitionRatio number - The quantity of the item that will be consumed after the transition period. redmeat, hide Here we'll show an example of a custom food item that transitions into rot: trans i t io na bleProps : [{ t ype : "Perish" , fres hHours : { avg : 240 }, trans i t io n Hours : { avg : 48 }, trans i t io ne dS ta ck { t ype : "item" , code : "game:rot" , qua nt i t y : 2 }, trans i t io n Ra t io : 1 }] We can see that it will take on average 10 days before the food begins to spoil, after which it will degrade over 2 days before turning into 2 rot. Rendering texture string required The texture definition for the item held in hand or dropped on the ground. Any Item textures string - Used to define the textures of a shape the item uses, rather than the singular item texture it pulls from. This is useful for items that use a shape and have visually distinct variants that you want to control in the item JSON. ingot, redmeat A very simple example is how the ingot item uses the metal variants to change the texture of the shape it uses. Where "metal" is the texture defined in the ingot shape JSON, and is overrode here in the item JSON: te x tures : { "metal" : { base : "block/metal/ingot/{metal}" }, }, shape CompositeShape - The items shape. If left empty the item will instead use the texture assigned to it and turn it into a shape by default. flint, stone voxelizeTexture boolean - Tells an item to render it's texture as a shape rather than utilizing the assigned shape redmeat (vintage) alternatives array of shapes - Can be used to give an item alternative shapes based off of different states. bow shape : { base : "item/tool/bow/{type}" , al ternates : [ { base : "item/tool/bow/{type}-charge1" }, { base : "item/tool/bow/{type}-charge2" }, { base : "item/tool/bow/{type}-charge3" } ] }, In this example taken from the bow we can see that it will use alternative shapes when the bow is in different "charged" states. guiTransform object item default Used for scaling, rotation or offsetting the item when rendered in guis. Any Item fphandtransform object item default Used for scaling, rotation or offsetting the item when rendered in the first person mode hand. Any Item tphandtransform object item default Used for scaling, rotation or offsetting the item when rendered in the third person mode hand. Any Item groundtransform object item default Used for scaling, rotation or offsetting the rendered as a dropped item on the ground. Any Item toolrackTransform string - If the item can be placed on a toolrack (using the rackable property) this will transform its position while on the toolrack. Axe, Hoe, Scythe, Sword glowLevel number 0 Gives the item a light value that it produces when held and dropped on the ground. gear (temporal) heldTpUseAnimation string - The animation played when an item is used with a right-click function. hoe heldTpHitAnimation string - The animation played when swinging the item with the left-click mouse button. axe, hoe, sword heldTpIdleAnimation string - Gives the item a unique animation when being idly held by the player. strawdummy The following are available animations used by items in the game: axe, breaktool, hoe, holdbothhandslarge, holdunderarm, knap, scythe, shears, shoveldig, smithing, spearhit, swordhit, twohandplaceblock, water. Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:JSON_Patching TITLE: Modding:JSON Patching --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . Contents 1 The basics 2 Advanced patching 3 Disabling Assets 4 Targeting server or client assets 5 File Path and Name Considerations 6 AddMerge operation 7 AddEach operation 8 Move operation 9 Copy operation 10 Testing patches 10.1 With errors 10.2 Without errors Vintage Story comes with a powerful patching system that lets you perform pinpoint modifications on Assets in JSON format. This significantly improves mod interoperability as you don't have to fully replace these files and overwrite other mod changes in the process. Furthermore you can change multiple files at once. Below is a basic guide to help you understand how patches are working. You can use the ModMaker 3000™ to start writing a patch. It is a a command line tool that ships with the game itself. If you modify vanilla assets, you can run ModMaker 3000™ to create a mod consisting of a set of patches based on the changes you made to the assets. You can then undo or reinstall the game to get your original files back. Note that before packaging the output of the ModMaker 3000™ in a mod, some edits should be made as described later in this document. The patch syntax is based on rfc6902 . However, in addition to what's described in the RFC, it also has "addmerge" and "addeach" operations. The source code for the json patcher is JsonPatchLoader.cs and the customized Tavis.JsonPatch . Note that Code Patching is sometimes a better alternative if hundreds of objects need to be patched. The basics The first thing you need to do is to set up a generic mod as outlined on the wiki. That information can be found on the mod packaging page. In that example the domain is the name of your mod. Patching is considered a content mod but if you have a .dll or .cs in your mod it is a code mod. Patches go in assets/domain/patches. Now that the mod files and folders are set up, it is time to make the first patch. Start by making a .json and name it however you'd like. Open the file with your favorite text editor and fill it in with the following. [ { file : "" , op : "" , path : "" , value : } ] File is where the file you want to change is located. Op stands for operation and it's what you want to do. Valid operations are add , addmerge , addeach , remove , replace , move and copy . Path is where the value you want to change in the file is located. Value is what you want to put in the file. Now let's give a few examples, using the wolf-male.json file. Note that specific line numbers etc. below might not match your own wolf-male.json file if you're using a different version of the game. In this first example, we'll modify wolf damage. For the file put, file : "game:entities/land/wolf-male" The game: is the domain. In this case, it's the domain for vanilla VS. Note that this is not the same thing as the mod folder; all three of the vanilla folders (survival, game, and creative) all use the domain "game". You can change the domain to modify other people's mods, as well. If you look in the vanilla game folders, under assets you'll find the file we are modifying under entities/land/wolf-male . That is why that part comes after the domain. Next put, op : "replace" Since wolves already deal damage, we simply want to replace the damage value. Now for the path, path : "/server/behaviors/9/aitasks/0/damage" This is the truly tricky part. In these JSON files, you'll find labels such as server: and behaviors: . You'll know it's a label because it's followed by the colon. Colon  : Next, we have arrays. These start and end in square brackets. Square brackets [ ] Within these square brackets, we have sections. These start and end in curly braces. Curly braces { } These sections don't have a label so they must be referenced by number. To figure out the number you must count starting with 0. Now to break down the example. We see the value we want to modify (damage) on line 119. Trace it back to the first label, which is server . From there the next label is behaviors . Here we notice the square brackets, so we must count -- in this case to 9. { code: "repulseagents" ... is section 0 { code: "controlledphysics" ... is section 1 { code: "despawn" ... is section 2 { code: "health" ... is section 3 { code: "deaddecay" ... is section 4 { code: "floatupwhenstuck" ... is section 5 { code: "harvestable" ... is section 6 { code: "breathe" ... is section 7 { code: "emotionstates" ... is section 8 { code: "taskai" ... is section 9 Next is the label aitasks , then we hit more square brackets. The count for this is 0 because that's the number you start on. Finally we get to the label damage . We can now move on to the last part, value value : 6 Since the value is a number we just put a number. If it were text, the value would be " 6 ". Advanced patching We will now move on to a more complex example using the same file. This time we'll add a drop to the male wolf. For this, you can make a new file or put a comma after the first patch and put the new patch on the next line. [ { file : "game:entities/land/wolf-male" , op : "replace" , path : "/server/behaviors/9/aitasks/0/damage" , value : 6 }, { file : "game:entities/land/wolf-male" , op : "add" , path : "/drops/-" , value : { type : "item" , code : "stick" , quantity : { avg : 2 , var : 1 } } } ] In this second example, we'll skip straight to the path. You'll notice the label drops and then a dash. Dash - This dash means append to the end of the array. This is useful because it prevents us from overwriting another drop and we may not know the number of drops if other mods added drops as well. For the value, there is an entire section showing that the value doesn't just have to be a number or text. In this case, we added the stick to the drops but by specifying a domain in front of stick like we do for the file it could be a stick from any mod! Disabling Assets Sometimes you'll want to disable a json entirely. This might be to disable a vanilla recipe for example. For this example I'll be disabling wolves because I don't want to upload another file and hopefully you are quite familiar with the file by this time. All jsons have various labels that the game looks for even if they are not there. In this case the file doesn't have the enabled label so we'll be adding it and setting it to false like so. [ { file : "game:entities/land/wolf-male" , op : "add" , path : "/enabled" , value : "false" } ] That's all you have to do. If there's already an enabled label in the file, then the "add" operation will replace it. Targeting server or client assets If you know that a target JSON file is only applied on the server or client, you can use the attribute "side" with the appropriate value to avoid unnecessary processing and the accompanying warnings in log files such as " Hint: This asset is usually only loaded Server side ". For example, a cooking recipe will only be loaded by the server and you could therefore add the attribute : side : "server" so the client doesn't try to patch anything. File Path and Name Considerations By default, when you generate a mod using ModMaker 3000™, the folder structure will use game as the domain, and use the same file name as the vanilla JSON file. This can lead to mod conflicts. Let's use the wolf examples from above. If you run ModMaker after making these changes to wolf-male.json , the resulting ZIP will contain the file assets\game\patches\survival-entities-land-wolf-male.json . This seems to suggest two conventions that VS expects; that you should put JSON patches under the game folder, and that you should match the source file name using a folder-hyphen notation. Neither are true, however. Say you split up the two different wolf changes into two different mods. The first is WolvesAreWimps and the second is WolvesDropSticks . ModMaker would generate identical folder structures and files for these two different mods; that is, both would contain the file assets\game\patches\survival-entities-land-wolf-male.json . These two mods will not work together because they have a filename conflict. (The one that is alphabetically last will take precedence: WolvesDropSticks .) To avoid the possibility of mod conflicts, do not use the game folder. Instead, use a folder with the same name as your modid ; that is, your mod's domain. So in this example, WolvesAreWimps.zip should instead have the folders assets\wolvesarewimps\patches and WolvesDropSticks.zip should instead have the folders assets\wolvesdropsticks\patches . File names are also arbitrary, you do not need to use survival-entities-land-wolf-male.json and can name the file anything you want, since its contents direct the game on what and how to patch, not the file's name. AddMerge operation In addition to the "add" operation, the "addmerge" operation was added in version 1.19.4. "addmerge" is safer, and should be generally be used instead of "add". They behave the same in many conditions: For targets that do not exist, both create the target For paths that end with dash - , both will append a new entry to the target array For paths that end with a number, both will insert the value into the target array at that location For paths that point to an existing primitive value (string, bool, or int), both will replace the value However, they differ when the target is an existing array or object. "add" will replace the target with the patch value. "addmerge" will append the patch value if the target is an array. "addmerge" will merge the value if the target is an object. For example, prior to version 1.19.4, the fat item did not have a behaviors array. Let's say a mod added the behaviors through a patch (notice the path does not end in dash - ): # Bad patch example [ { op : "add" , path : "/behaviors" , value : [{ name : "SealPlacedCrock" }], file : "game:itemtypes/resource/fat.json" } ] However, in version 1.19.4 the vanilla fat object was changed to include a behaviors array. # Section of fat.json from 1.19.4 behaviors : [ { name : "GroundStorable" , properties : { layout : 'Quadrants' , collisionBox : { x1 : 0 , y1 : 0 , z1 : 0 , x2 : 1 , y2 : 0.125 , z2 : 1 }, scale : 0.3 } } ] , So if the old patch was applied on the new game version, then it would replace the vanilla behavior instead of adding a new behavior. # Result of bad patch on 1.19.4 behaviors : [{ name : "SealPlacedCrock" }] , Whereas, instead the patch could use the "addmerge" operation (putting aside that the "addmerge" operation didn't exist in 1.19.3). # Good patch example [ { op : "addmerge" , path : "/behaviors" , value : [{ name : "SealPlacedCrock" }], file : "game:itemtypes/resource/fat.json" } ] If the target array exists (which it does in this example), then addmerge will append the patch array to the target array instead of replacing it. # Result of good patch on 1.19.4 behaviors : [ { name : "GroundStorable" , properties : { layout : 'Quadrants' , collisionBox : { x1 : 0 , y1 : 0 , z1 : 0 , x2 : 1 , y2 : 0.125 , z2 : 1 }, scale : 0.3 } }, { name : "SealPlacedCrock" } ] , Note that when the path ends with a number, "addmerge" will not merge the patch value into the existing array entry at that index. It will instead insert the patch value as a new entry at that index. Note : see the Compatibility page for other ways to handle conflicts and dependencies between mods. AddEach operation "addeach" is used to insert multiple entries at some index in an array. As example, let's say a mod wanted to insert two new behaviors before the AnimationAuthoritative behavior in the hammer tool. This is the initial value before the patch: # Section of hammer.json behaviors : [{ name : "GroundStorable" , properties : { layout : 'WallHalves' , wallOffY : 1 , sprintKey : true , selectionBox : { x1 : 0 , y1 : 0 , z1 : 0 , x2 : 1 , y2 : 0.1 , z2 : 1 }, collisionBox : { x1 : 0 , y1 : 0 , z1 : 0 , x2 : 0 , y2 : 0 , z2 : 0 }, } }, { name : "AnimationAuthoritative" }] , The two new behaviors could be inserted using two "add" or "addmerge" patches. # Example patch using "add" [ # Insert new behaviors in the hammer item before the existing AnimationAuthoritative behavior. { side : "server" , file : "game:itemtypes/tool/hammer" , op : "add" , path : "/behaviors/1" , value : { name : "NewBehavior2" } }, { side : "server" , file : "game:itemtypes/tool/hammer" , op : "add" , path : "/behaviors/1" , value : { name : "NewBehavior1" } }, ] Or to be more concise, "addeach" could be used to insert both entries in the array with one operation. Note that when using "addeach", the entries to insert are in standard (non-reversed) order. # Example patch using "addeach" [ # Insert new behaviors in the hammer item before the existing AnimationAuthoritative behavior. { side : "server" , file : "game:itemtypes/tool/hammer" , op : "addeach" , path : "/behaviors/1" , value : [ { name : "NewBehavior1" }, { name : "NewBehavior2" } ] }, ] "addeach" only works when the patch value is an array and the target is an array. Otherwise it will thrown an exception. Move operation The "move" operation temporarily moves an entry to a temporary path so we can use it later. An illustrative example will make this clear. Consider the following JSON: # Section of showball.json "damageByType" : { "*-snow" : 0.001 , "*-beenade" : 0.001 , "*" : 1 } Now let's use addmerge to add meteorite-iron to this list. What we get is: "damageByType" : { "*-snow" : 0.001 , "*-beenade" : 0.001 , "*" : 1 , "*-meteorite-iron" : 10 , } But this is no good! The wildcard is acted upon first; it "short circuits" the meteorite-iron entry. In other words, the meteorite-iron entry never gets a chance to match. What we need here is to move the wildcard entry to a temporary location, then add meteorite-iron, and then move the wildcard back to the end of the list. We can accomplish this with the move operation: # Move the wildcard out of the way { op : "move" , frompath : "/damageByType/*" , path : "/temp" , file : "game:itemtypes/snowball.json" } , # Now add meteorite-iron to the list here (not shown). { ... } # Move the wildcard back. { op : "move" , frompath : "/temp" , path : "/damageByType/*" , file : "game:itemtypes/snowball.json" } which results in exactly what we want: "damageByType" : { "*-snow" : 0.001 , "*-beenade" : 0.001 , "*-meteorite-iron" : 10 , "*" : 1 } Copy operation "copy" copies the "frompath" value to the given "path", merging if there is already a value at the destination. If the value is an array that already exists at the destination path, the copied values will be added to the end. At this point, you have all the tools for modifying assets using the patching system. This is an incredibly powerful tool that is only limited by your imagination. Have fun patching! Testing patches With errors The game is generally very good at highlighting not only the fact that a patch is broken but also pinpointing the exact location of the error. Let's suppose that you recreated the wolf patch found in Advanced Patching as an exercise but accidentally used the array index "8" instead of the correct index "9". When you load into a world with the patch mod activated, you will immediately notice an error printed in the Story log. For your convenience, this information can also be found in the "server-main" log file. While this error message might be overwhelming at first, in it you will find all the information needed to fix your patch. To make this example easier, highlighted in bold you will find the most important information. [Error] Patch 0 (target: game:entities/land/wolf-male.json) in mymodid:patches/wolfdamageanddrops.json failed because supplied path /server/behaviors/8/aitasks/0/damage is invalid: The json path /server/behaviors/8/aitasks/0/damage was not found. Could traverse until /server/behaviors/8, but then 'aitasks' does not exist. Full json at this path: { "code": "emotionstates", "states": [ { "code": "fleeondamage", "duration": 60, "chance": 0.75, "slot": 2, "priority": 5, "accumType": "max", "whenHealthRelBelow": 0.3 }, { "//comment": "Full json abridged for your convenience." } ] } Patch 0 tells you which patch in a file causes an error. Like array indices (or sections), patches start counting at 0. Consequently, this means that "Patch 0" is the first patch in the file. This is already valuable information because your wolf patch file contains two patches. mymodid:patches/wolfdamageanddrops.json . This section tells you the exact location of the patch file itself. In this example you can see that the faulty patch can be found in the mod with the mod ID "mymodid", inside a folder called "patches", with the name "wolfdamageanddrops". Could traverse until /server/behaviors/8, but then 'aitasks' does not exist. . This line tells you that the game was able to follow the path you provided until a certain point, at which it then failed to locate "aitasks", attaching data it found instead. With this information, you know that you made an error directly after "/server/behaviors/", which you now can use to navigate your target file to investigate where you went wrong. Without errors What if your patch didn't trigger any error messages, but it's not working either? This can happen if the game successfully patches your changes but never gets to apply them in-game. Let's suppose that you recreated the snowball patch found in Move operation as an exercise but stopped after the "Addmerge" step. Having little to no experience in patch writing, you have no way of knowing what went wrong, because for the game everything went right. These issues are harder to resolve because official debugging or logging tools for this sort of problem currently do not exist. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Json_Block_Behaviors TITLE: Modding:Json Block Behaviors --- CONTENT --- This page is outdated. Reason: A new system is being worked on to view JSON properties. Please use this page with caution, as it may not be up-to-date. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Adding behaviors to your block A block behavior encapsulates a modular, reusable feature that can be added to blocks. They are added through the behaviors field at the top level (same level as the code field) of a block json file. Below is an example of two behaviors, one with properties and one without properties. { ... code : "exampleblock" , ... behaviors : [{ na me : "behavior1" , proper t ies : { myProper t y 1 : "myValue1" , myProper t y 2 : "myValue2" }},{ na me : "behavior2" }], ... } Additionally, block entity behaviors can be added in the entityBehaviors field, although they are used less often. All Block Behaviors Here is a table containing all properties of the base game as of 1.18.14. Collectible behaviors can also be added to blocks; they are listed separately . Behavior Name Properties Used by Blocks BlockEntityInteract - door BreakIfFloating - termitemound, crackedrock, meteorite, ore, ore, ore, rock, rottenlog BreakSnowFirst - tallgrass, stonepath CanAttach sides drystonefence, woodenfence, roughhewnfence CanIgnite - torch CollectFrom - henbox Container - storagevessel, stove, hopper, quern, crate, labeledchest, trunk, chest, firepit, stationarybasket Decor sides sidedVariants nwOrientable drawIfCulled alternateZOffset notFullFace removable thickness linen, mediumcarpet, rushmat, smallcarpet, wallpaper, wool, hotspringbacteriasmooth, hotspringbacteria, attachingplant, overlay, overlay, oxidation, transition, fallenleaves, caveart Door - door DropNotSnowCovered - fern, flower ExchangeOnInteract exchangeStates (string array of block states) sounds (path from sounds folder) actionLangCode (language key from the lang file) trapdoor FiniteSpreadingLiquid liquidCollisionSound (path from sounds folder) sourceReplacementCode (block code) flowingReplacementCode (block code) collidesWith (other liquid) spreadDelay (int, default: 0) boilingwater, water, lava, saltwater Harvestable harvestTime (float, seconds, default: 0) harvestedStack ( BlockDropItemStack ) harvestingSound (path from sounds folder) harvestedBlockCode (block code) bigberrybush, saguarocactus, smallberrybush, log-resin HeatSource heatStrength (float) boilingwater, lava, forge, firepit, pitkiln, coalpile, ember, fire HorizontalAttachable handleDrops (bool, default: true) dropBlockFace (Compass Direction, default: "north") dropBlock (block name) attachmentAreas (side -> RotatableCube) attachmentArea (cuboid) tapestry, torchholder, mushroom, canvas, moldrack, painting, shelf, toolrack HorizontalOrientable dropBlockFace (Compass Direction, default: "North") drop (block or item name) agedwallpaperplanks, altar, bellows, bloomerybase, brake, cage, churn, clayoven, clutch, cokeovendoor, condenser, crank, creativerotor, helvehammerbase, jonas, labeledchest, metalpartpile, palisadestakes, palisadewall, pulverizerframe, resonator, skep, slantedroofing, slantedroofingcornerinner, slantedroofingcornerouter, statictranslocator, stationarybasket, stonecoffinsection, stove, trunk, verticalboiler, wagonwheels, windmillrotor, workbench HorizontalUpDownOrientable - - Ignitable - charcoalpit, clayoven, firepit JonasBoilerDoor - jonas JonasGasifier - jonas JonasHydraulicPump - jonas Ladder dropBlockFace (face direction, Default: north) isFlexible (bool, default: false) ladder Lockable - archimedesscrew, chest, chute, cokeovendoor, crate, door, hopper, irondoor, labeledchest, roughhewnfencegate, stationarybasket, storagevessel, trapdoor, trunk, woodenfencegate MilkingContainer - woodbucket Multiblock sizex sizey sizez type cposition jonas, riftward, verticalboiler, banner, trunk, painting MyceliumHost - creativegrass, forestfloor, log, soil NoParticles - claybrickchimney NWOrientable - bookshelves (legacy), claybrickchimney, fruitpress, fruitpresstop, mannequin, slantedroofingridge, transmission, trough, woodenpath, woodentoggle OmniAttachable facingCode (string, default: orientation) attachmentAreas (side -> RotatableCube) crystal, lantern OmniRotatable rotateH (bool, default: false) rotateV (bool, default: false) rotateV4 (bool, default: false) rotateSides (bool, default: false) facing (string, "block" or "player", default: "player") dropChance(float, default: 1) brickslabs, clayshinglelabs, cobblestoneslab, glassslab, mudbrickslab, plankslab, polishedrockslab, quartzslab, stonebrickslab, trapdoor Pillar invertedPlacement (bool, default: false) carvedlog, debarkedlog, hay, log, lognarrow, planks, plaster, quartzpillar, stackedbamboo, woodenaxle Pumpkin vineGrowthStage(int) vineGrowthQuantity(NatFloat) crop PushEventOnBlockBroken eventName (string) bamboo, bambooleaves, leaves, leavesbranchy, leavesnarrow, log, lognarrow, logsection RainDrip - - Reinforcable - - RightClickPickup dropsPickupMode (bool, default: false) sound (path from sounds folder) placeSound (path from sounds folder) basereturnteleporter, bowl, bowl-meal, clayplanter, claypot, crock, crucible, egg, flowerpot, ingotmold, jug, lantern, looseflints, looseores, loosestick, loosestones, metal, oillamp, pan, pie, pineapple, pumpkin, seashell, storagevessel, toolmold, wateringcan, woodbucket RopeTieable - roughhewnfence, woodenfence Slab - - Steaming - boilingwater SneakPlacing - pan, pie, wateringcan Unplaceable - bloomerychimney, bowl, bowl-meal, clayplanter, claypot, crock, crucible, flowerpot, ingotmold, jug, metal, pineapple, pumpkin, storagevessel, toolmold, wateringcan Unstable attachedToFaces (default: down) attachmentAreas (side -> RotatableCube) bunchocandles, crop, deadcrop, firepit, oreblastingbomb, silvertorchcactus, verticalboiler, wildbeehive, woodenpath, woodenrails UnstableFalling attachableFaces (string array) attachmentArea (cuboid) attachmentAreas (side -> RotatableCube) ignorePlaceTest (bool, default: false) exceptions (block code array) fallSideways (bool, default: false) dustIntensity (float, default: 0) fallSidewaysChance (float, default: 0.3) fallSound (path from sounds folder) impactDamageMul (float, default 1) anvil, anvilpart, barrel, barrelcactus, bloomerychimney, bonyremains, bonysoil, carcass, chair, chandelier, cheese, churn, clayplanter, coalpile, dirtygravel, displaycase, drycarcass, egg, flowerpot, gravel, groundstorage, ingotmold, knappingsurface, lightningrod, looseboulders, looseflints, loosegears, looseores, loosestick, loosestones, lootvessel, metal, metalpartpile, muddygravel, oillamp, omoktabletop, pan, pie, quern, sand, seashell, sieve, sludgygravel, snowlayer, stonecoffinlid, storagevessel, table, talldisplaycase, toolmold, wateringcan, woodbucket WrenchOrientable hideInteractionHelpInSurvival (bool, default: false) baseCode (string) banner, brickslabs, brickstairs, carvedlog, chute, clayshinglelabs, clayshinglestairs, clutter, cobblestoneslab, cobblestonestairs, debarkedlog, glassslab, jonas, log, mudbrickslab, planks, plankslab, plankstairs, plaster, polishedrockslab, quartzpillar, quartzslab, quartzstairs, stonebrickslab, stonebrickstairs, stonepathstairs WorldEditFixGhostBlockPlace - - Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Meal_Container TITLE: Modding:Meal Container --- CONTENT --- Semantically, a meal is an array of food ingredient item stacks it was cooked with. Each item stack in the meal must have the same size, and this becomes the initial serving size of the meal. For example, cooking 1 raw meat in slot 0 and slot 1 of a claypot-burned creates a meal. That meal has a 1x stack of raw meat in slot 0 and a 1x stack of raw meat in slot 1. Slots 2 and 3 are empty. However, in the VS implemenation, meals are tightly coupled with their containers. Meals never exist independently of their container, neither as collectibles (items/blocks), nor as C# objects. In the implementation, transferring a meal between containers actually involves creating a new container block and then transferring over the ingredients from the source container. Furthermore, meals and meal containers barely use behaviors, with almost all of the logic implemented in Block classes. This limits how much one can customize new meal containers in a mod, without having to resort to coding and subclassing the existing block classes. This tight coupling of meals and containers contrasts with liquids. Liquids have liquid portion items that are independent of their containers, although the liquid portions are rarely shown in the game. Having a separate liquid portion items creates a clean division between the liquid logic and the liquid container logic, and makes it easier to add new liquid containers. Contents 1 Viewing cooked meal ingredients 2 Meal containers blocks 3 Meal container block classses 4 Serving between containers 5 Meal nutrition gain Viewing cooked meal ingredients It is possible to demonstrate that the meal data is simply the ingredients that it was made with. All of the cooked meal containers inherit from BlockContainer in some way. While a BlockContainer is in an inventory (as oppsed to existing as a block on the ground outside of a ground storage), its inventory is stored in the contents attribute in the item stack. In creative mode, these attributes can be viewed with .gencraftjson command. Note that the item stack attributes are distinct from the attributes defined in the block's json file. Every block of a given type shares the block attributes, but the item stack attributes can be different for every stack of that block. For example, this is the output from an apple pie. Notice how it contains the 4 applies it was made with as well as the spelt dough. { t ype : "Block" , code : "game:pie-perfect" , a ttr ibu tes : { "pieSize" : 4 , "topCrustType" : 1 , "bakeLevel" : 2 , "contents" : { "0" : { "type" : "item" , "code" : "dough-spelt" , "attributes" : { "transitionstate" : { } }}, "1" : { "type" : "item" , "code" : "fruit-redapple" , "attributes" : { "transitionstate" : { } }}, "2" : { "type" : "item" , "code" : "fruit-redapple" , "attributes" : { "transitionstate" : { } }}, "3" : { "type" : "item" , "code" : "fruit-redapple" , "attributes" : { "transitionstate" : { } }}, "4" : { "type" : "item" , "code" : "fruit-redapple" , "attributes" : { "transitionstate" : { } }}, "5" : { "type" : "item" , "code" : "dough-spelt" , "attributes" : { "transitionstate" : { } }} } } } As another example, here is a bowl-meal holding a "Red meat stew with boiled cranberries." { t ype : "Block" , code : "game:bowl-meal" , a ttr ibu tes : { "contents" : { "0" : { "type" : "item" , "code" : "redmeat-raw" , "attributes" : { "transitionstate" : { "createdTotalHours" : 883.6644722075278 , "lastUpdatedTotalHours" : 904.0225999776667 , "freshHours" : [ 144 ], "transitionHours" : [ 36 ], "transitionedHours" : [ 13.331751 ] }, "temperature" : { "temperatureLastUpdate" : 898.3115222173333 , "temperature" : 0 } } }, "1" : { "type" : "item" , "code" : "redmeat-raw" , "attributes" : { "transitionstate" : { "createdTotalHours" : 883.6644722075278 , "lastUpdatedTotalHours" : 904.0225999776667 , "freshHours" : [ 144 ], "transitionHours" : [ 36 ], "transitionedHours" : [ 13.331751 ] }, "temperature" : { "temperatureLastUpdate" : 898.3115222173333 , "temperature" : 0 } } }, "2" : { "type" : "item" , "code" : "fruit-cranberry" , "attributes" : { "transitionstate" : { "createdTotalHours" : 883.6644722075278 , "lastUpdatedTotalHours" : 904.0225999776667 , "freshHours" : [ 144 ], "transitionHours" : [ 36 ], "transitionedHours" : [ 13.331751 ] }, "temperature" : { "temperatureLastUpdate" : 898.3115222173333 , "temperature" : 0 } } } }, "recipeCode" : "meatystew" , "quantityServings" : 1 } } Meal containers blocks Most of the cooking containers have a raw form, empty container form (sometimes called a cooking block in the game), and container with meal form (sometimes called a cooked block in the game). Raw Empty container Container with meal bowl-raw bowl-fired bowl-meal crock-raw-* crock-burned-* crock-burned-* claypot-raw claypot-burned claypot-cooked - dirtyclaypot-empty dirtyclaypot-cooked pie-raw - pie-partbaked, pie-perfect, pie-charred All of the meal containers are implemented as blocks. Many of the contains have comments like this in their json file, stating that they are only blocks for legacy reasons: { na me : "Unplaceable" , "__comment" : "The ground storable obsoletes this being a block. Should be an item, but is kept a block for backwards compatibility" } The defining characteristic of a block, relative to an item, is that blocks can be placed in the world. However, around version 1.16, the Unplaceable behavior and GroundStorable behavior were added to all of the meal containers (except for pie-* which is still directly placed on the ground). Now when the block is right clicked onto the ground, instead of directly setting the block at that location to the meal container's id, a groundstorage block is created that contains the block in inventory form. The meal containers may still exist as placed blocks in the world, if they were placed before version 1.16, and the world was upgraded. Despite the containers being blocks for legacy reasons, any new meal containers introduced by mods must be blocks, because BlockCookedContainerBase requires the serving target to be a block. Specifically some methods in BlockCookedContainerBase directly look at the Itemstack.Block field on the serving target instead of also checking Itemstack.Item or using Itemstack.Collectible . Regardless, the block entities of the meal containers can largely be ignored, because the meal containers typically only exist in item stacks (which do not have block entities). A block entity is only created if the block is directly placed in the world (storing it in a container like ground storage doesn't count). So the only meal container block entities are pies, and legacy block entities from pre 1.16 worlds. Meal container block classses This is the class hierachy for the meal container block classes. Block BlockContainer BlockMeal Features Right click to eat while holding in the hotbar. After eating, it is replaced with the block described in the eatenBlock attribute, or destroyed if the attribute is unset. Used by bowl-meal BlockPie Features Uses the pieSize item stack attribute to set the number of slices remaining Allows baking raw pies in an oven Reduces the spoilage rate of the raw ingredients Handles rendering full pies and sliced pies by calling MealMeshCache.GetPieMesh Prevents eating raw pies and pies with more than a single slice Reduces the nutrition of partbaked and charred pies based on the nutritionMul attribute. Used by pie-raw pie-partbaked pie-perfect pie-charred BlockCookedContainerBase Features Supports serving from this block (serving source) on the ground into empty meal containers. Specifically, the cooked container intercepts the right click event while it is inside of a groundstorage block. If the current item in the hotbar is an empty meal container, then the contents are served from the container on the ground into the container in the hotbar. BlockCookedContainer Features Handles serving from this block in the hotbar into empty meal containers. Used by dirtyclaypot-cooked claypot-cooked BlockCrock Features Reduces the spoilage rate by overriding GetContainingTransitionModifierContained Used by crock-burned-* BlockCookingContainer Features Allows cooking meals in a firepit, if the ingredients match a cooking recipe Used by claypot-burned Serving between containers Serving means transferring a meal from a source container to an empty target meal container. In order for the target to be recognized as an empty meal container, it must have the mealContainer=true block attribute. Also the mealBlockCode attribute must be set to a valid block code. The non-empty meal container must implement IBlockMealContainer , because that interface is used to fill the meal container. BlockCookedContainerBase has the logic to allow it to be the serving source, when it is inside of a groundstorage block, and the serving target is the active block in the hotbar. BlockCookedContainer has the logic to allow it to be the serving source, when it is active in the hotbar, and the serving target is on the ground. So for example, when holding a bowl-fired (serving target), it can serve from a claypot-cooked (serving source) on the ground, because the claypot-cooked's class derives from BlockCookedContainerBase, and the bowl-fired has mealContainer=true and mealBlockCode set. Similarly, when holding a claypot-cooked (serving source), it can serve into a bowl-fired (serving target) on the ground, because the claypot-cooked's class is BlockCookedContainer. Nothing can serve from a bowl-meal, because the bowl-meal's class, BlockMeal , does not inherit from BlockCookedContainerBase . Likewise, nothing can serve from a pie, because its class also does not inherit from BlockCookedContainerBase . Meal nutrition gain Meals have a nutrition boost comapred to eating their raw ingredients. This nutrition boost comes from the difference between the nutritionProps and nutritionPropsWhenInMeal fields for all of the ingredients. So the exact boost depends on each ingredient. Although all of the ingredients in the base game get a boost from putting them in the meal, it would be possible to make an ingredient that gets a debuff from putting it in a meal. For example, the nutrition of red meat is boosted by 140, because redmeat-cooked sets nutritionProps to { satiety: 280, health: 0, foodcategory: "Protein" } and the nutritionPropsWhenInMeal to { satiety: 420, foodcategory: "Protein" } . Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Minidimension TITLE: Modding:Minidimension --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Contents 1 Dimension identifiers 2 ChunkIndex3D 3 Life of a minidimension 3.1 Server side: Create the minidimension 3.2 Server side: register the minidimension with an index 3.3 Client side: create the minidimension 3.4 Server side: fill the minidimension 3.5 Clear the minidimension 4 Limitations Minidimensions contain chunks of blocks that are rendered along with the chunks of the normal world. By setting the CurrentPosition property on the minidimension, one can change the location at which the minidimension is rendered on top of the normal world. In addition to translating the minidimension relative to the normal world, the CurrentPosition property also allows arbitrary rotations. The base game use a minidimension in the world edit import tool to show a semitransparent preview of what the imported blocks would look like at the cursor position. The other intended use case for minidimensions is to function as a vehicle that the player can build with blocks. The base game has a demo of this which invoked in creative mode with the /we testlaunch command. However, currently numerous necessary features are missing in order for that use case to be anything more than a tech demo. Some of these limitations are described in the movchunks public notes documentation that was included in the 1.19.0 release notes. More limitations are described in this wiki page. Theoretically these limitations could be overcome with enough Harmony patches. Minidimensions are called "Mini Dimensions" in the official release notes. Dimension identifiers There are 3 different dimension related identifiers. The dimension id differentiates full dimensions. Currently the only used values are 0 for the normal world and 1 for the minidimension container. Constants for those value are stored in the Vintagestory.API.Config.Dimensions enum. Dimension ids 2 through 9 are reserved for future vanilla use. Dimension ids 10 through 1023 are available for mods, but the game does not have any functions to create those dimensions. The minidimension container is subdivided into minidimensions. These minidimesions are identified by their subdimension id. There are functions for creating minidimensions. Minidimensions are also identified by an index in the ServerMain.LoadedMiniDimensions dictionary. By convention, the minidimension index and subdimension id are set to the same value. ChunkIndex3D Chunks are cubes with a length of 32 blocks. Every valid BlockPos must point to a block inside of a valid chunk position. Internally the dictionary of loaded chunks is indexed by a long. That long is called a ChunkIndex3D, which contains the X, Y, Z, dimension, and sometimes subdimension coordinates of the chunk. The way in which these coordinates are packed into the long helps explain the (very generous) map size limits that were introduced when minidimensions were added. ChunkIndex3D fields in most significant bit first order reserved dimension guard chunkY chunkZ chunkX 2 bits 10 bits 1 bit 9 bits 21 bits 21 bits ChunkIndex3D field ranges: reserved - always 0 guard - always 0. This is used to detect if some mod accidentally created a BlockPos with a Y value above the limit. dimension - [0, 1024). Currently only 0 (normal world) and 1 (minidimension parent) are used. chunkX - [0, 2097152) chunkY - [0, 512) chunkZ - [0, 2097152) Since a chunk is 32 by 32 by 32, the limits on the chunk coordinates translate into these block ranges for the normal world. blockX - [0, 67108864) blockY - [0, 16384) blockZ - [0, 67108864) However, the minidimension container is further subdivided into minidimensions. This extra dimensional data is taken from the chunkX and chunkZ fields. Minidimension ChunkIndex3D fields in most significant bit first order reserved dimension guard chunkY subdimension upper chunkZ subdimension lower chunkX 2 bits 10 bits 1 bit 9 bits 12 bits 9 bits 12 bits 9 bits And these are the field ranges for a minidimension ChunkIndex3D: reserved - always 0 guard - always 0 dimension - always 1 for the minidimension container subdimension - [0, 16777216) minidimension chunkX - [0, 512) minidimension chunkY - [0, 512) minidimension chunkZ - [0, 512) The AdjustPosForSubDimension function packs the subdimension id into the upper bits X and Z bits of a BlockPos. It treats (0, 0, 0) as the center of a minidimension, whereas for the normal world, (0, 0, 0) is at the edge of the map. So the field ranges below take this translation into account. minidimension blockX - [-8192, 8192) minidimension blockY - [-8192, 8192) minidimension blockZ - [-8192, 8192) Life of a minidimension Server side: Create the minidimension Typically one calls CreateMiniDimension on the block accessor to create the IMiniDimension object. The function takes a Vec3d representing where in the normal world to anchor the center of the minidimension. If the center location not known yet, just use new Vec3d() . The center location can be easily adjusted later by setting the CurrentPos field on the IMiniDimension . Example: IMiniDimension dim = sapi . World . BlockAccessor . CreateMiniDimension ( new Vec3d ()); Internally, CreateMiniDimension constructs and returns a BlockAccessorMovable . BlockAccessorMovable is not part of the API module, but the class is still public and can be accessed from VintagestoryLib.dll. Many of the methods in BlockAccessorMovable are virtual and public or protected. It is possible to customize the minidimension behavior by creating a new class that inherits from BlockAccessorMovable and then overriding methods. In this case, the custom class would be constructed directly instead of calling CreateMiniDimension . Server side: register the minidimension with an index On the server, a IMiniDimension object can be registered to a new minidimension index with LoadMiniDimension . The saved game contains an integer for the highest minidimesion index ever used. That function returns the next value and increments the integer. The vanilla game does not contain any functions to free an minidimension index after it is allocated, and the saved file does not contain a free list of unused minidimension indices. Mods are encouraged to either: 1. Allocate a fixed number of minidimensions, and store those allocated indices inside of the save game mod data . 2. Maintain their own mod specific free list of minidimension indices. This free list should be stored inside the save game mod data . On the server side, to recycle a minidimension index, either the existing IMiniDimension object can be cleared and reused, or a new IMiniDimension object can be registered to the existing index with SetMiniDimension (instead of calling IServerAPI.LoadMiniDimension ). To maintain the subdimension id convention, after registering the minidimension to an index, the subDimensionId property of the IMiniDimension should be set to its index. IMiniDimension also has a SetSubDimensionId method. This method is redundant, and calling it just sets the property. Client side: create the minidimension The minidimension system only transfers chunk contents from the server to the client. It is up to the mod to transfer over any other settings for the minidimension. Notably, the CurrentPos must be set on both the server (it is set by CreateMiniDimension) and client side. The minidimension system will not automatically transfer the value set on the server to the client. The GetOrCreateDimension function on the IClientWorldAccessor is used to create a default initialized minidimension. The CurrentPos should always be set on the returned IMiniDimension, because GetOrCreateDimension only sets it if the dimension was newly created, and the IMiniDimension may have been default initialized earlier. If the server starts modifying blocks in the minidimension before the mod on the client explicitly creates the minidimension, then the minidimension will be automatically created on the client with default values. The default initialized minidimension will not have CurrentPos property set to a valid value. It is possible to fill in the chunks on the server side first, then fix the CurrentPos property on the client. However, if the minidimension is to be rendered as the semitransparent preview minidimension, then that must be set before the chunk contents are transferred. Because the semitransparency is a property of the tessellated chunks, and the chunks are tessellated shortly after they are transferred. Marking the dimension as the preview dimension will not cause the chunks to be retessellated. The preview dimension is set by passing the subdimensionid to SetBlocksPreviewDimension . There can only be one preview dimension per client (each player could theoretically have a different one). The preview dimension can be disabled by passing -1. On the client side, one can check whether a given IMiniDimension is the current preview dimension through the TrackSelection property. The preview dimension gets two special features: 1. Its blocks are rendered semitransparently. Specifically its blocks are rendered in the transparency pass instead of the opaque pass, and transparency is added to the block textures. 2. The dimension center follows around the cursor. Specifically, the current block selection for the dimension center instead of the CurrentPos property. Just like the server, GetOrCreateDimension uses BlockAccessorMovable as its implementation of IMiniDimension . It is possible to use a custom IMiniDimension implementation on the client by inheriting from BlockAccessorMovable and overriding fields. In this case, register the dimension directly adding it to the Dimensions dictionary in IClientWorldAccessor. Use the subdimension id as the index. One reason to create a custom IMiniDimension is to give it the transparency of the preview dimension without the cursor tracking. To disable the cursor tracking, override the GetRenderOffset method. There can still only be one transparent minidimension per client, because most of the transparency is handled by TesselatedChunkPart.AddToPools, which calls the static method BlockAccessorMovable.IsTransparent , which only looks at the subdimension id. Server side: fill the minidimension IMiniDimension inherits from IBlockAccessor. So it is the preferred object for setting blocks. With the correct BlockPos, it is also possible to replace blocks inside the minidimension chunks through the default block accessor (the one typically used for the normal world). The default block accessor does not always work for setting blocks the first time, because any chunks that it implicitly creates will not be registered with the minidimension, which means they will not be anchored into the normal world based on the CurrentPos field, and the chunks will not be sent to nearby players. To calculate a BlockPos inside the minidimension, first create a BlockPos relative to (0, 0, 0), then pass it to IMiniDimension.AdjustPosForSubDimension. That function will set the dimension index, and add the subdimension id bits to the X and Z coordinates. It is possible to set blocks through the IMiniDimension using unadjusted coordinates (such as (0, 0, 0)). BlockAccessorMovable will still find the chunks in the correct subdimension id. However, the unadjusted coordinates will be given to functions such as Block.OnBlockPlaced. So the block and its behaviors will not be able to see the block in the world or its neighbors. Clear the minidimension It is not possible to unregister a minidimension on the server side, without resorting to reflection or harmony patches. On the client side it is possible but not recommended to unregister a minidimension. Instead, call ClearChunks on both sides, and optionally UnloadUnusedServerChunks on the server side. Limitations Minidimensions cannot hold entities The following blocks are known to render improperly in minidimensions. An easy way to test whether a block renders correctly in the minidimension is to use worldedit to copy a schematic with the block to the clipboard, then use the import tool to preview it. Doors - does not render at all Wooden axle - does not render at all. The custom renderer for the rotating shaft does not take the minidimension render offset into account. The axle stands are not rendered because BEBehaviorMPBase uses the dimension unaware accessors, so it cannot see the neighboring blocks in the minidimension and tell which direction the stands should be anchored to. Minidimension chunks are only sent to nearby players when the chunks are modified. The chunks are not sent when a new player joins, when a player walks to a minidimension that was previously out of view range, or when a previously out of view range minidimension is moved to a player by setting CurrentPos. Except for server selection tracking minidimensions, whose chunks are always sent to all logged in players regardless of where they are. A minidimension is marked as server selection tracking passing its subdimension id to SetSelectionTrackingSubId_Server . Passing any other value to the function just reverts the minidimension to normal. BlockAccessorStrict will not allow reading blocks outside of the main dimension. Most accessors are relaxed, but a strict accessor can be created via the IWorldAccessor.GetBlockAccessor method with strict=true. Specifically GetBlock calls GetBlockId with pos.InternalY, and BlockAccessorStrict.GetBlockId fails because posY is greater than worldmap.MapSizeY, because the dimension is packed into the upper bits of posY. The dictionary of registered minidimensions is iterated on every server tick. So even though the ChunkIndex3D supports 16777216 subdimensions, registering 1000 or so will add server tick latency. More limitations are described in the movchunks public notes . Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Client Startup Parameters • Server Startup Parameters • Understanding the VS Engine • All Engine Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Mod-Engine_Compatibility TITLE: Modding:Mod-Engine Compatibility --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . The Vintage Story Game engine is not immune to code deprecation. When new releases are made, some API components may become obsolete or removed entirely. However, we the VS Team do very carefully consider every API breaking change to minimize breakage as much as possible. In general, if your mod consists mostly of JSON assets, you are likely safe from breakage across several major updates. And even if your mod contains C# code, I'd like to believe 9 out of 10 times, the breakage on a major update, if any, should be minimal and fixable within an hour or two. Every API change is also documented in every devlog entry, to help you with your migration process. We also have a prolonged and consistent release cycle to prepare modders for a new major update. 1. pre : A preview release, we consider the major API changes to be 90% complete at this point, but there can still be adjustments. The main focus in later previews is finishing up minor game content and any issues which have emerged during the preview phase. 2. rc : A release candidate, we consider all major API changes to be complete by this stage as well as all game features. The main focus is bugfixing. 3. stable : A stable version. We only change the API in extraordinary circumstances after this point. If you experience a pattern of major breakage that requires many hours of updating to get your mod working again - the dev team always has an open ear for suggestions. Please also keep in mind, we are not perfect. Sometimes breakage does happen, sometimes unintentionally. Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:ModConfig TITLE: Modding:ModConfig --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.20.3 . Contents 1 Mod Config 2 Creating Config Class 2.1 Serializable Objects 3 Store & Load Mod Config in Api 4 Seperating Config Files 4.1 Sending Server Config to Clients via World Config 5 Further Notes Mod Config When developing code mods, it is often important to allow the user to change some values to their liking. To do this, you can create mod config files. The game provides a number of functions that allow you to save and load your mod config in a JSON format. Creating Config Class The first step of creating a mod config is to create a class that is JSON serializable. These classes often offer little functionality, but are simply used as definitions for JSON files. A config class merely consists of a set of serializable data. public class MyConfigData { public bool ShouldModDoAThing = true ; public int DoThingTimes = 3 ; public string CustomModString = "Hello World!" ; } When the mod config is stored, the following file will be created: { "ShouldModDoAThing" : true , "DoThingTimes" : 3 , "CustomModString" : "Hello World!" } Serializable Objects Only objects that can be serialized should be included in a mod config class. These include, but are not limited to: String Bool Int Float Vectors (Vec2f, Vec3f, Vec2i, etc.) Other custom classes Store & Load Mod Config in Api Mod Config files need to me stored and loaded using the appropriate functions. The following code sample will load and store a mod config. public static MyConfigData config ; public override void Start ( ICoreAPI api ) { TryToLoadConfig ( api ); } private void TryToLoadConfig ( ICoreAPI api ) { //It is important to surround the LoadModConfig function in a try-catch. //If loading the file goes wrong, then the 'catch' block is run. try { config = api . LoadModConfig < MyConfigData >( "MyConfigData.json" ); if ( config == null ) //if the 'MyConfigData.json' file isn't found... { config = new MyConfigData (); } //Save a copy of the mod config. api . StoreModConfig < MyConfigData >( config , "MyConfigData.json" ); } catch ( Exception e ) { //Couldn't load the mod config... Create a new one with default settings, but don't save it. Mod . Logger . Error ( "Could not load config! Loading default settings instead." ); Mod . Logger . Error ( e ); config = new MyConfigData (); } } The loaded config settings can now be used by accessing the 'config' object anywhere in your code. Seperating Config Files When creating config files, it is important to remember that the server and client are often logically and physically seperated. Due to this, it is a good idea for mods to have a config file for the client, and a config file for a server. Seperated Configs public static MyClientConfigData clientConfig ; public static MyServerConfigData serverConfig ; public override void StartClientSide ( ICoreClientAPI api ) { TryToLoadClientConfig ( api ); } public override void StartServerSide ( ICoreServerAPI api ) { TryToLoadServerConfig ( api ); } private void TryToLoadClientConfig ( ICoreAPI api ) { //It is important to surround the LoadModConfig function in a try-catch. //If loading the file goes wrong, then the 'catch' block is run. try { clientConfig = api . LoadModConfig < MyClientConfigData >( "MyClientConfigData.json" ); if ( clientConfig == null ) //if the 'MyClientConfigData.json' file isn't found... { clientConfig = new MyClientConfigData (); } //Save a copy of the mod config. api . StoreModConfig < MyClientConfigData >( clientConfig , "MyClientConfigData.json" ); } catch ( Exception e ) { //Couldn't load the mod config... Create a new one with default settings, but don't save it. Mod . Logger . Error ( "Could not load client config! Loading default settings instead." ); Mod . Logger . Error ( e ); clientConfig = new MyClientConfigData (); } } private void TryToLoadServerConfig ( ICoreAPI api ) { //It is important to surround the LoadModConfig function in a try-catch. //If loading the file goes wrong, then the 'catch' block is run. try { serverConfig = api . LoadModConfig < MyServerConfigData >( "MyServerConfigData.json" ); if ( serverConfig == null ) //if the 'MyServerConfigData.json' file isn't found... { serverConfig = new MyServerConfigData (); } //Save a copy of the mod config. api . StoreModConfig < MyServerConfigData >( serverConfig , "MyServerConfigData.json" ); } catch ( Exception e ) { //Couldn't load the mod config... Create a new one with default settings, but don't save it. Mod . Logger . Error ( "Could not load server config! Loading default settings instead." ); Mod . Logger . Error ( e ); serverConfig = new MyServerConfigData (); } } When accessing a config file in this manner, always make sure to access the appropriate file based on the current client/server side. This can usually be retrieved by checking: if ( api . Side == EnumAppSide . Server ) { //Server side... } else { //Client side... } Sending Server Config to Clients via World Config If you need to send configuration values from the server to the client, or client to server, you will likely need to use one of the networking APIs in the game. However, there is an easier method that involves saving data to the world config. The following code snippet will load the config values on the server, and adds them to the World Config (which is a TreeAttribute ). The world config is retrieved from clients whenever they join. public override void StartClientSide ( ICoreClientAPI api ) { TryToLoadClientConfig ( api ); int valueFromServer = api . World . Config . GetInt ( "ServerSideValue" , 0 ); } public override void StartServerSide ( ICoreServerAPI api ) { TryToLoadServerConfig ( api ); api . World . Config . SetInt ( "ServerSideValue" , serverConfig . ServerSideValueOnly ); } Further Notes When loading, if a property exists in the C# class, but is not found in the JSON file, it will be set to a default value. When loading, if a property exists in the JSON file, but is not found in the C# file, it will be ignored. When loading, if a config file does not exist at the specified path, it will return null. Config files should be stored after they are loaded, to ensure any new properties are added. This will not change player-modified data. StoreModConfig will always overwrite an existing file. You should not store a new mod config if loading an existing one causes an error. This will remove any player-modified data. JSON5 is valid for config files. Currently, the config system does not allow specific JsonSerializerSettings, and will always use the default of these. Standard JSON.NET features are applied for the class. You can, for example, use the [JsonIgnore] attribute to not save a property. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Mod_Packaging TITLE: Modding:Mod Packaging --- CONTENT --- Other languages: English español русский This page was last verified for Vintage Story version 1.19.8 . Contents 1 Zip Files 2 Info file 3 Icon 4 Distribution and Installation Zip Files To make your mod easy to install and use, it should be packed into a zip file . Any assets (images, .json files, etc) go in a "assets" directory in the root of the zip, any source files into the "src" folder and any DLLs (and optionally, PDB files) go directly into the zip file root folder. Please note that if you have a code mod that has no assets (AKA, your mod is a single C# DLL), it is possible to ship the bare DLL without packing it into a zip. This is not really a good idea though, as when distributed this way it is hard to include a PDB file and downloading DLLs directly in Windows can lead to issues with your mod failing to load due to security policies applied to files downloaded from the internet. Zip File Structure .zip assets domain asset files ( tree overview ) modinfo.json modicon.png compiled sources files (*.dll and *.pdb) src source code files - folders allowed (*.cs) Info file Every zip-file mod requires a modinfo.json inside the root directory of the archive. Otherwise the entire mod will be ignored. This info file specifies what kind of mod it is, its name, author, etc. An example of a theme pack info file ( modinfo.json ): { "type" : "content" , "modid" : "creativemdexamplemod" , "name" : "My Example Content Mod" , "description" : "This is an example mod, which does nothing" , "authors" : [ "CreativeMD" ], "version" : "1.0" , "dependencies" : { "game" : "" , "survival" : "" }, "website" : "http://example.com" } "type" : "content" , Besides "type": "content" , there is also "type": "theme" and "type": "code" . "type": "content" : Defines this mod as being a Content Mod . Content Mods cannot contain any C# code (if it does they will be ignored). "type": "theme" : Defines this mod as being a Theme Pack . Theme Packs cannot override gameplay affecting assets categories (such as blocktypes, itemtypes and so on) and no C# code will be loaded. "type": "code" : Defines this mod as being a Code Mod . Code Mods are the same as a Content Mods, except they can also load C# code to do things that are not possible with JSON. "dependencies" : { "game" : "1.5.3" }, Means it requires at least VintageStory v1.5.3 or a newer version. For more info see Modinfo . Icon The file modicon.png (if it exists) will be used and shown in the mod manager menu. Distribution and Installation To install a mod simply, you can either place the zip file in the Mods directory in your VintagestoryData folder , or the Mods folder in your Vintage Story install folder. You can upload your mod to the Vintage Story Mod Database so people can find it. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Content Mods • Developing a Content Mod • Packaging & Release --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Moddable_Mod TITLE: Modding:Moddable Mod --- CONTENT --- Other languages: English русский This page is outdated. Reason: Please note that this tutorial uses the now-redundant mod-tools setup, and is a candidate for a rewrite. You may find some benefit in the information on this page, but please use with caution. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . This article requires a setup development environment. If you don't have one, read the tutorial on setting up your development environment. For brevity, this article presupposes some familiarity with VintageStory code modding, primarily how mods are packaged and how to setup a compiled mod project. If you are looking to create your first code mod, we recommend starting here instead. Contents 1 Introduction 2 TipMod & SpawnMod 3 Preparation 4 Setting up TipMod 5 Setting up SpawnMod 6 Mods interacting 6.1 Adding a reference to an external mod 6.2 Accessing the ModLoader 7 Conclusion 8 Troubleshooting 9 Distribution Introduction VintageStory makes all loaded mods available through it's expansive API, which you can explore in the API docs. This makes it possible for code mods to interact with each other and access each other's public properties. It does, however, make whatever mod is including another mod dependent on that mod. If the mod being included is not present or disabled, the including mod will not work. The loading and retrieving of mods is done through the IModLoader , which we retrieve from ICoreAPI . 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 smaller logical parts that communicate - a modpack. Extendable mods, for example - the creative GUI tool for growing trees, which lets you register new tree types and easily test them. 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 resolve at game runtime. Note that, excluding the use of reflection or the use of an intermediary library, which is outside the scope of this article, only compiled mods can interact with eachother . 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 tips about the command. Preparation 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 do the following: Add the necessary using directives. Create a class named after the mod it's contained in - TipMod and SpawnMod . Have our classes inherit ModSystem - the base for any VintageStory code mod. Override the StartServerSide method to access ICoreServerAPI . Make the TipMod class public so that it's accessible by other mods. // 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, called Tip . // 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 ; } } } Our resulting folder structure looks like this. We are now ready to extend our mods, starting with "TipMod" - our moddable mod. Setting up TipMod For "TipMod", we do the following: Store the ICoreServerAPI object as a field so that we can access it throughout our class. Define a List that stores Tip objects. Define a method that selects a random Tip from the List and prints it in chat for all players, printing a default message if the List is empty. Register that method to be called at set intervals by the Timer , which is available in the IServerEventAPI . Define a public method for adding new Tip objects. // In TipMod.cs 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 ); } } 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. We can now compile our mod, add it to our VintageStory mod folder and test it ingame. If the there are occasional chat messages saying "There aren't any listed tips", 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 Let's first setup our mods functionality. We register a command which teleports the player to spawn. Conveniently, the IServerPlayer object stores it's spawn position, as well as the IPlayerEntity , which defines a method for teleporting itself to a location. // In SpawnMod.cs class SpawnMod : ModSystem { public override void StartServerSide ( ICoreServerAPI api ) { base . StartServerSide ( api ); api . ChatCommands . Create ( "spawn" ) . WithDescription ( "Teleport to spawn" ) . RequiresPlayer () . RequiresPrivilege ( Privilege . chat ) . HandleWith ( OnCmdSpawn ); } private TextCommandResult OnCmdSpawn ( TextCommandCallingArgs args ) { //Spawn position is stored on the server - Our command is called on the server so we can turn our IPlayer into IServerPlayer. IServerPlayer player = args . Caller . Player as IServerPlayer ; player . Entity . TeleportTo ( player . GetSpawnPosition ( false )); return TextCommandResult . Success (); } } "SpawnMod" is ready. Let's test it. The command /spawn should now teleport us to spawn. If our mods are working, we're ready to make "SpawnMod" interact with "TipMod". Mods interacting Before we get started, we have to add a reference in "SpawnMod" to "TipMod". Adding a reference to an external mod If you are using Visual Studio, in your Solution Explorer, directly under your project, right-click "References" and select "Add Reference". 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. Accessing the ModLoader Now we're ready to have "SpawnMod" add tips. Let's go ahead and do the following. Add a using directive for the namespace tipmod.src . Retrieve TipMod through the ModLoader by passing it's type. Call the AddTip method provided by TipMod and add a variety of tips. public override void StartServerSide ( ICoreServerAPI api ) { base . StartServerSide ( api ); api . ChatCommands . Create ( "spawn" ) . WithDescription ( "Teleport to spawn" ) . RequiresPlayer () . RequiresPrivilege ( Privilege . chat ) . HandleWith ( OnCmdSpawn ); using tipmod.src ; 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" )); } Conclusion Following the steps in this article, this is what our resulting code looks like, spread across two mods. // 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 ); } } } // 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 ; } } } // 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 . ChatCommands . Create ( "spawn" ) . WithDescription ( "Teleport to spawn" ) . RequiresPlayer () . RequiresPrivilege ( Privilege . chat ) . HandleWith ( 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 TextCommandResult OnCmdSpawn ( TextCommandCallingArgs args ) { //Spawn position is stored on the server - Our command is called on the server so we can turn our IPlayer into IServerPlayer. IServerPlayer player = args . Caller . Player as IServerPlayer ; player . Entity . TeleportTo ( player . GetSpawnPosition ( false )); return TextCommandResult . Success (); } } } 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 and booting into a world, 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. 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: It's a .cs (source) mod and not a .dll (compiled) mod. Refer here for how to set a 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, "TipMod", is not present in the mod folder. Distribution To distribute this mod, run the following command in the vsmodtools cli pack , then copy the .zip file into your VintageStory mods folder. Here are the official versions: for VS v1.13.2: TipMod_vs1.13.2_v1.0.0.zip and SpawnMod_vs1.13.2_v1.0.0.zip . Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Modding_API_Updates TITLE: Modding:Modding API Updates --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . The Vintage Story API is responsible for almost all functionality for modding the game. This page will list changes made to the API in each version, taken directly from the VS News page . Changes will be sectioned by content , code , or other , based on what type of mod they mostly affect. Some points may contain additional information on how this will affect mods. This log starts at version 1.19.0, and changes from before then are not listed. Release candidate versions (suffixed with rc ) are not listed until they are released as stable versions. Contents 1 How to Utilise This Page for Modders 2 1.20 2.1 1.20.1 2.2 1.20.0 2.2.1 Content 2.2.2 Code 2.2.3 Other 3 1.19 3.1 1.19.8 3.2 1.19.6/1.19.7 3.2.1 Code 3.3 1.19.5 3.3.1 Content 3.3.2 Code 3.3.3 Other 3.4 1.19.4 3.4.1 Content 3.4.2 Code 3.4.3 Other 3.5 1.19.2/1.19.3 3.5.1 Code 3.6 1.19.1 How to Utilise This Page for Modders This page gives updates on all API updates for code and content modders. When updating a mod, it is recommended to find the version your mod was released for in this list. From there, scrolling up will display all the changes made to the API since that version. If your mod uses any of those API features, it is recommended to test them. This will give you a good idea of what parts of a mod may need updating for newer versions. 1.20 1.20.1 No API changes for this version. 1.20.0 See all update notes here . API changes are listed below. With the 1.20 release, the inbuilt modmaker program has been updated, and there now exists a JSON property reference page . It is recommended that you take a look at this! Content Feature: Allow json file inheritance for blocks, items and entities using property "inheritFrom", example: assets/survival/blocktypes/wood/woodtyped/roughhewnfence-fsnow.json Feature: Max animated elements cap is now 230 but up to 1000 can be set via settings (Technical info: Now sending animation shader data via UBOs instead of uniforms) I had to remove the 4x3 matrix optimization because UBOs seem to pad non 4-int rows and thus break the matrix stride. On the bright side this change should actually improve performance by a little bit by not uploading unused transformation matrixes (uploads now only until the necessary jointid instead of GlobalConstants.MaxAnimatedElements Feature: Modmaker Improvements Now asks the user if they wish to ignore the shapes folder when looking for file differences. The shapes folder contains nearly 4,000 relatively large files, it takes up a large portion of time when searching for file differences. However, many mods and modders do not have a need for this. Ignoring the shapes folder cut down execution time by an average of 65% (from 9055ms avg. to 3122ms avg.). Adds a function to the modmaker which restores all vanilla assets in their unmodified, vanilla state Add ability to re-export all the JSON assets in a standardized way. When editing vanilla files in Notepad++, nearly every property is highlighted in red due to it not using "s for property names, and similarly with Visual Studio. By importing each json file and immediately exporting them, it reorganises them in a much neater, IDE-friendly manner Adds the ability to delete the temporary folders created by the mod maker. In case any one has any issues with the mod maker, they can run this and it may resolve them. Saves them from manually finding and deleting temp folders Makes the mod maker compare files based on their JSON properties. Although this has a minor performance impact, it allows the ability to validate all the JSON files to ensure they are valid, as well as allows the modder to use different JSON formats if they so wish. Feature: New entity type property: "behaviorConfigs" . Can be used to pre-define entity behavior configs, which get applied to both server and client behaviors, eliminating the need to write configs twice. Feature: Can now also add hair elements to the seraph that are only shown when certain clothing items are worn. Example implementation: assets\game\shapes\entity\humanoid\seraphskinparts\hair-extra\braidedup.json Feature: Can now define IgnoreElements for block/item/entity shapes Feature: For semi-submerged boats, can use EntityBehaviorHideWaterSurface to not render water inside the boat using a flat plane of hiding elements in the boat model file. See also boat.json Feature: Can now use step-parented shape overlays for entities. Example: Male elk Feature: Added Multiple collision boxes entity behavior. Currently only available as an upgrade to the PassivePhysics entity behavior. Feature: Added new tool types in EnumTool Refactor: Removed property entity.GearInventory. Now replaced by EntityBehaviorContainer. Refactor: Removed EntitySkinnableShapeRenderer. Now replaced by EntityBehaviorTexturedClothing Refactor: Rename block clutter property randomizeDimensions to randomizeYSize Refactor: Collectible.IsBackPack() is now obsolete. Use collectiblebehavior { name: "HeldBag" } instead Tweak: Add "withDrops" property to BehaviorTransformBreak Tweak: Can now use shape overlays for clothing Tweak: Allow TransitionableProperties (perishableProps in cooking pot recipes) with transition type "None" Tweak: Cooking recipes add support for "DirtyPotOutputQuantity" property Tweak: Can now add a "whenSwimming: true/false" condition to AI tasks Tweak: Made henbox more moddable Tweak: Add support beam behavior property "dropWhenBroken" Tweak: Can now set vertex flag bit 28 to 1 for liquids to have them rendered mostly opaque Feature: The Pathfinding system can now differentiate between types of creature and alter pathfinding behavior accordingly. Set via property aiCreatureType on the BehaviorTaskAI Config. See also enum file EnumAICreatureType Refactor: Seraph left hand attachment point was not at the same spot as the right hand one (seraph.json model file edit only) Refactor: Move out trader trade lists into separate files, but the integrated trade list method still works Tweak: Do not play the default idle animation on any creature if some other "BlendMode=Average" animation runs. To modders that add entities with animations: You will want to set your standard walk and run animations to BlendMode: "Average" Tweak: Allow custom names for clutter depending on used texture Tweak: Add ability to define multiple cooking recipe in one recipe json file Tweak: inheritFrom can now be applied recursively (an inherited file can also use inheritFrom) Tweak: Can now configure humanoidTraversalCost via json, example implementation in blocktypes/wood/table.json. Added high traversal cost for several blocks such as chairs and tables. Tweak: Don't crash game if there is a syntax error in a dialogue file Tweak: structures.json now allows to set a MaxBelowSealevel. So sealevel - MaxBelowSealevel is the max height the schematic is allowed to spawn. MaxBelowSealevel defaults to 20 if not specified Tweak: structures.json minGroupDistance was limited to 46_340 now it can up to 3_037_000_498 blocks Tweak: Add "MinSpawnDistance" support for structures/ruins. Lets you define a certain distance constraint from the spawnpoint Tweak: Allow mods to define their own worldproperties/block/metal.json instead of needing to patch the file Tweak: Allow BlockBehaviorHarvestable to have multiple drops Tweak: Cooking system - allow modded meal names - Meals can now be created through just json files. Fixed: BlockEntityGenericTypedContainer should not require BEBehaviorAnimatable Code Refactor: Rename MultiBlockInteract.GetSounds to IMultiBlockInteract.MBGetSounds Refactor: Rename IClientWorldAccessor.Dimensions to IClientWorldAccessor.MiniDimensions Refactor: IMountableSeat.SuggestedAnimation now needs to return a AnimationMetaData instead of a string Refactor: Big rewrite in how entity inventories are coded. There is no longer a single placeholder entity.GearInventory field instead there is now a abstract class EntityBehaviorContainer that "container entity behaviors" can inherit from such as EntityBehaviorAntlerGrowth and the new EntityBehaviorDressable. This also necessitated the replacement of EntitySkinnableShapeRenderer by a new class EntityBehaviorTexturedClothing which inherits from EntityBehaviorContainer. Refactor: Clean up BEIngotMold and BEToolMold code Refactor: Entity.BodyYawLimits and EntityPlayer.BodyYawLimits and HeadYawLimits.HeadYawLimits are now of class AngleConstraint. Changed definition from min/max angle to center+range angle. Refactor: shape.SubclassForStepParenting() no longer prefixes a dash "-" to the texture codes Refactor: Block.ShouldPlayAmbientSound() is now obsolete, use GetAmbientsoundStrength() instead. Refactor: Add BlockPos argument to IDrawYAdjustable.AdjustYPosition() Refactor: Block.GetSounds(IBlockAccessor blockAccessor, BlockPos pos, ItemStack stack = null) is now Block.GetSounds(IBlockAccessor blockAccessor, BlockSelection blockSel, ItemStack stack = null) Refactor: Field InventoryBase.OnAcquireTransitionSpeed is now an event, i.e. use += to register to the event Refactor: Parts of BlockEntityContainer are now in a BE-agnostic class named "InWorldContainer", stored in the block entity field "container" Refactor: Rename IMountableSeat.CreateMountable() to IMountableSeat.GetMountable() Refactor: Rather major refactor of the player mounting system. Renamed IMountableSupplier to IMountable. The previous IMountable is now called IMountableSeat. The interface methods IMountableSupplier.IsMountedBy() and IMountableSupplier.IsBeingControlled() and are no longer required they are implemented by class extensions now. IMountableSupplier.MountPoints is now IMountable.Seats. Removed IMountableSupplier.GetMountOffset() and IMountable.MountPosition it is replaced by IMountableSeat.SeatPosition and IMountableSeat.RenderTransform(), which is a more intuitive, more concise and more powerful way of getting the player to the intended seating position, and with the right transforms. Refactor: Collectible.IsBackPack() is now obsolete. Use collectiblebehavior { name: "HeldBag" } instead Removed: EntityAgent.OnNoPath() and EntityBehavior.OnNoPath() method deleted they are not needed Tweak: Reserved characters for AssetLocation paths are listed in GlobalConstants.ReservedCharacterSequences (Detail: Textures only for now, as that is the main area where reserved characters should not be used. The game checks all mod filenames in the "textures/" path for these reserved characters at game startup, will throw an exception if found) Tweak: New particle property LightEmission Tweak: Most server side events are now more robust against crashing mods the event will continue to be executed on the remaining mods instead of stopping Tweak: Made IShaderProgram.AssetDomain available to mods Tweak: Clean up animation code related to loading the animator object. AnimationCache.InitManager() is now obsolete Tweak: new BlockFacing.HorizontalFromYaw() method to get facing from player yaw Tweak: Mainhand and offhand items can now apply any character stats via statModifier Tweak: Can now add a "whenSwimming: true/false" condition to AI tasks Tweak: Made henbox more moddable Tweak: Can now also associate sounds to an entity animation frame directly in the AnimationMetaData object Tweak: Made IMPPowered interface public Tweak: New ModSystem method: bool ShouldLoad(ICoreAPI api) Tweak: Added CollectibleObject.SetDurability() Tweak: entity.RenderColor is now an set-able property. Moved damage color overlay to BehaviorHealth.cs Tweak: Removed useless MeshData.AddVertexWithFlagsSkipTexture() which did not skip texture. :facepalm: Tweak: Simplified LinkTextComponent constructor to more compactly invoke link protocols, e.g. api.LinkProtocols["commandhandbook"].Invoke(new LinkTextComponent("commandhandbook://page-you-want-opened")); Fixed: Bug in meshdata.StepParentShape() related to animations caused a crash Fixed: shape.SubclassForStepParenting() did not subclass element names Fixed: Event AfterActiveSlotChanged simply crashing when triggered Fixed: Chat sub commands could not use a different privilege then the base command Feature: Add udp channels  similar to tcp channels using "api.Network.RegisterUdpChannel()" Feature: The Pathfinding system can now differentiate between types of creature and alter pathfinding behavior accordingly. Set via property aiCreatureType on the BehaviorTaskAI Config. See also enum file EnumAICreatureType Feature: Added Block.GetTraversalCost() method. Can be used to tell the pathfinding system how good or how bad a block is for traversal. Refactor: Make all internal fields and methods public in GameMain and ClientMain Refactor: Rewrite nametag handling. Code was a fat mess - all over the place. Now all in VSEssentials - BehaviorNameTag.cs Refactor: Moved engine coded SystemPlayerParticles to essentials mod ModSystemAmbientParticles Refactor: The event api.Event.OnTestBlockAccess, delegate argument "claimant" is now a ref, i.e. prefix it with "ref" in your code Tweak: BlockUpdate and DecorUpdate classes modified to accommodate subpositions (=cave art) Tweak: Added CollectibleBehavior.OnCreatedByCrafting Tweak: DiscDepositGenerator.absAvgQuantity is now public Tweak: Improve performance of SearchBlocks() by 2% Tweak: Added string<->AsseLocation conversion thing from Darkhekromant Tweak: Added interface ISearchTextProvider to provide custom text to the creative inventory search cache Tweak: Added EntityPlayer.HeadBobbingAmplitude property Tweak: Shape objects are now json serializable Tweak: Don't crash if a item/block is consumed/destroyed during OnHeldUseStep Tweak: Replaced ITextureAtlasAPI.LoadCompositeBitmap(AssetLocation path) with ITextureAtlasAPI.LoadCompositeBitmap(AssetLocationAndSource path) Tweak: BehaviorConversable now crashes if client side and server side dialogue path dont match Tweak: Readded Journal.DidDiscoverLore() method for a modder Tweak: Added damage type and damage tier to projectile for future use Tweak: IBlockAccessor.GetBlockRaw(x, y, z) method for use where x, y, z parameters are intentional and dimension-aware Tweak: Mark multiply tree as dirty after modifying totalDaysCooldownUntil Tweak: Moved IContainedMeshSource to getCollectibleInterface to support modded behaviors Fixed: If a custom InitWorldGenerator handler did throw an error subsequent handlers where skipped, now an exception is caught per handler Fixed: Number input gui element - up and down arrows not scaling well with the elementbounds Fixed: EntityPos.XYZFloat and EntityPos.XYZInt now take account of dimensions, same as EntityPos.XYZ Fixed: When patching files inside the worldgen folder (structures.json / storystructures.json , ...) those patches would be lost when running /wgen regen Other Feature: Entity Activity System and Ingame UI Editor. Open with `.dev aedit`. More info on this feature in another update Feature: Spherical fog system. More info on this feature in another update Feature: Improved dimensions support in the game engine and API. More info on all this in a future post Refactor: Unified Seraph and Trader head sizes and accessories Tweak: Added .debug dumpanimstate [entity selector] and /debug dumpanimstate [entity selector]. Helpful for investigating incorrect blending of animations Tweak: Added command ".debug sound list" to print all active sounds Tweak: Improved `.debug self` output. now prints animation weights per shape element for the player. Only in debug builds. Unborked armor stand again (broke due to creature inventory rewrite). Fixed seraph animation derps while riding. Tweak: Avoid game crashing / throwing exceptions on a variety missing vanilla blocks (rusty gear, boilingwater, default worldgen blocks) Fixed: Rare(?) crashes reporting a full texture atlas. (Technical info: RenderItemStackToAtlas did not create a new atlas if all others were full) Fixed: Fundamental design flaw: entityPlayer.Pos.Yaw was always off by 90° and being corrected in various places throughout the engine where such corrections dont belong, such as Vec3d.AheadCopy, EntityPos.GetViewVector() and EntityPos.AheadCopy. Those methods would yield wrong values for non-player entities. Fixed: Lod0Shape not applying alt textures Fixed: Should fix exception thrown in some cases when creating a large explosion Fixed: 3D Decors attached to chiseled blocks turned into a flat texture Fixed: .reload textures causing all climate/season colored geometry to disappear when lots of textures are loaded, e.g. via mods Fixed: Flaw in the texture loading system that caused white borders around distant / shallow angle geometry Fixed: Extremely slow world loading when a large amount of errors are thrown while in developer mode Tweak: /debug expclang now alphabetically sorted Tweak: Can now time-freeze (prevents temperature and spoilage changes) collectibles via /debug heldstattr timeFrozen bool true Tweak: Added logging of exceptions thrown on instantiating registry objects Tweak: Add ability to runtime toggle aitasks and activity system. Disable via /worldconfigcreate bool runAiTasks false and /worldconfigcreate bool runAiActivities false Tweak: Hacky fixed sounds with a high range not having a high range, until we can rewrite the sound attenuation model Tweak: Taller Dialogue dialog. Already used answers now in a different color (non-persistent at the moment), fixed not able to use VTML Tweak: Tapestry can now be set to preserve as pristine on placing down schematics. User /giveblock tapestry-north{type:"forlorn1",preserve:true} 1 Tweak: now all entity tasks, bows and spears have damage tiers for their projectiles Tweak: Add color blend mode OverlayCutout (dana PR) Tweak: Allow for more detailed Jam naming (pizza2004 PR) Tweak: Allow for modded buckets  (dana PR) Tweak: Don't hardcode barrel shapes  (dana PR) Tweak: Make barrels more moddable Tweak: Renamed 'The Seraph' music track, allowing it to be used in the music meta block for mods Tweak: expose waypoint data Tweak: entity.ServerPos and entity.Pos is now the same field, as we no longer need seperate positions on client and server removed in rc.8 ModMaker Tweaks Now allows for temporary assets to be created locally by copying an unmodified version of the game. Allows modmaking from pre-release versions Removes hardcoding for not allowing dev or other versions. Bug fixing for removing non-existent folders Fixed: Version 1 animation system not compatible with blender animations (thanks ketamine!) Fixed: Memory leak and heavy lag in some cases when using .tfedit Fixed: Entity variable of scope "entity" not working properly Fixed: Deleted almost all json duplicate keys in all assets Fixed: Block lighting bugs with lit schematics or lava Fixed: Raccoons harvesting berry bushes would only trigger the first harvestedStack. Fixed: Texture to index mapping now depends on file name 1.19 1.19.8 There are no API changes for this version. 1.19.6/1.19.7 Code API Fixed: RegisterEventBusListener() delegates called twice in some cases 1.19.5 Content Api Tweak: Items/Blocks can now define an attribute "inBarrelShape" to determine their shape inside a barrel Code Api Fixed: WorldAPI RegionMapSizeX would return a size off by one since game version 1.12.9. Affected methods are MapRegionIndex2D , MapRegionPosFromIndex2D , MapRegionIndex2DByBlockPos Api Fixed: Chatcommands with multiple `WithPreCondition` or a pre-condition and `RequiresPlayer` would over-write each other. Now all pre-conditions are checked and if one pre-condition returns EnumCommandStatus.Error it won't process the command further. Other Api Tweak: Don't crash on invalid GroundStack 1.19.4 Content Tweak: Added a new attachment point on the seraph model for off-hand animations Tweak: New and updated stackrandomizers are available to allow random loot to be placed in containers in schematics; and these randomizers can be modded to add or change loot in vanilla ruins: for details see the randomizers in assets/survival/itemtypes/meta Tweak: If an item specifies a renderVariant, and the shape cannot be found, log an error instead of crashing Tweak: Modded spears should now include behavior "AnimationAuthoritative", see vanilla spear.json for an example Tweak: Added entity property fallDamageMultiplier Notice: Entity boolean property fallDamage will become obsolete in v1.20 and removed in v1.21. Use fallDamageMultiplier: 0.0 instead Fixed: Block patches with NearSeaWater placement now detect salt water properly Fixed: Incorrect door and chest took on wrong appearance when animated in heavily modded games, due to multiple texture atlas creation, when using default texture atlas sizes Fixed: Microblocks not allowing 255 materials Code Additional minor tweaks relevant only to coded mods: KeyConfiguration used for hotkeys can now also represent a mouse button; mouse buttons are treated like keyboard keys with keycodes 240-247 For AITasks, the EntityPartitioning system is now divided more logically into Creatures and Inanimate entities, use EnumEntitySearchType to specify which For all string IndexOf(), StartsWith() or EndsWith() operations on strings representing game internal elements, for example AssetLocation, texture codes and other dictionary keys or anything found in JSON code, StringComparison.Ordinal should be used for culture independence. See .NET Best Practice Guide .  Vintagestory.API.Util now provides helper methods string.IndexOfOrdinal() , string.StartsWithOrdinal() and string.EndsWithOrdinal() which you can use; and for AssetLocations, ordinal comparison is built-in to new convenience method AssetLocation.PathStartsWith() Mods can access and change climbUpSpeed and climbDownSpeed in an entity's BehaviorControlledPhysics Marked UsingHeldItemTransformBefore/After as obsolete Exposed GetRecipeRegistry() in IWorldAccessor BlockEntity.GetBlockInfo() exceptions now trapped and logged, a simple error line is shown in the Block Info HUD Exceptions thrown by ticking a BlockEntity are now logged with position of entity and type; override BlockEntity.TickingExceptionHandler to change this behavior Exceptions thrown by BlockEntity.FromTreeAttributes() are now logged with position of entity and type Any mod directly accessing BlockEntityMicroBlock.sideAlmostSolid or .sidecenterSolid should re-compile for 1.19.4 (no actual code change in the mod should normally be required, but the field type changed, pull latest VSSurvivalMod source from GitHub) Notice: For coded mods using native binaries (.dll in Windows) in a /native folder, these will now not get loaded automatically (they would produce 'Bad IL format' errors at loading time anyhow). Applies to native binaries representing either libraries or a mod's own code not written in C#. Mods needing to load native binaries should instead write their own short DllImportResolver method [ example ] to locate their native binaries properly for each platform, and also include a static call to set that DllImportResolver [ example line of code ] Other Tweak: Missing dependencies now listed in the mod manager Tweak: Log an error if HeldRightReadyAnimation and HeldRightTpIdleAnimation for an item are set to the same animation 1.19.2/1.19.3 Code API Fixed: DidPlaceBlock() not called on the last block in the stack 1.19.1 There are no API changes for this version. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Debugging Content • API Updates • Modding Efficiently • Remapping Objects • Mod Compatibility • All Troubleshooting Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Modding_Basics_Portal TITLE: Modding:Modding Basics Portal --- CONTENT --- Other languages: English español русский українська Modding - First Steps Welcome to your first step into making mods for Vintage Story ! With the tools and knowledge that are available on this wiki, Vintage Story is a canvas ready for you to design and add whatever you want to it. For starters, take a look at some of these impressive mods that have been made by members of the modding community. We recommend playing with some mods before making them, so you can get a feel for the kind of things you want to add. Loads of mods are available on the Vintage Story Mod DB , and you'll learn how to upload your own mods there soon enough. Primitive Survival by SpearAndFang BetterRuins by NiclAss Banners by DanaCraluminum Bricklayers by Tels Exoskeletons by Maltiez Wolf Taming by Gerste Modding Basics - Portal Modding Basics Using The Modding Wiki Getting Started Mod Types Before moving on to actually creating mods, you should make sure you have read through the above articles! They will show you how to get the most out of the wiki and other resources, as well as how making mods works in Vintage Story . Also take a look at the Other Resources page for more modding info! Good luck on your modding journey! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Using the Modding Wiki • Getting Started • Types of Mod • Other Resources --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Modding_Efficiently TITLE: Modding:Modding Efficiently --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . Modding and game development usually requires lots of trial and error. The more time you can save on those iterations the better, as it accumulates quickly. Besides being quick to start up, VS offers a bundle of tricks to help you mod fast and efficiently. Here are some of the tricks every serious modder should use. Contents 1 Shortcuts 2 Finding Issues 3 When writing C# mods 3.1 Efficient Search Methods Shortcuts Use macros! Switching from/to creative/survival mode and /time set day bound to a keyboard shortcut is a must have. Hit CTRL+M to open the macro manager which will let you set those up! When you edit textures and shape files - these can be reloaded without restart using the commands .reload textures and .reload shapes . The latter may require you to place and remove a block so that the chunk gets redrawn Translation entries can be reloaded with .reload lang , the handbook can be reloaded with .debug reloadhandbook . Note that in 1.19.8, reloading lang files will not update the creative menu's search feature. If you're searching for an item and it doesn't come up, you should reload the world or search via its old name Set up a quickstart.bat script, that contains e.g. VintageStory.exe -oTestWorld -pcreativebuilding - this will launch you into a super flat creative world named "TestWorld" Do not fully restart the game to test out changes! In 95% of cases its enough to just leave the game world and rejoin or use the in game reload commands. You can quickly reload a world using the shortcut CTRL+F1 Leave your mod unpacked in the mods folder! No need to zip it up, it'll load up just fine unpacked Use .tfedit to modify the looks of your item/block inside the GUI, hands or on the ground - with a live preview Use .bsedit to edit block selection and collisionboxes in game - with a live preview. Note that this will only update selection boxes, but the values can be copied to your json file for collision boxes. Use the /debug expclang command to generate a neatly pre-formatted list of language entries destined for your en.json. It's placed in a file named collectiblelang.json in the same folder where VintageStory.exe is. It creates language entries for any block or item that currently has no translation. This can save you a ton of time by not needing to handwrite your translation file. Note that this does not create entries for anything other than blocks and items. If you modify a json file and want to include only the changes in your mod, you can create a JSON patch . Usually you can even save the time writing it yourself by using the ModMaker 3000™, a command line tool that ships with the game, to build the patches for you Finding Issues If you are stuck with an asset problem, look through the server-main.txt and client-main.txt! The engine is very verbose when it comes to asset errors. Permanently enable the error reporter via /errorreporter 1 to save yourself the work of finding and scouring through the log files for problems. This feature will make it so that a dialog pop ups during after up of the game if any errors were encountered. Note this is done on a per-world basis, so you will need to run this command on each world you test with. Also note that, as of 1.19.8, the 'Error Reporter' in the development settings does not currently function as expected. When writing C# mods Use break points for debugging Browse through the many utility classes provided by the VS API, you might be able to save a lot of coding efforts! (e.g. ColorUtil , GameMath , ArrayExtensions , DictExtensions , HashsetExtensions , JsonUtil , ReaderWriterExtensions , SerializerUtil , WildcardUtil ) The LINQ extension, part of the .net framework, is an extremely powerful tool to make code more expressive. Use the Hot reload feature of Visual Studio to modify code while the game is running, to see your changes immediately without restarting the world. Note that this feature works if you use the official mod template and set up your mod as a "compiled" mod. It does not work if you set up your mod as a "source" mod, and it may not work with all other development environment setups. If you have not already, make sure the game's log output ends up in the Visual Studio output window If you are working with shaders, you can reload them with .reload shaders Do not hold static references unless its primitive data. Static references are not garbage collected when the player leaves a server / game world. Example: Holding a static reference of a Block will keep that block in memory, which in turn keeps the API Instance in memory, which in turn keeps the entire game world in memory. The game client and server have a number of startup arguments to make your live easier Efficient Search Methods More often than not, you'd want your code to find something in the game world. As you might already know, computers are not exactly the best at parallel processing like human eyes are, so they have to sequentially look through the entire search space. When doing so, keep in mind that, first and foremost, the most efficient search method is one that never runs, within a search space that is of size zero. In other words, sometimes you can avoid searches altogether through other clever means! If you still need to do a search, here's some more optimal ways than the brute force way: Entity Search Use EntityPartitioning - api.ModLoader.GetModSystem().WalkEntityPartitions() . This system partitions entities into 8x8x8 block buckets through the entire loaded game world, then only searches inside those buckets, within your supplied search radius, greatly reducing the search space. Block Search a) If you want an entity to find a certain, not extremely common block, use the POI Registry . This registry lets block entities register a point of interest that entities can search through very efficiently. They are also partitioned into chunk column buckets to additionally reduce the search space. For a code sample, check out the berry bush block entity b) For regular full chunk scans, I recommend a separate background thread that loads the chunk and accesses the raw block data via chunk.Blocks . It is an array indexed with (y * chunksize + z) * chunksize + x , where the x, y, and z are local coordinates from 0 to chunkize-1. You should only write to a chunk in the main thread however! If you want to avoid threading for now, you can still iterate through chunk.Blocks in the main thread and even slice up the task across multiple ticks, like vanilla beehives do. c) For rare area scans, there is api.World.GetBlockAccessorPrefetch() . This block accessor lets you pre-load an area and then rather efficiently iterate through each block in given area Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Debugging Content • API Updates • Modding Efficiently • Remapping Objects • Mod Compatibility • All Troubleshooting Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Modding_Wiki_Guidelines TITLE: Modding:Modding Wiki Guidelines --- CONTENT --- Other languages: English русский Contents 1 Modding Wiki Contacts 2 Page Version Validation 3 Creating New Pages 3.1 Tutorial Pages 4 Categorize New Pages 5 Navigation The modding section of the wiki is a little different to the main portion of the wiki, and there are several guidelines that should be followed when editing this. Please read through all of this, but if you really want to be lazy, just read through the bold sections . Modding Wiki Contacts The modding wiki is actively maintained by Nat . If you have any questions about anything in the modding wiki, please contact me on Discord. It's @Nat, with the picture of a puppy. When this page uses 'I' or 'me', then it means Nat. Page Version Validation Due to the everchanging nature of modding, it is important to keep pages up-to-date and verified. Whilst help on maintaining pages is appreciated, please do not worry about doing this. I have a list of every page to verify, and I will validate each page regardless of whether someone else has done it. Also, please do not update pages to any pre-release or release candidate versions. Creating New Pages To help the organization, maintainability, and readibility of the modding wiki, there are a few rules that every page needs to abide by. Ensure that the information you are writing does not already exist elsewhere on the wiki. New pages must be in the modding namespace . Just include " Modding:" before the name of the page when you create it. Pages should be focused on a single topic . Any pages which focus on more than one topic or concept should be split into multiple pages. Pages should be specific to a single mod type. Pages which discuss content modding should not contain any code. If you need to do this, make two new pages. Pages should be created in line with the most stable version . All pages should be categorized with at least the default 'Modding' category. Tutorial Pages A good portion of the modding wiki are tutorials. Tutorials should follow these rules: Tutorials should mostly follow a pattern of do, analyse, experiment. Guide the reader through how to do something, then analyse how it works, then get the reader to experiment with what they have done. This follows the idea of inquiry-based learning , to provide new modders with the tools they need to understand what they are doing. All tutorials should begin with an "Objective", and then "Prerequisites" . The prerequisites section can simply be "It is recommended that you have completed the 'Basic Code Tutorials' before starting this tutorial.", or some such text. Keep in mind the audience of your tutorial. Is it a tutorial for beginners, or more rehearsed modders? Do not include information that will not be understood by your audience. New pages MUST be added to the 'New Pages' modding category. This will allow me to find them and add them to the relevant navigation boxes and lists. See the 'Categorize New Pages' section below. Categorize New Pages Please, please, please categorize your new pages using language-specific category definitions. Uncategorized pages will not be maintained and will lead to inconsistencies in the wiki. To add a page to a category, copy and paste the category codes below which are relevant to the very bottom of the page. You will need to use the source editor to do this. All modding pages should be added to the Modding category, and any other relevant categories. The following categories should be used: [[Category:Modding{{#translation:}}]] - For all pages. [[Category:Modding:Content{{#translation:}}]] - For any pages which discuss content mods. [[Category:Modding:Content Guides{{#translation:}}]] - For any pages which give information about content topics. These are not tutorials, but informational guides. [[Category:Modding:Community Content Tutorials{{#translation:}}]] - For any pages which are content tutorials. Please ensure that your tutorials meet the requirements in the ' Creating New Pages' section. [[Category:Modding:Code{{#translation:}}]] - For any pages which discuss code mods. [[Category:Modding:Code Guides{{#translation:}}]] - For any pages which give information about code topics. These are not tutorials, but informational guides. [[Category:Modding:Community Code Tutorials{{#translation:}}]] - For any pages which are code tutorials. Please ensure that your tutorials meet the requirements in the ' Creating New Pages' section. [[Category:Modding:Model Creator{{#translation:}}]] - For any pages which discuss how to use the VS Model Creator. Please note that there are some official tutorials coming soon to this category. [[Category:Modding:Troubleshooting{{#translation:}}]] - For any pages which discuss troubleshooting mod issues, and generally improving modding efficiency and tactics. [[Category:Modding:VS Engine{{#translation:}}]] - For pages which discuss some more complexities about how the game engine works. We're talking pages such as rendering methods, function execution orders, and other complex discussions. There are a few more categories, but these have specific requirements before being added. Please contact Nat if you think a page should be included in one of these categories: [[Category:Modding:Asset Types{{#translation:}}]] - For pages that give specific details on the asset types for content mods. These need to include certain details. [[Category:Modding:Basics{{#translation:}}]] - For pages that give details for new modders, usually explaining how modding in VS works. These should be very beginner friendly. [[Category:Modding:Fundamental Content Tutorials{{#translation:}}]] - For specific content tutorials. These are essentially tutorials which follow a very structured pattern, and would introduce a single and important concept to content mods. See the existing fundamental tutorials for more information. [[Category:Modding:Fundamental Code Tutorials{{#translation:}}]] - For specific code tutorials. These are essentially tutorials which follow a very structured pattern, and would introduce a single and important concept to code mods. See the existing fundamental tutorials for more information. New pages MUST be added to the 'New Pages' modding category. This will allow me to find them and add them to the relevant navigation boxes and lists. [[Category:Modding:New Pages{{#translation:}}]] - For all new modding pages. If this is not included, then expect your page to barely get found. Navigation Yes, okay, the navigation on the modding wiki is a nightmare as of writing this. I'm working on it. To try and improve the system, please do not edit any navigation boxes . Add your page to the 'new pages' category, and I will sort them out and add them to the relevant navigation boxes and lists. If you have any brilliant ideas on how to improve the navigation system, please let me know! Wondering where some links have gone? The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages. Modding Modding Introduction Getting Started • Theme Pack Content Modding Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts Code Modding Code Mods • Setting up your Development Environment Property Overview Item • Entity • Entity Behaviors • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • Collectible Behaviors • World properties Workflows & Infrastructure Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine Additional Resources Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Model_Creator_Portal TITLE: Modding:Model Creator Portal --- CONTENT --- Other languages: English русский Welcome to the model creator portal! The pages found here will give you an understanding of how to use the Vintage Story Model Creator , found here . VSMC Pages Many VSMC pages are being rewritten, and new official tutorials are being worked on! Please check back soon! Model Creator Basics Tutorials Model Animation Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Model Creator Basics • Tutorials • Model Animation • All VSMC Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Modinfo TITLE: Modding:Modinfo --- CONTENT --- Other languages: English español русский This page was last verified for Vintage Story version 1.19.8 . Overview Every mod needs some basic information to be recognized as a mod. This can either happen by putting this info into the assembly (not recommended anymore) or by using the modinfo.json. The modinfo.json sits at the root of your mod directory and looks for example like this: { "type" : "code" , "modid" : "mycoolmod" , "name" : "My Cool Mod (very cool)" , "authors" : [ "SakuraSpiritKid69" ], "description" : "Mod that is so cool it freezes you." , "version" : "1.2.3" , "dependencies" : { "game" : "1.14.10" , } } Reference The following properties and values are supported. Bold values are required. Properties are not case sensitive and may appear in any order. Property Example Description type "type": "code" The type of this mod. Can be "Theme", "Content" or "Code" name "name": "My cool Mod" The name of this mod. modid "modid": "mycoolmod" The mod id (domain) of this mod. Must only contain lowercase letters and numbers. No special chars, whitespaces, uppercase letters, etc. allowed. version "version": "1.2.3" Version of this mod (used for dependency resolution) networkVersion "networkVersion" : "1.2.3" The network version of this mod. Change this number when a user that has an older version of your mod should not be allowed to connected to server with a newer version. Default value is the version. textureSize "textureSize" : 32 If the mod is a texture pack that changes topsoil grass textures, define the texture size here. Default value = 32 description "description": "Mod that is so cool it freezes you." A short description of what this mod does. website "website": "https://wiki.vintagestory.at" Location of the website or project site of this mod. authors "authors": ["SakuraSpiritKid69"] Names of people working on this mod. Must be formatted as an array, even if there is only one author. contributors "contributors": ["noone"] Names of people contributing to this mod. side "side" : "Universal" Which side(s) this mod runs on. Can be "Server", "Client" or "Universal". Default value = "Universal" requiredOnClient "requiredOnClient": true If set to false and the mod is universal, clients don't need the mod to join. Default value = True requiredOnServer "requiredOnServer": true If set to false and the mod is universal, the mod is not disabled if it's not present on the server. Default value = True dependencies "dependencies": { "game": "1.12.14", "survival": "1.12.14"} List of mods (and versions) this mod depends on. The game uses SemVer to compare, so for example "1.15.0" > "1.15.0-rc.3" > "1.15.0-rc.2" > "1.15.0-pre.1". You can set the value to an empty string or an asterisk ("game": "*") to allow compatibility with all versions. Although the game follows the general logic of SemVer, it does not fully implement it. So for you can only use rc > pre > dev and is not supported. Technical Note The modinfo.json properties correspond directly to the fields and properties on the ModInfo API class (and are loaded into that class internally using JsonConvert.DeserializeObject ). Check the available fields in the api documentation to see a full up to date list of all possible properties. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Monkey_patching TITLE: Modding:Monkey patching --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Monkey patching (changing code at runtime) in Vintage Story is available through Harmony . With Harmony you can change methods, constructors and properties. Full documentation on harmony is available here. . It is recommended that you use dnSpy on any dlls or mods you wish to patch, rather than viewing the source code on GitHub. This is because there is often more in the dlls than can be found on GitHub. On Linux, this executable can be run through wine. Contents 1 Getting Started 2 Basic Patching 3 Transpilers 4 Cleaning Up 5 Remarks Getting Started To use Harmony in you Vintage Story mod you first have to reference the "0Harmony.dll" file in the Vintage Story "Lib" directory (VintageStory\Lib\0Harmony.dll) in your project. Then, in whatever Start() method you use, you have to create a Harmony-instance by calling the constructor with your unique identifier, preferrably your modid (this lets harmony differentiate between different patches and allows other people to patch your patches). var harmony = new Harmony ( "MyModId" ); Now you are all set to patch everything inside the game like described in the documentation. Basic Patching The simplest and easiest way to patch code is to run harmony.PatchAll() after initializing harmony with the constructor, and to put your patches into various "multi-patch classes". To mark a class as one of these multi-patch classes, put the [HarmonyPatch] annotation at the top. [HarmonyPatch] // Place on any class with harmony patches public class MyModSystem : ModSystem { public static ICoreAPI api ; public Harmony harmony ; public override void Start ( ICoreAPI api ) { MyModSystem . api = api ; // The mod is started once for the server and once for the client. // Prevent the patches from being applied by both in the same process. if (! Harmony . HasAnyPatches ( Mod . Info . ModID )) { harmony = new Harmony ( Mod . Info . ModID ); harmony . PatchAll (); // Applies all harmony patches } } // ... Harmony patch functions here Inside a multi-patch class, add [HarmonyPrefix] , [HarmonyPostix] , and [HarmonyPatch] annotations to tell functions to run before or after a certain in-game function is run. A prefix will run the function before the original function specified by [HarmonyPatch] , while a postfix will run the function after it. These functions should be marked public static void or, if you're writing a prefix and would like to prevent the original function from running, use public static bool and return false to cancel the original ( return true to run the original anyway). Every patch function can be given parameters with the same names as the original, or special names with double underscores at the start. For a full list of special names parameters can have, see this page of the harmony documentation . This function will print "Block {block code} at {position} is about to break!" to the console whenever a block in the world is about to break. [HarmonyPrefix] [HarmonyPatch(typeof(Block), nameof(Block.OnBlockBroken))] // Note that the name of the function does not matter public static void BlockBroken ( Block __instance , BlockPos pos ) { // For methods, use __instance to obtain the caller object api . Logger . Event ( "Block {0} at {1} is about to break!" , __instance . Code , pos . ToString ()); } Changing it to a postfix will instead run the function after the block has been destroyed. [HarmonyPostfix] [HarmonyPatch(typeof(Block), nameof(Block.OnBlockBroken))] // Note that the name of the function does not matter public static void AfterBlockBroken ( Block __instance , BlockPos pos ) { // For methods, use __instance to obtain the caller object api . Logger . Event ( "Block {0} at {1} has been broken!" , __instance . Code , pos . ToString ()); } Or, by changing the prefix to return false, we can make blocks never break. [HarmonyPrefix] [HarmonyPatch(typeof(Block), nameof(Block.OnBlockBroken))] // Note that the name of the function does not matter public static bool AfterBlockBroken () { return false ; } Transpilers Transpilers are a special kind of patch that allow you to change a small part of the code of a function, without canceling the whole thing with a prefix and rewriting it. To understand transpilers, you first have to understand IL code. IL code is what C# compiles into, and is what is stored and run in any .NET executable or .NET dll files. It's an assembly-like language which uses a stack to manage data. Instructions generally will pop data from the stack, perform some calculation on that data, then push the result back onto the stack. For a full list of IL instructions, look at the MS docs . To view the IL code of any in-game function, use a disassembler such as dnSpy . When you know what instruction(s) you want to change, add, or delete, you'll want to use the [HarmonyTranspiler] annotation in combination with a CodeMatcher object to locate and modify the IL code. You could also yield return instructions manually, though that is far more time consuming. For a full list of CodeMatcher methods, see the harmony documentation on the CodeMatcher object . This method patches the crash handler to include a little watermark. [HarmonyTranspiler] [HarmonyPatch(typeof(CrashReporter), "Crash")] public static IEnumerable < CodeInstruction > Crash ( IEnumerable < CodeInstruction > instructions , ILGenerator generator ) { return new CodeMatcher ( instructions , generator ) // This method finds the first time a {ldstr "Game Version: "} instruction occurs. // It then places the cursor at the start of it. . MatchStartForward ( new CodeMatch ( OpCodes . Ldstr , "Game Version: " )) // This method replaces the operand of the instruction that starts at the cursor. // Specifically, it replaces the string "Game Version: " with a new string including some extra text. . SetOperandAndAdvance ( "Hello from the wiki!" + Environment . NewLine + "Game Version: " ) // This method finalizes our changes with the methods above. . InstructionEnumeration (); } Cleaning Up For good practice, if you patch the game with your mod, make sure you unpatch it when your mod unloads. The easiest way to do this is to run harmony.UnpatchAll("MyModId") in the Dispose method of a ModSystem. Make sure you include the id in the call to UnpatchAll() , as otherwise you may accidentally unpatch other mods before they expect. public override void Dispose () { harmony ?. UnpatchAll ( Mod . Info . ModID ); } } Remarks Just because you can use monkey patching, does not mean you should. Patches are often fragile and break easily (especially if you access internal private methods). So if you can use normal means of modding you probably should. You can't use harmony to patch fields (because they are not called), so in order to change field access you have to patch all calls to this field with patches. You can patch getters and setters, if they are available. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:NatFloat TITLE: Modding:NatFloat --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . A somewhat more natural random number generator. The main difference to pure random is that you can define a distribution to influence how often certain values are chosen. Arguments avg : Average value var : Variation dist : Distribution type Overview - Distribution modes Name Explanation uniform Select completely random numbers within avg-var until avg+var (the default distribution). triangle Select random numbers with numbers near avg being the most commonly selected ones, following a triangle curve. gaussian Select random numbers with numbers near avg being the most commonly selected ones, following a gaussian curve. narrowgaussian Select random numbers with numbers near avg being the most commonly selected ones, following a narrow gaussian curve. verynarrowgaussian Select random numbers with numbers near avg being the most commonly selected ones, following a very narrow gaussian curve. narrowinversegaussian Select random numbers with numbers near avg being the least commonly selected ones, following an upside down gaussian curve. inversegaussian Select random numbers with numbers near avg being the least commonly selected ones, following an upside down gaussian curve. narrowinversegaussian Select random numbers with numbers near avg being the least commonly selected ones, following an upside down narrow gaussian curve. invexp Select numbers in the form of avg + var, wheras low value of var are preferred. stronginvexp Select numbers in the form of avg + var, wheras low value of var are strongly preferred. strongerinvexp Select numbers in the form of avg + var, wheras low value of var are very strongly preferred. dirac Select completely random numbers within avg-var until avg+var only ONCE and then always 0. Here are some plots for these distributions. They are created by generating 8000 random numbers between 0 and 200, where the X-Axis is the resulting value and the Y-Axis the amount of times this value was chosen. Translated to the Natfloat arguments the horizontal center point in these graphs denote the "avg" value, the width of the plot is determined by the "var" value. In json, the uniform distribution would be in the written as such: { avg: 100, var: 100, dist: "uniform" } See also EvolvingNatFloat Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Network_API TITLE: Modding:Network API --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Other languages: English русский Before starting, you should have a development environment set up. If you don't have one already you should read the tutorial Setting up your Development Environment . Furthermore, we assume that you have a basic understanding of the C# language and Object Oriented Programming. Let's get started! Hint : If you need to send data for Entities or BlockEntities, it may be more appropriate to use the inbuilt network methods for these. Check out the IServerNetworkAPI and IClientNetworkAPI interfaces for more information. Contents 1 Introduction 2 Preparation 3 ProtoContracts 4 NetworkApiTest 5 Server Setup 6 Client Setup 7 Handling Client Responses 8 Conclusion 9 Testing Introduction In this example mod we will show you how to send custom data back and forth between the Server and the Client. To do this, we will set up a network channel through which string messages will be sent in a network channel. We will create a server command that will send out a message to all clients, and if a client receives this, it will send a response back to the server; upon receiving this response, the server will display it along with the responding client's player name. If something goes wrong, Wireshark with the vs-protocol dissector is a helpful debug tool. Preparation Let's start by creating a new .cs file for this mod, and adding our imports and the namespace in which we'll wrap all of our classes: using ProtoBuf ; using Vintagestory.API.Client ; using Vintagestory.API.Common ; using Vintagestory.API.Config ; using Vintagestory.API.Server ; namespace Vintagestory.ServerMods { } ProtoContracts VintageStory uses multiple serialization formats . This tutorial uses the protobuf format. The Protobuf-net library can serialize classes declared with the ProtoContract attribute. Inside our namespace block, let's create 2 data packets using ProtoContract : [ProtoContract] public class NetworkApiTestMessage { [ProtoMember(1)] public string message ; } [ProtoContract] public class NetworkApiTestResponse { [ProtoMember(1)] public string response ; } The argument we pass to ProtoMember is a unique unsigned integer called a tag, and it's used to identify the fields flagged for serialization. When the client receives the packet containing the serialized ProtoContract , it won't know the type of its members, and will identify them by the tag number. We could also use the class header [ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] and skip all [ProtoMember] tags to flag all public fields for serialization, then tags are assigned in alphabetic order. ProtoBuf is beyond the scope of this tutorial, but you can read more about this powerful library here . The fields in these classes hold the data we wish to send through our channel. We'll use NetworkApiTestMessage to encase the initial message from the server, while NetWorkApiTestResponse will be used by the Client to send its response. NetworkApiTest Now we will create our main class that inherits from the ModSystem class, which is the base class for mod systems. You can read more about this here . Moreover, we'll be separating our Client and Server sides within the class with region blocks for readability purposes. public class NetworkApiTest : ModSystem { public override void Start ( ICoreAPI api ) { api . Network . RegisterChannel ( "networkapitest" ) . RegisterMessageType ( typeof ( NetworkApiTestMessage )) . RegisterMessageType ( typeof ( NetworkApiTestResponse )) ; } #region Client // Client side here #endregion #region Server // Server side here #endregion } The Core API contains the Network property, which provides us with the RegisterChannel method. We pass this method a string containing the name we choose for our channel, in this case we'll call our channel "networkapitest". The code in Start() is executed on the server as well as the client. We now have two connected endpoints. The system still needs to know what kind of packages you will want to send. We do this with the RegisterMessageType method. Server Setup Next, we'll set up our network channel on the Server side, and register a server command to dispatch our message. #region Server IServerNetworkChannel serverChannel ; ICoreServerAPI serverApi ; public override void StartServerSide ( ICoreServerAPI api ) { serverApi = api ; serverChannel = api . Network . GetChannel ( "networkapitest" ) . SetMessageHandler < NetworkApiTestResponse >( OnClientMessage ) ; api . ChatCommands . Create ( "nwtest" ) . WithDescription ( "Send a test network message" ) . RequiresPrivilege ( Privilege . controlserver ) . HandleWith ( new OnCommandDelegate ( OnNwTestCmd )); } #endregion We call SetMessageHandler on our channel, which takes a delegate that will be called every time when the given type of Packet is received from a client, in this case NetworkApiTestResponse will be handled by OnClientMessage which we'll declare later. Now that our channel is fully set up on the Server side, we'll want to register a server command that will broadcast a network message to all the clients listening to our "networkapitest" channel. #region Server IServerNetworkChannel serverChannel ; ICoreServerAPI serverApi ; public override void StartServerSide ( ICoreServerAPI api )... private TextCommandResult OnNwTestCmd ( TextCommandCallingArgs args ) { serverChannel . BroadcastPacket ( new NetworkApiTestMessage () { message = "Hello World!" , }); return TextCommandResult . Success (); } #endregion The server channel gives us the BroadcastPacket method which we'll use to send out a NetworkApiTestMessage instance to all clients listening to the "networkapitest" channel, in this case we send "Hello World!" as the message. Client Setup Above the Server Side region, we'll create our Client fields and an override for the StartClientSide , which is a Client side version of its Server side counterpart. #region Client IClientNetworkChannel clientChannel ; ICoreClientAPI clientApi ; public override void StartClientSide ( ICoreClientAPI api ) { clientApi = api ; clientChannel = api . Network . GetChannel ( "networkapitest" ) . SetMessageHandler < NetworkApiTestMessage >( OnServerMessage ) ; } #endregion Also on the client we call SetMessageHandler to listen for messages of type NetworkApiTestMessage , using a delegate named OnServerMessage . #region Client IClientNetworkChannel clientChannel ; ICoreClientAPI clientApi ; public override void StartClientSide ( ICoreClientAPI api )... private void OnServerMessage ( NetworkApiTestMessage networkMessage ) { clientApi . ShowChatMessage ( "Received following message from server: " + networkMessage . message ); clientApi . ShowChatMessage ( "Sending response." ); clientChannel . SendPacket ( new NetworkApiTestResponse () { response = "RE: Hello World!" }); } #endregion Here we simply show a chat message to the client with the contents of the message sent from the server. We then let the client know that we're sending our response to the server, and call SendPacket passing it an instance of NetworkApiTestResponse . Now let's go back to the Server side and create the handler for this response! Handling Client Responses In our Server side region, let's write a final delegate method: #region Server IServerNetworkChannel serverChannel ; ICoreServerAPI serverApi ; public override void StartServerSide ( ICoreServerAPI api )... private void OnNwTestCmd ( IServerPlayer player , int groupId , CmdArgs args )... private void OnClientMessage ( IPlayer fromPlayer , NetworkApiTestResponse networkMessage ) { serverApi . SendMessageToGroup ( GlobalConstants . GeneralChatGroup , "Received following response from " + fromPlayer . PlayerName + ": " + networkMessage . response , EnumChatType . Notification ); } #endregion Message handlers on the server have an additional parameter compared to client handlers, which is the player whose client sent the message. In this handler we simply broadcast a server-wide message containing the responding player's name and the content of the response. Conclusion If you followed the steps correctly, you should have the following code: using ProtoBuf ; using Vintagestory.API.Client ; using Vintagestory.API.Common ; using Vintagestory.API.Config ; using Vintagestory.API.Server ; namespace Vintagestory.ServerMods { [ProtoContract] public class NetworkApiTestMessage { [ProtoMember(1)] public string message ; } [ProtoContract] public class NetworkApiTestResponse { [ProtoMember(1)] public string response ; } /// /// A basic example of client<->server networking using a custom network communication /// public class NetworkApiTest : ModSystem { public override void Start ( ICoreAPI api ) { api . Network . RegisterChannel ( "networkapitest" ) . RegisterMessageType ( typeof ( NetworkApiTestMessage )) . RegisterMessageType ( typeof ( NetworkApiTestResponse )) ; } #region Server IServerNetworkChannel serverChannel ; ICoreServerAPI serverApi ; public override void StartServerSide ( ICoreServerAPI api ) { serverApi = api ; serverChannel = api . Network . GetChannel ( "networkapitest" ) . SetMessageHandler < NetworkApiTestResponse >( OnClientMessage ) ; api . ChatCommands . Create ( "nwtest" ) . WithDescription ( "Send a test network message" ) . RequiresPrivilege ( Privilege . controlserver ) . HandleWith ( new OnCommandDelegate ( OnNwTestCmd )); } private TextCommandResult OnNwTestCmd ( TextCommandCallingArgs args ) { serverChannel . BroadcastPacket ( new NetworkApiTestMessage () { message = "Hello World!" , }); return TextCommandResult . Success (); } private void OnClientMessage ( IPlayer fromPlayer , NetworkApiTestResponse networkMessage ) { serverApi . SendMessageToGroup ( GlobalConstants . GeneralChatGroup , "Received following response from " + fromPlayer . PlayerName + ": " + networkMessage . response , EnumChatType . Notification ); } #endregion #region Client IClientNetworkChannel clientChannel ; ICoreClientAPI clientApi ; public override void StartClientSide ( ICoreClientAPI api ) { clientApi = api ; clientChannel = api . Network . GetChannel ( "networkapitest" ) . SetMessageHandler < NetworkApiTestMessage >( OnServerMessage ) ; } private void OnServerMessage ( NetworkApiTestMessage networkMessage ) { clientApi . ShowChatMessage ( "Received following message from server: " + networkMessage . message ); clientApi . ShowChatMessage ( "Sending response." ); clientChannel . SendPacket ( new NetworkApiTestResponse () { response = "RE: Hello World!" }); } #endregion } } Testing Let's run the mod now! Once you're ingame, try entering /nwtest . You should be met with an initial "Hello World!" message as well as a confirmation message that the server received the response from the Client! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Other_Code_Tutorials TITLE: Modding:Other Code Tutorials --- CONTENT --- Other languages: English русский This is the landing page for the miscallaneous code tutorials. These will cover a wide variety of code modding topics, of varying complexity. Other Tutorials These tutorials are currently in progress! Please check back later! ModConfig Old Tutorials These tutorials are quite old, and most are due to be rewritten. Please use with caution! Block Behaviors Block Entities Chunk Moddata Code Asset Patching Commands Harmony Patching Inventory Handling Item Interactions Moddable Mod Network API Saving Mod Data Setting up your Dev Environment Simple Particles World Access WorldGen API Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Other_Content_Tutorials TITLE: Modding:Other Content Tutorials --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . This page is the landing page for the miscellaneous content tutorials. These often cover topics that solve specific tasks, and may be referenced from other tutorials. Other Tutorials Nothing here yet! Why don't you have a read of the Modding Wiki Guidelines and consider making a tutorial to put here? Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Empty --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Other_Resources TITLE: Modding:Other Resources --- CONTENT --- Other languages: English русский There are a number of resources that can help with modding the game. Some of these are maintained by the community, and others are official programs and documentation. Wiki Resources Community Resources - A list of community-made resources that may help with modding. Programming Languages - A variety of tutorials to help you learn the required languages for modding. Inbuilt ModMaker - A tutorial on how to use the inbuilt mod maker program. External Resources AnegoStudios Mod Examples - A GitHub repo of mod examples created by Anego Studios. Note that some of these are quite dated. Nat's Mod Examples - A GitHub repo of mod examples created by Nat. These are maintained and kept up-to-date with the current game version, and many of the wiki tutorials use these. Code API Docs - An auto-generated documentation for the available Vintage Story APIs. JSON/Content Docs - An auto-generated documentation for many JSON asset definitions. Includes required, recommended, and optional properties for a lot of the moddable assets. AnegoStudios GitHub - The AnegoStudios GitHub page. Contains a number of repositories that can be searched to help with modding. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Using the Modding Wiki • Getting Started • Types of Mod • Other Resources "" Community Resources • Programming Languages • Inbuilt ModMaker AnegoStudios Mod Examples • Nat's Mod Examples • Code API Docs • JSON/Content Docs • AnegoStudios GitHub --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Preparing_For_Code_Mods TITLE: Modding:Preparing For Code Mods --- CONTENT --- Other languages: English This page was last verified for Vintage Story version 1.19.8 . Contents 1 Installing an IDE 2 Installing .NET 7.0 SDK 3 Creating an Environment Variable 4 Installing Modding Templates 4.1 Test your template installation 5 Next Steps There is a small number of steps that must be followed before you can create a code mod. These steps only have to be done once - If you have completed this tutorial before, you may start from Creating a Code Mod . For more information on what can be achieved with a code mod, see Code Mods . This tutorial is suitable for Windows users . If you are using a differing operating system, please view the older Setting up your Development Environment tutorial page. Installing an IDE When developing a code mod, it is extremely important to choose a suitable IDE. For Vintage Story, it is highly recommended that you use Visual Studio Community - A free and powerful development environment that is used by the Vintage Story team to develop the game. When installing Visual Studio, you should also install the .NET desktop development workload, which can be done during installation or by following these steps . Although some other IDEs are suitable, this tutorial will assume you are using Visual Studio. If you cannot use Visual Studio, please view the older Setting up your Development Environment tutorial page. Installing .NET 7.0 SDK An SDK (Software Development Kit) is used to help you develop programs and code for specific platforms. Since Vintage Story relies on the .NET 7.0 SDK , it is important that you download and install this too. This SDK is sometimes referred to as dotnet. Download and install the latest .NET 7.0 SDK , ensuring you select the version that is correct for you. This will likely be the Windows x64 version. When the SDK has finished installing, it is recommended to check it has been configured correctly. Launch a command prompt in Windows, either by searching for ' cmd ' or ' PowerShell ' in Windows' search bar, and enter the following command: dotnet - -list-sdks This should give you a list of installed SDKs. If there is an entry that starts with 7.0., then you may continue. SDK not showing or getting an error? Restart your PC, and redo the given command in the command prompt or powershell. If it is still not visible, then repeat the installation for this section. Creating an Environment Variable An Environment Variable is a value that can be used by numerous applications across your PC. Because the modding environment is separate to the game itself, you need to create a new environment variable that links to the path of your game. Firstly, you need to open Vintage Story in your file explorer. If you have a shortcut to Vintage Story on your Desktop, you can right-click it and select ' Open file location '. If you do not have a shortcut, but have installed Vintage Story to the default install location, it will be found in your appdata folder. You can find this folder by using this method . If you have installed it elsewhere, you will need to find this folder in the file explorer. The opened folder should contain the ' assets ', ' mods ', and ' lib ' folders. Ensure you are not in the VintageStoryData folder. When you have your game folder open in Windows file explorer, you need to open this folder in PowerShell. Ensure you do not have a file or folder selected, and click 'File' in the menu bar, then click 'Open Windows PowerShell'. PowerShell will open with your current file path. Ensure that the file path stated does not include the ' assets ', ' mods ', or ' lib ' folders. Paste the following line into PowerShell, and press enter to run it. [Environment] :: SetEnvironmentVariable ( "VINTAGE_STORY" , ( $pwd . path ), "User" ) This will create the environment variable called "VINTAGE_STORY", and set its value to be the current working directory (the Vintage Story folder). The "user" parameter simply stores this variable as a user-only environment variable, meaning other users on your PC will not be able to use this variable. Test your environment variable To ensure your environment variable has been created correctly, open a new PowerShell instance and run the following command: echo $env:VINTAGE_STORY This should print the full path to your vintage story directory. If it does not, then repeat this section. When you have set a new environment variable, it is recommended to restart your PC. This will ensure that all programs are using your updated environment variables. Installing Modding Templates The final step is to install the mod templates. These mod templates will automatically setup your mod for you in Visual Studio, automatically adding any libraries and code that is needed to get your mods up and running. Open a PowerShell command line, paste the following, and press enter to run it. This execution should only take a few seconds, as it is a very small file. dotnet new install VintageStory . Mod . BasicTemplate This is where everything comes together. Your dotnet installation will download the Vintage Story mod template from NuGet (an online package manager for .NET), and install the templates to be used with Visual Studio. Looking for the advanced modding template, or want to make a dll-only mod? Since May 2024 , the template listed above will install only the basic mod template - Which automatically imports all relevant libraries. If you need to select which libraries to import, or you want to make a dll-only mod, you will need to install the advanced templates by entering the following command: dotnet new install VintageStory . Mod . Templates Test your template installation To ensure that the template has installed correctly, launch Visual Studio and select 'Create a new project'. In the furthest-right drop down list (' All project types' ), find 'Vintage Story' in the list. If it exists, and displays two projects, then your template has successfully installed and you are ready to start making code mods. Congratulations! If 'Vintage Story' does not exist in the project types, or there are no projects listed under this category, try restarting your PC and looking again. If there is still nothing, run the above command again. Still can't find the project type? You may have forgot to install the .NET desktop development workload through the Visual Studio installer. Launch the Visual Studio Installer, select modify, and tick the box labeled .NET desktop development (under the 'Desktop & Mobile' section). Next Steps Now that you have completed this tutorial, you can begin creating as many code mods as you wish. Move on to Creating a Code Mod to setup and customize your first mod! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Code Mods • Preparing For Code Mods • Creating A Code Mod --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Programming_Languages TITLE: Modding:Programming Languages --- CONTENT --- Other languages: English русский Vintage Story is primarily written in C# with OpenGL as the graphics backend. Learning OpenGL is only necessary if you want to do special rendering tricks, and learning C# is necessary if you want to go beyond asset modding. Here are a few learning resources that we can recommend: For C# https://www.w3schools.com/cs/index.php (Interactive non-video course, does not require account) https://www.youtube.com/user/IAmTimCorey (Beginner - Intermediate) https://www.youtube.com/c/RawCoding (Intermediate - Advanced) https://www.youtube.com/c/Elfocrash (Advanced - Expert) For OpenGL, and Shaders Playlist by ThinMatrix: https://www.youtube.com/watch?v=VS8wlS9hF8E&list=PLRIWtICgwaX0u7Rf9zkZhLoLuZVfUksDP Playlist by TheCherno: https://www.youtube.com/watch?v=W3gAzLwfIP0&list=PLlrATfBNZ98foTJPJ_Ev03o2oq3-GGOS2 https://www.youtube.com/watch?v=kfM-yu0iQBk&list=PLImQaTpSAdsCnJon-Eir92SZMl7tPBS4Z Maths for Game Devs https://www.youtube.com/watch?v=MOYiVLEnhrw&list=PLImQaTpSAdsD88wprTConznD1OY1EfK_V Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Using the Modding Wiki • Getting Started • Types of Mod • Other Resources "" Community Resources • Programming Languages • Inbuilt ModMaker AnegoStudios Mod Examples • Nat's Mod Examples • Code API Docs • JSON/Content Docs • AnegoStudios GitHub --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Rendering_API TITLE: Modding:Rendering API --- CONTENT --- Other languages: English русский This page is a candidate to be rewritten. This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. This page was last verified for Vintage Story version 1.19.8 . The render stages explain how the rendering is divided at a high level. The matrix operations page explains the matrix operations used by ChestLabelRenderer. A thorough tutorial is still missing, but until then you can look at the sample code on GitHub! Setting up a custom Shader: https://github.com/anegostudios/vsmodexamples/tree/master/code_mods/ScreenOverlayShaderExample Rendering a HUD Element: https://github.com/anegostudios/vsmodexamples/tree/master/code_mods/HudOverlaySample Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Client Startup Parameters • Server Startup Parameters • Understanding the VS Engine • All Engine Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:SaveGame_ModData TITLE: Modding:SaveGame ModData --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Other languages: English русский Before starting, you should have a development environment set up. If you don't have one already you should read the tutorial Setting up your Development Environment . Furthermore, we assume that you have a basic understanding of the C# language and Object Oriented Programming. Let's get started! Contents 1 Introduction 2 Custom Data in SaveGame 3 Preparation 4 Retrieving and Storing the List 5 Handling the Command 6 Conclusion 7 Testing Introduction Custom mod data can be attached to the top level SaveGame . Alternatively, mods can store custom chunk mod data on specific chunks. This tutorial goes over the following storage related methods and fields: ISaveGame.StoreData Update the mod data to store in the save game. ISaveGame.GetData Retrieve mod data from the save game. IServerEventAPI.GameWorldSave Register a delegate to call whenever a new save is about to be created. Here is where the mod should call ISaveGame.StoreData to copy the latest state from memory to the save game. IServerEventAPI.SaveGameLoaded Register a delegate to call whenever a save game is loaded from disk. Here is where the mod should call ISaveGame.GetData to read the saved state from disk and copy it to memory. Internally, the custom data is stored as an array of bytes to save space. Converting C# objects to/from an array of bytes is called serialization/deserialization. Serialization methods can either be invoked directly on SerializerUtil , or the generalized versions of ISaveGame.StoreData and ISaveGame.GetData can be used which implicitly use SerializerUtil . Custom Data in SaveGame Let us show you this powerful set of tools by making an example mod in which we implement an open list of players currently looking for a group to play with ingame. This list will be a List of players which will be stored to the world's SaveGame . Anyone can join it, leave it, or see who needs a group. We'll store this list as an array of bytes, and it can be accessed by the key "lfg". Preparation Let's start by creating a new .cs file for this mod, and adding our imports and the VintageStory.ServerMods namespace to wrap our class in. Additionally, we'll declare the class LookingForGroup that will inherit from ModSystem which is the base class for mod systems. You can read more about this here . using System.Collections.Generic ; using Vintagestory.API.Common ; using Vintagestory.API.Server ; using Vintagestory.API.Util ; namespace Vintagestory.ServerMods { class LookingForGroup : ModSystem { ICoreServerAPI serverApi ; List < string > lfgList ; public override void StartServerSide ( ICoreServerAPI api ) { serverApi = api ; api . Event . SaveGameLoaded += OnSaveGameLoading ; api . Event . GameWorldSave += OnSaveGameSaving ; api . ChatCommands . Create ( "lfg" ) . WithDescription ( "List or join the lfg list" ) . RequiresPrivilege ( Privilege . chat ) . RequiresPlayer () . WithArgs ( api . ChatCommands . Parsers . Word ( "cmd" , new string [] { "list" , "join" , "leave" })) . HandleWith ( new OnCommandDelegate ( OnLfg )); } } } In this class, we create an override for the StartServerSide method. This method of the ModSystem is called for all mods on the Server side by the game. The Core Server API contains an additional API for Event registration; this, in turn, contains two very important events (among others): SaveGameLoaded and GameWorldSave . These events are fired when the game is loaded, and when the game saves, respectively. We can assign delegates that will be called when the events fire by assigning these to the event with the += operator. When the Server side starts, we add two event delegates that will retrieve our list from the SaveGame when the we game loads, and that will save the list when the game saves. We also register a command that players will use to access our list of those looking for group. This will be /lfg with the arguments list, join or leave. Let us now define the event delegates! Retrieving and Storing the List When the game loads, OnSaveGameLoading gets called and attempts to get our list of players looking for group. The Core Server Api has a member called WorldManager which we use to access everything World related, including our SaveGame . private void OnSaveGameLoading () { byte [] data = serverApi . WorldManager . SaveGame . GetData ( "lfg" ); lfgList = data == null ? new List < string >() : SerializerUtil . Deserialize < List < string >>( data ); } We attempt to get our list by exposing the GetData method and passing it an identifier for our custom data, in this case our "lfg" list. Hint : If nothing is found stored under the key we provide, the GetData method returns null. In our mod example, this will happen until at least one player enters the list! byte[] data = serverApi.WorldManager.SaveGame.GetData("lfg"); As you can see, we're retrieving an array of bytes, which is the data type we actually store on the SaveGame . Let's convert it to a List of strings: lfgList = data == null ? new List() : SerializerUtil.Deserialize>(data); Here we use a ternary operator to assign our lfgList field a new List if there was nothing stored under the "lfg" key. If data is not null, we expose the Deserialize method of the SerializerUtil class. This method will deserialize an array of bytes into an instance of the type argument we pass it. Now that we have a delegate that fetches our list of players "lfg", let's create the delegate that stores this list when the Game World is saved: private void OnSaveGameSaving () { serverApi . WorldManager . SaveGame . StoreData ( "lfg" , SerializerUtil . Serialize ( lfgList )); } Here we call the StoreData method to save our list under the "lfg" key for later retrieval! Because we can only store data in the form of byte[] , we use the Serialize method of the SerializerUtil class to turn lfgList back to an array of bytes, which we pass as the second argument. We now have implemented a way to assign our list from storageto lfgList when the SaveGame is loaded, and a way to store this list once the game is saved. In between these two events, we want players to be added or removed from lfgList via our command. Let's create our command delegate! Handling the Command Server chat commands have three parameters: the player issuing the command, the group in which this command was entered, and the arguments sent for the command. private TextCommandResult OnLfg ( TextCommandCallingArgs args ) { string cmd = args [ 0 ] as string ; switch ( cmd ) { case "join" : break ; case "leave" : break ; case "list" : break ; default : return TextCommandResult . Error ( "/lfg [list|join|leave]" ); } } We use args[0] to collect the first argument passed (ignoring anything subsequent). We then start a switch statement for our valid arguments, and default to showing these to the player if none of them match or cmd is null. Let's handle each of these: case "join" : if ( lfgList . Contains ( args . Caller . Player . PlayerUID )) { return TextCommandResult . Error ( "You're already in the list!" ); } else { lfgList . Add ( args . Caller . Player . PlayerUID ); return TextCommandResult . Success ( "Successfully joined." ); } If /lfg join was entered, we'll first check if the player is already on the "lfg" list, letting the player know if so. Alternatively, we add the player's unique identifier to the list and show a success message. Hint: We do not want to store player names directly. This is because player names may change, therefore our list could become inaccurate. Next we handle /lfg leave : case "leave" : if (! lfgList . Remove ( args . Caller . Player . PlayerUID )) { return TextCommandResult . Error ( "You're not in the list!" ); } else { return TextCommandResult . Success ( "Successfully left." ); } The Remove method returns false if nothing matching the argument passed to it was found and removed, and true if it was. We let the player know if they were not on the list, or if they were and got successfully taken out of it. Finally, we handle /lfg list : case "list" : if ( lfgList . Count == 0 ) { return TextCommandResult . Success ( "No one is looking for a group." ); } else { string response = "Players looking for group:" ; lfgList . ForEach (( playerUid ) => { response += "\n" + serverApi . World . PlayerByUid ( playerUid ). PlayerName ; }); return TextCommandResult . Success ( response ); } In this case we simply let the player know if there is noone on the list, but if there are we build a string with all the player names on the list. Conclusion If you followed the steps correctly, you should have the following code: using System.Collections.Generic ; using Vintagestory.API.Common ; using Vintagestory.API.Server ; using Vintagestory.API.Util ; namespace Vintagestory.ServerMods { class LookingForGroup : ModSystem { ICoreServerAPI serverApi ; List < string > lfgList ; public override void StartServerSide ( ICoreServerAPI api ) { serverApi = api ; api . Event . SaveGameLoaded += OnSaveGameLoading ; api . Event . GameWorldSave += OnSaveGameSaving ; api . ChatCommands . Create ( "lfg" ) . WithDescription ( "List or join the lfg list" ) . RequiresPrivilege ( Privilege . chat ) . RequiresPlayer () . WithArgs ( api . ChatCommands . Parsers . Word ( "cmd" , new string [] { "list" , "join" , "leave" })) . HandleWith ( new OnCommandDelegate ( OnLfg )); } private void OnSaveGameLoading () { byte [] data = serverApi . WorldManager . SaveGame . GetData ( "lfg" ); lfgList = data == null ? new List < string >() : SerializerUtil . Deserialize < List < string >>( data ); } private void OnSaveGameSaving () { serverApi . WorldManager . SaveGame . StoreData ( "lfg" , SerializerUtil . Serialize ( lfgList )); } private TextCommandResult OnLfg ( TextCommandCallingArgs args ) { string cmd = args [ 0 ] as string ; switch ( cmd ) { case "join" : if ( lfgList . Contains ( args . Caller . Player . PlayerUID )) { return TextCommandResult . Error ( "You're already in the list!" ); } else { lfgList . Add ( args . Caller . Player . PlayerUID ); return TextCommandResult . Success ( "Successfully joined." ); } case "leave" : if (! lfgList . Remove ( args . Caller . Player . PlayerUID )) { return TextCommandResult . Error ( "You're not in the list!" ); } else { return TextCommandResult . Success ( "Successfully left." ); } case "list" : if ( lfgList . Count == 0 ) { return TextCommandResult . Success ( "No one is looking for a group." ); } else { string response = "Players looking for group:" ; lfgList . ForEach (( playerUid ) => { response += "\n" + serverApi . World . PlayerByUid ( playerUid ). PlayerName ; }); return TextCommandResult . Success ( response ); } default : return TextCommandResult . Error ( "/lfg [list|join|leave]" ); } } } } Testing Let's test our mod. Once you're ingame, try entering /lfg join . Now quit the game and join back in. Upon entering /lfg list , you should see your name on the list; this means that your custom data has persisted in the SaveGame ! Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Serialization_Formats TITLE: Modding:Serialization Formats --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Vintage Story uses multiple formats to serialize data. This page compares then. Contents 1 Formats 1.1 JSON 1.2 TreeAttribute 1.3 Protobuf 1.4 BinaryWriter 2 Converting 2.1 JsonTreeAttribute 2.2 Object 2.2.1 object to json string 2.2.2 json string to object 2.3 JsonObject 2.3.1 JSON String to JsonObject 2.3.2 JsonObject to json string 2.3.3 JsonObject to TreeAttribute 2.3.4 JsonObject to object 2.4 TreeAttribute 2.4.1 TreeAttribute to json string 2.4.2 TreeAttribute to serialized TreeAttribute 2.4.3 Serialized TreeAttribute to TreeAttribute 2.5 Protobuf 2.5.1 Protobuf to object 2.5.2 Object to protobuf Formats JSON JSON is a text format. It holds tree data, where the branches are identified either by a name (JSON object) or by a contiguous integer (JSON array). The leaves of the tree are integers, floating points, booleans, strings, or null. JSON is used for the configuration and asset files. Reserializing a JSON file (deserializing the JSON into an object, then serializing the object back to JSON) strips all of the custom formatting allowed by json5. So typically the game treats JSON as read-only data. The exceptions are: If the config files are missing, the game writes a new file with default values. That default file is created by constructing the configuration object with default values, then serializing it into JSON. Some of the debug messages log objects in JSON format. When the server transfers its assets to the client, most of the asset types that have an Attributes field will serialize it back to json to transfer it to the client. TreeAttribute Similar to JSON, TreeAttribute holds tree data, where the branches are identified either by a name or contiguous integer (when the attribute is an array). There are a few differences in the kind of tree data that TreeAttribute can hold as compared to JSON: Mixed arrays are not supported. For example, JSON allows an array to contain a mix of integers and strings, but that is not supported by TreeAttribute. The various decimal data types are distinguished. For example, 5 as int, 5 as long, and 5 as a float, and 5 as a double are all distinct in a TreeAttribute. However, JSON simply identifies 5 as a number. ItemStack is treated as a primitive type in TreeAttribute. ItemStack is treated as a generic object in JSON. TreeAttribute is a binary format, which makes its serialization more CPU efficient than JSON. Because the binary format still has to store a string for every object entry, a serialized TreeAttribute takes roughly the same space as the equivalent JSON file. TreeAttribute is used for transferring block entities from the server to client. It is also used to transfer entity attributes. Protobuf Protobufs serialize objects. They support most types of fields in the objects. A few annotations must be added to the classes so that the serializer knows which fields to serialize, and how to map the fields between versions for backwards compatibility. Protobufs are used extensively for the network packets sent by VintagestoryLib. Vintage Story actually uses two different protobuf implementations. silentorbit/protobuf is used inside VintagestoryLib, and is only visible by decompiling the library. protobuf-net seems to be better supported, because it is publicly exposed in vsapi. The Wireshark vs-protocol dissector can decode the outer Protobuf layer of the network packets. It is a helpful tool for debugging network problems. The rows in the main save game file contain protobuf encoded blobs. It is possible to deserialize them from the command line. BinaryWriter The BinaryWriter and BinaryReader classes are built into .NET. They only directly support serializing simple types. Serializing complex types is a very tediuous process. It invovles manually writing the code to break the complex object into simple types, and serialize those simple types. TreeAttribute internally uses a BinaryWriter. BinaryWriter is used to serialize entities, both over the network and to save files. It is also used for the network packets that set blocks. Converting The supported conversions are summarized in the diagram to the right. More detailed code is given in the sections below. Serialized formats are represented with rectangles. Objects in memory are represented with ellipses JsonTreeAttribute This is dead code. ClassRegistryAPI has a function that accepts it, but nothing calls the function. Ultimately there is nothing that constructs a JsonTreeAttribute. Object The diagram uses "object" to describe an instance of any specific class that supports serialization. An example is BlockType . object to json string Use the JsonConvert class from Newtonsoft. JsonConvert.SerializeObject(obj) json string to object JsonConvert.DeserializeObject(json) JsonObject This is a generic object for holding the memory representation of a JSON file. This type is used for the collectible attributes. JSON String to JsonObject JsonObject.FromJson(json_string) or new JsonObject(JToken.Parse(json_string)) . JsonObject to json string jobject.ToString() JsonObject to TreeAttribute jobject.ToAttribute() JsonObject to object jobject.AsObject() TreeAttribute Both the serialized binary format and the deserialized objects are called TreeAttribute. TreeAttribute to json string tree.ToJsonToken() TreeAttribute to serialized TreeAttribute tree.ToBytes() Serialized TreeAttribute to TreeAttribute tree.FromBytes(serialized) Protobuf Protobuf to object SerializerUtil.Deserialize(bytes) Object to protobuf SerializerUtil.Serialize(obj) Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Server-Client_Considerations TITLE: Modding:Server-Client Considerations --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Other languages: English русский Developing code-based mods for Vintage Story requires an appreciation of the server-client divide, particularly for more advanced applications. Understanding the division of tasks between server and client threads will make it easier to troubleshoot and to create well-designed mod systems, particularly if you plan for your mod to work on multiplayer servers. Contents 1 Basics 2 Is My Mod Server-Side, Client-Side, or Both? 3 Common "Gotchas" 4 Helpful Tips 5 Testing a Divided Server and Client 5.1 Notes Basics The core of a typical mod is the ModSystem, which is introduced in more detail on the Advanced Blocks page. public class ExampleMod : ModSystem As described in the API docs ( VintageStory.API.Common.ModSystem ), ModSystem includes a number of virtual methods which your mod may override. Perhaps the most commonly used method is ModSystem.Start(ICoreAPI) . You can use this method to define initialization code that should run when the game first loads. However, it is important to realize that, by default, the game creates one instance of your ModSystem on the server, and one additional ModSystem instance on each client. In the case of a single-player game, there will be one server and one client running on the same machine. As a result, any code that you put in ModSystem.Start will be run two or more times - once on the server thread and once on each client thread. Is My Mod Server-Side, Client-Side, or Both? TBA Common "Gotchas" In a singleplayer session, the server and the client share the same dataPath. This is important to keep in mind particularly if you are saving/reading any mod settings from the game's data path, such as by using the built in ModConfig methods. For example, your mod might appear to work fine on singleplayer if the server thread is reading your settings file, but then fail to work as expected on a multiplayer server, where the client thread should have been reading the player's settings. Helpful Tips In most cases, a mod can be designed to work on both singleplayer and multiplayer sessions with no special logic needed. However, if you need to check the state of the session for some reason, you can use the ICoreClientAPI.IsSinglePlayer property to check if the current game is a singleplayer game. If you have any doubts or concerns about your mod working on a multiplayer server, consider setting up a local server on your machine to test it out! Testing a Divided Server and Client It's easy to test how your mod will work on a multiplayer server, even if you're just working on a single local machine! The following instructions are for Windows, but other operating systems should work similarly. Before getting started, remove any mods that you've put directly in your Vintage Story directory (typically %appdata%/Vintagestory/Mods/ ). Mods in this directory will be shared by the server and client with the setup described below. Instead, move these mods into the data directory (on Windows, that is %appdata%/VintagestoryData/Mods/ by default). Set up a shortcut to the game's VintageStoryServer.exe file in your existing Vintage Story program directory. You'll use this shortcut to launch your test server. You will need to give it a custom data path using a run parameter to ensure that the server uses a different data path than the default data path. For example, on Windows your shortcut might have a target of C:\Program Files\Vintagestory\VintagestoryServer.exe --dataPath "%appdata%/VintagestoryData_Server" Double-click the shortcut to launch the server. At this point, the server will generate new folders and content in the --dataPath directory you specified. For example, you can now visit that path and add a copy of your mod to the /Mods subdirectory if you'd like the server to load your mod (after which you'd need to restart the server). The server will also output logs to the /Logs subdirectory here. If you'd like load a copy of your mod on the client side, make sure to add your mod to the normal data path directory (on windows, that is %appdata%/VintagestoryData/Mods/ by default). Launch the normal game client, and select "Multiplayer" from the main menu. Add a new server and configure it with a Host / IP Address value of "localhost". Then click "Save". You can now select your server from the Multiplayer menu to connect! All mods, settings, and logs should be separated, and you can test whether the mod will work as expected on a multiplayer server. Notes If for some reason you'd like to have a completely separate install for your test server, you can do that too. Either install a full version of the game in a separate directory and run your server from there, or download the "(server only)" TAR file from the Vintage Story download site. Keep in mind that you'll still need to set a custom data path to ensure the server files are separate from the normal game client. You can also configure additional custom path(s) for your server using other run parameters, such as --addModPath . For more info, check out the Client startup parameters page. If you're unfamiliar with server setup, you may also want to check out the Server Config page. For example, you might want to set the "AdvertiseServer" config value to "false" if you're only using this server to test mods locally. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Setting_up_your_Development_Environment TITLE: Modding:Setting up your Development Environment --- CONTENT --- Other languages: Deutsch English Nederlands español русский This page is outdated. Reason: A new template exists for Windows & Visual Studio users. Please see Preparing For Code Mods and Creating a Code Mod The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Contents 1 New Template 2 Software 3 Setup the Environment 3.1 Windows 3.2 Linux and Mac 4 Setup a Mod 4.1 Mod Template package 4.1.1 Visual Studio 4.1.2 Rider 4.1.3 Visual Studio Code 4.1.4 Other / Commandline 4.1.5 Updating the template package 4.1.6 Folder structure 4.2 Template Mod Project 4.3 Launch the Mod 4.3.1 Visual Studio Launch Mod 4.3.2 Rider Launch Mod 4.3.3 Visual Studio Code Launch Mod 5 Packaging the Mod 6 Updating the Mod 7 Moving Forward New Template If you are using Windows and Visual Studio, it is highly recommended that you instead follow the following tutorials: Preparing For Code Mods Creating A Code Mod There exists a new template that is significantly easier to setup and work with. Software When you are ready to begin coding mods for Vintage Story, you probably would want to set up a development environment that simplifies the process of coding and testing your creations. VintageStory is developed in C# and since version 1.18.8 we switched to .NET 7. There are several applications that aid you in the development of mods and the following Software is supported by our modding template: Visual Studio Community is a free and very powerful development environment made by Microsoft and has probably the best support for working with C# and dotnet which are also developed by Microsoft. This is also the tool the Vintage Story Team uses to build the game. It also has a MacOS Version . When using it make sure to install the .NET desktop development Workload as well. JetBrains Rider is a free powerful and user-friendly integrated development environment (IDE) specifically designed for .NET. Since it is made for the .NET it also has very good support for all the features you may need while being fully cross-platform. It is also used to develop the game by the Vintage Story Team. Visual Studio Code is a free, lightweight and versatile source code editor developed by Microsoft. It supports numerous programming languages and offers a wide range of extensions to enhance its functionality. There are extensions to support C# and thus can be a good choice to start out making mods. It is a lot used by our modding community. While you can use any text editor to edit source code files we highly recommend using any of the above tools to make your experience much more manageable. Choose one of the above IDEs or other alternatives. We suggest using Visual Studio Community for Windows. For Linux you may want to use Visual Studio Code or Rider . Note: If you are on Linux and plan to install Visual Studio Code or Rider using Flatpak or snap be aware that there might be some issues with using system tools like .NET 7 or the environment variables. So if possible please use a native installation method if possible. Next, you will need to install the .NET 7 SDK (Software Development Kit) this will also include the .NET 7 Dekstop Runtime which is needed to run the game since version 1.18.8-rc.1. Verify that the .NET 7 SDK is correctly installed by running the following command in a Terminal: On Windows open the Application Windows PowerShell which is preinstalled on all modern Windows versions (10 / 11). dotnet --list-sdks It should return a list of installed SDK's and should contain a line with 7.0.xxx . If that is the case you are good to continue. Setup the Environment Our Mod template makes use of the VINTAGE_STORY environment variable . An environment variable is a stored value within your operating system that can be easily accessed from any program. In this case, the VINTAGE_STORY variable allows our mod template to easily find our installation folder. The first step for setting up our environment is to create this environment variable to link to our game installation path. The VINTAGE_STORY environment variable simplifies reusing your Vintage Story game installation path and helps if multiple modders work on the same project to reference the VINTAGE_STORY Environment Variable and have their game installed where ever they want. As well as this, it increases the efficiency of updating any existing mods. Windows Here you have two options to set the environment variable: Use this short PowerShell script. Open the Windows PowerShell Application, paste the following into it, and hit ENTER. [ Environment ] ::SetEnvironmentVariable ( "VINTAGE_STORY" , " $Env :AppData\Vintagestory" , "User" ) Change $Env:AppData\Vintagestory if you used a custom installation path for your game. Follow these manual steps to set the Environment variable Search in Windows search for Edit the system environment variables > Environment Variables... > User variables for USER > New > Add insert the Variable name: VINTAGE_STORY and add the path to your Vintage Story installation. For example, the default installation directory would be C:\Users\\AppData\Roaming\Vintagestory , (replace with your username) if you are not sure where yours is type %appdata% into the URL field in the File Explorer and hit ENTER. It will take you to your AppData\Roaming folder where Vintage Story should be installed if you haven't changed the default installation location. Note: Do not confuse the AppData\Roaming\VintagestoryData directory with the AppData\Roaming\Vintagestory folder. The VintagestoryData directory only holds your Settings, Logs, Savegames and Mods. Linux and Mac To set an environment variable in Linux/Mac you need to add the following to your shells startup file: export VINTAGE_STORY = " $HOME /ApplicationData/vintagestory" Replace the path with the one where your Vintagestory installation is located. The above path should point to your install when you installed Vintagestory using the install.sh script. Run echo $SHELL in a terminal to see what shell you are using. For Bash place it in: ~/.bashrc or ~/.bash_profile at the end For Zsh place it in: ~/.zshrc or ~/.zprofile at the end If you use another shell see their documentation on how their shell startup file is called Note: If you are using ~/.bash_profile or ~/.zprofile you will have to Logout and Login again to apply the changes. When using ~/.bashrc or ~/.zshrc you only need to restart the application that needs to use the environment variable (Visual Studio, Rider, Visual Studio Code, Terminal). Setup a Mod Mod Template package The first and recommended option would be to use the template package . Install the mod Mod template package using the following command on the Terminal: dotnet new install VintageStory.Mod.Templates This will download the latest mod template package from nuget.org Once the template is installed you will see it inside Visual Studio and Rider. From there you can use the templates to create a new Project. Visual Studio We advise you to check the option Place solution and project in the same directory for Visual Studio This will flatten the project tree by one folder and make it easier to navigate. If you don't want the log output to show up in a separate window you can check the option SuppressWindowsConsoleWindow . When enabled the log output will still be shown inside the Visual Studio Debug Output. Open Visual Studio and click on Create a new project . If you installed the VintageStory.Mod.Templates you can then select Vintage Story Mod . Your project name must be only lowercase letters for your mod to function correctly. It is recommended to enable the various include options in the setup. Visual Studio Mod Template Visual Studio Mod Template setup Visual Studio Mod Template options Rider We advise you to check the option Put solution and project in the same directory for Rider. This will flatten the project tree by one folder and make it easier to navigate. Open Rider and click on New Solution . If you installed the VintageStory.Mod.Templates you can then select Vintage Story Mod . Visual Studio Code To create a new Mod using the template when using Visual Studio Code you have to resort to using the command line for now since there is no UI that supports the options as well. Open a folder in Visual Studio Code where you want your mod to be. Then open the terminal within Visual Studio code Terminal > New Terminal . Create a new Mod Project with launch.json and task.json to easily start it and a Solution file. Other IDE's create that automatically so that is why we have this additional --AddSolutionFile flag for VS Code. dotnet new vsmod --IncludeVSCode --AddSolutionFile For all possible options run the following in the terminal: dotnet new vsmod --help Once you opened or created a template Visual Studio will prompt you to install the recommended extension, click on Show Recommendations and install the the pre-release version of the C# Dev Kit extension. This will also install the required C# automatically for you. These two extensions are recommended and will add Syntax highlighting, Autocomlet and many more much-needed features for writing C# code. Other / Commandline If want to use the Commandline you can use all template options on the Commandline in any Terminal Application with the help of the dotnet command. Create a new VS mod in the folder mytestmod in the current location of the terminal. dotnet new vsmod --AddSolutionFile -o mytestmod or create a dll/code only mod dotnet new vsmoddll --AddSolutionFile -o mytestmod For all possible options run the following in the terminal: dotnet new vsmod --help When using the Commandline you can specify these options like so: # Will create a new Mod Project with the dependency for VSSurvivalMod dotnet new vsmod --AddSolutionFile --IncludeVSSurvivalMod -o mytestmod Finally in mytestmod\mytestmod\modinfo.json change the name , modid , authors and description to describe your mod. Updating the template package To update all installed templates you can run: dotnet new update But this should not be necessary when creating a new mod, since using the dotnet new vsmod will check automatically if a new version of the template is available and install it. Folder structure Here is an overview of where what files in your mod are located. Name Description mytestmod .vscode If you checked --IncludeVSCode this folder will contain all stuff needed to run, package and debug your mod using Visual Studio Code. CakeBuild This Project contains the code that builds mytestmod_x.x.x.zip. You can execute it by selecting the Run Configuration CakeBuild. mytestmod This is your project. It contains all your code as well as the assets and the modinfo.json assets Contains your assets. See Asset System modinfo.json Defines your mod. Author, Dependencies, Name and much more. See Modinfo Properties launchSettings.json This contains how to launch your mod (Visual Studio, Rider). You may want to add custom startup parameters to launch for example directly into a world when starting. Run Vintagestory.exe -h to see all options from a terminal opened in your Vintagestory install directory. mymod.csproj Your mods csproj file. Contains information like the .NET version to build against and references to the VintagestoryAPI for example. Releases Contains your mod release once the CakeBuild Run Configuration was run. mytestmod Your mod files before getting packaged into a .zip file. mytestmod_x.x.x.zip Ready to release mod zip. Template Mod Project Use the Github Mod template to create a new mod The Github Mod template provides an easy way to get the basics for mod setup so you can start directly with adding your modifications to the game. Without using git and Github account To get a copy of the template click the <> Code button on the GitHub repository site. Then download the template as ZIP file and extract it. Using Github and or git command You can directly click on Use this template on the GitHub repository site to create a copy of it to your GitHub account. After that, you can clone your new repository to your computer. If you do not use GitHub just clone the repo and upload it to your preferred Git hosting provider (Gitlab, BitBucket, ...) With a local copy of the template, you can go ahead and open the ModTemplate.sln either in Visual Studio, Rider or Open the folder in Visual Studio Code. The Template is ready to use for any of the aforementioned IDE's. Now you can already start the mod with the game. When opened you need to change the following to release the mod since a mod has to have a unique modid for the VSModDB: In modtemplate\resources\modinfo.json change the name , modid , authors and description to describe your mod. For a full list of available options see Modinfo . The modid has to contain only lowercase letters and numbers. No special chars, whitespaces, uppercase letters, etc. are allowed. Next, rename the folder modtemplate\resources\assets\mymodid to match what your modid is. Finally change in the file modtemplate\modtemplateModSystem.cs the line Lang . Get ( "mymodid:hello" ) from mymodid to your new modid. The file modtemplate\modtemplateModSystem.cs can be called whatever you like. (Optional) Further, you can change the name ModTemplate to whatever you like as long as you change all of the following: Folder ModTemplate File ModTemplate\ModTemplate.csproj In File CakeBuild\Program.cs on line ProjectName = "ModTemplate" ; If you are on linux and used the Github Template make sure to update in ModTemplate\Properties\launchSettings.json the executablePath . There you need to remove the .exe Launch the Mod Visual Studio Launch Mod Rider Launch Mod Rider Launch Mod (New UI) Rider Launch Mod Visual Studio Code Launch Mod If you are having trouble, you might be able to learn from the vsmodexamples project Packaging the Mod With the new template, we are using the Cake build system. This includes a second Project within the Solution in the template. In all supported IDE's you can select from the dropdown that you used to launch the mod the CakeBuild option and run it. When using Visual Studio Code you can also create a package by running the package task. For that got to Terminal > Run Task... and select package . It will perform a JSON validation on your assets so they are at least parsable by Newtonsoft.Json and then build a Releases/mymodid_1.0.0.zip . The version is taken from the mymod/modinfo.json file. Congratulations now you have your mod development environment set up and a ready-to-release mod package. Note: Make sure you use your own unique modid else you won't be able to upload the mod to the VSModDB. Updating the Mod If your mod is set up correctly, updating can be done by updating the VINTAGE_STORY environment variable to your new path. Repeat the steps made in Setup the Environment to edit the environment variable, and rebuild your mod. It is likely that mods will break on certain game updates, however fixing them is often quite simple, and errors are well documented. To confirm that your update has worked, debug your mod and check the game version on the main menu. Moving Forward If you've successfully managed to set up your development environment and can run Vintage Story through your Mod project in your IDE then it's time to get started on your first code mod. At this point there are many options you can choose from as code mods can alter almost any aspect of the game. However, the best place to start is likely once again with Blocks and Items (since you should be familiar with making them using JSONS by this point). Head over to the Advanced Blocks page to make your first advanced code block. Or head over to the Advanced Items page to make your first advanced code item. You can setup a decompiler to see the unpublished source for Vintage Story. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Simple_Particles TITLE: Modding:Simple Particles --- CONTENT --- This page is a candidate to be rewritten. This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. This page was last verified for Vintage Story version 1.19.8 . Other languages: English русский VintageStory offers a variety of different types of particles. This tutorial will explain you how to create simple particles and show you some of the possibilities. If you want to have an example of where to use them I either suggested you to the read about the block particles or about collectibles . Contents 1 Spawn particles 2 Properties Overview 2.1 Position 2.2 Velocity 2.3 Life length 2.4 Quantity 2.5 Color 2.6 Gravity 2.7 Size 2.8 Model 2.9 Opacity 2.10 Self Propelled 2.11 Die In Air/ Liquid Spawn particles So let's spawn some basic particles. I suggest to create a static field for your particle properties: public static SimpleParticleProperties myParticles = new SimpleParticleProperties ( 1 , 1 , ColorUtil . ColorFromRgba ( 220 , 220 , 220 , 255 ), new Vec3d (), new Vec3d (), new Vec3f (), new Vec3f ()); Now we have the property, the only thing left to do is to set the position and spawn the particles into the world. I'm gonna use the OnHeldInteractStart and *Step method inside a custom made Item class (you can read more about this in the Collectibles Tutorial ): public override void OnHeldInteractStart ( ItemSlot slot , EntityAgent byEntity , BlockSelection blockSel , EntitySelection entitySel , bool firstEvent , ref EnumHandHandling handHandling , ref EnumHandling handling ) { handHandling = EnumHandHandling . Handled ; } public override bool OnHeldInteractStep ( float secondsUsed , ItemSlot slot , EntityAgent byEntity , BlockSelection blockSel , EntitySelection entitySel , ref EnumHandling handling ) { myParticles . MinPos = byEntity . Pos . XYZ . Add ( 0 , byEntity . LocalEyePos . Y , 0 ). Ahead ( 1f , byEntity . Pos . Pitch , byEntity . Pos . Yaw ); byEntity . World . SpawnParticles ( myParticles ); return true ; } This will spawn some white cube particles right in front of the player, which simply fall to the ground. Properties Overview There are several things you can configure. Here is an overview which covers all of the properties: Position Basically there are two properties. One of them is the MinPos property, which will determine the exact position where particles will spawn. AddPos is relative to this position, once a particle will spawn it will be randomized and added to the particle's position. If AddPos is not defined all particles will spawn at the exact some position. Same position: If we now set AddPos ... myParticles . AddPos = new Vec3d ( 0.5 , 0.5 , 0.5 ); ... particles will spawn in an 0.5³ area: Velocity It follows the same way as position. You can configure a minimum velocity ( MinVelocity ) and an additional velocity (AddVelocity). myParticles . MinVelocity = new Vec3f ( 0 , 3 , 0 ); In this case all particles have the same velocity (particles fly in the air but will eventually fall down again): Life length This property is pretty straight forward. A life length of one equals one second. Particles will exist for one 200 milliseconds ... myParticles . LifeLength = 0.2F ; Particles will exist for one second ... myParticles . LifeLength = 1F ; Quantity Determines how many particles will spawn. myParticles . MinQuantity = 3 ; This will spawn 3 particles each tick. Furthermore you can specify an additional quantity chance ... myParticles . MinQuantity = 0 ; myParticles . AddQuantity = 100 ; This will spawn 0 to 100 particles per tick. Color A color can be specified using ARGB (Alhpa, Red, Green, Blue). All values have a range of 0 to 255. myParticles . Color = ColorUtil . ColorFromRgba ( 255 , 0 , 0 , 255 ); This will spawn red particles ... Random rand = new Random (); myParticles . Color = ColorUtil . ColorFromRgba ( rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), 255 ); This will spawn particles with a random color ... Gravity Gravity effect can not only be increase or decreased, ... myParticles . GravityEffect = 0.1F ; ... but you can also disable it completely (using zero gravity) or invert it (particles will fly up). Size Again, you can specify a minimum size and an additional randomized size ... myParticles . MinSize = 0.1F ; myParticles . MaxSize = 1.0F ; Furthermore you can specify a SizeEvolve like so ... myParticles . MinSize = 0.1F ; myParticles . MaxSize = 1.0F ; myParticles . SizeEvolve = new EvolvingNatFloat ( EnumTransformFunction . LINEAR , 2 ); There are many EnumTransformFunctions you can play around with. Linear is the most basic one. Model There are two types of models. Cube (the one we used so far) and Quads : myParticles . ParticleModel = EnumParticleModel . Quad ; Opacity Quads support custom opacity. This allows you to make them transparent ... myParticles . Color = ColorUtil . ColorFromRgba ( rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), rand . Next ( 0 , 255 ), 130 ); Similar to SizeEvolve you can add an OpacityEvolve ... myParticles . OpacityEvolve = new EvolvingNatFloat ( EnumTransformFunction . LINEAR , - 255 ); Self Propelled This property will make particles don't lose their velocity, even if they would collide with something, as soon as there is enough space they will continue to fly in the direction (with the same speed) ... myParticles . MinVelocity = new Vec3f (( float ) ( rand . NextDouble () - 0.5 ), 1f , ( float ) ( rand . NextDouble () - 0.5 )); myParticles . SelfPropelled = true ; Die In Air/ Liquid This property is useful for particles in water, which should disappear once they reach the surface (die in liquid). Of course this can also be used the other way round (die in air). myParticles . ShouldDieInAir = true ; myParticles . ShouldDieInLiquid = true ; myParticles . ShouldSwimOnLiquid = true ; Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:The_Remapper TITLE: Modding:The Remapper --- CONTENT --- Other languages: English русский This page was last verified for Vintage Story version 1.19.8 . The Remapper is a server side system that, under ideal circumstances, no one should know even exists. It's the thing that makes sure your world doesn't go bonkers from installing or removing a mod that adds blocks or items. Let's say you're writing a mod that implements an Uberbasket. You must compose a recipe for users to follow to create the Uberbasket, and the recipe must specify both the input (the "ingredients") and the output (the Uberbasket). The mod specifies the Uberbasket as the recipe's output with a code in the form of a string: maybe some words separated by dashes. However, these codes do not store efficiently, so to reduce memory usage, the engine internally assigns a per-world unique number to each item and block, then stores a table that maps codes to ids into the save game. These ids are created when the game loads the blocks and items, and once created, they are never deleted from the mapping. This is what the mapping table might look like: Code Id stick 6 coal 7 ingot-iron 8 ... ... Storing this mapping table into the save game is necessary. To see why, consider what would happen if we added a new mod that inserts a new block into the mapping table before the stick. This would cause the Id numbering to change: suddenly, every stick in the world would turn into coal, every coal in the world would turn into an iron ingot, and so on. New blocks can potentially get added or removed whenever the base game version changes or when a mod gets added or removed. How does this affect modders? The issue arises when you decide to change the code of a block or item, or when a user wants to remove one of your mods. When this happens, the modded blocks in the user's savegame get replaced with something else. Let's say your mod added the Uberbasket, but you realize it was not powerful enough and changed it into the Hyperbasket, including its item code, in your latest mod update. As soon as a player updates to the latest version of your mod, all their Uberbaskets will become unknown items, because the code<->id mapping for the Uberbasket is still stored in the savegame. Thankfully you can help the player fix this! The commands /bir and /iir let you edit the mapping table. In this case, the player needs to type: /iir remap hyperbasket uberbasket force This command effectively searches for "uberbasket" in the mapping table, and then replace its id with the hyperbasket id. When the user reloads the world, all the Uberbaskets will have turned into Hyperbaskets. The remaps.json For even greater player convenience, you can create a json patch that adds an entry to assets/game/config/remaps.json. This file lets you add your remaps and the player will get a user friendly dialog popping up that remappings are required. You might want to use the "remapq" subcommand to reduce chat log spam. Limitations You may not remap two blocks or items to a single block or item. By definition, all ids must be unique, but if you were to remap 2 to 1, it would mean an item or block would have 2 ids. The game was not designed to handle multiple ids for the same block or item, so the results would be unpredictable. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Debugging Content • API Updates • Modding Efficiently • Remapping Objects • Mod Compatibility • All Troubleshooting Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Theme_Pack TITLE: Modding:Theme Pack --- CONTENT --- Other languages: English español русский This page was last verified for Vintage Story version 1.19.8 . Contents 1 Overriding Game Files 2 Overriding Mod Files Theme Packs are mods that do not change the game mechanics, only visuals, sounds, etc. They are packed like any other mod , using the type "theme" . Due to the way they work, theme packs never add new content, only replace existing content. These mods only affect the player who has them installed (the client), not the server. Overriding Game Files Replacing game files is fairly easy, just create a directory names "assets" with a directory named "game" inside it, then copy replacements for any asset in the game's "assets" directory here. For example, to overwrite the texture for low fertility soil blocks ( assets/survival/textures/block/soil/fertlow.png ) you would place your replacement at assets/game/textures/block/soil/fertlow.png inside your zip file. Note that the original file is stored in the 'survival' folder, however the overwritten folder is stored within the 'game' folder. This is because all base-game blocks use the 'game' prefix in their ID. Check out an example theme pack for a simple demo of overriding game assets. Overriding Mod Files Overriding assets from other mods is also possible, see the asset system page for more details. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:TreeAttribute TITLE: Modding:TreeAttribute --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Other languages: English русский A TreeAttribute is a nested data structure whose primary purpose is to represent the tree attribute serialization format in memory. It can hold generic data for most primitives, such as int, string, float, as well as more complex types like ItemStacks and other TreeAttributes (hence a nested structure). Additionally you can store data as byte[] if it is not supported by the TreeAttribute itself (for this you could use Vintagestory.API.Util.SerializerUtil ). Attributes are written and read via keys. To use TreeAttribute, add the using statement for Vintagestory.API.Datastructures . Use in API Within the API, TreeAttribute is often employed to store persistent entity data (items a basket contains, content of a barrel/bucket) and to access data extracted from JSON files. TreeAttribute is often supplied as the interface ITreeAttribute . Example in code using Vintagestory.API.Datastructures ; public class TreeAttributeMod : ModSystem { public override void StartServerSide ( ICoreServerAPI api ) { // Setting and getting values TreeAttribute tree ; tree = new TreeAttribute (); float someValue = 0.35f ; string someKey = "valueKey" ; // Set the value tree . SetFloat ( someKey , someValue ); // Retrieve the value tree . GetFloat ( someKey ); } } For documentation, see TreeAttribute and ITreeAttribute . Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Trees TITLE: Modding:Trees --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Contents 1 Overview 2 Tree generation options 3 Tree generation commands 4 Height 5 Branch angles 6 Child branches 7 Tree branch blocks 8 Multiple trunk entries 8.1 2x2 trunk 8.2 Vertically branching off trunk 9 Forest floor generation Overview The overall steps for tree placement is that it first generates the tree, then generates the tree floor. Within tree generation, each trunk is treated as a branch. As the branch grows, it starts off placing wood blocks. Once the branch gets too "narrow", then it switches to branch leaf blocks, then leaf blocks. Along the way it may generate new sub-branches. Tree generation options The tree generation options are set through the worldgen/treegen asset files. Each json file contains a TreeGenConfig . Inside the TreeGenConfig are TreeGenTrunks and TreeGenBranches . Tree generation commands Both the wgen and WorldEdit commands can place trees. wgen reloads the tree generators before placing the tree. WorldEdit reloads the tree generators when the tree variety is selected. WorldEdit is especially helpful for testing changes to the tree generators, with the following sequence: /we tool TreeGen - select the tree generator /we tv walnut - select the tree variety right click whilst holding a block - to place the tree /we undo - clean up the test tree Height The suggested size is passed into the tree generation. A value of 1.0 can be thought of as a normal sized tree for that variety. BESapling uses a randomly selected float in the [0.6, 1.1] range. The suggested size is converted into the adjusted size with the following formula, where sizeMultiplier and sizeVar are settings in the TreeGenConfig. Since sizeMultiplier defaults to 0, every tree sets it, typically to a value around 1. sizeVar is a way to add extra variance to the tree height. It defaults to 0, which most trees use. The adjusted size is typically in the [0.18, 2.2] range. adjusted size = suggested size * sizeMultiplier + sizeVar Internally the term "width" is used to describe something like the length of a branch. Do not confuse it with the literal width of a branch. The real width of every branch is 1 block (2x2 tree trunks are actually 4 touching trunks). The adjusted size is converted into a trunk "width" with the following formula. The trunk widthMultiplier is redundant with the above parameters. So most trees let it default to 1. trunk width = adjusted size * trunk widthMultiplier After each trunk block is placed, its "width" gets narrower, by the widthLoss. That is how the width turns into a length. The initial width loss can either be specified by as a constant through the widthLoss field, or as a random variable through the randomWidthLoss field. Most trees use the constant widthLoss field. From there, the trunk grows as a branch. The tree height is the length of the trunk as it grows as a branch, plus maybe extra height from the branches that come off the trunk. So the tree height is roughly the trunk width divided by the trunk's widthLoss. Branch angles Each branch has a horizontal and vertical angle, measured in radians. The vertical angle is relative to the Y axis. So a 0 radian vertical angle is a branch that's pointing straight up. A pi/2 vertical angle is a branch that's pointing completely horizontal. When the horizontal angle is 0, the horizontal component of the branch points east. When the horizontal angle is pi/2, it points south. This means the horizontal angle increases counter clockwise, as you look downwards at the ground. The trunk's angle comes from the angleVert and angleHori options. At each step, the angle changes using the angleVertEvolve and angleHoriEvolve options, if they are set (by default they do not change the angle). The gravityDrag option can have an effect somewhat like changing the angle. The effect of gravityDrag varies depending on how horizontal the branch is. Completely horizontal branches are vertically shifted by the whole gravityDrag at every step. Whereas completely vertical branches are not affected by gravityDrag at all. Child branches The simple length of a branch (called totaldistance in the code) is calculated by dividing the "width" by the widthLoss. For example, if the width is 1.0 and the widthLoss is 0.1, then the simple length would be 10 blocks. The simple length is not necessarily the real length of the branch. There are two ways it can differ: 1. The simple length calculation assumes the "width" is reduced all the way to 0. However, the dieAt option specifies what "width" the branch should stop growing at. So for example, if dieAt was set to 0.2, then the branch would stop before reaching its simple length. 2. If the widthLossCurve curve is set to less than 1 (default is 1), then the width loss will get lower (less loss) on each time a block is placed for the branch. So this way the branch could end up longer than calculated by the simple length. The simple length and the distance traveled so far by the branch are used in the relative distance calculation. relative distance = simple length / distance traveled The branchStart option is specified as a relative distance. So assuming the parent branch's real length matched its simple length, then a branchStart value of 0.3 would mean that its first child branch would start 30% out from the root of the parent branch. Child branches can be disabled by setting the branchStart to a value of 1.0 or greater (assuming that the real length is less than or equal to the simple length). The branchSpacing option is specified as a relative distance of the remaining simple length, compared to the position of the previous branch. So let's say the parent branch is 20 blocks long (and its simple length matches its real length), its first child branch was at 4 blocks from the root, and its branchSpacing is 0.5. After the first branch is placed, the parent has 16 blocks remaining. 50% of that is 8, so the next branch would be started 12 blocks from the root. Now it only has 8 blocks remaining, and 50% of that is 4. So the next branch would start 16 blocks from the root, and so forth. Every time the parent branch decides to create child branches, it uses the branchQuantity option to control how many are created. This is used to make the branches look more evenly balanced instead of just individual branches popping out at random intervals. The branchHorizontalAngle option (units in radians) controls the horizontal angle of the child branch relative to the parent branch (it is added to the parent branch's horizontal angle). To reduce the changes of two children using the same horizontal angle, the tree generator tries to pick a horizontal angle that's branchHorizontalAngle.var / 5 away from the previous child. However, the formula only takes into account the previous child (not two more more children earlier), and it does not handle 360 degree rotations correctly. So it may end up placing child branches at similar angles anyway. The vertical angle for the child branches is directly calculated from branchVerticalAngle. Unlike the horizontal angle, the parent branch's angle is not added to the child's vertical angle. So most trees use a branchVerticalAngle close to pi/2 (about 1.57) plus some variance so that the child branches are close to horizontal. Each child branch inherits the "width" of the parent at the branch point, times a multiplier. The multiplier is initially branchWidthMultiplier. If branchWidthMultiplierEvolve is set, then it is used to change the multiplier on each depth of the tree. The trunk uses it's options to create the first level of branches. The next level of branches takes its options from the first entry in the branches array in the json file. The next level uses the second entry in the array. When the array runs out of entries, the last entry in the array is used for the remaining depths. So for example, if the branches array is empty, then the trunk options are used for all child branches at every level. Tree branch blocks When the "width" is greater than or equal to 0.3, then a log block is used. This is typically the logBlockCode listed in the treeBlocks section, but there are some exceptions. If the branch is a trunk and the trunk has segment set to a value of 1 or greater, then a block from trunkSegmentBase + trunkSegmentVariants is used instead. This is used for trees like redwood that have different trunk and branch logs. These log block exceptions only apply for trees generated by a command or worldgen (not grown by sapling): The otherLogBlockCode may be used instead according to otherLogChance * treeGenParams.otherBlockChance -- this is typically a leaking resin block. A moss decor may be placed on top of the wood block if mossDecorBlock and mossGrowthChance are set. A vine block may be placed on top of the wood block if vinesGrowthChance and vinesBlock are set. If the width is less then 0.3, then a leaf block is placed instead. Closer to the root of the branch, branchy leaf blocks are used. At the end of the branch, regular leaf blocks are used. The transition point depends on leafLevelFactor (defaults to 5). If the width is greater than or equal to (1 - .5)/leafLevelFactor, then a branchy leaf block is used. So by default, branch leaf blocks are used from width 0.3 to 0.1, and regular leaf blocks are used from 0.1 to 0. By setting the dieAt option, the leaves can be reduced. Setting it to 0.1 (assuming the default leafLevelFactor) would create a tree with no regular leaf blocks. Setting it to 0.3 would create a tree with branches but no leaf blocks at all. Setting widthlossCurve to a value less than 1.0 causes the width loss to reduce over the length of the branch. This causes the branch to generate more blocks in the leaf phase relative to the log phase. Multiple trunk entries There are two reasons to put multiple entries in the trunk array: 1. To create a 2x2 trunk, like a redwood. 2. To create a trunk that vertically branches off, like acacia. In both cases, each trunk entry grows independently. It is okay for the trunks to overlap in the sense that they will keep growing when the recognize the location is already occupied by a block from the tree generation (only stops if it runs into a block not belonging to the tree). Note that each trunk creates its child branches independently. So by adding multiple trunks, you may end up with more child branches than you expected. 2x2 trunk Create 4 entries in the trunks array. Use the dx and dz options to place each trunk in the center of each of the base blocks ( (0.5, 0.5), (0.5, 1.5), (1.5, 1.5), and (1.5, 0.5) ). Each trunk will independently grow from from its base. Ensure the angleVert is unset so that the trunks grow completely vertical (rather than separate mid way up the tree). Give each trunk a segment index from 1 to 4. Use the trunkSegmentBase and trunkSegmentVariants fields to give the trunks different block types (to show bark only on the outer faces of the trunk sections). Vertically branching off trunk Leave the angleHori unset. It defaults to randomly selecting any horizontal angle. Set the angleVert and optionally angleVertEvolve on the trunks. angleVert controls the angle that the trunks start at. angleVertEvolve controls how the trunks spread as they grow. Forest floor generation Forest floor generation is only run when the tree is created as part of worldgen. It is skipped when the tree is grown from a sapling. During the tree generation, the (x,z) positions of the placed blocks (trunk, branches, leaves, etc.) is tracked. A 2 block blur is applied to those positions (represents the wider area that is under the shadow of the canopy), then they are updated to forest floor blocks. In addition to placing forest floor blocks, block patches may also be applied, which spawn things like berry bushes and mushrooms. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Troubleshooting_Portal TITLE: Modding:Troubleshooting Portal --- CONTENT --- Other languages: English русский Welcome to the troubleshooting portal. The pages found here will help you to develop mods efficiently and effectively, and how to deal with some common (and not-so-common) issues when making any type of mod. Troubleshooting Pages Debugging Content - Lists a number of common issues for content mods, as well as how to use the console to search for errors. API Updates - A list of all modding API updates. Check here after each update to see what things have changed to help you update your mod to a new game version! Modding Efficiently - Gives a few tips and tricks to help you mod efficiently. Remapping Objects - The remapper is used when updating mods and game versions, to allow pre-created worlds to update blocks, items, and entities correctly. Mod Compatibility - Shows how to use patches to achieve inter-mod compatibility. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Debugging Content • API Updates • Modding Efficiently • Remapping Objects • Mod Compatibility • All Troubleshooting Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Types_Of_Mod TITLE: Modding:Types Of Mod --- CONTENT --- Other languages: English русский Modding - Types of Mod There are three distinct mod types for Vintage Story, and each has its own capabilities, advantages, and disadvantages. Theme Pack Content Mod Code Mod Theme Pack A Client-Side Content Mod Uses the "theme" type when packaging. Easy to setup! Can only modify the following asset types: Language Files Sounds Shapes Shaders Textures Music Dialog It is recommended to understand content mods if you are designing theme packs. Content Mod Can be client-side, server-side, or both. Uses the "content" type when packaging. Easy to setup! Primarily uses JSON . Can create and modify any assets, including: Blocks Items Entities World Generation Settings Recipes Textures Shapes Cannot be used to create new complex behaviors or gui screens. Code Mod An extension of a content mod that also uses code. Can be client-side, server-side, or both. Uses the "code" type when packaging. Requires more complex setup. Uses a mix of C# and JSON . Can change or add nearly anything, including: New AI Tasks Complex block behaviors Entity Behaviors New GUI Screens New types of recipes or assets Modifying any existing code It is highly recommended to understand content mods before starting a code mod. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Using the Modding Wiki • Getting Started • Types of Mod • Other Resources --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Using_The_Modding_Wiki TITLE: Modding:Using The Modding Wiki --- CONTENT --- Other languages: English español русский українська Contents 1 Modding - Using the Wiki 1.1 Read The Pages 1.2 The Wiki on Mobile 1.3 Searching & Navigating the Wiki 1.3.1 Using the Navboxes 1.3.2 Navigating Categories Modding - Using the Wiki Welcome to the modding wiki! When learning to mod, this will be the most invaluable source of information for the new topics and techniques you will need to know. However, it can be a bit daunting at times, so here's some tips on how to make the most of your wiki-based journey. Read The Pages Seriously . It is not recommended to cherry-pick articles when learning how to mod. Many of the modding tutorials are built to be progressive from one another, and a lot of them will have a "prerequisites" section in their introduction that gives a list of pages that are recommended to read or tutorials to complete. Do not ignore these prerequisites. They are there for a reason. The tutorials created for the wiki are tutorials , and are here to teach you how to mod. They are not there to provide mod examples that can be copy and pasted - See the official mod examples if that is what you are looking for. If you copy and paste the code provided in the wiki tutorials without following along the previous steps, do not expect things to work. The Wiki on Mobile Due to the complex nature of some content included in the wiki, it is recommended to use a device with landscape support . Some mobile devices, including phones with small screen-widths, may struggle to display all of the necessary content. Searching & Navigating the Wiki By default, the wiki's search feature excludes the modding pages. To update your search preferences, click the "Search Modding" button below. The results page will show that the Main and Modding namespaces are checked. If you are logged in , you can check "Remember selection for future searches", then click search to save the changes. Main Modding Using the Navboxes Unfortunately, due to some limitations with the wiki, the search feature is not always ideal. To counter this, and to ensure that information can be easily found, pages are built using a hierachy system. As shown below and on the home page, there are currently 7 main categories : Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine Every page will contain a navbox (like the one above) at the very bottom of the page. Rather than showing every link on the modding wiki, these will display links that are only relevant to the category and subcategories you are currently viewing. Note that the modding wiki has over 170 pages which have to be maintained, and it is not feasible for anyone involved (both maintainers and readers) to have to navigate through a navbox of 170 links of similarly-named articles to find what they are looking for. Learn to use the categories and navboxes to your advantage, and you'll find what you're looking for . Navigating Categories For a more direct approach, and to observe a full list of pages that exist in the wiki, you can navigate the categories directly. Every page on the modding wiki should be added to a category. You can view categories at the bottom of the left-hand panel of a page. Categories are split in a hierachy too, but can be harder to navigate than using the navboxes. It is recommended to use the category pages when you are more experienced with what wiki pages exist. By using a combination of the navboxes, searching, and categories, you should be able to find the pages relevant for what you need. Use this link [[::Category:Modding| ]] to go to the modding category. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Using the Modding Wiki • Getting Started • Types of Mod • Other Resources --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:VCDBS_format TITLE: Modding:VCDBS format --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . The VCDBS file holds the main save game data. At the top level it is a sqlite database, but very few of the sqlite features are used. There are no database constraints and only one secondary index. Most of the data is stored in BLOB fields, which are then protobuf encoded, and thus fields within the protobuf cannot be queried at the SQL level. Contents 1 ChunkPos 2 Schema 2.1 gamedata 2.2 playerdata 2.3 chunk 2.4 mapchunk 2.5 mapregion ChunkPos Several of the tables are indexed by a ChunkPos. This is an integer packing of the chunk position. It is similar to, but has a slightly different encoding than the ChunkIndex3D . ChunkPos fields in most significant bit first order reserved chunkY dimension high part guard chunkZ dimension low part guard chunkX 1 bit 9 bits 5 bit 1 bit 21 bits 5 bits 1 bit 21 bits Schema The tables in the save file can be listed with the .tables sqlite3 command. You can also use an online SQLite viewer to read the save files. sqlite> .tables chunk gamedata mapchunk mapregion playerdata The .schema command shows the schema of a requested table. Although, it does not show the protobuf schema used on the blob fields. The deserializing from the command line page has details on how to install tools such as sqlite to read the database contents from the command line. sqlite> .schema chunk CREATE TABLE chunk (position integer PRIMARY KEY, data BLOB); Table name Each row covers Index column Value column Value column protobuf schema gamedata Entire save game savegameid (integer) - always 1 data SaveGame playerdata A player playerid (integer) - autogenerated, required by sqlite3 playeruid (string) - UUID of the player data Vintagestory.Server.ServerWorldPlayerData chunk 32x32x32 volume of blocks position (integer) - ChunkPos data Vintagestory.Server.ServerChunk mapchunk 2D data for a column of chunks position (integer) - ChunkPos with Y set to 0 data Vintagestory.Server.ServerMapChunk mapregion 2D data for a 16x16 region of chunks position (integer) - ChunkPos of the most northwest covered chunk, with Y set to 0 data Vintagestory.Server.ServerMapRegion gamedata There is exactly one gamedata row in each savegame. It contains settings for the world and counters for the global identifiers. There is a tutorial on adding custom moddata to the row. $ sqlite3 modtestworld.vcdbs "SELECT writefile('gamedata_' || savegameid || '.binpb', data) FROM gamedata;" 436882 $ protoc --decode SaveGame --proto_path=$(dirname ~/schema.proto) ~/schema.proto ~/playerdata.txt PlayerUID: "6mkR85hnMVMvGjIY0hSWWj3+" inventoriesSerialized { key: "backpack-6mkR85hnMVMvGjIY0hSWWj3+" value: "\001\006qslots\004\000\000\000\006\005slots\000\000" } inventoriesSerialized { key: "character-6mkR85hnMVMvGjIY0hSWWj3+" value: "\001\006qslots\017\000\000\000\006\005slots\007\0012\000\001\000\000\000|\n\000\000\001\000\000\000\004\tcondition\025\300\233>\000\007\0013\000\001\000\000\000#\n\000\000\001\000\000\000\004\tcondition\237\372\223>\000\007\0014\000\001\000\000\000\306\t\000\000\001\000\000\000\004\tconditionsXE=\000\007\0015\000\001\000\000\000\354\t\000\000\001\000\000\000\004\tcondition\327\214o>\000\007\00211\000\001\000\000\000\253\n\000\000\001\000\000\000\004\tcondition%%\205>\000\000\000" } inventoriesSerialized { key: "craftinggrid-6mkR85hnMVMvGjIY0hSWWj3+" value: "\001\006qslots\t\000\000\000\006\005slots\000\000" } inventoriesSerialized { key: "creative-6mkR85hnMVMvGjIY0hSWWj3+" value: "\000" } inventoriesSerialized { key: "ground-6mkR85hnMVMvGjIY0hSWWj3+" value: "\000" } inventoriesSerialized { key: "hotbar-6mkR85hnMVMvGjIY0hSWWj3+" value: "\001\006qslots\013\000\000\000\006\005slots\007\0010\000\000\000\000\000}$\000\000\001\000\000\000\000\000\000" } inventoriesSerialized { key: "mouse-6mkR85hnMVMvGjIY0hSWWj3+" value: "\000" } EntityPlayerSerialized: "\014EntityPlayer\0061.19.8\3734\000\000\000\000\000\000\006\nanimations\000\006\rextraInfoText\000\005\tplayerUID\0306mkR85hnMVMvGjIY0hSWWj3+\004\006onHurt\000\000\000\000\006\007nametag\005\004name\017bluelightning32\t\027showtagonlywhentargeted\000\001\013renderRange\347\003\000\000\000\006\006health\004\rbasemaxhealth\000\000pA\004\rcurrenthealth\000\000pA\004\tmaxhealth\000\000pA\000\006\006hunger\004\021currentsaturation\000\200;D\004\rmaxsaturation\000\200\273D\004\030saturationlossdelayfruit\000\000pB\004\034saturationlossdelayvegetable\000\000pB\004\030saturationlossdelaygrain\000\000pB\004\032saturationlossdelayprotein\000\000pB\004\030saturationlossdelaydairy\000\000pB\004\nfruitLevel\000\000\000\000\004\016vegetableLevel\000\000\000\000\004\ngrainLevel\000\000\000\000\004\014proteinLevel\000\000\000\000\004\ndairyLevel\000\000\000\000\000\006\006oxygen\004\tmaxoxygen\000@\034G\004\rcurrentoxygen\000@\034G\t\006hasair\001\000\006\ttiredness\004\ttiredness\000\000@A\000\006\010bodyTemp\004\010bodytemp\000\000\024B\003\030bodyTempUpdateTotalHours\260\005[\260\205Z\220@\004\026nearHeatSourceStrength\000\000\000\000\000\003\033lastWetnessUpdateTotalHours\260\005[\260\205Z\220@\006\nskinConfig\006\014appliedParts\005\010baseskin\005skin2\005\010eyecolor\005azure\005\tunderwear\007leotard\005\tvoicetype\004oboe\005\nvoicepitch\004high\005\010hairbase\004afro\005\thairextra\ntieddreads\005\020facialexpression\010very-sad\005\010mustache\004none\005\005beard\004none\005\thaircolor\010sanddune\000\000\005\tvoicetype\004oboe\005\nvoicepitch\004high\003\021temporalStability\000\000\000\000\000\000\360?\003\024lastReviveTotalHours\301\263\241\002\266W\220@\004\007headYaw\372\355\353:\004\theadPitch+\207\226=\006\005stats\006\023healingeffectivness\004\004base\000\000\200?\004\013wearablemod\000\000\000\000\000\006\024maxhealthExtraPoints\004\004base\000\000\200?\000\006\twalkspeed\004\004base\000\000\200?\004\013wearablemod\000\000\000\000\000\006\nhungerrate\004\004base\000\000\200?\004\013wearablemod\000\000\000\000\000\006\020rangedWeaponsAcc\004\004base\000\000\200?\004\013wearablemod\000\000\000\000\000\006\022rangedWeaponsSpeed\004\004base\000\000\200?\004\013wearablemod\000\000\000\000\000\006\023rangedWeaponsDamage\004\004base\000\000\200?\000\006\022meleeWeaponsDamage\004\004base\000\000\200?\000\006\021mechanicalsDamage\004\004base\000\000\200?\000\006\022animalLootDropRate\004\004base\000\000\200?\000\006\016forageDropRate\004\004base\000\000\200?\000\006\020wildCropDropRate\004\004base\000\000\200?\000\006\026vesselContentsDropRate\004\004base\000\000\200?\000\006\013oreDropRate\004\004base\000\000\200?\000\006\021rustyGearDropRate\004\004base\000\000\200?\000\006\016miningSpeedMul\004\004base\000\000\200?\000\006\022animalSeekingRange\004\004base\000\000\200?\000\006\023armorDurabilityLoss\004\004base\000\000\200?\000\006\032armorWalkSpeedAffectedness\004\004base\000\000\200?\000\006\022bowDrawingStrength\004\004base\000\000\200?\000\006\025wholeVesselLootChance\004\004base\000\000\200?\000\006\030temporalGearTLRepairCost\004\004base\000\000\200?\000\006\024animalHarvestingTime\004\004base\000\000\200?\000\000\005\016characterClass\010commoner\t\006canEat\001\004\007wetness\000\000\000\000\004\026freezingEffectStrength\000\000\000\000\001\nentityDead\000\000\000\000\001\013deathReason\006\000\000\000\001\017deathDamageType\000\000\000\000\004\014intoxication\000\000\000\000\001\025positionVersionNumber\017\000\000\000\003\006kbdirX\000\000\000\000\000\000\000\000\003\006kbdirY\000\000\000\000\000\000\000\000\003\006kbdirZ\000\000\000\000\000\000\000\000\004\tonHurtDir\000\300y\304\t\006onFire\000\000\314\35691-@\037A\000\000\000\000\000\200^@\276\301\247sx?\037A\000\000\000\000\275R\252@\233UO@\000\000\000\000\002\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\002\000\000\000\000\000\000\000\314\35691-@\037A\000\000\000\000\000\200^@\276\301\247sx?\037A\006player\003\026tempStabChangeVelocityh\255T\375$Iy?\000\006\013activeAnims\000\000\000\000\000\000\314\35691-@\037A\000\000\000\000\000\200^@\276\301\247sx?\037A\000\000\000\000\275R\252@\233UO@\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000" GameMode: EnumGameMode_Creative MoveSpeedMultiplier: 1 FreeMove: true Viewdistance: 160 selectedHotbarslot: 1 PickingRange: 100 ModData { key: "createCharacter" value: "\010\001" } PreviousPickingRange: 100 Deaths: 15 chunk A row in the chunk table covers a 32x32x32 section of the world. There is a tutorial on adding custom moddata to a chunk. Each chunk contains the following information: Every block in the solid layer Every block in the fluid layer Sparse map of block positions to block entities List of entities Sparse map of decor blocks . The map is indexed by both the block position and decor index. The decor index includes which face of the parent block the decor is attached to, and optionally a location on a 16x16 grid on that block face (used by caveart). Map of modid to a byte array of custom moddata Format version metadata The blockentities and entities are further encoded in BinaryWriter format , with another layer of protobuf format under that, which is why they are not fully decoded on the command line. Sample data: $ sqlite3 modtestworld.vcdbs "SELECT writefile('chunk_' || position || '.binpb', data) FROM chunk LIMIT 1;" 92 $ protoc --decode ServerChunk --proto_path=$(dirname ~/schema.proto) ~/schema.proto &\231\236\343\322\350s\344\024\020\001" lightCompressed: "(\265/\375`\000/\325\003\000D\005\000\000\000\000p\000\000\000`\000\000\377\377\377\333\377\377\333\377\377\377\375\377\377\377\333\377\377\333\377\377\333\377\377\333\377\377\333\377\377\333\377\377\333\377\377\000\000\000 \000\000$\000\000$\000\000\000\002\000\000\000$\000\000$\000\000$\000\000$\000\000$\000\000$\000\000$\000\000\025\020\000\217\336\207\214\033m\310\270\321\2740T:j\324x\264\0372n\264!\343F\363\302X\216I\206F\021\001" lightSatCompressed: "(\265/\375 \024\241\000\000\000\000\000\000\026\000\000\000\025\000\000\000\024\000\000\000\023\000\000\000" EntitiesCount: 3 EntitiesSerialized: "\nEntityItem\0061.19.8Q\000\000\000\000\000\000\000\006\rextraInfoText\000\007\titemstack\000\000\000\000\000\324$\000\000\004\000\000\000\000\006\005stats\000\000\306d1\323\342?\037A\000\000\000\000\000\000\010@\377\372?X\264?\037A\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\200\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\306d1\323\342?\037A\000\000\000\000\000\000\010@\377\372?X\264?\037A\004item\004\tdeathTime\250\034\337B\000\000" EntitiesSerialized: "\nEntityItem\0061.19.8R\000\000\000\000\000\000\000\006\rextraInfoText\000\007\titemstack\000\001\000\000\0009\004\000\000\001\000\000\000\000\006\005stats\000\000\000\000\000\000\363?\037A\000\000\000\000\000\000\000@v\3735X\271?\037A\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\000\000\000\000\000\000\363?\037A\000\000\000\000\000\000\000@v\3735X\271?\037A\004item\004\tdeathTime\225\303\201B\000\000" BlockEntitiesCount: 21 BlockEntitiesSerialized: "\017VEBELVGenerator\001\004posx\372\317\007\000\001\004posy\003\000\000\000\001\004posz\352\317\007\000\005\tblockCode\032vinteng:velvgenerator-east\005\014machinestate\010Sleeping\001\010priority\005\000\000\000\002\014currentpower\000\004\000\000\000\000\000\000\010\013connections\022\000\n\020\010\000\022\014\n\n\010\375\237\037\020\003\030\352\237\037\010\nnetworkids\006\000\n\004\010\000\020\001\006\tinventory\001\006qslots\001\000\000\000\006\005slots\007\0010\000\001\000\000\000[\002\000\000\003\000\000\000\000\000\000\006\017faceConnections\t\tfaceNorth\000\t\010faceEast\000\t\tfaceSouth\000\t\010faceWest\000\000\004\007genTemp\000\000\240A\001\007maxTemp\024\005\000\000\004\014fuelBurnTimeWj A\000" ... BlockEntitiesSerialized: "\017VEBELVGenerator\001\004posx\371\317\007\000\001\004posy\003\000\000\000\001\004posz\355\317\007\000\005\tblockCode\032vinteng:velvgenerator-east\005\014machinestate\002On\001\010priority\005\000\000\000\002\014currentpowers\002\000\000\000\000\000\000\010\013connections\022\000\n\020\010\000\022\014\n\n\010\375\237\037\020\002\030\356\237\037\010\nnetworkids\006\000\n\004\010\000\020\025\006\tinventory\001\006qslots\001\000\000\000\006\005slots\007\0010\000\001\000\000\000[\002\000\000\004\000\000\000\000\000\000\006\017faceConnections\t\tfaceNorth\000\t\010faceEast\000\t\tfaceSouth\000\t\010faceWest\000\000\004\007genTemp\000\200\242D\001\007maxTemp\024\005\000\000\004\014fuelBurnTimeUh\327A\000" GameVersionCreated: "1.19.8" DecorsSerialized: "" savedCompressionVersion: 2 liquidsCompressed: "\000\000\000\000" BlocksPlaced: 65 BlocksRemoved: 49 mapchunk Each mapchunk row has 2d data for a column of chunks in the game. Most of the fields in the mapchunk, such as RainHeightMap, are 32x32 arrays that are indexed by the relative X Z block position. Each entry in the RainHeightMap array holds the y coordinate of the highest block in the column that is solid enough to block rain. It is updated as blocks are broken and placed. Whereas, the WorldGenTerrainHeightMap array is not updated after the chunk is generated. Despite the name, the mapchunk table does not directly store the client's minimap. The client actually generates the minimap by looking getting the Y level for every block from the RainHeightMap, looking up that block from the chunk data (not mapchunks), then converting that block into a pixel. $ sqlite3 modtestworld.vcdbs "SELECT writefile('mapchunk_' || position || '.binpb', data) FROM mapchunk;" $ protoc --decode ServerMapChunk --proto_path=$(dirname ~/schema.proto) ~/schema.proto ~/mapchunk.txt Moddata { key: "lastSnowAccumUpdateTotalHours" value: "\t\000\000\000\000\000P\223@" } RainHeightMap: 116 ... RainHeightMap: 119 currentpass: 6 WorldGenTerrainHeightMap: 119 ... WorldGenTerrainHeightMap: 120 YMax: 133 CaveHeightDistort: "py\177\177\210\217\217\213\203\177\177~|\177\177\206\220\227\234\235\234\227\217\203\177\177\177\177\177\202\205\200z\177\200\213\224\231\232\227\222\212\202\177\177\177\204\215\222\225\226\227\224\216\204\177zqnpu{\177~~\177\203\216\225\232\235\236\235\232\223\213\206\207\212\216\216\213\211\206\203\177\177tia_`dhlnz\177\177\205\214\223\232\240\243\242\236\225\216\211\210\205\200\177\177\177\177zqg_ZYZ\\_actu}\177\177\210\224\237\244\245\240\227\214\203\177\177\177xsrsqkd]ZYZ[\\\\\\qnr{\177\201\220\234\242\242\234\221\203\177\177xqkilpsqjc_]^_]ZXsor{\177\204\222\233\237\235\226\211\177\177vqlhgksz{vmgeed`ZV|xz\177\177\214\227\235\236\233\223\207\177\177xtqnkmu~\177\177vmjjic[V\177\177\177\177\206\222\233\237\240\234\226\215\202\177\177\177\177|vsu|\177\177xmhhie^X\200\177\177\203\213\225\235\241\242\240\234\227\216\205\201\201\201\177\177zvy~}ticcee`[\202\201\201\204\212\223\233\240\243\243\241\236\227\220\215\216\217\213\200\177}{}|sga`aa_]\177\177\177\177\204\214\225\235\241\243\243\241\233\225\222\224\226\225\216\202\177\177\177\177|oea_^]^\177\177\177\177\177\200\213\225\234\241\242\241\234\225\221\223\227\231\226\215\204\201\203\202\177\177sib^]a||{{}\177\177\211\223\233\240\240\234\225\217\217\224\230\227\221\211\207\213\215\212\200\177uibagusrqrv\177\177\210\225\236\240\236\230\220\215\220\224\225\220\211\207\213\220\220\213\177\177rjjtrmkjjlt\177\201\223\236\243\243\236\226\220\220\223\225\221\212\205\207\214\217\215\203\177xqu\177rljihjq\177\201\225\241\246\246\243\235\227\226\230\231\226\217\207\204\207\212\211\201\177zw~\177tnmmlnv\177\207\231\244\250\250\246\243\240\237\240\240\236\227\216\207\206\207\206\177\177{z\177\201wssssu|\177\212\232\244\250\251\251\250\246\246\247\246\244\236\225\215\212\211\206\177\177}{\177\177xxyxwvz\177\205\224\237\245\250\252\252\252\252\251\250\246\240\227\216\212\211\207\201\177\177|\177\177wyywsopx\177\207\225\237\246\251\253\252\252\250\247\243\235\221\206\201\202\203\200\177\177|{\177oqpnifejv\177\207\226\240\246\250\250\246\244\241\234\224\207\177\177\177\177\177\177}wvydcaa`_`ckz\177\216\232\240\242\241\236\233\231\225\215\200\177{z}}ytqps[WVX\\^`bht\177\211\225\231\231\227\225\224\224\223\217\204\177|vspmkkmpVRRU[bffju\177\211\224\226\224\222\221\224\227\231\227\217\203\177zpjghkptUQQU^gkjkv\177\213\225\227\226\225\227\232\235\237\236\230\216\200\177tmjlry}VSSXaikiis\177\212\226\233\235\235\237\241\242\242\240\233\221\203\177xrsx\177\177\177[WW\\djhdcl\177\204\224\235\241\242\243\243\242\240\234\226\213\177\177vu{\177\177\201\177b``chje__fw\177\215\231\236\240\240\237\233\226\222\213\200\177wru\177\177\204\207\203jkklljc]\\bp\177\200\215\224\227\226\223\216\211\205\200\177\177uqu\177\177\206\206\200qvwtoia[[_it\177\177\201\206\207\204\200\177\177\177\177\177{wz\177\177\205\202\177v|~ypg^ZY]dkry\177\177\177\177\177\177\177\203\202\177\177\177\177\177\203\206\200\177" // One for every X, Z block position in the map chunk. The value is unused and always 0. SedimentaryThicknessMap: 0 ... SedimentaryThicknessMap: 0 TopRockIdMap: 6774 ... TopRockIdMap: 6776 SnowAccum { Key { X: 511840 Y: 511808 } Value: -1 } ... SnowAccum { Key { X: 511871 Y: 511839 } Value: -1 } WorldGenVersion: 2 mapregion The table mostly contains world generation information. It is the serialized format of IMapRegion . Each row contains 2D data for a 16x16 section of mapchunks. Most of the fields are IntDataMap2Ds . Each IntDataMap2D covers a square section (not rectangle) of the world. The Size field describes the size of one of the sides, such that there are Size*Size entries in the array. To smooth out world generation at the border of the mapregions, many of the IntDataMap2D s contain padding, which are entries that belong to neighboring map regions. This way when the neighboring map region is generated, its border is seeded with values that match its neighbor. The TopLeftPadding field indicates both how many extra rows are on the top of the matrix and how many extra columns are to the left. The BottomRightPadding field indicates how many extra rows are on the bottom of the matrix and extra columns on the right. Both paddings are added to the Size field, which is one reason Size can be greater than 16. The InnerSize property is the Size minus the padding. Even the InnerSize can be larger than 16 (the number of map chunks covered by the map region). The IntDataMap2D is interopolated during the world generation, and a larger InnerSize provides extra resolution. For example, the ore generation maps have 4 entries per chunk for an inner size of 32 (instead of 16). There are multiple RockStrata IntDataMap2D s. Each one corresponds to one of the variants in the survival/worldgen/rockstrata.json file. It is possible to attach server-side only mod data to a map region with IMapRegion.SetModData , but no tutorial has been written yet. $ sqlite3 'creepy kingdom lands.vcdbs' "SELECT writefile('mapregion_' || position || '.binpb', data) FROM mapregion;" 213896 ... $ protoc --decode ServerMapRegion --proto_path=$(dirname ~/schema.proto) ~/schema.proto Set Texture base path and choose the Vintagestory\assets\survival\textures folder. Do the same for shapes with File > Set Shape base path and choose the Vintagestory\assets\survival\shapes folder. Hold Shift on the Size/Position/Origin or UV arrows to increase its value only by 0.1 or Ctrl for 0.5 instead of 1. Hold Ctrl on the Position arrows to not move the origin point. In the Size/Position/Origin Text fields, use the mouse wheel to modify the value increments by 1. Hold down Shift to do increments of 0.1. Or hold Ctrl on the position fields to also move the origin. Hold Ctrl and click on a box in the middle window. It will select that box. To move the box around, keep holding Ctrl . To modify the size, instead of the position, hold Ctrl with the right mouse button held down. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Model Creator Basics • Tutorials • Model Animation • All VSMC Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:VS_Model_Creator/Animation_Intermediate TITLE: Modding:VS Model Creator/Animation Intermediate --- CONTENT --- This page is a candidate to be rewritten. Reason: The VSMC Tutorials are being rewritten and restructured. Thank you for the patience! This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. Below can be found modified version of an VSMC animation tutorial [1] created by Luke . This is an intermediate tutorial and it is recommended that you have completed the Animation Basics Tutorial before starting this one. Contents 1 Preparing 2 Animation Process 2.1 Step 1: Keyframes 2.2 Step 2: Frame Distances 2.3 Step 3: Patterns 2.4 Step 4: Body Connections 2.5 Step 5: Adding Keyframes 3 Notes 4 References Preparing Vintage Story has its own model creator called VS Model Creator that allows anyone to create, texture, and animate custom shapes for use in Vintage Story mods. The newest version can be downloaded here . This tutorial uses a premade model. You can use your own or download Luke's model here (trusted file, published by game developers team member). After loading it in, go ahead and click the “Keyframe” tab to open up the animator. Animation Process This will be an intermediate level tutorial done mostly in the form of techniques and ideas to keep in mind when animating. In particular, we'll be working with a walk cycle for Balduranne's panther model. Okay, so we’re working on a walk cycle. For this example, let’s focus on quadruped walking. Here's the original walk cycle: Step 1: Keyframes When making a walk animation, I’d suggest starting with two keyframes. More generally speaking: when making an animation, I’d suggest starting with keyframes that show the extremes of motion. For a walk cycle, this is the moment when the animal reaches its front foot to step forward and the moment when the animal pushes its hind foot off the ground behind it. So a reach and a push. And this happens with both legs. Now, I know what you’re thinking, “That sounds like four keyframes instead of two”. Well, you’re right, but that’s harder to do at the beginning. For now, I’d say synchronize the reach and push to be at the same time and then make one keyframe for the right legs and one for the left legs. That might be a little confusing. What I’m saying is, make one keyframe where the left front leg and left hind leg are reaching forward, and then make another keyframe where the right front leg and the right hind leg are reaching forward. Result of changes: Step 2: Frame Distances Now, look at how jerky the motion is. Walk cycles don’t have to be perfectly smooth, but the very nature of walking is generally stable and repetitive. If your keyframes are too far apart from each other, the animation will have slow moments and fast moments. This can be good later on (particularly if you want a limping cycle), but for now we want stability. Stability also helps us see what else we can do to improve the animation, since jerkiness can be harder to read. So generally what I do is, I’ll place one keyframe at frame #0, and then the other keyframe at the middle of the animation. So since this animation has 35 frames total, I’ll put it at frame #17. This ensures that the frames are an even distance from each other, which gives us ideal stability. You don’t have to use #0 and #17 if you don’t want to. So long as the keyframes are equidistant from each other, you can place them anywhere and it will work fine, but using the beginning and middle of the animation just makes it a lot simpler and cleaner, if you ask me. Result: Step 3: Patterns Okay, this can be tricky because a lot of animals will vary their walking patterns depending on their speed. Some of them have like eight different patterns. In this particular case, the panther is actually made with the wrong pattern for this speed. Notice how the legs are paired diagonally (meaning the right front leg and the left hind leg have the same motion)? This is a trot, and it’s something they tend to do more when they’re sneaking as opposed to walking. Their walking pattern is actually more of a pace (I think it’s a stepping pace, if we’re being specific), where their legs are paired laterally, meaning the right front leg and the right hind leg have the same motion. Look up horse or dog gaits for a better understanding of walk/run patterns. It makes it a lot easier. They even include how many feet are touching the ground at one time. [2] Apparently equestrian people love this stuff, idk. Result: Step 4: Body Connections Acting out animations is probably the best thing you can do. Take note of how your body moves. It moves A LOT. If you don’t know how an animal moves, just watch footage of it. If you get stuck and you’re not sure how to improve it, consider the principles of animation . As is mentioned in the article, squash and stretch are very important. Even though the ah, stretch tool doesn’t work in the editor, you can still simulate stretch through rotation and positioning. Consider the panther: When he’s reaching his front left paw forward, what do you think his shoulder is doing? It’s pushing the arm forward, right? To do that, the torso itself is rotating towards the arm. Specifically, it’s rotating down and to the left, and this can be conveyed through all three axes. Try reaching forward with your own arm and watch how your torso rotates. It probably does the same thing as the panther. A helpful mindset is to think that every piece of the animal is connected. If you move one, it should invariably affect the rest of the pieces. Oftentimes the causality isn’t that simple, but maybe that’s getting too complicated. Consider the panther again: You can follow along with this video to see what I’m talking about. I’ll break down the motions and connections step by step: Starting the motion from the rear, the panther contracts its right hind leg, pushes into the ground, and uses that force to propel itself forward. This causes the right hind leg to extend as the body is pushed forward. The panther’s hips are attached to the right hind leg, so they’re pulled towards that leg. Pulled down and to the right. Now we have the left hind leg, which is attached to the hips. Since the hips are rotating down and to the right, the left hind leg is now lifted up and to the right. This places it closer to the center of the body, so it acts as a vertical pillar to hold the panther’s weight after it finishes its right hind leg push. Okay, now what else is attached to the hips? The torso. Since the lower half of the body is reaching down and to the right, the torso has to counterbalance or the panther will fall over. So the torso rotates up and to the left. The front left arm, having been pushed forward by the rotating torso, is now in a perfect position to reach forward for another step. The front right arm has been pulled back into a central position like the left hind leg. It also acts as a pillar to hold the panther’s weight while the front left arm is reaching. What else is connected to the torso? The neck. As the torso rotates, it pushes and pulls the neck with it. Pull to the right, push to the left. Since the panther wants to look straight ahead, the neck has to compensate for the torso, so it rotates in the opposing direction. This keeps the head fairly stable, but it will compensate a bit depending on how much the torso affects the neck. The direction it rotates in is generally just towards the front. B-but what about the ears and the tail?? Eh, they’re kind of autonomous. Sometimes they don’t rotate at all, sometimes they do. You’ll have to experiment to see what you like. Generally, I’ll animate them to show emotion, as a lot of animals will use them when attacking or fleeing. Ah, text is a poor medium for this. A good understanding of newtonian physics is also very useful here in that it can help you determine where to simulate force (every action has an equal and opposite reaction) and that can translate into a sense of weightiness in the animation. Yeah, anatomy and physics are super useful. Also music. Results: Okay, that’s looking better. The body movements are still a little awkward, but it’s good enough for now. Sometimes you just have to look at it again the next day to see what needs fixing. Step 5: Adding Keyframes Okay, remember what I said at the beginning about how this walk cycle should actually have at least 4 keyframes? The panther’s legs may move laterally, but they don’t move simultaneously. When the right front leg is reaching into the air, the right hind leg is already touching the ground to support it. They’re on the same rhythm, but not the same beat, if that makes sense. Ah, I can’t remember the proper musical term. So what we want to do is, add more keyframes so that each leg can touch the ground at its own pace, creating a more natural walk for the panther. You might want to look around for more information about the Stepping Pace to figure out exactly how the feet are supposed to act. If you don’t want to do that, here’s a summary: It walks in a 1-2—3-4 pattern, with a larger gap between 2 and 3. And it usually has three feet down and one foot in the air at any given time. The hind legs always land before the front legs, so we can say the pattern is hind-front—hind-front. And just to make that more visual, let’s just say it’s Right Hind-Right Front—Left Hind-Left Front. Implementing this might be a little complicated if you’ve never tried it before. It’s kind of done by adding keyframes just for one leg, duplicating the original values for the leg’s rotation and position, and then removing their original keyframe. The idea is just that you’re transposing the whole animation cycle for one leg onto a different part of the rhythm. And also making sure the leg appears to stay in place on the ground when it’s not being lifted. Definitely make sure you have a backup file saved at this point, if you’re not used to it. Results: Ah, it’s fun working with more anatomically realistic models. And more work lol. So maybe it’s just me, but I feel like after this step it really started to come together. Notice how fluid the movements are? I only did the left side because A) it’ll hopefully make it easier to see what I did with the keyframes, and B) it’s late. I left out a lot of the technical steps in favor of the abstract, because I feel that's more easily applied to more animations. Here's the updated json file if you wanna look it over: big cat final (also trusted file for same reason). Notes Stratch tool is no longer a thing in new versions. References ↑ https://www.vintagestory.at/forums/topic/505-vs-model-creator-basics/?do=findComment&comment=3831 ↑ https://en.wikipedia.org/wiki/Horse_gait Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Model Creator Basics • Tutorials • Model Animation • All VSMC Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:VS_Model_Creator_tutorials TITLE: Modding:VS Model Creator tutorials --- CONTENT --- Other languages: English русский Vintage Story has its own model creator called VS Model Creator that allows anyone to create, texture, and animate custom shapes for use in Vintage Story mods. The newest version can be downloaded here . Below can be found modified version of a modeling tutorial created by Balduranne . Click here to visit the original forum thread where this tutorial was originally posted. Contents 1 Modeling 1.1 Modeling basics 1.2 Model duplication 2 Texturing 3 Parenting Modeling Modeling basics The model creator only allows working with cubes or parts of cubes, which can be created by clicking the Red/Green/Blue icon below the selection window. Once something is selected, the modeling can begin. Voxel Size determines the shape and size of the object, Position moves it around the grid, while Origin is from where the model is supposed to 'start' from. This mostly comes into play when rotating the object. When adding more cubes, it's important to keep in mind what cube is selected. In this case, Cube2 is docked under Cube1, so if the latter is moved, Cube2 will end up moving along with Cube1. Cube 3 in this example is sitting on its own and is unaffected by the other two. After a little fiddling, a model is created. In this case, it's a candle holder with a candle: Model duplication Duplicating an element and moving it around can speed up the modeling process. It's less time-consuming than making a fresh object and moving it in the correct place and shape: I'm sure very often you'll find yourself just applying the same texture to the entire object. This is where the Entity Texturing Mode comes into play: Once clicked, the entire model will be assigned one texture. It's handy, but we still need to tweak it by hand. While the mode is on, you can only move an entire object around in the UV map editor (Left), that's hardly ideal in some cases. That's why we want to disable the Entity Texturing Mode (Don't worry, the texture will stay on), and go back to the Face menu and manually move the texture around. This is especially important in cases where we want to use a single texture for a model, as is the case with my Loom model, or the NPC models (Including the player one): Furthermore, if you're using one texture, you can tweak the texture size for larger objects: Another useful trick is exporting the UV map of your model. Once you have it unwrapped (That is, spread around your texture sheet), you can export the UV map to make a proper texture for it, or in the case of the player model - Make your own skin for it: That should about cover the basics. One important thing to keep in mind is that one block is 16x16x16 (The size of the grid) in the editor. Model Editor controls, for reference - They're in the Controls & Credits submenu (It really helps to memorize them!): Once you have the hang of it, you can use Render Passes. An explanation of the different modes can be found by clicking here , but in layman's terms, they determine how a block will behave visually (Think - Glass, Ice, etc): Texturing Once we have our model, we can slap some textures on: Let's look at this interface - Textures are assigned separately for each side of the model, which we need to apply manually. We can speed it up by clicking Copy and then Paste for every side. If a side is not going to be visible in game, you can deselect "Enabled", which will make that side invisible. But first we need to click Image and import a texture we want to use. To make life easier, you can set the default texture path to take textures from. This is useful if you're going to use vanilla textures. Once a texture is imported - Click on it and select Apply. Please note that if your texture is outside of the game folder when Imported, you will need to tweak the model's shape (.json) file to reflect its location for the game. You will have to do this most of the time. If we want to fine tune our UV Mapping (Aka how the texture is lined up on the model), we can use the Face UV controls to change the shape of the face. In this case, we stretch it out so that all the rivets are visible. One should also drag and move around the individual faces in the leftmost menu for best effect: Once we're satisfied with the effect, we can save the model and use it in a mod: Parenting The parenting system allows you to attach models to the individual parts of a set backdrop model. This is how armor models stay attached to the arms and legs of the player model while that model is performing its own set of animations like running or crouching. This tutorial will cover how to use this system to implement armor models onto the player, but the same method can be used to implement custom clothing models or hairstyles for the player. If you use a different backdrop you can add custom accessories to any animated model of your choice. Now there are two ways to start, the first method starts with an existing parented model from the game, this is far easier than the second method, but it's not as flexible. This will help you learn how the models in the base game are parented so you can eventually create your own from scratch. The second method shows you how to parent any shape to any backdrop element, which gives you far more freedom. For method #1: Start by importing the shape json of an item that is similar to what you want to add. Here we will be using the "legs.json" file located in your game directory and nested in Vintagestory/assets/survival/shapes/entity/humanoid/seraph/armor/plate . Go ahead and import this model into the Model Creator by dragging and dropping it into the window. This should usually prompt you with a window asking to import a backdrop shape: Sometimes it will not prompt you with an import window and you will need to manually set the backdrop. You can do this by going to the tab "project" at the top of the window and selecting "Set Backdrop...": In the import backdrop pop-up window navigate to the following location for the appropriate seraph shape JSON to import, you should use seraph-hairless.json located in Vintagestory/assets/game/shapes/entity/humanoid/seraph-hairless for most things as it's the cleanest model to work with, but you can always create your own custom backdrop version with certain elements removed like the clothing tiles to strip the model down even more. You can also delete the face cubes if they are in the way. If you edit the seraph shape JSON be sure to save it as your own version and try not to mess with the naming of the major body parts, preferably name it something like "seraph-backdrop" and place it in an easily accessible location so you can easily import the file again. With the backdrop set your model workspace should look like the first image, note the leg armor model is still bunched up in the corner, sometimes the model will update itself and automatically fix itself, but most of the time you will need to update it manually. You can update the model by selecting any piece of the leg armor model and moving its position once in any direction, then just press Ctrl + Z and undo the move. This should update the model and then it will look like the second image: Now that you have the model and backdrop loaded into the model creator you are free to edit the shape however you'd like, just be sure to make your edited/added shapes branch off the original parts, the original parts are pre-parented to the backdrop so any newly added voxels must be attached to the tree of that part. Note how in the image "ArmorKneeSide9" is attached to and under the tree of "ArmorLowerLegL", Most major shapes at the start of trees or under the root are usually parented to the backdrop already. Just export the finished shape JSON and use it as normal, and be sure to edit the texture locations in the exported JSON to their correct locations. For method #2: This method should be used if you already have a model created and want to parent it to the appropriate part of the body. We will start with these boots, which I want to attach to the bottom of each leg on the player model. Navigate up to the tab "project" at the top of the window and select "Set Backdrop...", as mentioned in method #1 you should use the seraph-hairless.json shape located in Vintagestory/assets/game/shapes/entity/humanoid/seraph-hairless , or a custom stripped down version. Select your backdrop shape of choice and then press the "Import backdrop" button on the pop-up window. Your model creator window should now show an un-textured seraph model in the center like this: Next, select the part of your shape that you want to have attached to the backdrop model, here we are selecting the right boot main cube (it's the main cube because all the detail cubes are attached to it in the element tree), next scroll down on the left side of the model creator window and at the bottom you will see a "Not set" button under "Stepparrent element", press this button and you will see the following pop up window: Now in this pop-up window, you will need to select the part of the backdrop model you want this cube you selected to be attached to, in this case I selected the right boot's main cube so I want it attached to the lower part of the right leg which in this case is all the way at the bottom of the "Element" drop down list. Once you select the right element go ahead and press the "Apply" button. Note: The element names under this drop-down menu correspond to the exact names on the player model used in the game. This is why it's important to not rename them if you use a custom backdrop model. Also, if you don't know what an element corresponds to name-wise, simply open up the backdrop shape file your using in a separate model creator window and select the element you want to know the name of and it should show in the left window under the "New element" button and tree window. After pressing the "Apply" button in the window it should close and then attach that cube to the backdrop element you selected in the list: Now repeat this process for each element you want attached to the backdrop model, you may need to modify the position of each model to properly align it to the backdrop. Also, when creating armor/clothing models be sure to separate the model up into several major cubes so they are easy to parent to the seraph model. For instance, break up the leg armor into two major sections, the upper and lower leg, then add your detail cubes under the tree/ attached to one of these major parts. Nearly every single shape that shows up on the player model can be imported/parented/and edited this way, and you can also extend this process to create armor models for other entities like adding trader clothing. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Model Creator Basics • Tutorials • Model Animation • All VSMC Pages --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:Variants TITLE: Modding:Variants --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Contents 1 Basic Understanding 2 Usage in JSON 2.1 Defining Variant Groups 2.1.1 SkipVariants 2.1.2 AllowVariants 2.2 Wildcards 2.3 {Variant Group} Substitutions 2.4 ByType Properties 3 Detailed Understanding 3.1 Calculating Variant Asset Locations 3.2 Analysing ByType & {VariantCodes} 4 Conclusion Basic Understanding Vintage Story has a very large number of items, blocks, and entities, and this is helped by having variants of many objects. Take, for example, the hammer item. There exists 9 hammers in the game and each one has identical functionality, with some minor details changed. Without variants, each hammer would have to be created individually which would result in a lot of time, and a lot of different files. By using variants, a single hammer item is created, and variant groups are added to it. Most properties in our hammer item file remain the same, however some are changed dependent on the variant of hammer. Specifically, each hammer has identical functionality, however each hammer has a different texture, tooltier, durability, and attack power. The hammer is referenced throughout this page, so here is the file in its entirety: itemtypes/tool/hammer.json { code : "hammer" , class : "ItemHammer" , s t orageFlags : 257 , a ttr ibu tes : { ha n dbook : { groupBy : [ "hammer-*" ] }, t oolrackTra nsf orm : { ro tat io n : { y : 1 , z : -1 }, translat io n : { x : -0.2 , y : 0.02 }, scale : 1.5 , }, grou n dS t orageTra nsf orm : { translat io n : { x : 0 , y : -0.16 , z : 0.33 }, ro tat io n : { x : 26 , y : 88 , z : -87 }, scale : 1.01 } }, behaviors : [{ na me : "GroundStorable" , proper t ies : { layou t : 'WallHalves' , wallO ff Y : 1 , spri nt Key : true , selec t io n Box : { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 1 , y 2 : 0.1 , z 2 : 1 }, collisio n Box : { x 1 : 0 , y 1 : 0 , z 1 : 0 , x 2 : 0 , y 2 : 0 , z 2 : 0 }, } }], varia nt groups : [ { code : "metal" , s tates : [ "copper" , "tinbronze" , "bismuthbronze" , "blackbronze" , "gold" , "silver" , "iron" , "meteoriciron" , "steel" ] }, ], t ool : "hammer" , damagedby : [ "blockbreaking" , "attacking" ], heldTpHi t A n ima t io n : "smithingwide" , shape : { base : "item/tool/hammer" }, te x tures ByType : { "*" : { "metal" : { base : "block/metal/ingot/{metal}" }, "wood" : { base : "item/tool/material/wood" } } }, t ool t ierby t ype : { "*-copper" : 2 , "*-gold" : 2 , "*-silver" : 2 , "*-bismuthbronze" : 3 , "*-tinbronze" : 3 , "*-blackbronze" : 3 , "*-iron" : 4 , "*-meteoriciron" : 4 , "*-steel" : 5 }, durabili t yby t ype : { "hammer-stone" : 60 , "hammer-gold" : 250 , "hammer-silver" : 250 , "hammer-copper" : 500 , "hammer-tinbronze" : 750 , "hammer-bismuthbronze" : 900 , "hammer-blackbronze" : 1100 , "hammer-iron" : 1800 , "hammer-meteoriciron" : 2100 , "hammer-steel" : 4500 }, a tta ckpowerby t ype : { "hammer-stone" : 1 , "hammer-copper" : 1.25 , "hammer-gold" : 1.5 , "hammer-silver" : 1.5 , "hammer-bismuthbronze" : 1.5 , "hammer-tinbronze" : 1.75 , "hammer-blackbronze" : 2 , "hammer-iron" : 2.25 , "hammer-meteoriciron" : 2.35 , "hammer-steel" : 2.5 }, crea t ivei n ve nt ory : { "general" : [ "*" ], "items" : [ "*" ], "tools" : [ "*" ] }, f pHa n dTra nsf orm : { translat io n : { x : 0.0468 , y : -0.2 , z : 0 }, ro tat io n : { x : -33 , y : 7 , z : 90 }, scale : 2.75 }, guiTra nsf orm : { translat io n : { x : 0 , y : 0 , z : 0 }, ro tat io n : { x : -77 , y : 46 , z : 8 }, origi n : { x : 0.59 , y : 0.5 , z : 0.49 }, scale : 2.6 }, grou n dTra nsf orm : { translat io n : { x : 0 , y : 0 , z : 0 }, ro tat io n : { x : 0 , y : 0 , z : 0 }, origi n : { x : 0.5 , y : 0.45 , z : 0.5 }, scale : 4.5 }, t pHa n dTra nsf orm : { translat io n : { x : -0.65 , y : -0.48 , z : -0.52 }, ro tat io n : { x : 90 , y : 1 , z : 0 }, scale : 1 } } Usage in JSON Variant groups are extremely useful for content and code modders, so it is important to know exactly how to use them in your assets. Defining Variant Groups To create a set of variants, you will need to define at least one variant group, which includes the variant code , and all the states for that variant. varia nt groups : [ { code : "metal" , s tates : [ "copper" , "tinbronze" , "bismuthbronze" , "blackbronze" , "gold" , "silver" , "iron" , "meteoriciron" , "steel" ] } ] The variant group for the hammer is shown above. The code is metal , and the states are a list of metals. Each state will result in a new object, with the state ID being added onto the end of the item's code. Hammers, for example, all have a code of " hammer-{metal}" , where {metal} is replaced with the appropriate state. An object can have any number of variant groups. Take a look at the seashell.json variant groups: varia nt groups : [ { code : "type" , s tates : [ "scallop" , "sundial" , "turritella" , "clam" , "conch" , "seastar" , "volute" ] }, { code : "color" , s tates : [ "latte" , "plain" , "seafoam" , "darkpurple" , "cinnamon" , "turquoise" ] } ] Seashells have two variant groups: t ype and color . For each state in type , there exists every state in color , giving a total of 42 different sea shells. Every sea shell in game has a code of " seashell-{type}-{color} ". Variant states can also be created from any World Properties : varia nt groups : [ { code : "rock" , loadFromProper t ies : "block/rock" }, ], SkipVariants On occasion, a combination of variant groups can cause the creation of unwanted objects. Say, for example, that turquoise clams were unrealistic and shouldn't be in the game. To remove variants, you can use the ' SkipVariants' property. The seashell variants would now look like this: varia nt groups : [ { code : "type" , s tates : [ "scallop" , "sundial" , "turritella" , "clam" , "conch" , "seastar" , "volute" ] }, { code : "color" , s tates : [ "latte" , "plain" , "seafoam" , "darkpurple" , "cinnamon" , "turquoise" ] } ], skipVaria nts : [ "seashell-clam-turquoise" ] Any code within skipVariants will not be created in game. AllowVariants Sometimes, you may want to specifically choose which variants should be created in game. For example, you may only want a specific selection of seashells to be created in the game for testing purposes. To only allow specific variants, you can use the ' AllowedVariants' property. The seashell variants would now look like this: varia nt groups : [ { code : "type" , s tates : [ "scallop" , "sundial" , "turritella" , "clam" , "conch" , "seastar" , "volute" ] }, { code : "color" , s tates : [ "latte" , "plain" , "seafoam" , "darkpurple" , "cinnamon" , "turquoise" ] } ], allowedVaria nts : [ "seashell-scallop-seafoam" , "seashell-clam-darkpurple" , "seashell-sundial-latte" , "seashell-sundial-seafoam" ] If allowedVariants exists, any code that is not within it will not be created in game. It is important to remember that skipVariants takes presedence over allowedVariants - If an element exists in both the allowed and skip list, it will be skipped. SkipVariants and AllowedVariants both accept wildcard parameters. Wildcards When referencing asset locations from a json file, wildcards can be used to reference any number of assets. By using the wildcard character ( * ) inside an asset location, any matching code with satisfy the wildcard. The use and purpose of a wildcard depends on where it is being used. Some examples of wildcards are: hammer-* - Any hammer will satisfy this condition. seashell-scallop-* - Any seashell with the type scallop . seashell-*-latte - Any seashell with the color latte . *-copper - Any item, block, or entity who's last variant state is copper . Wildcards are very versatile and are used commonly throughout json files. Wildcards can be used in, but not including: SkipVariants and AllowedVariants when creating an object. ByType variant groups. Any matching code will use the byType property. Recipe ingredients. Any matching code will work as a valid ingredient for the recipe. {Variant Group} Substitutions One way to specify slight differences between object variants is by using the variant substitution system. In an asset file that defines variants, any property that accepts a string can include a variant code surrounded by { }'s. This will then get substituted by the variant's state. For example, each seashell chooses its texture based on its color variant, and its shape based on its type variant. te x tures : { color : { base : "block/creature/seashell/{color}" } } shape : { base : "block/seashell/{type}" } For example, a plain clam would look for a texture at textures/ block/creature/seashell/plain , and would look for a shape at shapes/ block/seashell/clam . ByType Properties Another way to add differences to variants is by adding ' byType ' onto the end of any property in a file that has defined variants. When using a key with byType, the value becomes a new dictionary, with the keys being asset locations and the values being whatever the original property was. For example, the following code would set all hammers to have the same durability: durabili t y : 60 , However, hammers different durabilities based on their variant type. The following code sets different durabilities for each hammer type: durabili t yby t ype : { "hammer-stone" : 60 , "hammer-gold" : 250 , "hammer-silver" : 250 , "hammer-copper" : 500 , "hammer-tinbronze" : 750 , "hammer-bismuthbronze" : 900 , "hammer-blackbronze" : 1100 , "hammer-iron" : 1800 , "hammer-meteoriciron" : 2100 , "hammer-steel" : 4500 }, Our durability property has now become durability bytype , and accepts a dictionary of all the hammers to each have their own durability. By using a combination of ByType Properties, wildcards, and Variant Substitutions, objects can be created much more efficiently. The following code is an excerpt from the blade.json tool. shapeByType : { "blade-falx-*" : { base : "item/tool/blade/falx/{metal}" }, "blade-longsword-admin" : { base : "item/tool/blade/admin" }, "blade-*-ruined" : { base : "item/tool/blade/ruined/{type}" }, "*" : { base : "item/tool/blade/{type}" } }, There's a few important things to note here: All JSON properties are case-insensitive in content mods, including the ByType suffix. The ByType keys can use wildcards to group variants together by their type. The values in ByType can still use variant substitutions. A wildcard of " * " will simply satisfy every object. ByType keys are analysed from top to bottom - If an object's code satisfies more than one key in byType, it will always use the topmost entry. Detailed Understanding Calculating Variant Asset Locations In Vintage Story, all items, blocks, and entities are loaded as registry objects ( GitHub ). When a registry object is loaded into the game, before any other properties are read, both its code and variant properties are analysed. There exist 3 json properties to control the creation of variants, which are: VariantGroups - An array of all variant groups available for the object. SkipVariants - An array of wildcards - Any variants that match these wildcards will never be created. AllowedVariants - An array of wildcards - If this property exists, then only variants that match these wildcards will be created, unless they exist in the skip list. When these arrays are loaded, the game begins to resolve the variant entries by creating unique asset locations for each variant entry. There can be numerous variant groups for any object, so every possible permutation for all the variant states is calculated. This is easy to see if we analyse the seashell.json file. varia nt groups : [ { code : "type" , s tates : [ "scallop" , "sundial" , "turritella" , "clam" , "conch" , "seastar" , "volute" ] }, { code : "color" , s tates : [ "latte" , "plain" , "seafoam" , "darkpurple" , "cinnamon" , "turquoise" ] } ] Seashells have two variant groups: t ype and color, meaning that every variant of seashell will follow the code of " seashell-type-color ". For every type of seashell, there exists every color of that type. In total, as there are 7 types and 6 colors , this resolves into a total of 42 unique sea shell asset locations. All permutations are analysed against the skip and allowed arrays, creating a populated list of every allowed code. Analysing ByType & {VariantCodes} When all the available variant permutations exist, all json properties are cloned for each resolved variant. Any property in a json file can be defined as ' byType' , except from the code and variant properties. The ByType properties are analysed based on the provided wildcards, and properties that do not match the variant are removed. After the byType is analysed, any variant groups referenced in curly braces ( { } ) are substituted with their appropriate variant state. When all the variants, byTypes, and variant groups are resolved, the cloned json properties are loaded as a unique object. Conclusion Variants are exceptionally useful when creating content for Vintage Story, and it is highly recommended to understand how and why variants are used. To read about more content modding concepts, go to the Content Mod Concepts page. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:WorldGen_API TITLE: Modding:WorldGen API --- CONTENT --- This page is outdated. Reason: This tutorial uses some features that are no longer available, and is due to be rewritten. Please use with caution. The content on this page is not up to date with the most recent game update. If you do wish to contribute, please request wiki edit access on the Discord . Other languages: English русский Contents 1 Intro 2 VSTreasureChest Mod 3 Getting started 4 The VSTreasureChestMod class 5 The /treasure command 6 Placing our chest 7 Adding items to our chest 8 Hooking in to the world gen API 9 Placing blocks during world gen 10 Finding where to place the chest 11 Detecting trees 12 Summary 13 Excercises Intro We will be walking through how to plug in and add your own features to the world generation code of Vintage Story by looking at a demo mod called VSTreasureChest. The full source code for this project can be found on Github . We are going to walk through coding it from scratch. VSTreasureChest Mod This mod places treasure chests around the world for the user to find. Each treasure chest has a random number of ingots in them. The mod only places treasure chests beside trees. It also adds a server command that can be run in the chat window called /treasure that places a treasure chest in front of the player. This can be useful for testing in case you want to change what items are in the chest and you don't want to bother looking for the chest for verification. Getting started Please follow the instructions here for setting up your development environment. We named our project VSTreasureChest but you can choose any name you like. The VSTreasureChestMod class The main class and the starting point of our mod will be VSTreasureChestMod. using System ; using System.Collections.Generic ; using Vintagestory.API ; using Vintagestory.API.Datastructures ; using Vintagestory.API.Interfaces ; namespace Vintagestory.Mods.TreasureChest { public class VSTreasureChestMod : ModSystem { private ICoreServerAPI api ; public override void StartServerSide ( ICoreServerAPI api ) { this . api = api ; } public override bool ShouldLoad ( EnumAppSide side ) { return side == EnumAppSide . Server ; } } } The first thing to note is the using directives at the top. Those that start with Vintagestory will allow us to access classes in the Vintagestory api. Next the StartServerSide is a method we are overriding from ModSystem that is called once when the server is start up. Here we start by just storing a reference to the ICoreServerAPI for convenient access later. We will also be registering call backs for other events here. We also override ShouldLoad to tell the system to only load this on the server side and not the client side. It would work without this but it's not necessary for the client to load this mod since all our code happens server side. The /treasure command Next we are going to add the /treasure command. To do this we must register a delegate so that we can be notified when the user types our command. We do this with the new ChatCommands feature. In StartServerSide add the following: api . ChatCommands . Create ( "treasure" ). RequiresPlayer () . WithDescription ( "Place a treasure chest with random items" ) . RequiresPrivilege ( Privilege . controlserver ) . HandleWith ( new OnCommandDelegate ( PlaceTreasureChestInFrontOfPlayer )); This is registering a treasure command with the server with a brief description that is used to describe the command for when the user types /help . The other important argument is the PlaceTreasureChestInFrontOfPlayer argument which is a reference to a method we haven't written yet. So lets add the following method below StartServerSide . private TextCommandResult PlaceTreasureChestInFrontOfPlayer ( TextCommandCallingArgs args ) { } This method will now be called when the user types /treasure command in the chat window. Wasn't that easy!!! Now we need to write the code to place our chest in that method. Placing our chest The first thing we need to do is figure out how to tell the API that we want a chest and not grass or stone or some other block. Every Block has a numerical ID that gets assigned when the server starts but this ID may change. Luckily there is a property of the Block class that identifies it and does not change. This is the Code property. Block codes can be found in Vintagestory\assets\blocktypes in json files. The one for chest is Vintagestory\assets\blocktypes\wood\generic\chest.json . If you open that file you will see at the very top the code property is set to "chest". We also need to append the type of the shape that basically tells the system which way the chest is facing. So for simplicity we are going to pick south. So the resulting block code we will be using is "chest-south". Ok lets see some code. private TextCommandResult PlaceTreasureChestInFrontOfPlayer ( TextCommandCallingArgs args ) { Block chest = api . World . GetBlock ( new AssetLocation ( "chest-south" )); chest . TryPlaceBlockForWorldGen ( api . World . BlockAccessor , args . Caller . Player . Entity . Pos . HorizontalAheadCopy ( 2 ). AsBlockPos , BlockFacing . UP , null ); return TextCommandResult . Success (); } The first line of code asks the IWorldManagerAPI to get the numeric block id for the block code "chest-south". Next we get the Block class that represents that chest. And finally we call a method called TryPlaceBlockForWorldGen and pass in an IBlockAccessor. There are many implementations of IBlockAccessor and we will cover that in a little more detail later. For now we are using this one. The second argument just calculates the world coordinates for 2 blocks in front of the player. Now if you run the mod from Visual Studio by pressing "Start" at the top you should be able to execute the /treasure command in the game. Once you do that you will have a fancy new chest appear in front of you with no items. Well I guess it's time for us to add some items. Adding items to our chest We want to add some items to the chest since after all it is a "treasure" chest. For now we are going to add various ingots to the chest. However there are lots of items in Vintagestory and I encourage you to play with this and feel free to add other items. We also want to add items at random. Not only the type of items should be random but also the ingots we add should be random. There are all kinds of ways to do this but lets write a data structure that represents a grab bag full of items and lets pull items out of that bag and place them in the chest like Santa Claus! We will create a ShuffleBag class so in Visual Studio right click on your project and go to Add->Class. Call it ShuffleBag.cs and press Add. Here's the code to place in the ShuffleBag class. using System ; using System.Collections.Generic ; namespace Vintagestory.Mods.TreasureChest { /// /// Data structure for picking random items /// public class ShuffleBag < T > { private Random random = new Random (); private List < T > data ; private T currentItem ; private int currentPosition = - 1 ; private int Capacity { get { return data . Capacity ; } } public int Size { get { return data . Count ; } } public ShuffleBag ( int initCapacity ) { this . data = new List < T >( initCapacity ); this . random = new Random (); } public ShuffleBag ( int initCapacity , Random random ) { this . random = random ; this . data = new List < T >( initCapacity ); } /// /// Adds the specified number of the given item to the bag /// public void Add ( T item , int amount ) { for ( int i = 0 ; i < amount ; i ++) data . Add ( item ); currentPosition = Size - 1 ; } /// /// Returns the next random item from the bag /// public T Next () { if ( currentPosition < 1 ) { currentPosition = Size - 1 ; currentItem = data [ 0 ]; return currentItem ; } var pos = random . Next ( currentPosition ); currentItem = data [ pos ]; data [ pos ] = data [ currentPosition ]; data [ currentPosition ] = currentItem ; currentPosition --; return currentItem ; } } } This is all basic C# stuff but the idea is that we are going to call Add on our ShuffleBag several times to load it up, then we are going to call Next to pull items out of it. This will let us kind of control the probability or rarity of items placed in chests. Items that are more rare will have fewer items in the bag. Lets add a couple of class variables to control the minimum and maximum number of items that will go in our chest. private const int MIN_ITEMS = 3 ; private const int MAX_ITEMS = 10 ; Since we will want to create a new ShuffleBag with each chest we place lets go ahead and create a MakeShuffleBag method. private ShuffleBag < string > MakeShuffleBag () { ShuffleBag < string > shuffleBag = new ShuffleBag < string >( 100 , api . World . Rand ); shuffleBag . Add ( "ingot-iron" , 10 ); shuffleBag . Add ( "ingot-bismuth" , 5 ); shuffleBag . Add ( "ingot-silver" , 5 ); shuffleBag . Add ( "ingot-zinc" , 5 ); shuffleBag . Add ( "ingot-titanium" , 5 ); shuffleBag . Add ( "ingot-platinum" , 5 ); shuffleBag . Add ( "ingot-chromium" , 5 ); shuffleBag . Add ( "ingot-tin" , 5 ); shuffleBag . Add ( "ingot-lead" , 5 ); shuffleBag . Add ( "ingot-gold" , 5 ); return shuffleBag ; } This loads up our ShuffleBag with various ingots. The only more common item is iron. Feel free to change the item counts as you see fit. One thing to note here is the API does give us an instance of Random that we can use so we pass it in to our ShuffleBag. We are almost ready to place these items in the chest but we need to create an ItemStack for each slot we will be taking up in the chest. The chest is an instance of IBlockEntityContainer which has an Inventory property. An Inventory is made up of several IItemSlot instances. We need a utility method to create a list of ItemStacks to place in those slots. private IEnumerable < ItemStack > MakeItemStacks () { ShuffleBag < string > shuffleBag = MakeShuffleBag (); Dictionary < string , ItemStack > itemStacks = new Dictionary < string , ItemStack >(); int grabCount = api . World . Rand . Next ( MIN_ITEMS , MAX_ITEMS ); for ( int i = 0 ; i < grabCount ; i ++) { string nextItem = shuffleBag . Next (); Item item = api . World . GetItem ( new AssetLocation ( nextItem )); if ( itemStacks . ContainsKey ( nextItem )) { itemStacks [ nextItem ]. StackSize ++; } else { itemStacks . Add ( nextItem , new ItemStack ( item )); } } return itemStacks . Values ; } Here we make our ShuffleBag by calling MakeShuffleBag . We calculate a grabCount which is random number between MIN_ITEMS and MAX_ITEMS that controls the number of times Santa is going to reach into his bag. We don't want to create an ItemStack for each item because we may get 3 iron ingots for example. We don't want 3 slots with one iron ingot, we want one slot with 3 iron ingots. So we create a Dictionary and add items of the same type to the same ItemStack . This method returns an IEnumerable that we can loop over so we need to add a method that can loop over a list of ItemStacks and add them to our chest. private void AddItemStacks ( IBlockEntityContainer chest , IEnumerable < ItemStack > itemStacks ) { int slotNumber = 0 ; foreach ( ItemStack itemStack in itemStacks ) { slotNumber = Math . Min ( slotNumber , chest . Inventory . Count - 1 ); IItemSlot slot = chest . Inventory [ slotNumber ]; slot . Itemstack = itemStack ; slotNumber ++; } } This method does just that. It advances the IItemSlot number each time, ensuring not to place more ItemStacks than there are IItemSlots , and sets the IItemSlot.ItemStack value to the current ItemStack in the loop. We are almost there! We have all the pieces. Now we just need to get a reference to our chest that we have already placed and pass it to this method along with the ItemStacks . Lets go back to our PlaceTreasureChest method and replace the contents with the following code snippet. Block chest = api . World . GetBlock ( new AssetLocation ( "chest-south" )); BlockPos pos = args . Caller . Player . Entity . Pos . HorizontalAheadCopy ( 2 ). AsBlockPos ; chest . TryPlaceBlockForWorldGen ( api . World . BlockAccessor , pos , BlockFacing . UP , null ); IBlockEntityContainer chestEntity = ( IBlockEntityContainer ) api . World . BlockAccessor . GetBlockEntity ( pos ); AddItemStacks ( chestEntity , MakeItemStacks ()); return TextCommandResult . Success (); The second line is just capturing the BlockPos position object so that we can use it in two places. The next is the same as before, just placing the chest in the world. Next we go back to the IBlockAccessor to get the block entity at that position. It's important to call GetBlockEntity here because a chest is an Entity . An Entity is something that has extra behavior attached to it as opposed to a normal block. This method returns an IBlockEntity which is a generic interface for all block entities. However we specifically need a block entity that has an Inventory. Since we know the block entity we just placed is a chest then it's safe to to cast the returned IBlockEntity to an IBlockEntityContainer which is a specialized version of IBlockEntity that provides access to an Inventory . Now that we have that we pass it along to our AddItemStacks method we created earlier and additionally pass in the list of ItemStacks that are created by our MakeItemStacks method that we also created earlier. Now if you run the code again and type /treasure you should have random items in there! Try it several times and you will see them change. That's really cool and all but not real fun for a game play experience. We want the player to find these chests and encourage them to explore the world! So lets plug in to world gen next! Hooking in to the world gen API Hooking into the world gen api is very similar to how we registered our command. We will pass a delegate callback that gets called when a chunk column is generated. Add the following to StartServerSide this . api . Event . ChunkColumnGeneration ( OnChunkColumnGeneration , EnumWorldGenPass . PreDone , "standard" ); The first argument is a delegate, which is a reference to a method. You will get a compiler error after adding that line because we have not yet created the OnChunkColumnGeneration method. We will create that next. The second argument is an Enum that indicates which world generation pass we need to hook into. Vintage story uses several passes to generate the world. Different features are available during different world gen passes. Since we will be placing chests next to trees, we need trees to be in the world so we choose to be notified during the EnumWorldGenPass.PreDone which tells the engine that we need neighbor chunks, block layers, tall grass, bushes and trees to be available. private void OnChunkColumnGeneration ( IChunkColumnGenerateRequest request ) { } The above code is our empty OnChunkColumnGeneration which will be called when a chunk is generated. Now we need to look at how to place blocks in this method. We have already seen the IBlockAccessor interface and we will be using that to place our blocks but we can't use the implementation we used before. In the next section we will see how to get the IBlockAccessor we need and use it to place our chest. Placing blocks during world gen The server does not generate chunks on the main game thread because the game's frame rate would drop significantly and it would really be disruptive to game play. The server spawns a separate thread for this and there is a special IBlockAccessor that is used in that thread that we need to get a reference to in order to place our chest. First lets add two variables at the top of our class of type IBlockAccessor . One to hold our game thread IBlockAccessor and one to hold the one used by the server world gen thread. private IBlockAccessor chunkGenBlockAccessor ; private IBlockAccessor worldBlockAccessor ; Let's initialize worldBlockAccessor at the top of StartServerSide . This is the IBlockAccessor we used in our /treasure command. Later we will refactor that code to use this variable. this . worldBlockAccessor = api . World . BlockAccessor ; Next we will register a call back delegate to be passed the IBlockAccessor we need. Place this in StartServerSide . this . api . Event . GetWorldgenBlockAccessor ( OnWorldGenBlockAccessor ); Then add this method to your class: private void OnWorldGenBlockAccessor ( IChunkProviderThread chunkProvider ) { chunkGenBlockAccessor = chunkProvider . GetBlockAccessor ( true ); } Let's do a quick refactoring before we move on. Remember our PlaceTreasureChestInFrontOfPlayer method? The code in that to place the chest is going to be reused by our world gen code but we won't have a player in that case and we will also need to use a different IBlockAccessor to place our block. So let's refactor that to use a new method we call PlaceTreasureChest . So replace PlaceTreasureChestInFrontOfPlayer with the following. private TextCommandResult PlaceTreasureChestInFrontOfPlayer ( TextCommandCallingArgs args ) { PlaceTreasureChest ( api . World . BlockAccessor , args . Caller . Entity . Pos . HorizontalAheadCopy ( 2 ). AsBlockPos ); return TextCommandResult . Success (); } private bool PlaceTreasureChest ( IBlockAccessor blockAccessor , BlockPos pos ) { var blockID = api . WorldManager . GetBlockId ( new AssetLocation ( "chest-south" )); var chest = api . World . BlockAccessor . GetBlock ( blockID ); if ( chest . TryPlaceBlockForWorldGen ( blockAccessor , pos , BlockFacing . UP , null )) { var block = blockAccessor . GetBlock ( pos ); if ( block . EntityClass != chest . EntityClass ) { return false ; } var blockEntity = blockAccessor . GetBlockEntity ( pos ); if ( blockEntity != null ) { blockEntity . Initialize ( api ); if ( blockEntity is IBlockEntityContainer chestEntity ) { AddItemStacks ( chestEntity , MakeItemStacks ()); Debug . WriteLine ( "Placed treasure chest at " + pos , new object [] { }); return true ; } } } Debug . WriteLine ( "FAILED TO PLACE TREASURE CHEST AT " + pos , new object [] { }); return false ; } So what we did is make a PlaceTreasureChest that takes an IBlockAccessor and a BlockPos for placing the chest. The null check on chestEntity is probably not necessary however while developing this I found I was misusing the API by using the wrong IBlockAccessor so the null check helps detect this scenario and provides a more meaningful message than just a NullReferenceException so I suggest leaving this in. Also notice that we are printing a message to the console when a chest is placed. This is also optional however it's helpful for finding chests in the world when testing. Our PlaceTreasureChestInFrontOfPlayer method now calls our new method passing it the appropriate IBlockAccessor and the BlockPos 2 blocks in front of the player. Now that this refactoring has been done, we are ready to find a suitable spot to place our chests. Finding where to place the chest Now we are ready to place code in our OnChunkColumnGeneration to find a suitable spot for our chest. We are going to set up a nested for loop to loop through each x,y,z location in the chunk and see if that spot is beside a tree. Before we set up our loop we are going to add a few more variables at the top of our class. private const int MAX_CHESTS_PER_CHUNK = 1 ; private const float CHEST_SPAWN_PROBABILITY = 0.80f ; private int chunkSize ; private ISet < string > treeTypes ; They are mostly self explanatory. The first one indicates how many chests we are going to allow to be placed per chunk. CHEST_SPAWN_PROBABILITY is a probability of placing a chest in the current chunk at all. chunkSize is just stored as a convenient way to access the chunk size. To initialize it we need the following in StartServerSide : this . chunkSize = worldBlockAccessor . ChunkSize ; Make sure to add this after you set worldBlockAccessor ! I'll explain the treeTypes variable in a bit. Just add it for now. Replace the empty OnChunkColumnGeneration with the following: private void OnChunkColumnGeneration ( IChunkColumnGenerateRequest request ) { var chestsPlacedCount = 0 ; for ( var i = 0 ; i < request . Chunks . Length ; i ++) { if ( ShouldPlaceChest ()) { var blockPos = new BlockPos ( Dimensions . NormalWorld ); for ( var x = 0 ; x < chunkSize ; x ++) { for ( var z = 0 ; z < chunkSize ; z ++) { for ( var y = 0 ; y < worldBlockAccessor . MapSizeY ; y ++) { if ( chestsPlacedCount < MAX_CHESTS_PER_CHUNK ) { blockPos . X = request . ChunkX * chunkSize + x ; blockPos . Y = y ; blockPos . Z = request . ChunkZ * chunkSize + z ; var chestLocation = TryGetChestLocation ( blockPos ); if ( chestLocation != null ) { var chestWasPlaced = PlaceTreasureChest ( chunkGenBlockAccessor , chestLocation ); if ( chestWasPlaced ) { chestsPlacedCount ++; } } } else //Max chests have been placed for this chunk { return ; } } } } } } } private bool ShouldPlaceChest () { int randomNumber = api . World . Rand . Next ( 0 , 100 ); return randomNumber > 0 && randomNumber <= CHEST_SPAWN_PROBABILITY * 100 ; } private BlockPos TryGetChestLocation ( BlockPos pos ) { return null ; } We've added a couple of methods here that I'll explain first. The ShouldPlaceChest method simply generates a random number between 0 and 100. If the number is between 0 and our CHEST_SPAWN_PROBABILITY multiplied by 100 then it returns true. This is called before we do our loop to see whether we should proceed in placing our chest. We will fill in the details of TryGetChestLocation in the next section so just leave it for now. The loop goes through each x, z then y coordinate and converts the coordinates to world coordinates by multiplying the chunkX coordinate by the chunkSize and adding our x coordinate in the loop. The same goes for Z. This is very important! Our IBlockAccessor expects world coordinates. World coordinates start at the beginning of the world whereas chunk coordinates start at the beginning of the chunk. Always keep your coordinate system in mind. Another thing to note here is that BlockPos is created outside our loop and reused to cut down on object creation. You don't want to create a ton of objects and cause garbage collection because this will negatively impact performance. Try to create as few objects as you can in code that will be executed frequently. The meat of the code calls our TryGetChestLocation to get a BlockPos . If the method returns null that means the current x,y,z is not a suitable location. However if it is then it moves on to our PlaceTreasureChest and increments the chestsPlaced counter which is used to check and make sure we aren't placing more chests in the chunk than MAX_CHESTS_PER_CHUNK . Our loop and main logic is finished. Now it's time to implement TryGetChestLocation to detect trees. Detecting trees Remember the treeTypes variable we created at the top of our class? Now it's time to populate that with the block codes of the tree logs we will be looking for. Let's add a method called LoadTreeTypes . private void LoadTreeTypes ( ISet < string > treeTypes ) { var treeTypesFromFile = api . Assets . TryGet ( "worldproperties/block/wood.json" ). ToObject < StandardWorldProperty >(); foreach ( var variant in treeTypesFromFile . Variants ) { treeTypes . Add ( $"log-grown-{variant.Code.Path}-ud" ); } } This method reads the log types from worldproperties/block/wood.json and adds them to our treeTypes set. Since we are looking for logs we prepend "log-" and we pick the variant "-ud" which means "Up/Down" since that's the variant of the tree log that is at the base of trees. Now to initialize our treeTypes set just add the following to StartServerSide : this . treeTypes = new HashSet < string >(); LoadTreeTypes ( treeTypes ); Now to detect our logs we can simply compare the Block.Code property to values in our treeTypes set. Lets write a quick helper method for this: private bool IsTreeLog ( Block block ) { return treeTypes . Contains ( block . Code . Path ); } The algorithm we will use to detect the tree log is pretty simple. We will see if the current block is a tree log, if so we will iterate downward to find the bottom log by detecting the first non-log type. Once we find it we simply find an adjacent air Block. Air blocks have a Block.Id value of 0. Here's the code: private BlockPos TryGetChestLocation ( BlockPos pos ) { Block block = chunkGenBlockAccessor . GetBlock ( pos ); if ( IsTreeLog ( block )) { for ( int posY = pos . Y ; posY >= 0 ; posY --) { while ( pos . Y -- > 0 ) { Block underBlock = chunkGenBlockAccessor . GetBlock ( pos ); if ( IsTreeLog ( underBlock )) { continue ; } foreach ( BlockFacing facing in BlockFacing . HORIZONTALS ) { BlockPos adjacentPos = pos . AddCopy ( facing ). Up (); if ( chunkGenBlockAccessor . GetBlock ( adjacentPos ). Id == 0 ) { return adjacentPos ; } } } } } return null ; } There are some improvements that could be made to this algorithm. I list them in the Exercises below. However, now you should be able to run the code and find treasure chests in your world!! Summary You should now have an idea of how to register commands and place blocks during world gen. There's plenty more to explore. If you want to take this code further please see the suggested exercises below. Excercises A few things could be done to improve this code and it's left as an exercise for the reader. Doing this will help you get familiar with the API without overwhelming you. Currently the code will place chests over water or air. Change TryGetChestLocation to only place chests over solid blocks. Make the chest face away from the tree log(and player) correctly. Currently it always faces south. Hint: use chest-north, chest-east, chest-west. Make chests a more rare item to find. Chests should have more interesting items. Modify the code to put some more useful things in there. Maybe tools or weapons that can't be crafted. A harder exercise might be to only place chests in caves. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:WorldGen_Concept TITLE: Modding:WorldGen Concept --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Other languages: English русский This article introduces how world generation is handled in Vintage story, and the extent to which it can be modded. It is also intended as a general introduction for modding endeavors in regards to world generation. Contents 1 Terminology 2 Concept 3 Content modding 3.1 Moddable content 4 Code modding 4.1 World types 4.2 Passes 4.3 Events 4.3.1 Accessing events Terminology Important terminology to note while reading this article: Chunk : An data structure that holds 32x32x32 blocks. It is of note that chunks do not span the world height. Chunk column : A set of chunks spanning the vertical height of the world. Given a world height of 256, a chunk column would represent 8 vertically stacked chunks Map chunk : A data structure that holds information on a chunk column, such as the rain height or snow accumulation Map Region : A data structure that holds information over an area of 16x16 chunk columns, such as weather and maps required for world generation Concept World generation is the process of generating necessary data about the world when the player requires it. World generation is done on a region, map chunk and chunk column basis - each acting as a meaningful subdivision of the world. Each region, map chunk and chunk column is only generated once and stores important data about that area. World generation is very modular. Almost all generated parts of the world, such as ore deposits, creatures or trees, are specified via JSON content mods. On a coding level, each stage of generation triggers an event which the base game registers handlers to. Event handlers can be registered or removed through the API to alter any part of generation. Content modding Using content modding, here are the various things that can be added, modified or removed from world generation. Properties relative to world generation are generally located at Vintagestory/assets/survival/worldgen Moddable content Item Description Deposits Anything that is "deposited" in the ground on generation, such as ores or patches of blocks like gravel and chalk Structures Pre-defined structures, such as the ruins found throughout. The " How to use WorldEdit " page has instructions on building and exporting structures Trees Tree and shrub placement Block layers Layers of blocks found above the rock layer Block patches Patches of blocks, also vegetation Geological provinces Geological attributes and placement Landforms Land layout and the general arrangement of terrain Code modding Using code modding, it is possible to remove any specific part of world generation from the base game entirely, as well as add new functionality. World types There are currently three world types - "standard", "superflat", "empty". World types exist under EnumWorldGenPreset . World type has to be specified when registering event handlers for world generation. Passes The chunk column generation event is additionally subdivided into passes. Passes are essentially layers in the generation of a chunk column - first laying down the rock layer in one pass, and doing more general cross-chunk actions in later passes, such as flooding them with light or growing patches of grass. Passes exist under EnumWorldGenPass . The pass has to be specified when registering event handlers for chunk column generation. Index Name Description Requires neighbor chunks 0 None Nothing generated yet false 1 Terrain Basic terrain, rock strata, caves, block layers false 2 TerrainFeatures Deposits, structures, ponds true 3 Vegetation Block patches, shrubbery and trees, rivulets, sunlight flooding in chunk true 4 NeighbourSunLightFlood Snow layer, sunlight flooding to nearby chunks true 5 PreDone Done generating, spawn creatures, neighbor chunks might still generate additional things true 6 Done Pass is not triggered as an event - Events The generation of either a region ( IMapRegion ), map chunk ( IMapChunk ), or chunks contained in that chunk column ( IServerChunk and IWorldChunk ), triggers an event (in that order). Each of these interfaces holds some specific data about the given region. It is possible to register handlers to events, wherein access is provided for the given interface. The order in which the handlers are assigned is based on the execute order of their specific mod systems. Accessing events class Mod : ModSystem { public override double ExecuteOrder () { // Make sure all delegates have been assigned return 10 ; } public override void StartServerSide ( ICoreServerAPI api ) { // Get the handler for interacting with world generation. IWorldGenHandler handler = api . Event . GetRegisteredWorldGenHandlers ( "standard" ); // Example registering events using a delegate. api . Event . ChunkColumnGeneration ( /* Handler here */ , EnumWorldGenPass . None , "standard" ); api . Event . MapChunkGeneration ( /* Handler here */ , "standard" ); api . Event . MapRegionGeneration ( /* Handler here */ , "standard" ); // Retrieving registered handlers. List < MapChunkGeneratorDelegate > onMapChunkGen = handler . OnMapChunkGen ; List < ChunkColumnGenerationDelegate >[] onChunkColGen = handler . OnChunkColumnGen ; List < MapRegionGeneratorDelegate > onMapRegionGen = handler . OnMapRegionGen ; foreach ( var deleg in onMapChunkGen ) { // Printing class name of delegate instance. // This can be useful for finding and removing any particular event from the list. api . Server . LogEvent ( deleg . Target . ToString ()); } } } Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Entity Instance Attributes • EvolvingNatFloat • GUIs • Meal Containers • Serialization Formats • Server-Client Considerations • TreeAttribute Data • WorldGen Concepts --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:WorldGen_Configuration TITLE: Modding:WorldGen Configuration --- CONTENT --- This page was last verified for Vintage Story version 1.19.8 . Other languages: English русский Contents 1 Landforms 1.1 Octaves 1.2 Y-Thresholds 1.3 Testing Landforms Landforms Landforms define the shape of the terrain. Let's take a look at an existing landform. { "code" : "landforms" , "variants" : [ { "code" : "humongous mountain" , "comment" : "humongous mountains with caverns in them" , "hexcolor" : "#5BC184" , "weight" : 2 , "useClimateMap" : false , "terrainOctaves" : [ 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 0.6 , 0.2 ], "terrainOctaveThresholds" : [ 0 , 0 , 0 , 0 , 0.5 , 0 , 0 , 0 , 0 , 0 ], "terrainYKeyPositions" : [ 0 , 0.33 , 0.37 , 0.42 , 0.43 , 0.5 , 0.6 , 0.7 , 0.9 , 0.97 , 1 ], "terrainYKeyThresholds" : [ 1 , 1 , 0.87 , 0.84 , 0.7 , 0.94 , 1 , 1 , 0.1 , 0.05 , 0 ] }, ] } There are 2 system at work that let you control the shape of the terrain - the octaves and the Y-Thresholds. Octaves If you know perlin noise, you will know what this is, but shortly explained low octaves will give you very smooth terrain whereas high octave give you rugged/chaotic terrain. You can mix an match freely between those. Examples: terrainOctaves: [1, 0, 0, 0, 0, 0, 0, 0, 0, 0] would give you veeeery gradually changing terrain terrainOctaves: [0, 0, 0, 0, 0, 0, 0, 0, 1, 1] would give you veeeery rugged and chaotic terrain Octave Thresholds These act as substractor to the octaves. This allows you for example to generate a flat terrain with a few rare bumps, by setting something like this: terrainOctaves: [1, 1, 1, 0, 0, 0, 1, 0, 0, 0], terrainOctaveThresholds: [0, 0, 0, 0, 0, 0, 0.7, 0, 0, 0], This causes the 7th octave to be only active 30% of the time, as most of it is substracted away. Note: The amount of octaves is always fixed at 10. You can't configure less or more than 10. Y-Thresholds Imagine you had a side view of a mountain and you could make it wider and thinner at any given elevation. This is approximately how the y-threshold system works. terrainYKeyPositions are the relative (0=bottom, 1=top of the world) coordinates and terrainYKeyThresholds define how much Percent of terrain should be solid blocks (0 = always solid, 1 = always air), or in other words it's density. We could also make a graph of this: In this example at maximum world height the density is 0, so there will always be air blocks. In the middle we have a jump where the y-position changes, but not the density, so this will create cliffs in the mountain. On the lower end the opposite happens. The Y-Position changes very little but it's density increases a lot. If that position happens to be at the sealevel we would get flat beaches. The default sealevel y-position is 0.43 And below a certain y-position the density is always 1. If we would set that to a lower value, we would get very deep lakes. The amount of configurable Y-Thresholds is arbitrary you may use any quantity, just make sure both lists have the same amount of elements. However, it wouldn't make sense to configure more than 256 thresholds for a world height of 256 blocks. You can build such graph using the new world gen tool Testing Landforms You can trial&error landforms very quickly without the need to restart the game every time you made changes to the landforms.json /wgen autogen 0 - disables automatic generation of chunks /wgen del 10 - delete all chunks with a radius 10 chunks (Warning: Cannot be undone!) /wgen regen 5 - Reloads the landforms.json then deletes and regenerates all chunks with a radius of 5 chunks Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Content Basics • Tutorials • Concepts & Guides • All Content Pages "" Asset System • Debugging Content • Grid Recipes Guide • Mod Compatibility • Mod Info • NatFloat • Patching Assets • Remapping Objects • Theme Packs • Tree Generation • Variants • VTML • WorldGen Configuration --- PAGE END --- --- PAGE START --- URL: https://wiki.vintagestory.at/Modding:World_Access TITLE: Modding:World Access --- CONTENT --- Other languages: English русский This page is a candidate to be rewritten. This page may use or reference older systems, and is due to be rewritten. Feel free to use it, but please be aware that its contents may not reflect the rest of the wiki. This page was last verified for Vintage Story version 1.19.8 . Get and Set Blocks through the API Let's Take our basic mod init code for a server mod public class SampleMod : ModSystem { ICoreServerAPI api ; public override bool ShouldLoad ( EnumAppSide side ) { return side == EnumAppSide . Server ; } public override void StartServerSide ( ICoreServerAPI api ) { this . api = api ; } } With this you can already start placing or reading blocks using the api object. Please note that mods are loaded before the world is loaded, so you cannot immediately access the world. Depending on what you want to achieve, you have to register to the appropriate event. For some examples in order to do something... after the world has loaded you could register to the RunGame phase: api.Event.ServerRunPhase(EnumServerRunPhase.RunGame, OnRunGame); during world generation we'd have to register to the appropriate world gen events . when a player joins the game: api.Event.PlayerJoin += OnPlayerJoin; Let's register to the player join event. public override void StartServerSide ( ICoreServerAPI api ) { this . api = api ; api . Event . PlayerJoin += OnPlayerJoin ; } private void OnPlayerJoin ( IServerPlayer byPlayer ) { } Let's do 2 simple things whenever a player joins: Always place some fire clay bricks just below his feet Count the amount log blocks around the player in a 3 block radius Now, blocks are always uniquely identified by a number of characters called the block code. If you want to find out a blocks code, a simple way of doing it is to start the game, enable the debug info mode via the command .edi and look at a block in the creative inventory: So the code of a fire brick block is game:claybricks-fire. The first part before the colon is the domain, for the default domain 'game' we can usually ignore it. We now have to retrieve it's number identifier for this particular world in order to get or set it. We do this as followed: private void OnPlayerJoin ( IServerPlayer byPlayer ) { Block firebrickblock = api . World . GetBlock ( new AssetLocation ( "claybricks-fire" )); int blockId = firebrickblock . BlockId ; } Now all we have to do is set the block right below the players position through the IBlockAccessor interface. BlockPos plrpos = byPlayer . Entity . Pos . AsBlockPos ; api . World . BlockAccessor . SetBlock ( blockId , plrpos . DownCopy ()); We're using plrpos.DownCopy() to keep the original player position for later use and to move the position down by 1 for placing the block below the player. And finally let's count the number of log blocks around the player and inform him via chat message. // Check a 7x7x7 area for logs int quantityLogs = 0 ; for ( int x = - 3 ; x <= 3 ; x ++) { for ( int y = - 3 ; y <= 3 ; y ++) { for ( int z = - 3 ; z <= 3 ; z ++) { Block block = api . World . BlockAccessor . GetBlock ( new BlockPos ( plrpos . X + x , plrpos . Y + y , plrpos . Z + z , plrpos . dimension )); if ( block . Code . Path . Contains ( "log" )) { quantityLogs ++; } } } } byPlayer . SendMessage ( GlobalConstants . GeneralChatGroup , "You have " + quantityLogs + " logs nearby you" , EnumChatType . Notification ); Alternatively we could also use a shorthand method to have more elegant code. int quantityLogs = 0 ; api . World . BlockAccessor . WalkBlocks ( plrpos . AddCopy (- 3 , - 3 , - 3 ), plrpos . AddCopy ( 3 , 3 , 3 ), ( block , x , y , z ) => quantityLogs += block . Code . Path . Contains ( "log" ) ? 1 : 0 ); byPlayer . SendMessage ( GlobalConstants . GeneralChatGroup , "You have " + quantityLogs + " logs nearby you" , EnumChatType . Notification ); And the complete code and final result: private void OnPlayerJoin ( IServerPlayer byPlayer ) { Block firebrickblock = api . World . GetBlock ( new AssetLocation ( "claybricks-fire" )); int blockId = firebrickblock . BlockId ; BlockPos plrpos = byPlayer . Entity . Pos . AsBlockPos ; api . World . BlockAccessor . SetBlock ( blockId , plrpos . DownCopy ()); // Check a 7x7x7 area for logs int quantityLogs = 0 ; api . World . BlockAccessor . WalkBlocks ( plrpos . AddCopy (- 3 , - 3 , - 3 ), plrpos . AddCopy ( 3 , 3 , 3 ), ( block , x , y , z ) => quantityLogs += block . Code . Path . Contains ( "log" ) ? 1 : 0 ); byPlayer . SendMessage ( GlobalConstants . GeneralChatGroup , "You have " + quantityLogs + " logs nearby you" , EnumChatType . Notification ); } I hope this taught you the basics of accessing the world. More ways to access blocks and entities will be added here in the future. Modding "" Basics • Content • Code • Asset Types • Model Creator • Troubleshooting • VS Engine "" Code Basics • Tutorials • Concepts & Guides • All Code Pages "" Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Other Tutorials "" Block Behaviors • Block Entities • Chunk Moddata • Code Asset Patching • Commands • Harmony Patching • Inventory Handling • Item Interactions • ModConfig • Moddable Mod • Network API • Saving Mod Data • Setting up your Dev Environment • Simple Particles • World Access • WorldGen API --- PAGE END ---