Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
1.13/1.14 update primer

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

BUT do note that this can be updated, so leave a link here so readers can see the updated information themselves.

1.13 and 1.14 are lumped together in this doc, you're on your own if you just want to go to 1.13 and not 1.14, for some reason.

1.15 stuff: https://gist.github.com/williewillus/30d7e3f775fe93c503bddf054ef3f93e

Things 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 (/).
  • MOD LIFECYCLE EVENTS RUN IN PARALLEL. YOU NEED PROPER CONCURRENCY CONTROL IF YOU'RE COMMUNICATING TO OTHER MODS
    • In most cases, just use IMC and it should all be fine (tm)
    • Otherwise, go learn some threading or ask on discord
  • Gradle info: https://gist.github.com/mcenderdragon/6c7af2daf6f72b0cadf0c63169a87583
  • MCP renames
    • There is a script here that can take the csv's from the folder above and generated IntelliJ migration mappings, allowing you to perform MCP renames with a few clicks across your whole project.
    • The csv for 1.14 isn't up yet, so for now use this
    • Blocks.GRASS moved to Blocks.GRASS_BLOCK, with the 1-tall grass shrub taking its place. Check your codebase for bugs!
  • Registry events are now fired on mod-specific event buses. Check the bus argument to EventBusSubscriber for more info
  • slicedlime (mojang dev) has made a high-level overview of 1.14's technical changes: https://www.youtube.com/watch?v=D6P5BvItdoc

Rendering Changes

  • ModelLoader.setCustomModelResourceLocation has been removed since it is no longer needed due to the flattening.
    • Items are looked up using their own registry name, meaning foomod:fooitem will look in assets/foomod/models/item/fooitem.json by default
    • If you need something else, look into using a custom baked model in ModelBakeEvent.
  • 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, just do it manually; for splitting on a variant, should not happen anymore due to flattening)
  • Vanilla blockstate jsons are smarter now
    • Before: the variant string was expected in a very specific format: alphabetized list of propertyname=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
    • How overlapping predicates are handled is unknown, but from brief investigation it seems to be an error.
    • 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.
    • The benefit is that you can organize models in subdirectories other than block now.
  • Custom ItemMeshDefinitions are gone as their functionality can be completely replaced with a custom baked model with custom ItemOverrideList
  • 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.
    • Keyboard.KEY_FOO => GLFW.GLFW_KEY_FOO

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 translation keys 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.
    • That method is IResourceManager.getAllResourceLocations. Example usage 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 recipes participating in the vanilla recipe system, and data/<domain>/structures across ALL domains for structure NBT files. Your subfolder in the datapack probably needs to have your mod-id somewhere again. E.g. data/botania/botania/petal_apothecary/ or data/projecte/pe_custom_conversions, such that an addon or modpack can add to data/my_addon/botania/petal_apothecary/ or data/my_modpack/pe_custom_conversions
    • So if you aren't careful and put something unrelated in data/yourmod/recipes, vanilla will try to load it as a vanilla recipe JSON
  • 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.
    • This can be done by just using the getDamage and setDamage ItemStack calls
  • 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.
  • NO, JUST BECAUSE YOU ALREADY USED GET/SETITEMDAMAGE AND IT COMPILES DOES NOT MEAN YOU CAN LEAVE IT. FLATTEN YOUR ITEMS PROPERLY OR I WILL HUNT YOU DOWN.

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).
    • Things like fence connections are handled by Block.func_196242_c (updateNeighbors) and Block.func_196271_a (currently updatePostPlacement). See MCP comments for more information
    • For rendering-only properties that don't need to be set serverside, use extended states instead (an improvement to them is TBD and so this is not working just yet)
  • 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...
  • 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.
    • If otherwise unsure when to flatten or not, look at vanilla and emulate.
  • 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 (OreDictionary for ctrl+f)

  • Read https://mcforge.readthedocs.io/en/1.13.x/utilities/tags/
  • 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 important, 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. In <1.13 such mods must slowly iterate the OreDictionary (which is built for items, not blocks) and/or perform caching logic. With block tags, it would just be a constant time set lookup.

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 net.minecraft.data.RecipeProvider 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.
  • Furnace recipes are now be specified using the normal recipe JSONs.

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

Commands

  • 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)
  • Not much is known yet, see vanilla for examples

Fluids and waterlogged blocks

  • 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
  • Fluids are now a registry object in vanilla, and your == Blocks.WATER/LAVA checks probably need to be changed to tag checks (see BlockCoral.canLive for an example)

World gen changes

Data Fixers

  • Got an extremely category-/type-theory heavy makeover due to the extreme changes to the world format
  • No really, here's the paper: https://arxiv.org/ftp/arxiv/papers/1703/1703.10857.pdf
  • TBD as it gets deciphered, but I imagine fixers will not be very useful for mods anymore.

