Add in storage, detection and validation of the ItemID array between client and server

and also for world saves. May help with item configuration issues.
This commit is contained in:
Christian 2012-12-07 01:52:16 -05:00 committed by LexManos
parent e6d66322fe
commit f4070ff625
21 changed files with 705 additions and 54 deletions

View File

@ -39,6 +39,7 @@ import net.minecraft.client.WorldClient;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.MapDifference;
import cpw.mods.fml.client.modloader.ModLoaderClientHelper;
import cpw.mods.fml.client.registry.KeyBindingRegistry;
@ -61,8 +62,10 @@ import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket;
import cpw.mods.fml.common.network.EntitySpawnPacket;
import cpw.mods.fml.common.network.ModMissingPacket;
import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.IEntityAdditionalSpawnData;
import cpw.mods.fml.common.registry.IThrowableEntity;
import cpw.mods.fml.common.registry.ItemData;
import cpw.mods.fml.common.registry.LanguageRegistry;
@ -113,6 +116,8 @@ public class FMLClientHandler implements IFMLSidedHandler
private DuplicateModsFoundException dupesFound;
private boolean serverShouldBeKilledQuietly;
/**
* Called to start the whole game off
*
@ -404,6 +409,7 @@ public class FMLClientHandler implements IFMLSidedHandler
@Override
public void beginServerLoading(MinecraftServer server)
{
serverShouldBeKilledQuietly = false;
// NOOP
}
@ -459,4 +465,49 @@ public class FMLClientHandler implements IFMLSidedHandler
{
return NetClientHandler.getConnectionCompatibilityLevel();
}
public void warnIDMismatch(MapDifference<Integer, ItemData> idDifferences, boolean mayContinue)
{
GuiIdMismatchScreen mismatch = new GuiIdMismatchScreen(idDifferences, mayContinue);
client.func_71373_a(mismatch);
}
public void callbackIdDifferenceResponse(boolean response)
{
if (response)
{
serverShouldBeKilledQuietly = false;
GameRegistry.releaseGate(true);
client.continueWorldLoading();
}
else
{
serverShouldBeKilledQuietly = true;
GameRegistry.releaseGate(false);
// Reset and clear the client state
client.func_71403_a((WorldClient)null);
client.func_71373_a(null);
}
}
@Override
public boolean shouldServerShouldBeKilledQuietly()
{
return serverShouldBeKilledQuietly;
}
@Override
public void disconnectIDMismatch(MapDifference<Integer, ItemData> s, NetHandler toKill, INetworkManager mgr)
{
// Nuke the connection
((NetClientHandler)toKill).func_72553_e();
// Stop GuiConnecting
GuiConnecting.forceTermination((GuiConnecting)client.field_71462_r);
// pulse the network manager queue to clear cruft
mgr.func_74428_b();
// Nuke the world client
client.func_71403_a((WorldClient)null);
// Show error screen
warnIDMismatch(s, false);
}
}

View File

@ -0,0 +1,87 @@
package cpw.mods.fml.client;
import java.util.List;
import java.util.Map.Entry;
import com.google.common.collect.Lists;
import com.google.common.collect.MapDifference;
import com.google.common.collect.MapDifference.ValueDifference;
import cpw.mods.fml.common.registry.ItemData;
import cpw.mods.fml.common.versioning.ArtifactVersion;
import net.minecraft.src.GuiButton;
import net.minecraft.src.GuiYesNo;
import net.minecraft.src.StringTranslate;
public class GuiIdMismatchScreen extends GuiYesNo {
private List<String> missingIds = Lists.newArrayList();
private List<String> mismatchedIds = Lists.newArrayList();
private boolean allowContinue;
public GuiIdMismatchScreen(MapDifference<Integer, ItemData> idDifferences, boolean allowContinue)
{
super(null,"ID mismatch", "Should I continue?", 1);
field_73942_a = this;
for (Entry<Integer, ItemData> entry : idDifferences.entriesOnlyOnLeft().entrySet())
{
missingIds.add(String.format("ID %d (ModID: %s, type %s) is missing", entry.getValue().itemId, entry.getValue().modId, entry.getValue().itemType));
}
for (Entry<Integer, ValueDifference<ItemData>> entry : idDifferences.entriesDiffering().entrySet())
{
ItemData world = entry.getValue().leftValue();
ItemData game = entry.getValue().rightValue();
mismatchedIds.add(String.format("ID %d is mismatched. World: (ModID: %s, type %s, ordinal %d) Game (ModID: %s, type %s, ordinal %d)", world.itemId, world.modId, world.itemType, world.ordinal, game.modId, game.itemType, game.ordinal));
}
this.allowContinue = allowContinue;
}
@Override
public void func_73878_a(boolean choice, int p_73878_2_)
{
FMLClientHandler.instance().callbackIdDifferenceResponse(choice);
}
@Override
public void func_73863_a(int p_73863_1_, int p_73863_2_, float p_73863_3_)
{
this.func_73873_v_();
if (!allowContinue && field_73887_h.size() == 2)
{
field_73887_h.remove(0);
}
int offset = Math.max(85 - missingIds.size() * 10 + mismatchedIds.size() * 30, 10);
this.func_73732_a(this.field_73886_k, "Forge Mod Loader has found world ID mismatches", this.field_73880_f / 2, offset, 0xFFFFFF);
offset += 10;
for (String s: missingIds) {
this.func_73732_a(this.field_73886_k, s, this.field_73880_f / 2, offset, 0xEEEEEE);
offset += 10;
}
for (String s: mismatchedIds) {
this.func_73732_a(this.field_73886_k, s, this.field_73880_f / 2, offset, 0xEEEEEE);
offset += 10;
}
offset += 10;
if (allowContinue)
{
this.func_73732_a(this.field_73886_k, "Do you wish to continue loading?", this.field_73880_f / 2, offset, 0xFFFFFF);
offset += 10;
}
else
{
this.func_73732_a(this.field_73886_k, "You cannot connect to this server", this.field_73880_f / 2, offset, 0xFFFFFF);
offset += 10;
}
// super.super. Grrr
for (int var4 = 0; var4 < this.field_73887_h.size(); ++var4)
{
GuiButton var5 = (GuiButton)this.field_73887_h.get(var4);
var5.field_73743_d = Math.min(offset + 10, this.field_73881_g - 20);
if (!allowContinue)
{
var5.field_73746_c = this.field_73880_f / 2 - 75;
var5.field_73744_e = StringTranslate.func_74808_a().func_74805_b("gui.done");
}
var5.func_73737_a(this.field_73882_e, p_73863_1_, p_73863_2_);
}
}
}

View File

@ -28,6 +28,7 @@ import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Lists;
import com.google.common.collect.MapDifference;
import com.google.common.collect.MapMaker;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@ -35,6 +36,7 @@ import com.google.common.collect.Sets;
import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket;
import cpw.mods.fml.common.network.EntitySpawnPacket;
import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
import cpw.mods.fml.common.registry.ItemData;
import cpw.mods.fml.common.registry.TickRegistry;
import cpw.mods.fml.server.FMLServerHandler;
@ -427,4 +429,14 @@ public class FMLCommonHandler
}
}
}
public boolean shouldServerBeKilledQuietly()
{
return sidedDelegate.shouldServerShouldBeKilledQuietly();
}
public void disconnectIDMismatch(MapDifference<Integer, ItemData> serverDifference, NetHandler toKill, INetworkManager network)
{
sidedDelegate.disconnectIDMismatch(serverDifference, toKill, network);
}
}

View File

@ -25,6 +25,9 @@ import com.google.common.collect.MapMaker;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.ItemData;
/**
* @author cpw
*
@ -70,6 +73,9 @@ public class FMLDummyContainer extends DummyModContainer implements WorldAccessC
list.func_74742_a(mod);
}
fmlData.func_74782_a("ModList", list);
NBTTagList itemList = new NBTTagList();
GameRegistry.writeItemData(itemList);
fmlData.func_74782_a("ModItemData", itemList);
return fmlData;
}
@ -96,5 +102,16 @@ public class FMLDummyContainer extends DummyModContainer implements WorldAccessC
}
}
}
if (tag.func_74764_b("ModItemData"))
{
NBTTagList modList = tag.func_74761_m("ModItemData");
Set<ItemData> worldSaveItems = GameRegistry.buildWorldItemData(modList);
GameRegistry.validateWorldSave(worldSaveItems);
}
else
{
GameRegistry.validateWorldSave(null);
}
}
}

View File

@ -17,6 +17,13 @@ import java.util.Random;
import net.minecraft.shared.*;
/**
* Deprecated without replacement, use vanilla DispenserRegistry code
*
* @author cpw
*
*/
@Deprecated
public interface IDispenseHandler
{
/**

View File

@ -5,9 +5,13 @@ import java.util.Random;
import net.minecraft.shared.*;
/**
*
* Deprecated without replacement. Use vanilla DispenserRegistry code.
*
* @author cpw
*
*/
@Deprecated
public interface IDispenserHandler
{
/**

View File

@ -2,11 +2,14 @@ package cpw.mods.fml.common;
import java.util.List;
import com.google.common.collect.MapDifference;
import net.minecraft.server.MinecraftServer;
import net.minecraft.shared.*;
import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket;
import cpw.mods.fml.common.network.EntitySpawnPacket;
import cpw.mods.fml.common.network.ModMissingPacket;
import cpw.mods.fml.common.registry.ItemData;
import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
public interface IFMLSidedHandler
@ -38,4 +41,8 @@ public interface IFMLSidedHandler
void setClientCompatibilityLevel(byte compatibilityLevel);
byte getClientCompatibilityLevel();
boolean shouldServerShouldBeKilledQuietly();
void disconnectIDMismatch(MapDifference<Integer, ItemData> s, NetHandler toKill, INetworkManager mgr);
}

View File

@ -721,7 +721,7 @@ public class Loader
public ModContainer activeModContainer()
{
return modController.activeContainer();
return modController != null ? modController.activeContainer() : null;
}
public boolean isInState(LoaderState state)
@ -735,7 +735,7 @@ public class Loader
}
public boolean hasReachedState(LoaderState state) {
return modController.hasReachedState(state);
return modController != null ? modController.hasReachedState(state) : false;
}
public String getMCPVersionString() {

View File

@ -31,7 +31,7 @@ import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
public class FMLNetworkHandler
{
private static final int FML_HASH = Hashing.murmur3_32().hashString("FML").asInt();
private static final int PROTOCOL_VERSION = 0x1;
private static final int PROTOCOL_VERSION = 0x2;
private static final FMLNetworkHandler INSTANCE = new FMLNetworkHandler();
// List of states for connections from clients to server
@ -70,7 +70,12 @@ public class FMLNetworkHandler
private void handleFMLPacket(Packet250CustomPayload packet, INetworkManager network, NetHandler netHandler)
{
FMLPacket pkt = FMLPacket.readPacket(packet.field_73629_c);
FMLPacket pkt = FMLPacket.readPacket(network, packet.field_73629_c);
// Part of an incomplete multipart packet
if (pkt == null)
{
return;
}
String userName = "";
if (netHandler instanceof NetLoginHandler)
{

View File

@ -1,12 +1,15 @@
package cpw.mods.fml.common.network;
import java.util.Arrays;
import java.util.concurrent.ConcurrentMap;
import java.util.logging.Level;
import net.minecraft.shared.*;
import com.google.common.base.Throwables;
import com.google.common.collect.MapMaker;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.Ints;
import com.google.common.primitives.UnsignedBytes;
import cpw.mods.fml.common.FMLLog;
@ -18,38 +21,45 @@ public abstract class FMLPacket
/**
* Opening salutation from the server to the client -> request all mods from the client
*/
MOD_LIST_REQUEST(ModListRequestPacket.class),
MOD_LIST_REQUEST(ModListRequestPacket.class, false),
/**
* The client responds with the list of mods and versions it has. This is verified by the server.
*/
MOD_LIST_RESPONSE(ModListResponsePacket.class),
MOD_LIST_RESPONSE(ModListResponsePacket.class, false),
/**
* At which point the server tells the client the mod identifiers for this session.
*/
MOD_IDENTIFIERS(ModIdentifiersPacket.class),
MOD_IDENTIFIERS(ModIdentifiersPacket.class, false),
/**
* Or, if there is missing stuff, the server tells the client what's missing and drops the connection.
*/
MOD_MISSING(ModMissingPacket.class),
MOD_MISSING(ModMissingPacket.class, false),
/**
* Open a GUI on the client from the server
*/
GUIOPEN(OpenGuiPacket.class),
GUIOPEN(OpenGuiPacket.class, false),
/**
* Spawn an entity on the client from the server
*/
ENTITYSPAWN(EntitySpawnPacket.class),
ENTITYSPAWN(EntitySpawnPacket.class, false),
/**
* Fixes entity location data after spawning
*/
ENTITYSPAWNADJUSTMENT(EntitySpawnAdjustmentPacket.class);
ENTITYSPAWNADJUSTMENT(EntitySpawnAdjustmentPacket.class, false),
/**
* The ID map to send to the client
*/
MOD_IDMAP(ModIdMapPacket.class, true);
private Class<? extends FMLPacket> packetType;
private boolean isMultipart;
private ConcurrentMap<INetworkManager, FMLPacket> partTracker;
private Type(Class<? extends FMLPacket> clazz)
private Type(Class<? extends FMLPacket> clazz, boolean isMultipart)
{
this.packetType = clazz;
this.isMultipart = isMultipart;
}
FMLPacket make()
@ -65,20 +75,64 @@ public abstract class FMLPacket
throw new FMLNetworkException(e);
}
}
public boolean isMultipart()
{
return isMultipart;
}
private FMLPacket findCurrentPart(INetworkManager network)
{
if (partTracker == null)
{
partTracker = new MapMaker().weakKeys().weakValues().makeMap();
}
if (!partTracker.containsKey(network))
{
partTracker.put(network, make());
}
return partTracker.get(network);
}
}
private Type type;
public static byte[][] makePacketSet(Type type, Object... data)
{
if (!type.isMultipart())
{
return new byte[0][];
}
byte[] packetData = type.make().generatePacket(data);
byte[][] chunks = new byte[packetData.length / 32000 + 1][];
for (int i = 0; i < packetData.length / 32000 + 1; i++)
{
int len = Math.min(32000, packetData.length - i* 32000);
chunks[i] = Bytes.concat(new byte[] { UnsignedBytes.checkedCast(type.ordinal()), UnsignedBytes.checkedCast(i), UnsignedBytes.checkedCast(chunks.length)}, Ints.toByteArray(len), Arrays.copyOfRange(packetData, i * 32000, len));
}
return chunks;
}
public static byte[] makePacket(Type type, Object... data)
{
byte[] packetData = type.make().generatePacket(data);
return Bytes.concat(new byte[] { UnsignedBytes.checkedCast(type.ordinal()) }, packetData );
}
public static FMLPacket readPacket(byte[] payload)
public static FMLPacket readPacket(INetworkManager network, byte[] payload)
{
int type = UnsignedBytes.toInt(payload[0]);
return Type.values()[type].make().consumePacket(Arrays.copyOfRange(payload, 1, payload.length));
Type eType = Type.values()[type];
FMLPacket pkt;
if (eType.isMultipart())
{
pkt = eType.findCurrentPart(network);
}
else
{
pkt = eType.make();
}
return pkt.consumePacket(Arrays.copyOfRange(payload, 1, payload.length));
}
public FMLPacket(Type type)
@ -91,8 +145,4 @@ public abstract class FMLPacket
public abstract FMLPacket consumePacket(byte[] data);
public abstract void execute(INetworkManager network, FMLNetworkHandler handler, NetHandler netHandler, String userName);
{
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,99 @@
package cpw.mods.fml.common.network;
import java.io.IOException;
import java.util.BitSet;
import java.util.Set;
import java.util.logging.Level;
import com.google.common.collect.MapDifference;
import com.google.common.io.ByteArrayDataInput;
import com.google.common.io.ByteStreams;
import com.google.common.primitives.Bytes;
import com.google.common.primitives.Ints;
import com.google.common.primitives.UnsignedBytes;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.ItemData;
import net.minecraft.src.CompressedStreamTools;
import net.minecraft.src.INetworkManager;
import net.minecraft.src.NBTTagCompound;
import net.minecraft.src.NBTTagList;
import net.minecraft.src.NetHandler;
import net.minecraft.src.WorldClient;
import static cpw.mods.fml.common.network.FMLPacket.Type.MOD_IDMAP;
public class ModIdMapPacket extends FMLPacket {
private byte[][] partials;
public ModIdMapPacket()
{
super(MOD_IDMAP);
}
@Override
public byte[] generatePacket(Object... data)
{
NBTTagList completeList = (NBTTagList) data[0];
NBTTagCompound wrap = new NBTTagCompound();
wrap.func_74782_a("List", completeList);
try
{
return CompressedStreamTools.func_74798_a(wrap);
}
catch (Exception e)
{
FMLLog.log(Level.SEVERE, e, "A critical error writing the id map");
throw new FMLNetworkException(e);
}
}
@Override
public FMLPacket consumePacket(byte[] data)
{
ByteArrayDataInput bdi = ByteStreams.newDataInput(data);
int chunkIdx = UnsignedBytes.toInt(bdi.readByte());
int chunkTotal = UnsignedBytes.toInt(bdi.readByte());
int chunkLength = bdi.readInt();
if (partials == null)
{
partials = new byte[chunkTotal][];
}
partials[chunkIdx] = new byte[chunkLength];
bdi.readFully(partials[chunkIdx]);
for (int i = 0; i < partials.length; i++)
{
if (partials[i] == null)
{
return null;
}
}
return this;
}
@Override
public void execute(INetworkManager network, FMLNetworkHandler handler, NetHandler netHandler, String userName)
{
byte[] allData = Bytes.concat(partials);
GameRegistry.initializeServerGate(1);
try
{
NBTTagCompound serverList = CompressedStreamTools.func_74792_a(allData);
NBTTagList list = serverList.func_74761_m("List");
Set<ItemData> itemData = GameRegistry.buildWorldItemData(list);
GameRegistry.validateWorldSave(itemData);
MapDifference<Integer, ItemData> serverDifference = GameRegistry.gateWorldLoadingForValidation();
if (serverDifference!=null)
{
FMLCommonHandler.instance().disconnectIDMismatch(serverDifference, netHandler, network);
}
}
catch (IOException e)
{
}
}
}

View File

@ -3,6 +3,7 @@ package cpw.mods.fml.common.network;
import static cpw.mods.fml.common.network.FMLPacket.Type.MOD_IDENTIFIERS;
import static cpw.mods.fml.common.network.FMLPacket.Type.MOD_LIST_RESPONSE;
import static cpw.mods.fml.common.network.FMLPacket.Type.MOD_MISSING;
import static cpw.mods.fml.common.network.FMLPacket.Type.MOD_IDMAP;
import java.util.List;
import java.util.Map;
@ -20,6 +21,7 @@ import com.google.common.io.ByteStreams;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.ModContainer;
import cpw.mods.fml.common.registry.GameRegistry;
public class ModListResponsePacket extends FMLPacket
{
@ -110,16 +112,25 @@ public class ModListResponsePacket extends FMLPacket
FMLLog.info("User %s connection failed: missing %s, bad versions %s", userName, missingClientMods, versionIncorrectMods);
// Mark this as bad
FMLNetworkHandler.setHandlerState((NetLoginHandler) netHandler, FMLNetworkHandler.MISSING_MODS_OR_VERSIONS);
pkt.field_73628_b = pkt.field_73629_c.length;
network.func_74429_a(pkt);
}
else
{
pkt.field_73629_c = FMLPacket.makePacket(MOD_IDENTIFIERS, netHandler);
Logger.getLogger("Minecraft").info(String.format("User %s connecting with mods %s", userName, modVersions.keySet()));
FMLLog.info("User %s connecting with mods %s", userName, modVersions.keySet());
pkt.field_73628_b = pkt.field_73629_c.length;
network.func_74429_a(pkt);
NBTTagList itemList = new NBTTagList();
GameRegistry.writeItemData(itemList);
byte[][] registryPackets = FMLPacket.makePacketSet(MOD_IDMAP, itemList);
for (int i = 0; i < registryPackets.length; i++)
{
network.func_74429_a(PacketDispatcher.getPacket("FML", registryPackets[i]));
}
}
pkt.field_73628_b = pkt.field_73629_c.length;
network.func_74429_a(pkt);
// reset the continuation flag - we have completed extra negotiation and the login should complete now
NetLoginHandler.func_72531_a((NetLoginHandler) netHandler, true);
}

