PAr fixes

This commit is contained in:
Daniel Mills 2020-05-13 21:32:13 -04:00
parent 1b2ce750ca
commit 14e63bd47d
36 changed files with 4253 additions and 71 deletions

View File

@ -1,25 +1,32 @@
package ninja.bytecode.iris;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.event.HandlerList;
import org.bukkit.generator.ChunkGenerator;
import org.bukkit.inventory.ItemStack;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import ninja.bytecode.iris.generator.IrisGenerator;
import ninja.bytecode.iris.object.IrisBiome;
import ninja.bytecode.iris.object.IrisDimension;
import ninja.bytecode.iris.object.IrisObject;
import ninja.bytecode.iris.util.BiomeResult;
import ninja.bytecode.iris.util.BoardManager;
import ninja.bytecode.iris.util.BoardProvider;
@ -28,6 +35,7 @@ import ninja.bytecode.iris.util.CNG;
import ninja.bytecode.iris.util.GroupedExecutor;
import ninja.bytecode.iris.util.IO;
import ninja.bytecode.iris.util.ScoreDirection;
import ninja.bytecode.iris.wand.WandController;
import ninja.bytecode.shuriken.collections.KList;
import ninja.bytecode.shuriken.execution.J;
import ninja.bytecode.shuriken.format.Form;
@ -39,8 +47,9 @@ public class Iris extends JavaPlugin implements BoardProvider
public static KList<GroupedExecutor> executors = new KList<>();
public static Iris instance;
public static IrisDataManager data;
private static String last = "";
public static IrisHotloadManager hotloader;
public static WandController wand;
private static String last = "";
private BoardManager manager;
private RollingSequence hits = new RollingSequence(20);
@ -54,6 +63,7 @@ public class Iris extends JavaPlugin implements BoardProvider
instance = this;
hotloader = new IrisHotloadManager();
data = new IrisDataManager(getDataFolder());
wand = new WandController();
manager = new BoardManager(this, BoardSettings.builder().boardProvider(this).scoreDirection(ScoreDirection.UP).build());
}
@ -83,6 +93,9 @@ public class Iris extends JavaPlugin implements BoardProvider
lines.add(ChatColor.GREEN + "Loss" + ChatColor.GRAY + ": " + ChatColor.BOLD + "" + ChatColor.GRAY + Form.duration(g.getMetrics().getLoss().getAverage(), 4) + "");
lines.add(ChatColor.GREEN + "Generators" + ChatColor.GRAY + ": " + Form.f(CNG.creates));
lines.add(ChatColor.GREEN + "Noise" + ChatColor.GRAY + ": " + Form.f((int) hits.getAverage()));
lines.add(ChatColor.GREEN + "Parallax Regions" + ChatColor.GRAY + ": " + Form.f((int) g.getParallaxMap().getLoadedRegions().size()));
lines.add(ChatColor.GREEN + "Parallax Chunks" + ChatColor.GRAY + ": " + Form.f((int) g.getParallaxMap().getLoadedChunks().size()));
lines.add(ChatColor.GREEN + "Sliver Buffer" + ChatColor.GRAY + ": " + Form.f((int) g.getSliverBuffer()));
if(er != null && b != null)
{
@ -110,6 +123,8 @@ public class Iris extends JavaPlugin implements BoardProvider
executors.clear();
manager.onDisable();
Bukkit.getScheduler().cancelTasks(this);
HandlerList.unregisterAll((Plugin) this);
}
@Override
@ -120,10 +135,79 @@ public class Iris extends JavaPlugin implements BoardProvider
if(args.length == 0)
{
imsg(sender, "/iris dev - Create a new dev world");
imsg(sender, "/iris wand - Get a wand");
}
if(args.length >= 1)
{
if(args[0].equalsIgnoreCase("wand"))
{
((Player) sender).getInventory().addItem(WandController.createWand());
}
if(args[0].equalsIgnoreCase("save") && args.length >= 2)
{
ItemStack wand = ((Player) sender).getInventory().getItemInMainHand();
IrisObject o = WandController.createSchematic(wand);
try
{
o.write(new File(getDataFolder(), "objects/" + args[1] + ".iob"));
imsg(sender, "Saved " + "objects/" + args[1] + ".iob");
}
catch(IOException e)
{
imsg(sender, "Failed to save " + "objects/" + args[1] + ".iob");
e.printStackTrace();
}
}
if(args[0].equalsIgnoreCase("load") && args.length >= 2)
{
File file = new File(getDataFolder(), "objects/" + args[1] + ".iob");
boolean intoWand = false;
for(String i : args)
{
if(i.equalsIgnoreCase("-edit"))
{
intoWand = true;
}
}
if(!file.exists())
{
imsg(sender, "Can't find " + "objects/" + args[1] + ".iob");
}
ItemStack wand = ((Player) sender).getInventory().getItemInMainHand();
IrisObject o = new IrisObject(0, 0, 0);
try
{
o.read(new File(getDataFolder(), "objects/" + args[1] + ".iob"));
imsg(sender, "Loaded " + "objects/" + args[1] + ".iob");
Location block = ((Player) sender).getTargetBlock((Set<Material>) null, 256).getLocation().clone().add(0, 1, 0);
if(intoWand && WandController.isWand(wand))
{
wand = WandController.createWand(block.clone().subtract(o.getCenter()).add(o.getW() - 1, o.getH(), o.getD() - 1), block.clone().subtract(o.getCenter()));
((Player) sender).getInventory().setItemInMainHand(wand);
imsg(sender, "Updated wand for " + "objects/" + args[1] + ".iob");
}
WandController.pasteSchematic(o, block);
imsg(sender, "Placed " + "objects/" + args[1] + ".iob");
}
catch(IOException e)
{
imsg(sender, "Failed to load " + "objects/" + args[1] + ".iob");
e.printStackTrace();
}
}
if(args[0].equalsIgnoreCase("dev"))
{
String dim = "Overworld";

View File

@ -10,6 +10,7 @@ import ninja.bytecode.iris.object.IrisBiome;
import ninja.bytecode.iris.object.IrisDimension;
import ninja.bytecode.iris.object.IrisRegion;
import ninja.bytecode.iris.util.IO;
import ninja.bytecode.iris.util.ObjectResourceLoader;
import ninja.bytecode.iris.util.ResourceLoader;
@Data
@ -17,10 +18,10 @@ public class IrisDataManager
{
private File dataFolder;
private File packs;
private ResourceLoader<IrisBiome> biomeLoader;
private ResourceLoader<IrisRegion> regionLoader;
private ResourceLoader<IrisDimension> dimensionLoader;
private ObjectResourceLoader objectLoader;
public void hotloaded()
{
@ -28,6 +29,7 @@ public class IrisDataManager
this.regionLoader = new ResourceLoader<>(packs, "regions", "Region", IrisRegion.class);
this.biomeLoader = new ResourceLoader<>(packs, "biomes", "Biome", IrisBiome.class);
this.dimensionLoader = new ResourceLoader<>(packs, "dimensions", "Dimension", IrisDimension.class);
this.objectLoader = new ObjectResourceLoader(packs, "objects", "Object");
writeExamples();
}

View File

@ -250,11 +250,18 @@ public abstract class ContextualChunkGenerator extends ChunkGenerator implements
CNG.hits = 0;
Iris.instance.hit(hits);
metrics.getLoss().put(sx.getMilliseconds() - s.getMilliseconds());
return c;
}
catch(Throwable e)
{
fail(e);
}
return generateChunkDataFailure(world, no, x, z, biomeGrid);
}
protected void fail(Throwable e)
{
failing = true;
Iris.error("ERROR! Failed to generate chunk! Iris has entered a failed state!");
@ -270,9 +277,6 @@ public abstract class ContextualChunkGenerator extends ChunkGenerator implements
onFailure(e);
}
return generateChunkDataFailure(world, no, x, z, biomeGrid);
}
@Override
public List<BlockPopulator> getDefaultPopulators(World world)
{

View File

@ -5,6 +5,7 @@ import org.bukkit.entity.Player;
import lombok.Data;
import lombok.EqualsAndHashCode;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.IrisContext;
import ninja.bytecode.iris.object.IrisRegion;
import ninja.bytecode.iris.util.BiomeResult;
@ -39,13 +40,14 @@ public class IrisGenerator extends ParallaxChunkGenerator implements IrisContext
@Override
protected void onTick(int ticks)
{
super.onTick(ticks);
}
@Override
protected void onClose()
{
super.onClose();
Iris.info("Closing Iris Dimension " + getWorld().getName());
}
@Override

View File

@ -1,22 +1,172 @@
package ninja.bytecode.iris.generator;
import java.io.IOException;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import lombok.Data;
import lombok.EqualsAndHashCode;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.object.IrisBiome;
import ninja.bytecode.iris.object.IrisObjectPlacement;
import ninja.bytecode.iris.object.atomics.AtomicSliver;
import ninja.bytecode.iris.object.atomics.AtomicSliverMap;
import ninja.bytecode.iris.object.atomics.AtomicWorldData;
import ninja.bytecode.iris.util.BiomeMap;
import ninja.bytecode.iris.util.ChunkPosition;
import ninja.bytecode.iris.util.HeightMap;
import ninja.bytecode.iris.util.IObjectPlacer;
import ninja.bytecode.iris.util.RNG;
import ninja.bytecode.shuriken.collections.KMap;
@Data
@EqualsAndHashCode(callSuper = false)
public abstract class ParallaxChunkGenerator extends TerrainChunkGenerator
public abstract class ParallaxChunkGenerator extends TerrainChunkGenerator implements IObjectPlacer
{
private KMap<ChunkPosition, AtomicSliver> sliverCache;
protected AtomicWorldData parallaxMap;
private int sliverBuffer = 0;
public ParallaxChunkGenerator(String dimensionName, int threads)
{
super(dimensionName, threads);
sliverCache = new KMap<>();
}
public void onInit(World world, RNG rng)
{
super.onInit(world, rng);
parallaxMap = new AtomicWorldData(world);
}
protected void onClose()
{
super.onClose();
try
{
parallaxMap.unloadAll(true);
}
catch(IOException e)
{
e.printStackTrace();
}
}
@Override
protected void onPostGenerate(RNG random, int x, int z, ChunkData data, BiomeGrid grid, HeightMap height)
public int getHighest(int x, int z)
{
return sampleSliver(x, z).getHighestBlock();
}
@Override
public void set(int x, int y, int z, BlockData d)
{
getParallaxSliver(x, z).set(y, d);
}
@Override
public BlockData get(int x, int y, int z)
{
BlockData b = sampleSliver(x, z).getBlock().get(y);
return b == null ? AIR : b;
}
public AtomicSliver getParallaxSliver(int wx, int wz)
{
return getParallaxChunk(wx >> 4, wz >> 4).getSliver(wx & 15, wz & 15);
}
public boolean hasParallaxChunk(int x, int z)
{
try
{
return getParallaxMap().hasChunk(x, z);
}
catch(IOException e)
{
fail(e);
}
return false;
}
public AtomicSliverMap getParallaxChunk(int x, int z)
{
try
{
return getParallaxMap().loadChunk(x, z);
}
catch(IOException e)
{
fail(e);
}
return new AtomicSliverMap();
}
@Override
protected void onPostGenerate(RNG random, int x, int z, ChunkData data, BiomeGrid grid, HeightMap height, BiomeMap biomeMap)
{
onGenerateParallax(random, x, z);
getParallaxChunk(x, z).inject(data);
sliverBuffer = sliverCache.size();
sliverCache.clear();
}
protected void onGenerateParallax(RNG random, int x, int z)
{
ChunkPosition pos = Iris.data.getObjectLoader().getParallaxSize();
for(int i = x - pos.getX() / 2; i <= x + pos.getX() / 2; i++)
{
for(int j = z - pos.getZ() / 2; j <= z + pos.getZ() / 2; j++)
{
IrisBiome b = sampleBiome((i * 16) + 7, (j * 16) + 7).getBiome();
int g = 1;
for(IrisObjectPlacement k : b.getObjects())
{
placeObject(k, i, j, random.nextParallelRNG((i * 30) + (j * 30) + g++));
}
}
}
}
@Override
protected void onTick(int ticks)
{
if(ticks % 100 == 0)
{
parallaxMap.clean();
}
}
protected void placeObject(IrisObjectPlacement o, int x, int z, RNG rng)
{
for(int i = 0; i < o.getTriesForChunk(rng); i++)
{
o.getSchematic(rng).place((x * 16) * rng.nextInt(16), (z * 16) + rng.nextInt(16), this);
}
}
public AtomicSliver sampleSliver(int x, int z)
{
ChunkPosition key = new ChunkPosition(x, z);
if(sliverCache.containsKey(key))
{
return sliverCache.get(key);
}
AtomicSliver s = new AtomicSliver(x & 15, z & 15);
onGenerateColumn(x >> 4, z >> 4, x, z, x & 15, z & 15, s, null);
sliverCache.put(key, s);
return s;
}
}

View File

@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.object.atomics.AtomicSliver;
import ninja.bytecode.iris.object.atomics.AtomicSliverMap;
import ninja.bytecode.iris.util.BiomeMap;
import ninja.bytecode.iris.util.GroupedExecutor;
import ninja.bytecode.iris.util.HeightMap;
import ninja.bytecode.iris.util.RNG;
@ -37,11 +38,11 @@ public abstract class ParallelChunkGenerator extends BiomeChunkGenerator
}
}
protected abstract void onGenerateColumn(int cx, int cz, int wx, int wz, int x, int z, AtomicSliver sliver);
protected abstract void onGenerateColumn(int cx, int cz, int wx, int wz, int x, int z, AtomicSliver sliver, BiomeMap biomeMap);
protected abstract int onSampleColumnHeight(int cx, int cz, int wx, int wz, int x, int z);
protected abstract void onPostGenerate(RNG random, int x, int z, ChunkData data, BiomeGrid grid, HeightMap height);
protected abstract void onPostGenerate(RNG random, int x, int z, ChunkData data, BiomeGrid grid, HeightMap height, BiomeMap biomeMap);
protected int sampleHeight(int x, int z)
{
@ -53,6 +54,7 @@ public abstract class ParallelChunkGenerator extends BiomeChunkGenerator
AtomicSliverMap map = new AtomicSliverMap();
HeightMap height = new HeightMap();
String key = "c" + x + "," + z;
BiomeMap biomeMap = new BiomeMap();
int ii, jj;
for(ii = 0; ii < 16; ii++)
@ -68,14 +70,19 @@ public abstract class ParallelChunkGenerator extends BiomeChunkGenerator
tx.queue(key, () ->
{
onGenerateColumn(x, z, wx, wz, i, j, sliver);
onGenerateColumn(x, z, wx, wz, i, j, sliver, biomeMap);
});
}
}
tx.waitFor(key);
map.write(data, grid, height);
onPostGenerate(random, x, z, data, grid, height);
onPostGenerate(random, x, z, data, grid, height, biomeMap);
}
protected void onClose()
{
tx.close();
}
public void onInit(World world, RNG rng)
@ -87,6 +94,6 @@ public abstract class ParallelChunkGenerator extends BiomeChunkGenerator
@Override
public boolean isParallelCapable()
{
return true;
return false;
}
}