Containers

Particles

Particles are now handled in a much more complete and cohesive manner. There are two components to the particle system, the logical side (teaching the game about your particle, how to spawn it, etc.) and the rendering side (teaching the game how to render your particle).

Logical Particles

IParticleType

IParticleType describes the general "type" of the particle (e.g. "redstone dust"). This class is similar to Block and Item, you subclass it and register it on both sides using the standard Forge registry events (RegistryEvent.Register<IParticleType>).

IParticleData

This class is to IParticleType as ItemStack is to Item. That is, IParticleType describes the general type of the particle, while IParticleData describes a specific instance of it (e.g. "redstone dust with mint green color"). It's the unit of handling for particles, and is what you pass to world.addParticle to actually spawn stuff.

Subclasses of this type carry additional data that describe a particle's appearance. For example, vanilla's ItemParticleData, used for showing the item pickup animation, holds the ItemStack being picked up, and RedstoneParticleData, as mentioned above, holds the color of the particle. If you're implementing your own data, you'll have to teach the game how to write/read it from the network PacketBuffer, as well as how to read it from the command line (to support the /particle command). See vanilla for examples.

If your particle needs no additional data, then just use BasicParticleType, a convenience class provided by vanilla that implements both IParticleType and IParticleData as a singleton that writes and reads nothing from the network. The vast majority of vanilla particles in ParticleTypes use this.

Particle

The Particle class is what actually represents a moving particle in game. It's client-side only and has a tick method for you to do custom movement, etc.

Particle Factories

The above two classes are all that's needed serverside -- the server only needs to know the particle type, the additional data, and how to get it onto the network. On the client, we have to concern ourselves with how to get from the IParticleData instance to a subclass of Particle, the actual particle type. This is the job of IParticleFactory. During ParticleFactoryRegisterEvent, you must inform the game by calling Minecraft.getInstance().particleManager.registerFactory(yourType, yourIParticleFactory). The interface only has one method, which receives the world, position, motion, and IParticleData (basically everything you passed to world.addParticle) and returns a Particle. The code here depends purely on your own needs, and can be as simple as just constructing your Particle subclass and passing the IParticleData to it. Again, see vanilla for examples.

Example: Botania

Botania wisps uses vanilla's systems for the above, though it renders the particles on its own (so the next section doesn't apply). The particle type class is here, and the data carried by each particle is here. As you can see, the factory literally just pulls data out of the data class and passes it into the particle type FXWisp. To spawn it, I call a WispParticleData.wisp helper, then pass it to world.addParticle. Example. This also demonstrates that your IParticleData should be immutable, and if so, you can reuse it as many times as you want.

Rendering Particles

Particle JSON

That's most of the story done, but now we want to actually render the particle. First off, note that for any of your IParticleType, you need a particle json corresponding to it. For botania:wisp, there exists assets/botania/particles/wisp.json with a json object inside. This is required even if you aren't using vanilla's animated sprite system.

AnimatedSprite system

