Skip to content

Instantly share code, notes, and snippets.

@dags-
Forked from williewillus/primer.md
Created July 20, 2018 16:33
Show Gist options
  • Save dags-/2b3d4d8b1bdcf77366422301f79e17d0 to your computer and use it in GitHub Desktop.
Save dags-/2b3d4d8b1bdcf77366422301f79e17d0 to your computer and use it in GitHub Desktop.
1.13 update primer

This primer is licensed under CC0, do whatever you want.

BUT do note that this is still under construction, and the contents may change anywhere from very little to drastically from now until forge 1.13 is stable, so if you derive something from this I'd prefer that you either constantly check back for updates until forge 1.13 comes out, or leave a link here so readers can see the updated information themselves

Warnings in Advance

  • ResourceLocation now throw on non-snake-case names instead of silently lowercasing for you, so you probably should go and change all those string constants now. More precisely, domains must only contain alphanumeric lowercase, underscore (_), dash (-), or dot (.). Paths have the same restrictions, but can also contain forward slashes (/).
  • MCP renames: https://github.com/ModCoderPack/MCPBot-Issues/blob/master/migrations/1.12-to-1.13.md

Rendering Changes

  • Statemappers are now gone, replaced with a function hardcoding the old default logic in BlockModelShapes
    • This is because for vanilla usecases, the flattening has completely removed any need they have for statemappers
    • For most mods, the flattening should also solve most of the cases you need a statemapper (for appending a suffix, flattening; for splitting on a variant, also flattening
  • Vanilla blockstate jsons are smarter now
    • Before: the variant string was expected in a very specific format: alphabetized list of propertynaem=value pairs, based on the statemapper
    • Now: the string is split on "," and dynamically built into a Predicate<IBlockState>. This obsoletes statemapper ignoring since if you don't care about changing rendering for a given property, just don't specify it in the blockstate json
    • Side effect: the "normal" variant should now be named "", defining a predicate that matches all incoming IBlockStates
  • Vanilla model jsons are slightly smarter now
    • {} is now valid and represents an empty model
    • Only setting textures without setting elements is now valid, useful for e.g. setting the break particle of a TESR
  • The textures/blocks and textures/items folders have been renamed to textures/block and textures/item, matching model/. Various vanilla textures have been renamed for consistency.
    • Yes, this is automatable. Read up on sed and grep, and/or write a shell or Python script
    • Texture errors should make it obvious where the errors are (you DO keep your mods clean and you DO try not to spam the log with useless errors so you can see the real ones, right? ... right? ...)
  • In blockstate jsons, the block subdirectory is no longer inferred.
    • In other words, in a vanilla blockstate json, foo:bar no longer points to foo:models/block/bar, but rather foo:models/bar. You must put the block back in by specifying foo:block/bar in the blockstate json
    • Yes, this is also automatable, stop complaining.
    • (todo verify this) The benefit is that you can organize models in subdirectories other than block now.
  • LWJGL has been bumped to version 3.x. So you can now use anything we've missed from the last 2+ years of LWJGL3 development.
    • Mojang appears to have ditched Java BufferedImage in favour of LWJGL 3's stbimage bindings. This shouldn't affect most modders.
    • More here as info comes out

Lang changes

  • Lang files are now json. instead of key=value, now one large json object with every translation inside e.g. { "block.minecraft.dirt": "Dirt" }
  • By default, blocks and item unlocalized names now use their registry name (replacing the colon with a dot), and the block prefix is now "block." instead of "tile.". E.g. block.minecraft.dirt, item.minecraft.diamond
    • Not as scriptable due to the flattening, but still doable
  • ".name" is also no longer added to the end of the lang

Data packs

  • Move your advancements, functions, loot tables, recipes, and structures from assets/ to data/.
  • assets/ should once again become a place where client-only resources live. Anything needed server side should be found in data/ instead.
  • It appears that Mojang has a general util method for walking a subdirectory of data/, while respecting datapack cascading/overriding, which will be great. I imagine you could move most of your configuration or machine recipes here, and gain the benefit of the cascading resource system.
    • TODO: Put name of said method here
    • IMPORTANT: If you do move some of your gameplay (e.g. machine recipes) to datapacks, be very wary of namespace collisions.
    • E.g. Mojang has already reserved data/<domain>/recipes across ALL domains for vanilla crafting recipes, and data/<domain>/structures across ALL domains for structure files. Your subfolder in the datapack probably needs to have your mod-id somewhere again. E.g. data/botania/botania/petal_apothecary/, such that an addon can add their recipes to data/my_addon/botania/petal_apothecary/
    • If you aren't careful and put your machine recipes in data/yourmod/recipes, vanilla will try to load it as a workbench or furnace recipe
  • See https://minecraft.gamepedia.com/Data_pack for more information

What's the deal with metadata?

  • Metadata has been removed. All of it. No more magic numbers!
  • Information formerly expressed using metadata is either no longer needed (blocks), flattened (blocks and items), or moved to NBT (item tool damage).
  • STOP BEING SCARED of using more block and item ID's. They are virtually unlimited now. (Well, the limit is how many Block and Item instances you can hold in memory at once. The answer is: millions of them. Blocks and Items are tiny). Of course, there are things that should still stay as variants of a single Block or Item (or as fields on a TE), but use your best judgment. Prefer flattening and using a new ID to not doing so, and just don't be stupid. Ask experienced modders on Discord or #minecraftforge if unsure.
  • Also if you are unsure, check out the vanilla wiki page on the flattening to get a feel for what should be split out into a new ID and what should stay as blockstate properties.
    • No, before you ask, you still can't make a block with 500 blockstate properties each with a dozen values. You'd run out of memory because all possible IBlockState are generated at startup. If you need something that dynamic, use a TE.
  • Using more ID's does not mean you can't reuse code, you have always been able to instantiate a class multiple times and register it under different names.

Dealing with Item metadata

  • If dealing with tool damage, move it to an NBT tag inside the "tag" tag. There will likely be a utility method on ItemStack for this (TODO: put its name here) (TODO: vanilla has a DataFixer to do this, will it work on modded items automatically?)
  • Otherwise, flatten the Item, moving each of its subvariants to a different ID.
    • Example: Instead of botania:manaresource @ <arbitrary magic number>, we now have botania:mana_diamond, botania:mana_pearl, etc.
    • If you didn't use a god item with a billion meta values for everything, good for you, you have almost nothing to do (e.g. Pam's Harvestcraft already uses a new item ID for everything instead of metas => literally zero work)
    • Split the field apart, comparing item meta values should just change to an == check on the flattened Item instances.
    • Old == checks on the unflattened item can either check tags (see below) or be an instanceof, if appropriate.
    • Update your recipes and loot tables
    • Update your lang files
  • Please don't just do NBT hacks in your Items. Unless absolutely necessary (e.g. you have infinitely many variants, in which you would have been using NBT already).
  • The cost of an extra ID is the Item instance (a handful of bytes allocated once), and the ID (a handful of bytes for the string).
  • The cost of NBT hacking is extra tags on EVERY stack (= extra storage space, extra processing overhead, extra network bandwidth consumed). Syncing a plain itemstack in our new flattened world is just sending two ints (int ID and stacksize). Adding an NBT tag significantly increases the overhead, and this overhead is paid on EVERY stack. It adds up fast.
  • In addition, many vanilla and modded subsystems will not (and should not, IMO) support NBT, most notably being the Tag system. NBT should be the exception, not the norm.

