diff --git a/build.gradle b/build.gradle
index 8fd9b9ebe..4daed6d1c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,16 +23,16 @@ sourceCompatibility = 1.8
 targetCompatibility = 1.8
 
 archivesBaseName = "fabric"
-version = "0.0.2"
+version = "0.0.3-SNAPSHOT"
 
 minecraft {
 	refmapName = "net.fabricmc.fabric.refmap.json"
 }
 
 dependencies {
-	minecraft "com.mojang:minecraft:18w45a"
-	mappings "net.fabricmc:pomf:18w45a.5"
-	modCompile "net.fabricmc:fabric-loader:18w44a-0.1.0.45"
+	minecraft "com.mojang:minecraft:18w46a"
+	mappings "net.fabricmc:pomf:18w46a.4"
+	modCompile "net.fabricmc:fabric-loader:18w44a-0.1.0.46"
 }
 
 apply from: 'https://github.com/FabricMC/fabric-docs/raw/master/gradle/maven.gradle'
diff --git a/src/main/java/net/fabricmc/fabric/mixin/networking/MixinClientPlayNetworkHandler.java b/src/main/java/net/fabricmc/fabric/mixin/networking/MixinClientPlayNetworkHandler.java
index 7538cb40c..2caa901d6 100644
--- a/src/main/java/net/fabricmc/fabric/mixin/networking/MixinClientPlayNetworkHandler.java
+++ b/src/main/java/net/fabricmc/fabric/mixin/networking/MixinClientPlayNetworkHandler.java
@@ -21,7 +21,7 @@ import net.fabricmc.fabric.networking.CustomPayloadHandlerRegistry;
 import net.fabricmc.fabric.networking.PacketContext;
 import net.fabricmc.fabric.networking.SPacketCustomPayloadAccessor;
 import net.minecraft.client.MinecraftGame;
-import net.minecraft.client.network.handler.ClientGameNetworkHandler;
+import net.minecraft.client.network.handler.ClientPlayNetworkHandler;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerServer;
 import net.minecraft.network.handler.ServerPlayNetworkHandler;
@@ -35,15 +35,13 @@ import org.spongepowered.asm.mixin.injection.At;
 import org.spongepowered.asm.mixin.injection.Inject;
 import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
 