What's the animated sprite system? It's the vanilla way of having "default-looking" particles (billboarded flat textures, possibly switching between multiple textures during the particle's lifetime. For an example, view the json and code for particle type minecraft:poof (/assets/minecraft/particles/poof.json). You can see that it has a list of particle textures, which will be loaded and stitched by the game on startup. Also observe that vanilla's registerFactory call for POOF is a weird overload that requires a function that takes an IAnimatedSprite and itself produces a factory. That IAnimatedSprite is the code representation of the list of textures in the json. IAnimatedSprite has two methods, one to select a texture randomly, and one to select a texture based on how old the particle is. This is how for example vanilla's dust particles fade away into fewer pixels, by switching textures based on particle age. This all sounds super abstract but if you just follow what vanilla does for POOF from the factory all the way to the PoofParticle constructor and tick methods (especially what it does with the IAnimatedSprite every tick), it'll be pretty clear what's going on. Subclass from SpriteTexturedParticle, create the json, and call registerFactory, and update the sprite every tick as necessary. The actual rendering is pretty much completely handled by vanilla. Note that you don't have to have multiple textures to use this system, all "default-looking" vanilla particles use this, even if they have only one texture.

Doing your own rendering

Simply override renderParticle and feed your vertex data into the passed BufferBuidler (or do your own GL rendering for IParticleRenderType.CUSTOM). Note that you'll have to pick a IParticleRenderType which determines the GL state under which your particle renders, or create your own implementation.

Spawning Particles

A couple notes on spawning particles. The World.addParticle methods only work clientside, meaning that they do not communicate over the network and calling them on the logical server does nothing. To actually send a message, use the ServerWorld.addParticle overloads, which actually build a packet with your serialized IParticleData and send it to the client, which will then deserialize it , call the factory, and render it.

Of course, if you're spawning tens to hundreds of particles, it would be prudent to send a single custom packet to the client and have the for loop 1 to 100 clientside, instead of looping server side and sending 100 particle packets, wasting bandwidth.

Additionally note that after all this, your particle should be completely supported by the /particle vanilla command, provided you implemented the necessary method in your IParticleData Deserializer.

GUI/Mouse/Keyboard/Input/Keybindings

  • LWJGL3 now uses GLFW as the backing windowing system, so things have shifted accordingly.
  • If you need something, look around to see if MouseHelper and KeyboardListener have what you need already (both accessible from Minecraft). Common things like the mouse position and button states etc are already here.
  • Otherwise, you can use direct GLFW calls to get input state, see glfw input guide. The article is for C, but you can generally directly translate by adding the GLFW class in front of the function names and constants given. E.g. glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS => int state = GLFW.glfwGetKey(mc.mainWindow.getHandle(), GLFW.GLFW_KEY_E) == GLFW.GLFW_PRESS
  • Notably, Gui method signatures have changed a bit. See IGuiEventListener
    • mouseClicked and mouseReleased receives the x, y, and modifiers
    • mouseDragged receives the same, plus the x and y drag amounts
    • mouseScrolled receives the amount scrolled
    • keyPressed and keyReleased receives the keyCode, scanCode, and modifiers. The scan code is likely not useful to you, check the keyCode with constants in GLFW instead, e.g. keyCode == GLFW.GLFW_KEY_E
    • charTyped receives a any textual input, as the form of unicode codepoint and the modifiers. Note that java char is only 16 bit so cannot encode all possible unicode points, but should be "enough" for most keyboard input uses. This call should be used for text input purposes, while keyPressed should be used for "key bind" purposes.
  • To check whether a key binding is pressed, given the keyCode and scanCode, call <KEYBINDING_OBEJCT>.isActiveAndMatches(InputMappings.getInputByCode(keyCode, scanCode))
  • Misc
    • mc.setIngameFocus -> mc.mainWindow.grabMouse/ungrabMouse

Dimensions

  • The class names are a bit screwy, so here's a brief rundown
  • DimensionType: Vanilla class, unique handle to a world. That is, there is a 1:1 correspondence between DimensionType and WorldServer (so it's not really the dimension type, the name is a bad one). This has a string name, and replaces any ints you used in previous versions. AKA if you save stuff to disk, use the string name of this object.
  • ModDimension: Forge class, the actual template/type for new dimensions. That is, you can have one ModDimension and produce multiple DimensionTypes from it
  • Dimension: Vanilla class, created by and tied to DimensionType. Not very useful on it's own. See vanilla subclasses for more info, you can probably reuse one of them directly.
  • To register:
    • During RegistryEvent.Register<ModDimension>, register your ModDimensions like you would blocks and items
    • During RegisterDimensionsEvent, DimensionTypes are registered. Some special handling is required. This event does NOT behave like other events, in that registrations are "sticky". Once a DimensionType has been registered once in a given save, it will show up again already without needing to be re-registered. Thus you should perform an existence check and only then register one or more DimensionType using your ModDimension. See this for an example.
  • MinecraftServer.getWorld
  • entity.dimension.getType()

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, so translate all the things!
  • 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
  • More things are stored in registries now: Entities, BiomeProviders, ChunkGenerators, ParticleTypes, Stats, Paintings
  • As always, check the vanilla 1.13.x patch notes for anything I missed that's gameplay-facing
  • Your world renderers (RenderWorldLastEvent) look weird? give this a read: https://discordapp.com/channels/313125603924639766/454376090362970122/588257071812837376

Nitty Gritty Random Things (Ctrl+F section)

  • SideOnly(Side.CLIENT) => OnlyIn(Dist.CLIENT)
  • SidedProxy => DistExecutor
    • e.g. public static IProxy proxy = DistExecutor.runForDist(() -> ClientProxy::new, () -> ServerProxy::new);
    • Why the additional lambda at the front? It's to prevent the ClientProxy and ServerProxy classes from directly being referenced by the main mod class.
  • mcmod.info moved to mods.toml, see MDK examplemod src/main/resources/META-INF/mods.toml
  • EventBus is a separate library (fix imports). Can register handlers without annotations (so you can use private methods for example). See MDK ExampleMod
  • Loader.isModLoaded => ModList.get().isLoaded()
  • important renames
    • Entity.onUpdate => tick
    • Entity.onEntityUpdate => basetick
    • Entity.setDead => remove
    • World.setBlockToAir => removeBlock
    • World.scheduleUpdate => world.getPendingBlockTicks().scheduleTick (blocks and fluids have separate scheduled ticks now)
    • Block methods: IBlockState moved to front of all forge methods
  • checking physical side => FMLEnvironment.dist
  • checking logical sides:
    • getEffectiveSide => EffectiveSide.get (HIGHLY encouraged to move to world.isRemote checks)
    • getting logical server: ServerLifecycleHooks.getCurrentServer()
  • network: SimpleImpl -> SimpleChannel
  • ScaledResolution.<x> => Minecraft.getInstance().mainWindow.<x>
  • isOpaqueCube => remove, mc now checks this with the block's voxelshape
  • ArmorMaterial => implement IArmorMaterial, no more enum hackery needed
  • ToolMaterial => implement IItemTier, no more enum hackery needed
  • EnumHelper => gone. some enums were abstracted to interfaces by vanilla for us (like the above), and the rest have moved to dedicated create methods on each individual enum. For example, BannerPattern.create(...)
  • TileEntity.shouldRefresh => logic moved to Block.onReplaced. see BlockFurnace.onReplaced for an example.
  • getBlockFaceShape => gone, the game is smart enough to check your VoxelShapes from getShape now

Cross-referencing, a quick how-to

  • Oh no! Method FooClass.BarMethod disappeared in 1.13/1.14! Where did it go? Follow these easy steps for a guaranteed 80% success rate!
  1. Open a 1.12 workspace (this is why you use a separate workspace to update, by the way)
  2. Browse to FooClass.BarMethod
  3. Use your IDE's find usages tool to see where it was called from in vanilla
  4. Pick a call site
  5. Go to that same call site in 1.13/1.14
  6. What does it call instead?
  7. ???
  8. Profit

(if these steps don't apply, then you're allowed to ask)

Some parting words

  • 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.
@Boundarybreaker

This comment has been minimized.

Copy link

@Boundarybreaker Boundarybreaker commented Jul 15, 2018

Are you planning to update the primer for the new information about 1.13?

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Jul 15, 2018

Are there any things you're specifically aware of that aren't listed here?

@CJMinecraft01

This comment has been minimized.

Copy link

@CJMinecraft01 CJMinecraft01 commented Jul 16, 2018

One thing you showed but didn't explicitly state regarding the language file. Prior to 1.13 the name would be something like tile.dirt.name and 1.13 onwards is now block.minecraft.dirt, appending the domain name to the language key. I'm not sure what the impact is on unlocalized names but I assume that these unlocalized names have been replaced by their registry names and hence the language file now uses the domain name in the default item and block language key

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Jul 17, 2018

yup, noted here:
By default, blocks and item unlocalized names now use their registry name (replacing the colon with a dot)

@Drullkus

This comment has been minimized.

Copy link

@Drullkus Drullkus commented Jul 19, 2018

Regarding

  • 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/

So there's no way to coerce Vanilla to add a new deserializer for new recipe types? Or is because their system for parsing stuff in this folder a hardcoded mess? Or both?

Also:

  • 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

[internal screaming]

@lofcz

This comment has been minimized.

Copy link

@lofcz lofcz commented Jul 19, 2018

When is 1.13 branch coming up for Forge on github?
Thanks in advance.

@hYdos

This comment has been minimized.

Copy link

@hYdos hYdos commented Jul 21, 2018

if ANYONE has the 1.13 server src plz PLZ send it 2 me

@xfl03

This comment has been minimized.

Copy link

@xfl03 xfl03 commented Jul 23, 2018

@hYdos
What's your email?

@pizza2004

This comment has been minimized.

Copy link

@pizza2004 pizza2004 commented Aug 18, 2018

@Drullkus I imagine it's specifically so that mod data packs don't cause issues if loaded into a vanilla world.

@dargonforce

This comment has been minimized.

Copy link

@dargonforce dargonforce commented Sep 25, 2018

instructions unclear, I accidentally pirated terraria

@ARandomGuy213

This comment has been minimized.

Copy link

@ARandomGuy213 ARandomGuy213 commented Oct 14, 2018

Nice work. This is really helpful! How did you find this info?

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Nov 3, 2018

@GamerzForLif
access to the decompiled code, snapshot changelogs

@stfwi

This comment has been minimized.

Copy link

@stfwi stfwi commented Dec 10, 2018

Hey willie, thanks for fiddling that out for all, much appreciated (+1).

@CatRabbit499

This comment has been minimized.

Copy link

@CatRabbit499 CatRabbit499 commented Feb 10, 2019

Does anyone know what the replacement for this enum is?:
NetworkRegistry.INSTANCE.registerGuiHandler(Modid.INSTANCE, Modid.GUI_HANDLER = new GUIHandler())

The register method for a GUI handler isn't where it was in 1.12.2.

Example from my simple 1.12.2 mod:
NetworkRegistry.INSTANCE.registerGuiHandler(TestModPleaseIgnore.INSTANCE, TestModPleaseIgnore.GUI_HANDLER = new GUIHandler());

(I'm currently porting it to the 1.13-pre test build 1.13-24.0.172-1.13-pre)

@LordMonoxide

This comment has been minimized.

Copy link

@LordMonoxide LordMonoxide commented Feb 12, 2019

Hey, it seems that the information about datapacks might be incorrect. IResourceManager.getAllResourceLocations is only giving me locations from my assets directory. See my post for more info.

If anyone has information on accessing files in datapacks, it would be greatly appreciated.

@liach

This comment has been minimized.

Copy link

@liach liach commented Feb 16, 2019

@LordMonoxide It will give you the correct locations for data stuff when and only when you start a dedicated or integrated server. In other words, that is when you load a local world in a client or run a server.

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Feb 16, 2019

@LordMonoxide yes, datapacks are only on the logical server.

@galaran

This comment has been minimized.

Copy link

@galaran galaran commented Feb 17, 2019

LWJGL section addition: LWJGL 3.x switched input library from JInput to GLFW, which has different key mapping. See GLFW_KEY_* constants in org.lwjgl.glfw.GLFW

@Dalarion

This comment has been minimized.

Copy link

@Dalarion Dalarion commented Feb 21, 2019

How do I add ArmorMaterial now ? Cause I can't use EnumHelper.addArmorMaterial(); ?

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Feb 24, 2019

@Dalarion updated

@peter1745

This comment has been minimized.

Copy link

@peter1745 peter1745 commented Mar 1, 2019

I can't figure out how to register TileEntities, I've found that I need to register a TileEntityType, but then the game crashes on load

@peter1745

This comment has been minimized.

Copy link

@peter1745 peter1745 commented Mar 1, 2019

I can't figure out how to register TileEntities, I've found that I need to register a TileEntityType, but then the game crashes on load

Nevermind, i figured it out, you have to register the TileEntityType very early on in mod construction, in your mod classes constructor

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Mar 5, 2019

@peter1745 no, that's not the right time. TileEntityType should be registered in a registry event.

@derfl007

This comment has been minimized.

Copy link

@derfl007 derfl007 commented Mar 13, 2019

Changing id:model to id:block/model in the blockstates doesn't work for me, i still have to leave out the block, otherwise it references to models/block/block/model.json
Edit: Okay so this only happens for some blockstate files, could it be that it only happens to non-forge blockstate files?

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Mar 14, 2019

@derfl007 the change noted is only for the vanilla blockstate json format. the forge format needs to be updated, but currently uses the old way

@marcus8448

This comment has been minimized.

Copy link

@marcus8448 marcus8448 commented Mar 15, 2019

Is there any way to make an EnumRarity? There's no create method

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Mar 15, 2019

@marcus8448 you'll have to ask forge to add one

@Parker8283

This comment has been minimized.

Copy link

@Parker8283 Parker8283 commented Mar 23, 2019

@CatRabbit499
You want to register your GUI handler with ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.GUIFACTORY, ...);, where the ... parameter is replaced with a Supplier to a Function that takes an FMLPlayMessages.OpenContainer as its parameter and returns a GuiScreen. See this for how IronChest does it.

To open a GUI, you would use the NetworkHooks.openGui family of functions. Here's how IronChest does it.

I'm not sure if this would count as a good thing to add to the Primer since it's more of an FML update than a Minecraft update, but that's what you're going for.

@Ivorforce

This comment has been minimized.

Copy link

@Ivorforce Ivorforce commented Mar 26, 2019

For anybody intending to read states from legacy block ID + metadata:

public static IBlockState fromLegacyMetadata(String blockID, byte metadata)
{
    int legacyBlockID = BlockStateFlatternEntities.getBlockId(blockID);
    Dynamic<?> dynamicNBT = BlockStateFlatteningMap.getFixedNBTForID((legacyBlockID << 4) + metadata & 15);
    NBTTagCompound compound = (NBTTagCompound) dynamicNBT.getValue();
    return NBTUtil.readBlockState(compound);
}

I haven't tested it yet but it should work roughly like this for any vanilla entries. I'm not sure how Forge is intending to solve this for modded stuff yet though.

@cpw

This comment has been minimized.

Copy link

@cpw cpw commented Mar 30, 2019

SidedProxy

=> DistExecutor

e.g. public static IProxy proxy = DistExecutor.runForDist(() -> ClientProxy::new, () -> ServerProxy::new);

Please note : ()->()->new ClientProxy() is the preferred form - I believe the method reference will cause the method and class to resolve in the context of the primary class, not the lambda, and could therefore fail on the wrong side.

@Wabbit0101

This comment has been minimized.

Copy link

@Wabbit0101 Wabbit0101 commented Apr 11, 2019

Soooo...where did all the @Optional.InterfaceList and @Optional.Interface annotations go? Is this no longer a thing and if not, how do we optionally include APIs and have them magically go away (OptionalMod doesn't seem to be a replacement but that's the closed thing I've been able to find...) Are mod authors expected to provide their own annotations for instance...ala JEI and InvTweaks?

@RobertSkalko

This comment has been minimized.

Copy link

@RobertSkalko RobertSkalko commented Apr 24, 2019

how do you get stuff like EnumParticleTypes now?
Edit: it's just Particles now.

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Apr 30, 2019

@Wabbit0101 capabilities and IMC.

@multiplemonomials

This comment has been minimized.

Copy link

@multiplemonomials multiplemonomials commented May 6, 2019

The textures/blocks and textures/items folders have been renamed to textures/block and textures/item

[external screaming]

I have already spent too many hours of my life fiddling around with incorrect JSON block/texture/whatever paths. I miss the days when you could just write regular code...

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented May 10, 2019

@multiplemonomials you do realize a one-line sed script could fix this completely for you? you're a programmer, you should aim to automate all of this away.

besides, you don't even have to change this for your own mod if you don't want to, it's only mojang's textures that have moved.

don't complain about trivial things :P

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented May 20, 2019

fixed

@lehjr

This comment has been minimized.

Copy link

@lehjr lehjr commented May 24, 2019

Probably a good time to update the info here to reflect extended states being replaced with... um... welll, this: MinecraftForge/MinecraftForge#5564

@desht

This comment has been minimized.

Copy link

@desht desht commented Jun 9, 2019

So, a few major things I've hit so far in porting from 1.13.2 to 1.14.2:

  • Containers & GUI creation has changed
    • ExtensionPoint.GUIFACTORY is gone. Container types are registry objects now and are associated with a GUI object (subclass of Screen) via ScreenManager.registerFactory()
    • IInteractionObject is gone, replaced with INamedContainerProvider
    • So IInteractionObject#getGuiID() is also gone, maybe replaced with an integer parameter in IContainerProvider#createMenu() ? FMLPlayMessages.OpenContainer now also has an int rather than ResourceLocation id field which backs that up. Update: the integer windowId is just an internal value you pass to the super constructor, but never need to otherwise worry about.
    • Container constructor now takes a ContainerType parameter which is a registry object, as mentioned above.
    • Haven't really figured this out yet. See https://www.minecraftforge.net/forum/topic/71577-1142-containers-and-guis/ for some more info
  • BlockFaceShape has gone, not needed anymore. "the game is smart enough to check your VoxelShapes from getShape now"
  • There's a new BlockRayTraceResult class which is used in several place to aggregate some parameters. E.g. ItemUseContext and BlockUseContext constructors, ItemStack.onItemUseFirst() etc.
  • RecipeSerializers is gone; recipe serializers are now registry objects and should be registered as in https://github.com/desht/ModularRouters/blob/MC1.14-master/src/main/java/me/desht/modularrouters/core/ModRecipes.java#L23-L24 and used as in https://github.com/desht/ModularRouters/tree/MC1.14-master/src/main/java/me/desht/modularrouters/recipe
@multiplemonomials

This comment has been minimized.

Copy link

@multiplemonomials multiplemonomials commented Jun 10, 2019

Okay, got a good start on my 1.13 port. A few questions:

  • What's the replacement for FMLCommonHandler.instance().getMinecraftServerInstance()?
  • I18n is now marked as client-only. Is there any possible way to do translations on the server side now? Asking... for a friend
  • What's the equivalent for the acceptableRemoteVersions = "*" argument for @mod? I want to make it so my mod is only required on the server.
@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Jun 11, 2019

@multiplemonomials

  1. that's in the doc
  2. Never translate on the server, send TextComponentTranslations to the client
  3. dunno
@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Jun 11, 2019

@desht updated for containers

@multiplemonomials

This comment has been minimized.

Copy link

@multiplemonomials multiplemonomials commented Jun 11, 2019

I could not find anything about FMLCommonHandler anywhere in this doc, or anywhere else...

I ended up replacing my

FMLCommonHandler.instance().getMinecraftServerInstance().getPlayerList().sendMessage(messageText);

with

MinecraftServer server = deadEntity.world.getServer();
server.getPlayerList().sendMessage(new TextComponentString(messageText));
@Rongmario

This comment has been minimized.

Copy link

@Rongmario Rongmario commented Jun 11, 2019

@multiplemonomials check again in the Nitty Gritty Random Things section.

@Wabbit0101

This comment has been minimized.

Copy link

@Wabbit0101 Wabbit0101 commented Jun 11, 2019

PotionHelper -> PotionBrewing EXCEPT all of the mod-used APIs like addMix, addContainer seem to be private now with no apparent replacements. As these don't seem to be noted above:

  • PotionTypes -> Potions
  • PotionType -> Potion
  • Potion -> Effect
  • PotionEffect -> EffectInstance
@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Jun 12, 2019

@Wabbit0101 the forge AT should be making those public, if not the names probably changed and the AT hasn't been updated. ask around on discord and file a bug

@desht

This comment has been minimized.

Copy link

@desht desht commented Jun 13, 2019

Couple of notes regarding (ticking) mod tile entities:

  • Be sure that your TE implements ITickableTileEntity and not ITickable. If you don't, you won't see any errors, but your TE won't tick.
  • When registering your TE, be sure to pass all the blocks for that TE (most likely just one block, but can be multiple) to the TileEntityType.Builder.create() method. Again, if you don't - no errors, but no ticking (and no rendering, if you're using a TESR). Probably good practice to do this for every TE regardless.
@Justsnoopy30

This comment has been minimized.

Copy link

@Justsnoopy30 Justsnoopy30 commented Jun 16, 2019

@williewillus About the MCP section, how do you update plain non-forge MCP 1.12.2 to 1.14.2?

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Jun 16, 2019

@Justsnoopy30 this doc doesn't support non forge cases, I assume if you're using mcp without forge you know what you're doing :D

@Wabbit0101

This comment has been minimized.

Copy link

@Wabbit0101 Wabbit0101 commented Jun 17, 2019

@multiplemonomials I'm stumped here as well...but I think you use ServerLifecycleHooks.getCurrentServer() to get the server...FakePlayer does! (Warning I'm still in the omg-bellions-and-bellions-of-compile-errors phase so i cannot verify...)

@desht

This comment has been minimized.

Copy link

@desht desht commented Jun 18, 2019

@williewillus - one note to update: "For rendering-only properties that don't need to be set serverside, use extended states instead (an improvement to them is TBD and so this is not working just yet)"

The IModelData interface is now in Forge, and works - MinecraftForge/MinecraftForge#5564. At least, I had it working well in my mod for 1.13.2, but Forge for 1.14.2 currently doesn't fire the ModelBakeEvent which prevents my mod from being able to test it out there (MinecraftForge/MinecraftForge#5805). Update it's fired since Forge 26.0.37 and appears to be working fine.

@Sinhika

This comment has been minimized.

Copy link

@Sinhika Sinhika commented Jun 18, 2019

A note for people porting their armor and tool mods from 1.12.2 or lower: for models the material name needs to be prefixed by the modid, (e.g. "simpleores:copper" instead of just "copper") otherwise the texture loading routine defaults to searching in the "minecraft" resource location and fails to find your armor textures (e.g. there is no "minecraft:textures/models/armor/copper_boots"). I haven't ported the tools over yet, but I anticipate finding the same quirk.

Also, it pays to read the fine print in the documentation. In recipes, an "item" and a "tag" are NOT the same thing, and if you specify your ore dictionary tags as "item", your recipe will give up in despair when it tries to load a non-existent "forge:ingots/copper" ITEM. It works fine if you tell it to use a "tag" : "forge:ingots/copper" TAG. (Presuming you defined ingots/copper in the first place).

@JohnTheLate

This comment has been minimized.

Copy link

@JohnTheLate JohnTheLate commented Jul 22, 2019

removeBlock does not have the same effect on liquids that setBlockToAir had. In order to remove a liquid, use worldIn.setBlockState(blockpos, Blocks.AIR.getDefaultState(), 11); instead.

@Draco18s

This comment has been minimized.

Copy link

@Draco18s Draco18s commented Aug 17, 2019

It should be noted that RegistryEvent.Register fires on the MOD bus, but the RegisterDimensionsEvent fires on the FORGE bus. Just to make registering a new dimension a pain in the ass. And oh yeah, you only need to register it once, when you load a world with your dim in it already you don't need to register it again (the game will whine and complain about it) so in order to have a reference to your DimensionType (which, you know, you need in order to get the World object for it) you have to pull it back out of the registry yourself.

@bl4ckscor3

This comment has been minimized.

Copy link

@bl4ckscor3 bl4ckscor3 commented Sep 20, 2019

@Commoble made a Dimension tutorial gist (still WIP according to Commoble), might be worth putting in here for further reference. https://gist.github.com/Commoble/ac7d7b57c9cbbfcae310c4ab110c3cc0

@Draco18s

This comment has been minimized.

Copy link

@Draco18s Draco18s commented Nov 29, 2019

Your world renderers (RenderWorldLastEvent) look weird? give this a read: https://discordapp.com/channels/313125603924639766/454376090362970122/588257071812837376

You can't link channel messages like that. People who aren't already in the server will not be able to see it. For example, I know that there are only six or so people who can view this link:
https://discordapp.com/channels/272860530505547776/272860530505547776/646443404854820884

@desht

This comment has been minimized.

Copy link

@desht desht commented Dec 4, 2019

@Draco18s is correct regarding Discord links.

The problem being described is that the GL translation when this event is fired is different from 1.12.2. Code ported directly over will show anything rendered in this event too high on the Y axis, by the player's eye height. You can work around this problem by calling GlStateManager.translated(-TileEntityRendererDispatcher.staticPlayerX, -TileEntityRendererDispatcher.staticPlayerY, -TileEntityRendererDispatcher.staticPlayerZ); at the start of your event handler code (where previously you would have translated to the player's interpolated X/Y/Z position).

