/*
* This file is part of SpoutcraftPlugin.
*
* Copyright (c) 2011 SpoutcraftDev <http://spoutcraft.org//>
* SpoutcraftPlugin is licensed under the GNU Lesser General Public License.
*
* SpoutcraftPlugin is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SpoutcraftPlugin is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.getspout.spout.inventory;
import java.io.File;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import gnu.trove.list.array.TByteArrayList;
import gnu.trove.list.array.TIntArrayList;
import gnu.trove.map.hash.TIntObjectHashMap;
import net.minecraft.server.v1_6_R3.Item;
import org.bukkit.Bukkit;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Recipe;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.getspout.spout.block.SpoutCraftBlock;
import org.getspout.spout.player.SpoutCraftPlayer;
import org.getspout.spoutapi.SpoutManager;
import org.getspout.spoutapi.block.SpoutChunk;
import org.getspout.spoutapi.inventory.ItemMap;
import org.getspout.spoutapi.inventory.MaterialManager;
import org.getspout.spoutapi.inventory.SpoutShapedRecipe;
import org.getspout.spoutapi.inventory.SpoutShapelessRecipe;
import org.getspout.spoutapi.io.store.FlatFileStore;
import org.getspout.spoutapi.material.CustomBlock;
import org.getspout.spoutapi.material.CustomItem;
import org.getspout.spoutapi.material.Material;
import org.getspout.spoutapi.material.MaterialData;
import org.getspout.spoutapi.packet.PacketCustomBlockChunkOverride;
import org.getspout.spoutapi.packet.PacketCustomBlockOverride;
import org.getspout.spoutapi.packet.PacketCustomMultiBlockOverride;
import org.getspout.spoutapi.packet.SpoutPacket;
import org.getspout.spoutapi.player.SpoutPlayer;
import org.getspout.spoutapi.util.map.TIntPairObjectHashMap;
public class SimpleMaterialManager extends AbstractBlockManager implements MaterialManager {
private final TIntObjectHashMap<String> itemPlugin = new TIntObjectHashMap<String>();
private final HashMap<World, TIntPairObjectHashMap<BlockOverrides>> queuedChunkBlockOverrides = new HashMap<World, TIntPairObjectHashMap<BlockOverrides>>(10);
public static void disableFlintStackMix() {
try {
Method a = Item.class.getDeclaredMethod("a", new Class[] { boolean.class });
a.setAccessible(true);
a.invoke(Item.byId[318], Boolean.TRUE);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
@Override
public void reset() {
super.reset();
}
@Override
public void onPlayerJoin(SpoutPlayer player) {
if (player.isSpoutCraftEnabled()) {
for (CustomBlock block : MaterialData.getCustomBlocks()) {
if (block instanceof SpoutPacket) {
player.sendPacket((SpoutPacket)block);
}
}
for (CustomItem item : MaterialData.getCustomItems()) {
CustomBlock owner = MaterialData.getCustomBlock(item.getCustomId());
if (item instanceof SpoutPacket && owner == null) {
player.sendPacket((SpoutPacket)item);
}
}
}
super.onPlayerJoin(player);
}
@Override
public int registerCustomItemName(Plugin plugin, String key) {
int id = ItemMap.getRootMap().register(key);
itemPlugin.put(id, plugin.getDescription().getName());
return id;
}
@Override
public boolean removeBlockOverride(Block block) {
SpoutCraftBlock scb = (SpoutCraftBlock) block;
if (scb.isCustomBlock()) {
scb.getCustomBlock().onBlockDestroyed(scb.getWorld(), scb.getX(), scb.getY(), scb.getZ());
}
scb.removeCustomBlockData();
queueBlockOverrides(scb, null, (byte) 0);
return true;
}
@Override
public boolean overrideBlock(Block block, CustomBlock customBlock) {
return overrideBlock(block, customBlock, (byte) 0);
}
@Override
public boolean overrideBlock(Block block, CustomBlock customBlock, byte data) {
block.setTypeId(customBlock.getBlockId());
int blockId = customBlock.getCustomId();
SpoutCraftBlock scb = (SpoutCraftBlock) block;
customBlock.onBlockPlace(scb.getWorld(), scb.getX(), scb.getY(), scb.getZ());
scb.setCustomBlockId(blockId);
scb.setCustomBlockData(data);
queueBlockOverrides(scb, blockId, data);
return true;
}
@Override
public boolean overrideBlock(World world, int x, int y, int z, CustomBlock customBlock) {
return overrideBlock(world, x, y, z, customBlock, (byte) 0);
}
@Override
public boolean overrideBlock(World world, int x, int y, int z, CustomBlock customBlock, byte data) {
int blockId = customBlock.getCustomId();
SpoutManager.getChunkDataManager().setBlockData(blockIdString, world, x, y, z, blockId);
((SpoutChunk) world.getChunkAt(x<<4, z<<4)).setCustomBlockData(x, y, z, data);
queueBlockOverrides(world, x, y, z, blockId, data);
return true;
}
public void queueBlockOverrides(SpoutCraftBlock block, Integer blockId, byte data) {
if (block != null) {
queueBlockOverrides(block.getWorld(), block.getX(), block.getY(), block.getZ(), blockId, data);
}
}
public void queueBlockOverrides(World world, int x, int y, int z, Integer blockId, byte data) {
if (world != null) {
TIntPairObjectHashMap<BlockOverrides> chunkOverrides = queuedChunkBlockOverrides.get(world);
if (chunkOverrides == null) {
chunkOverrides = new TIntPairObjectHashMap<BlockOverrides>(100);
queuedChunkBlockOverrides.put(world, chunkOverrides);
}
BlockOverrides overrides = chunkOverrides.get(x >> 4, z >> 4);
if (overrides == null) {
overrides = new BlockOverrides(world);
chunkOverrides.put(x >> 4, z >> 4, overrides);
}
overrides.putOverride(x, y, z, blockId != null ? blockId.intValue() : -1, data);
}
}
@Override
public boolean registerSpoutRecipe(Recipe recipe) {
SpoutRecipe toAdd;
if (recipe instanceof SpoutRecipe) {
toAdd = (SpoutRecipe) recipe;
} else {
if (recipe instanceof SpoutShapedRecipe) {
toAdd = SimpleSpoutShapedRecipe.fromSpoutRecipe((SpoutShapedRecipe) recipe);
} else if (recipe instanceof SpoutShapelessRecipe) {
toAdd = SimpleSpoutShapelessRecipe.fromSpoutRecipe((SpoutShapelessRecipe) recipe);
} else {
return false;
}
}
toAdd.addToCraftingManager();
return true;
}
public void onTick() {
for (World world : Bukkit.getServer().getWorlds()) {
TIntPairObjectHashMap<BlockOverrides> chunkOverrides = queuedChunkBlockOverrides.get(world);
if (chunkOverrides != null && chunkOverrides.size() > 0 && world.getPlayers().size() > 0) {
//long time = System.nanoTime();
for (BlockOverrides override : chunkOverrides.valueCollection()) {
override.sendPacket();
}
chunkOverrides.clear();
//System.out.println("Sending block overrides took " + (System.nanoTime() - time) * 1E-6D + " ms");
}
}
}
private boolean glassUpdated = false;
// Fired when MaterialData.addCustomItem or MaterialData.addCustomBlock is called
public void onCustomMaterialRegistered(Material mat) {
if (mat instanceof CustomBlock && !glassUpdated) {
if (!((CustomBlock)mat).isOpaque()) {
org.getspout.spout.block.mcblock.CustomMCBlock.updateGlass();
glassUpdated = true;
}
}
}
private class BlockOverrides {
private World world;
private TIntArrayList xCoords = new TIntArrayList();
private TIntArrayList yCoords = new TIntArrayList();
private TIntArrayList zCoords = new TIntArrayList();
private TIntArrayList typeIds = new TIntArrayList();
private TByteArrayList data = new TByteArrayList();
BlockOverrides(World world) {
this.world = world;
}
protected void putOverride(int x, int y, int z, int id, byte data) {
xCoords.add(x);
yCoords.add(y);
zCoords.add(z);
typeIds.add(id);
this.data.add(data);
}
protected void sendPacket() {
List<Player> players = world.getPlayers();
if (xCoords.size() > 6) {
SpoutPacket packet;
if (xCoords.size() > 128) {
int chunkX = xCoords.get(0) >> 4;
int chunkZ = zCoords.get(0) >> 4;
packet = new PacketCustomBlockChunkOverride(SpoutManager.getChunkDataManager().getCustomBlockIds(world, chunkX, chunkZ), SpoutManager.getChunkDataManager().getCustomBlockData(world, chunkX, chunkZ),chunkX, chunkZ);
} else {
packet = new PacketCustomMultiBlockOverride(xCoords, yCoords, zCoords, typeIds, data);
}
for (Player player : players) {
if (player instanceof SpoutCraftPlayer) {
SpoutCraftPlayer spc = (SpoutCraftPlayer) player;
if (spc.isSpoutCraftEnabled()) {
spc.sendPacket(packet);
}
}
}
} else {
for (int i = 0; i < xCoords.size(); i++) {
SpoutPacket packet = new PacketCustomBlockOverride(xCoords.get(i), yCoords.get(i), zCoords.get(i), typeIds.get(i), data.get(i));
for (Player player : players) {
if (player instanceof SpoutCraftPlayer) {
SpoutCraftPlayer spc = (SpoutCraftPlayer) player;
if (spc.isSpoutCraftEnabled()) {
spc.sendPacket(packet);
}
}
}
}
}
}
}
@Override
public void renameMaterialKey(JavaPlugin plugin, String oldKey, String newKey) {
String fullOldKey = plugin.getDescription().getName() + "." + oldKey;
String fullNewKey = plugin.getDescription().getName() + "." + newKey;
ItemMap.getRootMap().rename(fullOldKey, fullNewKey);
for (File worldFolder : Bukkit.getWorldContainer().listFiles()) {
if ((new File(worldFolder, "spout_meta/worldItemMap.txt")).exists()) {
World world = Bukkit.getWorld(worldFolder.getName());
if (world != null) {
ItemMap worldItemMap = SpoutManager.getChunkDataManager().getItemMap(world);
if (worldItemMap != null) {
worldItemMap.rename(fullOldKey, fullNewKey);
continue;
}
}
FlatFileStore<Integer> fs = new FlatFileStore<Integer>(new File(worldFolder, "spout_meta/worldItemMap.txt"), Integer.class);
fs.load();
ItemMap worldItemMap = new ItemMap(ItemMap.getRootMap(), fs, null);
worldItemMap.rename(fullOldKey, fullNewKey);
}
}
}
}