View File

@ -1,8 +1,10 @@
package cpw.mods.fml.common.registry;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import net.minecraft.shared.BiomeGenBase;
@ -19,10 +21,15 @@ import net.minecraft.shared.TileEntity;
import net.minecraft.shared.World;
import net.minecraft.shared.WorldType;
import com.google.common.base.Function;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.MapDifference;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.common.collect.Sets.SetView;
import cpw.mods.fml.common.FMLLog;
import cpw.mods.fml.common.ICraftingHandler;
@ -41,11 +48,10 @@ import cpw.mods.fml.common.ModContainer;
public class GameRegistry
{
private static Multimap<ModContainer, BlockProxy> blockRegistry = ArrayListMultimap.create();
private static Multimap<ModContainer, ItemProxy> itemRegistry = ArrayListMultimap.create();
private static Set<ItemData> itemRegistry = Sets.newHashSet();
private static Set<IWorldGenerator> worldGenerators = Sets.newHashSet();
private static List<IFuelHandler> fuelHandlers = Lists.newArrayList();
private static List<ICraftingHandler> craftingHandlers = Lists.newArrayList();
private static List<IDispenserHandler> dispenserHandlers = Lists.newArrayList();
private static List<IPickupNotifier> pickupHandlers = Lists.newArrayList();
private static List<IPlayerTracker> playerTrackers = Lists.newArrayList();
@ -83,33 +89,29 @@ public class GameRegistry
}
}
/**
* Deprecated without replacement. Use vanilla DispenserRegistry code
*
* @param handler
*/
@Deprecated
public static void registerDispenserHandler(IDispenserHandler handler)
{
dispenserHandlers.add(handler);
}
/**
* Register a handler for dispensers
* Deprecated without replacement. Use vanilla DispenserRegistry code
*
* @param handler
*/
@Deprecated
public static void registerDispenserHandler(final IDispenseHandler handler)
{
registerDispenserHandler(new IDispenserHandler()
{
@Override
public int dispense(int x, int y, int z, int xVelocity, int zVelocity, World world, ItemStack item, Random random, double entX, double entY, double entZ)
{
return handler.dispense(x, y, z, xVelocity, zVelocity, world, item, random, entX, entY, entZ);
}
});
}
/**
* Callback hook for dispenser activities - if you add a block and want mods to be able
* to extend their dispenser related activities to it call this
*
* Deprecated without replacement, use vanilla DispenserRegistry code
*
* @param world
* @param x
@ -119,16 +121,9 @@ public class GameRegistry
* @param zVelocity
* @param item
*/
@Deprecated
public static int tryDispense(World world, int x, int y, int z, int xVelocity, int zVelocity, ItemStack item, Random random, double entX, double entY, double entZ)
{
for (IDispenserHandler handler : dispenserHandlers)
{
int dispensed = handler.dispense(x, y, z, xVelocity, zVelocity, world, item, random, entX, entY, entZ);
if (dispensed>-1)
{
return dispensed;
}
}
return -1;
}
/**
@ -302,4 +297,134 @@ public class GameRegistry
for(IPlayerTracker tracker : playerTrackers)
tracker.onPlayerRespawn(player);
}
public static void newItemAdded(Item item)
{
ModContainer mc = Loader.instance().activeModContainer();
if (mc == null)
{
mc = Loader.instance().getMinecraftModContainer();
if (Loader.instance().hasReachedState(LoaderState.AVAILABLE))
{
FMLLog.severe("It appears something has tried to allocate an Item outside of the initialization phase of Minecraft, this could be very bad for your network connectivity.");
}
}
String itemType = item.getClass().getName();
itemRegistry.add(new ItemData(item, mc));
System.out.printf("Adding item %s(%d) owned by %s\n", item.getClass().getName(), item.field_77779_bT, mc);
}
public static void validateWorldSave(Set<ItemData> worldSaveItems)
{
isSaveValid = true;
shouldContinue = true;
// allow ourselves to continue if there's no saved data
if (worldSaveItems == null)
{
serverValidationLatch.countDown();
try
{
clientValidationLatch.await();
}
catch (InterruptedException e)
{
}
return;
}
Function<? super ItemData, Integer> idMapFunction = new Function<ItemData, Integer>() {
public Integer apply(ItemData input) {
return input.itemId;
};
};
Map<Integer,ItemData> worldMap = Maps.uniqueIndex(worldSaveItems,idMapFunction);
Map<Integer,ItemData> gameMap = Maps.uniqueIndex(itemRegistry, idMapFunction);
difference = Maps.difference(worldMap, gameMap);
if (!difference.entriesDiffering().isEmpty() || !difference.entriesOnlyOnLeft().isEmpty())
{
isSaveValid = false;
serverValidationLatch.countDown();
}
else
{
isSaveValid = true;
serverValidationLatch.countDown();
}
try
{
clientValidationLatch.await();
if (!shouldContinue)
{
throw new RuntimeException("This server instance is going to stop abnormally because of a fatal ID mismatch");
}
}
catch (InterruptedException e)
{
}
}
public static void writeItemData(NBTTagList itemList)
{
for (ItemData dat : itemRegistry)
{
itemList.func_74742_a(dat.toNBT());
}
}
/**
* Initialize the server gate
* @param gateCount the countdown amount. If it's 2 we're on the client and the client and server
* will wait at the latch. 1 is a server and the server will proceed
*/
public static void initializeServerGate(int gateCount)
{
serverValidationLatch = new CountDownLatch(gateCount - 1);
clientValidationLatch = new CountDownLatch(gateCount - 1);
}
public static MapDifference<Integer, ItemData> gateWorldLoadingForValidation()
{
try
{
serverValidationLatch.await();
if (!isSaveValid)
{
return difference;
}
}
catch (InterruptedException e)
{
}
difference = null;
return null;
}
public static void releaseGate(boolean carryOn)
{
shouldContinue = carryOn;
clientValidationLatch.countDown();
}
public static Set<ItemData> buildWorldItemData(NBTTagList modList)
{
Set<ItemData> worldSaveItems = Sets.newHashSet();
for (int i = 0; i < modList.func_74745_c(); i++)
{
NBTTagCompound mod = (NBTTagCompound) modList.func_74743_b(i);
ItemData dat = new ItemData(mod);
worldSaveItems.add(dat);
}
return worldSaveItems;
}
private static CountDownLatch serverValidationLatch;
private static CountDownLatch clientValidationLatch;
private static MapDifference<Integer, ItemData> difference;
private static boolean shouldContinue = true;
private static boolean isSaveValid = true;
}