Example: 1.14.4 code, and the equivalent 1.12.2 code.

@Funwayguy

This comment has been minimized.

Copy link

@Funwayguy Funwayguy commented Jan 6, 2020

Would anyone here happen to know the 1.14 equivalent to Minecraft.getMinecraft().getFramebuffer().enableStencil() if even that's still a thing? (and no scissor doesn't work for my use case)

@rikka0w0

This comment has been minimized.

Copy link

@rikka0w0 rikka0w0 commented Apr 23, 2020

We still need StataMappers or its replacement!
In 1.12.2, my mod uses StataMapper to avoid writing tons of json files (BlockState json) files and redirect the loading of variants to my own loader.
In 1.15.2, the StataMapper has been removed and the hardcoded logic in ModelBakery::loadBlockstate(ResourceLocation) suggests that it only accepts JSON files! Which means I have to write a JSON Blockstate file for each block, flattening makes things worse as it increases the number of JSON files required.
I have three solutions, the first is to write a json blockstate file for each block, it points to a json model file, which then tell forge to use my own IModelLoader to load the model. The second is to add back the StateMapper, it will take some effort but I definitely think it is worthwhile. The last one is to use IForgeTileEntity::getModelData, but some of my blocks don't have a tileEntity.

@Rongmario

This comment has been minimized.