-@Mixin(ClientGameNetworkHandler.class)
+@Mixin(ClientPlayNetworkHandler.class)
 public class MixinClientPlayNetworkHandler implements PacketContext {
 	@Shadow
 	private MinecraftGame game;
 
 	@Inject(method = "onCustomPayload", at = @At("HEAD"), cancellable = true)
 	public void onCustomPayload(CPacketCustomPayload packet, CallbackInfo info) {
-		MinecraftGame game = MinecraftGame.getInstance();
-
 		if (CustomPayloadHandlerRegistry.CLIENT.accept(packet.getChannel(), this, packet.getData())) {
 			info.cancel();
 		}
diff --git a/src/main/java/net/fabricmc/fabric/mixin/registry/MixinBootstrap.java b/src/main/java/net/fabricmc/fabric/mixin/registry/MixinBootstrap.java
new file mode 100644
index 000000000..c1ea6cbde
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/mixin/registry/MixinBootstrap.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.registry;
+
+import net.fabricmc.fabric.networking.CustomPayloadHandlerRegistry;
+import net.fabricmc.fabric.registry.ListenableRegistry;
+import net.fabricmc.fabric.registry.RegistrySyncManager;
+import net.fabricmc.fabric.registry.listeners.BootstrapBiomeRegistryListener;
+import net.fabricmc.fabric.registry.listeners.BootstrapBlockRegistryListener;
+import net.fabricmc.fabric.registry.listeners.BootstrapFluidRegistryListener;
+import net.fabricmc.fabric.registry.listeners.BootstrapItemRegistryListener;
+import net.minecraft.Bootstrap;
+import net.minecraft.block.Block;
+import net.minecraft.block.Blocks;
+import net.minecraft.fluid.Fluid;
+import net.minecraft.fluid.Fluids;
+import net.minecraft.item.Item;
+import net.minecraft.item.Items;
+import net.minecraft.util.registry.Registry;
+import net.minecraft.world.biome.Biome;
+import net.minecraft.world.biome.Biomes;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(Bootstrap.class)
+public class MixinBootstrap {
+	@Inject(method = "setOutputStreams", at = @At("RETURN"))
+	private static void initialize(CallbackInfo info) {
+		// access Blocks, Items, ...
+		Object o0 = Biomes.THE_END;
+		Object o1 = Blocks.AIR;
+		Object o3 = Fluids.EMPTY;
+		Object o2 = Items.AIR;
+
+		((ListenableRegistry<Biome>) Registry.BIOMES).registerListener(new BootstrapBiomeRegistryListener());
+		((ListenableRegistry<Block>) Registry.BLOCKS).registerListener(new BootstrapBlockRegistryListener());
+		((ListenableRegistry<Fluid>) Registry.FLUIDS).registerListener(new BootstrapFluidRegistryListener());
+		((ListenableRegistry<Item>) Registry.ITEMS).registerListener(new BootstrapItemRegistryListener());
+
+		// The packet code is not side-specific, so this is fine!
+		CustomPayloadHandlerRegistry.CLIENT.register(RegistrySyncManager.ID, RegistrySyncManager::receivePacket);
+	}
+}
\ No newline at end of file
diff --git a/src/main/java/net/fabricmc/fabric/mixin/registry/MixinIdList.java b/src/main/java/net/fabricmc/fabric/mixin/registry/MixinIdList.java
new file mode 100644
index 000000000..c64b423f1
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/mixin/registry/MixinIdList.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.registry;
+
+import net.fabricmc.fabric.registry.ExtendedIdList;
+import net.minecraft.util.IdList;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+
+import java.util.IdentityHashMap;
+import java.util.List;
+
+@Mixin(IdList.class)
+public class MixinIdList implements ExtendedIdList {
+	@Shadow
+	private int field_11099;
+	@Shadow
+	private IdentityHashMap idMap;
+	@Shadow
+	private List list;
+
+	@Override
+	public void clear() {
+		field_11099 = 0;
+		idMap.clear();
+		list.clear();
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/mixin/registry/MixinIdRegistry.java b/src/main/java/net/fabricmc/fabric/mixin/registry/MixinIdRegistry.java
new file mode 100644
index 000000000..3a57b05b9
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/mixin/registry/MixinIdRegistry.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.registry;
+
+import com.google.common.collect.BiMap;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import net.fabricmc.fabric.registry.ListenableRegistry;
+import net.fabricmc.fabric.registry.RegistryListener;
+import net.fabricmc.fabric.registry.RemapException;
+import net.fabricmc.fabric.registry.RemappableRegistry;
+import net.minecraft.class_3513;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.IdRegistry;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(IdRegistry.class)
+public abstract class MixinIdRegistry<T> implements RemappableRegistry, ListenableRegistry<T>, RegistryListener<T> {
+	@Shadow
+	protected static final Logger ID_LOGGER = LogManager.getLogger();
+	@Shadow
+	protected class_3513<T> idStore;
+	@Shadow
+	protected BiMap<Identifier, T> objectMap;
+
+	private Object2IntMap<Identifier> initialIdMap;
+	private RegistryListener[] listeners;
+
+	@Override
+	public void registerListener(RegistryListener<T> listener) {
+		if (listeners == null) {
+			listeners = new RegistryListener[] { listener };
+		} else {
+			RegistryListener[] newListeners = new RegistryListener[listeners.length + 1];
+			System.arraycopy(listeners, 0, newListeners, 0, listeners.length);
+			newListeners[listeners.length] = listener;
+			listeners = newListeners;
+		}
+	}
+
+	@Inject(method = "set", at = @At("HEAD"))
+	public void set(int id, Identifier identifier, Object object, CallbackInfoReturnable info) {
+		IdRegistry<Object> registry = (IdRegistry<Object>) (Object) this;
+		if (listeners != null) {
+			for (RegistryListener listener : listeners) {
+				listener.beforeRegistered(registry, id, identifier, object, !registry.contains(identifier));
+			}
+		}
+	}
+
+	@Override
+	public void remap(Object2IntMap<Identifier> idMap) throws RemapException {
+		//noinspection unchecked
+		IdRegistry<Object> registry = (IdRegistry<Object>) (Object) this;
+
+		if (!idMap.keySet().equals(registry.keys())) {
+			throw new RemapException("Source and destination keys differ!");
+		}
+
+		if (initialIdMap == null) {
+			initialIdMap = new Object2IntOpenHashMap<>();
+			for (Identifier id : registry.keys()) {
+				//noinspection unchecked
+				initialIdMap.put(id, registry.getRawId(registry.get(id)));
+			}
+		}
+
+		if (listeners != null) {
+			for (RegistryListener listener : listeners) {
+				listener.beforeCleared(registry);
+			}
+		}
+
+		// We don't really need to clear anything but idStore yet.
+		idStore.method_15229();
+
+		for (Identifier identifier : objectMap.keySet()) {
+			int id = idMap.getInt(identifier);
+			T object = objectMap.get(identifier);
+			idStore.method_15230(object, id);
+
+			if (listeners != null) {
+				for (RegistryListener listener : listeners) {
+					listener.beforeRegistered(registry, id, identifier, object, false);
+				}
+			}
+		}
+	}
+
+	@Override
+	public void unmap() throws RemapException {
+		if (initialIdMap != null) {
+			remap(initialIdMap);
+			initialIdMap = null;
+		}
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/mixin/registry/MixinServerPlayNetworkHandler.java b/src/main/java/net/fabricmc/fabric/mixin/registry/MixinServerPlayNetworkHandler.java
new file mode 100644
index 000000000..437b6f798
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/mixin/registry/MixinServerPlayNetworkHandler.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.registry;
+
+import net.fabricmc.api.Side;
+import net.fabricmc.fabric.networking.CustomPayloadHandlerRegistry;
+import net.fabricmc.fabric.networking.PacketContext;
+import net.fabricmc.fabric.networking.SPacketCustomPayloadAccessor;
+import net.fabricmc.fabric.registry.RegistrySyncManager;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerServer;
+import net.minecraft.network.ClientConnection;
+import net.minecraft.network.Packet;
+import net.minecraft.network.handler.ServerPlayNetworkHandler;
+import net.minecraft.network.packet.server.SPacketCustomPayload;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.ThreadTaskQueue;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
+
+@Mixin(ServerPlayNetworkHandler.class)
+public abstract class MixinServerPlayNetworkHandler {
+	@Shadow
+	public abstract void sendPacket(Packet<?> var1);
+
+	@Inject(method = "<init>", at = @At("RETURN"))
+	public void init(MinecraftServer server, ClientConnection connection, EntityPlayerServer player, CallbackInfo info) {
+		//if (server.isDedicated()) {
+		sendPacket(RegistrySyncManager.createPacket());
+		//}
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/mixin/registry/client/MixinBlockColorMap.java b/src/main/java/net/fabricmc/fabric/mixin/registry/client/MixinBlockColorMap.java
new file mode 100644
index 000000000..627422670
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/mixin/registry/client/MixinBlockColorMap.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.registry.client;
+
+import net.fabricmc.fabric.registry.ListenableRegistry;
+import net.fabricmc.fabric.registry.listeners.IdListUpdater;
+import net.minecraft.block.Block;
+import net.minecraft.client.render.block.BlockColorMap;
+import net.minecraft.client.render.block.BlockColorMapper;
+import net.minecraft.client.render.item.ItemColorMap;
+import net.minecraft.client.render.item.ItemColorMapper;
+import net.minecraft.util.IdList;
+import net.minecraft.util.registry.Registry;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(BlockColorMap.class)
+public class MixinBlockColorMap implements IdListUpdater.Container<BlockColorMapper> {
+	@Shadow
+	private IdList<BlockColorMapper> mappers;
+
+	@Inject(method = "create", at = @At("RETURN"))
+	private static void create(CallbackInfoReturnable<BlockColorMap> info) {
+		((ListenableRegistry) Registry.BLOCKS).registerListener(new IdListUpdater<Block, BlockColorMapper>((IdListUpdater.Container<BlockColorMapper>) (Object) info.getReturnValue()));
+	}
+
+	@Override
+	public IdList<BlockColorMapper> getIdListForRegistryUpdating() {
+		return mappers;
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/mixin/registry/client/MixinItemColorMap.java b/src/main/java/net/fabricmc/fabric/mixin/registry/client/MixinItemColorMap.java
new file mode 100644
index 000000000..b184aee34
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/mixin/registry/client/MixinItemColorMap.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.mixin.registry.client;
+
+import net.fabricmc.fabric.registry.ListenableRegistry;
+import net.fabricmc.fabric.registry.listeners.IdListUpdater;
+import net.minecraft.client.render.block.BlockColorMap;
+import net.minecraft.client.render.item.ItemColorMap;
+import net.minecraft.client.render.item.ItemColorMapper;
+import net.minecraft.item.Item;
+import net.minecraft.util.IdList;
+import net.minecraft.util.registry.Registry;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.injection.At;
+import org.spongepowered.asm.mixin.injection.Inject;
+import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+
+@Mixin(ItemColorMap.class)
+public class MixinItemColorMap implements IdListUpdater.Container<ItemColorMapper> {
+	@Shadow
+	private IdList<ItemColorMapper> field_1996;
+
+	@Inject(method = "method_1706", at = @At("RETURN"))
+	private static void method_1706(BlockColorMap blockMap, CallbackInfoReturnable<ItemColorMap> info) {
+		((ListenableRegistry) Registry.ITEMS).registerListener(new IdListUpdater<Item, ItemColorMapper>((IdListUpdater.Container<ItemColorMapper>) (Object) info.getReturnValue()));
+	}
+
+	@Override
+	public IdList<ItemColorMapper> getIdListForRegistryUpdating() {
+		return field_1996;
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/networking/CustomPayloadHandlerRegistry.java b/src/main/java/net/fabricmc/fabric/networking/CustomPayloadHandlerRegistry.java
index e0db7486c..478b5c65d 100644
--- a/src/main/java/net/fabricmc/fabric/networking/CustomPayloadHandlerRegistry.java
+++ b/src/main/java/net/fabricmc/fabric/networking/CustomPayloadHandlerRegistry.java
@@ -42,9 +42,14 @@ public class CustomPayloadHandlerRegistry {
 	}
 
 	public boolean accept(Identifier identifier, PacketContext context, PacketByteBuf buf) {
-		BiConsumer<PacketContext, PacketByteBuf> consumer = consumerMap.get(context);
+		BiConsumer<PacketContext, PacketByteBuf> consumer = consumerMap.get(identifier);
 		if (consumer != null) {
-			consumer.accept(context, buf);
+			try {
+				consumer.accept(context, buf);
+			} catch (Throwable t) {
+				// TODO: handle better
+				t.printStackTrace();
+			}
 			return true;
 		} else {
 			return false;
diff --git a/src/main/java/net/fabricmc/fabric/registry/ExtendedIdList.java b/src/main/java/net/fabricmc/fabric/registry/ExtendedIdList.java
new file mode 100644
index 000000000..5155438f1
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/ExtendedIdList.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry;
+
+public interface ExtendedIdList {
+	void clear();
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/ListenableRegistry.java b/src/main/java/net/fabricmc/fabric/registry/ListenableRegistry.java
new file mode 100644
index 000000000..32e1f5f9f
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/ListenableRegistry.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry;
+
+public interface ListenableRegistry<T> {
+	void registerListener(RegistryListener<T> listener);
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/RegistryListener.java b/src/main/java/net/fabricmc/fabric/registry/RegistryListener.java
new file mode 100644
index 000000000..62c05d237
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/RegistryListener.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry;
+
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+public interface RegistryListener<T> {
+	default void beforeCleared(Registry<T> registry) {}
+	default void beforeRegistered(Registry<T> registry, int id, Identifier identifier, T object, boolean isNew) {}
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/RegistrySyncManager.java b/src/main/java/net/fabricmc/fabric/registry/RegistrySyncManager.java
new file mode 100644
index 000000000..f5f590536
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/RegistrySyncManager.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry;
+
+import com.google.common.collect.ImmutableSet;
+import io.netty.buffer.Unpooled;
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
+import net.fabricmc.fabric.networking.CustomPayloadHandlerRegistry;
+import net.fabricmc.fabric.networking.PacketContext;
+import net.minecraft.entity.player.EntityPlayerServer;
+import net.minecraft.nbt.TagCompound;
+import net.minecraft.network.packet.client.CPacketCustomPayload;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.PacketByteBuf;
+import net.minecraft.util.registry.IdRegistry;
+import net.minecraft.util.registry.ModifiableRegistry;
+import net.minecraft.util.registry.Registry;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public final class RegistrySyncManager {
+	public static final Identifier ID = new Identifier("fabric", "registry/sync");
+	private static final Set<Identifier> REGISTRY_BLACKLIST = ImmutableSet.of();
+	private static final Set<Identifier> REGISTRY_BLACKLIST_NETWORK = ImmutableSet.of();
+
+	private RegistrySyncManager() {
+
+	}
+
+	public static CPacketCustomPayload createPacket() {
+		PacketByteBuf buf = new PacketByteBuf(Unpooled.buffer());
+		buf.writeVarInt(1);
+		buf.writeTagCompound(toTag(true));
+
+		CPacketCustomPayload packet = new CPacketCustomPayload(ID, buf);
+		return packet;
+	}
+
+	public static void receivePacket(PacketContext context, PacketByteBuf buf) {
+		int version = buf.readVarInt();
+		if (version != 1) {
+			// TODO: log error
+		}
+
+		TagCompound compound = buf.readTagCompound();
+		try {
+			apply(compound);
+		} catch (RemapException e) {
+			// TODO: log error properly
+			e.printStackTrace();
+		}
+	}
+
+	public static TagCompound toTag(boolean isClientSync) {
+		TagCompound mainTag = new TagCompound();
+
+		for (Identifier registryId : Registry.REGISTRIES.keys()) {
+			if (REGISTRY_BLACKLIST.contains(registryId)) {
+				continue;
+			} else if (isClientSync && REGISTRY_BLACKLIST_NETWORK.contains(registryId)) {
+				continue;
+			}
+
+			ModifiableRegistry registry = Registry.REGISTRIES.get(registryId);
+			if (registry instanceof IdRegistry && registry instanceof RemappableRegistry) {
+				TagCompound registryTag = new TagCompound();
+				//noinspection unchecked
+				for (Identifier identifier : (Set<Identifier>) registry.keys()) {
+					registryTag.setInt(identifier.toString(), registry.getRawId(registry.get(identifier)));
+				}
+				mainTag.setTag(registryId.toString(), registryTag);
+			}
+		}
+
+		return mainTag;
+	}
+
+	public static void apply(TagCompound mainTag) throws RemapException {
+		for (Identifier registryId : Registry.REGISTRIES.keys()) {
+			if (!mainTag.hasKey(registryId.toString())) {
+				continue;
+			}
+
+			TagCompound registryTag = mainTag.getTagCompound(registryId.toString());
+			ModifiableRegistry registry = Registry.REGISTRIES.get(registryId);
+			if (registry instanceof IdRegistry && registry instanceof RemappableRegistry) {
+				Object2IntMap<Identifier> idMap = new Object2IntOpenHashMap<>();
+				for (String key : registryTag.getKeys()) {
+					idMap.put(new Identifier(key), registryTag.getInt(key));
+				}
+				((RemappableRegistry) registry).remap(idMap);
+			}
+		}
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/RemapException.java b/src/main/java/net/fabricmc/fabric/registry/RemapException.java
new file mode 100644
index 000000000..5e8328375
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/RemapException.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry;
+
+public class RemapException extends Exception {
+	public RemapException(String message) {
+		super(message);
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/RemappableRegistry.java b/src/main/java/net/fabricmc/fabric/registry/RemappableRegistry.java
new file mode 100644
index 000000000..6ecbe6e8e
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/RemappableRegistry.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry;
+
+import it.unimi.dsi.fastutil.objects.Object2IntMap;
+import net.minecraft.util.Identifier;
+
+public interface RemappableRegistry {
+	void remap(Object2IntMap<Identifier> idMap) throws RemapException;
+	void unmap() throws RemapException;
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapBiomeRegistryListener.java b/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapBiomeRegistryListener.java
new file mode 100644
index 000000000..d95adc976
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapBiomeRegistryListener.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry.listeners;
+
+import net.fabricmc.fabric.registry.ExtendedIdList;
+import net.fabricmc.fabric.registry.RegistryListener;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+import net.minecraft.world.biome.Biome;
+
+public class BootstrapBiomeRegistryListener implements RegistryListener<Biome> {
+	@Override
+	public void beforeCleared(Registry<Biome> registry) {
+		((ExtendedIdList) Biome.PARENT_BIOME_ID_MAP).clear();
+	}
+
+	@Override
+	public void beforeRegistered(Registry<Biome> registry, int id, Identifier identifier, Biome object, boolean isNew) {
+		// refer net.minecraft.biome.Biomes
+		if (object.hasParent()) {
+			Biome.PARENT_BIOME_ID_MAP.add(object, id);
+		}
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapBlockRegistryListener.java b/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapBlockRegistryListener.java
new file mode 100644
index 000000000..b0595e30a
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapBlockRegistryListener.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry.listeners;
+
+import com.google.common.collect.UnmodifiableIterator;
+import net.fabricmc.fabric.registry.ExtendedIdList;
+import net.fabricmc.fabric.registry.RegistryListener;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+public class BootstrapBlockRegistryListener implements RegistryListener<Block> {
+	@Override
+	public void beforeCleared(Registry<Block> registry) {
+		((ExtendedIdList) Block.BLOCKSTATE_ID_LIST).clear();
+	}
+
+	@Override
+	public void beforeRegistered(Registry<Block> registry, int id, Identifier identifier, Block object, boolean isNew) {
+		// refer net.minecraft.block.Blocks
+		for (BlockState state : object.getStateFactory().getStates()) {
+			state.method_11590();
+			Block.BLOCKSTATE_ID_LIST.method_10205(state);
+		}
+
+		object.getDropTableId();
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapFluidRegistryListener.java b/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapFluidRegistryListener.java
new file mode 100644
index 000000000..d1db91ea3
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapFluidRegistryListener.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry.listeners;
+
+import net.fabricmc.fabric.registry.ExtendedIdList;
+import net.fabricmc.fabric.registry.RegistryListener;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockState;
+import net.minecraft.fluid.Fluid;
+import net.minecraft.fluid.FluidState;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+public class BootstrapFluidRegistryListener implements RegistryListener<Fluid> {
+	@Override
+	public void beforeCleared(Registry<Fluid> registry) {
+		((ExtendedIdList) Block.BLOCKSTATE_ID_LIST).clear();
+	}
+
+	@Override
+	public void beforeRegistered(Registry<Fluid> registry, int id, Identifier identifier, Fluid object, boolean isNew) {
+		// refer net.minecraft.fluid.Fluids
+		for (FluidState state : object.getStateFactory().getStates()) {
+			Fluid.field_15904.method_10205(state);
+		}
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapItemRegistryListener.java b/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapItemRegistryListener.java
new file mode 100644
index 000000000..124f48086
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/listeners/BootstrapItemRegistryListener.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry.listeners;
+
+import net.fabricmc.fabric.registry.ExtendedIdList;
+import net.fabricmc.fabric.registry.RegistryListener;
+import net.minecraft.item.Item;
+import net.minecraft.item.block.ItemBlock;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+public class BootstrapItemRegistryListener implements RegistryListener<Item> {
+	@Override
+	public void beforeCleared(Registry<Item> registry) {
+		Item.BLOCK_ITEM_MAP.clear();
+	}
+
+	@Override
+	public void beforeRegistered(Registry<Item> registry, int id, Identifier identifier, Item object, boolean isNew) {
+		// refer net.minecraft.item.Items
+		if (object instanceof ItemBlock) {
+			((ItemBlock) object).method_7713(Item.BLOCK_ITEM_MAP, object);
+		}
+	}
+}
diff --git a/src/main/java/net/fabricmc/fabric/registry/listeners/IdListUpdater.java b/src/main/java/net/fabricmc/fabric/registry/listeners/IdListUpdater.java
new file mode 100644
index 000000000..8c97c29f8
--- /dev/null
+++ b/src/main/java/net/fabricmc/fabric/registry/listeners/IdListUpdater.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2016, 2017, 2018 FabricMC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.fabricmc.fabric.registry.listeners;
+
+import net.fabricmc.fabric.registry.ExtendedIdList;
+import net.fabricmc.fabric.registry.RegistryListener;
+import net.minecraft.util.IdList;
+import net.minecraft.util.Identifier;
+import net.minecraft.util.registry.Registry;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class IdListUpdater<K, V> implements RegistryListener<K> {
+	public interface Container<V> {
+		IdList<V> getIdListForRegistryUpdating();
+	}
+
+	private final IdList<V> mappers;
+	private Map<Identifier, V> mapperCache = new HashMap<>();
+
+	public IdListUpdater(Container<V> container) {
+		this(container.getIdListForRegistryUpdating());
+	}
+
+	public IdListUpdater(IdList<V> mappers) {
+		this.mappers = mappers;
+	}
+
+	@Override
+	public void beforeCleared(Registry<K> registry) {
+		mapperCache.clear();
+		for (Identifier id : registry.keys()) {
+			int rawId = registry.getRawId(registry.get(id));
+			V mapper = mappers.getInt(rawId);
+			if (mapper != null) {
+				mapperCache.put(id, mapper);
+			}
+		}
+
+		((ExtendedIdList) mappers).clear();
+	}
+
+	@Override
+	public void beforeRegistered(Registry<K> registry, int id, Identifier identifier, K object, boolean isNew) {
+		if (mapperCache.containsKey(identifier)) {
+			mappers.add(mapperCache.get(identifier), id);
+		}
+	}
+}
diff --git a/src/main/resources/net.fabricmc.fabric.mixins.client.json b/src/main/resources/net.fabricmc.fabric.mixins.client.json
index a3943b5ae..bb2aad595 100644
--- a/src/main/resources/net.fabricmc.fabric.mixins.client.json
+++ b/src/main/resources/net.fabricmc.fabric.mixins.client.json
@@ -4,6 +4,8 @@
   "compatibilityLevel": "JAVA_8",
   "mixins": [
     "networking.MixinClientPlayNetworkHandler",
+    "registry.client.MixinBlockColorMap",
+    "registry.client.MixinItemColorMap",
     "resources.MixinMinecraftGame"
   ],
   "injectors": {
diff --git a/src/main/resources/net.fabricmc.fabric.mixins.common.json b/src/main/resources/net.fabricmc.fabric.mixins.common.json
index a571e0a38..08d667486 100644
--- a/src/main/resources/net.fabricmc.fabric.mixins.common.json
+++ b/src/main/resources/net.fabricmc.fabric.mixins.common.json
@@ -6,6 +6,10 @@
     "commands.MixinServerCommandManager",
     "networking.MixinServerPlayNetworkHandler",
     "networking.MixinSPacketCustomPayload",
+    "registry.MixinBootstrap",
+    "registry.MixinIdList",
+    "registry.MixinIdRegistry",
+    "registry.MixinServerPlayNetworkHandler",
     "resources.MixinMinecraftServer"
   ],
   "injectors": {