Dealing with Block metadata

Here's the extremely quick rundown. Assumes prior knowledge of blockstates:

  • ALL vanilla IBlockStates are completely saved now (yes, this includes things that used to be under getActualState like fence connections, which were not saved before 1.13). Forge extended states remain a rendering-only concept and therefore are still unsaved. See the fence classes to see how connections and other things formerly related to getActualState are now handled.
    • Note the implications of this: making connections is now serverside, and should be done on block update - you're changing the blockstate. Be careful about updating things when doing worldgen.
    • It formerly was clientside and only done when rerendering the chunk
    • Again, check vanilla for examples.
    • If you still need something like getActualState (e.g. reading from TE to set a property), move the logic to getExtendedState
  • Remove your getMetaFromState/getStateFromMeta methods, they are no longer needed. Hopefully you aren't heavily relying on them because you were going the easy way when porting to 1.8...
  • TODO: How does save conversion work for modded? We may still need to know old metas to convert them to a blockstate to save in the palette.
  • Flatten all of your blocks, meaning that anything that might need a separate ItemBlock, needs to become a separate block itself. Inherent properties of the Block itself remain as blockstate properties.
    • Example: Vanilla logs are no longer crammed into minecraft:log[axis, variant], but are minecraft:oak_log[axis=x,y,z], minecraft:oak_bark, etc. Because a different axis log doesn't need another ItemBlock (since axis depends purely on placement position), but a different type of log needs another ItemBlock (since Items no longer have metas)
    • A simple heuristic that works in many (but not all) cases is to take any PropertyEnum VARIANT you might have had, and just pass that Enum instance into the Block's constructor and hold it as a field instead. Instead of checking the blockstate property, just check the field. See BlockShulkerBox to see how vanilla does this - it holds onto a final EnumDyeColor field for each block instance.
  • Flatten all of your ItemBlocks accordingly. It should just be passing the newly flattened block instances into more instances of the same class.
    • Please don't do NBT hacks, for all the reasons described above.
  • Accordingly, comparing variants should just change to == check on the flattened Block instances.
  • If you were saving blockstates using registry name + meta, you should now use NBTUtil.read/writeBlockstate. See how Endermen save their carried block for an example.