Copy link

@Rongmario Rongmario commented Apr 23, 2020

@rikka0w0 use data providers!

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Apr 23, 2020

use data generators.

@williewillus

This comment has been minimized.

Copy link
Owner Author

@williewillus williewillus commented Apr 23, 2020

also, I don't know what you hope to achieve by commenting here, I don't have control over vanilla or forge decisions.
Please visit a support site like the various discord servers for live help.

@rikka0w0

This comment has been minimized.

Copy link

@rikka0w0 rikka0w0 commented Apr 24, 2020

also, I don't know what you hope to achieve by commenting here, I don't have control over vanilla or forge decisions.
Please visit a support site like the various discord servers for live help.

https://www.minecraftforge.net/forum/topic/84344-1152-looking-for-statamapper-replacement/?tab=comments#comment-397904

I just want to have others suggestions before making major change to my mod structure /w
Finally I decide to write JSON files, as it is the standard way now and the future trend (declarative rather than hard coding)
Although I know this is not a standard case, but still I hope this can help other people porting their mod

@Rongmario

This comment has been minimized.

Copy link

@Rongmario Rongmario commented Apr 24, 2020

Yeah, @rikka0w0 try not to listen to diesieben07 much; just extend BlockStateProvider and generate instead of manually writing.

@rikka0w0