View File

@ -9,6 +9,7 @@ import lombok.EqualsAndHashCode;
import ninja.bytecode.iris.object.IrisBiome;
import ninja.bytecode.iris.object.IrisRegion;
import ninja.bytecode.iris.object.atomics.AtomicSliver;
import ninja.bytecode.iris.util.BiomeMap;
import ninja.bytecode.iris.util.BiomeResult;
import ninja.bytecode.iris.util.CNG;
import ninja.bytecode.iris.util.RNG;
@ -18,6 +19,7 @@ import ninja.bytecode.shuriken.collections.KList;
@EqualsAndHashCode(callSuper = false)
public abstract class TerrainChunkGenerator extends ParallelChunkGenerator
{
protected static final BlockData AIR = Material.AIR.createBlockData();
protected static final BlockData STONE = Material.STONE.createBlockData();
protected static final BlockData WATER = Material.WATER.createBlockData();
protected CNG terrainNoise;
@ -34,7 +36,7 @@ public abstract class TerrainChunkGenerator extends ParallelChunkGenerator
}
@Override
protected void onGenerateColumn(int cx, int cz, int rx, int rz, int x, int z, AtomicSliver sliver)
protected void onGenerateColumn(int cx, int cz, int rx, int rz, int x, int z, AtomicSliver sliver, BiomeMap biomeMap)
{
BlockData block;
int fluidHeight = getDimension().getFluidHeight();
@ -64,7 +66,12 @@ public abstract class TerrainChunkGenerator extends ParallelChunkGenerator
for(int k = Math.max(height, fluidHeight); k >= 0; k--)
{
boolean underwater = k > height && k <= fluidHeight;
if(biomeMap != null)
{
sliver.set(k, biome.getDerivative());
biomeMap.setBiome(x, z, biome);
}
if(underwater)
{
@ -76,7 +83,6 @@ public abstract class TerrainChunkGenerator extends ParallelChunkGenerator
block = layers.hasIndex(depth) ? layers.get(depth) : STONE;
depth++;
}
sliver.set(k, block);
}
}

View File

@ -0,0 +1,6 @@
package ninja.bytecode.iris.object;
public class Blueprint
{
}

View File

@ -25,7 +25,7 @@ public class IrisBiome extends IrisRegisteredObject
private KList<String> children = new KList<>();
private KList<IrisBiomePaletteLayer> layers = new KList<IrisBiomePaletteLayer>().qadd(new IrisBiomePaletteLayer());
private KList<IrisBiomeDecorator> decorators = new KList<IrisBiomeDecorator>();
private KList<IrisObjectPlacement> objects = new KList<IrisObjectPlacement>();
private transient ReentrantLock lock = new ReentrantLock();
private transient CellGenerator childrenCell;
private transient InferredType inferredType;

View File

@ -1,32 +1,98 @@
package ninja.bytecode.iris.object;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.bukkit.Location;
import org.bukkit.block.data.BlockData;
import org.bukkit.util.BlockVector;
import lombok.Data;
import lombok.EqualsAndHashCode;
import ninja.bytecode.iris.util.BlockDataTools;
import ninja.bytecode.iris.util.IObjectPlacer;
import ninja.bytecode.shuriken.collections.KMap;
import ninja.bytecode.shuriken.collections.KSet;
public class IrisObject
@Data
@EqualsAndHashCode(callSuper = false)
public class IrisObject extends IrisRegisteredObject
{
private String name;
private KMap<BlockVector, BlockData> blocks;
private KSet<BlockVector> mount;
private int w;
private int d;
private int h;
private transient BlockVector center;
public IrisObject(String name, int w, int h, int d)
public IrisObject(int w, int h, int d)
{
blocks = new KMap<>();
mount = new KSet<>();
this.w = w;
this.h = h;
this.d = d;
this.name = name;
center = new BlockVector(w / 2, h / 2, d / 2);
}
public static BlockVector sampleSize(File file) throws IOException
{
FileInputStream in = new FileInputStream(file);
DataInputStream din = new DataInputStream(in);
BlockVector bv = new BlockVector(din.readInt(), din.readInt(), din.readInt());
din.close();
return bv;
}
public void read(InputStream in) throws IOException
{
DataInputStream din = new DataInputStream(in);
this.w = din.readInt();
this.h = din.readInt();
this.d = din.readInt();
center = new BlockVector(w / 2, h / 2, d / 2);
int s = din.readInt();
for(int i = 0; i < s; i++)
{
blocks.put(new BlockVector(din.readShort(), din.readShort(), din.readShort()), BlockDataTools.getBlockData(din.readUTF()));
}
}
public void read(File file) throws IOException
{
FileInputStream fin = new FileInputStream(file);
read(fin);
fin.close();
}
public void write(File file) throws IOException
{
file.getParentFile().mkdirs();
FileOutputStream out = new FileOutputStream(file);
write(out);
out.close();
}
public void write(OutputStream o) throws IOException
{
DataOutputStream dos = new DataOutputStream(o);
dos.writeInt(w);
dos.writeInt(h);
dos.writeInt(d);
dos.writeInt(blocks.size());
for(BlockVector i : blocks.k())
{
dos.writeShort(i.getBlockX());
dos.writeShort(i.getBlockY());
dos.writeShort(i.getBlockZ());
dos.writeUTF(blocks.get(i).getAsString(true));
}
}
public void setUnsigned(int x, int y, int z, BlockData block)
{
if(x >= w || y >= h || z >= d)
@ -46,4 +112,22 @@ public class IrisObject
blocks.put(v, block);
}
}
public void place(int x, int z, IObjectPlacer placer)
{
int y = placer.getHighest(x, z) + getCenter().getBlockY();
for(BlockVector i : blocks.k())
{
placer.set(x + i.getBlockX(), y + i.getBlockY(), z + i.getBlockZ(), blocks.get(i));
}
}
public void place(Location at)
{
for(BlockVector i : blocks.k())
{
at.clone().add(0, getCenter().getY(), 0).add(i).getBlock().setBlockData(blocks.get(i), false);
}
}
}

View File

@ -0,0 +1,42 @@
package ninja.bytecode.iris.object;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.util.RNG;
import ninja.bytecode.shuriken.collections.KList;
public class IrisObjectPlacement
{
private KList<String> place = new KList<>();
private double chance = 1;
private int density = 1;
public IrisObjectPlacement()
{
}
public IrisObject getSchematic(RNG random)
{
if(place.isEmpty())
{
return null;
}
return Iris.data.getObjectLoader().load(place.get(random.nextInt(place.size())));
}
public int getTriesForChunk(RNG random)
{
if(chance <= 0)
{
return 0;
}
if(chance >= 1 || random.nextDouble() < chance)
{
return density;
}
return 0;
}
}

View File

@ -53,6 +53,11 @@ public class AtomicRegionData
public void set(int rx, int rz, AtomicSliverMap data) throws IOException
{
if(data == null)
{
return;
}
ByteArrayOutputStream boas = new ByteArrayOutputStream();
data.write(boas);
tag.put(rx + "." + rz, new ByteArrayTag(rx + "." + rz, boas.toByteArray()));
@ -60,12 +65,13 @@ public class AtomicRegionData
public AtomicSliverMap get(int rx, int rz) throws IOException
{
AtomicSliverMap data = new AtomicSliverMap();
if(!contains(rx, rz))
{
return null;
return data;
}
AtomicSliverMap data = new AtomicSliverMap();
ByteArrayTag btag = (ByteArrayTag) tag.get(rx + "." + rz);
ByteArrayInputStream in = new ByteArrayInputStream(btag.getValue());
data.read(in);

View File

@ -12,13 +12,14 @@ import org.bukkit.generator.ChunkGenerator.ChunkData;
import lombok.Data;
import ninja.bytecode.iris.util.BlockDataTools;
import ninja.bytecode.iris.util.HeightMap;
import ninja.bytecode.shuriken.collections.KMap;
@Data
public class AtomicSliver
{
private static final BlockData AIR = BlockDataTools.getBlockData("AIR");
private BlockData[] block;
private Biome[] biome;
private KMap<Integer, BlockData> block;
private KMap<Integer, Biome> biome;
private int highestBlock = 0;
private int highestBiome = 0;
private int x;
@ -28,19 +29,19 @@ public class AtomicSliver
{
this.x = x;
this.z = z;
this.block = new BlockData[256];
this.biome = new Biome[256];
this.block = new KMap<>();
this.biome = new KMap<>();
}
public void set(int h, BlockData d)
{
block[h] = d;
block.put(h, d);
highestBlock = h > highestBlock ? h : highestBlock;
}
public void set(int h, Biome d)
{
biome[h] = d;
biome.put(h, d);
highestBiome = h > highestBiome ? h : highestBiome;
}
@ -48,14 +49,14 @@ public class AtomicSliver
{
for(int i = 0; i <= highestBlock; i++)
{
if(block[i] == null)
if(block.get(i) == null)
{
d.setBlock(x, i, z, AIR);
}
else
{
d.setBlock(x, i, z, block[i]);
d.setBlock(x, i, z, block.get(i));
}
}
}
@ -64,7 +65,10 @@ public class AtomicSliver
{
for(int i = 0; i <= highestBiome; i++)
{
d.setBiome(x, i, z, biome[i]);
if(biome.get(i) != null)
{
d.setBiome(x, i, z, biome.get(i));
}
}
}
@ -75,11 +79,11 @@ public class AtomicSliver
public void read(DataInputStream din) throws IOException
{
this.block = new BlockData[256];
this.block = new KMap<Integer, BlockData>();
int h = din.readByte() - Byte.MIN_VALUE;
for(int i = 0; i <= h; i++)
{
block[i] = BlockDataTools.getBlockData(din.readUTF());
block.put(i, BlockDataTools.getBlockData(din.readUTF()));
}
}
@ -89,7 +93,8 @@ public class AtomicSliver
for(int i = 0; i <= highestBlock; i++)
{
dos.writeUTF(block[i].getAsString(true));
BlockData dat = block.get(i);
dos.writeUTF((dat == null ? AIR : dat).getAsString(true));
}
}
@ -97,15 +102,27 @@ public class AtomicSliver
{
for(int i = 0; i < 256; i++)
{
if(block[i] == null || block[i].equals(AIR))
if(block.get(i) == null || block.get(i).equals(AIR))
{
BlockData b = atomicSliver.block[i];
BlockData b = atomicSliver.block.get(i);
if(b == null || b.equals(AIR))
{
continue;
}
block[i] = b;
block.put(i, b);
}
}
}
public void inject(ChunkData currentData)
{
for(int i = 0; i < getHighestBlock(); i++)
{
if(block.get(i) != null && !block.get(i).equals(AIR))
{
BlockData b = block.get(i);
currentData.setBlock(x, i, z, b);
}
}
}

View File

@ -73,4 +73,12 @@ public class AtomicSliverMap
}
}
}
public void inject(ChunkData currentData)
{
for(AtomicSliver i : slivers)
{
i.inject(currentData);
}
}
}

View File