Tags

  • With flattened blocks and items, we have a lot more unique ID's. No one wants to write 16 lines to match all 16 colors of wool, all of which now have different ID's.
  • Mojang's solution to this is called "tags" (https://minecraft.gamepedia.com/1.13#General). You have jsons within the data/modid/tags directory which define all of their members.
  • Separate tags exist for blocks, items, and functions
    • Example: data/modid/tags/blocks/foo/bar.json defines a Block tag with ID modid:foo/bar. Its contents is simply a list of Block ID's part of that tag. (see vanilla examples)
    • jsons in other datapacks will append to the tag definition, unless otherwise specified. (see vanilla wiki)
  • Advancements and recipes can now use these tags to match stacks instead of enumerating everything. See vanilla's recipes and advancement jsons for examples.
    • Remember: Block tags (tags/blocks/...)are only used for commands like /execute which query the blocks in world directly. Advancements and recipes both operate on ItemBlocks and use item tags (tags/items/...). Yes this will result in some duplication. If you're lazy, you can skip out on the block tags since the item tags are much more importantm, but doing them is still recommended for good compatibility.
      • For example, mods which have in-world recipes like Botania might want to use block tags. Currently such mods must slowly iterate the OreDictionary (which is built for items) and/or perform caching logic. With block tags, it would just be a constant time lookup.
  • Tags can also be queried from code, see enderman's pickupable blocks
  • Tags are synced to the client when connecting with a dedicated server. (SP clients just read directly from static fields set up by the integrated server)
  • TODO: Integration of this system with the oredict? See MinecraftForge/MinecraftForge#4577 for discussion.