This comment has been minimized.

Copy link

@rikka0w0 rikka0w0 commented Apr 24, 2020

BlockStateProvider

Just one more question, how to invoke my own BlockStateProvider?
The DataGatherEvent is not called during the launching of Minecraft.

@Rongmario

This comment has been minimized.

Copy link

@Rongmario Rongmario commented Apr 24, 2020

Just one more question, how to invoke my own BlockStateProvider?
The DataGatherEvent is not called during the launching of Minecraft.

Extend BlockStateProvider and do your stuff, subscribe to the event then run the idea task to generate before running mc. Email me if you have any other concerns and maybe I can help; as here is not the place.

@magicus

This comment has been minimized.

Copy link

@magicus magicus commented Jun 14, 2020

If you try to run the python IntelliJ migration script, some helpful hints:

  • It needs python3
  • On macOS, the directory to put the generated migration script in is ~/Library/Preferences/IdeaIC2019.2/migration. There is a ~/Library/Application\ Support/IdeaIC2019.2/ directory, but it's not the right. (Took me a while to figure out...)
@GangGang77

This comment has been minimized.

Copy link

@GangGang77 GangGang77 commented Jun 15, 2020

What is the link to the discord?

@LemADEC

This comment has been minimized.

Copy link

@LemADEC LemADEC commented Jul 14, 2020

  • When upgrading from 1.12, you need to replace trove with fastutils.
  • The certificateFingerprint and related FMLFingerprintViolationEvent are AWOL. The JVM will check it like all other signatures, If you have invalid signatures then the JVM will crash. (Source: cpw/Lex on Discord Feb 2019).
  • There's no replacement for @Optional.Interface and @Optional.Method as they are completely incompatible with Java9+ (Source: cpw on Discord Sept 2019).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.