@ -7,29 +7,36 @@ import java.io.IOException;
import org.bukkit.World;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.util.ChunkPosition;
import ninja.bytecode.shuriken.collections.KList;
import ninja.bytecode.shuriken.collections.KMap;
import ninja.bytecode.shuriken.math.M;
public class AtomicWorldData
{
private World world;
private KMap<ChunkPosition, AtomicSliverMap> loadedChunks;
private KMap<ChunkPosition, AtomicRegionData> loadedSections;
private KMap<ChunkPosition, Long> lastRegion;
public AtomicWorldData(World world)
{
this.world = world;
loadedSections = new KMap<>();
loadedChunks = new KMap<>();
lastRegion = new KMap<>();
getSubregionFolder().mkdirs();
}
public KList<ChunkPosition> getLoadedRegions()
public KMap<ChunkPosition, AtomicRegionData> getLoadedRegions()
{
return loadedSections.k();
return loadedSections;
}
public AtomicRegionData getSubregion(int x, int z) throws IOException
{
lastRegion.put(new ChunkPosition(x, z), M.ms());
if(!isSectionLoaded(x, z))
{
loadedSections.put(new ChunkPosition(x, z), loadSection(x, z));
@ -42,6 +49,8 @@ public class AtomicWorldData
public void saveAll() throws IOException
{
saveChunks();
for(ChunkPosition i : loadedSections.keySet())
{
saveSection(i);
@ -50,10 +59,16 @@ public class AtomicWorldData
public void unloadAll(boolean save) throws IOException
{
saveChunks();
for(ChunkPosition i : loadedSections.keySet())
{
unloadSection(i, save);
}
loadedSections.clear();
loadedChunks.clear();
lastRegion.clear();
}
public void deleteSection(int x, int z) throws IOException
@ -105,6 +120,7 @@ public class AtomicWorldData
return false;
}
saveChunks(s);
AtomicRegionData data = loadedSections.get(s);
FileOutputStream fos = new FileOutputStream(getSubregionFile(s.getX(), s.getZ()));
data.write(fos);
@ -112,29 +128,84 @@ public class AtomicWorldData
return true;
}
public void saveChunks() throws IOException
{
for(ChunkPosition i : loadedChunks.k())
{
saveChunk(i);
}
}
public void saveChunks(ChunkPosition reg) throws IOException
{
for(ChunkPosition i : loadedChunks.k())
{
int x = i.getX();
int z = i.getZ();
if(x >> 5 == reg.getX() && z >> 5 == reg.getZ())
{
saveChunk(i);
}
}
}
public void saveChunk(ChunkPosition i) throws IOException
{
int x = i.getX();
int z = i.getZ();
AtomicRegionData dat = loadSection(x >> 5, z >> 5);
dat.set(x & 31, z & 31, loadedChunks.get(i));
loadedChunks.remove(i);
}
public AtomicSliverMap loadChunk(int x, int z) throws IOException
{
return loadSection(x >> 5, z >> 5).get(x & 31, z & 31);
ChunkPosition pos = new ChunkPosition(x, z);
if(loadedChunks.containsKey(pos))
{
return loadedChunks.get(pos);
}
AtomicRegionData dat = loadSection(x >> 5, z >> 5);
AtomicSliverMap m = dat.get(x & 31, z & 31);
loadedChunks.put(pos, m);
Iris.info("Loaded chunk: sections: " + loadedSections.size());
return m;
}
public boolean hasChunk(int x, int z) throws IOException
{
return loadSection(x >> 5, z >> 5).contains(x & 31, z & 31);
}
public AtomicRegionData loadSection(int x, int z) throws IOException
{
ChunkPosition pos = new ChunkPosition(x, z);
lastRegion.put(pos, M.ms());
if(isSectionLoaded(x, z))
{
return loadedSections.get(new ChunkPosition(x, z));
return loadedSections.get(pos);
}
File file = getSubregionFile(x, z);
if(!file.exists())
{
return createSection(x, z);
AtomicRegionData dat = createSection(x, z);
loadedSections.put(pos, dat);
return dat;
}
FileInputStream fin = new FileInputStream(file);
AtomicRegionData data = new AtomicRegionData(world);
data.read(fin);
fin.close();
loadedSections.put(pos, data);
return data;
}
@ -160,4 +231,30 @@ public class AtomicWorldData
{
return new File(world.getWorldFolder(), "subregion");
}
public KMap<ChunkPosition, AtomicSliverMap> getLoadedChunks()
{
return loadedChunks;
}
public void clean()
{
for(ChunkPosition i : lastRegion.k())
{
if(M.ms() - lastRegion.get(i) > 3000)
{
lastRegion.remove(i);
try
{
unloadSection(i, true);
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
}
}

View File

@ -0,0 +1,31 @@
package ninja.bytecode.iris.util;
import org.bukkit.util.Vector;
public enum Axis
{
X(1, 0, 0),
Y(0, 1, 0),
Z(0, 0, 1);
private int x;
private int y;
private int z;
private Axis(int x, int y, int z)
{
this.x = x;
this.y = y;
this.z = z;
}
public Vector positive()
{
return new Vector(x, y, z);
}
public Vector negative()
{
return VectorMath.reverse(positive());
}
}

View File

@ -0,0 +1,23 @@
package ninja.bytecode.iris.util;
import ninja.bytecode.iris.object.IrisBiome;
public class BiomeMap
{
private final IrisBiome[] height;
public BiomeMap()
{
height = new IrisBiome[256];
}
public void setBiome(int x, int z, IrisBiome h)
{
height[x * 16 + z] = h;
}
public IrisBiome getBiome(int x, int z)
{
return height[x * 16 + z];
}
}

View File

@ -0,0 +1,49 @@
package ninja.bytecode.iris.util;
public class CDou
{
private double number;
private double max;
public CDou(double max)
{
number = 0;
this.max = max;
}
public CDou set(double n)
{
number = n;
circ();
return this;
}
public CDou add(double a)
{
number += a;
circ();
return this;
}
public CDou sub(double a)
{
number -= a;
circ();
return this;
}
public double get()
{
return number;
}
public void circ()
{
if(number < 0)
{
number = max - (Math.abs(number) > max ? max : Math.abs(number));
}
number = number % (max);
}
}

View File

@ -0,0 +1,884 @@
package ninja.bytecode.iris.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.block.Block;
import org.bukkit.configuration.serialization.ConfigurationSerializable;
import org.bukkit.entity.Entity;
import ninja.bytecode.shuriken.collections.KList;
/**
* Cuboids
*
* @author cyberpwn
*/
public class Cuboid implements Iterable<Block>, Cloneable, ConfigurationSerializable
{
protected final String worldName;
protected int x1, y1, z1;
protected int x2, y2, z2;
/**
* Construct a Cuboid given two Location objects which represent any two corners
* of the Cuboid.
*
* @param l1
* one of the corners
* @param l2
* the other corner
*/
public Cuboid(Location l1, Location l2)
{
if(!l1.getWorld().equals(l2.getWorld()))
{
throw new IllegalArgumentException("locations must be on the same world");
}
worldName = l1.getWorld().getName();
x1 = Math.min(l1.getBlockX(), l2.getBlockX());
y1 = Math.min(l1.getBlockY(), l2.getBlockY());
z1 = Math.min(l1.getBlockZ(), l2.getBlockZ());
x2 = Math.max(l1.getBlockX(), l2.getBlockX());
y2 = Math.max(l1.getBlockY(), l2.getBlockY());
z2 = Math.max(l1.getBlockZ(), l2.getBlockZ());
}
public KList<Entity> getEntities()
{
KList<Entity> en = new KList<Entity>();
for(Chunk i : getChunks())
{
for(Entity j : i.getEntities())
{
if(contains(j.getLocation()))
{
en.add(j);
}
}
}
return en;
}
/**
* Set the locations
*
* @param l1
* a
* @param l2
* b
*/
public void set(Location l1, Location l2)
{
x1 = Math.min(l1.getBlockX(), l2.getBlockX());
y1 = Math.min(l1.getBlockY(), l2.getBlockY());
z1 = Math.min(l1.getBlockZ(), l2.getBlockZ());
x2 = Math.max(l1.getBlockX(), l2.getBlockX());
y2 = Math.max(l1.getBlockY(), l2.getBlockY());
z2 = Math.max(l1.getBlockZ(), l2.getBlockZ());
}
/**
* Construct a one-block Cuboid at the given Location of the Cuboid.
*
* @param l1
* location of the Cuboid
*/
public Cuboid(Location l1)
{
this(l1, l1);
}
/**
* Copy constructor.
*
* @param other
* the Cuboid to copy
*/
public Cuboid(Cuboid other)
{
this(other.getWorld().getName(), other.x1, other.y1, other.z1, other.x2, other.y2, other.z2);
}
/**
* Construct a Cuboid in the given World and xyz co-ordinates
*
* @param world
* the Cuboid's world
* @param x1
* X co-ordinate of corner 1
* @param y1
* Y co-ordinate of corner 1
* @param z1
* Z co-ordinate of corner 1
* @param x2
* X co-ordinate of corner 2
* @param y2
* Y co-ordinate of corner 2
* @param z2
* Z co-ordinate of corner 2
*/
public Cuboid(World world, int x1, int y1, int z1, int x2, int y2, int z2)
{
this.worldName = world.getName();
this.x1 = Math.min(x1, x2);
this.x2 = Math.max(x1, x2);
this.y1 = Math.min(y1, y2);
this.y2 = Math.max(y1, y2);
this.z1 = Math.min(z1, z2);
this.z2 = Math.max(z1, z2);
}
/**
* Construct a Cuboid in the given world name and xyz co-ordinates.
*
* @param worldName
* the Cuboid's world name
* @param x1
* X co-ordinate of corner 1
* @param y1
* Y co-ordinate of corner 1
* @param z1
* Z co-ordinate of corner 1
* @param x2
* X co-ordinate of corner 2
* @param y2
* Y co-ordinate of corner 2
* @param z2
* Z co-ordinate of corner 2
*/
private Cuboid(String worldName, int x1, int y1, int z1, int x2, int y2, int z2)
{
this.worldName = worldName;
this.x1 = Math.min(x1, x2);
this.x2 = Math.max(x1, x2);
this.y1 = Math.min(y1, y2);
this.y2 = Math.max(y1, y2);
this.z1 = Math.min(z1, z2);
this.z2 = Math.max(z1, z2);
}
public Cuboid(Map<String, Object> map)
{
worldName = (String) map.get("worldName");
x1 = (Integer) map.get("x1");
x2 = (Integer) map.get("x2");
y1 = (Integer) map.get("y1");
y2 = (Integer) map.get("y2");
z1 = (Integer) map.get("z1");
z2 = (Integer) map.get("z2");
}
@Override
public Map<String, Object> serialize()
{
Map<String, Object> map = new HashMap<String, Object>();
map.put("worldName", worldName);
map.put("x1", x1);
map.put("y1", y1);
map.put("z1", z1);
map.put("x2", x2);
map.put("y2", y2);
map.put("z2", z2);
return map;
}
public Cuboid flatten(int level)
{
return new Cuboid(getWorld(), x1, level, z1, x2, level, z2);
}
/**
* Get the Location of the lower northeast corner of the Cuboid (minimum XYZ
* co-ordinates).
*
* @return Location of the lower northeast corner
*/
public Location getLowerNE()
{
return new Location(getWorld(), x1, y1, z1);
}
/**
* Get the Location of the upper southwest corner of the Cuboid (maximum XYZ
* co-ordinates).
*
* @return Location of the upper southwest corner
*/
public Location getUpperSW()
{
return new Location(getWorld(), x2, y2, z2);
}
/**
* Get the the centre of the Cuboid
*
* @return Location at the centre of the Cuboid
*/
public Location getCenter()
{
int x1 = getUpperX() + 1;
int y1 = getUpperY() + 1;
int z1 = getUpperZ() + 1;
return new Location(getWorld(), getLowerX() + (x1 - getLowerX()) / 2.0, getLowerY() + (y1 - getLowerY()) / 2.0, getLowerZ() + (z1 - getLowerZ()) / 2.0);
}
/**
* Get the Cuboid's world.
*
* @return the World object representing this Cuboid's world
* @throws IllegalStateException
* if the world is not loaded
*/
public World getWorld()
{
World world = Bukkit.getWorld(worldName);
if(world == null)
{
throw new IllegalStateException("world '" + worldName + "' is not loaded");
}
return world;
}
/**
* Get the size of this Cuboid along the X axis
*
* @return Size of Cuboid along the X axis
*/
public int getSizeX()
{
return (x2 - x1) + 1;
}
/**
* Get the size of this Cuboid along the Y axis
*
* @return Size of Cuboid along the Y axis
*/
public int getSizeY()
{
return (y2 - y1) + 1;
}
/**
* Get the size of this Cuboid along the Z axis
*
* @return Size of Cuboid along the Z axis
*/
public int getSizeZ()
{
return (z2 - z1) + 1;
}
/**
* Get the cuboid dimensions
*
* @return the dimensions
*/
public Dimension getDimension()
{
return new Dimension(getSizeX(), getSizeY(), getSizeZ());
}
/**
* Get the minimum X co-ordinate of this Cuboid
*
* @return the minimum X co-ordinate
*/
public int getLowerX()
{
return x1;
}
/**
* Get the minimum Y co-ordinate of this Cuboid
*
* @return the minimum Y co-ordinate
*/
public int getLowerY()
{
return y1;
}
/**
* Get the minimum Z co-ordinate of this Cuboid
*
* @return the minimum Z co-ordinate
*/
public int getLowerZ()
{
return z1;
}
/**
* Get the maximum X co-ordinate of this Cuboid
*
* @return the maximum X co-ordinate
*/
public int getUpperX()
{
return x2;
}
/**
* Get the maximum Y co-ordinate of this Cuboid
*
* @return the maximum Y co-ordinate
*/
public int getUpperY()
{
return y2;
}
/**
* Get the maximum Z co-ordinate of this Cuboid
*
* @return the maximum Z co-ordinate
*/
public int getUpperZ()
{
return z2;
}
/**
* Get the Blocks at the eight corners of the Cuboid.
*
* @return array of Block objects representing the Cuboid corners
*/
public Block[] corners()
{
Block[] res = new Block[8];
World w = getWorld();
res[0] = w.getBlockAt(x1, y1, z1);
res[1] = w.getBlockAt(x1, y1, z2);
res[2] = w.getBlockAt(x1, y2, z1);
res[3] = w.getBlockAt(x1, y2, z2);
res[4] = w.getBlockAt(x2, y1, z1);
res[5] = w.getBlockAt(x2, y1, z2);
res[6] = w.getBlockAt(x2, y2, z1);
res[7] = w.getBlockAt(x2, y2, z2);
return res;
}
/**
* Expand the Cuboid in the given direction by the given amount. Negative
* amounts will shrink the Cuboid in the given direction. Shrinking a cuboid's
* face past the opposite face is not an error and will return a valid Cuboid.
*
* @param dir
* the direction in which to expand
* @param amount
* the number of blocks by which to expand
* @return a new Cuboid expanded by the given direction and amount
*/
public Cuboid expand(CuboidDirection dir, int amount)
{
switch(dir)
{
case North:
return new Cuboid(worldName, x1 - amount, y1, z1, x2, y2, z2);
case South:
return new Cuboid(worldName, x1, y1, z1, x2 + amount, y2, z2);
case East:
return new Cuboid(worldName, x1, y1, z1 - amount, x2, y2, z2);
case West:
return new Cuboid(worldName, x1, y1, z1, x2, y2, z2 + amount);
case Down:
return new Cuboid(worldName, x1, y1 - amount, z1, x2, y2, z2);
case Up:
return new Cuboid(worldName, x1, y1, z1, x2, y2 + amount, z2);
default:
throw new IllegalArgumentException("invalid direction " + dir);
}
}
public Cuboid expand(Direction dir, int amount)
{
int ax = dir.toVector().getBlockX() == 1 ? amount : 0;
int sx = dir.toVector().getBlockX() == -1 ? -amount : 0;
int ay = dir.toVector().getBlockY() == 1 ? amount : 0;
int sy = dir.toVector().getBlockY() == -1 ? -amount : 0;
int az = dir.toVector().getBlockZ() == 1 ? amount : 0;
int sz = dir.toVector().getBlockZ() == -1 ? -amount : 0;
return new Cuboid(worldName, x1 + sx, y1 + sy, z1 + sz, x2 + ax, y2 + ay, z2 + az);
}
/**
* Shift the Cuboid in the given direction by the given amount.
*
* @param dir
* the direction in which to shift
* @param amount
* the number of blocks by which to shift
* @return a new Cuboid shifted by the given direction and amount
*/
public Cuboid shift(CuboidDirection dir, int amount)
{
return expand(dir, amount).expand(dir.opposite(), -amount);
}
/**
* Outset (grow) the Cuboid in the given direction by the given amount.
*
* @param dir
* the direction in which to outset (must be Horizontal, Vertical, or
* Both)
* @param amount
* the number of blocks by which to outset
* @return a new Cuboid outset by the given direction and amount
*/
public Cuboid outset(CuboidDirection dir, int amount)
{
Cuboid c;
switch(dir)
{
case Horizontal:
c = expand(CuboidDirection.North, amount).expand(CuboidDirection.South, amount).expand(CuboidDirection.East, amount).expand(CuboidDirection.West, amount);
break;
case Vertical:
c = expand(CuboidDirection.Down, amount).expand(CuboidDirection.Up, amount);
break;
case Both:
c = outset(CuboidDirection.Horizontal, amount).outset(CuboidDirection.Vertical, amount);
break;
default:
throw new IllegalArgumentException("invalid direction " + dir);
}
return c;
}
/**
* Inset (shrink) the Cuboid in the given direction by the given amount.
* Equivalent to calling outset() with a negative amount.
*
* @param dir
* the direction in which to inset (must be Horizontal, Vertical, or
* Both)
* @param amount
* the number of blocks by which to inset
* @return a new Cuboid inset by the given direction and amount
*/
public Cuboid inset(CuboidDirection dir, int amount)
{
return outset(dir, -amount);
}
/**
* Return true if the point at (x,y,z) is contained within this Cuboid.
*
* @param x
* the X co-ordinate
* @param y
* the Y co-ordinate
* @param z
* the Z co-ordinate
* @return true if the given point is within this Cuboid, false otherwise
*/
public boolean contains(int x, int y, int z)
{
return x >= x1 && x <= x2 && y >= y1 && y <= y2 && z >= z1 && z <= z2;
}
/**
* Check if the given Block is contained within this Cuboid.
*
* @param b
* the Block to check for
* @return true if the Block is within this Cuboid, false otherwise
*/
public boolean contains(Block b)
{
return contains(b.getLocation());
}
/**
* Check if the given Location is contained within this Cuboid.
*
* @param l
* the Location to check for
* @return true if the Location is within this Cuboid, false otherwise
*/
public boolean contains(Location l)
{
return worldName.equals(l.getWorld().getName()) && contains(l.getBlockX(), l.getBlockY(), l.getBlockZ());
}
/**
* Get the volume of this Cuboid.
*
* @return the Cuboid volume, in blocks
*/
public int volume()
{
return getSizeX() * getSizeY() * getSizeZ();
}
/**
* Get the average light level of all empty (air) blocks in the Cuboid. Returns
* 0 if there are no empty blocks.
*
* @return the average light level of this Cuboid
*/
public byte averageLightLevel()
{
long total = 0;
int n = 0;
for(Block b : this)
{
if(b.isEmpty())
{
total += b.getLightLevel();
++n;
}
}
return n > 0 ? (byte) (total / n) : 0;
}
/**
* Contract the Cuboid, returning a Cuboid with any air around the edges
* removed, just large enough to include all non-air blocks.
*
* @return a new Cuboid with no external air blocks
*/
public Cuboid contract()
{
return this.contract(CuboidDirection.Down).contract(CuboidDirection.South).contract(CuboidDirection.East).contract(CuboidDirection.Up).contract(CuboidDirection.North).contract(CuboidDirection.West);
}
/**
* Contract the Cuboid in the given direction, returning a new Cuboid which has
* no exterior empty space. E.g. a direction of Down will push the top face
* downwards as much as possible.
*
* @param dir
* the direction in which to contract
* @return a new Cuboid contracted in the given direction
*/
public Cuboid contract(CuboidDirection dir)
{
Cuboid face = getFace(dir.opposite());
switch(dir)
{
case Down:
while(face.containsOnly(Material.AIR) && face.getLowerY() > this.getLowerY())
{
face = face.shift(CuboidDirection.Down, 1);
}
return new Cuboid(worldName, x1, y1, z1, x2, face.getUpperY(), z2);
case Up:
while(face.containsOnly(Material.AIR) && face.getUpperY() < this.getUpperY())
{
face = face.shift(CuboidDirection.Up, 1);
}
return new Cuboid(worldName, x1, face.getLowerY(), z1, x2, y2, z2);
case North:
while(face.containsOnly(Material.AIR) && face.getLowerX() > this.getLowerX())
{
face = face.shift(CuboidDirection.North, 1);
}
return new Cuboid(worldName, x1, y1, z1, face.getUpperX(), y2, z2);
case South:
while(face.containsOnly(Material.AIR) && face.getUpperX() < this.getUpperX())
{
face = face.shift(CuboidDirection.South, 1);
}
return new Cuboid(worldName, face.getLowerX(), y1, z1, x2, y2, z2);
case East:
while(face.containsOnly(Material.AIR) && face.getLowerZ() > this.getLowerZ())
{
face = face.shift(CuboidDirection.East, 1);
}
return new Cuboid(worldName, x1, y1, z1, x2, y2, face.getUpperZ());
case West:
while(face.containsOnly(Material.AIR) && face.getUpperZ() < this.getUpperZ())
{
face = face.shift(CuboidDirection.West, 1);
}
return new Cuboid(worldName, x1, y1, face.getLowerZ(), x2, y2, z2);
default:
throw new IllegalArgumentException("Invalid direction " + dir);
}
}
/**
* Get the Cuboid representing the face of this Cuboid. The resulting Cuboid
* will be one block thick in the axis perpendicular to the requested face.
*
* @param dir
* which face of the Cuboid to get
* @return the Cuboid representing this Cuboid's requested face
*/
public Cuboid getFace(CuboidDirection dir)
{
switch(dir)
{
case Down:
return new Cuboid(worldName, x1, y1, z1, x2, y1, z2);
case Up:
return new Cuboid(worldName, x1, y2, z1, x2, y2, z2);
case North:
return new Cuboid(worldName, x1, y1, z1, x1, y2, z2);
case South:
return new Cuboid(worldName, x2, y1, z1, x2, y2, z2);
case East:
return new Cuboid(worldName, x1, y1, z1, x2, y2, z1);
case West:
return new Cuboid(worldName, x1, y1, z2, x2, y2, z2);
default:
throw new IllegalArgumentException("Invalid direction " + dir);
}
}
/**
* Check if the Cuboid contains only blocks of the given type
*
* @param material
* the material to check for
* @return true if this Cuboid contains only blocks of the given type
*/
public boolean containsOnly(Material material)
{
for(Block b : this)
{
if(b.getType() != material)
{
return false;
}
}
return true;
}
/**
* Get the Cuboid big enough to hold both this Cuboid and the given one.
*
* @param other
* the other Cuboid to include
* @return a new Cuboid large enough to hold this Cuboid and the given Cuboid
*/
public Cuboid getBoundingCuboid(Cuboid other)
{
if(other == null)
{
return this;
}
int xMin = Math.min(getLowerX(), other.getLowerX());
int yMin = Math.min(getLowerY(), other.getLowerY());
int zMin = Math.min(getLowerZ(), other.getLowerZ());
int xMax = Math.max(getUpperX(), other.getUpperX());
int yMax = Math.max(getUpperY(), other.getUpperY());
int zMax = Math.max(getUpperZ(), other.getUpperZ());
return new Cuboid(worldName, xMin, yMin, zMin, xMax, yMax, zMax);
}
/**
* Get a block relative to the lower NE point of the Cuboid.
*
* @param x
* the X co-ordinate
* @param y
* the Y co-ordinate
* @param z
* the Z co-ordinate
* @return the block at the given position
*/
public Block getRelativeBlock(int x, int y, int z)
{
return getWorld().getBlockAt(x1 + x, y1 + y, z1 + z);
}
/**
* Get a block relative to the lower NE point of the Cuboid in the given World.
* This version of getRelativeBlock() should be used if being called many times,
* to avoid excessive calls to getWorld().
*
* @param w
* the World
* @param x
* the X co-ordinate
* @param y
* the Y co-ordinate
* @param z
* the Z co-ordinate
* @return the block at the given position
*/
public Block getRelativeBlock(World w, int x, int y, int z)
{
return w.getBlockAt(x1 + x, y1 + y, z1 + z);
}
/**
* Get a list of the chunks which are fully or partially contained in this
* cuboid.
*
* @return a list of Chunk objects
*/
public List<Chunk> getChunks()
{
List<Chunk> res = new ArrayList<Chunk>();
World w = getWorld();
int x1 = getLowerX() & ~0xf;
int x2 = getUpperX() & ~0xf;
int z1 = getLowerZ() & ~0xf;
int z2 = getUpperZ() & ~0xf;
for(int x = x1; x <= x2; x += 16)
{
for(int z = z1; z <= z2; z += 16)
{
res.add(w.getChunkAt(x >> 4, z >> 4));
}
}
return res;
}
/**
* Set all the blocks within the Cuboid to the given MaterialData, using a
* MassBlockUpdate object for fast updates.
*
* @param mat
* the MaterialData to set
* @param mbu
* the MassBlockUpdate object
*/
/**
* Reset the light level of all blocks within this Cuboid.
*/
/*
* (non-Javadoc)
*
* @see java.lang.Iterable#iterator()
*/
@Override
public Iterator<Block> iterator()
{
return new CuboidIterator(getWorld(), x1, y1, z1, x2, y2, z2);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#clone()
*/
@Override
public Cuboid clone() throws CloneNotSupportedException
{
return new Cuboid(this);
}
/*
* (non-Javadoc)
*
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "Cuboid: " + worldName + "," + x1 + "," + y1 + "," + z1 + "=>" + x2 + "," + y2 + "," + z2;
}
public class CuboidIterator implements Iterator<Block>
{
private World w;
private int baseX, baseY, baseZ;
private int x, y, z;
private int sizeX, sizeY, sizeZ;
public CuboidIterator(World w, int x1, int y1, int z1, int x2, int y2, int z2)
{
this.w = w;
baseX = x1;
baseY = y1;
baseZ = z1;
sizeX = Math.abs(x2 - x1) + 1;
sizeY = Math.abs(y2 - y1) + 1;
sizeZ = Math.abs(z2 - z1) + 1;
x = y = z = 0;
}
@Override
public boolean hasNext()
{
return x < sizeX && y < sizeY && z < sizeZ;
}
@Override
public Block next()
{
Block b = w.getBlockAt(baseX + x, baseY + y, baseZ + z);
if(++x >= sizeX)
{
x = 0;
if(++y >= sizeY)
{
y = 0;
++z;
}
}
return b;
}
@Override
public void remove()
{
// nop
}
}
public enum CuboidDirection
{
North,
East,
South,
West,
Up,
Down,
Horizontal,
Vertical,
Both,
Unknown;
public CuboidDirection opposite()
{
switch(this)
{
case North:
return South;
case East:
return West;
case South:
return North;
case West:
return East;
case Horizontal:
return Vertical;
case Vertical:
return Horizontal;
case Up:
return Down;
case Down:
return Up;
case Both:
return Both;
default:
return Unknown;
}
}
}
}

View File

@ -0,0 +1,16 @@
package ninja.bytecode.iris.util;
/**
* Represents a cuboid exception
*
* @author cyberpwn
*/
public class CuboidException extends Exception
{
public CuboidException(String string)
{
super(string);
}
private static final long serialVersionUID = 1L;
}

View File

@ -0,0 +1,20 @@
package ninja.bytecode.iris.util;
import org.bukkit.util.Vector;
public abstract class DOP
{
private String type;
public DOP(String type)
{
this.type = type;
}
public abstract Vector op(Vector v);
public String getType()
{
return type;
}
}

View File

@ -0,0 +1,86 @@
package ninja.bytecode.iris.util;
/**
* Dimensions
*
* @author cyberpwn
*/
public class Dimension
{
private final int width;
private final int height;
private final int depth;
/**
* Make a dimension
*
* @param width
* width of this (X)
* @param height
* the height (Y)
* @param depth
* the depth (Z)
*/
public Dimension(int width, int height, int depth)
{
this.width = width;
this.height = height;
this.depth = depth;
}
/**
* Make a dimension
*
* @param width
* width of this (X)
* @param height
* the height (Y)
*/
public Dimension(int width, int height)
{
this.width = width;
this.height = height;
this.depth = 0;
}
/**
* Get the direction of the flat part of this dimension (null if no thin
* face)
*
* @return the direction of the flat pane or null
*/
public DimensionFace getPane()
{
if(width == 1)
{
return DimensionFace.X;
}
if(height == 1)
{
return DimensionFace.Y;
}
if(depth == 1)
{
return DimensionFace.Z;
}
return null;
}
public int getWidth()
{
return width;
}
public int getHeight()
{
return height;
}
public int getDepth()
{
return depth;
}
}

View File

@ -0,0 +1,24 @@
package ninja.bytecode.iris.util;
/**
* Represents a dimension (coordinates not worlds)
*
* @author cyberpwn
*/
public enum DimensionFace
{
/**
* The X dimension (width)
*/
X,
/**
* The Y dimension (height)
*/
Y,
/**
* The Z dimension (depth)
*/
Z
}

View File

@ -0,0 +1,535 @@
package ninja.bytecode.iris.util;
import org.bukkit.Axis;
import org.bukkit.block.BlockFace;
import org.bukkit.util.Vector;
import ninja.bytecode.iris.util.Cuboid.CuboidDirection;
import ninja.bytecode.shuriken.collections.KList;
import ninja.bytecode.shuriken.collections.KMap;
/**
* Directions
*
* @author cyberpwn
*/
public enum Direction
{
U(0, 1, 0, CuboidDirection.Up),
D(0, -1, 0, CuboidDirection.Down),
N(0, 0, -1, CuboidDirection.North),
S(0, 0, 1, CuboidDirection.South),
E(1, 0, 0, CuboidDirection.East),
W(-1, 0, 0, CuboidDirection.West);
private static KMap<GBiset<Direction, Direction>, DOP> permute = null;
private int x;
private int y;
private int z;
private CuboidDirection f;
public static Direction getDirection(BlockFace f)
{
switch(f)
{
case DOWN:
return D;
case EAST:
return E;
case EAST_NORTH_EAST:
return E;
case EAST_SOUTH_EAST:
return E;
case NORTH:
return N;
case NORTH_EAST:
return N;
case NORTH_NORTH_EAST:
return N;
case NORTH_NORTH_WEST:
return N;
case NORTH_WEST:
return N;
case SELF:
return U;
case SOUTH:
return S;
case SOUTH_EAST:
return S;
case SOUTH_SOUTH_EAST:
return S;
case SOUTH_SOUTH_WEST:
return S;
case SOUTH_WEST:
return S;
case UP:
return U;
case WEST:
return W;
case WEST_NORTH_WEST:
return W;
case WEST_SOUTH_WEST:
return W;
}
return D;
}
@Override
public String toString()
{
switch(this)
{
case D:
return "Down";
case E:
return "East";
case N:
return "North";
case S:
return "South";
case U:
return "Up";
case W:
return "West";
}
return "?";
}
public boolean isVertical()
{
return equals(D) || equals(U);
}
public static Direction closest(Vector v)
{
double m = Double.MAX_VALUE;
Direction s = null;
for(Direction i : values())
{
Vector x = i.toVector();
double g = x.dot(v);
if(g < m)
{
m = g;
s = i;
}
}
return s;
}
public static Direction closest(Vector v, Direction... d)
{
double m = Double.MAX_VALUE;
Direction s = null;
for(Direction i : d)
{
Vector x = i.toVector();
double g = x.distance(v);
if(g < m)
{
m = g;
s = i;
}
}
return s;
}
public static Direction closest(Vector v, KList<Direction> d)
{
double m = Double.MAX_VALUE;
Direction s = null;
for(Direction i : d)
{
Vector x = i.toVector();
double g = x.distance(v);
if(g < m)
{
m = g;
s = i;
}
}
return s;
}
public Vector toVector()
{
return new Vector(x, y, z);
}
public boolean isCrooked(Direction to)
{
if(equals(to.reverse()))
{
return false;
}
if(equals(to))
{
return false;
}
return true;
}
private Direction(int x, int y, int z, CuboidDirection f)
{
this.x = x;
this.y = y;
this.z = z;
this.f = f;
}
public Vector angle(Vector initial, Direction d)
{
calculatePermutations();
for(GBiset<Direction, Direction> i : permute.keySet())
{
if(i.getA().equals(this) && i.getB().equals(d))
{
return permute.get(i).op(initial);
}
}
return initial;
}
public Direction reverse()
{
switch(this)
{
case D:
return U;
case E:
return W;
case N:
return S;
case S:
return N;
case U:
return D;
case W:
return E;
default:
break;
}
return null;
}
public int x()
{
return x;
}
public int y()
{
return y;
}
public int z()
{
return z;
}
public CuboidDirection f()
{
return f;
}
public static KList<Direction> news()
{
return new KList<Direction>().add(N, E, W, S);
}
public static Direction getDirection(Vector v)
{
Vector k = VectorMath.triNormalize(v.clone().normalize());
for(Direction i : udnews())
{
if(i.x == k.getBlockX() && i.y == k.getBlockY() && i.z == k.getBlockZ())
{
return i;
}
}
return Direction.N;
}
public static KList<Direction> udnews()
{
return new KList<Direction>().add(U, D, N, E, W, S);
}
/**
* Get the directional value from the given byte from common directional blocks
* (MUST BE BETWEEN 0 and 5 INCLUSIVE)
*
* @param b
* the byte
* @return the direction or null if the byte is outside of the inclusive range
* 0-5
*/
public static Direction fromByte(byte b)
{
if(b > 5 || b < 0)
{
return null;
}
if(b == 0)
{
return D;
}
else if(b == 1)
{
return U;
}
else if(b == 2)
{
return N;
}
else if(b == 3)
{
return S;
}
else if(b == 4)
{
return W;
}
else
{
return E;
}
}
/**
* Get the byte value represented in some directional blocks
*
* @return the byte value
*/
public byte byteValue()
{
switch(this)
{
case D:
return 0;
case E:
return 5;
case N:
return 2;
case S:
return 3;
case U:
return 1;
case W:
return 4;
default:
break;
}
return -1;
}
public static void calculatePermutations()
{
if(permute != null)
{
return;
}
permute = new KMap<GBiset<Direction, Direction>, DOP>();
for(Direction i : udnews())
{
for(Direction j : udnews())
{
GBiset<Direction, Direction> b = new GBiset<Direction, Direction>(i, j);
if(i.equals(j))
{
permute.put(b, new DOP("DIRECT")
{
@Override
public Vector op(Vector v)
{
return v;
}
});
}
else if(i.reverse().equals(j))
{
if(i.isVertical())
{
permute.put(b, new DOP("R180CCZ")
{
@Override
public Vector op(Vector v)
{
return VectorMath.rotate90CCZ(VectorMath.rotate90CCZ(v));
}
});
}
else
{
permute.put(b, new DOP("R180CCY")
{
@Override
public Vector op(Vector v)
{
return VectorMath.rotate90CCY(VectorMath.rotate90CCY(v));
}
});
}
}
else if(getDirection(VectorMath.rotate90CX(i.toVector())).equals(j))
{
permute.put(b, new DOP("R90CX")
{
@Override
public Vector op(Vector v)
{
return VectorMath.rotate90CX(v);
}
});
}
else if(getDirection(VectorMath.rotate90CCX(i.toVector())).equals(j))
{
permute.put(b, new DOP("R90CCX")
{
@Override
public Vector op(Vector v)
{
return VectorMath.rotate90CCX(v);
}
});
}
else if(getDirection(VectorMath.rotate90CY(i.toVector())).equals(j))
{
permute.put(b, new DOP("R90CY")
{
@Override
public Vector op(Vector v)
{
return VectorMath.rotate90CY(v);
}
});
}
else if(getDirection(VectorMath.rotate90CCY(i.toVector())).equals(j))
{
permute.put(b, new DOP("R90CCY")
{
@Override
public Vector op(Vector v)
{
return VectorMath.rotate90CCY(v);
}
});
}
else if(getDirection(VectorMath.rotate90CZ(i.toVector())).equals(j))
{
permute.put(b, new DOP("R90CZ")
{
@Override
public Vector op(Vector v)
{
return VectorMath.rotate90CZ(v);
}
});
}
else if(getDirection(VectorMath.rotate90CCZ(i.toVector())).equals(j))
{
permute.put(b, new DOP("R90CCZ")
{
@Override
public Vector op(Vector v)
{
return VectorMath.rotate90CCZ(v);
}
});
}
else
{
permute.put(b, new DOP("FAIL")
{
@Override
public Vector op(Vector v)
{
return v;
}
});
}
}
}
}
public BlockFace getFace()
{
switch(this)
{
case D:
return BlockFace.DOWN;
case E:
return BlockFace.EAST;
case N:
return BlockFace.NORTH;
case S:
return BlockFace.SOUTH;
case U:
return BlockFace.UP;
case W:
return BlockFace.WEST;
}
return null;
}
public Axis getAxis()
{
switch(this)
{
case D:
return Axis.Y;
case E:
return Axis.X;
case N:
return Axis.Z;
case S:
return Axis.Z;
case U:
return Axis.Y;
case W:
return Axis.X;
}
return null;
}
}

View File

@ -0,0 +1,161 @@
package ninja.bytecode.iris.util;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
/**
* Simple Bukkit Particles API with 1.7 to 1.13.2 support !
* <p>
* You can find the project on <a href="https://github.com/MrMicky-FR/FastParticles">GitHub</a>
*
* @author MrMicky
*/
public final class FastParticle {
private static final ParticleSender PARTICLE_SENDER;
static {
if (FastReflection.optionalClass("org.bukkit.Particle$DustOptions").isPresent()) {
PARTICLE_SENDER = new ParticleSender.ParticleSender1_13();
} else if (FastReflection.optionalClass("org.bukkit.Particle").isPresent()) {
PARTICLE_SENDER = new ParticleSender.ParticleSenderImpl();
} else {
PARTICLE_SENDER = new ParticleSenderLegacy();
}
}
private FastParticle() {
throw new UnsupportedOperationException();
}
/*
* Worlds methods
*/
public static void spawnParticle(World world, ParticleType particle, Location location, int count) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count);
}
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count) {
spawnParticle(world, particle, x, y, z, count, null);
}
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count, T data) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, data);
}
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
T data) {
spawnParticle(world, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data);
}
public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX,
double offsetY, double offsetZ) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ);
}
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ) {
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, null);
}
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count,
double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY,
offsetZ, data);
}
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data);
}
public static void spawnParticle(World world, ParticleType particle, Location location, int count, double offsetX,
double offsetY, double offsetZ, double extra) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra);
}
public static void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra) {
spawnParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null);
}
public static <T> void spawnParticle(World world, ParticleType particle, Location location, int count,
double offsetX, double offsetY, double offsetZ, double extra, T data) {
spawnParticle(world, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data);
}
public static <T> void spawnParticle(World world, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra, T data) {
sendParticle(world, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
/*
* Player methods
*/
public static void spawnParticle(Player player, ParticleType particle, Location location, int count) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count);
}
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count) {
spawnParticle(player, particle, x, y, z, count, null);
}
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count, T data) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, data);
}
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
T data) {
spawnParticle(player, particle, x, y, z, count, 0.0D, 0.0D, 0.0D, data);
}
public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX,
double offsetY, double offsetZ) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ);
}
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ) {
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, null);
}
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count,
double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, data);
}
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, T data) {
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, 1.0D, data);
}
public static void spawnParticle(Player player, ParticleType particle, Location location, int count, double offsetX,
double offsetY, double offsetZ, double extra) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra);
}
public static void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra) {
spawnParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, null);
}
public static <T> void spawnParticle(Player player, ParticleType particle, Location location, int count,
double offsetX, double offsetY, double offsetZ, double extra, T data) {
spawnParticle(player, particle, location.getX(), location.getY(), location.getZ(), count, offsetX, offsetY, offsetZ, extra, data);
}
public static <T> void spawnParticle(Player player, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra, T data) {
sendParticle(player, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
private static void sendParticle(Object receiver, ParticleType particle, double x, double y, double z, int count,
double offsetX, double offsetY, double offsetZ, double extra, Object data) {
if (!particle.isSupported()) {
throw new IllegalArgumentException("The particle '" + particle + "' is not compatible with your server version");
}
PARTICLE_SENDER.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
}

View File

@ -0,0 +1,59 @@
package ninja.bytecode.iris.util;
import org.bukkit.Bukkit;
import java.util.Optional;
/**
* Small reflection class to use CraftBukkit and NMS
*
* @author MrMicky
*/
public final class FastReflection {
public static final String OBC_PACKAGE = "org.bukkit.craftbukkit";
public static final String NMS_PACKAGE = "net.minecraft.server";
public static final String VERSION = Bukkit.getServer().getClass().getPackage().getName().substring(OBC_PACKAGE.length() + 1);
private FastReflection() {
throw new UnsupportedOperationException();
}
public static String nmsClassName(String className) {
return NMS_PACKAGE + '.' + VERSION + '.' + className;
}
public static Class<?> nmsClass(String className) throws ClassNotFoundException {
return Class.forName(nmsClassName(className));
}
public static Optional<Class<?>> nmsOptionalClass(String className) {
return optionalClass(nmsClassName(className));
}
public static String obcClassName(String className) {
return OBC_PACKAGE + '.' + VERSION + '.' + className;
}
public static Class<?> obcClass(String className) throws ClassNotFoundException {
return Class.forName(obcClassName(className));
}
public static Optional<Class<?>> obcOptionalClass(String className) {
return optionalClass(obcClassName(className));
}
public static Optional<Class<?>> optionalClass(String className) {
try {
return Optional.of(Class.forName(className));
} catch (ClassNotFoundException e) {
return Optional.empty();
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
public static Object enumValueOf(Class<?> enumClass, String enumName) {
return Enum.valueOf((Class<Enum>) enumClass, enumName.toUpperCase());
}
}

View File

@ -0,0 +1,77 @@
package ninja.bytecode.iris.util;
import java.io.Serializable;
/**
* A Biset
*
* @author cyberpwn
*
* @param <A>
* the first object type
* @param <B>
* the second object type
*/
@SuppressWarnings("hiding")
public class GBiset<A, B> implements Serializable
{
private static final long serialVersionUID = 1L;
private A a;
private B b;
/**
* Create a new Biset
*
* @param a
* the first object
* @param b
* the second object
*/
public GBiset(A a, B b)
{
this.a = a;
this.b = b;
}
/**
* Get the object of the type A
*
* @return the first object
*/
public A getA()
{
return a;
}
/**
* Set the first object
*
* @param a
* the first object A
*/
public void setA(A a)
{
this.a = a;
}
/**
* Get the second object
*
* @return the second object
*/
public B getB()
{
return b;
}
/**
* Set the second object
*
* @param b
*/
public void setB(B b)
{
this.b = b;
}
}

View File

@ -0,0 +1,51 @@
package ninja.bytecode.iris.util;
import java.util.List;
import ninja.bytecode.shuriken.collections.KList;
/**
* Adapts a list of objects into a list of other objects
*
* @author cyberpwn
* @param <FROM>
* the from object in lists (the item INSIDE the list)
* @param <TO>
* the to object in lists (the item INSIDE the list)
*/
public abstract class GListAdapter<FROM, TO>
{
/**
* Adapts a list of FROM to a list of TO
*
* @param from
* the from list
* @return the to list
*/
public List<TO> adapt(List<FROM> from)
{
List<TO> adapted = new KList<TO>();
for(FROM i : from)
{
TO t = onAdapt(i);
if(t != null)
{
adapted.add(onAdapt(i));
}
}
return adapted;
}
/**
* Adapts a list object FROM to TO for use with the adapt method
*
* @param from
* the from object
* @return the to object
*/
public abstract TO onAdapt(FROM from);
}

View File

@ -0,0 +1,12 @@
package ninja.bytecode.iris.util;
import org.bukkit.block.data.BlockData;
public interface IObjectPlacer
{
public int getHighest(int x, int z);
public void set(int x, int y, int z, BlockData d);
public BlockData get(int x, int y, int z);
}

View File

@ -0,0 +1,104 @@
package ninja.bytecode.iris.util;
import java.io.File;
import org.bukkit.util.BlockVector;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.object.IrisObject;
public class ObjectResourceLoader extends ResourceLoader<IrisObject>
{
private ChunkPosition parallaxSize;
public ObjectResourceLoader(File root, String folderName, String resourceTypeName)
{
super(root, folderName, resourceTypeName, IrisObject.class);
}
public ChunkPosition getParallaxSize()
{
lock.lock();
if(parallaxSize == null)
{
int x = 0;
int z = 0;
for(File i : getFolders())
{
for(File j : i.listFiles())
{
if(j.isFile() && j.getName().endsWith(".iob"))
{
try
{
BlockVector b = IrisObject.sampleSize(j);
x = b.getBlockX() > x ? b.getBlockX() : x;
z = b.getBlockZ() > z ? b.getBlockZ() : z;
}
catch(Throwable e)
{
}
}
}
}
x = (Math.max(x, 16) + 16) >> 4;
z = (Math.max(z, 16) + 16) >> 4;
x = x % 2 == 0 ? x + 1 : x;
z = z % 2 == 0 ? z + 1 : z;
parallaxSize = new ChunkPosition(x, z);
Iris.info("Parallax View Distance: " + x + "x" + z);
}
lock.unlock();
return parallaxSize;
}
public IrisObject load(String name)
{
String key = name + "-" + objectClass.getCanonicalName();
if(loadCache.containsKey(key))
{
IrisObject t = loadCache.get(key);
return t;
}
lock.lock();
for(File i : getFolders())
{
for(File j : i.listFiles())
{
if(j.isFile() && j.getName().endsWith(".iob") && j.getName().split("\\Q.\\E")[0].equals(name))
{
try
{
IrisObject t = new IrisObject(0, 0, 0);
t.read(j);
loadCache.put(key, t);
Iris.hotloader.track(j);
Iris.info("Loading " + resourceTypeName + ": " + j.getPath());
t.setLoadKey(name);
lock.unlock();
return t;
}
catch(Throwable e)
{
lock.unlock();
Iris.warn("Couldn't read " + resourceTypeName + " file: " + j.getPath() + ": " + e.getMessage());
}
}
}
}
Iris.warn("Couldn't find " + resourceTypeName + ": " + name);
lock.unlock();
return null;
}
}

View File

@ -0,0 +1,130 @@
package ninja.bytecode.iris.util;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Particle;
import org.bukkit.World;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.Player;
import org.bukkit.material.MaterialData;
/**
* Particle sender using the Bukkit particles API for 1.9+ servers
*
* @author MrMicky
*/
@SuppressWarnings("deprecation")
interface ParticleSender
{
void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data);
Object getParticle(ParticleType particle);
boolean isValidData(Object particle, Object data);
default double color(double color)
{
return color / 255.0;
}
class ParticleSenderImpl implements ParticleSender
{
@Override
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data)
{
Particle bukkitParticle = Particle.valueOf(particle.toString());
if(data instanceof Color)
{
if(particle.getDataType() == Color.class)
{
Color color = (Color) data;
count = 0;
offsetX = color(color.getRed());
offsetY = color(color.getGreen());
offsetZ = color(color.getBlue());
extra = 1.0;
}
data = null;
}
if(receiver instanceof World)
{
((World) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
else if(receiver instanceof Player)
{
((Player) receiver).spawnParticle(bukkitParticle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
}
@Override
public Particle getParticle(ParticleType particle)
{
try
{
return Particle.valueOf(particle.toString());
}
catch(IllegalArgumentException e)
{
return null;
}
}
@Override
public boolean isValidData(Object particle, Object data)
{
return isValidDataBukkit((Particle) particle, data);
}
public boolean isValidDataBukkit(Particle particle, Object data)
{
return particle.getDataType() == Void.class || particle.getDataType().isInstance(data);
}
}
class ParticleSender1_13 extends ParticleSenderImpl
{
@Override
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY, double offsetZ, double extra, Object data)
{
Particle bukkitParticle = Particle.valueOf(particle.toString());
if(bukkitParticle.getDataType() == Particle.DustOptions.class)
{
if(data instanceof Color)
{
data = new Particle.DustOptions((Color) data, 1);
}
else if(data == null)
{
data = new Particle.DustOptions(Color.RED, 1);
}
}
else if(bukkitParticle.getDataType() == BlockData.class && data instanceof MaterialData)
{
data = Bukkit.createBlockData(((MaterialData) data).getItemType());
}
super.spawnParticle(receiver, particle, x, y, z, count, offsetX, offsetY, offsetZ, extra, data);
}
@Override
public boolean isValidDataBukkit(Particle particle, Object data)
{
if(particle.getDataType() == Particle.DustOptions.class && data instanceof Color)
{
return true;
}
if(particle.getDataType() == BlockData.class && data instanceof MaterialData)
{
return true;
}
return super.isValidDataBukkit(particle, data);
}
}
}

View File

@ -0,0 +1,166 @@
package ninja.bytecode.iris.util;
import org.bukkit.Color;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Legacy particle sender with NMS for 1.7/1.8 servers
*
* @author MrMicky
*/
@SuppressWarnings("deprecation")
class ParticleSenderLegacy implements ParticleSender {
private static final boolean SERVER_IS_1_8;
private static final Constructor<?> PACKET_PARTICLE;
private static final Class<?> ENUM_PARTICLE;
private static final Method WORLD_GET_HANDLE;
private static final Method WORLD_SEND_PARTICLE;
private static final Method PLAYER_GET_HANDLE;
private static final Field PLAYER_CONNECTION;
private static final Method SEND_PACKET;
private static final int[] EMPTY = new int[0];
static {
ENUM_PARTICLE = FastReflection.nmsOptionalClass("EnumParticle").orElse(null);
SERVER_IS_1_8 = ENUM_PARTICLE != null;
try {
Class<?> packetParticleClass = FastReflection.nmsClass("PacketPlayOutWorldParticles");
Class<?> playerClass = FastReflection.nmsClass("EntityPlayer");
Class<?> playerConnectionClass = FastReflection.nmsClass("PlayerConnection");
Class<?> worldClass = FastReflection.nmsClass("WorldServer");
Class<?> entityPlayerClass = FastReflection.nmsClass("EntityPlayer");
Class<?> craftPlayerClass = FastReflection.obcClass("entity.CraftPlayer");
Class<?> craftWorldClass = FastReflection.obcClass("CraftWorld");
if (SERVER_IS_1_8) {
PACKET_PARTICLE = packetParticleClass.getConstructor(ENUM_PARTICLE, boolean.class, float.class,
float.class, float.class, float.class, float.class, float.class, float.class, int.class,
int[].class);
WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("sendParticles", entityPlayerClass, ENUM_PARTICLE,
boolean.class, double.class, double.class, double.class, int.class, double.class, double.class,
double.class, double.class, int[].class);
} else {
PACKET_PARTICLE = packetParticleClass.getConstructor(String.class, float.class, float.class, float.class,
float.class, float.class, float.class, float.class, int.class);
WORLD_SEND_PARTICLE = worldClass.getDeclaredMethod("a", String.class, double.class, double.class,
double.class, int.class, double.class, double.class, double.class, double.class);
}
WORLD_GET_HANDLE = craftWorldClass.getDeclaredMethod("getHandle");
PLAYER_GET_HANDLE = craftPlayerClass.getDeclaredMethod("getHandle");
PLAYER_CONNECTION = playerClass.getField("playerConnection");
SEND_PACKET = playerConnectionClass.getMethod("sendPacket", FastReflection.nmsClass("Packet"));
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
@Override
public void spawnParticle(Object receiver, ParticleType particle, double x, double y, double z, int count, double offsetX, double offsetY,
double offsetZ, double extra, Object data) {
try {
int[] datas = toData(particle, data);
if (data instanceof Color) {
if (particle.getDataType() == Color.class) {
Color color = (Color) data;
count = 0;
offsetX = color(color.getRed());
offsetY = color(color.getGreen());
offsetZ = color(color.getBlue());
extra = 1.0;
}
}
if (receiver instanceof World) {
Object worldServer = WORLD_GET_HANDLE.invoke(receiver);
if (SERVER_IS_1_8) {
WORLD_SEND_PARTICLE.invoke(worldServer, null, getEnumParticle(particle), true, x, y, z, count, offsetX, offsetY, offsetZ, extra, datas);
} else {
String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]);
WORLD_SEND_PARTICLE.invoke(worldServer, particleName, x, y, z, count, offsetX, offsetY, offsetZ, extra);
}
} else if (receiver instanceof Player) {
Object packet;
if (SERVER_IS_1_8) {
packet = PACKET_PARTICLE.newInstance(getEnumParticle(particle), true, (float) x, (float) y,
(float) z, (float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count, datas);
} else {
String particleName = particle.getLegacyName() + (datas.length != 2 ? "" : "_" + datas[0] + "_" + datas[1]);
packet = PACKET_PARTICLE.newInstance(particleName, (float) x, (float) y, (float) z,
(float) offsetX, (float) offsetY, (float) offsetZ, (float) extra, count);
}
Object entityPlayer = PLAYER_GET_HANDLE.invoke(receiver);
Object playerConnection = PLAYER_CONNECTION.get(entityPlayer);
SEND_PACKET.invoke(playerConnection, packet);
}
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
@Override
public boolean isValidData(Object particle, Object data) {
return true;
}
@Override
public Object getParticle(ParticleType particle) {
if (!SERVER_IS_1_8) {
return particle.getLegacyName();
}
try {
return getEnumParticle(particle);
} catch (IllegalArgumentException e) {
return null;
}
}
private Object getEnumParticle(ParticleType particleType) {
return FastReflection.enumValueOf(ENUM_PARTICLE, particleType.toString());
}
private int[] toData(ParticleType particle, Object data) {
Class<?> dataType = particle.getDataType();
if (dataType == ItemStack.class) {
if (!(data instanceof ItemStack)) {
return SERVER_IS_1_8 ? new int[2] : new int[]{1, 0};
}
ItemStack itemStack = (ItemStack) data;
return new int[]{itemStack.getType().getId(), itemStack.getDurability()};
}
if (dataType == MaterialData.class) {
if (!(data instanceof MaterialData)) {
return SERVER_IS_1_8 ? new int[1] : new int[]{1, 0};
}
MaterialData materialData = (MaterialData) data;
if (SERVER_IS_1_8) {
return new int[]{materialData.getItemType().getId() + (materialData.getData() << 12)};
} else {
return new int[]{materialData.getItemType().getId(), materialData.getData()};
}
}
return EMPTY;
}
}

View File

@ -0,0 +1,177 @@
package ninja.bytecode.iris.util;
import org.bukkit.Color;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.MaterialData;
/**
* @author MrMicky
*/
@SuppressWarnings("deprecation")
public enum ParticleType {
// 1.7+
EXPLOSION_NORMAL("explode", "poof"),
EXPLOSION_LARGE("largeexplode", "explosion"),
EXPLOSION_HUGE("hugeexplosion", "explosion_emitter"),
FIREWORKS_SPARK("fireworksSpark", "firework"),
WATER_BUBBLE("bubble", "bubble"),
WATER_SPLASH("splash", "splash"),
WATER_WAKE("wake", "fishing"),
SUSPENDED("suspended", "underwater"),
SUSPENDED_DEPTH("depthsuspend", "underwater"),
CRIT("crit", "crit"),
CRIT_MAGIC("magicCrit", "enchanted_hit"),
SMOKE_NORMAL("smoke", "smoke"),
SMOKE_LARGE("largesmoke", "large_smoke"),
SPELL("spell", "effect"),
SPELL_INSTANT("instantSpell", "instant_effect"),
SPELL_MOB("mobSpell", "entity_effect"),
SPELL_MOB_AMBIENT("mobSpellAmbient", "ambient_entity_effect"),
SPELL_WITCH("witchMagic", "witch"),
DRIP_WATER("dripWater", "dripping_water"),
DRIP_LAVA("dripLava", "dripping_lava"),
VILLAGER_ANGRY("angryVillager", "angry_villager"),
VILLAGER_HAPPY("happyVillager", "happy_villager"),
TOWN_AURA("townaura", "mycelium"),
NOTE("note", "note"),
PORTAL("portal", "portal"),
ENCHANTMENT_TABLE("enchantmenttable", "enchant"),
FLAME("flame", "flame"),
LAVA("lava", "lava"),
// FOOTSTEP("footstep", null),
CLOUD("cloud", "cloud"),
REDSTONE("reddust", "dust"),
SNOWBALL("snowballpoof", "item_snowball"),
SNOW_SHOVEL("snowshovel", "item_snowball"),
SLIME("slime", "item_slime"),
HEART("heart", "heart"),
ITEM_CRACK("iconcrack", "item"),
BLOCK_CRACK("blockcrack", "block"),
BLOCK_DUST("blockdust", "block"),
// 1.8+
BARRIER("barrier", "barrier", 8),
WATER_DROP("droplet", "rain", 8),
MOB_APPEARANCE("mobappearance", "elder_guardian", 8),
// ITEM_TAKE("take", null, 8),
// 1.9+
DRAGON_BREATH("dragonbreath", "dragon_breath", 9),
END_ROD("endRod", "end_rod", 9),
DAMAGE_INDICATOR("damageIndicator", "damage_indicator", 9),
SWEEP_ATTACK("sweepAttack", "sweep_attack", 9),
// 1.10+
FALLING_DUST("fallingdust", "falling_dust", 10),
// 1.11+
TOTEM("totem", "totem_of_undying", 11),
SPIT("spit", "spit", 11),
// 1.13+
SQUID_INK(13),
BUBBLE_POP(13),
CURRENT_DOWN(13),
BUBBLE_COLUMN_UP(13),
NAUTILUS(13),
DOLPHIN(13),
// 1.14+
SNEEZE(14),
CAMPFIRE_COSY_SMOKE(14),
CAMPFIRE_SIGNAL_SMOKE(14),
COMPOSTER(14),
FLASH(14),
FALLING_LAVA(14),
LANDING_LAVA(14),
FALLING_WATER(14),
// 1.15+
DRIPPING_HONEY(15),
FALLING_HONEY(15),
LANDING_HONEY(15),
FALLING_NECTAR(15);
private static final int SERVER_VERSION_ID;
static {
String ver = FastReflection.VERSION;
SERVER_VERSION_ID = ver.charAt(4) == '_' ? Character.getNumericValue(ver.charAt(3)) : Integer.parseInt(ver.substring(3, 5));
}
private final String legacyName;
private final String name;
private final int minimumVersion;
// 1.7 particles
ParticleType(String legacyName, String name) {
this(legacyName, name, -1);
}
// 1.13+ particles
ParticleType(int minimumVersion) {
this.legacyName = null;
this.name = name().toLowerCase();
this.minimumVersion = minimumVersion;
}
// 1.8-1.12 particles
ParticleType(String legacyName, String name, int minimumVersion) {
this.legacyName = legacyName;
this.name = name;
this.minimumVersion = minimumVersion;
}
public boolean hasLegacyName() {
return legacyName != null;
}
public String getLegacyName() {
if (!hasLegacyName()) {
throw new IllegalStateException("Particle " + name() + " don't have legacy name");
}
return legacyName;
}
public String getName() {
return name;
}
public boolean isSupported() {
return minimumVersion <= 0 || SERVER_VERSION_ID >= minimumVersion;
}
public Class<?> getDataType() {
switch (this) {
case ITEM_CRACK:
return ItemStack.class;
case BLOCK_CRACK:
case BLOCK_DUST:
case FALLING_DUST:
//noinspection deprecation
return MaterialData.class;
case REDSTONE:
return Color.class;
default:
return Void.class;
}
}
public static ParticleType getParticle(String particleName) {
try {
return ParticleType.valueOf(particleName.toUpperCase());
} catch (IllegalArgumentException e) {
for (ParticleType particle : values()) {
if (particle.getName().equalsIgnoreCase(particleName)) {
return particle;
}
if (particle.hasLegacyName() && particle.getLegacyName().equalsIgnoreCase(particleName)) {
return particle;
}
}
}
return null;
}
}

View File

@ -12,13 +12,13 @@ import ninja.bytecode.shuriken.collections.KMap;
public class ResourceLoader<T extends IrisRegisteredObject>
{
private File root;
private String folderName;
private String resourceTypeName;
private KMap<String, T> loadCache;
private KList<File> folderCache;
private Class<? extends T> objectClass;
private ReentrantLock lock;
protected File root;
protected String folderName;
protected String resourceTypeName;
protected KMap<String, T> loadCache;
protected KList<File> folderCache;
protected Class<? extends T> objectClass;
protected ReentrantLock lock;
public ResourceLoader(File root, String folderName, String resourceTypeName, Class<? extends T> objectClass)
{

View File

@ -0,0 +1,755 @@
package ninja.bytecode.iris.util;
import org.bukkit.Axis;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Entity;
import org.bukkit.util.Vector;
import ninja.bytecode.shuriken.collections.KList;
import ninja.bytecode.shuriken.format.Form;
/**
* Vector utilities
*
* @author cyberpwn
*/
public class VectorMath
{
public static Vector scaleStatic(Axis x, Vector v, double amt)
{
switch(x)
{
case X:
return scaleX(v, amt);
case Y:
return scaleY(v, amt);
case Z:
return scaleZ(v, amt);
}
return null;
}
public static Vector scaleX(Vector v, double amt)
{
double x = v.getX();
double y = v.getY();
double z = v.getZ();
double rx = x == 0 ? 1 : amt / x;
return new Vector(x * rx, y * rx, z * rx);
}
public static Vector scaleY(Vector v, double amt)
{
double x = v.getX();
double y = v.getY();
double z = v.getZ();
double rx = y == 0 ? 1 : amt / y;
return new Vector(x * rx, y * rx, z * rx);
}
public static Vector scaleZ(Vector v, double amt)
{
double x = v.getX();
double y = v.getY();
double z = v.getZ();
double rx = z == 0 ? 1 : amt / z;
return new Vector(x * rx, y * rx, z * rx);
}
public static Vector reverseXZ(Vector v)
{
v.setX(-v.getX());
v.setZ(-v.getZ());
return v;
}
public static boolean isLookingNear(Location a, Location b, double maxOff)
{
Vector perfect = VectorMath.direction(a, b);
Vector actual = a.getDirection();
return perfect.distance(actual) <= maxOff;
}
public static Vector rotate(Direction current, Direction to, Vector v)
{
if(current.equals(to))
{
return v;
}
else if(current.equals(to.reverse()))
{
if(current.isVertical())
{
return new Vector(v.getX(), -v.getY(), v.getZ());
}
else
{
return new Vector(-v.getX(), v.getY(), -v.getZ());
}
}
else
{
Vector c = current.toVector().clone().add(to.toVector());
if(c.getX() == 0)
{
if(c.getY() != c.getZ())
{
return rotate90CX(v);
}
return rotate90CCX(v);
}
else if(c.getY() == 0)
{
if(c.getX() != c.getZ())
{
return rotate90CY(v);
}
return rotate90CCY(v);
}
else if(c.getZ() == 0)
{
if(c.getX() != c.getY())
{
return rotate90CZ(v);
}
return rotate90CCZ(v);
}
}
return v;
}
// Y X 0 0
// X Z 0 0
// 0 X Y 0
// 0 Z X 0
public static Vector rotate(Direction current, Direction to, Vector v, int w, int h, int d)
{
if(current.equals(to))
{
return v;
}
else if(current.equals(to.reverse()))
{
if(current.isVertical())
{
return new Vector(v.getX(), -v.getY() + h, v.getZ());
}
else
{
return new Vector(-v.getX() + w, v.getY(), -v.getZ() + d);
}
}
else
{
Vector c = current.toVector().clone().add(to.toVector());
if(c.getX() == 0)
{
if(c.getY() != c.getZ())
{
return rotate90CX(v, d);
}
return rotate90CCX(v, h);
}
else if(c.getY() == 0)
{
if(c.getX() != c.getZ())
{
return rotate90CY(v, d);
}
return rotate90CCY(v, w);
}
else if(c.getZ() == 0)
{
if(c.getX() != c.getY())
{
return rotate90CZ(v, w);
}
return rotate90CCZ(v, h);
}
}
return v;
}
public static Vector rotate90CX(Vector v)
{
return new Vector(v.getX(), -v.getZ(), v.getY());
}
public static Vector rotate90CCX(Vector v)
{
return new Vector(v.getX(), v.getZ(), -v.getY());
}
public static Vector rotate90CY(Vector v)
{
return new Vector(-v.getZ(), v.getY(), v.getX());
}
public static Vector rotate90CCY(Vector v)
{
return new Vector(v.getZ(), v.getY(), -v.getX());
}
public static Vector rotate90CZ(Vector v)
{
return new Vector(v.getY(), -v.getX(), v.getZ());
}
public static Vector rotate90CCZ(Vector v)
{
return new Vector(-v.getY(), v.getX(), v.getZ());
}
public static Vector rotate90CX(Vector v, int s)
{
return new Vector(v.getX(), -v.getZ() + s, v.getY());
}
public static Vector rotate90CCX(Vector v, int s)
{
return new Vector(v.getX(), v.getZ(), -v.getY() + s);
}
public static Vector rotate90CY(Vector v, int s)
{
return new Vector(-v.getZ() + s, v.getY(), v.getX());
}
public static Vector rotate90CCY(Vector v, int s)
{
return new Vector(v.getZ(), v.getY(), -v.getX() + s);
}
public static Vector rotate90CZ(Vector v, int s)
{
return new Vector(v.getY(), -v.getX() + s, v.getZ());
}
public static Vector rotate90CCZ(Vector v, int s)
{
return new Vector(-v.getY() + s, v.getX(), v.getZ());
}
public static Vector getAxis(Direction current, Direction to)
{
if(current.equals(Direction.U) || current.equals(Direction.D))
{
if(to.equals(Direction.U) || to.equals(Direction.D))
{
return new Vector(1, 0, 0);
}
else
{
if(current.equals(Direction.N) || current.equals(Direction.S))
{
return Direction.E.toVector();
}
else
{
return Direction.S.toVector();
}
}
}
return new Vector(0, 1, 0);
}
private static double round(double value, int precision)
{
return Double.valueOf(Form.f(value, precision));
}
public static Vector clip(Vector v, int decimals)
{
v.setX(round(v.getX(), decimals));
v.setY(round(v.getY(), decimals));
v.setZ(round(v.getZ(), decimals));
return v;
}
public static Vector rotateVectorCC(Vector vec, Vector axis, double deg)
{
double theta = Math.toRadians(deg);
double x, y, z;
double u, v, w;
x = vec.getX();
y = vec.getY();
z = vec.getZ();
u = axis.getX();
v = axis.getY();
w = axis.getZ();
double xPrime = u * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + x * Math.cos(theta) + (-w * y + v * z) * Math.sin(theta);
double yPrime = v * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + y * Math.cos(theta) + (w * x - u * z) * Math.sin(theta);
double zPrime = w * (u * x + v * y + w * z) * (1d - Math.cos(theta)) + z * Math.cos(theta) + (-v * x + u * y) * Math.sin(theta);
return clip(new Vector(xPrime, yPrime, zPrime), 4);
}
/**
* Get all SIMPLE block faces from a more specific block face (SOUTH_EAST) =
* (south, east)
*
* @param f
* the block face
* @return multiple faces, or one if the face is already simple
*/
public static KList<BlockFace> split(BlockFace f)
{
KList<BlockFace> faces = new KList<BlockFace>();
switch(f)
{
case DOWN:
faces.add(BlockFace.DOWN);
break;
case EAST:
faces.add(BlockFace.EAST);
break;
case EAST_NORTH_EAST:
faces.add(BlockFace.EAST);
faces.add(BlockFace.EAST);
faces.add(BlockFace.NORTH);
break;
case EAST_SOUTH_EAST:
faces.add(BlockFace.EAST);
faces.add(BlockFace.EAST);
faces.add(BlockFace.SOUTH);
break;
case NORTH:
faces.add(BlockFace.NORTH);
break;
case NORTH_EAST:
faces.add(BlockFace.NORTH);
faces.add(BlockFace.EAST);
break;
case NORTH_NORTH_EAST:
faces.add(BlockFace.NORTH);
faces.add(BlockFace.NORTH);
faces.add(BlockFace.EAST);
break;
case NORTH_NORTH_WEST:
faces.add(BlockFace.NORTH);
faces.add(BlockFace.NORTH);
faces.add(BlockFace.WEST);
break;
case NORTH_WEST:
faces.add(BlockFace.NORTH);
faces.add(BlockFace.WEST);
break;
case SELF:
faces.add(BlockFace.SELF);
break;
case SOUTH:
faces.add(BlockFace.SOUTH);
break;
case SOUTH_EAST:
faces.add(BlockFace.SOUTH);
faces.add(BlockFace.EAST);
break;
case SOUTH_SOUTH_EAST:
faces.add(BlockFace.SOUTH);
faces.add(BlockFace.SOUTH);
faces.add(BlockFace.EAST);
break;
case SOUTH_SOUTH_WEST:
faces.add(BlockFace.SOUTH);
faces.add(BlockFace.SOUTH);
faces.add(BlockFace.WEST);
break;
case SOUTH_WEST:
faces.add(BlockFace.SOUTH);
faces.add(BlockFace.WEST);
break;
case UP:
faces.add(BlockFace.UP);
break;
case WEST:
faces.add(BlockFace.WEST);
break;
case WEST_NORTH_WEST:
faces.add(BlockFace.WEST);
faces.add(BlockFace.WEST);
faces.add(BlockFace.NORTH);
break;
case WEST_SOUTH_WEST:
faces.add(BlockFace.WEST);
faces.add(BlockFace.WEST);
faces.add(BlockFace.SOUTH);
break;
default:
break;
}
return faces;
}
/**
* Get a normalized vector going from a location to another
*
* @param from
* from here
* @param to
* to here
* @return the normalized vector direction
*/
public static Vector direction(Location from, Location to)
{
return to.clone().subtract(from.clone()).toVector().normalize();
}
public static Vector directionNoNormal(Location from, Location to)
{
return to.clone().subtract(from.clone()).toVector();
}
/**
* Get the vector direction from the yaw and pitch
*
* @param yaw
* the yaw
* @param pitch
* the pitch
* @return the vector
*/
public static Vector toVector(float yaw, float pitch)
{
return new Vector(Math.cos(pitch) * Math.cos(yaw), Math.sin(pitch), Math.cos(pitch) * Math.sin(-yaw));
}
/**
* Add an impulse (force) to an entity
*
* @param e
* the entity
* @param v
* the vector
*/
public static void impulse(Entity e, Vector v)
{
impulse(e, v, 1.0);
}
/**
* Add an impulse (force) on an entity
*
* @param e
* the entity
* @param v
* the vector
* @param effectiveness
* the effectiveness
*/
public static void impulse(Entity e, Vector v, double effectiveness)
{
Vector vx = e.getVelocity();
vx.add(v.clone().multiply(effectiveness));
e.setVelocity(vx);
}
/**
* Reverse a direction
*
* @param v
* the direction
* @return the reversed direction
*/
public static Vector reverse(Vector v)
{
if(v.getX() != 0)
{
v.setX(-v.getX());
}
if(v.getY() != 0)
{
v.setY(-v.getY());
}
if(v.getZ() != 0)
{
v.setZ(-v.getZ());
}
return v;
}
/**
* Get a speed value from a vector (velocity)
*
* @param v
* the vector
* @return the speed
*/
public static double getSpeed(Vector v)
{
Vector vi = new Vector(0, 0, 0);
Vector vt = new Vector(0, 0, 0).add(v);
return vi.distance(vt);
}
/**
* Shift all vectors based on the given vector
*
* @param vector
* the vector direction to shift the vectors
* @param vectors
* the vectors to be shifted
* @return the shifted vectors
*/
public static KList<Vector> shift(Vector vector, KList<Vector> vectors)
{
return new KList<Vector>(new GListAdapter<Vector, Vector>()
{
@Override
public Vector onAdapt(Vector from)
{
return from.add(vector);
}
}.adapt(vectors));
}
/**
* Attempt to get the blockFace for the vector (will be tri-normalized)
*
* @param v
* the vector
* @return the block face or null
*/
public static BlockFace getBlockFace(Vector v)
{
Vector p = triNormalize(v);
for(BlockFace i : BlockFace.values())
{
if(p.getX() == i.getModX() && p.getY() == i.getModY() && p.getZ() == i.getModZ())
{
return i;
}
}
for(BlockFace i : BlockFace.values())
{
if(p.getX() == i.getModX() && p.getZ() == i.getModZ())
{
return i;
}
}
for(BlockFace i : BlockFace.values())
{
if(p.getY() == i.getModY() && p.getZ() == i.getModZ())
{
return i;
}
}
for(BlockFace i : BlockFace.values())
{
if(p.getX() == i.getModX() || p.getY() == i.getModY())
{
return i;
}
}
for(BlockFace i : BlockFace.values())
{
if(p.getX() == i.getModX() || p.getY() == i.getModY() || p.getZ() == i.getModZ())
{
return i;
}
}
return null;
}
/**
* Angle the vector in a self relative direction
*
* @param v
* the initial direction
* @param amt
* the amount to shift in the direction
* @return the shifted direction
*/
public static Vector angleLeft(Vector v, float amt)
{
Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
l.setDirection(v);
float y = l.getYaw();
float p = l.getPitch();
CDou cy = new CDou(360);
CDou cp = new CDou(180);
cy.set(y);
cp.set(p);
cy.sub(amt);
l.setYaw((float) cy.get());
l.setPitch((float) cp.get());
return l.getDirection();
}
/**
* Angle the vector in a self relative direction
*
* @param v
* the initial direction
* @param amt
* the amount to shift in the direction
* @return the shifted direction
*/
public static Vector angleRight(Vector v, float amt)
{
Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
l.setDirection(v);
float y = l.getYaw();
float p = l.getPitch();
CDou cy = new CDou(360);
CDou cp = new CDou(180);
cy.set(y);
cp.set(p);
cy.add(amt);
l.setYaw((float) cy.get());
l.setPitch((float) cp.get());
return l.getDirection();
}
/**
* Angle the vector in a self relative direction
*
* @param v
* the initial direction
* @param amt
* the amount to shift in the direction
* @return the shifted direction
*/
public static Vector angleUp(Vector v, float amt)
{
Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
l.setDirection(v);
float y = l.getYaw();
float p = l.getPitch();
CDou cy = new CDou(360);
cy.set(y);
l.setYaw((float) cy.get());
l.setPitch((float) Math.max(-90, p - amt));
return l.getDirection();
}
/**
* Angle the vector in a self relative direction
*
* @param v
* the initial direction
* @param amt
* the amount to shift in the direction
* @return the shifted direction
*/
public static Vector angleDown(Vector v, float amt)
{
Location l = new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
l.setDirection(v);
float y = l.getYaw();
float p = l.getPitch();
CDou cy = new CDou(360);
cy.set(y);
l.setYaw((float) cy.get());
l.setPitch((float) Math.min(90, p + amt));
return l.getDirection();
}
/**
* (clone) Force normalize the vector into three points, 1, 0, or -1. If the
* value is > 0.333 (1) if the value is less than -0.333 (-1) else 0
*
* @param direction
* the direction
* @return the vector
*/
public static Vector triNormalize(Vector direction)
{
Vector v = direction.clone();
v.normalize();
if(v.getX() > 0.333)
{
v.setX(1);
}
else if(v.getX() < -0.333)
{
v.setX(-1);
}
else
{
v.setX(0);
}
if(v.getY() > 0.333)
{
v.setY(1);
}
else if(v.getY() < -0.333)
{
v.setY(-1);
}
else
{
v.setY(0);
}
if(v.getZ() > 0.333)
{
v.setZ(1);
}
else if(v.getZ() < -0.333)
{
v.setZ(-1);
}
else
{
v.setZ(0);
}
return v;
}
}

View File

@ -0,0 +1,307 @@
package ninja.bytecode.iris.wand;
import java.awt.Color;
import java.util.Iterator;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.Sound;
import org.bukkit.block.Block;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
import ninja.bytecode.iris.Iris;
import ninja.bytecode.iris.object.IrisObject;
import ninja.bytecode.iris.util.Cuboid;
import ninja.bytecode.shuriken.collections.KList;
import ninja.bytecode.shuriken.math.M;
public class WandController implements Listener
{
public WandController()
{
Bukkit.getScheduler().scheduleSyncRepeatingTask(Iris.instance, () ->
{
for(Player i : Bukkit.getOnlinePlayers())
{
tick(i);
}
}, 0, 0);
Bukkit.getPluginManager().registerEvents(this, Iris.instance);
}
public void tick(Player p)
{
try
{
if(isWand(p.getInventory().getItemInMainHand()))
{
Location[] d = getCuboid(p.getInventory().getItemInMainHand());
draw(d, p);
}
}
catch(Throwable e)
{
}
}
public void draw(Location[] d, Player p)
{
Vector gx = Vector.getRandom().subtract(Vector.getRandom()).normalize().clone().multiply(0.65);
d[0].getWorld().spawnParticle(Particle.CRIT_MAGIC, d[0], 1, 0.5 + gx.getX(), 0.5 + gx.getY(), 0.5 + gx.getZ(), 0, null, false);
Vector gxx = Vector.getRandom().subtract(Vector.getRandom()).normalize().clone().multiply(0.65);
d[1].getWorld().spawnParticle(Particle.CRIT, d[1], 1, 0.5 + gxx.getX(), 0.5 + gxx.getY(), 0.5 + gxx.getZ(), 0, null, false);
if(!d[0].getWorld().equals(d[1].getWorld()))
{
return;
}
if(d[0].distanceSquared(d[1]) > 64 * 64)
{
return;
}
int minx = Math.min(d[0].getBlockX(), d[1].getBlockX());
int miny = Math.min(d[0].getBlockY(), d[1].getBlockY());
int minz = Math.min(d[0].getBlockZ(), d[1].getBlockZ());
int maxx = Math.max(d[0].getBlockX(), d[1].getBlockX());
int maxy = Math.max(d[0].getBlockY(), d[1].getBlockY());
int maxz = Math.max(d[0].getBlockZ(), d[1].getBlockZ());
for(double j = minx - 1; j < maxx + 1; j += 0.25)
{
for(double k = miny - 1; k < maxy + 1; k += 0.25)
{
for(double l = minz - 1; l < maxz + 1; l += 0.25)
{
if(M.r(0.2))
{
boolean jj = j == minx || j == maxx;
boolean kk = k == miny || k == maxy;
boolean ll = l == minz || l == maxz;
double aa = j;
double bb = k;
double cc = l;
if((jj && kk) || (jj && ll) || (ll && kk))
{
Vector push = new Vector(0, 0, 0);
if(j == minx)
{
push.add(new Vector(-0.55, 0, 0));
}
if(k == miny)
{
push.add(new Vector(0, -0.55, 0));
}
if(l == minz)
{
push.add(new Vector(0, 0, -0.55));
}
if(j == maxx)
{
push.add(new Vector(0.55, 0, 0));
}
if(k == maxy)
{
push.add(new Vector(0, 0.55, 0));
}
if(l == maxz)
{
push.add(new Vector(0, 0, 0.55));
}
Location lv = new Location(d[0].getWorld(), aa, bb, cc).clone().add(0.5, 0.5, 0.5).clone().add(push);
Color color = Color.getHSBColor((float) (0.5f + (Math.sin((aa + bb + cc + (p.getTicksLived() / 2)) / 20f) / 2)), 1, 1);
int r = color.getRed();
int g = color.getGreen();
int b = color.getBlue();
p.spawnParticle(Particle.REDSTONE, lv.getX(), lv.getY(), lv.getZ(), 1, 0, 0, 0, 0, new Particle.DustOptions(org.bukkit.Color.fromRGB(r, g, b), 0.75f));
}
}
}
}
}
}
@EventHandler
public void on(PlayerInteractEvent e)
{
if(e.getHand().equals(EquipmentSlot.HAND) && isWand(e.getPlayer().getInventory().getItemInMainHand()))
{
if(e.getAction().equals(Action.LEFT_CLICK_BLOCK))
{
e.setCancelled(true);
e.getPlayer().getInventory().setItemInMainHand(update(true, e.getClickedBlock().getLocation(), e.getPlayer().getInventory().getItemInMainHand()));
e.getPlayer().playSound(e.getClickedBlock().getLocation(), Sound.BLOCK_END_PORTAL_FRAME_FILL, 1f, 0.67f);
e.getPlayer().updateInventory();
}
else if(e.getAction().equals(Action.RIGHT_CLICK_BLOCK))
{
e.setCancelled(true);
e.getPlayer().getInventory().setItemInMainHand(update(false, e.getClickedBlock().getLocation(), e.getPlayer().getInventory().getItemInMainHand()));
e.getPlayer().playSound(e.getClickedBlock().getLocation(), Sound.BLOCK_END_PORTAL_FRAME_FILL, 1f, 1.17f);
e.getPlayer().updateInventory();
}
}
}
public static void pasteSchematic(IrisObject s, Location at)
{
s.place(at);
}
public static IrisObject createSchematic(ItemStack wand)
{
if(!isWand(wand))
{
return null;
}
try
{
Location[] f = getCuboid(wand);
Cuboid c = new Cuboid(f[0], f[1]);
IrisObject s = new IrisObject(c.getSizeX(), c.getSizeY(), c.getSizeZ());
Iterator<Block> bb = c.iterator();
while(bb.hasNext())
{
Block b = bb.next();
if(b.getType().equals(Material.AIR))
{
continue;
}
BlockVector bv = b.getLocation().subtract(c.getLowerNE().toVector()).toVector().toBlockVector();
s.setUnsigned(bv.getBlockX(), bv.getBlockY(), bv.getBlockZ(), b.getBlockData());
}
return s;
}
catch(Throwable e)
{
e.printStackTrace();
}
return null;
}
public static Location stringToLocation(String s)
{
try
{
String[] f = s.split("\\Q in \\E");
String[] g = f[0].split("\\Q,\\E");
return new Location(Bukkit.getWorld(f[1]), Integer.valueOf(g[0]), Integer.valueOf(g[1]), Integer.valueOf(g[2]));
}
catch(Throwable e)
{
return null;
}
}
public static String locationToString(Location s)
{
if(s == null)
{
return "<#>";
}
return s.getBlockX() + "," + s.getBlockY() + "," + s.getBlockZ() + " in " + s.getWorld().getName();
}
public static ItemStack createWand()
{
return createWand(null, null);
}
public static ItemStack update(boolean left, Location a, ItemStack item)
{
if(!isWand(item))
{
return item;
}
Location[] f = getCuboid(item);
Location other = left ? f[1] : f[0];
if(other != null && !other.getWorld().getName().equals(a.getWorld().getName()))
{
other = null;
}
return createWand(left ? a : other, left ? other : a);
}
public static ItemStack createWand(Location a, Location b)
{
ItemStack is = new ItemStack(Material.BLAZE_ROD);
is.addUnsafeEnchantment(Enchantment.ARROW_INFINITE, 1);
ItemMeta im = is.getItemMeta();
im.setDisplayName(ChatColor.BOLD + "" + ChatColor.GOLD + "Wand of Iris");
im.setUnbreakable(true);
im.addItemFlags(ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_PLACED_ON, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_DESTROYS, ItemFlag.HIDE_ENCHANTS);
im.setLore(new KList<String>().add(locationToString(a), locationToString(b)));
is.setItemMeta(im);
return is;
}
public static boolean isWand(Player p)
{
ItemStack is = p.getInventory().getItemInMainHand();
return !(is == null || !isWand(is));
}
public static Location[] getCuboid(ItemStack is)
{
ItemMeta im = is.getItemMeta();
return new Location[] {stringToLocation(im.getLore().get(0)), stringToLocation(im.getLore().get(1))};
}
public static boolean isWand(ItemStack item)
{
if(!item.getType().equals(createWand().getType()))
{
return false;
}
if(!item.getItemMeta().getEnchants().equals(createWand().getItemMeta().getEnchants()))
{
return false;
}
if(!item.getItemMeta().getDisplayName().equals(createWand().getItemMeta().getDisplayName()))
{
return false;
}
return true;
}
}