I have been experiencing a severe glitch with the inventory under seemingly random circumstances. It appears to be causing a desynchronization between the server and the client.
Here is what I am experiencing:
With one TileEntity (machine) in the world, the changes made in the inventory by a player (like moving things between slots) do not seem to be reflected in the server-side inventory. This manifests as situations like the following:
Player A has a hotbar full of the following, left to right: Dirt, dirt, cobble, wood, glass, empty, piston, redstone, torch. They go around and do stuff, and in the process they have rearranged their hotbar to look like the following (they have not interacted with a chest): cobble, cobble, stone, redstone, empty, empty, empty, torch, dirt.
They try to place the dirt block. Instead, a dirt block appears for a split second, and then a torch appears in that place instead. If they were to try to place the piston, what they would have seen is the piston appear for a second and then disappear, with the piston seemingly disappearing from their inventory.
When the world is reloaded, the GUI is totally rearranged to reflect what the server had in mind all along, the original hotbar.
That is, it seems like the client and server are disagreeing on player-GUI-made changes to the location of items in the player inventory, and since the server controls the placing of blocks, it always "wins out", causing it to look like the inventory is scrambling itself or placing different things than are intended.
Similarly, trying to toss items from an inventory can cause them to just disappear without spawning the item entities. When reloading the world, they reappear in the inventory, as if the "drop event" was never processed server-side.
Also worth noting is that block breaking exhibits a related glitch - blocks can be broken and their items picked up (making the popping noise), but they do not appear in the inventory, as the client is not getting the update that says "item is in slot x now". Again, reloading resyncs them.
I must emphasize that this bug is NOT consistent - its behavior is, but it exhibits itself at random times and it was only with great effort that I was able to link it to being (probably) dependent on one TileEntity existing in the world.
The TE Code. Note that is has NO functions for editing inventories or player data.
@Override
public void updateEntity(World world, int x, int y, int z, int meta) {
super.updateTileEntity();
tickcount++;
this.getIOSides(world, x, y, z, meta);
this.getPower(false, false);
power = omega*torque;
boolean nodig = true;
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 5; j++) {
if (cutShape[i][j]) {
nodig = false;
i = j = 7;
}
}
}
if (nodig)
return;
//ModLoader.getMinecraftInstance().thePlayer.addChatMessage(String.valueOf(cutShape[0][0]));
if (omega <= 0)
return;
if (this.operationComplete(tickcount, 0)) {
this.calcReqPower(world, x, y, z, meta);
//ModLoader.getMinecraftInstance().ingameGUI.addChatMessage(String.format("%d", this.reqpow));
if (power > reqpow && reqpow != -1)
this.dig(world, x, y, z, meta);
tickcount = 0;
if (reqpow == -1)
step = ReikaMathLibrary.extrema(step-1, 1, "max");
}
}
public void reqPowAdd(World world, int xread, int yread, int zread, int metadata) {
if (world.getBlockId(xread, yread, zread) != 0) {
reqpow += (int)(DIGPOWER*10*Block.blocksList[world.getBlockId(xread, yread, zread)].getBlockHardness(world, xread, yread, zread));
if (ReikaMathLibrary.ceil2exp((int)(16*10*Block.blocksList[world.getBlockId(xread, yread, zread)].getBlockHardness(world, xread, yread, zread))) > mintorque)
mintorque = ReikaMathLibrary.ceil2exp((int)(16*10*Block.blocksList[world.getBlockId(xread, yread, zread)].getBlockHardness(world, xread, yread, zread)));
if (world.getBlockId(xread, yread, zread) == 7)
reqpow = -1;
}
}
public void calcReqPower(World world, int x, int y, int z, int metadata) {
reqpow = 0;
int mintorque = -1;
int a = 0;
if (metadata > 1)
a = 1;
int b = 1-a;
int xread;
int yread;
int zread;
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 5; j++) {
if (cutShape[i][j] || step == 1) {
xread = x+step*xstep+a*(i-3); yread = y+step*ystep+(4-j); zread = z+step*zstep+b*(i-3);
this.reqPowAdd(world, xread, yread, zread, metadata);
}
}
}
if (torque < mintorque)
reqpow = -1;
//ModLoader.getMinecraftInstance().ingameGUI.addChatMessage(String.format("%d", mintorque));
}
public void support(World world, int x, int y, int z, int metadata) {
int a = 0;
if (metadata > 1)
a = 1;
int b = 1-a;
int xread;
int yread;
int zread;
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 5; j++) {
if (cutShape[i][j] || step == 1) {
xread = x+step*xstep+a*(i-3); yread = y+step*ystep+(4-j); zread = z+step*zstep+b*(i-3);
int id = world.getBlockId(xread, yread+1, zread);
if (id == Block.sand.blockID || id == Block.gravel.blockID)
if (this.checkTop(i, j))
if (id == Block.sand.blockID)
ReikaWorldHelper.legacySetBlockWithNotify(world, xread, yread, zread, Block.sandStone.blockID);
else
ReikaWorldHelper.legacySetBlockWithNotify(world, xread, yread, zread, Block.stone.blockID);
}
}
}
}
private boolean checkTop(int i, int j) {
while (j > 0) {
j--;
if (cutShape[i][j])
return false;
}
return true;
}
public void dropBlocks(int xread, int yread, int zread, World world, int x, int y, int z, int id, int meta) {
if (drops && id != 0) {
if (id == Block.mobSpawner.blockID) {
TileEntityMobSpawner spw = (TileEntityMobSpawner)world.getBlockTileEntity(xread, yread, zread);
if (spw != null) {
if (world.isRemote)
return;
MobSpawnerBaseLogic lgc = spw.func_98049_a();
String mob = lgc.getEntityNameToSpawn();
//ModLoader.getMinecraftInstance().thePlayer.addChatMessage(String.format("%s", mob));
int dmg = -1;
if (mob == "Zombie")
dmg = 0;
if (mob == "Spider")
dmg = 1;
if (mob == "CaveSpider")
dmg = 2;
if (mob == "Skeleton")
dmg = 3;
if (mob == "Silverfish")
dmg = 4;
if (mob == "Blaze")
dmg = 5;
if (dmg == -1)
return;
ItemStack is = new ItemStack(mod_RotaryCraft.spawner.itemID, 1, dmg);
EntityItem ent = new EntityItem(world, x, y, z, is);
ent.delayBeforeCanPickup = 10;
world.spawnEntityInWorld(ent);
return;
}
}
Block.blocksList[id].dropBlockAsItem(world, x, y+1, z, world.getBlockMetadata(xread, yread, zread), 0);
}
}
public void dig(World world, int x, int y, int z, int metadata) {
this.support(world, x, y, z, metadata);
int a = 0;
if (metadata > 1)
a = 1;
int b = 1-a;
int xread;
int yread;
int zread;
if (step == 1) {
pipemeta2 = pipemeta;
pipemeta = 3;
}
else if (pipemeta > 2 && pipemeta2 != 3)
pipemeta = pipemeta2;
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 5; j++) {
if (cutShape[i][j] || step == 1) {
xread = x+step*xstep+a*(i-3); yread = y+step*ystep+(4-j); zread = z+step*zstep+b*(i-3);
this.dropBlocks(xread, yread, zread, world, x, y, z, world.getBlockId(xread, yread, zread), world.getBlockMetadata(xread, yread, zread));
ReikaWorldHelper.legacySetBlockAndMetadataWithNotify(world, xread, yread, zread, mod_RotaryCraft.miningpipe.blockID, pipemeta);
}
}
}
step++;
}
@Override
public void writeToNBT(NBTTagCompound NBT)
{
super.writeToNBT(NBT);
NBT.setInteger("mode", mode);
NBT.setBoolean("drops", drops);
NBT.setInteger("step", step);
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 5; j++)
NBT.setBoolean("cut"+String.valueOf(i*7+j), cutShape[i][j]);
}
}
/**
* Reads a tile entity from NBT.
*/
@Override
public void readFromNBT(NBTTagCompound NBT)
{
super.readFromNBT(NBT);
mode = NBT.getInteger("mode");
drops = NBT.getBoolean("drops");
step = NBT.getInteger("step");
for (int i = 0; i < 7; i++) {
for (int j = 0; j < 5; j++)
cutShape[i][j] = NBT.getBoolean("cut"+String.valueOf(i*7+j));
}
}