View File

@ -0,0 +1,72 @@
package cpw.mods.fml.common.registry;
import java.util.Map;
import com.google.common.base.Objects;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Maps;
import com.google.common.collect.Multiset;
import cpw.mods.fml.common.ModContainer;
import net.minecraft.src.Item;
import net.minecraft.src.NBTTagCompound;
public class ItemData {
private static Map<String, Multiset<String>> modOrdinals = Maps.newHashMap();
public final String modId;
public final String itemType;
public final int itemId;
public final int ordinal;
public ItemData(Item item, ModContainer mc)
{
this.itemId = item.field_77779_bT;
this.itemType = item.getClass().getName();
this.modId = mc.getModId();
if (!modOrdinals.containsKey(mc.getModId()))
{
modOrdinals.put(mc.getModId(), HashMultiset.<String>create());
}
this.ordinal = modOrdinals.get(mc.getModId()).add(itemType, 1);
}
public ItemData(NBTTagCompound tag)
{
this.modId = tag.func_74779_i("ModId");
this.itemType = tag.func_74779_i("ItemType");
this.itemId = tag.func_74762_e("ItemId");
this.ordinal = tag.func_74762_e("ordinal");
}
public NBTTagCompound toNBT()
{
NBTTagCompound tag = new NBTTagCompound();
tag.func_74778_a("ModId", modId);
tag.func_74778_a("ItemType", itemType);
tag.func_74768_a("ItemId", itemId);
tag.func_74768_a("ordinal", ordinal);
return tag;
}
@Override
public int hashCode()
{
return Objects.hashCode(modId, itemType, itemId, ordinal);
}
@Override
public boolean equals(Object obj)
{
try
{
ItemData other = (ItemData) obj;
return Objects.equal(modId, other.modId) && Objects.equal(itemType, other.itemType) && Objects.equal(itemId, other.itemId) && Objects.equal(ordinal, other.ordinal);
}
catch (ClassCastException cce)
{
return false;
}
}
}

View File

@ -15,6 +15,7 @@ package cpw.mods.fml.server;
import java.util.List;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.MapDifference;
import net.minecraft.server.MinecraftServer;
import net.minecraft.shared.Entity;
@ -31,6 +32,8 @@ import cpw.mods.fml.common.network.EntitySpawnAdjustmentPacket;
import cpw.mods.fml.common.network.EntitySpawnPacket;
import cpw.mods.fml.common.network.ModMissingPacket;
import cpw.mods.fml.common.registry.EntityRegistry.EntityRegistration;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.ItemData;
import cpw.mods.fml.common.registry.LanguageRegistry;
/**
@ -88,6 +91,7 @@ public class FMLServerHandler implements IFMLSidedHandler
{
Loader.instance().initializeMods();
LanguageRegistry.reloadLanguageTable();
GameRegistry.initializeServerGate(1);
}
@Override
@ -173,4 +177,15 @@ public class FMLServerHandler implements IFMLSidedHandler
{
return 0;
}
@Override
public boolean shouldServerShouldBeKilledQuietly()
{
return false;
}
@Override
public void disconnectIDMismatch(MapDifference<Integer, ItemData> s, NetHandler handler, INetworkManager mgr)
{
}
}

View File

@ -69,3 +69,5 @@ public qu.bN #FD:EntityVillager/field_70958_bB
public qu.bO #FD:EntityVillager/field_70960_bC
# GuiButtonMerchant
public avu #CL:GuiButtonMerchant
protected asy.a #FD:GuiYesNo/field_73942_a #guiScreen

View File

@ -29,7 +29,29 @@
}
else
{
@@ -484,8 +491,10 @@
@@ -429,6 +436,10 @@
}
catch (Throwable var48)
{
+ if (FMLCommonHandler.instance().shouldServerBeKilledQuietly())
+ {
+ return;
+ }
var48.printStackTrace();
field_71306_a.log(Level.SEVERE, "Encountered an unexpected exception " + var48.getClass().getSimpleName(), var48);
CrashReport var2 = null;
@@ -459,6 +470,10 @@
{
try
{
+ if (FMLCommonHandler.instance().shouldServerBeKilledQuietly())
+ {
+ return;
+ }
this.func_71260_j();
this.field_71316_v = true;
}
@@ -484,8 +499,10 @@
public void func_71217_p()
{
@ -40,7 +62,7 @@
++this.field_71315_w;
if (this.field_71295_T)
@@ -531,6 +540,7 @@
@@ -531,6 +548,7 @@
this.field_71304_b.func_76319_b();
this.field_71304_b.func_76319_b();
@ -48,7 +70,7 @@
}
public void func_71190_q()
@@ -558,6 +568,7 @@
@@ -558,6 +576,7 @@
}
this.field_71304_b.func_76320_a("tick");
@ -56,7 +78,7 @@
CrashReport var6;
try
@@ -582,6 +593,7 @@
@@ -582,6 +601,7 @@
throw new ReportedException(var6);
}
@ -64,7 +86,7 @@
this.field_71304_b.func_76319_b();
this.field_71304_b.func_76320_a("tracker");
var4.func_73039_n().func_72788_a();
@@ -1150,6 +1162,12 @@
@@ -1150,6 +1170,12 @@
@SideOnly(Side.SERVER)
public static void main(String[] p_main_0_)
{

View File

@ -0,0 +1,19 @@
--- ../src-base/common/net/minecraft/src/Item.java
+++ ../src-work/common/net/minecraft/src/Item.java
@@ -2,6 +2,8 @@
import cpw.mods.fml.common.Side;
import cpw.mods.fml.common.asm.SideOnly;
+import cpw.mods.fml.common.registry.GameRegistry;
+
import java.util.List;
import java.util.Random;
@@ -187,6 +189,7 @@
}
field_77698_e[256 + p_i3659_1_] = this;
+ GameRegistry.newItemAdded(this);
}
public Item func_77665_c(int p_77665_1_)

View File

@ -9,7 +9,7 @@
public class SaveHandler implements ISaveHandler, IPlayerFileData
{
@@ -97,14 +99,16 @@
@@ -97,17 +99,23 @@
File var1 = new File(this.field_75770_b, "level.dat");
NBTTagCompound var2;
NBTTagCompound var3;
@ -28,7 +28,14 @@
}
catch (Exception var5)
{
@@ -120,7 +124,9 @@
+ if (FMLCommonHandler.instance().shouldServerBeKilledQuietly())
+ {
+ throw (RuntimeException)var5;
+ }
var5.printStackTrace();
}
}
@@ -120,7 +128,9 @@
{
var2 = CompressedStreamTools.func_74796_a(new FileInputStream(var1));
var3 = var2.func_74775_l("Data");
@ -39,7 +46,7 @@
}
catch (Exception var4)
{
@@ -136,7 +142,7 @@
@@ -136,7 +146,7 @@
NBTTagCompound var3 = p_75755_1_.func_76082_a(p_75755_2_);
NBTTagCompound var4 = new NBTTagCompound();
var4.func_74782_a("Data", var3);
@ -48,7 +55,7 @@
try
{
File var5 = new File(this.field_75770_b, "level.dat_new");
@@ -174,7 +180,7 @@
@@ -174,7 +184,7 @@
NBTTagCompound var2 = p_75761_1_.func_76066_a();
NBTTagCompound var3 = new NBTTagCompound();
var3.func_74782_a("Data", var2);

View File

@ -5,6 +5,8 @@
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.util.glu.GLU;
+
+import com.google.common.collect.MapDifference;
+
+import cpw.mods.fml.client.FMLClientHandler;
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.Side;
@ -73,7 +75,31 @@
this.field_71424_I.func_76319_b();
this.field_71423_H = func_71386_F();
}
@@ -1920,6 +1934,12 @@
@@ -1777,8 +1799,23 @@
}
this.field_71413_E.func_77450_a(StatList.field_75936_f, 1);
+ GameRegistry.initializeServerGate(2);
this.field_71437_Z = new IntegratedServer(this, p_71371_1_, p_71371_2_, p_71371_3_);
this.field_71437_Z.func_71256_s();
+ MapDifference<Integer, ItemData> idDifferences = GameRegistry.gateWorldLoadingForValidation();
+ if (idDifferences!=null)
+ {
+ FMLClientHandler.instance().warnIDMismatch(idDifferences, true);
+ }
+ else
+ {
+ GameRegistry.releaseGate(true);
+ continueWorldLoading();
+ }
+
+ }
+ public void continueWorldLoading()
+ {
this.field_71455_al = true;
this.field_71461_s.func_73720_a(StatCollector.func_74838_a("menu.loadingLevel"));
@@ -1993,6 +2030,12 @@
public static void main(String[] p_main_0_)
{

View File

@ -0,0 +1,13 @@
--- ../src-base/minecraft/net/minecraft/src/GuiConnecting.java
+++ ../src-work/minecraft/net/minecraft/src/GuiConnecting.java
@@ -122,4 +122,10 @@
{
return p_74251_0_.field_73882_e;
}
+
+ public static void forceTermination(GuiConnecting gui)
+ {
+ gui.field_74258_b = true;
+ gui.field_74259_a = null;
+ }
}