Recipes

  • You need a json for every recipe now (dramatic music plays)
    • But not really: You just need a json to allow it to be disabled by datapacks (and syncing to client)
    • Basically there is a registry from type -> Json deserializer, so your recipe jsons can now have whatever custom JSON format you want, including all code (think Loot table LootFunctions and LootConditions).
    • See vanilla mapcloning and mapextending.json and their in-code equivalents, the json simply specifies the type, and the entire recipe is implemented in code.
    • For ordinary recipes, you probably should convert them to jsons.
  • TBD: Mojang seems to have some kind of auto recipe dumper at CL_0198_fo (18w02a) that generates the recipe json as well as an advancement to unlock the recipe. This tool appears to be runnable by running the minecraft jar with a different start class.
  • BIG TBD: furnace recipes are now be specified using the normal recipe JSONs. this potentially means any mod can use the JSON system (and all Forge's improvements to it and the Ingredient system) to specify their own machine recipes.

Advanced Recipes

  • You can now register custom deserializers for recipes, which are dispatched based on the type field in the recipe json.
  • Your recipe type must be syncable over the network (since recipes are now sent to all clients on login and datapack reloads)
    • No, don't just send the json text over. These are sent on login, so you don't want the client to waste time reparsing stuff. Ideally, you want to send the absolute least amount of information you can to define the recipe.
  • One-to-many and super dynamic recipes are still TBD, talk with forge devs or modders of interest (tterrag) if you want to discuss a solution for this
  • TBD once things get named

Commands (TBD)

  • Commands have been overhauled, and now use a separate Mojang library called Brigadier (it's not obfuscated, so you can just look at the source)
  • Command syntax is now much more declarative (instead of trying to manually parse things out of the command string each time)
  • Command syntax now synced from server to client
  • Tab completion only asks the server if the specific argument in the command says that it should
  • Completion for things that have a static fixed set of options done clientside (e.g. /gamemode)
  • TBD when everything's deciphered

Waterlogged blocks (TBD)

  • 1.13 brings a new "water in block" mechanic
  • The interfaces are very general - World now has getFluidState and setFluidState methods, and fluids are elevated to almost block-level status (they can even have their own IProperty's)
  • However, the current fluid state implementations usually just defers to blockstate properties on the underlying block such as the waterlogged property.
    • The system is likely incomplete; expect this to evolve in future versions to be like Bedrock Ed (where water can reside in any block)
  • more info on classes to implement, etc. when everything's deciphered

World gen changes (EXTREME TBD)

  • HUGE amount of changes, likely the largest subsystem change in this update
  • Now threaded. I hope your worldgen functions are pure!
  • TBD as things get named, but it's highly likely you will have to make significant changes.

Data Fixers

Misc Things

  • Vanilla now has a "different AABB depending on where you look" system (aka vanilla diet hoppers but even better). Play with the hopper in 1.13 to see what I mean. See hopper or end portal frame block classes for examples.
  • Structure Blocks (and anything you made using them) need to be reloaded into a 1.12 world, the world run in 1.13, then resaved in 1.13. No idea why Mojang doesn't have an automated way to fix this, but it is what it is
  • Block attributes like their material or hardness are now passed in a builder/POJO to the block constructor, and are final
    • Similar for Items
  • Blocks no longer have a creativetab, that belongs on their ItemBlocks
  • Biome ids are internally ints now instead of bytes, which raises the maximum number of allowed biomes from 255 to 2 billion.
  • Enchantments are now stored by registry name instead of int id (about time...)
  • A word about integer ID's
    • For Items and Blocks, you should have already switched to fully using registry names for everything. The registry name <-> int ID maps still exist in vanilla for performance purposes, but they should never be used for anything other than network communication because they are NOT guaranteed to be the same after joining a different world. This should already be an established modding convention since 1.8, but people still break it, so I'll mention it again :P
  • There's lots of superinterfaces on world now representing different things you can do (e.g. there's an interface for read-only access to the world). If possible, try using the least specific one.
  • Most nameable things that still used strings now use text components
  • There's multiple kinds of air block now (air, void_air, and cave_air). So if you're doing == Blocks.AIR checks still, change them to IBlockState.isAir checks (or whatever name the method ends up getting)

Recommended Porting Procedure

  1. Make a branch off of 1.12, and flatten all your items there first. Stop being scared of using more ID's. This is useful because you can still run the game and make sure everything you've done so far works. Make a DataFixer to migrate saves for you (or just decide to break world compat). (easy, tedious) 1a. By request from Mezz: Update JEI integration to remove all uses of deprecated methods in 1.12. This is so he can remove those methods directly in 1.13.
  2. If using my autogenerator, convert all your standard recipes to JSON. Leave any "dynamic"/special recipes in code. (easy, potentially tedious) 2b. If using Mojang's autogenerator (more info on using this once 1.13 actually comes out), do this after step 6.
  3. Reload all your structure blocks into a 1.12 world (easy)
  4. Switch Gradle over to 1.13. Stop being scared of using more ID's. (easy)
  5. Move everything that needs to go in data/ from assets/ to data/ (easy)
  6. Flatten your blocks, possibly disabling and readding as you go. Stop being scared of using more ID's. (medium, tedious)
  7. Reload the 1.12 world and resave your structure block templates (easy)
  8. Fix any recipes to use the new vanilla system (mostly involves adding JSONs for custom IRecipe's) (easy-medium, potentially tedious)
  9. Update rendering - perform whatever renames necessary, simplifications of blockstate jsons due to flattening, etc.
  10. (TODO how to do this?) Migrate old saves to flattened format (medium?)
  11. Update your commands (just look at how vanilla commands work and emulate. Hopefully it's all deobfuscated and names mapped by the time you're doing this :P) (medium?)
  12. Tag all your flattened items and blocks appropriately (easy, potentially automatable)
  13. Fix your lang files (easy-medium, potentially tedious if flattening impacts you heavily, potentially automatable)

Some parting words

  • As always, the world is not ending
  • PLEASE do not spread FUD (fear, uncertainty, doubt) or random dubious facts about the port's difficulty. It just makes it harder for the community as a whole.
  • If you are smart and methodical about how you do it, this port should be extremely straightforward. There will be tedious parts but at every step it should be crystal clear what needs to be done.
    • Unless you're doing worldgen in which case good luck and godspeed lol
  • Ask experienced modders for help on Discord/IRC or look at their own ports
  • Help others needing help, but only if you're 100% certain what you're talking about. As always, vanilla should be a first reference regarding mechanisms and conventions

Reporting Errata

  • If you notice something incorrect in this primer, please let me (williewillus) know as soon as possible on the Modded Minecraft discord, twitter, or #minecraftforge IRC if I'm there. I don't want to spread misinformation, so I appreciate the speedy notification.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment