for the full license, including
+ * the MIT license.
+ */
+package org.spout.api;
+
+import com.flowpowered.commons.Named;
+import com.flowpowered.events.EventManager;
+import com.flowpowered.filesystem.FileSystem;
+import org.spout.api.geo.WorldManager;
+import org.spout.api.scheduler.Scheduler;
+
+/**
+ * Represents the core of an implementation of an engine (powers a game).
+ */
+public interface Engine extends Named {
+
+ /**
+ * Gets the version.
+ *
+ * @return build version
+ */
+ public String getVersion();
+
+ public Platform getPlatform();
+
+ /**
+ * Ends this engine instance safely. All worlds, players, and configuration data is saved, and all threads are ended cleanly.
Players will be sent a default disconnect message.
+ *
+ * @return true for for the first stop
+ */
+ public boolean stop();
+
+ /**
+ * Ends this engine instance safely. All worlds, players, and configuration data is saved, and all threads are ended cleanly.
If any players are connected, will kick them with the given reason.
+ *
+ * @param reason for stopping the game instance
+ * @return true for for the first stop
+ */
+ public boolean stop(String reason);
+
+ /**
+ * Returns true if the game is running in debug mode
To start debug mode, start Spout with -debug
+ *
+ * @return true if server is started with the -debug flag, false if not
+ */
+ public boolean debugMode();
+
+ public Scheduler getScheduler();
+
+ /**
+ * Gets an abstract representation of the engine Filesystem. The Filesystem handles the loading of all resources.
On the client, loading a resource will load the resource from the harddrive.
+ * On the server, it will notify all clients to load the resource, as well as provide a representation of that resource.
+ *
+ * @return the filesystem instance
+ */
+ public FileSystem getFileSystem();
+
+ /**
+ * Returns the game's {@link EventManager} Event listener registration and calling is handled through this.
+ *
+ * @return Our EventManager instance
+ */
+ public EventManager getEventManager();
+
+ public WorldManager getWorldManager();
+}
diff --git a/src/main/java/org/spout/api/Platform.java b/src/main/java/org/spout/api/Platform.java
new file mode 100644
index 0000000..4526236
--- /dev/null
+++ b/src/main/java/org/spout/api/Platform.java
@@ -0,0 +1,51 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api;
+
+/**
+ * Platform describes whether the plugin was written for the client, the server, or for both.
+ */
+public enum Platform {
+ SERVER(false, true),
+ CLIENT(true, false),
+ SINGLEPLAYER(true, true);
+
+ private final boolean client, server;
+
+ private Platform(boolean client, boolean server) {
+ this.client = client;
+ this.server = server;
+ }
+
+ public boolean isClient() {
+ return client;
+ }
+
+ public boolean isServer() {
+ return server;
+ }
+}
diff --git a/src/main/java/org/spout/api/Server.java b/src/main/java/org/spout/api/Server.java
new file mode 100644
index 0000000..871d15d
--- /dev/null
+++ b/src/main/java/org/spout/api/Server.java
@@ -0,0 +1,84 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api;
+
+import java.util.Collection;
+
+import org.spout.api.entity.Player;
+import org.spout.api.geo.ServerWorldManager;
+
+/**
+ * Represents the server-specific implementation.
+ */
+public interface Server extends Engine {
+ /**
+ * Gets all players currently online
+ *
+ * @return array of all active players
+ */
+ public Collection getOnlinePlayers();
+
+ /**
+ * Gets the maximum number of players this game can host, or -1 if infinite
+ *
+ * @return max players
+ */
+ public int getMaxPlayers();
+
+ /**
+ * Broadcasts the given message to all players
+ *
+ * The implementation of broadcast is identical to iterating over {@link #getOnlinePlayers()} and invoking {@link Player#sendMessage(String)} for each player.
+ *
+ * @param message to send
+ */
+ public void broadcastMessage(String message);
+
+ /**
+ * Broadcasts the given message to all players
+ *
+ * The implementation of broadcast is identical to calling a {@link org.spout.api.event.server.permissions.PermissionGetAllWithNodeEvent} event, iterating over each element in getReceivers, invoking
+ * {@link org.spout.api.command.CommandSource#sendMessage(String)} for each CommandSource.
+ *
+ * @param permission the permission needed to receive the broadcast
+ * @param message to send
+ */
+ public void broadcastMessage(String permission, String message);
+
+ /**
+ * Gets the {@link Player} by the given username.
If searching for the exact name, this method will iterate and check for exact matches.
Otherwise, this method will iterate
+ * over over all players and find the closest match to the given name, by comparing the length of other player names that start with the given parameter.
This method is case-insensitive.
+ *
+ * @param name to look up
+ * @param exact Whether to use exact lookup
+ * @return Player if found, else null
+ */
+ public Player getPlayer(String name, boolean exact);
+
+ @Override
+ public ServerWorldManager getWorldManager();
+}
diff --git a/src/main/java/org/spout/api/Singleplayer.java b/src/main/java/org/spout/api/Singleplayer.java
new file mode 100644
index 0000000..556f476
--- /dev/null
+++ b/src/main/java/org/spout/api/Singleplayer.java
@@ -0,0 +1,4 @@
+package org.spout.api;
+
+public interface Singleplayer extends Server, Client {
+}
diff --git a/src/main/java/org/spout/api/Spout.java b/src/main/java/org/spout/api/Spout.java
new file mode 100644
index 0000000..7252ae5
--- /dev/null
+++ b/src/main/java/org/spout/api/Spout.java
@@ -0,0 +1,217 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.flowpowered.events.EventManager;
+import com.flowpowered.filesystem.FileSystem;
+
+/**
+ * Represents the Spout core, to get singleton {@link Engine} instance
+ */
+public final class Spout {
+ private static Engine instance = null;
+ private static final Logger logger = Logger.getLogger("Spout");
+
+ private Spout() {
+ throw new IllegalStateException("Can not construct Spout instance");
+ }
+
+ /**
+ * Gets the {@link Logger} instance that is used to write to the console.
+ *
+ * @return logger
+ */
+ public static Logger getLogger() {
+ return logger;
+ }
+
+ /**
+ * Prints the specified object if debug mode is enabled.
+ *
+ * @param obj to print
+ */
+ public static void debug(Object obj) {
+ if (debugMode()) {
+ info(obj.toString());
+ }
+ }
+
+ /**
+ * Logs the specified message to print if debug mode is enabled.
+ *
+ * @param log message
+ * @param t to throw
+ * @see #debugMode()
+ */
+ public static void debug(String log, Throwable t) {
+ if (debugMode()) {
+ info(log, t);
+ }
+ }
+
+ /**
+ * Logs the specified message to print if debug mode is enabled.
+ *
+ * @param log message
+ * @param params of message
+ * @see #debugMode()
+ */
+ public static void debug(String log, Object... params) {
+ if (debugMode()) {
+ info(log, params);
+ }
+ }
+
+ public static void finest(String log, Throwable t) {
+ logger.log(Level.FINEST, log, t);
+ }
+
+ public static void finest(String log, Object... params) {
+ logger.log(Level.FINEST, log, params);
+ }
+
+ public static void finer(String log, Throwable t) {
+ logger.log(Level.FINER, log, t);
+ }
+
+ public static void finer(String log, Object... params) {
+ logger.log(Level.FINER, log, params);
+ }
+
+ public static void fine(String log, Throwable t) {
+ logger.log(Level.FINE, log, t);
+ }
+
+ public static void fine(String log, Object... params) {
+ logger.log(Level.FINE, log, params);
+ }
+
+ public static void info(String log, Throwable t) {
+ logger.log(Level.INFO, log, t);
+ }
+
+ public static void info(String log, Object... params) {
+ logger.log(Level.INFO, log, params);
+ }
+
+ public static void warn(String log, Throwable t) {
+ logger.log(Level.WARNING, log, t);
+ }
+
+ public static void warn(String log, Object... params) {
+ logger.log(Level.WARNING, log, params);
+ }
+
+ public static void severe(String log, Throwable t) {
+ logger.log(Level.SEVERE, log, t);
+ }
+
+ public static void severe(String log, Object... params) {
+ logger.log(Level.SEVERE, log, params);
+ }
+
+ public static void setEngine(Engine game) {
+ if (instance == null) {
+ instance = game;
+ } else {
+ throw new UnsupportedOperationException("Can not redefine singleton Game instance");
+ }
+ }
+
+ /**
+ * Gets the currently running engine instance.
+ *
+ * @return engine
+ */
+ public static Engine getEngine() {
+ return instance;
+ }
+
+ /**
+ * Ends this game instance safely. All worlds, players, and configuration data is saved, and all threads are ended cleanly.
Players will be sent a default disconnect message.
+ */
+ public static void stop() {
+ instance.stop();
+ }
+
+ /**
+ * Returns the game's {@link EventManager} Event listener registration and calling is handled through this.
+ *
+ * @return Our EventManager instance
+ */
+ public static EventManager getEventManager() {
+ return instance.getEventManager();
+ }
+ /**
+ * Returns the {@link Platform} that the game is currently running on.
+ *
+ * @return current platform type
+ */
+ public static Platform getPlatform() {
+ return instance.getPlatform();
+ }
+
+ /**
+ * Returns true if the game is running in debug mode
To start debug mode, start Spout with -debug
+ *
+ * @return true if server is started with the -debug flag, false if not
+ */
+ public static boolean debugMode() {
+ return instance.debugMode();
+ }
+
+ /**
+ * Logs the given string using {@Link Logger#info(String)} to the default logger instance.
+ *
+ * @param arg to log
+ */
+ public static void log(String arg) {
+ logger.info(arg);
+ }
+
+ /**
+ * Returns the String version of the API.
+ *
+ * @return version
+ */
+ public static String getAPIVersion() {
+ return instance.getClass().getPackage().getImplementationVersion();
+ }
+
+ /**
+ * Gets an abstract representation of the engine's {@link FileSystem}.
The Filesystem handles the loading of all resources.
On the client, loading a resource will load the
+ * resource from the harddrive.
On the server, it will notify all clients to load the resource, as well as provide a representation of that resource.
+ *
+ * @return filesystem from the engine.
+ */
+ public static FileSystem getFileSystem() {
+ return instance.getFileSystem();
+ }
+}
diff --git a/src/main/java/org/spout/api/component/BaseComponentOwner.java b/src/main/java/org/spout/api/component/BaseComponentOwner.java
new file mode 100644
index 0000000..57e373d
--- /dev/null
+++ b/src/main/java/org/spout/api/component/BaseComponentOwner.java
@@ -0,0 +1,257 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.component;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.logging.Level;
+
+import com.flowpowered.commons.datatable.ManagedHashMap;
+import com.flowpowered.events.Listener;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.HashBiMap;
+
+import org.spout.api.Spout;
+
+public class BaseComponentOwner implements ComponentOwner {
+ /**
+ * Map of class name, component
+ */
+ private final BiMap, Component> components = HashBiMap.create();
+ private final ManagedHashMap data;
+
+ public BaseComponentOwner() {
+ data = new ManagedHashMap();
+ }
+
+ public BaseComponentOwner(ManagedHashMap data) {
+ this.data = data;
+ }
+
+ /**
+ * For use de-serializing a list of components all at once, without having to worry about dependencies
+ */
+ protected void add(Class extends Component>... components) {
+ HashSet added = new HashSet<>();
+ synchronized (components) {
+ for (Class extends Component> type : components) {
+ if (!this.components.containsKey(type)) {
+ added.add(add(type, false));
+ }
+ }
+ }
+ for (Component type : added) {
+ type.onAttached();
+ }
+ }
+
+ @Override
+ public T add(Class type) {
+ return add(type, true);
+ }
+
+ /**
+ * Adds a component to the map
+ *
+ * @param type to add
+ * @param attach whether to call the component onAttached
+ * @return instantiated component
+ */
+ protected T add(Class type, boolean attach) {
+ return add(type, type, attach);
+ }
+
+ /**
+ * Adds a component to the map
+ *
+ * @param key the component class used as the lookup key
+ * @param type of component to instantiate
+ * @param attach whether to call the component onAttached
+ * @return instantiated component
+ */
+ @SuppressWarnings ("unchecked")
+ protected final T add(Class key, Class extends Component> type, boolean attach) {
+ if (type == null || key == null) {
+ return null;
+ }
+
+ synchronized (components) {
+ T component = (T) components.get(key);
+
+ if (component != null) {
+ return component;
+ }
+
+ try {
+ component = (T) type.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+
+ if (component != null) {
+ try {
+ attachComponent(key, component, attach);
+ } catch (Exception e) {
+ Spout.getLogger().log(Level.SEVERE, "Error while attaching component " + type + ": ", e);
+ }
+ }
+ return component;
+ }
+ }
+
+ protected void attachComponent(Class extends Component> key, Component component, boolean attach) throws Exception {
+ if (component.attachTo(this)) {
+ components.put(key, component);
+ if (attach) {
+ try {
+ component.onAttached();
+ } catch (Exception e) {
+ // Remove the component from the component map if onAttached can't be
+ // called, pass exception to next catch block.
+ components.remove(key);
+ throw e;
+ }
+ }
+ }
+ }
+
+ @Override
+ public T detach(Class extends Component> type) {
+ return detach(type, false);
+ }
+
+ @SuppressWarnings ("unchecked")
+ protected T detach(Class extends Component> type, boolean force) {
+ Preconditions.checkNotNull(type);
+ synchronized (components) {
+ T component = (T) get(type);
+
+ if (component != null && (component.isDetachable() || force)) {
+ components.inverse().remove(component);
+ try {
+ component.onDetached();
+ if (component instanceof Listener) {
+ Spout.getEventManager().unRegisterEvents((Listener) component);
+ }
+ } catch (Exception e) {
+ Spout.getLogger().log(Level.SEVERE, "Error detaching component " + type + " from holder: ", e);
+ }
+ }
+
+ return component;
+ }
+ }
+
+ @SuppressWarnings ("unchecked")
+ @Override
+ public T get(Class type) {
+ Preconditions.checkNotNull(type);
+ Component component = components.get(type);
+
+ if (component == null) {
+ component = findComponent(type);
+ }
+ return (T) component;
+ }
+
+ @Override
+ public T getType(Class type) {
+ Preconditions.checkNotNull(type);
+
+ T component = findComponent(type);
+
+ return component;
+ }
+
+ @SuppressWarnings ("unchecked")
+ @Override
+ public T getExact(Class type) {
+ Preconditions.checkNotNull(type);
+ synchronized (components) {
+ return (T) components.get(type);
+ }
+ }
+
+ @SuppressWarnings ("unchecked")
+ @Override
+ public Collection getAll(Class type) {
+ Preconditions.checkNotNull(type);
+ synchronized (components) {
+ ArrayList matches = new ArrayList<>();
+ for (Component component : components.values()) {
+ if (type.isAssignableFrom(component.getClass())) {
+ matches.add((T) component);
+ }
+ }
+ return matches;
+ }
+ }
+
+ @SuppressWarnings ("unchecked")
+ @Override
+ public Collection getAllOfType(Class type) {
+ Preconditions.checkNotNull(type);
+ synchronized (components) {
+ ArrayList matches = new ArrayList<>();
+ for (Component component : components.values()) {
+ if (type.isAssignableFrom(component.getClass())) {
+ matches.add((T) component);
+ }
+ }
+ return matches;
+ }
+ }
+
+ @Override
+ public Collection values() {
+ synchronized (components) {
+ return new ArrayList<>(components.values());
+ }
+ }
+
+ @Override
+ public ManagedHashMap getData() {
+ return data;
+ }
+
+ @SuppressWarnings ("unchecked")
+ private T findComponent(Class type) {
+ Preconditions.checkNotNull(type);
+ synchronized (components) {
+ for (Component component : values()) {
+ if (type.isAssignableFrom(component.getClass())) {
+ return (T) component;
+ }
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/main/java/org/spout/api/component/BlockComponentOwner.java b/src/main/java/org/spout/api/component/BlockComponentOwner.java
new file mode 100644
index 0000000..01915e8
--- /dev/null
+++ b/src/main/java/org/spout/api/component/BlockComponentOwner.java
@@ -0,0 +1,70 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.component;
+
+import com.flowpowered.commons.datatable.ManagedHashMap;
+
+import org.spout.api.geo.World;
+import org.spout.api.geo.cuboid.Block;
+import org.spout.api.geo.cuboid.Chunk;
+
+// TODO: I don't see why we shouldn't move this to Spout
+public class BlockComponentOwner extends BaseComponentOwner {
+ /**
+ * Stored as world, not chunk, coords
+ */
+ private final int x, y, z;
+ private final World world;
+
+ public BlockComponentOwner(ManagedHashMap chunkData, int x, int y, int z, World world) {
+ super(new ManagedHashMap(chunkData, "" + (x & Chunk.BLOCKS.MASK) + "," + (y & Chunk.BLOCKS.MASK) + "," + (z & Chunk.BLOCKS.MASK)));
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.world = world;
+ }
+
+ public Block getBlock() {
+ return world.getBlock(x, y, z);
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getZ() {
+ return z;
+ }
+
+ public World getWorld() {
+ return world;
+ }
+}
diff --git a/src/main/java/org/spout/api/component/Component.java b/src/main/java/org/spout/api/component/Component.java
new file mode 100644
index 0000000..c3a5bd5
--- /dev/null
+++ b/src/main/java/org/spout/api/component/Component.java
@@ -0,0 +1,106 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.component;
+
+import com.flowpowered.commons.datatable.SerializableMap;
+import org.spout.api.scheduler.tickable.Tickable;
+
+public abstract class Component implements Tickable {
+ private ComponentOwner owner;
+
+ public Component() {
+ }
+
+ @Override
+ public boolean canTick() {
+ return true;
+ }
+
+ @Override
+ public final void tick(float dt) {
+ if (canTick()) {
+ onTick(dt);
+ }
+ }
+
+ @Override
+ public void onTick(float dt) {
+ }
+
+ /**
+ * Attaches to a component owner.
+ *
+ * @param owner the component owner to attach to
+ * @return true if successful
+ */
+ public boolean attachTo(ComponentOwner owner) {
+ this.owner = owner;
+ return true;
+ }
+
+ /**
+ * Gets the component owner that owns this component.
+ *
+ * @return the component owner
+ */
+ public ComponentOwner getOwner() {
+ if (owner == null) {
+ throw new IllegalStateException("Trying to access the owner of this component before it was attached");
+ }
+ return owner;
+ }
+
+ /**
+ * Called when this component is attached to a owner.
+ */
+ public void onAttached() {
+ }
+
+ /**
+ * Called when this component is detached from a owner.
+ */
+ public void onDetached() {
+ }
+
+ /**
+ * Specifies whether or not this component can be detached, after it has already been attached to an owner..
+ *
+ * @return true if it can be detached
+ */
+ public boolean isDetachable() {
+ return true;
+ }
+
+ /**
+ * Gets the {@link SerializableMap} which a ComponentOwner always has This is merely a convenience method.
+ *
+ * @return SerializableMap of the owner
+ */
+ public final SerializableMap getData() {
+ return getOwner().getData();
+ }
+}
diff --git a/src/main/java/org/spout/api/component/ComponentOwner.java b/src/main/java/org/spout/api/component/ComponentOwner.java
new file mode 100644
index 0000000..a5f011a
--- /dev/null
+++ b/src/main/java/org/spout/api/component/ComponentOwner.java
@@ -0,0 +1,106 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.component;
+
+import java.util.Collection;
+
+import com.flowpowered.commons.datatable.ManagedMap;
+
+/**
+ * Represents an object which may own components.
+ */
+public interface ComponentOwner {
+ /**
+ * Adds the component of the specified type to the owner and returns it if it is not present. Otherwise, it returns the component of the specified type if there was one present.
+ *
+ * @param type whose component is to be added to the owner
+ * @return the new component that was added, or the existing one if it had one
+ */
+ public T add(Class type);
+
+ /**
+ * Returns the component of the specified type (or a child implementation) from the owner if it is present.
+ *
+ * @param type whose component is to be returned from the holder
+ * @return the component, or null if one was not found
+ */
+ public T get(Class type);
+
+ /**
+ * Returns all components of the specified type (or a child implementation).
+ *
+ * @param type whose components are to be returned from the owner
+ * @return the component list.
+ */
+ public Collection getAll(Class type);
+
+ /**
+ * Returns all instances of the specified type from the owner if they are present.
+ *
+ * @param type whose components are to be returned from the owner
+ * @return the component list.
+ */
+ public Collection getAllOfType(Class type);
+
+ /**
+ * Returns the component of the specified type (not a child implementation) from the holder if it is present.
+ *
+ * @param type whose component is to be returned from the owner
+ * @return the component, or null if one was not found.
+ */
+ public T getExact(Class type);
+
+ /**
+ * Returns an instance of the specified type from the owner if it is present.
+ *
+ * @param type whose component is to be returned from the owner
+ * @return the component, or null if one was not found
+ */
+ public T getType(Class type);
+
+ /**
+ * Removes the component of the specified type from the owner if it is present.
+ *
+ * @param type whose component is to be removed from the owner
+ * @return the removed component, or null if there was not one
+ */
+ public T detach(Class extends Component> type);
+
+ /**
+ * Gets all components held by the owner.
+ *
+ * @return A collection of held components
+ */
+ public Collection values();
+
+ /**
+ * Gets the {@link ManagedMap} of the owner.
+ *
+ * @return datatable component
+ */
+ public ManagedMap getData();
+}
diff --git a/src/main/java/org/spout/api/component/block/BlockComponent.java b/src/main/java/org/spout/api/component/block/BlockComponent.java
new file mode 100644
index 0000000..e2ab86a
--- /dev/null
+++ b/src/main/java/org/spout/api/component/block/BlockComponent.java
@@ -0,0 +1,87 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.component.block;
+
+import org.spout.api.component.BlockComponentOwner;
+import org.spout.api.component.Component;
+import org.spout.api.component.ComponentOwner;
+import org.spout.api.entity.Entity;
+import org.spout.api.geo.cuboid.Block;
+import org.spout.api.geo.discrete.Point;
+
+public abstract class BlockComponent extends Component {
+ @Override
+ public final boolean attachTo(ComponentOwner owner) {
+ if (!(owner instanceof BlockComponentOwner)) {
+ throw new IllegalStateException("BlockComponents may only be attached to a BlockComponentOwner.");
+ }
+ return super.attachTo(owner);
+ }
+
+ @Override
+ public final BlockComponentOwner getOwner() {
+ return (BlockComponentOwner) super.getOwner();
+ }
+
+ /**
+ * Gets the position of this block component
+ *
+ * @return position
+ */
+ public Point getPoint() {
+ final BlockComponentOwner owner = getOwner();
+ if (owner == null) {
+ throw new IllegalStateException("This component has no owner and therefore no point");
+ }
+ return new Point(owner.getWorld(), owner.getX(), owner.getY(), owner.getZ());
+ }
+
+ /**
+ * Gets the {@link Block} who owns this component.
+ *
+ * The structure of BlockComponents differ from the other {@link ComponentOwner}s. {@link BlockComponentOwner} is what does BlockComponent management but Block itself owns the block. To keep things
+ * easy to access, this convenience method is provided.
+ *
+ * @return the block associated with the BlockComponentOwner
+ */
+ public Block getBlock() {
+ final BlockComponentOwner owner = getOwner();
+ if (owner == null) {
+ throw new IllegalStateException("This component has no owner and therefore no block");
+ }
+ return owner.getBlock();
+ }
+
+ /**
+ * Called when the owning {@link org.spout.api.geo.cuboid.Block} is collided with an {@link Entity}.
+ *
+ * @param point the point where collision occurred.
+ * @param entity the entity that collided with the owner TODO EntityCollideBlockEvent
+ */
+ public void onCollided(Point point, Entity entity) {
+ }
+}
diff --git a/src/main/java/org/spout/api/component/entity/EntityComponent.java b/src/main/java/org/spout/api/component/entity/EntityComponent.java
new file mode 100644
index 0000000..2279256
--- /dev/null
+++ b/src/main/java/org/spout/api/component/entity/EntityComponent.java
@@ -0,0 +1,82 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.component.entity;
+
+import java.util.Random;
+
+import org.spout.api.Engine;
+import org.spout.api.component.Component;
+import org.spout.api.component.ComponentOwner;
+import org.spout.api.entity.Entity;
+import org.spout.math.GenericMath;
+
+/**
+ * Represents a component who shapes the logic behind an {@link Entity}.
+ */
+public abstract class EntityComponent extends Component {
+ @Override
+ public final boolean attachTo(ComponentOwner owner) {
+ if (!(owner instanceof Entity)) {
+ throw new IllegalStateException("EntityComponents may only be attached to Entities.");
+ }
+ return super.attachTo(owner);
+ }
+
+ @Override
+ public Entity getOwner() {
+ return (Entity) super.getOwner();
+ }
+
+ /**
+ * Returns a deterministic random number generator
+ *
+ * @return random the random generator
+ */
+ public final Random getRandom() {
+ return GenericMath.getRandom();
+ }
+
+ public final Engine getEngine() {
+ final Entity owner = getOwner();
+ if (owner == null) {
+ throw new IllegalStateException("Can not access the engine w/o an owner");
+ }
+ return owner.getEngine();
+ }
+
+ /**
+ * Called when the owner comes within range of another owner with an attached {@link ObserverComponent}. TODO EntityObservedEvent
+ */
+ public void onObserved() {
+ }
+
+ /**
+ * Called when the owner is out of range of any owners with attached {@link ObserverComponent}s.
TODO EntityUnObservedEvent
+ */
+ public void onUnObserved() {
+ }
+}
diff --git a/src/main/java/org/spout/api/component/world/WorldComponent.java b/src/main/java/org/spout/api/component/world/WorldComponent.java
new file mode 100644
index 0000000..8798e2b
--- /dev/null
+++ b/src/main/java/org/spout/api/component/world/WorldComponent.java
@@ -0,0 +1,46 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.component.world;
+
+import org.spout.api.component.Component;
+import org.spout.api.component.ComponentOwner;
+import org.spout.api.geo.World;
+
+public abstract class WorldComponent extends Component {
+ @Override
+ public boolean attachTo(ComponentOwner owner) {
+ if (!(owner instanceof World)) {
+ throw new IllegalStateException("WorldComponents are only allowed to be attached to Worlds.");
+ }
+ return super.attachTo(owner);
+ }
+
+ @Override
+ public World getOwner() {
+ return (World) super.getOwner();
+ }
+}
diff --git a/src/main/java/org/spout/api/entity/Entity.java b/src/main/java/org/spout/api/entity/Entity.java
new file mode 100644
index 0000000..d7682ba
--- /dev/null
+++ b/src/main/java/org/spout/api/entity/Entity.java
@@ -0,0 +1,118 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.entity;
+
+import java.util.UUID;
+
+import com.flowpowered.commons.datatable.ManagedMap;
+
+import org.spout.api.Engine;
+import org.spout.api.component.ComponentOwner;
+import org.spout.api.geo.WorldSource;
+import org.spout.api.geo.cuboid.Chunk;
+import org.spout.api.geo.cuboid.Region;
+import org.spout.api.scheduler.tickable.Tickable;
+
+/**
+ * Represents an entity, which may or may not be spawned into the world.
+ */
+public interface Entity extends Tickable, WorldSource, ComponentOwner {
+ /**
+ * Gets the current ID of this entity within the current game session
+ *
+ * @return The entities' id.
+ */
+ public int getId();
+
+ /**
+ * Gets the entity's persistent unique id. Can be used to look up the entity and persists between starts.
+ *
+ * @return persistent {@link UUID}
+ */
+ public UUID getUID();
+
+ /**
+ * Gets the {@link Engine} that spawned and is managing this entity
+ *
+ * @return {@link Engine}
+ */
+ public Engine getEngine();
+
+ /**
+ * Removes the entity. This takes effect at the next snapshot.
+ */
+ public void remove();
+
+ /**
+ * True if the entity is removed.
+ *
+ * @return removed
+ */
+ public boolean isRemoved();
+
+ /**
+ * Sets whether or not the entity should be saved.
+ *
+ * @param savable True if the entity should be saved, false if not
+ */
+ public void setSavable(boolean savable);
+
+ /**
+ * Returns true if this entity should be saved.
+ *
+ * @return savable
+ */
+ public boolean isSavable();
+
+ /**
+ * Gets the {@link Chunk} this entity resides in, or null if removed.
+ *
+ * @return {@link Chunk} the entity is in, or null if removed.
+ */
+ public Chunk getChunk();
+
+ /**
+ * Gets the region the entity is associated and managed with, or null if removed.
+ *
+ * @return {@link Region} the entity is in.
+ */
+ public Region getRegion();
+
+ /**
+ * Creates an immutable snapshot of the entity state at the time the method is called
+ *
+ * @return immutable snapshot
+ */
+ public EntitySnapshot snapshot();
+
+ /**
+ * Gets the {@link ManagedMap} which an Entity always has.
+ *
+ * @return {@link ManagedMap}
+ */
+ public ManagedMap getData();
+}
diff --git a/src/main/java/org/spout/api/entity/EntityPrefab.java b/src/main/java/org/spout/api/entity/EntityPrefab.java
new file mode 100644
index 0000000..e6e21ae
--- /dev/null
+++ b/src/main/java/org/spout/api/entity/EntityPrefab.java
@@ -0,0 +1,46 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.entity;
+
+import java.util.List;
+import java.util.Map;
+
+import com.flowpowered.commons.Named;
+
+import org.spout.api.component.Component;
+import org.spout.api.geo.discrete.Point;
+import org.spout.api.geo.discrete.Transform;
+
+public interface EntityPrefab extends Named {
+ public List> getComponents();
+
+ public Map getData();
+
+ public Entity createEntity(Point point);
+
+ public Entity createEntity(Transform transform);
+}
diff --git a/src/main/java/org/spout/api/entity/EntitySnapshot.java b/src/main/java/org/spout/api/entity/EntitySnapshot.java
new file mode 100644
index 0000000..4533bce
--- /dev/null
+++ b/src/main/java/org/spout/api/entity/EntitySnapshot.java
@@ -0,0 +1,110 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.entity;
+
+import java.util.List;
+import java.util.UUID;
+
+import com.flowpowered.commons.datatable.SerializableMap;
+
+import org.spout.api.component.Component;
+import org.spout.api.geo.discrete.Transform;
+
+/**
+ * Represents a snapshot of an entity state at a specific UTC timestamp, with immutable values
+ */
+public interface EntitySnapshot {
+ /**
+ * Returns the entity reference, if the entity still exists
+ *
+ * @return entity reference if it exists, else null
+ */
+ public Entity getReference();
+
+ /**
+ * Gets the id of the entity. Entity ids' may become invalid if the server has stopped and started. They do not persist across server instances. For persistent ids, use {@link #getUID()}.
+ *
+ * @return id
+ */
+ public int getId();
+
+ /**
+ * Gets the UID for the entity. This id is persistent across server instances, unique to this entity
+ *
+ * @return uid
+ */
+ public UUID getUID();
+
+ /**
+ * Gets the transform for the entity. Note: if the world that the entity was in has been unloaded, the world in the transform will be null.
+ *
+ * @return transform
+ */
+ public Transform getTransform();
+
+ /**
+ * Gets the UUID of the world that the entity was in at the time of this snapshot
+ *
+ * @return uid
+ */
+ public UUID getWorldUID();
+
+ /**
+ * Gets the name of the world that the entity was in at the time of this snapshot
+ *
+ * @return world name
+ */
+ public String getWorldName();
+
+ /**
+ * Gets a copy of the data map for the entity, created at the time of this snapshot
+ *
+ * @return data map
+ */
+ public SerializableMap getDataMap();
+
+ /**
+ * Gets the savable flag for the entity at the time of the snapshot
+ *
+ * @return savable
+ */
+ public boolean isSavable();
+
+ /**
+ * Gets a list of the classes of components attached to this entity
+ *
+ * @return entity
+ */
+ public List> getComponents();
+
+ /**
+ * Gets the UTC system clock time at the time this snapshot was created Equivalent to the output of System.currentTimeMillis()
+ *
+ * @return UTC system time
+ */
+ public long getSnapshotTime();
+}
diff --git a/src/main/java/org/spout/api/entity/Player.java b/src/main/java/org/spout/api/entity/Player.java
new file mode 100644
index 0000000..9ffe6e5
--- /dev/null
+++ b/src/main/java/org/spout/api/entity/Player.java
@@ -0,0 +1,142 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.entity;
+
+import java.util.List;
+
+import org.spout.flow.commands.CommandSender;
+
+public interface Player extends CommandSender {
+ /**
+ * Gets the player's name.
+ *
+ * @return the player's name
+ */
+ public String getName();
+
+ /**
+ * Gets the player's display name.
+ *
+ * @return the player's display name
+ */
+ public String getDisplayName();
+
+ /**
+ * Sets the player's display name.
+ *
+ * @param name the player's new display name
+ */
+ public void setDisplayName(String name);
+
+ /**
+ * Gets if the player is online
+ *
+ * @return true if online
+ */
+ public boolean isOnline();
+
+ /**
+ * Gets if the player has joined before
+ *
+ * @return true if joined before
+ */
+ public boolean hasJoinedBefore();
+
+ /**
+ * Kicks the player without giving a reason, or forcing it.
+ */
+ public void kick();
+
+ /**
+ * Kicks the player for the given reason.
+ *
+ * @param reason the message to send to the player.
+ */
+ public void kick(String reason);
+
+ /**
+ * Bans the player without giving a reason.
+ */
+ public void ban();
+
+ /**
+ * Bans the player for the given reason.
+ *
+ * @param kick whether to kick or not
+ */
+ public void ban(boolean kick);
+
+ /**
+ * Bans the player for the given reason.
+ *
+ * @param kick whether to kick or not
+ * @param reason for ban
+ */
+ public void ban(boolean kick, String reason);
+
+ /**
+ * Immediately saves the players state to disk
+ *
+ * @return true if successful
+ */
+ public boolean save();
+
+ /**
+ * If an entity is set as invisible, it will not be sent to the client.
+ */
+ public void setVisible(Entity entity, boolean visible);
+
+ /**
+ * Retrieves a list of all invisible {@link Entity}'s to the player
+ *
+ * @return {@link List<{@link Entity}>} of invisible {@link Entity}'s
+ */
+ public List getInvisibleEntities();
+
+ /**
+ * Returns true if the {@link Entity} provided is invisible this this {@link Player}
+ *
+ * @param entity Entity to check if invisible to the {@link Player}
+ * @return true if the {@link Entity} is invisible
+ */
+ public boolean isInvisible(Entity entity);
+
+ /**
+ * Sends a command to be processed on the opposite Platform. This is basically a shortcut method to prevent the need to register a command locally with a {@link Command.NetworkSendType} of {@code
+ * SEND}.
+ *
+ * @param command to send
+ * @param args to send
+ */
+ public void sendCommand(String command, String... args);
+
+ /**
+ *
+ * @return the entity that this player is controlling
+ */
+ public Entity getEntity();
+}
diff --git a/src/main/java/org/spout/api/event/cause/BlockCause.java b/src/main/java/org/spout/api/event/cause/BlockCause.java
new file mode 100644
index 0000000..3177eff
--- /dev/null
+++ b/src/main/java/org/spout/api/event/cause/BlockCause.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.event.cause;
+
+import com.flowpowered.events.Cause;
+
+import org.spout.api.geo.cuboid.Block;
+
+public class BlockCause extends Cause {
+ private final Block block;
+
+ public BlockCause(Block block) {
+ this(null, block);
+ }
+
+ public BlockCause(Cause> parent, Block block) {
+ super(parent);
+ this.block = block;
+ }
+
+ @Override
+ public Block getSource() {
+ return block;
+ }
+}
diff --git a/src/main/java/org/spout/api/event/cause/EntityCause.java b/src/main/java/org/spout/api/event/cause/EntityCause.java
new file mode 100644
index 0000000..84a69a9
--- /dev/null
+++ b/src/main/java/org/spout/api/event/cause/EntityCause.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.event.cause;
+
+import com.flowpowered.events.Cause;
+
+import org.spout.api.entity.Entity;
+
+public class EntityCause extends Cause {
+ private final Entity entity;
+
+ public EntityCause(Entity entity) {
+ this(null, entity);
+ }
+
+ public EntityCause(Cause> parent, Entity entity) {
+ super(parent);
+ this.entity = entity;
+ }
+
+ @Override
+ public Entity getSource() {
+ return entity;
+ }
+}
diff --git a/src/main/java/org/spout/api/event/cause/MaterialCause.java b/src/main/java/org/spout/api/event/cause/MaterialCause.java
new file mode 100644
index 0000000..47a15e7
--- /dev/null
+++ b/src/main/java/org/spout/api/event/cause/MaterialCause.java
@@ -0,0 +1,61 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.event.cause;
+
+import com.flowpowered.events.Cause;
+
+import org.spout.api.geo.cuboid.Block;
+import org.spout.api.material.Material;
+
+public class MaterialCause extends Cause {
+ private final T material;
+ private final Block block;
+
+ public MaterialCause(T material, Block block) {
+ this(null, material, block);
+ }
+
+ public MaterialCause(Cause> parent, T material, Block block) {
+ super(parent);
+ this.material = material;
+ this.block = block;
+ }
+
+ @Override
+ public T getSource() {
+ return material;
+ }
+
+ /**
+ * Gets the block that caused the change
+ *
+ * @return block
+ */
+ public Block getBlock() {
+ return block;
+ }
+}
diff --git a/src/main/java/org/spout/api/event/cause/WorldCause.java b/src/main/java/org/spout/api/event/cause/WorldCause.java
new file mode 100644
index 0000000..b63a294
--- /dev/null
+++ b/src/main/java/org/spout/api/event/cause/WorldCause.java
@@ -0,0 +1,49 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.event.cause;
+
+import com.flowpowered.events.Cause;
+
+import org.spout.api.geo.World;
+
+public class WorldCause extends Cause {
+ private final World world;
+
+ public WorldCause(World world) {
+ this.world = world;
+ }
+
+ public WorldCause(Cause> parent, World world) {
+ super(parent);
+ this.world = world;
+ }
+
+ @Override
+ public World getSource() {
+ return world;
+ }
+}
diff --git a/src/main/java/org/spout/api/generator/EmptyWorldGenerator.java b/src/main/java/org/spout/api/generator/EmptyWorldGenerator.java
new file mode 100644
index 0000000..74d41bf
--- /dev/null
+++ b/src/main/java/org/spout/api/generator/EmptyWorldGenerator.java
@@ -0,0 +1,52 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.generator;
+
+import org.spout.api.geo.World;
+import org.spout.api.geo.cuboid.Chunk;
+import org.spout.api.material.BlockMaterial;
+import org.spout.api.util.cuboid.CuboidBlockMaterialBuffer;
+
+/**
+ * Generates an empty world using air blocks
+ */
+public class EmptyWorldGenerator implements WorldGenerator {
+ @Override
+ public void generate(CuboidBlockMaterialBuffer blockData, World world) {
+ blockData.flood(BlockMaterial.AIR);
+ }
+
+ @Override
+ public Populator[] getPopulators() {
+ return new Populator[0];
+ }
+
+ @Override
+ public String getName() {
+ return "EmptyWorld";
+ }
+}
diff --git a/src/main/java/org/spout/api/generator/FlatWorldGenerator.java b/src/main/java/org/spout/api/generator/FlatWorldGenerator.java
new file mode 100644
index 0000000..f3ddbd4
--- /dev/null
+++ b/src/main/java/org/spout/api/generator/FlatWorldGenerator.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.generator;
+
+import org.spout.api.geo.World;
+import org.spout.api.geo.cuboid.Chunk;
+import org.spout.api.material.BlockMaterial;
+import org.spout.api.util.cuboid.CuboidBlockMaterialBuffer;
+
+/**
+ * Generates a flat world of a Spout-colored material
+ */
+public class FlatWorldGenerator implements WorldGenerator {
+ private final BlockMaterial material;
+
+ public FlatWorldGenerator() {
+ material = BlockMaterial.SOLID_BLUE;
+ }
+
+ public FlatWorldGenerator(BlockMaterial material) {
+ this.material = material;
+ }
+
+ @Override
+ public void generate(CuboidBlockMaterialBuffer blockData, World world) {
+ if (blockData.getBase().getY() < 0) {
+ blockData.flood(material);
+ }
+ }
+
+ @Override
+ public Populator[] getPopulators() {
+ return new Populator[0];
+ }
+
+ @Override
+ public String getName() {
+ return "FlatWorld";
+ }
+}
diff --git a/src/main/java/org/spout/api/generator/GeneratorPopulator.java b/src/main/java/org/spout/api/generator/GeneratorPopulator.java
new file mode 100644
index 0000000..d5d12d7
--- /dev/null
+++ b/src/main/java/org/spout/api/generator/GeneratorPopulator.java
@@ -0,0 +1,36 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.generator;
+
+import org.spout.api.util.cuboid.CuboidBlockMaterialBuffer;
+
+/**
+ * Represents a populator for a generator which should operate on a material buffer.
+ */
+public interface GeneratorPopulator {
+ public void populate(CuboidBlockMaterialBuffer blockData, int x, int y, int z, long seed);
+}
diff --git a/src/main/java/org/spout/api/generator/LayeredWorldGenerator.java b/src/main/java/org/spout/api/generator/LayeredWorldGenerator.java
new file mode 100644
index 0000000..71caeac
--- /dev/null
+++ b/src/main/java/org/spout/api/generator/LayeredWorldGenerator.java
@@ -0,0 +1,194 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.generator;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.spout.api.geo.World;
+import org.spout.api.geo.cuboid.Chunk;
+import org.spout.api.material.BlockMaterial;
+import org.spout.api.util.cuboid.CuboidBlockMaterialBuffer;
+
+/**
+ * A world generator that generates using previously-specified layers of blocks
+ */
+public class LayeredWorldGenerator implements WorldGenerator {
+ private List layers = new ArrayList<>();
+ private int minimum = Integer.MAX_VALUE;
+ private int height = Integer.MIN_VALUE;
+ private short floorid = 0, floordata = 0;
+
+ @Override
+ public void generate(CuboidBlockMaterialBuffer blockData, World world) {
+ final int startY = blockData.getBase().getFloorY();
+ final int endY = blockData.getTop().getFloorY();
+ int y, height;
+ for (Layer layer : this.layers) {
+ if (layer.getTop() > startY && layer.getY() < endY) {
+ y = Math.max(startY, layer.getY());
+ height = Math.min(endY, layer.getTop()) - y;
+ blockData.setHorizontalLayer(y, height, layer.getId(), layer.getData());
+ }
+ }
+ // Floor layer
+ if (startY < this.minimum) {
+ height = Math.min(endY, this.minimum) - startY;
+ blockData.setHorizontalLayer(startY, height, this.floorid, this.floordata);
+ }
+ }
+
+ @Override
+ public Populator[] getPopulators() {
+ return new Populator[0];
+ }
+
+ @Override
+ public String getName() {
+ return "LayeredWorld";
+ }
+
+ /**
+ * Gets the total height of all layers
+ *
+ * @return Layer height
+ */
+ public int getHeight() {
+ return this.height;
+ }
+
+ /**
+ * Gets an immutable list of layers specified for this layered World Generator
+ *
+ * @return List of layers
+ */
+ public List getLayers() {
+ return Collections.unmodifiableList(this.layers);
+ }
+
+ /**
+ * Sets the floor layer material, the material for below the lowest layer
By default this layer is full of empty material (air)
+ *
+ * @param material of the layer
+ */
+ protected void setFloorLayer(BlockMaterial material) {
+ this.setFloorLayer(material.getId(), material.getData());
+ }
+
+ /**
+ * Sets the floor layer material, the material for below the lowest layer
By default this layer is full of empty material (air)
+ *
+ * @param id of the material of the layer
+ * @param data of the layer
+ */
+ protected void setFloorLayer(short id, short data) {
+ this.floorid = id;
+ this.floordata = data;
+ }
+
+ /**
+ * Stacks a new layer on top of a previous one
At least one layer added using addLayer should be defined before calling this method
Otherwise the y-coordinate of this layer will be incorrect
+ *
+ * @param height of the new layer
+ * @param material of the layer
+ */
+ protected void stackLayer(int height, BlockMaterial material) {
+ this.addLayer(this.height, height, material);
+ }
+
+ /**
+ * Stacks a new layer on top of a previous one
At least one layer added using addLayer should be defined before calling this method
Otherwise the y-coordinate of this layer will be incorrect
+ *
+ * @param height of the new layer
+ * @param id of the material of the layer
+ * @param data of the layer
+ */
+ protected void stackLayer(int height, short id, short data) {
+ this.addLayer(this.height, height, id, data);
+ }
+
+ /**
+ * Adds a single layer
+ *
+ * @param y - coordinate of the start of the layer
+ * @param height of the layer
+ * @param material of the layer
+ */
+ protected void addLayer(int y, int height, BlockMaterial material) {
+ this.addLayer(y, height, material.getId(), material.getData());
+ }
+
+ /**
+ * Adds a single layer
+ *
+ * @param y - coordinate of the start of the layer
+ * @param height of the layer
+ * @param id of the material of the layer
+ * @param data of the layer
+ */
+ protected void addLayer(int y, int height, short id, short data) {
+ final Layer layer = new Layer(y, height, id, data);
+ this.layers.add(layer);
+ this.height = Math.max(this.height, layer.getTop());
+ this.minimum = Math.min(this.minimum, layer.getY());
+ }
+
+ public static class Layer {
+ private final short id, data;
+ private final int y, height, topy;
+
+ public Layer(int y, int height, short id, short data) {
+ this.y = y;
+ this.height = height;
+ this.topy = y + height;
+ this.id = id;
+ this.data = data;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getTop() {
+ return topy;
+ }
+
+ public short getId() {
+ return id;
+ }
+
+ public short getData() {
+ return data;
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/generator/Populator.java b/src/main/java/org/spout/api/generator/Populator.java
new file mode 100644
index 0000000..63d7c05
--- /dev/null
+++ b/src/main/java/org/spout/api/generator/Populator.java
@@ -0,0 +1,66 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.generator;
+
+import java.util.Random;
+
+import org.spout.api.geo.cuboid.Chunk;
+
+/**
+ * Represents a populator for a chunk
+ */
+public abstract class Populator {
+ private boolean needsClearance;
+
+ public Populator() {
+ this(false);
+ }
+
+ public Populator(boolean needsClearance) {
+ this.needsClearance = needsClearance;
+ }
+
+ public boolean needsClearance() {
+ return needsClearance;
+ }
+
+ /**
+ * Populates the chunk.
+ *
+ * This method may make full use of the block modifying methods of the API.
+ *
+ * This method will be called once per chunk and it is guaranteed that a 2x2x2 cube of chunks containing the chunk will be loaded.
+ *
+ * The chunk to populate is the chunk with the lowest x, y and z coordinates of the cube.
+ *
+ * This allows the populator to create features that cross chunk boundaries.
+ *
+ * @param chunk the chunk to populate
+ * @param random The RNG for this chunk
+ */
+ public abstract void populate(Chunk chunk, Random random);
+}
diff --git a/src/main/java/org/spout/api/generator/WorldGenerator.java b/src/main/java/org/spout/api/generator/WorldGenerator.java
new file mode 100644
index 0000000..e2083e4
--- /dev/null
+++ b/src/main/java/org/spout/api/generator/WorldGenerator.java
@@ -0,0 +1,65 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.generator;
+
+import com.flowpowered.commons.Named;
+
+import org.spout.api.geo.World;
+import org.spout.api.util.cuboid.CuboidBlockMaterialBuffer;
+
+/**
+ * Represents a World generator.
+ *
+ * WorldGenerators are used to generate {@link World}s (surprise surprise)
+ */
+public interface WorldGenerator extends Named {
+ /**
+ * Gets the block structure for a Chunk.
+ *
+ * The CuboidBuffer will always be chunk-aligned, and could be of a variable (chunk) size.
Use {@link CuboidBlockMaterialBuffer#getBase()} and {@link CuboidBlockMaterialBuffer#getTop()} to
+ * obtain the Block bounds in which can be generated.
+ *
+ * It is recommended that seeded random number generators from WorldGeneratorUtils are used.
+ *
+ * @param blockData a zeroed CuboidBuffer which has to be fully generated
+ * @param world in which is generated
+ */
+ public void generate(CuboidBlockMaterialBuffer blockData, World world);
+
+ /**
+ * Gets an array of Populators for the world generator
+ *
+ * @return the Populator array
+ */
+ public Populator[] getPopulators();
+
+ /**
+ * Gets the name of the generator. This name should be unique to prevent two generators overwriting the same world
+ */
+ @Override
+ public String getName();
+}
diff --git a/src/main/java/org/spout/api/generator/WorldGeneratorObject.java b/src/main/java/org/spout/api/generator/WorldGeneratorObject.java
new file mode 100644
index 0000000..5b2066e
--- /dev/null
+++ b/src/main/java/org/spout/api/generator/WorldGeneratorObject.java
@@ -0,0 +1,72 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.generator;
+
+import org.spout.api.geo.World;
+
+/**
+ * Represents an Object for a WorldGenerator
+ */
+public abstract class WorldGeneratorObject {
+ /**
+ * Verify if the object can be placed at the given coordinates.
+ *
+ * @param w The world w.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param z The z coordinate.
+ * @return true if the object can be placed, false if it can't.
+ */
+ public abstract boolean canPlaceObject(World w, int x, int y, int z);
+
+ /**
+ * Place this object into the world at the given coordinates.
+ *
+ * @param w The world w.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param z The z coordinate.
+ */
+ public abstract void placeObject(World w, int x, int y, int z);
+
+ /**
+ * Attempts placement of this object into the world at the given coordinates. The attempt will fail if {@link #canPlaceObject(org.spout.api.geo.World, int, int, int)} returns false.
+ *
+ * @param w The world w.
+ * @param x The x coordinate.
+ * @param y The y coordinate.
+ * @param z The z coordinate.
+ * @return True if the object was placed, false if otherwise.
+ */
+ public boolean tryPlaceObject(World w, int x, int y, int z) {
+ if (canPlaceObject(w, x, y, z)) {
+ placeObject(w, x, y, z);
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/AreaBlockAccess.java b/src/main/java/org/spout/api/geo/AreaBlockAccess.java
new file mode 100644
index 0000000..413cdc1
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/AreaBlockAccess.java
@@ -0,0 +1,275 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo;
+
+import com.flowpowered.events.Cause;
+
+import org.spout.api.geo.cuboid.Block;
+import org.spout.api.material.BlockMaterial;
+import org.spout.api.util.cuboid.CuboidBlockMaterialBuffer;
+import org.spout.math.vector.Vector3f;
+
+public interface AreaBlockAccess extends AreaBlockSource {
+ /**
+ * Sets the data for the block at (x, y, z) to the given data.
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param data to set to
+ * @param cause of the change, or null if non-specific cause
+ */
+ public boolean setBlockData(int x, int y, int z, short data, Cause> cause);
+
+ /**
+ * Adds a value to the data for the block at (x, y, z)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param data to add
+ * @param cause of the change, or null if non-specific cause
+ */
+ public boolean addBlockData(int x, int y, int z, short data, Cause> cause);
+
+ /**
+ * Sets the material and data for the block at (x, y, z) to the given material and data.
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param data value to set to
+ * @param material to set to
+ * @param cause of the change, or null if non-specific cause
+ */
+ public boolean setBlockMaterial(int x, int y, int z, BlockMaterial material, short data, Cause> cause);
+
+ /**
+ * Sets the data of the block at (x, y, z) if the expected state matches
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param expect is the state of the block it expects
+ * @param data to set to if it matches
+ * @param cause of the change, or null if non-specific cause
+ * @return whether setting was successful
+ */
+ public boolean compareAndSetData(int x, int y, int z, int expect, short data, Cause> cause);
+
+ /**
+ * Sets the given bits in the data for the block at (x, y, z)
newData = oldData | (bits)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param bits the bits to set
+ * @return the old data for the block
+ */
+ public short setBlockDataBits(int x, int y, int z, int bits, Cause> cause);
+
+ /**
+ * Sets the given bits in the data for the block at (x, y, z)
newData = oldData | (bits)
or
newData = oldData & ~(bits)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param bits the bits to set or clear
+ * @param set true to set, false to clear
+ * @return the old data for the block
+ */
+ public short setBlockDataBits(int x, int y, int z, int bits, boolean set, Cause> source);
+
+ /**
+ * Clears the given bits in the data for the block at (x, y, z)
newData = oldData & (~bits)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param bits the bits to clear
+ * @param cause of the change, or null if non-specific cause
+ * @return the old data for the block
+ */
+ public short clearBlockDataBits(int x, int y, int z, int bits, Cause> source);
+
+ /**
+ * Gets the data field from the block at (x, y, z)
field = (data & bits) >> (shift)
The shift value used shifts the least significant non-zero bit of bits to the LSB position
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param bits the bits of the field
+ * @return the field value
+ */
+ public int getBlockDataField(int x, int y, int z, int bits);
+
+ /**
+ * Gets if any of the indicated bits are set.
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param bits the bits of the field
+ * @return true if any of the given bits are set
+ */
+ public boolean isBlockDataBitSet(int x, int y, int z, int bits);
+
+ /**
+ * Sets the data field for the block at (x, y, z). This is the reverse operation to the getBlockDataField method.
newData = ((value << shift) & bits) | (oldData & (~bits))
The
+ * shift value used shifts the least significant non-zero bit of bits to the LSB position
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param bits the bits of the field
+ * @param value the new value of the field
+ * @param cause of the change, or null if non-specific cause
+ * @return the old value of the field
+ */
+ public int setBlockDataField(int x, int y, int z, int bits, int value, Cause> source);
+
+ /**
+ * Adds a value to the data field for the block at (x, y, z). This is the reverse operation to the getBlockDataField method.
newData = (((oldData + (value << shift)) & bits) | (oldData &
+ * ~bits))
The shift value used shifts the least significant non-zero bit of bits to the LSB position
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param bits the bits of the field
+ * @param value to add to the value of the field
+ * @return the old value of the field
+ */
+ public int addBlockDataField(int x, int y, int z, int bits, int value, Cause> source);
+
+ /**
+ * Gets if a block is contained in this area
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @return true if it is contained, false if not
+ */
+ public boolean containsBlock(int x, int y, int z);
+
+ /**
+ * Gets a {@link Block} representing the block at (x, y, z)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @return the Block
+ */
+ public Block getBlock(int x, int y, int z);
+
+ /**
+ * Gets a {@link Block} representing the block at (x, y, z)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @return the Block
+ */
+ public Block getBlock(float x, float y, float z);
+
+ /**
+ * Gets a {@link Block} representing the block at the position given
+ *
+ * @param position of the block
+ * @return the Block
+ */
+ public Block getBlock(Vector3f position);
+
+ /**
+ * Atomically sets the cuboid volume to the values inside of the cuboid buffer, if the contents of the buffer's backbuffer matches the world.
+ *
+ * @param cause that is setting the cuboid volume
+ */
+ public boolean commitCuboid(CuboidBlockMaterialBuffer buffer, Cause> cause);
+
+ /**
+ * Atomically sets the cuboid volume to the values inside of the cuboid buffer.
+ *
+ * @param cause that is setting the cuboid volume
+ */
+ public void setCuboid(CuboidBlockMaterialBuffer buffer, Cause> cause);
+
+ /**
+ * Atomically sets the cuboid volume to the values inside of the cuboid buffer with the base located at the given coords
+ *
+ * @param cause that is setting the cuboid volume
+ */
+ public void setCuboid(int x, int y, int z, CuboidBlockMaterialBuffer buffer, Cause> cause);
+
+ /**
+ * Atomically gets the cuboid volume
+ *
+ * @param backBuffer true for a buffer with a back buffer
+ */
+ public CuboidBlockMaterialBuffer getCuboid(boolean backBuffer);
+
+ /**
+ * Atomically gets the cuboid volume with the base located at the given coords of the given size.
The buffer returned contains a back buffer
Note: The block at the base coordinate is inside
+ * the buffer
+ *
+ * @param bx base x-coordinate
+ * @param by base y-coordinate
+ * @param bz base z-coordinate
+ * @param sx size x-coordinate
+ * @param sy size y-coordinate
+ * @param sz size z-coordinate
+ */
+ public CuboidBlockMaterialBuffer getCuboid(int bx, int by, int bz, int sx, int sy, int sz);
+
+ /**
+ * Atomically gets the cuboid volume with the base located at the given coords of the given size.
Note: The block at the base coordinate is inside the buffer
+ *
+ * @param bx base x-coordinate
+ * @param by base y-coordinate
+ * @param bz base z-coordinate
+ * @param sx size x-coordinate
+ * @param sy size y-coordinate
+ * @param sz size z-coordinate
+ * @param backBuffer true for a buffer with a back buffer
+ */
+ public CuboidBlockMaterialBuffer getCuboid(int bx, int by, int bz, int sx, int sy, int sz, boolean backBuffer);
+
+ /**
+ * Atomically gets the cuboid volume with the base located at the given coords and the size of the given buffer.
Note: The block at the base coordinate is inside the
+ *
+ * @param bx base x-coordinate
+ * @param by base y-coordinate
+ * @param bz base z-coordinate
+ */
+ public void getCuboid(int bx, int by, int bz, CuboidBlockMaterialBuffer buffer);
+
+ /**
+ * Atomically gets the cuboid volume contained within the given buffer
+ *
+ * @param buffer the buffer
+ */
+ public void getCuboid(CuboidBlockMaterialBuffer buffer);
+}
diff --git a/src/main/java/org/spout/api/geo/AreaBlockSource.java b/src/main/java/org/spout/api/geo/AreaBlockSource.java
new file mode 100644
index 0000000..2e1145e
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/AreaBlockSource.java
@@ -0,0 +1,61 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo;
+
+import org.spout.api.material.BlockMaterial;
+
+public interface AreaBlockSource {
+ /**
+ * Gets the material for the block at (x, y, z)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @return the block's material from the snapshot
+ */
+ public BlockMaterial getBlockMaterial(int x, int y, int z);
+
+ /**
+ * Gets the packed BlockFullData for the block at (x, y, z). Handler methods are provided by the BlockFullState class.
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @return the block's full state from the snapshot
+ */
+ public int getBlockFullState(int x, int y, int z);
+
+ /**
+ * Gets the data for the block at (x, y, z)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @return the block's data from the snapshot
+ */
+ public short getBlockData(int x, int y, int z);
+}
\ No newline at end of file
diff --git a/src/main/java/org/spout/api/geo/AreaChunkAccess.java b/src/main/java/org/spout/api/geo/AreaChunkAccess.java
new file mode 100644
index 0000000..eb10e68
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/AreaChunkAccess.java
@@ -0,0 +1,120 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo;
+
+import java.util.List;
+
+import org.spout.api.geo.cuboid.Chunk;
+import org.spout.math.vector.Vector3f;
+
+public interface AreaChunkAccess extends AreaBlockAccess {
+ /**
+ * Gets the {@link Chunk} at chunk coordinates (x, y, z)
+ *
+ * @param x coordinate of the chunk
+ * @param y coordinate of the chunk
+ * @param z coordinate of the chunk
+ * @param loadopt to control whether to load and/or generate the chunk, if needed
+ * @return the chunk
+ */
+ public Chunk getChunk(int x, int y, int z, LoadOption loadopt);
+
+ /**
+ * Gets if a chunk is contained in this area
+ *
+ * @param x coordinate of the chunk
+ * @param y coordinate of the chunk
+ * @param z coordinate of the chunk
+ * @return True if it is contained, False if not
+ */
+ public boolean containsChunk(int x, int y, int z);
+
+ /**
+ * Gets the {@link Chunk} at block coordinates (x, y, z)
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @param loadopt to control whether to load and/or generate the chunk, if needed
+ * @return the chunk
+ */
+ public Chunk getChunkFromBlock(int x, int y, int z, LoadOption loadopt);
+
+ /**
+ * Gets the {@link Chunk} at the given position
+ *
+ * @param position of the block
+ * @param loadopt to control whether to load and/or generate the chunk, if needed
+ * @return the chunk
+ */
+ public Chunk getChunkFromBlock(Vector3f position, LoadOption loadopt);
+
+ /**
+ * True if the region has a loaded chunk at the (x, y, z).
+ *
+ * @param x coordinate of the chunk
+ * @param y coordinate of the chunk
+ * @param z coordinate of the chunk
+ * @return true if chunk exists
+ */
+ public boolean hasChunk(int x, int y, int z);
+
+ /**
+ * True if the region has a loaded chunk at the block coordinates (x, y, z).
+ *
+ * @param x coordinate of the block
+ * @param y coordinate of the block
+ * @param z coordinate of the block
+ * @return true if chunk exists
+ */
+ public boolean hasChunkAtBlock(int x, int y, int z);
+
+ /**
+ * Queues a chunk for saving at the next available opportunity.
+ *
+ * @param x coordinate of the chunk
+ * @param y coordinate of the chunk
+ * @param z coordinate of the chunk
+ */
+ public void saveChunk(int x, int y, int z);
+
+ /**
+ * Unloads a chunk, and queues it for saving, if requested.
+ *
+ * @param x coordinate of the chunk
+ * @param y coordinate of the chunk
+ * @param z coordinate of the chunk
+ */
+ public void unloadChunk(int x, int y, int z, boolean save);
+
+ /**
+ * Gets the number of currently loaded chunks
+ *
+ * @return number of loaded chunks
+ */
+ public int getNumLoadedChunks();
+}
diff --git a/src/main/java/org/spout/api/geo/AreaRegionAccess.java b/src/main/java/org/spout/api/geo/AreaRegionAccess.java
new file mode 100644
index 0000000..a0bd931
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/AreaRegionAccess.java
@@ -0,0 +1,121 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo;
+
+import java.util.Collection;
+
+import org.spout.api.geo.cuboid.Region;
+import org.spout.math.vector.Vector3f;
+
+public interface AreaRegionAccess extends AreaChunkAccess {
+ /**
+ * Gets an unmodifiable collection of all loaded regions
+ *
+ * @return all loaded regions
+ */
+ public Collection getRegions();
+
+ /**
+ * Gets the {@link Region} at region coordinates (x, y, z)
+ *
+ * @param x the region x coordinate
+ * @param y the region y coordinate
+ * @param z the region z coordinate
+ * @return the region
+ */
+ public Region getRegion(int x, int y, int z);
+
+ /**
+ * Gets the {@link Region} at region coordinates (x, y, z)
+ *
+ * @param x the region x coordinate
+ * @param y the region y coordinate
+ * @param z the region z coordinate
+ * @param loadopt to control whether to load and/or generate the region, if needed
+ * @return the region
+ */
+ public Region getRegion(int x, int y, int z, LoadOption loadopt);
+
+ /**
+ * Gets the {@link Region} at chunk coordinates (x, y, z)
+ *
+ * @param x the chunk x coordinate
+ * @param y the chunk y coordinate
+ * @param z the chunk z coordinate
+ * @return the region
+ */
+ public Region getRegionFromChunk(int x, int y, int z);
+
+ /**
+ * Gets the {@link Region} at chunk coordinates (x, y, z)
+ *
+ * @param x the chunk x coordinate
+ * @param y the chunk y coordinate
+ * @param z the chunk z coordinate
+ * @param loadopt to control whether to load and/or generate the region, if needed
+ * @return the region
+ */
+ public Region getRegionFromChunk(int x, int y, int z, LoadOption loadopt);
+
+ /**
+ * Gets the {@link Region} at block coordinates (x, y, z)
+ *
+ * @param x the block x coordinate
+ * @param y the block y coordinate
+ * @param z the block z coordinate
+ * @return the region
+ */
+ public Region getRegionFromBlock(int x, int y, int z);
+
+ /**
+ * Gets the {@link Region} at block coordinates (x, y, z)
+ *
+ * @param x the block x coordinate
+ * @param y the block y coordinate
+ * @param z the block z coordinate
+ * @param loadopt to control whether to load and/or generate the region, if needed
+ * @return the region
+ */
+ public Region getRegionFromBlock(int x, int y, int z, LoadOption loadopt);
+
+ /**
+ * Gets the {@link Region} at block coordinates (x, y, z)
+ *
+ * @param position of the block
+ * @return the region
+ */
+ public Region getRegionFromBlock(Vector3f position);
+
+ /**
+ * Gets the {@link Region} at block coordinates (x, y, z)
+ *
+ * @param position of the block
+ * @param loadopt to control whether to load and/or generate the region, if needed
+ * @return the region
+ */
+ public Region getRegionFromBlock(Vector3f position, LoadOption loadopt);
+}
diff --git a/src/main/java/org/spout/api/geo/LoadOption.java b/src/main/java/org/spout/api/geo/LoadOption.java
new file mode 100644
index 0000000..27d5b87
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/LoadOption.java
@@ -0,0 +1,79 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo;
+
+public class LoadOption {
+ /**
+ * Do not load or generate chunk/region if not currently loaded
+ */
+ public static final LoadOption NO_LOAD = new LoadOption(false, false, true);
+ /**
+ * Load chunk/region if not currently loaded, but do not generate it if it does not yet exist
+ */
+ public static final LoadOption LOAD_ONLY = new LoadOption(true, false, true);
+ /**
+ * Load chunk/region if not currently loaded, and generate it if it does not yet exist
+ */
+ public static final LoadOption LOAD_GEN = new LoadOption(true, true, true);
+ /**
+ * Don't load the chunk if it has already been generated, only generate if it does not yet exist
+ */
+ public static final LoadOption GEN_ONLY = new LoadOption(false, true, true);
+
+ private final boolean load;
+ private final boolean generate;
+ private final boolean wait;
+
+ private LoadOption(boolean load, boolean generate, boolean wait) {
+ this.load = load;
+ this.generate = generate;
+ this.wait = wait;
+ }
+
+ /**
+ * Test if chunk/region should be loaded if not currently loaded
+ *
+ * @return true if yes, false if no
+ */
+ public final boolean loadIfNeeded() {
+ return load;
+ }
+
+ /**
+ * Test if chunk/region should be generated if it does not exist
+ *
+ * @return true if yes, false if no
+ */
+ public final boolean generateIfNeeded() {
+ return generate;
+ }
+
+ public boolean isWait() {
+ return wait;
+ }
+
+}
diff --git a/src/main/java/org/spout/api/geo/LocalAreaAccess.java b/src/main/java/org/spout/api/geo/LocalAreaAccess.java
new file mode 100644
index 0000000..f510753
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/LocalAreaAccess.java
@@ -0,0 +1,66 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo;
+
+import org.spout.api.geo.cuboid.Chunk;
+import org.spout.api.geo.cuboid.Region;
+import org.spout.api.material.block.BlockFace;
+
+public interface LocalAreaAccess {
+ /**
+ * Gets a neighbouring region. Only the 3x3x3 cube of regions centered on this region can be obtained by this method.
+ */
+ public Region getLocalRegion(BlockFace face, LoadOption loadopt);
+
+ /**
+ * Gets a neighbouring region. The coordinates provided range from 0 to 2, rather than -1 to +1. If all 3 coordinates are 1, then this region is returned.
+ */
+ public Region getLocalRegion(int dx, int dy, int dz, LoadOption loadopt);
+
+ /**
+ * Gets a chunk relative to a given chunk. The given chunk must be in this region and the requested chunk must be in the 3x3x3 cube of regions centred on this region.
+ */
+ public Chunk getLocalChunk(Chunk c, BlockFace face, LoadOption loadopt);
+
+ /**
+ * Gets a chunk relative to a given chunk. The given chunk must be in this region and the requested chunk must be in the 3x3x3 cube of regions centred on this region.
(ox, oy, oz) is the
+ * offset to the desired chunk. The coordinates of the offset can not have a magnitude greater than 16.
+ */
+ public Chunk getLocalChunk(Chunk c, int ox, int oy, int oz, LoadOption loadopt);
+
+ /**
+ * Gets a chunk relative to given chunk coordinates. The given chunk must be in this region and the requested chunk must be in the 3x3x3 cube of regions centred on this region.
(x, y, z)
+ * are the coordinates of a chunk in this region.
(ox, oy, oz) is the offset to the desired chunk. The coordinates of the offset can not have a magnitude greater than 16.
+ */
+ public Chunk getLocalChunk(int x, int y, int z, int ox, int oy, int oz, LoadOption loadopt);
+
+ /**
+ * Gets a chunk in the 3x3x3 cube of regions centered on this region.
The valid range for the (x, y, z) coordinates is -16 to 31.
To request a chunk in this region, all three
+ * coordinates must be in the range of 0 to 15.
+ */
+ public Chunk getLocalChunk(int x, int y, int z, LoadOption loadopt);
+}
diff --git a/src/main/java/org/spout/api/geo/ServerWorld.java b/src/main/java/org/spout/api/geo/ServerWorld.java
new file mode 100644
index 0000000..550c5b2
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/ServerWorld.java
@@ -0,0 +1,66 @@
+package org.spout.api.geo;
+
+import java.io.File;
+import java.util.List;
+import org.spout.api.generator.WorldGenerator;
+import org.spout.api.geo.discrete.Transform;
+import org.spout.math.vector.Vector3f;
+
+public interface ServerWorld extends World {
+
+ /**
+ * Gets the {@link WorldGenerator} responsible for generating new chunks for this world
+ *
+ * @return generator
+ */
+ WorldGenerator getGenerator();
+
+ /**
+ * Gets the world's seed. This value is immutable and set at world creation
+ *
+ * @return the seed
+ */
+ long getSeed();
+
+ // Techinically server-only
+ /**
+ * Gets the world's spawn point
+ *
+ * @return the spawn point
+ */
+ Transform getSpawnPoint();
+
+ /**
+ * Sets the world's spawn point
+ *
+ * @param transform the Transform of the spawn point
+ */
+ void setSpawnPoint(Transform transform);
+
+ /**
+ * Unloads the world from the server. Undefined behavior will occur if any players are currently alive on the world while it is being unloaded.
+ */
+ void unload(boolean save);
+
+ /**
+ * Saves all world data to world data file. Note: World data does not include chunks, regions, or other data. World data pertains to world age, world name, and world data maps.
+ */
+ public void save();
+
+ public File getDirectory();
+
+ /**
+ * Queues a list of chunks for generation. The Vector3 values are in chunk coords.
+ *
+ * @param chunks a list of chunk coordinates
+ */
+ public void queueChunksForGeneration(List chunks);
+
+ /**
+ * Queues a chunk for generation. The Vector3 value is in chunk coords.
+ *
+ * @param chunk chunk coordinates
+ */
+ public void queueChunkForGeneration(Vector3f chunk);
+
+}
diff --git a/src/main/java/org/spout/api/geo/ServerWorldManager.java b/src/main/java/org/spout/api/geo/ServerWorldManager.java
new file mode 100644
index 0000000..c07deb2
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/ServerWorldManager.java
@@ -0,0 +1,75 @@
+package org.spout.api.geo;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.List;
+
+import org.spout.api.generator.WorldGenerator;
+
+public interface ServerWorldManager extends WorldManager {
+
+ /**
+ * Gets the default world generator for this game. Specific generators can be specified when loading new worlds.
+ *
+ * @return default world generator.
+ */
+ public WorldGenerator getDefaultGenerator();
+
+ /**
+ * Loads a {@link World} with the given name and {@link WorldGenerator}
If the world doesn't exist on disk, it creates it.
if the world is already loaded, this functions the same as
+ * {@link #getWorld(String)}
+ *
+ * @param name Name of the world
+ * @param generator World Generator
+ * @return {@link World} loaded or created.
+ */
+ public World loadWorld(String name, WorldGenerator generator);
+
+ /**
+ * Unloads this world from memory.
Note: Worlds can not be unloaded if players are currently on them.
+ *
+ * @param name of the world to unload
+ * @param save whether or not to save the world state to file
+ * @return true if the world was unloaded, false if not
+ */
+ public boolean unloadWorld(String name, boolean save);
+
+ /**
+ * Unloads this world from memory.
Note: Worlds can not be unloaded if players are currently on them.
+ *
+ * @param world to unload
+ * @param save whether or not to save the world state to file
+ * @return true if the world was unloaded, false if not
+ */
+ public boolean unloadWorld(ServerWorld world, boolean save);
+
+ /**
+ * Initiates a save of the server state, including configuration files.
It will save the state of the world, if specificed, and the state of players, if specified.
+ *
+ * @param worlds true to save the state of all active worlds
+ * @param players true to save the state of all active players
+ */
+ public void save(boolean worlds, boolean players);
+
+ /**
+ * Gets the world folders which match the world name.
+ *
+ * @param worldName to match the world folders with
+ * @return the world folders that match the world name
+ */
+ public Collection matchWorldFolder(String worldName);
+
+ /**
+ * Gets all the individual world folders where world data is stored.
This includes offline worlds.
+ *
+ * @return a list of available world folders
+ */
+ public List getWorldFolders();
+
+ /**
+ * Gets the folder that contains the world save data.
If the folder is unusued, the file path will be '.'
+ *
+ * @return world folder
+ */
+ public File getWorldFolder();
+}
diff --git a/src/main/java/org/spout/api/geo/World.java b/src/main/java/org/spout/api/geo/World.java
new file mode 100644
index 0000000..0656f86
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/World.java
@@ -0,0 +1,352 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo;
+
+import java.io.File;
+import java.util.List;
+import java.util.UUID;
+
+import com.flowpowered.commons.Named;
+import com.flowpowered.commons.datatable.ManagedMap;
+import com.flowpowered.events.Cause;
+
+import org.spout.api.Engine;
+import org.spout.api.component.Component;
+import org.spout.api.component.ComponentOwner;
+import org.spout.api.entity.Entity;
+import org.spout.api.entity.EntityPrefab;
+import org.spout.api.entity.Player;
+import org.spout.api.generator.WorldGenerator;
+import org.spout.api.geo.discrete.Point;
+import org.spout.api.geo.discrete.Transform;
+import org.spout.api.scheduler.TaskManager;
+import org.spout.api.util.cuboid.CuboidBlockMaterialBuffer;
+
+/**
+ * Represents a World.
+ */
+public interface World extends AreaRegionAccess, Named, ComponentOwner {
+ /**
+ * Gets the name of the world
+ *
+ * @return the name of the world
+ */
+ @Override
+ public String getName();
+
+ /**
+ * Gets the age of the world in ms. This count cannot be modified, and increments on every tick.
+ *
+ * @return the world's age in ms
+ */
+ public long getAge();
+
+ /**
+ * Gets the UID representing the world. With extremely high probability the UID is unique to each world.
+ *
+ * @return the name of the world
+ */
+ public UUID getUID();
+
+ /**
+ * Gets the entity with the matching unique id Performs a search on each region for the entity, stopping when it is found, or after all the worlds have been searched upon failure.
+ *
+ * @param uid to search and match
+ * @return entity that matched the uid, or null if none was found
+ */
+ public Entity getEntity(UUID uid);
+
+ /**
+ * Creates a new {@link Entity} at the {@link Point} with the {@link Component} classes attached.
+ *
+ * @param point The area in space where spawn will occur
+ * @param classes The classes to attach
+ * @return The entity set to spawn at the point provided with components attached
+ */
+ public Entity createEntity(Point point, Class extends Component>... classes);
+
+ /**
+ * Creates a new {@link Entity} at the {@link Point} blueprinted with the {@link EntityPrefab} provided.
+ *
+ * @param point The area in space where spawn will occur
+ * @param prefab The blueprint
+ * @return The entity set to spawn at the point provided with the prefab applied
+ */
+ public Entity createEntity(Point point, EntityPrefab prefab);
+
+ /**
+ * Spawns the {@link Entity}.
+ *
+ * @param e Entity to spawn
+ */
+ public void spawnEntity(Entity e);
+
+ /**
+ * Creates and spawns an {@link Entity} at the {@link Point} blueprinted with the {@link EntityPrefab} provided.
The {@link LoadOption} parameter is used to tell Spout if it should load, create
+ * and load, or not load the chunk for the point provided. Great caution should be used; only load (and more so create) if absolutely necessary.
+ *
+ * @param point The area in space to spawn
+ * @param option Whether to not load, load, or load and create the chunk
+ * @param prefab The blueprint
+ * @return The spawned entity at the point with the prefab applied
+ */
+ public Entity createAndSpawnEntity(Point point, LoadOption option, EntityPrefab prefab);
+
+ /**
+ * Creates and spawns an {@link Entity} at the {@link Point} with the {@link Component} classes attached.
The {@link LoadOption} parameter is used to tell Spout if it should load, create and
+ * load, or not load the chunk for the point provided. Great caution should be used; only load (and more so create) if absolutely necessary.
+ *
+ * @param point The area in space to spawn
+ * @param option Whether to not load, load, or load and create the chunk
+ * @param classes The classes to attach
+ * @return The spawned entity at the point with the components attached
+ */
+ public Entity createAndSpawnEntity(Point point, LoadOption option, Class extends Component>... classes);
+
+ /**
+ * Creates and spawns multiple {@link Entity} at the {@link Point}s with the {@link Component} classes attached.
The {@link LoadOption} parameter is used to tell Spout if it should load, create
+ * and load, or not load the chunk for the points provided. Great caution should be used; only load (and more so create) if absolutely necessary.
+ *
+ * @param points The areas in space to spawn
+ * @param option Whether to not load, load, or load and create the chunk
+ * @param classes The classes to attach
+ * @return The spawned entities at the points with the components attached
+ */
+ public Entity[] createAndSpawnEntity(Point[] points, LoadOption option, Class extends Component>... classes);
+
+ /**
+ * Gets the engine associated with this world
+ *
+ * @return the engine
+ */
+ public Engine getEngine();
+
+ /**
+ * Gets all entities with the specified type.
+ *
+ * @return A collection of entities with the specified type.
+ */
+ public List getAll();
+
+ /**
+ * Gets an entity by its id.
+ *
+ * @param id The id.
+ * @return The entity, or {@code null} if it could not be found.
+ */
+ public Entity getEntity(int id);
+
+ /**
+ * Gets the task manager responsible for parallel region tasks.
All tasks are submitted to all loaded regions at the start of the next tick.
Repeating tasks are also submitted to
+ * all new regions when they are created.
Repeated tasks are NOT guaranteed to happen in the same tick for all regions, as each task is submitted individually to each region.
This task
+ * manager does not support async tasks.
If the Runnable for the task is a ParallelRunnable, then a new instance of the Runnable will be created for each region.
+ *
+ * @return the parallel task manager for the engine
+ */
+ public TaskManager getParallelTaskManager();
+
+ /**
+ * Gets the TaskManager associated with this world
+ *
+ * @return task manager
+ */
+ public abstract TaskManager getTaskManager();
+
+ /**
+ * Gets a list of nearby entities of the point, inside of the range
+ *
+ * @param position of the center
+ * @param ignore Entity to ignore
+ * @param range to look for
+ * @return the list of nearby entities (or empty if none)
+ */
+ public List getNearbyEntities(Point position, Entity ignore, int range);
+
+ /**
+ * Gets a set of nearby players to the point, inside of the range
+ *
+ * @param position of the center
+ * @param range to look for
+ * @return A set of nearby Players
+ */
+ public List getNearbyEntities(Point position, int range);
+
+ /**
+ * Gets a set of nearby players to the entity, inside of the range
+ *
+ * @param entity marking the center and which is ignored
+ * @param range to look for
+ * @return A set of nearby Players
+ */
+ public List getNearbyEntities(Entity entity, int range);
+
+ /**
+ * Gets the absolute closest player from the specified point within a specified range.
+ *
+ * @param position to search from
+ * @param ignore to ignore while searching
+ * @param range to search
+ * @return nearest player
+ */
+ public Entity getNearestEntity(Point position, Entity ignore, int range);
+
+ /**
+ * Gets the absolute closest player from the specified point within a specified range.
+ *
+ * @param position center of search
+ * @param range to search
+ * @return nearest player
+ */
+ public Entity getNearestEntity(Point position, int range);
+
+ /**
+ * Gets the absolute closest player from the specified point within a specified range.
+ *
+ * @param entity to search from
+ * @param range to search
+ * @return nearest player
+ */
+ public Entity getNearestEntity(Entity entity, int range);
+
+ /**
+ * Gets a set of nearby players to the point, inside of the range. The search will ignore the specified entity.
+ *
+ * @param position of the center
+ * @param ignore Entity to ignore
+ * @param range to look for
+ * @return A set of nearby Players
+ */
+ public List getNearbyPlayers(Point position, Player ignore, int range);
+
+ /**
+ * Gets a set of nearby players to the point, inside of the range
+ *
+ * @param position of the center
+ * @param range to look for
+ * @return A set of nearby Players
+ */
+ public List getNearbyPlayers(Point position, int range);
+
+ /**
+ * Gets a set of nearby players to the entity, inside of the range
+ *
+ * @param entity marking the center and which is ignored
+ * @param range to look for
+ * @return A set of nearby Players
+ */
+ public List getNearbyPlayers(Entity entity, int range);
+
+ /**
+ * Gets the absolute closest player from the specified point within a specified range.
+ *
+ * @param position to search from
+ * @param ignore to ignore while searching
+ * @param range to search
+ * @return nearest player
+ */
+ public Player getNearestPlayer(Point position, Player ignore, int range);
+
+ /**
+ * Gets the absolute closest player from the specified point within a specified range.
+ *
+ * @param position center of search
+ * @param range to search
+ * @return nearest player
+ */
+ public Player getNearestPlayer(Point position, int range);
+
+ /**
+ * Gets the absolute closest player from the specified point within a specified range.
+ *
+ * @param entity to search from
+ * @param range to search
+ * @return nearest player
+ */
+ public Player getNearestPlayer(Entity entity, int range);
+
+ /**
+ * Atomically sets the cuboid volume to the values inside of the cuboid buffer.
+ *
+ * @param cause that is setting the cuboid volume
+ */
+ @Override
+ public void setCuboid(CuboidBlockMaterialBuffer buffer, Cause> cause);
+
+ /**
+ * Atomically sets the cuboid volume to the values inside of the cuboid buffer with the base located at the given coords
+ *
+ * @param cause that is setting the cuboid volume
+ */
+ @Override
+ public void setCuboid(int x, int y, int z, CuboidBlockMaterialBuffer buffer, Cause> cause);
+
+ /**
+ * Atomically gets the cuboid volume with the base located at the given coords of the given size.
Note: The block at the base coordinate is inside the
+ *
+ * @param bx base x-coordinate
+ * @param by base y-coordinate
+ * @param bz base z-coordinate
+ * @param sx size x-coordinate
+ * @param sy size y-coordinate
+ * @param sz size z-coordinate
+ */
+ @Override
+ public CuboidBlockMaterialBuffer getCuboid(int bx, int by, int bz, int sx, int sy, int sz);
+
+ /**
+ * Atomically gets the cuboid volume with the base located at the given coords and the size of the given buffer.
Note: The block at the base coordinate is inside the
+ *
+ * @param bx base x-coordinate
+ * @param by base y-coordinate
+ * @param bz base z-coordinate
+ */
+ @Override
+ public void getCuboid(int bx, int by, int bz, CuboidBlockMaterialBuffer buffer);
+
+ /**
+ * Atomically gets the cuboid volume contained within the given buffer
+ *
+ * @param buffer the buffer
+ */
+ @Override
+ public void getCuboid(CuboidBlockMaterialBuffer buffer);
+
+ /**
+ * Gets the {@link ManagedMap} which a world always has.
+ *
+ * @return ManagedMap
+ */
+ @Override
+ public ManagedMap getData();
+
+ /**
+ * Gets a set of all players on active on this world
+ *
+ * @return all players on this world
+ */
+ public List getPlayers();
+}
diff --git a/src/main/java/org/spout/api/geo/WorldManager.java b/src/main/java/org/spout/api/geo/WorldManager.java
new file mode 100644
index 0000000..6879a7e
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/WorldManager.java
@@ -0,0 +1,59 @@
+package org.spout.api.geo;
+
+import java.util.Collection;
+import java.util.UUID;
+
+public interface WorldManager {
+
+ /**
+ * Searches for an actively loaded world that exactly matches the given name.
The implementation is identical to iterating over {@link #getWorlds()} and checking for a world that matches
+ * {@link World#getName()}.
+ *
+ * Worlds are added to the list immediately, but removed at the end of a tick.
+ *
+ * @param name of the world to search for
+ * @return {@link World} if found, else null
+ */
+ public World getWorld(String name);
+
+ /**
+ * Searches for an actively loaded world that exactly matches the given name.
If searching for the exact name, this method will iterate and check for exact matches.
Otherwise,
+ * this method will iterate over over all worlds and find the closest match to the given name, by comparing the length of other player names that start with the given parameter.
+ *
+ * Worlds are added to the list immediately, but removed at the end of a tick.
+ *
+ * @param name of the world to search for
+ * @param exact Whether to use exact lookup
+ * @return world if found, else null
+ */
+ public World getWorld(String name, boolean exact);
+
+ /**
+ * Searches for actively loaded worlds that matches the given name.
The implementation is identical to iterating over {@link #getWorlds()} and checking for a world that matches {@link
+ * World#getName()}
+ *
+ * Worlds are added to the list immediately, but removed at the end of a tick.
+ *
+ * @param name of the world to search for, or part of it
+ * @return a collection of worlds that matched the name
+ */
+ public Collection matchWorld(String name);
+
+ /**
+ * Searches for an actively loaded world has the given {@link UUID}.
The implementation is identical to iterating over {@link #getWorlds()} and checking for a world that matches {@link
+ * World#getUID()}.
+ *
+ * Worlds are added to the list immediately, but removed at the end of a tick.
+ *
+ * @param uid of the world to search for
+ * @return {@link World} if found, else null
+ */
+ public World getWorld(UUID uid);
+
+ /**
+ * Gets a List of all currently loaded worlds
Worlds are added to the list immediately, but removed at the end of a tick.
+ *
+ * @return {@link Collection} of actively loaded worlds
+ */
+ public Collection getWorlds();
+}
diff --git a/src/main/java/org/spout/api/geo/WorldSource.java b/src/main/java/org/spout/api/geo/WorldSource.java
new file mode 100644
index 0000000..5636e60
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/WorldSource.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo;
+
+/**
+ * Represents an object that can be contained within a {@link World}
+ */
+public interface WorldSource {
+ /**
+ * Gets the World
+ *
+ * @return the World
+ */
+ public World getWorld();
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/Block.java b/src/main/java/org/spout/api/geo/cuboid/Block.java
new file mode 100644
index 0000000..a570a80
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/Block.java
@@ -0,0 +1,280 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import com.flowpowered.events.Cause;
+
+import org.spout.api.component.ComponentOwner;
+import org.spout.api.geo.World;
+import org.spout.api.geo.WorldSource;
+import org.spout.api.geo.discrete.Point;
+import org.spout.api.material.BlockMaterial;
+import org.spout.api.material.Material;
+import org.spout.api.material.block.BlockFace;
+import org.spout.math.vector.Vector3f;
+import org.spout.math.vector.Vector3i;
+
+public interface Block extends WorldSource, ComponentOwner {
+ /**
+ * Gets the {@link Point} position of this block in the world
+ *
+ * @return the position
+ */
+ public Point getPosition();
+
+ /**
+ * Gets the {@link Chunk} this block is in
+ *
+ * @return the Chunk
+ */
+ public Chunk getChunk();
+
+ /**
+ * Gets the {@link Region} this block is in
+ *
+ * @return the Region
+ */
+ public Region getRegion();
+
+ /**
+ * Gets the {@link World} this block is in
+ *
+ * @return the World
+ */
+ @Override
+ public World getWorld();
+
+ /**
+ * Gets the x-coordinate of this block
+ *
+ * @return the x-coordinate
+ */
+ public int getX();
+
+ /**
+ * Gets the y-coordinate of this block
+ *
+ * @return the y-coordinate
+ */
+ public int getY();
+
+ /**
+ * Gets the z-coordinate of this block
+ *
+ * @return the z-coordinate
+ */
+ public int getZ();
+
+ /**
+ * Translates this block using the offset and distance given
+ *
+ * @param offset BlockFace to translate
+ * @param distance to translate
+ * @return a new Block instance
+ */
+ public Block translate(BlockFace offset, int distance);
+
+ /**
+ * Translates this block using the offset given
+ *
+ * @param offset BlockFace to translate
+ * @return a new Block instance
+ */
+ public Block translate(BlockFace offset);
+
+ /**
+ * Translates this block using the offset given
+ *
+ * @param offset Vector to translate
+ * @return a new Block instance
+ */
+ public Block translate(Vector3f offset);
+
+ /**
+ * Translates this block using the offset given
+ *
+ * @param offset Vector to translate
+ * @return a new Block instance
+ */
+ public Block translate(Vector3i offset);
+
+ /**
+ * Translates this block using the offsets given
+ *
+ * @param dx offset to translate
+ * @param dy offset to translate
+ * @param dz offset to translate
+ * @return a new Block instance
+ */
+ public Block translate(int dx, int dy, int dz);
+
+ /**
+ * Gets the block material of this block
+ *
+ * @return block material
+ */
+ public BlockMaterial getMaterial();
+
+ /**
+ * Gets the block data for this block
+ *
+ * @return data
+ */
+ public short getBlockData();
+
+ /**
+ * Sets the data of this block to the given material's data
+ *
+ * @param data to set to
+ * @return this Block
+ */
+ public Block setData(BlockMaterial data);
+
+ /**
+ * Sets the data of this block
+ *
+ * @param data to set to
+ * @return this Block
+ */
+ public Block setData(int data);
+
+ /**
+ * Sets the data of this block
+ *
+ * @param data to set to
+ * @param cause of the change
+ * @return this Block
+ */
+ public Block setData(int data, Cause> cause);
+
+ /**
+ * Adds the value to the data of this block
+ *
+ * @param data to add
+ * @return this Block
+ */
+ public Block addData(int data);
+
+ /**
+ * Sets the material of this block
+ *
+ * @param material to set to
+ * @return whether the material set was successful
+ */
+ public boolean setMaterial(BlockMaterial material);
+
+ /**
+ * Sets the material of this block
+ *
+ * @param material to set to
+ * @param cause of the change
+ * @return whether the material set was successful
+ */
+ public boolean setMaterial(BlockMaterial material, Cause> cause);
+
+ /**
+ * Sets the material and data of this block
+ *
+ * @param material to set to
+ * @param data to set to
+ * @return whether the material set was successful
+ */
+ public boolean setMaterial(BlockMaterial material, int data);
+
+ /**
+ * Sets the material and data of this block
+ *
+ * @param material to set to
+ * @param data to set to
+ * @param cause of the change
+ * @return whether the material set was successful
+ */
+ public boolean setMaterial(BlockMaterial material, int data, Cause> cause);
+
+ /**
+ * Sets the given bits in the data for the block
newData = oldData | (bits)
+ *
+ * @param bits the bits to set
+ * @return the old data for the block
+ */
+ public short setDataBits(int bits);
+
+ /**
+ * Sets the given bits in the data for the block
newData = oldData | (bits)
or
newData = oldData & ~(bits)
+ *
+ * @param bits the bits to set or clear
+ * @param set True to set the bits, False to clear
+ * @return the old data for the block
+ */
+ public short setDataBits(int bits, boolean set);
+
+ /**
+ * Clears the given bits in the data for the block
newData = oldData & (~bits)
+ *
+ * @param bits the bits to clear
+ * @return the old data for the block
+ */
+ public short clearDataBits(int bits);
+
+ /**
+ * Gets the data field from the block
field = (data & bits) >> (shift)
The shift value used shifts the least significant non-zero bit of bits to the LSB position
+ *
+ * @param bits the bits of the field
+ * @return the field value
+ */
+ public int getDataField(int bits);
+
+ /**
+ * Gets if any of the indicated bits are set.
+ *
+ * @param bits the bits to check
+ * @return true if any of the given bits are set
+ */
+ public boolean isDataBitSet(int bits);
+
+ /**
+ * Sets the data field for the block. This is the reverse operation to the getDataField method.
newData = ((value << shift) & bits) | (oldData & (~bits))
The shift value used
+ * shifts the least significant non-zero bit of bits to the LSB position
+ *
+ * @param bits the bits of the field
+ * @param value the new value of the field
+ * @return the old value of the field
+ */
+ public int setDataField(int bits, int value);
+
+ /**
+ * Adds a value to the data field for the block. This is the reverse operation to the getBlockDataField method.
newData = (((oldData + (value << shift)) & bits) | (oldData & ~bits))
+ *
The shift value used shifts the least significant non-zero bit of bits to the LSB position
+ *
+ * @param bits the bits of the field
+ * @param value to add to the value of the field
+ * @return the old value of the field
+ */
+ public int addDataField(int bits, int value);
+
+ public boolean isMaterial(Material... materials);
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/BlockComponentContainer.java b/src/main/java/org/spout/api/geo/cuboid/BlockComponentContainer.java
new file mode 100644
index 0000000..756d5f6
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/BlockComponentContainer.java
@@ -0,0 +1,46 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import org.spout.api.component.BlockComponentOwner;
+
+public interface BlockComponentContainer extends CubicContainer {
+ /**
+ * Sets the next BlockComponentSnapshot in the sequence
+ */
+ public void setBlockComponent(int x, int y, int z, BlockComponentOwner snapshot);
+
+ /**
+ * Sets the number of block components. This method is called before the first call to setBlockComponent();
+ */
+ public void setBlockComponentCount(int count);
+
+ /**
+ * Sets the number of block components. This method is called before the first call to setBlockComponent();
+ */
+ public int getBlockComponentCount();
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/BlockContainer.java b/src/main/java/org/spout/api/geo/cuboid/BlockContainer.java
new file mode 100644
index 0000000..ebd4e94
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/BlockContainer.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+public interface BlockContainer extends CubicContainer {
+ /**
+ * Sets the state for the next block in the sequence.
+ */
+ public void setBlockFullState(int state);
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/Chunk.java b/src/main/java/org/spout/api/geo/cuboid/Chunk.java
new file mode 100644
index 0000000..ad45b28
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/Chunk.java
@@ -0,0 +1,379 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.Future;
+
+import com.flowpowered.commons.BitSize;
+
+import org.spout.api.entity.Entity;
+import org.spout.api.entity.Player;
+import org.spout.api.geo.AreaBlockAccess;
+import org.spout.api.geo.LoadOption;
+import org.spout.api.geo.World;
+import org.spout.api.geo.cuboid.ChunkSnapshot.EntityType;
+import org.spout.api.geo.cuboid.ChunkSnapshot.ExtraData;
+import org.spout.api.geo.cuboid.ChunkSnapshot.SnapshotType;
+import org.spout.api.geo.discrete.Point;
+import org.spout.api.material.block.BlockFace;
+import org.spout.math.vector.Vector3f;
+
+/**
+ * Represents a cube containing 16x16x16 Blocks
+ */
+public abstract class Chunk extends Cube implements AreaBlockAccess {
+ /**
+ * Stores the size of the amount of blocks in this Chunk
+ */
+ public static final BitSize BLOCKS = new BitSize(4);
+ /**
+ * Mask to convert a block integer coordinate into the point base
+ */
+ public final static int POINT_BASE_MASK = -BLOCKS.SIZE;
+ private final int blockX;
+ private final int blockY;
+ private final int blockZ;
+
+ public Chunk(World world, float x, float y, float z) {
+ super(new Point(world, x, y, z), BLOCKS.SIZE);
+ blockX = super.getX() << BLOCKS.BITS;
+ blockY = super.getY() << BLOCKS.BITS;
+ blockZ = super.getZ() << BLOCKS.BITS;
+ }
+
+ /**
+ * Performs the necessary tasks to unload this chunk from the world.
+ *
+ * @param save whether the chunk data should be saved.
+ */
+ public abstract void unload(boolean save);
+
+ /**
+ * Performs the necessary tasks to save this chunk.
+ */
+ public abstract void save();
+
+ /**
+ * Gets a snapshot of the data for the chunk.
This process may result in tearing if called during potential updates
This is the same as calling getSnapshot(BOTH, WEAK_ENTITIES,
+ * NO_EXTRA_DATA)
+ *
+ * @return the snapshot
+ */
+ public abstract ChunkSnapshot getSnapshot();
+
+ /**
+ * Gets a snapshot of the data for the chunk.
This process may result in tearing if called during potential updates
+ *
+ * @param type the type of basic snapshot information to be stored
+ * @param entities whether to include entity data in the snapshot
+ * @param data the extra data, if any, to be stored
+ * @return the snapshot
+ */
+ public abstract ChunkSnapshot getSnapshot(SnapshotType type, EntityType entities, ExtraData data);
+
+ /**
+ * Fills the given block container with the block data for this chunk
+ */
+ public abstract void fillBlockContainer(BlockContainer container);
+
+ /**
+ * Fills the given block component container with the block components for this chunk
+ */
+ public abstract void fillBlockComponentContainer(BlockComponentContainer container);
+
+ /**
+ * Gets a snapshot of the data for the chunk. The snapshot will be taken at a stable moment in the tick.
This is the same as calling getFutureSnapshot(BOTH, WEAK_ENTITIES, NO_EXTRA_DATA)
+ *
+ * @return the snapshot
+ */
+ public abstract Future getFutureSnapshot();
+
+ /**
+ * Gets a snapshot of the data for the chunk. The snapshot will be taken at a stable moment in the tick.
+ *
+ * @param type the type of basic snapshot information to be stored
+ * @param entities whether to include entity data in the snapshot
+ * @param data the extra data, if any, to be stored
+ * @return the snapshot
+ */
+ public abstract Future getFutureSnapshot(SnapshotType type, EntityType entities, ExtraData data);
+
+ /**
+ * Refresh the distance between a player and the chunk, and adds the player as an observer if not previously observing.
+ *
+ * @param player the player
+ * @return false if the player was already observing the chunk
+ */
+ public abstract boolean refreshObserver(Entity player);
+
+ /**
+ * De-register a player as observing the chunk.
+ *
+ * @param player the player
+ * @return true if the player was observing the chunk
+ */
+ public abstract boolean removeObserver(Entity player);
+
+ /**
+ * Gets the region that this chunk is located in
+ *
+ * @return region
+ */
+ public abstract Region getRegion();
+
+ /**
+ * Tests if the chunk is currently loaded
+ *
+ * Chunks may be unloaded at the end of each tick
+ */
+ public abstract boolean isLoaded();
+
+ /**
+ * Populates the chunk with all the Populators attached to the WorldGenerator of its world.
+ */
+ public abstract boolean populate();
+
+ /**
+ * Populates the chunk with all the Populators attached to the WorldGenerator of its world.
+ *
+ * @param force forces to populate the chunk even if it already has been populated.
+ */
+ public abstract boolean populate(boolean force);
+
+ /**
+ * Populates the chunk with all the Populators attached to the WorldGenerator of its world.
Warning: populating with force observer should not be called from within populators as it could
+ * lead to a population cascade
+ *
+ * @param sync queues the population to occur at a later time
+ * @param observe forces the chunk to be observed for population
+ */
+ public abstract void populate(boolean sync, boolean observe);
+
+ /**
+ * Populates the chunk with all the Populators attached to the WorldGenerator of its world.
Warning: populating with force observer should not be called from within populators as it could
+ * lead to a population cascade
+ *
+ * @param sync queues the population to occur at a later time
+ * @param observe forces the chunk to be observed for population
+ * @param priority adds the chunk to the high priority queue
+ */
+ public abstract void populate(boolean sync, boolean observe, boolean priority);
+
+ /**
+ * Gets if this chunk already has been populated.
+ *
+ * @return if the chunk is populated.
+ */
+ public abstract boolean isPopulated();
+
+ /**
+ * Gets the entities in the chunk at the last snapshot
+ *
+ * @return the entities
+ */
+ public abstract List getEntities();
+
+ /**
+ * Gets the entities currently in the chunk
+ *
+ * @return the entities
+ */
+ public abstract List getLiveEntities();
+
+ /**
+ * Gets the number of observers viewing this chunk. If the number of observing entities falls to zero, this chunk may be reaped at any time.
+ *
+ * @return number of observers
+ */
+ public abstract int getNumObservers();
+
+ /**
+ * Gets the observing players of this chunk (done based on the player's view distance).
+ *
+ * @return List containing the observing players
+ */
+ public abstract Set extends Player> getObservingPlayers();
+
+ /**
+ * Gets the observers of this chunk
+ *
+ * @return Set containing the observing players
+ */
+ public abstract Set extends Entity> getObservers();
+
+ @Override
+ public boolean containsBlock(int x, int y, int z) {
+ return x >> BLOCKS.BITS == this.getX() && y >> BLOCKS.BITS == this.getY() && z >> BLOCKS.BITS == this.getZ();
+ }
+
+ /**
+ * Gets the x-coordinate of this chunk as a Block coordinate
+ *
+ * @return the x-coordinate of the first block in this chunk
+ */
+ public int getBlockX() {
+ return blockX;
+ }
+
+ /**
+ * Gets the y-coordinate of this chunk as a Block coordinate
+ *
+ * @return the y-coordinate of the first block in this chunk
+ */
+ public int getBlockY() {
+ return blockY;
+ }
+
+ /**
+ * Gets the z-coordinate of this chunk as a Block coordinate
+ *
+ * @return the z-coordinate of the first block in this chunk
+ */
+ public int getBlockZ() {
+ return blockZ;
+ }
+
+ /**
+ * Gets the Block x-coordinate in the world
+ *
+ * @param x-coordinate within this Chunk
+ * @return x-coordinate within the World
+ */
+ public int getBlockX(int x) {
+ return this.blockX + (x & BLOCKS.MASK);
+ }
+
+ /**
+ * Gets the Block x-coordinate in the world
+ *
+ * @param y y-coordinate within this Chunk
+ * @return y-coordinate within the World
+ */
+ public int getBlockY(int y) {
+ return this.blockY + (y & BLOCKS.MASK);
+ }
+
+ /**
+ * Gets the Block x-coordinate in the world
+ *
+ * @param z z-coordinate within this Chunk
+ * @return z-coordinate within the World
+ */
+ public int getBlockZ(int z) {
+ return this.blockZ + (z & BLOCKS.MASK);
+ }
+
+ /**
+ * Gets a random Block x-coordinate using a Random
+ *
+ * @param random to use
+ * @return x-coordinate within the World in this Chunk
+ */
+ public int getBlockX(Random random) {
+ return this.blockX + random.nextInt(BLOCKS.SIZE);
+ }
+
+ /**
+ * Gets a random Block y-coordinate using a Random
+ *
+ * @param random to use
+ * @return y-coordinate within the World in this Chunk
+ */
+ public int getBlockY(Random random) {
+ return this.blockY + random.nextInt(BLOCKS.SIZE);
+ }
+
+ /**
+ * Gets a random Block z-coordinate using a Random
+ *
+ * @param random to use
+ * @return z-coordinate within the World in this Chunk
+ */
+ public int getBlockZ(Random random) {
+ return this.blockZ + random.nextInt(BLOCKS.SIZE);
+ }
+
+ /**
+ * Gets a chunk relative to this chunk
+ *
+ * @param offset of the chunk relative to this chunk
+ * @param opt True to load the chunk if it is not yet loaded
+ * @return The Chunk, or null if not loaded and load is False
+ */
+ public Chunk getRelative(Vector3f offset, LoadOption opt) {
+ return this.getWorld().getChunk(this.getX() + (int) offset.getX(), this.getY() + (int) offset.getY(), this.getZ() + (int) offset.getZ(), opt);
+ }
+
+ /**
+ * Gets a chunk relative to this chunk, loads if needed
+ *
+ * @param offset of the chunk relative to this chunk
+ * @return The Chunk
+ */
+ public Chunk getRelative(Vector3f offset) {
+ return this.getRelative(offset, LoadOption.LOAD_GEN);
+ }
+
+ /**
+ * Gets a chunk relative to this chunk
+ *
+ * @param offset of the chunk relative to this chunk
+ * @param opt True to load the chunk if it is not yet loaded
+ * @return The Chunk, or null if not loaded and load is False
+ */
+ public Chunk getRelative(BlockFace offset, LoadOption opt) {
+ return this.getRelative(offset.getOffset(), opt);
+ }
+
+ /**
+ * Gets a chunk relative to this chunk, loads if needed
+ *
+ * @param offset of the chunk relative to this chunk
+ * @return The Chunk
+ */
+ public Chunk getRelative(BlockFace offset) {
+ return this.getRelative(offset.getOffset());
+ }
+
+ /**
+ * Gets the generation index for this chunk. Only chunks generated as part of the same bulk initialize have the same index.
+ *
+ * @return a unique generation id, or -1 if the chunk was loaded from disk
+ */
+ public abstract int getGenerationIndex();
+
+ /**
+ * Converts a point in such a way that it points to the first block (the base block) of the chunk
This is similar to performing the following operation on the x, y and z coordinate:
- Convert
+ * to the chunk coordinate
- Multiply by chunk size
+ */
+ public static Point pointToBase(Point p) {
+ return new Point(p.getWorld(), (int) p.getX() & POINT_BASE_MASK, (int) p.getY() & POINT_BASE_MASK, (int) p.getZ() & POINT_BASE_MASK);
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/ChunkSnapshot.java b/src/main/java/org/spout/api/geo/cuboid/ChunkSnapshot.java
new file mode 100644
index 0000000..f61af0c
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/ChunkSnapshot.java
@@ -0,0 +1,201 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import java.util.List;
+import java.util.Set;
+
+import com.flowpowered.commons.datatable.SerializableMap;
+import org.spout.api.component.block.BlockComponent;
+import org.spout.api.entity.EntitySnapshot;
+import org.spout.api.geo.AreaBlockSource;
+import org.spout.api.geo.World;
+import org.spout.api.geo.discrete.Point;
+
+public abstract class ChunkSnapshot extends Cube implements AreaBlockSource {
+ /**
+ * Internal size of a side of a chunk
+ */
+ public final static int CHUNK_SIZE = 16;
+ /**
+ * Number of bits on the side of a chunk
+ */
+ public final static int CHUNK_SIZE_BITS = 4;
+ /**
+ * Mask to convert a block integer coordinate into the chunk's base
+ */
+ public final static int BASE_MASK = -CHUNK_SIZE;
+
+ public ChunkSnapshot(World world, float x, float y, float z) {
+ super(new Point(world, x, y, z), CHUNK_SIZE);
+ }
+
+ /**
+ * Gets a copy of the raw block ids.
+ *
+ * @return raw block ids
+ */
+ public abstract short[] getBlockIds();
+
+ /**
+ * Gets a copy of the raw block data.
+ *
+ * @return block data
+ */
+ public abstract short[] getBlockData();
+
+ /**
+ * Gets the region that this chunk is located in.
+ */
+ public abstract Region getRegion();
+
+ /**
+ * Gets an unmodifiable list of all of the entity snapshots associated with this chunk.
+ *
+ * @return the entities
+ */
+ public abstract List getEntities();
+
+ /**
+ * Gets if this chunk snapshot had already been populated.
+ *
+ * @return if the chunk snapshot was populated.
+ */
+ public abstract boolean isPopulated();
+
+ /**
+ * A thread-safe copy of the map of data attached to the chunk.
+ *
+ * @return data map
+ */
+ public abstract SerializableMap getDataMap();
+
+ /**
+ * Gets a unmodifiable list of all of the block component snapshots associated with this chunk.
+ *
+ * @return list of block component snapshots
+ */
+ public abstract List getBlockComponents();
+
+ public static enum SnapshotType {
+ /**
+ * Loads no block data (ids, data, skylight, blocklight) in the snapshot
+ */
+ NO_BLOCK_DATA,
+ /**
+ * Loads only block ids, no block data, skylight, or blocklight
+ */
+ BLOCK_IDS_ONLY,
+ /**
+ * Loads only block ids and block data, no skylight or blocklight
+ */
+ BLOCKS_ONLY,
+ /**
+ * Loads only skylight and blocklight, no block ids or block data
+ */
+ LIGHT_ONLY,
+ /**
+ * Loads block ids, block data, skylight, and blocklight
+ */
+ BOTH,
+ }
+
+ public static enum EntityType {
+ /**
+ * Saves no entity information in the snapshot
+ */
+ NO_ENTITIES,
+ /**
+ * Saves class and datatable information regarding block components in the snapshot
+ */
+ BLOCK_COMPONENTS,
+ /**
+ * Saves entity snapshot information
+ */
+ ENTITIES,
+ /**
+ * Saves both entity and block component information in the snapshot
+ */
+ BOTH
+ }
+
+ public static enum ExtraData {
+ /**
+ * Loads no extra data in the snapshot
+ */
+ NO_EXTRA_DATA,
+ /**
+ * Loads only extra biome data in the snapshot
+ */
+ BIOME_DATA,
+ /**
+ * Loads only extra datatable information in the snapshot
+ */
+ DATATABLE,
+ /**
+ * Loads both biome and datatable information in the snapshot
+ */
+ BOTH;
+ }
+
+ public static interface BlockComponentSnapshot {
+ /**
+ * Gets the world block x-coordinate for this snapshot
+ *
+ * @return x-coordinate
+ */
+ public int getX();
+
+ /**
+ * Gets the world block x-coordinate for this snapshot
+ *
+ * @return y-coordinate
+ */
+ public int getY();
+
+ /**
+ * Gets the world block x-coordinate for this snapshot
+ *
+ * @return z-coordinate
+ */
+ public int getZ();
+
+ /**
+ * Gets the block component class
+ *
+ * @return block component class
+ */
+ public Set> getComponents();
+
+ /**
+ * Gets a copy of the block component data
+ *
+ * @return data
+ */
+ public SerializableMap getData();
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/ChunkSnapshotGroup.java b/src/main/java/org/spout/api/geo/cuboid/ChunkSnapshotGroup.java
new file mode 100644
index 0000000..1f1a61a
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/ChunkSnapshotGroup.java
@@ -0,0 +1,82 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import org.spout.api.material.BlockMaterial;
+
+public interface ChunkSnapshotGroup {
+ public int getX();
+
+ public int getY();
+
+ public int getZ();
+
+ /**
+ * Gets if the chunk was unloaded. Unload models only indicate an unload occurred and contain no data.
+ */
+ public boolean isUnload();
+
+ /**
+ * Gets the current center chunk of this model
+ */
+ public ChunkSnapshot getCenter();
+
+ /**
+ * Clears all references to live chunks and regions
+ */
+ public void cleanUp();
+
+ /**
+ * Gets the chunk at world chunk coordinates
Note: Coordinates must be within this model, or index out of bounds will be thrown.
+ *
+ * @param cx coordinate of the chunk
+ * @param cy coordinate of the chunk
+ * @param cz coordinate of the chunk
+ * @return The chunk, or null if not available
+ */
+ public ChunkSnapshot getChunk(int cx, int cy, int cz);
+
+ /**
+ * Gets the chunk at world block coordinates
Note: Coordinates must be within this model, or index out of bounds will be thrown.
+ *
+ * @param bx coordinate of the block
+ * @param by coordinate of the block
+ * @param bz coordinate of the block
+ * @return The chunk, or null if not available
+ */
+ public ChunkSnapshot getChunkFromBlock(int bx, int by, int bz);
+
+ /**
+ * Gets the block material at the world block coordinates.
Note: Coordinates must be within this model, or index out of bounds will be thrown.
+ *
+ * @param bx coordinate of the block
+ * @param by coordinate of the block
+ * @param bz coordinate of the block
+ * @return The block, or null if not available
+ */
+ public BlockMaterial getBlock(int bx, int by, int bz);
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/ContainerFillOrder.java b/src/main/java/org/spout/api/geo/cuboid/ContainerFillOrder.java
new file mode 100644
index 0000000..0dc0780
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/ContainerFillOrder.java
@@ -0,0 +1,112 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+/**
+ * \ Indicates fill orders for fill containers
The intended usages is of the form
int sourceIndex = 0;
int targetIndex = 0;
int thirdStep =
+ * targetFillOrder.thirdStep(source, sizeX, sizeY, sizeZ);
int secondStep = targetFillOrder.secondStep(source, sizeX, sizeY, sizeZ);
int firstStep = targetFillOrder.firstStep(source, sizeX,
+ * sizeY, sizeZ);
int thirdMax = target.getThirdSize(sizeX, sizeY, sizeZ);
int secondMax = target.getSecondSize(sizeX, sizeY, sizeZ);
int firstMax = target.getFirstSize(sizeX, sizeY,
+ * sizeZ);
for (int third = 0; third < thirdMax; third++) {
int secondStart = sourceIndex;
for (int second = 0; second <
+ * secondMax; second++) {
int firstStart = sourceIndex;
for (int first = 0; first < firstMax; first++) {
+ * target[targetIndex++] = source[sourceIndex += firstStep];
}
+ * sourceIndex = firstStart + secondStep;
}
sourceIndex = secondStart + thirdStep;
}
+ *
+ */
+public enum ContainerFillOrder {
+
+ YXZ(Coord.Y, Coord.X, Coord.Z),
+ YZX(Coord.Y, Coord.Z, Coord.X),
+ ZXY(Coord.Z, Coord.X, Coord.Y),
+ ZYX(Coord.Z, Coord.Y, Coord.X),
+ XYZ(Coord.X, Coord.Y, Coord.Z),
+ XZY(Coord.X, Coord.Z, Coord.Y);
+ private final Coord first;
+ private final Coord second;
+ private final Coord third;
+
+ ContainerFillOrder(Coord first, Coord second, Coord third) {
+ this.first = first;
+ this.second = second;
+ this.third = third;
+ }
+
+ public int firstStep(ContainerFillOrder source, int sizeX, int sizeY, int sizeZ) {
+ return step(source, first, sizeX, sizeY, sizeZ);
+ }
+
+ public int getFirstSize(int sizeX, int sizeY, int sizeZ) {
+ return first.getSize(sizeX, sizeY, sizeZ);
+ }
+
+ public int secondStep(ContainerFillOrder source, int sizeX, int sizeY, int sizeZ) {
+ return step(source, second, sizeX, sizeY, sizeZ);
+ }
+
+ public int getSecondSize(int sizeX, int sizeY, int sizeZ) {
+ return second.getSize(sizeX, sizeY, sizeZ);
+ }
+
+ public int thirdStep(ContainerFillOrder source, int sizeX, int sizeY, int sizeZ) {
+ return step(source, third, sizeX, sizeY, sizeZ);
+ }
+
+ public int getThirdSize(int sizeX, int sizeY, int sizeZ) {
+ return third.getSize(sizeX, sizeY, sizeZ);
+ }
+
+ private int step(ContainerFillOrder source, Coord coord, int sizeX, int sizeY, int sizeZ) {
+ if (coord == source.first) {
+ return 1;
+ } else if (coord == source.second) {
+ return source.first.getSize(sizeX, sizeY, sizeZ);
+ } else if (coord == source.third) {
+ return source.first.getSize(sizeX, sizeY, sizeZ) * source.second.getSize(sizeX, sizeY, sizeZ);
+ } else {
+ throw new IllegalStateException("At least one coord must match");
+ }
+ }
+
+ private enum Coord {
+ X,
+ Y,
+ Z;
+
+ public int getSize(int sizeX, int sizeY, int sizeZ) {
+ switch (this) {
+ case X:
+ return sizeX;
+ case Y:
+ return sizeY;
+ case Z:
+ return sizeZ;
+ default:
+ throw new IllegalStateException("Unknown coord: " + this);
+ }
+ }
+
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/Cube.java b/src/main/java/org/spout/api/geo/cuboid/Cube.java
new file mode 100644
index 0000000..2350755
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/Cube.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import org.spout.api.geo.discrete.Point;
+import org.spout.math.vector.Vector3f;
+
+/**
+ * Represents a Cube that is located somewhere in a world.
+ */
+public class Cube extends Cuboid {
+ public Cube(Point base, float size) {
+ super(base, new Vector3f(size, size, size));
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/CubicContainer.java b/src/main/java/org/spout/api/geo/cuboid/CubicContainer.java
new file mode 100644
index 0000000..032f45f
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/CubicContainer.java
@@ -0,0 +1,34 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+public interface CubicContainer {
+ /**
+ * Gets the fill order for the container
+ */
+ public ContainerFillOrder getOrder();
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/Cuboid.java b/src/main/java/org/spout/api/geo/cuboid/Cuboid.java
new file mode 100644
index 0000000..7181f0d
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/Cuboid.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+import org.spout.api.geo.World;
+import org.spout.api.geo.WorldSource;
+import org.spout.api.geo.discrete.Point;
+import org.spout.math.vector.Vector3f;
+
+/**
+ * Represents a Cuboid shaped volume that is located somewhere in a world.
+ */
+public class Cuboid implements WorldSource {
+ protected final Point base;
+ protected final Vector3f size;
+ private final int x;
+ private final int y;
+ private final int z;
+ /**
+ * Hashcode caching
+ */
+ private volatile boolean hashed = false;
+ private volatile int hashcode = 0;
+ /**
+ * Vertex cache
+ */
+ private Vector3f[] vertices = null;
+
+ /**
+ * Constructs a cubiod with the point as the base point, and
+ */
+ public Cuboid(Point base, Vector3f size) {
+ this.base = base;
+ this.size = size;
+ this.x = (int) (base.getX() / size.getX());
+ this.y = (int) (base.getY() / size.getY());
+ this.z = (int) (base.getZ() / size.getZ());
+ }
+
+ public Point getBase() {
+ return base;
+ }
+
+ public Vector3f getSize() {
+ return size;
+ }
+
+ public int getX() {
+ return x;
+ }
+
+ public int getY() {
+ return y;
+ }
+
+ public int getZ() {
+ return z;
+ }
+
+ @Override
+ public World getWorld() {
+ return base.getWorld();
+ }
+
+ /**
+ * Returns the vertices of this Cuboid.
+ *
+ * @return The vertices
+ */
+ public Vector3f[] getVertices() {
+ if (vertices == null) {
+ vertices = new Vector3f[8];
+
+ // Front
+ vertices[0] = new Vector3f(base.getX(), base.getY(), base.getZ() + size.getZ());
+ vertices[1] = new Vector3f(base.getX() + size.getX(), base.getY(), base.getZ() + size.getZ());
+ vertices[2] = new Vector3f(base.getX() + size.getX(), base.getY() + size.getY(), base.getZ() + size.getZ());
+ vertices[3] = new Vector3f(base.getX(), base.getY() + size.getY(), base.getZ() + size.getZ());
+ // Back
+ vertices[4] = new Vector3f(base.getX(), base.getY(), base.getZ());
+ vertices[5] = new Vector3f(base.getX() + size.getX(), base.getY(), base.getZ());
+ vertices[6] = new Vector3f(base.getX() + size.getX(), base.getY() + size.getY(), base.getZ());
+ vertices[7] = new Vector3f(base.getX(), base.getY() + size.getY(), base.getZ());
+ }
+
+ return vertices;
+ }
+
+ public boolean contains(Vector3f vec) {
+ Vector3f max = base.add(size);
+ return (base.getX() <= vec.getX() && vec.getX() < max.getX()) && (base.getY() <= vec.getY() && vec.getY() < max.getY()) && (base.getZ() <= vec.getZ() && vec.getZ() < max.getZ());
+ }
+
+ @Override
+ public int hashCode() {
+ if (!hashed) {
+ hashcode = new HashCodeBuilder(563, 21).append(base).append(size).toHashCode();
+ hashed = true;
+ }
+ return hashcode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+
+ if (obj == null) {
+ return false;
+ } else if (!(obj instanceof Cuboid)) {
+ return false;
+ } else {
+ Cuboid cuboid = (Cuboid) obj;
+
+ return cuboid.size.getX() == size.getX() && cuboid.size.getY() == size.getY() && cuboid.size.getZ() == size.getZ() && cuboid.getWorld().equals(getWorld()) && cuboid.getX() == getX() && cuboid.getY() == getY() && cuboid.getZ() == getZ();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Cuboid[" + size.getX() + ", " + size.getY() + ", " + size.getZ() + "]@[" + getX() + ", " + getY() + ", " + getZ() + "]";
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/Region.java b/src/main/java/org/spout/api/geo/cuboid/Region.java
new file mode 100644
index 0000000..6fde011
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/Region.java
@@ -0,0 +1,283 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import java.util.Iterator;
+import java.util.List;
+
+import com.flowpowered.commons.BitSize;
+import org.spout.api.entity.Entity;
+import org.spout.api.entity.Player;
+import org.spout.api.geo.AreaChunkAccess;
+import org.spout.api.geo.LoadOption;
+import org.spout.api.geo.LocalAreaAccess;
+import org.spout.api.geo.World;
+import org.spout.api.geo.discrete.Point;
+import org.spout.api.scheduler.TaskManager;
+
+/**
+ * Represents a cube containing 16x16x16 Chunks (256x256x256 Blocks)
+ */
+public abstract class Region extends Cube implements AreaChunkAccess, LocalAreaAccess, Iterable {
+ /**
+ * Stores the size of the amount of chunks in this Region
+ */
+ public static final BitSize CHUNKS = new BitSize(4);
+ /**
+ * Stores the size of the amount of blocks in this Region
+ */
+ public static final BitSize BLOCKS = new BitSize(CHUNKS.BITS + Chunk.BLOCKS.BITS);
+ private final int blockX;
+ private final int blockY;
+ private final int blockZ;
+ private final int chunkX;
+ private final int chunkY;
+ private final int chunkZ;
+
+ public Region(World world, float x, float y, float z) {
+ super(new Point(world, x, y, z), BLOCKS.SIZE);
+ this.blockX = super.getX() << BLOCKS.BITS;
+ this.blockY = super.getY() << BLOCKS.BITS;
+ this.blockZ = super.getZ() << BLOCKS.BITS;
+ this.chunkX = super.getX() << CHUNKS.BITS;
+ this.chunkY = super.getY() << CHUNKS.BITS;
+ this.chunkZ = super.getZ() << CHUNKS.BITS;
+ }
+
+ /**
+ * Gets the x-coordinate of this region as a Block coordinate
+ *
+ * @return the x-coordinate of the first block in this region
+ */
+ public int getBlockX() {
+ return this.blockX;
+ }
+
+ /**
+ * Gets the y-coordinate of this region as a Block coordinate
+ *
+ * @return the y-coordinate of the first block in this region
+ */
+ public int getBlockY() {
+ return this.blockY;
+ }
+
+ /**
+ * Gets the z-coordinate of this region as a Block coordinate
+ *
+ * @return the z-coordinate of the first block in this region
+ */
+ public int getBlockZ() {
+ return this.blockZ;
+ }
+
+ /**
+ * Gets the x-coordinate of this region as a Chunk coordinate
+ *
+ * @return the x-coordinate of the first chunk in this region
+ */
+ public int getChunkX() {
+ return this.chunkX;
+ }
+
+ /**
+ * Gets the y-coordinate of this region as a Chunk coordinate
+ *
+ * @return the y-coordinate of the first chunk in this region
+ */
+ public int getChunkY() {
+ return this.chunkY;
+ }
+
+ /**
+ * Gets the z-coordinate of this region as a Chunk coordinate
+ *
+ * @return the z-coordinate of the first chunk in this region
+ */
+ public int getChunkZ() {
+ return this.chunkZ;
+ }
+
+ /**
+ * Gets the Block x-coordinate in the world
+ *
+ * @param x-coordinate within this Region
+ * @return x-coordinate within the World
+ */
+ public int getBlockX(int x) {
+ return this.blockX + (x & BLOCKS.MASK);
+ }
+
+ /**
+ * Gets the Block y-coordinate in the world
+ *
+ * @param y-coordinate within this Region
+ * @return y-coordinate within the World
+ */
+ public int getBlockY(int y) {
+ return this.blockY + (y & BLOCKS.MASK);
+ }
+
+ /**
+ * Gets the Block z-coordinate in the world
+ *
+ * @param z-coordinate within this Region
+ * @return z-coordinate within the World
+ */
+ public int getBlockZ(int z) {
+ return this.blockZ + (z & BLOCKS.MASK);
+ }
+
+ /**
+ * Gets the Chunk x-coordinate in the world
+ *
+ * @param x-coordinate within this Region
+ * @return x-coordinate within the World
+ */
+ public int getChunkX(int x) {
+ return this.chunkX + (x & CHUNKS.MASK);
+ }
+
+ /**
+ * Gets the Chunk y-coordinate in the world
+ *
+ * @param y-coordinate within this Region
+ * @return y-coordinate within the World
+ */
+ public int getChunkY(int y) {
+ return this.chunkY + (y & CHUNKS.MASK);
+ }
+
+ /**
+ * Gets the Chunk z-coordinate in the world
+ *
+ * @param z-coordinate within this Region
+ * @return z-coordinate within the World
+ */
+ public int getChunkZ(int z) {
+ return this.chunkZ + (z & CHUNKS.MASK);
+ }
+
+ @Override
+ public boolean containsBlock(int x, int y, int z) {
+ return x >> BLOCKS.BITS == this.getX() && y >> BLOCKS.BITS == this.getY() && z >> BLOCKS.BITS == this.getZ();
+ }
+
+ @Override
+ public boolean containsChunk(int x, int y, int z) {
+ return x >> CHUNKS.BITS == this.getX() && y >> CHUNKS.BITS == this.getY() && z >> CHUNKS.BITS == this.getZ();
+ }
+
+ /**
+ * Queues all chunks for saving at the next available opportunity.
+ */
+ public abstract void save();
+
+ /**
+ * Performs the nessecary tasks to unload this region from the world, and all associated chunks.
+ *
+ * @param save whether to save the region and associated data.
+ */
+ public abstract void unload(boolean save);
+
+ /**
+ * Gets all entities with the specified type.
+ *
+ * @return A set of entities with the specified type.
+ */
+ public abstract List getAll();
+
+ /**
+ * Gets an entity by its id.
+ *
+ * @param id The id.
+ * @return The entity, or {@code null} if it could not be found.
+ */
+ public abstract Entity getEntity(int id);
+
+ public abstract List getPlayers();
+
+ /**
+ * Gets the TaskManager associated with this region
+ */
+ public abstract TaskManager getTaskManager();
+
+ public abstract boolean isLoaded();
+
+ @Override
+ public Iterator iterator() {
+ return new ChunkIterator();
+ }
+
+ private class ChunkIterator implements Iterator {
+ private Chunk next;
+
+ public ChunkIterator() {
+ loop:
+ for (int dx = 0; dx < CHUNKS.SIZE; dx++) {
+ for (int dy = 0; dy < CHUNKS.SIZE; dy++) {
+ for (int dz = 0; dz < CHUNKS.SIZE; dz++) {
+ next = getChunk(dx, dy, dz, LoadOption.NO_LOAD);
+ if (next != null) {
+ break loop;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public Chunk next() {
+ Chunk current = next;
+ next = null;
+ final int cx = current.getX() & CHUNKS.MASK;
+ final int cy = current.getY() & CHUNKS.MASK;
+ final int cz = current.getZ() & CHUNKS.MASK;
+ for (int dx = cx; dx < CHUNKS.SIZE; dx++) {
+ for (int dy = cy; dy < CHUNKS.SIZE; dy++) {
+ for (int dz = cz; dz < CHUNKS.SIZE; dz++) {
+ next = getChunk(dx, dy, dz, LoadOption.NO_LOAD);
+ if (next != null && next != current) {
+ return current;
+ }
+ }
+ }
+ }
+ return current;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException("Operation not supported");
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/UpdateOption.java b/src/main/java/org/spout/api/geo/cuboid/UpdateOption.java
new file mode 100644
index 0000000..1b0737d
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/UpdateOption.java
@@ -0,0 +1,69 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+public enum UpdateOption {
+ /**
+ * Updates the block itself
+ */
+ SELF(true, false),
+ /**
+ * Updates the blocks surrounding this block
+ */
+ AROUND(false, true),
+ /**
+ * Updates the block and the blocks surrounding the blocks
+ */
+ SELF_AROUND(true, true);
+
+ private final boolean self;
+ private final boolean around;
+
+ private UpdateOption(boolean self, boolean around) {
+ this.self = self;
+ this.around = around;
+ }
+
+ /**
+ * Test if chunk/region should be loaded if not currently loaded
+ *
+ * @return true if yes, false if no
+ */
+ public final boolean updateSelf() {
+ return self;
+ }
+
+ /**
+ * Test if chunk/region should be generated if it does not exist
+ *
+ * @return true if yes, false if no
+ */
+ public final boolean updateAround() {
+ return around;
+ }
+
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/Voxel.java b/src/main/java/org/spout/api/geo/cuboid/Voxel.java
new file mode 100644
index 0000000..fb8818e
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/Voxel.java
@@ -0,0 +1,41 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid;
+
+import org.spout.api.geo.World;
+import org.spout.api.geo.discrete.Point;
+
+/**
+ * Represents a cube with an edge length of 1/16 of the edge of a Block.
+ */
+public abstract class Voxel extends Cube {
+ protected final static float EDGE = 1.f / 16.0f;
+
+ public Voxel(World world, float x, float y, float z) {
+ super(new Point(world, x, y, z), EDGE);
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/reference/ChunkReference.java b/src/main/java/org/spout/api/geo/cuboid/reference/ChunkReference.java
new file mode 100644
index 0000000..a6949ef
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/reference/ChunkReference.java
@@ -0,0 +1,96 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid.reference;
+
+import java.lang.ref.WeakReference;
+
+import org.spout.api.geo.LoadOption;
+import org.spout.api.geo.cuboid.Chunk;
+import org.spout.api.geo.cuboid.Region;
+import org.spout.api.geo.discrete.Point;
+
+/**
+ * This holds a {@code WeakReference} that can be used streamline the get() with a isLoaded check. It also adds a
+ * store of a {@code Point} representing the base. Because of this, a ChunkReference may contain only base info.
+ */
+public class ChunkReference {
+ private final Point base;
+ private final RegionReference region;
+ private WeakReference chunk;
+
+ public ChunkReference(Chunk referent) {
+ this.chunk = new WeakReference<>(referent);
+ this.region = new RegionReference(referent.getRegion());
+ this.base = referent.getBase();
+ }
+
+ public ChunkReference(Point base) {
+ this.chunk = null;
+ this.region = new RegionReference(new Point(base.getWorld(), base.getFloorX() >> Region.BLOCKS.BITS, base.getFloorY() >> Region.BLOCKS.BITS, base.getFloorZ() >> Region.BLOCKS.BITS));
+ this.base = base;
+ }
+
+ public Chunk get() {
+ Chunk get = chunk == null ? null : chunk.get();
+ if (get != null) {
+ if (!get.isLoaded()) {
+ chunk = null;
+ return null;
+ }
+ }
+ return get;
+ }
+
+ public Chunk refresh(LoadOption opt) {
+ Chunk newChunk = get();
+ if (newChunk != null) return newChunk;
+
+ Region newRegion = region.refresh(opt);
+ if (newRegion == null) return null;
+
+ newChunk = newRegion.getChunkFromBlock(base, opt);
+ this.chunk = newChunk == null ? null : new WeakReference<>(newChunk);
+ return newChunk;
+ }
+
+ @Override
+ public int hashCode() {
+ return base.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof ChunkReference) {
+ return base.equals(((ChunkReference) obj).base);
+ }
+ return false;
+ }
+
+ public Point getBase() {
+ return base;
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/cuboid/reference/RegionReference.java b/src/main/java/org/spout/api/geo/cuboid/reference/RegionReference.java
new file mode 100644
index 0000000..1a28305
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/cuboid/reference/RegionReference.java
@@ -0,0 +1,87 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.cuboid.reference;
+
+import java.lang.ref.WeakReference;
+
+import org.spout.api.geo.LoadOption;
+import org.spout.api.geo.cuboid.Region;
+import org.spout.api.geo.discrete.Point;
+
+/**
+ * This holds a {@code WeakReference} that can be used streamline the get() with a isLoaded check. It also adds a
+ * store of a {@code Point} representing the base. Because of this, a RegionReference may contain only base info.
+ */
+public class RegionReference {
+ private final Point base;
+ private WeakReference region;
+ public RegionReference(Region referent) {
+ this.region = new WeakReference<>(referent);
+ base = referent.getBase();
+ }
+
+ public RegionReference(Point base) {
+ region = null;
+ this.base = base;
+ }
+
+ public Region get() {
+ Region get = region == null ? null : region.get();
+ if (get != null) {
+ if (!get.isLoaded()) {
+ region = null;
+ return null;
+ }
+ }
+ return get;
+ }
+
+ public Region refresh(LoadOption opt) {
+ Region newRegion = get();
+ if (newRegion != null) return newRegion;
+ newRegion = base.getRegion(opt);
+ this.region = newRegion == null ? null : new WeakReference<>(newRegion);
+ return newRegion;
+ }
+
+ @Override
+ public int hashCode() {
+ return base.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof RegionReference) {
+ return base.equals(((RegionReference) obj).base);
+ }
+ return false;
+ }
+
+ public Point getBase() {
+ return base;
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/discrete/Point.java b/src/main/java/org/spout/api/geo/discrete/Point.java
new file mode 100644
index 0000000..c8cdce0
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/discrete/Point.java
@@ -0,0 +1,340 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.discrete;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+
+import com.flowpowered.commons.StringUtil;
+
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+import org.spout.api.Spout;
+
+import org.spout.api.geo.LoadOption;
+import org.spout.api.geo.World;
+import org.spout.api.geo.WorldSource;
+import org.spout.api.geo.cuboid.Block;
+import org.spout.api.geo.cuboid.Chunk;
+import org.spout.math.vector.Vector3f;
+import org.spout.api.geo.cuboid.Region;
+
+/**
+ * Represents a position in a World
+ */
+public class Point extends Vector3f implements WorldSource {
+ private static final long serialVersionUID = 1L;
+ protected final World world;
+ public static final Point invalid = new Point(null, 0, 0, 0);
+ /**
+ * Hashcode caching
+ */
+ private transient volatile boolean hashed = false;
+ private transient volatile int hashcode = 0;
+
+ public Point(Point point) {
+ super(point);
+ world = point.getWorld();
+ }
+
+ public Point(Vector3f vector, World w) {
+ super(vector);
+ world = w;
+ }
+
+ public Point(World world, float x, float y, float z) {
+ super(x, y, z);
+ this.world = world;
+ }
+
+ @Override
+ public Point div(float val) {
+ return new Point(super.div(val), world);
+ }
+
+ @Override
+ public Point div(double val) {
+ return new Point(super.div(val), world);
+ }
+
+ @Override
+ public Point div(Vector3f other) {
+ return new Point(super.div(other), world);
+ }
+
+ @Override
+ public Point div(double x, double y, double z) {
+ return new Point(super.div(x, y, z), world);
+ }
+
+ @Override
+ public Point div(float x, float y, float z) {
+ return new Point(super.div(x, y, z), world);
+ }
+
+ @Override
+ public Point mul(float val) {
+ return new Point(super.mul(val), world);
+ }
+
+ @Override
+ public Point mul(double val) {
+ return new Point(super.mul(val), world);
+ }
+
+ @Override
+ public Point mul(Vector3f other) {
+ return new Point(super.mul(other), world);
+ }
+
+ @Override
+ public Point mul(double x, double y, double z) {
+ return new Point(super.mul(x, y, z), world);
+ }
+
+ @Override
+ public Point mul(float x, float y, float z) {
+ return new Point(super.mul(x, y, z), world);
+ }
+
+ public Point add(Point other) {
+ if (world != other.world) {
+ throw new IllegalArgumentException("Cannot add two points in seperate worlds");
+ }
+ return new Point(super.add(other), world);
+ }
+
+ @Override
+ public Point add(Vector3f other) {
+ return new Point(super.add(other), world);
+ }
+
+ @Override
+ public Point add(float x, float y, float z) {
+ return new Point(super.add(x, y, z), world);
+ }
+
+ @Override
+ public Point add(double x, double y, double z) {
+ return new Point(super.add(x, y, z), world);
+ }
+
+ @Override
+ public Point sub(Vector3f other) {
+ return new Point(super.sub(other), world);
+ }
+
+ @Override
+ public Point sub(float x, float y, float z) {
+ return new Point(super.sub(x, y, z), world);
+ }
+
+ @Override
+ public Point sub(double x, double y, double z) {
+ return new Point(super.sub(x, y, z), world);
+ }
+
+ public int getBlockX() {
+ return this.getFloorX();
+ }
+
+ public int getBlockY() {
+ return this.getFloorY();
+ }
+
+ public int getBlockZ() {
+ return this.getFloorZ();
+ }
+
+ public int getChunkX() {
+ return this.getFloorX() >> Chunk.BLOCKS.BITS;
+ }
+
+ public int getChunkY() {
+ return this.getFloorY() >> Chunk.BLOCKS.BITS;
+ }
+
+ public int getChunkZ() {
+ return this.getFloorZ() >> Chunk.BLOCKS.BITS;
+ }
+
+ public Chunk getChunk(LoadOption loadopt) {
+ return world.getChunk(getChunkX(), getChunkY(), getChunkZ(), loadopt);
+ }
+
+ public Region getRegion(LoadOption loadopt) {
+ return world.getRegionFromChunk(getChunkX(), getChunkY(), getChunkZ(), loadopt);
+ }
+
+ /**
+ * Gets the square of the distance between two points.
+ *
+ * This will return Double.MAX_VALUE if the other Point is null, either world is null, or the two points are in different worlds.
+ *
+ * Otherwise, it returns the Manhattan distance.
+ */
+ public double getSquaredDistance(Point other) {
+ if (other == null || world == null || other.world == null || !world.equals(other.world)) {
+ return Double.MAX_VALUE;
+ }
+ double dx = getX() - other.getX();
+ double dy = getY() - other.getY();
+ double dz = getZ() - other.getZ();
+ return dx * dx + dy * dy + dz * dz;
+ }
+
+ /**
+ * Gets the distance between two points.
+ *
+ * This will return Double.MAX_VALUE if the other Point is null, either world is null, or the two points are in different worlds.
+ *
+ * Otherwise, it returns the Manhattan distance.
+ */
+ public double getDistance(Point other) {
+ return Math.sqrt(getSquaredDistance(other));
+ }
+
+ /**
+ * Gets the Manhattan distance between two points.
+ *
+ * This will return Double.MAX_VALUE if the other Point is null, either world is null, or the two points are in different worlds.
+ *
+ * Otherwise, it returns the Manhattan distance.
+ */
+ public double getManhattanDistance(Point other) {
+ if (other == null || world == null || other.world == null || !world.equals(other.world)) {
+ return Double.MAX_VALUE;
+ }
+ return Math.abs(getX() - other.getX()) + Math.abs(getY() - other.getY()) + Math.abs(getZ() - other.getZ());
+ }
+
+ /**
+ * Gets the largest distance between two points, when projected onto one of the axes.
+ *
+ * This will return Double.MAX_VALUE if the other Point is null, either world is null, or the two points are in different worlds.
+ *
+ * Otherwise, it returns the max distance.
+ */
+ public double getMaxDistance(Point other) {
+ if (other == null || world == null || other.world == null || !world.equals(other.world)) {
+ return Double.MAX_VALUE;
+ }
+ return Math.max(Math.abs(getX() - other.getX()),
+ Math.max(Math.abs(getY() - other.getY()),
+ Math.abs(getZ() - other.getZ())));
+ }
+
+ /**
+ * Gets the world this point is locate in
+ *
+ * @return the world
+ */
+ @Override
+ public World getWorld() {
+ return world;
+ }
+
+ /**
+ * Gets the block this point is locate in
+ *
+ * @return the world
+ */
+ public Block getBlock() {
+ return world.getBlock(getX(), getY(), getZ());
+ }
+
+ @Override
+ public int hashCode() {
+ if (!hashed) {
+ hashcode = new HashCodeBuilder(5033, 61).appendSuper(super.hashCode()).append(world).toHashCode();
+ hashed = true;
+ }
+ return hashcode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Point)) {
+ return false;
+ } else {
+ Point point = (Point) obj;
+ boolean worldEqual = point.world == world || (point.world != null && point.world.equals(world));
+ return worldEqual && point.getX() == getX() && point.getY() == getY() && point.getZ() == getZ();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + StringUtil.toString(world, getX(), getY(), getZ());
+ }
+
+ public String toBlockString() {
+ return "{" + world.getName() + ":" + getBlockX() + ", " + getBlockY() + ", " + getBlockZ() + "}";
+ }
+
+ public String toChunkString() {
+ return "{" + world.getName() + ":" + getChunkX() + ", " + getChunkY() + ", " + getChunkZ() + "}";
+ }
+
+ //Custom serialization logic because world can not be made serializable
+ private void writeObject(java.io.ObjectOutputStream out) throws IOException {
+ out.writeFloat(this.getX());
+ out.writeFloat(this.getY());
+ out.writeFloat(this.getZ());
+ out.writeUTF(world != null ? world.getName() : "null");
+ }
+
+ private void readObject(java.io.ObjectInputStream in) throws IOException {
+ float x = in.readFloat();
+ float y = in.readFloat();
+ float z = in.readFloat();
+ String world = in.readUTF();
+ World w = Spout.getEngine().getWorldManager().getWorld(world, true);
+ try {
+ Field field;
+
+ field = Vector3f.class.getDeclaredField("x");
+ field.setAccessible(true);
+ field.set(this, x);
+
+ field = Vector3f.class.getDeclaredField("y");
+ field.setAccessible(true);
+ field.set(this, y);
+
+ field = Vector3f.class.getDeclaredField("z");
+ field.setAccessible(true);
+ field.set(this, z);
+
+ field = Point.class.getDeclaredField("world");
+ field.setAccessible(true);
+ field.set(this, w);
+ } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
+ if (Spout.debugMode()) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/discrete/Transform.java b/src/main/java/org/spout/api/geo/discrete/Transform.java
new file mode 100644
index 0000000..0ef82a9
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/discrete/Transform.java
@@ -0,0 +1,324 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.discrete;
+
+import java.io.Serializable;
+
+import com.flowpowered.commons.StringUtil;
+
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+import org.spout.api.geo.World;
+import org.spout.api.util.concurrent.SpinLock;
+
+import org.spout.math.imaginary.Quaternionf;
+import org.spout.math.matrix.Matrix3f;
+import org.spout.math.matrix.Matrix4f;
+import org.spout.math.vector.Vector3f;
+
+public final class Transform implements Serializable {
+ private static final long serialVersionUID = 2L;
+ private final transient SpinLock lock = new SpinLock();
+ private Point position;
+ private Quaternionf rotation;
+ private Vector3f scale;
+
+ public Transform() {
+ this(Point.invalid, Quaternionf.IDENTITY, new Vector3f(1, 1, 1));
+ }
+
+ public Transform(Transform transform) {
+ set(transform);
+ }
+
+ public Transform(Point position, Quaternionf rotation, Vector3f scale) {
+ this.position = position;
+ this.rotation = rotation;
+ this.scale = scale;
+ }
+
+ public Point getPosition() {
+ try {
+ lock.lock();
+ return position;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public Transform setPosition(Point position) {
+ try {
+ lock.lock();
+ this.position = position;
+ } finally {
+ lock.unlock();
+ }
+ return this;
+ }
+
+ public Transform translate(float x, float y, float z) {
+ return translate(new Vector3f(x, y, z));
+ }
+
+ public Transform translate(Vector3f offset) {
+ try {
+ lock.lock();
+ this.position = this.position.add(offset);
+ } finally {
+ lock.unlock();
+ }
+ return this;
+ }
+
+ public Transform rotate(Quaternionf offset) {
+ try {
+ lock.lock();
+ this.rotation = offset.mul(rotation);
+ } finally {
+ lock.unlock();
+ }
+ return this;
+ }
+
+ public Transform scale(Vector3f offset) {
+ try {
+ lock.lock();
+ this.scale = this.scale.add(offset);
+ } finally {
+ lock.unlock();
+ }
+ return this;
+ }
+
+ public Transform translateAndSetRotation(Vector3f offset, Quaternionf rotation) {
+ try {
+ lock.lock();
+ this.position = this.position.add(offset);
+ this.rotation = rotation;
+ } finally {
+ lock.unlock();
+ }
+ return this;
+ }
+
+ public Quaternionf getRotation() {
+ try {
+ lock.lock();
+ return rotation;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public Transform setRotation(Quaternionf rotation) {
+ try {
+ lock.lock();
+ this.rotation = rotation;
+ } finally {
+ lock.unlock();
+ }
+ return this;
+ }
+
+ public Vector3f getScale() {
+ try {
+ lock.lock();
+ return scale;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ public Transform setScale(Vector3f scale) {
+ try {
+ lock.lock();
+ this.scale = scale;
+ } finally {
+ lock.unlock();
+ }
+ return this;
+ }
+
+ /**
+ * Atomically sets the value of this transform to the value of another transform
+ *
+ * @param transform the other transform
+ * @return this transform
+ */
+ public Transform set(Transform transform) {
+ if (transform == null) {
+ throw new NullPointerException("Transform can not be a null argument!");
+ }
+
+ try {
+ SpinLock.dualLock(lock, transform.lock);
+ setUnsafe(transform.position, transform.rotation, transform.scale);
+ } finally {
+ SpinLock.dualUnlock(lock, transform.lock);
+ }
+ return this;
+ }
+
+ /**
+ * Atomically sets the value of this transform.
+ *
+ * @param world the world
+ * @param px the x coordinate of the position
+ * @param py the y coordinate of the position
+ * @param pz the z coordinate of the position
+ * @param rx the x coordinate of the quaternion
+ * @param ry the y coordinate of the quaternion
+ * @param rz the z coordinate of the quaternion
+ * @param rw the w coordinate of the quaternion
+ * @param sx the x coordinate of the scale
+ * @param sy the y coordinate of the scale
+ * @param sz the z coordinate of the scale
+ * @return this transform
+ */
+ public Transform set(World world, float px, float py, float pz, float rx, float ry, float rz, float rw, float sx, float sy, float sz) {
+ return this.set(new Point(world, px, py, pz), new Quaternionf(rx, ry, rz, rw), new Vector3f(sx, sy, sz));
+ }
+
+ /**
+ * Atomically sets this point to the given components
+ *
+ * @return this transform
+ */
+ public Transform set(Point p, Quaternionf r, Vector3f s) {
+ try {
+ lock.lock();
+ setUnsafe(p, r, s);
+ } finally {
+ lock.unlock();
+ }
+ return this;
+ }
+
+ private void setUnsafe(Point p, Quaternionf r, Vector3f s) {
+ this.position = p;
+ this.rotation = r;
+ this.scale = s;
+ }
+
+ /**
+ * Creates a Transform that is a copy of this transform
+ *
+ * @return the snapshot
+ */
+ public Transform copy() {
+ try {
+ lock.lock();
+ return new Transform(position, rotation, scale);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Gets a String representation of this transform
+ *
+ * Note: unsafe, could return torn values
+ *
+ * @return the string
+ */
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + StringUtil.toString(position, rotation, scale);
+ }
+
+ @Override
+ public int hashCode() {
+ HashCodeBuilder builder = new HashCodeBuilder(41, 63);
+ try {
+ lock.lock();
+ builder.append(position).append(rotation).append(scale);
+ } finally {
+ lock.unlock();
+ }
+ return builder.toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (!(other instanceof Transform)) {
+ return false;
+ }
+ Transform t = (Transform) other;
+ try {
+ SpinLock.dualLock(lock, t.lock);
+ return position.equals(t.position) && rotation.equals(t.rotation) && scale.equals(t.scale);
+ } finally {
+ SpinLock.dualUnlock(lock, t.lock);
+ }
+ }
+
+ /**
+ * Returns the 4x4 matrix that represents this transform object
+ */
+ public Matrix4f toMatrix() {
+ Matrix4f translate = Matrix4f.createTranslation(getPosition());
+ Matrix4f rotate = Matrix4f.createRotation(getRotation());
+ Matrix4f scale = Matrix4f.createScaling(getScale().toVector4(1));
+ return scale.mul(rotate).mul(translate);
+ }
+
+ /**
+ * Returns a unit vector that points in the forward direction of this transform
+ */
+ public Vector3f forwardVector() {
+ return Matrix3f.createRotation(getRotation()).transform(Vector3f.FORWARD);
+ }
+
+ /**
+ * Returns a unit vector that points right in relation to this transform
+ */
+ public Vector3f rightVector() {
+ return Matrix3f.createRotation(getRotation()).transform(Vector3f.RIGHT);
+ }
+
+ /**
+ * Returns a unit vector that points up in relation to this transform
+ */
+ public Vector3f upVector() {
+ return Matrix3f.createRotation(getRotation()).transform(Vector3f.UP);
+ }
+
+ /**
+ * Returns if this Transform is "empty" Empty is defined by Position, {@link Point}, of the transform equaling {@link Point#invalid}, Rotation, {@link org.spout.math.imaginary.Quaternionf}, of the transform equaling
+ * {@link org.spout.math.imaginary.Quaternionf#IDENTITY}, and Scale, {@link org.spout.math.vector.Vector3f}, equaling {@link org.spout.math.vector.Vector3f#ONE}.
+ *
+ * @return True if empty, false if not
+ */
+ public boolean isEmpty() {
+ try {
+ lock.lock();
+ return position.equals(Point.invalid) && rotation.equals(Quaternionf.IDENTITY) && scale.equals(Vector3f.ONE);
+ } finally {
+ lock.unlock();
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/geo/discrete/Transform2D.java b/src/main/java/org/spout/api/geo/discrete/Transform2D.java
new file mode 100644
index 0000000..2f96f20
--- /dev/null
+++ b/src/main/java/org/spout/api/geo/discrete/Transform2D.java
@@ -0,0 +1,98 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.geo.discrete;
+
+import org.spout.math.imaginary.Complexf;
+import org.spout.math.matrix.Matrix3f;
+import org.spout.math.vector.Vector2f;
+
+public class Transform2D {
+ private Vector2f position;
+ private Complexf rotation;
+ private Vector2f scale;
+
+ public Transform2D() {
+ this(Vector2f.ZERO, Complexf.IDENTITY, Vector2f.ONE);
+ }
+
+ public Transform2D(Vector2f position, Complexf rotation, Vector2f scale) {
+ this.position = position;
+ this.rotation = rotation;
+ this.scale = scale;
+ }
+
+ public void add(float x, float y) {
+ setPosition(getPosition().add(x, y));
+ }
+
+ public void setPosition(float x, float y) {
+ this.position = new Vector2f(x, y);
+ }
+
+ public void setPosition(Vector2f position) {
+ this.position = position;
+ }
+
+ public Vector2f getPosition() {
+ return position;
+ }
+
+ public void setScale(float scale) {
+ this.scale = new Vector2f(scale, scale);
+ }
+
+ public void setScale(float scaleX, float scaleY) {
+ this.scale = new Vector2f(scaleX, scaleY);
+ }
+
+ public void setScale(Vector2f scale) {
+ this.scale = scale;
+ }
+
+ public Vector2f getScale() {
+ return scale;
+ }
+
+ public void setRotation(float angle) {
+ this.rotation = Complexf.fromAngleDeg(angle);
+ }
+
+ public void setRotation(Complexf rotation) {
+ this.rotation = rotation;
+ }
+
+ public Complexf getRotation() {
+ return rotation;
+ }
+
+ public Matrix3f toMatrix() {
+ Matrix3f rotation = Matrix3f.createRotation(this.rotation);
+ Matrix3f translation = Matrix3f.createTranslation(position);
+ Matrix3f scale = Matrix3f.createScaling(this.scale.toVector3(1));
+ return scale.mul(rotation).mul(translation);
+ }
+}
diff --git a/src/main/java/org/spout/api/io/nbt/QuaternionTag.java b/src/main/java/org/spout/api/io/nbt/QuaternionTag.java
new file mode 100644
index 0000000..a1dc05b
--- /dev/null
+++ b/src/main/java/org/spout/api/io/nbt/QuaternionTag.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.io.nbt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spout.math.imaginary.Quaternionf;
+import org.spout.nbt.FloatTag;
+import org.spout.nbt.ListTag;
+import org.spout.nbt.Tag;
+import org.spout.nbt.util.NBTMapper;
+
+public class QuaternionTag extends ListTag {
+ public QuaternionTag(String name, Quaternionf q) {
+ super(name, FloatTag.class, quaternionToList(q));
+ }
+
+ private static List quaternionToList(Quaternionf q) {
+ List list = new ArrayList<>(4);
+ list.add(new FloatTag("", q.getX()));
+ list.add(new FloatTag("", q.getY()));
+ list.add(new FloatTag("", q.getZ()));
+ list.add(new FloatTag("", q.getW()));
+ return list;
+ }
+
+ @SuppressWarnings ("unchecked")
+ public static Quaternionf getValue(Tag> tag) {
+ try {
+ return getValue((ListTag) tag);
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+
+ public static Quaternionf getValue(ListTag list) {
+ if (list == null) {
+ return null;
+ }
+ return getValue(list.getValue());
+ }
+
+ public static Quaternionf getValue(List list) {
+ if (list == null || list.size() != 4) {
+ return null;
+ }
+ Float x = NBTMapper.toTagValue(list.get(0), Float.class, null);
+ Float y = NBTMapper.toTagValue(list.get(1), Float.class, null);
+ Float z = NBTMapper.toTagValue(list.get(2), Float.class, null);
+ Float w = NBTMapper.toTagValue(list.get(3), Float.class, null);
+ if (x == null || y == null || z == null || w == null) {
+ return null;
+ }
+ return new Quaternionf(x, y, z, w);
+ }
+}
diff --git a/src/main/java/org/spout/api/io/nbt/TransformTag.java b/src/main/java/org/spout/api/io/nbt/TransformTag.java
new file mode 100644
index 0000000..e61a3db
--- /dev/null
+++ b/src/main/java/org/spout/api/io/nbt/TransformTag.java
@@ -0,0 +1,92 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.io.nbt;
+
+import org.spout.api.geo.World;
+import org.spout.api.geo.discrete.Point;
+import org.spout.api.geo.discrete.Transform;
+import org.spout.math.imaginary.Quaternionf;
+import org.spout.math.vector.Vector3f;
+import org.spout.nbt.CompoundMap;
+import org.spout.nbt.CompoundTag;
+import org.spout.nbt.Tag;
+
+public class TransformTag extends CompoundTag {
+ public TransformTag(String name, Transform t) {
+ this(name, t.getPosition(), t.getRotation(), t.getScale());
+ }
+
+ public TransformTag(String name, float px, float py, float pz, float qx, float qy, float qz, float qw, float sx, float sy, float sz) {
+ this(name, new Vector3f(px, py, pz), new Quaternionf(qx, qy, qz, qw), new Vector3f(sx, sy, sz));
+ }
+
+ public TransformTag(String name, Vector3f p, Quaternionf q, Vector3f s) {
+ super(name, toMap(p, q, s));
+ }
+
+ private static CompoundMap toMap(Vector3f p, Quaternionf q, Vector3f s) {
+ CompoundMap map = new CompoundMap();
+ map.put(new Vector3Tag("pos", p));
+ map.put(new QuaternionTag("rot", q));
+ map.put(new Vector3Tag("scale", s));
+ return map;
+ }
+
+ public static Transform getValue(World w, Tag> tag) {
+ try {
+ return getValue(w, (CompoundTag) tag);
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+
+ public static Transform getValue(World w, CompoundTag map) {
+ if (map == null || w == null) {
+ return null;
+ }
+ return getValue(w, map.getValue());
+ }
+
+ public static Transform getValue(World w, CompoundMap map) {
+ if (map == null || w == null) {
+ return null;
+ }
+ Vector3f pVector = Vector3Tag.getValue(map.get("pos"));
+
+ Quaternionf r = QuaternionTag.getValue(map.get("rot"));
+
+ Vector3f s = Vector3Tag.getValue(map.get("scale"));
+
+ if (pVector == null || r == null || s == null) {
+ return null;
+ }
+
+ Point p = new Point(pVector, w);
+
+ return new Transform(p, r, s);
+ }
+}
diff --git a/src/main/java/org/spout/api/io/nbt/UUIDTag.java b/src/main/java/org/spout/api/io/nbt/UUIDTag.java
new file mode 100644
index 0000000..de06a3d
--- /dev/null
+++ b/src/main/java/org/spout/api/io/nbt/UUIDTag.java
@@ -0,0 +1,78 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.io.nbt;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.spout.nbt.ListTag;
+import org.spout.nbt.LongTag;
+import org.spout.nbt.Tag;
+import org.spout.nbt.util.NBTMapper;
+
+public class UUIDTag extends ListTag {
+ public UUIDTag(String name, UUID u) {
+ super(name, LongTag.class, UUIDToList(u));
+ }
+
+ private static List UUIDToList(UUID u) {
+ List list = new ArrayList<>(2);
+ list.add(new LongTag("", u.getMostSignificantBits()));
+ list.add(new LongTag("", u.getLeastSignificantBits()));
+ return list;
+ }
+
+ @SuppressWarnings ("unchecked")
+ public static UUID getValue(Tag> tag) {
+ try {
+ return getValue((ListTag) tag);
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+
+ public static UUID getValue(ListTag list) {
+ if (list == null) {
+ return null;
+ }
+ return getValue(list.getValue());
+ }
+
+ public static UUID getValue(List list) {
+ if (list == null || list.size() != 2) {
+ return null;
+ }
+ Long m = NBTMapper.toTagValue(list.get(0), Long.class, null);
+ Long l = NBTMapper.toTagValue(list.get(1), Long.class, null);
+
+ if (m == null || l == null) {
+ return null;
+ }
+ return new UUID(m, l);
+ }
+}
diff --git a/src/main/java/org/spout/api/io/nbt/Vector3Tag.java b/src/main/java/org/spout/api/io/nbt/Vector3Tag.java
new file mode 100644
index 0000000..9226112
--- /dev/null
+++ b/src/main/java/org/spout/api/io/nbt/Vector3Tag.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.io.nbt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.spout.math.vector.Vector3f;
+import org.spout.nbt.FloatTag;
+import org.spout.nbt.ListTag;
+import org.spout.nbt.Tag;
+import org.spout.nbt.util.NBTMapper;
+
+public class Vector3Tag extends ListTag {
+ public Vector3Tag(String name, Vector3f v) {
+ super(name, FloatTag.class, vector3ToList(v));
+ }
+
+ private static List vector3ToList(Vector3f v) {
+ List list = new ArrayList<>(3);
+ list.add(new FloatTag("", v.getX()));
+ list.add(new FloatTag("", v.getY()));
+ list.add(new FloatTag("", v.getZ()));
+ return list;
+ }
+
+ @SuppressWarnings ("unchecked")
+ public static Vector3f getValue(Tag> tag) {
+ try {
+ return getValue((ListTag) tag);
+ } catch (ClassCastException e) {
+ return null;
+ }
+ }
+
+ public static Vector3f getValue(ListTag list) {
+ if (list == null) {
+ return null;
+ }
+ return getValue(list.getValue());
+ }
+
+ public static Vector3f getValue(List list) {
+ if (list == null || list.size() != 3) {
+ return null;
+ }
+ Float x = NBTMapper.toTagValue(list.get(0), Float.class, null);
+ Float y = NBTMapper.toTagValue(list.get(1), Float.class, null);
+ Float z = NBTMapper.toTagValue(list.get(2), Float.class, null);
+
+ if (x == null || y == null || z == null) {
+ return null;
+ }
+
+ return new Vector3f(x, y, z);
+ }
+}
diff --git a/src/main/java/org/spout/api/material/BlockMaterial.java b/src/main/java/org/spout/api/material/BlockMaterial.java
new file mode 100644
index 0000000..11e2998
--- /dev/null
+++ b/src/main/java/org/spout/api/material/BlockMaterial.java
@@ -0,0 +1,556 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material;
+
+import java.util.Set;
+
+import com.flowpowered.commons.bytebit.ByteBitSet;
+import com.flowpowered.events.Cause;
+
+import com.google.common.collect.ImmutableSet;
+
+import org.spout.api.component.block.BlockComponent;
+import org.spout.api.entity.Entity;
+import org.spout.api.event.cause.MaterialCause;
+import org.spout.api.geo.cuboid.Block;
+import org.spout.api.geo.discrete.Point;
+import org.spout.api.material.basic.Air;
+import org.spout.api.material.basic.Solid;
+import org.spout.api.material.block.BlockFace;
+import org.spout.api.material.block.BlockFaces;
+import org.spout.math.GenericMath;
+import org.spout.math.vector.Vector3f;
+import org.spout.physics.ReactDefaults;
+import org.spout.physics.collision.shape.CollisionShape;
+
+/**
+ * Defines the specific characteristics of a Block
+ */
+@SuppressWarnings ("unchecked")
+public class BlockMaterial extends Material implements Placeable {
+ public static final BlockMaterial AIR = new Air();
+ public static final BlockMaterial UNBREAKABLE = new Solid("Unbreakable");
+ public static final BlockMaterial UNGENERATED = new Solid("Ungenerated").setInvisible();
+ public static final BlockMaterial ERROR = new Solid("Missing Plugin");
+ public static final BlockMaterial SOLID_BLUE = new Solid("SolidBlue");
+ public static final BlockMaterial SOLID_BROWN = new Solid("SolidBrown");
+ public static final BlockMaterial SOLID_GREEN = new Solid("SolidGreen");
+ public static final BlockMaterial SOLID_LIGHTGREEN = new Solid("SolidLightGreen");
+ public static final BlockMaterial SOLID_RED = new Solid("SolidRed");
+ public static final BlockMaterial SOLID_SKYBLUE = new Solid("SolidSkyBlue");
+ private final Set> components;
+ private ByteBitSet occlusion = new ByteBitSet(BlockFaces.NESWBT);
+ private float hardness = 0F;
+ private byte opacity = 0xF;
+ private boolean invisible = false;
+ //Collision
+ private CollisionShape shape;
+ private float mass = 1;
+ private float friction = ReactDefaults.DEFAULT_FRICTION_COEFFICIENT;
+ private float restitution = ReactDefaults.DEFAULT_RESTITUTION_COEFFICIENT;
+ private boolean isGhost = false;
+
+ public BlockMaterial(short dataMask, String name, CollisionShape shape, Class extends BlockComponent>... components) {
+ super(dataMask, name);
+ this.components = ImmutableSet.copyOf(components);
+ this.shape = shape;
+ }
+
+ public BlockMaterial(String name, int data, Material parent, String model, CollisionShape shape, Class extends BlockComponent>... components) {
+ super(name, data, parent, model);
+ this.components = ImmutableSet.copyOf(components);
+ this.shape = shape;
+ }
+
+ protected BlockMaterial(String name, short id, CollisionShape shape, Class extends BlockComponent>... components) {
+ super(name, id);
+ this.components = ImmutableSet.copyOf(components);
+ this.shape = shape;
+ }
+
+ protected BlockMaterial(String name, CollisionShape shape, Class extends BlockComponent>... components) {
+ super(name);
+ this.components = ImmutableSet.copyOf(components);
+ this.shape = shape;
+ }
+
+ /**
+ * Gets the block material with the given id, or null if none found
+ *
+ * @param id to get
+ * @return block, or null if none found
+ */
+ public static BlockMaterial get(short id) {
+ Material mat = Material.get(id);
+ if (!(mat instanceof BlockMaterial)) {
+ return null;
+ }
+
+ return (BlockMaterial) mat;
+ }
+
+ /**
+ * Gets the block (sub-)material with the given id and data, or null if none found
+ *
+ * @param id to get
+ * @return block, or null if none found
+ */
+ public static BlockMaterial get(short id, short data) {
+ Material m = Material.get(id, data);
+ if (m instanceof BlockMaterial) {
+ return (BlockMaterial) m;
+ }
+ return null;
+ }
+
+ /**
+ * Gets the associated block material with it's name. Case-insensitive.
+ *
+ * @param name to lookup
+ * @return material, or null if none found
+ */
+ public static BlockMaterial get(String name) {
+ Material mat = Material.get(name);
+ if (!(mat instanceof BlockMaterial)) {
+ return null;
+ }
+
+ return (BlockMaterial) mat;
+ }
+
+ @Override
+ public BlockMaterial getSubMaterial(short data) {
+ return (BlockMaterial) super.getSubMaterial(data);
+ }
+
+ /**
+ * Gets the hardness of this block
+ *
+ * @return hardness value
+ */
+ public float getHardness() {
+ return this.hardness;
+ }
+
+ /**
+ * Sets the hardness of this block
+ *
+ * @param hardness hardness value
+ * @return this material
+ */
+ public BlockMaterial setHardness(float hardness) {
+ this.hardness = hardness;
+ return this;
+ }
+
+ /**
+ * Gets the amount of light this block emits
+ *
+ * @return light level
+ */
+ public byte getLightLevel(short data) {
+ return 0;
+ }
+
+ /**
+ * Gets the amount of light this block emits
+ *
+ * @return light level
+ */
+ public byte getLightLevel() {
+ return getLightLevel(getData());
+ }
+
+ /**
+ * Gets the amount of light blocked by this block.
+ *
+ * 0xF (15) represents a fully opaque block.
+ *
+ * @return opacity
+ */
+ public byte getOpacity() {
+ return this.opacity;
+ }
+
+ /**
+ * Returns true if the block is opaque, false if not.
+ *
+ * @return True if opacity is 15, false if less than.
+ */
+ public boolean isOpaque() {
+ return this.opacity == 0xF;
+ }
+
+ /**
+ * Sets the amount of light blocked by this block.
+ *
+ * 0xF (15) represents a fully opaque block.
+ *
+ * @param level of opacity, a value from 0 to 15
+ * @return this material
+ */
+ public BlockMaterial setOpacity(int level) {
+ this.opacity = (byte) GenericMath.clamp(level, 0, 15);
+ return this;
+ }
+
+ /**
+ * Turns this Block Material in a fully opaque block, not letting light through from any side
Sets opacity to 15 and sets occlusion to all faces
+ *
+ * @return this Block Material
+ */
+ public BlockMaterial setOpaque() {
+ this.occlusion.set(BlockFaces.NESWBT);
+ return this.setOpacity(15);
+ }
+
+ /**
+ * Turns this Block Material in a fully transparent block, letting light through from all sides
Sets the opacity to 0 and sets occlusion to none
+ *
+ * @return this Block Material
+ */
+ public BlockMaterial setTransparent() {
+ this.occlusion.set(BlockFaces.NONE);
+ return this.setOpacity(0);
+ }
+
+ /**
+ * True if this block acts as an obstacle when placing a block on it false if not.
+ *
+ * If the block is not an obstacle, placement will replace this block.
+ *
+ * @return if this block acts as a placement obstacle
+ */
+ public boolean isPlacementObstacle() {
+ return true;
+ }
+
+ /**
+ * True if this block requires physic updates when a neighbor block changes, false if not.
+ *
+ * @return if this block requires physics updates
+ */
+ public boolean hasPhysics() {
+ return false;
+ }
+
+ /**
+ * Called when a block near to this material is changed.
+ *
+ * @param oldMaterial the previous material, or null if the update was not due to a material change
+ * @param block that got updated
+ * @return true if the block was updated
+ */
+ public void onUpdate(BlockMaterial oldMaterial, Block block) {
+ }
+
+ /**
+ * Performs the block destroy procedure
+ *
+ * @param block to destroy
+ * @return True if destroying was successful
+ */
+ public boolean destroy(Block block, Cause> cause) {
+ if (this.onDestroy(block, cause)) {
+ this.onPostDestroy(block);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Called when this block has to be destroyed.
This function performs the actual destruction of the block.
+ *
+ * @param block that got destroyed
+ * @return true if the destruction occurred
+ */
+ public boolean onDestroy(Block block, Cause> cause) {
+ return block.setMaterial(AIR, cause);
+ }
+
+ /**
+ * Called after this block has been destroyed.
This function performs possible post-destroy operations, such as effects.
+ *
+ * @param block of the material that got destroyed
+ */
+ public void onPostDestroy(Block block) {
+ }
+
+ /**
+ * Gets the occluded faces of this Block Material for the data value specified
Occluded faces do not let light though and require rendering behind it at those faces
+ *
+ * @param data value of the material
+ * @return the occluded faces
+ */
+ public ByteBitSet getOcclusion(short data) {
+ return this.occlusion;
+ }
+
+ /**
+ * Sets the occludes faces of this Block Material
Occluded faces do not let light though and require rendering behind it at those faces
+ *
+ * @param data of this Block Material
+ * @param faces to make this Block Material occlude
+ * @return this Block Material
+ */
+ public BlockMaterial setOcclusion(short data, BlockFaces faces) {
+ this.getOcclusion(data).set(faces);
+ return this;
+ }
+
+ /**
+ * Sets the occludes face of this Block Material
Occluded faces do not let light though and require rendering behind it at those faces
+ *
+ * @param data of this Block Material
+ * @param face to make this Block Material occlude
+ * @return this Block Material
+ */
+ public BlockMaterial setOcclusion(short data, BlockFace face) {
+ this.getOcclusion(data).set(face);
+ return this;
+ }
+
+ /**
+ * Gets if the a face should be rendered
+ *
+ * @param face the fact to render
+ * @param material the material of the neighbouring block
+ */
+ public boolean isFaceRendered(BlockFace face, BlockMaterial material) {
+ return true;
+ }
+
+ @Override
+ public boolean canPlace(Block block, short data, BlockFace against, Vector3f clickedPos, boolean isClickedBlock, Cause> cause) {
+ return canCreate(block, data, cause);
+ }
+
+ @Override
+ public void onPlacement(Block block, short data, BlockFace against, Vector3f clickedPos, boolean isClickedBlock, Cause> cause) {
+ this.onCreate(block, data, cause);
+ }
+
+ /**
+ * Checks the block to see if it can be created at that position
Orientation-specific checks are performed in the {@link #canPlace(org.spout.api.geo.cuboid.Block, short, org.spout.api.material.block.BlockFace, org.spout.math.vector.Vector3f, boolean, org.spout.api.event.Cause)} method
Use this method to see if creation is possible at a
+ * given position when not placed
+ *
+ * @param block this Block Material should be created in
+ * @param data for the material
+ * @param cause of this creation
+ * @return True if creation is possible, False if not
+ */
+ public boolean canCreate(Block block, short data, Cause> cause) {
+ return true;
+ }
+
+ /**
+ * Creates this Block Material at a block in the world
Orientation-specific changes are performed in the {@link #onPlacement(org.spout.api.geo.cuboid.Block, short, org.spout.api.material.block.BlockFace, org.spout.math.vector.Vector3f, boolean, org.spout.api.event.Cause)} method
Use this method to create the block at a given position
+ * when not placed
+ *
+ * @param block to create this Block Material in
+ * @param data for the material
+ * @param cause of this creation
+ */
+ public void onCreate(Block block, short data, Cause> cause) {
+ block.setMaterial(this, data, cause);
+ }
+
+ /**
+ * Returns true if the block is completely invisible
+ *
+ * @return True if the block should never be rendered
+ */
+ public boolean isInvisible() {
+ return this.invisible;
+ }
+
+ /**
+ * Turns this material invisible and sets it as non-occluding. Invisible blocks are not rendered.
+ */
+ public BlockMaterial setInvisible() {
+ this.invisible = true;
+ this.occlusion.set(BlockFaces.NONE);
+ return this;
+ }
+
+ /**
+ * Returns true if the block is transparent, false if not.
+ *
+ * @return True if opacity is 0, false if more than.
+ */
+ public boolean isTransparent() {
+ return this.opacity == 0;
+ }
+
+ /**
+ * Called by the dynamic block update system. If a material is changed into a material that it is not compatible with, then this will automatically trigger a block reset.
+ *
+ * @param m the other material
+ * @return true if the two materials are compatible
+ */
+ public boolean isCompatibleWith(BlockMaterial m) {
+ return (m.getId() == getId() && ((m.getData() ^ getData()) & getDataMask()) == 0);
+ }
+
+ /**
+ * Helper method to create a MaterialCause.
+ *
+ * Same as using new MaterialCause(material, block)
+ *
+ * @param block location of the event
+ * @return cause
+ */
+ public Cause toCause(Block block) {
+ return new MaterialCause<>(this, block);
+ }
+
+ /**
+ * Helper method to create a MaterialCause.
+ *
+ * Same as using new MaterialCause(material, block)
+ *
+ * @param p location of the event
+ * @return cause
+ */
+ public Cause toCause(Point p) {
+ return new MaterialCause<>(this, p.getWorld().getBlock(p));
+ }
+
+ public Set> getComponents() {
+ return components;
+ }
+
+ /**
+ * Returns if this BlockMaterial is a ghost object.
+ *
+ * @return True if ghost, false if not
+ */
+ public boolean isGhost() {
+ return isGhost;
+ }
+
+ /**
+ * Sets if this BlockMaterial should be a detector "ghost" material. This means any collisions with this BlockMaterial will not incur adjustments for the {@link Entity} which collided: instead
+ * callbacks will be alerted and the Entity will be able to move freely through this BlockMaterial (by default).
If this BlockMaterial has a null {@link CollisionShape}, this setting will have no
+ * effect until a reference is set.
+ */
+ public void setGhost(final boolean isGhost) {
+ this.isGhost = isGhost;
+ }
+
+ /**
+ * Get the mass of this BlockMaterial
+ *
+ * @return The mass
+ */
+ public float getMass() {
+ return mass;
+ }
+
+ /**
+ * Sets the mass of this BlockMaterial
+ *
+ * @param mass The new mass
+ * @return This material, for chaining
+ * @throws IllegalArgumentException If provided mass is < 1f
+ */
+ public BlockMaterial setMass(final float mass) {
+ if (mass < 1) {
+ throw new IllegalArgumentException("Mass must be greater than or equal to 1f");
+ }
+ this.mass = mass;
+ return this;
+ }
+
+ /**
+ * Get the friction of this BlockMaterial
+ *
+ * @return The friction
+ */
+ public float getFriction() {
+ return friction;
+ }
+
+ /**
+ * Sets the friction of this BlockMaterial
+ *
+ * @param friction The new friction
+ * @return This material, for chaining
+ * @throws IllegalArgumentException If provided friction is < 0f
+ */
+ public BlockMaterial setFriction(final float friction) {
+ if (friction < 0 || friction > 1) {
+ throw new IllegalArgumentException("Friction must be between 0 and 1 (inclusive)");
+ }
+ this.friction = friction;
+ return this;
+ }
+
+ /**
+ * Get the restitution of this BlockMaterial
+ *
+ * @return The restitution
+ */
+ public float getRestitution() {
+ return restitution;
+ }
+
+ /**
+ * Sets the restitution of this BlockMaterial
+ *
+ * @param restitution The new restitution
+ * @return This material, for chaining
+ * @throws IllegalArgumentException If provided restitution is < 0f
+ */
+ public BlockMaterial setRestitution(final float restitution) {
+ if (restitution < 0 || restitution > 1) {
+ throw new IllegalArgumentException("Restitution must be between 0 and 1 (inclusive)");
+ }
+ this.restitution = restitution;
+ return this;
+ }
+
+ /**
+ * Gets the {@link CollisionShape} this BlockMaterial has.
+ *
+ * @return the collision shape
+ */
+ public CollisionShape getShape() {
+ return shape;
+ }
+
+ /**
+ * Sets the {@link CollisionShape} this BlockMaterial has/
+ *
+ * @param shape The new collision shape
+ * @return This material, for chaining
+ */
+ public BlockMaterial setShape(final CollisionShape shape) {
+ this.shape = shape;
+ return this;
+ }
+}
diff --git a/src/main/java/org/spout/api/material/Material.java b/src/main/java/org/spout/api/material/Material.java
new file mode 100644
index 0000000..93d48b0
--- /dev/null
+++ b/src/main/java/org/spout/api/material/Material.java
@@ -0,0 +1,356 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material;
+
+/**
+ * Defines the characteristics of Blocks or Items.
+ */
+
+import com.flowpowered.commons.LogicUtil;
+
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.spout.math.GenericMath;
+
+public abstract class Material extends MaterialRegistry {
+ private final short id;
+ private final short data;
+ private final String name;
+ private final boolean isSubMaterial;
+ private final Material parent;
+ private final Material root;
+ private String displayName;
+ private int maxStackSize = 64;
+ private short maxData = Short.MAX_VALUE;
+ private final AtomicReference subMaterials;
+ private Material[] submaterialsContiguous = null;
+ private volatile boolean submaterialsDirty = true;
+ private final short dataMask;
+
+ /**
+ * Creates a material with a dataMask, name
+ */
+ public Material(short dataMask, String name) {
+ this.isSubMaterial = false;
+ this.displayName = name;
+ this.name = getClass().getCanonicalName() + "_" + name.replace(' ', '_');
+ this.parent = this;
+ this.data = 0;
+ this.id = (short) MaterialRegistry.register(this);
+ this.subMaterials = MaterialRegistry.getSubMaterialReference(this.id);
+ this.dataMask = dataMask;
+ this.root = this;
+ }
+
+ /**
+ * Creates and registers a material
+ *
+ * @param name of the material
+ */
+ public Material(String name) {
+ this((short) 0, name);
+ }
+
+ /**
+ * Creates and registers a sub material
+ *
+ * @param name of the material
+ * @param parent material
+ */
+ public Material(String name, int data, Material parent) {
+ this(name, data, parent, null);
+ }
+
+ /**
+ * Creates and registers a sub material
+ *
+ * @param name of the material
+ * @param parent material
+ */
+ public Material(String name, int data, Material parent, String model) {
+ this.isSubMaterial = true;
+ this.displayName = name;
+ this.name = name.replace(' ', '_');
+ this.parent = parent;
+ this.data = (short) data;
+ this.id = (short) MaterialRegistry.register(this);
+ this.subMaterials = MaterialRegistry.getSubMaterialReference(this.id);
+ this.dataMask = parent.getDataMask();
+ this.root = parent.getRoot();
+ }
+
+ /**
+ * Creates a material with a reserved id
+ *
+ * @param name of the material
+ * @param id to reserve
+ */
+ protected Material(String name, short id) {
+ this.isSubMaterial = false;
+ this.displayName = name;
+ this.name = name.replace(' ', '_');
+ this.parent = this;
+ this.data = 0;
+ this.id = (short) MaterialRegistry.register(this, id);
+ this.subMaterials = MaterialRegistry.getSubMaterialReference(this.id);
+ this.dataMask = 0;
+ this.root = this;
+ }
+
+ public final short getId() {
+ return this.id;
+ }
+
+ /**
+ * Gets the data value associated with this material. if this material does not have or is not a sub material, then (getData() & getDataMask()) is equal to zero.
+ *
+ * @return data value
+ */
+ public final short getData() {
+ return this.data;
+ }
+
+ /**
+ * Gets the data mask for this material, and sub-materials. When determining sub-material, this mask is applied to the data before the comparison is performed.
+ *
+ * @return data mask
+ */
+ public final short getDataMask() {
+ return this.dataMask;
+ }
+
+ /**
+ * Checks if this material is a sub material or not
+ *
+ * @return true if it is a sub material
+ */
+ public final boolean isSubMaterial() {
+ return isSubMaterial;
+ }
+
+ /**
+ * Checks if this material has other materials mapped by data
+ *
+ * @return true if this material has sub materials
+ */
+ public final boolean hasSubMaterials() {
+ return this.subMaterials.get().length > 1;
+ }
+
+ /**
+ * Gets all sub materials of this material
+ *
+ * @return an array of sub materials
+ */
+ public final Material[] getSubMaterials() {
+ if (submaterialsDirty) {
+ int materialCount = 0;
+ Material[] sm = subMaterials.get();
+ for (int i = 0; i < sm.length; i++) {
+ if (sm[i] != null) {
+ materialCount++;
+ }
+ }
+ Material[] newSubmaterials = new Material[materialCount];
+ materialCount = 0;
+ for (int i = 0; i < sm.length; i++) {
+ if (sm[i] != null) {
+ newSubmaterials[materialCount++] = sm[i];
+ }
+ }
+ this.submaterialsContiguous = newSubmaterials;
+ submaterialsDirty = false;
+ }
+ Material[] sm = submaterialsContiguous;
+ return Arrays.copyOf(sm, sm.length);
+ }
+
+ /**
+ * Recursively gets the sub material mapped to the data value specified
+ *
+ * @param data to search for
+ * @return the sub material, or this material if not found
+ */
+ public Material getSubMaterial(short data) {
+ short maskedData = (short) (data & dataMask);
+ return subMaterials.get()[maskedData];
+ }
+
+ /**
+ * Registers the sub material for this material
+ *
+ * @param material to register
+ */
+ public final void registerSubMaterial(Material material) {
+ submaterialsDirty = true;
+ try {
+ int data = material.data & 0xFFFF;
+ if ((data & dataMask) != data) {
+ throw new IllegalArgumentException("Sub material of: " + material.getId() + " with data value: " + data + " is outside data mask: " + Integer.toHexString(dataMask));
+ }
+ if (material.isSubMaterial) {
+ if (material.getParentMaterial() == this) {
+ boolean success = false;
+ while (!success) {
+ Material[] sm = subMaterials.get();
+ if (data >= sm.length) {
+ int newSize = GenericMath.roundUpPow2(data + (data >> 1) + 1);
+ Material[] newSubmaterials = new Material[newSize];
+ System.arraycopy(sm, 0, newSubmaterials, 0, sm.length);
+ success = subMaterials.compareAndSet(sm, newSubmaterials);
+ } else {
+ success = true;
+ }
+ }
+ Material[] sm = subMaterials.get();
+ if (sm[data] == null) {
+ sm[data] = material;
+ } else {
+ throw new IllegalArgumentException("Two sub material registered for the same data value");
+ }
+ } else {
+ throw new IllegalArgumentException("Sub Material is registered to a material different than the parent!");
+ }
+ } else {
+ throw new IllegalArgumentException("Material is not a valid sub material!");
+ }
+ } finally {
+ submaterialsDirty = true;
+ }
+ }
+
+ /**
+ * Gets the parent of this sub material
+ *
+ * @return the material of the parent
+ */
+ public Material getParentMaterial() {
+ return this.parent;
+ }
+
+ /**
+ * Gets the root parent of this sub material
+ *
+ * @return the material root
+ */
+ public Material getRoot() {
+ return this.root;
+ }
+
+ /**
+ * Gets the name of this material
+ *
+ * @return the name
+ */
+ public final String getName() {
+ return this.name;
+ }
+
+ /**
+ * Gets the display name of this material
+ *
+ * @return the display name
+ */
+ public final String getDisplayName() {
+ return this.displayName;
+ }
+
+ /**
+ * Sets the display name of this material
+ *
+ * @param name the new display name
+ */
+ public final void setDisplayName(String name) {
+ this.displayName = name;
+ }
+
+ /**
+ * Gets the maximum size of a stack of this material
+ *
+ * @return the current max size
+ */
+ public final int getMaxStackSize() {
+ return this.maxStackSize;
+ }
+
+ /**
+ * Sets the maximum size of a stack of this material
+ *
+ * @param newValue the new maximum stack size
+ */
+ public final void setMaxStackSize(int newValue) {
+ this.maxStackSize = newValue;
+ }
+
+ /**
+ * Gets the maximum data a stack of this material can have
+ */
+ public final short getMaxData() {
+ return this.maxData;
+ }
+
+ /**
+ * Sets the maximum of the data value this material can have
+ *
+ * @param newValue the new maximum data
+ */
+ public final void setMaxData(short newValue) {
+ this.maxData = newValue;
+ }
+
+ public boolean isMaterial(Material... materials) {
+ if (LogicUtil.equalsAny(this, materials)) {
+ return true;
+ }
+ if (this.getRoot() != this && LogicUtil.equalsAny(this.getRoot(), materials)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (other instanceof Material) {
+ return other == this;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Material {" + getName() + "}";
+ }
+
+ /**
+ * Indicates that the dataMask covers the least significant bits.
This method is used when verifying that the dataMask is set correctly
+ */
+ public boolean hasLSBDataMask() {
+ return true;
+ }
+}
diff --git a/src/main/java/org/spout/api/material/MaterialRegistry.java b/src/main/java/org/spout/api/material/MaterialRegistry.java
new file mode 100644
index 0000000..0b6d6c8
--- /dev/null
+++ b/src/main/java/org/spout/api/material/MaterialRegistry.java
@@ -0,0 +1,267 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material;
+
+import com.flowpowered.commons.store.MemoryStore;
+
+import java.io.File;
+import java.util.HashSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.spout.api.Server;
+import org.spout.api.Spout;
+import org.spout.api.material.block.BlockFullState;
+import org.spout.api.store.BinaryFileStore;
+import org.spout.api.util.SyncedStringMap;
+import org.spout.math.GenericMath;
+
+/**
+ * Handles all registered materials on the server statically.
+ */
+public abstract class MaterialRegistry {
+ private final static ConcurrentHashMap nameLookup = new ConcurrentHashMap<>(1000);
+ private final static int MAX_SIZE = 1 << 16;
+ @SuppressWarnings ("unchecked")
+ private final static AtomicReference[] materialLookup = new AtomicReference[MAX_SIZE];
+ private static boolean setup = false;
+ private static SyncedStringMap materialRegistry;
+ private final static Material[] NULL_MATERIAL_ARRAY = new Material[] {null};
+
+ static {
+ for (int i = 0; i < materialLookup.length; i++) {
+ materialLookup[i] = new AtomicReference<>();
+ materialLookup[i].set(NULL_MATERIAL_ARRAY);
+ }
+ }
+
+ /**
+ * Sets up the material registry for its first use. May not be called more than once.
This attempts to load the materials.dat file from the 'worlds' directory into memory.
+ *
+ * Can throw an {@link IllegalStateException} if the material registry has already been setup.
+ *
+ * @return StringToUniqueIntegerMap of registered materials
+ */
+ public static SyncedStringMap setupRegistry() {
+ if (setup) {
+ throw new IllegalStateException("Can not setup material registry twice!");
+ }
+ if (Spout.getPlatform().isServer()) {
+ setupServer();
+ } else {
+ setupClient();
+ }
+
+ setup = true;
+ return materialRegistry;
+ }
+
+ private static void setupServer() {
+ File serverItemMap = new File(((Server) Spout.getEngine()).getWorldManager().getWorldFolder(), "materials.dat");
+ BinaryFileStore store = new BinaryFileStore(serverItemMap);
+ materialRegistry = SyncedStringMap.create(null, store, 1, Short.MAX_VALUE, Material.class.getName());
+ if (serverItemMap.exists()) {
+ store.load();
+ }
+ }
+
+ private static void setupClient() {
+ materialRegistry = SyncedStringMap.create(null, new MemoryStore(), 1, Short.MAX_VALUE, Material.class.getName());
+ }
+
+ /**
+ * Registers the material in the material lookup service
+ *
+ * @param material to register
+ * @return id of the material registered
+ */
+ protected static int register(Material material) {
+ if (material.isSubMaterial()) {
+ material.getParentMaterial().registerSubMaterial(material);
+ nameLookup.put(formatName(material.getDisplayName()), material);
+ return material.getParentMaterial().getId();
+ } else {
+ int id = materialRegistry.register(material.getName());
+ Material[] subArray = new Material[] {material};
+ if (!materialLookup[id].compareAndSet(NULL_MATERIAL_ARRAY, subArray)) {
+ throw new IllegalArgumentException(materialLookup[id].get() + " is already mapped to id: " + material.getId() + "!");
+ }
+
+ nameLookup.put(formatName(material.getDisplayName()), material);
+ return id;
+ }
+ }
+
+ protected static AtomicReference getSubMaterialReference(short id) {
+ return materialLookup[id];
+ }
+
+ /**
+ * Registers the material in the material lookup service
+ *
+ * @param material to register
+ * @return id of the material registered.
+ */
+ protected static int register(Material material, int id) {
+ materialRegistry.register(material.getName(), id);
+ Material[] subArray = new Material[] {material};
+ if (!materialLookup[id].compareAndSet(NULL_MATERIAL_ARRAY, subArray)) {
+ throw new IllegalArgumentException(materialLookup[id].get()[0] + " is already mapped to id: " + material.getId() + "!");
+ }
+
+ nameLookup.put(formatName(material.getName()), material);
+ return id;
+ }
+
+ /**
+ * Gets the material from the given id
+ *
+ * @param id to get
+ * @return material or null if none found
+ */
+ public static Material get(short id) {
+ if (id < 0 || id >= materialLookup.length) {
+ return null;
+ }
+ return materialLookup[id].get()[0];
+ }
+
+ /**
+ * Gets the material from the given id and data
+ *
+ * @param id to get
+ * @param data to get
+ * @return material or null if none found
+ */
+ public static Material get(short id, short data) {
+ if (id < 0 || id >= materialLookup.length) {
+ return null;
+ }
+ Material[] parent = materialLookup[id].get();
+ if (parent[0] == null) {
+ return null;
+ }
+
+ data &= parent[0].getDataMask();
+ return materialLookup[id].get()[data];
+ }
+
+ /**
+ * Gets the material for the given BlockFullState
+ *
+ * @param state the full state of the block
+ * @return Material of the BlockFullState
+ */
+ public static Material get(BlockFullState state) {
+ return get(state.getPacked());
+ }
+
+ /**
+ * Gets the material for the given packed full state
+ *
+ * @param state the full state of the block
+ * @return Material of the id
+ */
+ public static BlockMaterial get(int packedState) {
+ short id = BlockFullState.getId(packedState);
+ if (id < 0 || id >= materialLookup.length) {
+ return null;
+ }
+ Material[] material = materialLookup[id].get();
+ if (material[0] == null) {
+ return null;
+ }
+ return (BlockMaterial) material[BlockFullState.getData(packedState) & (material[0].getDataMask())];
+ }
+
+ /**
+ * Returns all current materials in the game
+ *
+ * @return an array of all materials
+ */
+ public static Material[] values() {
+ //TODO: This is wrong, need to count # of registered materials
+ HashSet set = new HashSet<>(1000);
+ for (int i = 0; i < materialLookup.length; i++) {
+ if (materialLookup[i].get() != null) {
+ set.add(materialLookup[i].get()[0]);
+ }
+ }
+ return set.toArray(new Material[0]);
+ }
+
+ /**
+ * Gets the associated material with its name. Case-insensitive.
+ *
+ * @param name to lookup
+ * @return material, or null if none found
+ */
+ public static Material get(String name) {
+ return nameLookup.get(formatName(name));
+ }
+
+ /**
+ * Returns a human legible material name from the full material.
+ *
+ * This will strip any '_' and replace with spaces, strip out extra whitespace, and lowercase the material name.
+ *
+ * @return human legible name of the material.
+ */
+ private static String formatName(String matName) {
+ return matName.trim().replaceAll(" ", "_").toLowerCase();
+ }
+
+ /**
+ * Gets the minimum data mask required to account for all sub-materials of the material
+ *
+ * @param m the material
+ * @return the minimum data mask
+ */
+ public static short getMinimumDatamask(Material m) {
+ Material root = m;
+ while (root.isSubMaterial()) {
+ root = m.getParentMaterial();
+ }
+
+ if (root.getData() != 0) {
+ throw new IllegalStateException("Root materials must have data set to zero");
+ }
+ Material[] subMaterials = root.getSubMaterials();
+
+ short minimumMask = 0;
+
+ for (Material sm : subMaterials) {
+ minimumMask |= sm.getData() & 0xFFFF;
+ }
+
+ if (m.hasLSBDataMask()) {
+ minimumMask = (short) (GenericMath.roundUpPow2(minimumMask + 1) - 1);
+ }
+
+ return minimumMask;
+ }
+}
diff --git a/src/main/java/org/spout/api/material/Placeable.java b/src/main/java/org/spout/api/material/Placeable.java
new file mode 100644
index 0000000..09d6936
--- /dev/null
+++ b/src/main/java/org/spout/api/material/Placeable.java
@@ -0,0 +1,63 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material;
+
+import com.flowpowered.events.Cause;
+
+import org.spout.api.geo.cuboid.Block;
+import org.spout.api.material.block.BlockFace;
+import org.spout.math.vector.Vector3f;
+
+/**
+ * An interface defining a {@link Material} that can be placed
+ */
+public interface Placeable {
+ /**
+ * Called when this block is about to be placed (before {@link #onPlacement(Block, short, BlockFace, boolean)}), checking if placement is allowed or not.
+ *
+ * @param block to place
+ * @param data block data to use during placement
+ * @param against face against the block is placed
+ * @param isClickedBlock whether the block is to be placed at the clicked block
+ * @param cause the cause of the placement
+ * @return true if placement is allowed
+ */
+ public boolean canPlace(Block block, short data, BlockFace against, Vector3f clickedPos, boolean isClickedBlock, Cause> cause);
+
+ /**
+ * Called when this block is placed, handling the actual placement
This method should only change properties that rely on the face it is placed against, or in what way it is placed. All other
+ * logic should be performed in onCreate.
+ *
+ * @param block to affect
+ * @param data block data to use during placement
+ * @param against face against the block is placed
+ * @param clickedPos relative position the block was clicked to place this block
+ * @param isClickedBlock whether the block is being placed at the clicked block
+ * @param cause the cause of the placement
+ */
+ public void onPlacement(Block block, short data, BlockFace against, Vector3f clickedPos, boolean isClickedBlock, Cause> cause);
+}
diff --git a/src/main/java/org/spout/api/material/basic/Air.java b/src/main/java/org/spout/api/material/basic/Air.java
new file mode 100644
index 0000000..11ebab1
--- /dev/null
+++ b/src/main/java/org/spout/api/material/basic/Air.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material.basic;
+
+import com.flowpowered.events.Cause;
+import org.spout.api.geo.cuboid.Block;
+
+import org.spout.api.material.BlockMaterial;
+
+public final class Air extends BlockMaterial {
+ @SuppressWarnings ("unchecked")
+ public Air() {
+ super("Air", (short) 0, null);
+ this.setTransparent().setInvisible();
+ }
+
+ @Override
+ public boolean isPlacementObstacle() {
+ return false;
+ }
+
+ @Override
+ public boolean onDestroy(Block block, Cause> cause) {
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/org/spout/api/material/basic/Solid.java b/src/main/java/org/spout/api/material/basic/Solid.java
new file mode 100644
index 0000000..201391f
--- /dev/null
+++ b/src/main/java/org/spout/api/material/basic/Solid.java
@@ -0,0 +1,39 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material.basic;
+
+import org.spout.api.material.BlockMaterial;
+
+import org.spout.physics.collision.shape.BoxShape;
+import org.spout.physics.math.Vector3;
+
+public class Solid extends BlockMaterial {
+ public Solid(String name) {
+ super((short) 0, name, new BoxShape(new Vector3(1f, 1f, 1f)));
+ setHardness(100);
+ }
+}
diff --git a/src/main/java/org/spout/api/material/block/BlockFace.java b/src/main/java/org/spout/api/material/block/BlockFace.java
new file mode 100644
index 0000000..77f3c6b
--- /dev/null
+++ b/src/main/java/org/spout/api/material/block/BlockFace.java
@@ -0,0 +1,142 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material.block;
+
+import com.flowpowered.commons.bytebit.ByteBitMask;
+
+import java.io.Serializable;
+
+import gnu.trove.map.hash.TIntObjectHashMap;
+
+import org.spout.math.imaginary.Quaternionf;
+import org.spout.math.vector.Vector3f;
+import org.spout.math.vector.Vector3i;
+
+/**
+ * Indicates the facing of a Block
+ */
+public enum BlockFace implements ByteBitMask, Serializable {
+ TOP(0x1, 0, 1, 0, Quaternionf.fromAngleDegAxis(-90, 1, 0, 0)),
+ BOTTOM(0x2, 0, -1, 0, Quaternionf.fromAngleDegAxis(90, 1, 0, 0), TOP),
+ NORTH(0x4, -1, 0, 0, Quaternionf.fromAngleDegAxis(-90, 0, 1, 0)),
+ SOUTH(0x8, 1, 0, 0, Quaternionf.fromAngleDegAxis(90, 0, 1, 0), NORTH),
+ EAST(0x10, 0, 0, -1, Quaternionf.fromAngleDegAxis(180, 0, 1, 0)),
+ WEST(0x20, 0, 0, 1, Quaternionf.fromAngleDegAxis(0, 0, 1, 0), EAST),
+ THIS(0x40, 0, 0, 0, Quaternionf.IDENTITY);
+ private final byte mask;
+ private final Vector3f offset;
+ private final Vector3i intOffset;
+ private final Quaternionf direction;
+ private BlockFace opposite = this;
+ private static final TIntObjectHashMap OFFSET_MAP = new TIntObjectHashMap<>(7);
+ private static final long serialVersionUID = 1L;
+
+ static {
+ for (BlockFace face : values()) {
+ OFFSET_MAP.put(getOffsetHash(face.getOffset()), face);
+ }
+ }
+
+ private BlockFace(int mask, int dx, int dy, int dz, Quaternionf direction, BlockFace opposite) {
+ this(mask, dx, dy, dz, direction);
+ this.opposite = opposite;
+ opposite.opposite = this;
+ }
+
+ private BlockFace(int mask, int dx, int dy, int dz, Quaternionf direction) {
+ this.offset = new Vector3f(dx, dy, dz);
+ this.intOffset = new Vector3i(dx, dy, dz);
+ this.direction = direction;
+ this.mask = (byte) mask;
+ }
+
+ private static int getOffsetHash(Vector3f offset) {
+ int x = offset.getFloorX();
+ int y = offset.getFloorY();
+ int z = offset.getFloorZ();
+ x += 1;
+ y += 1;
+ z += 1;
+ return x | y << 2 | z << 4;
+ }
+
+ /**
+ * Represents the rotation of the BlockFace in the world as a Quaternion. This is the rotation form the west face to this face.
+ *
+ * @return the direction of the blockface.
+ */
+ public Quaternionf getDirection() {
+ return this.direction;
+ }
+
+ /**
+ * Represents the directional offset of this Blockface as a Vector3.
+ *
+ * @return the offset of this directional.
+ */
+ public Vector3f getOffset() {
+ return this.offset;
+ }
+
+ /**
+ * Represents the directional offset of this Blockface as an IntVector3.
+ *
+ * @return the offset of this directional.
+ */
+ public Vector3i getIntOffset() {
+ return this.intOffset;
+ }
+
+ /**
+ * Gets the opposite BlockFace. If this BlockFace has no opposite the method will return itself.
+ *
+ * @return the opposite BlockFace, or this if it has no opposite.
+ */
+ public BlockFace getOpposite() {
+ return this.opposite;
+ }
+
+ @Override
+ public byte getMask() {
+ return this.mask;
+ }
+
+ /**
+ * Uses a yaw angle to get the north, east, west or south face which points into the same direction.
+ *
+ * @param yaw to use
+ * @return the block face
+ */
+ public static BlockFace fromYaw(float yaw) {
+ return BlockFaces.WSEN.get(Math.round(yaw / 90f) & 0x3);
+ }
+
+ public static BlockFace fromOffset(Vector3f offset) {
+ offset = offset.normalize().round();
+ return OFFSET_MAP.get(getOffsetHash(offset));
+ }
+}
diff --git a/src/main/java/org/spout/api/material/block/BlockFaces.java b/src/main/java/org/spout/api/material/block/BlockFaces.java
new file mode 100644
index 0000000..2185e7e
--- /dev/null
+++ b/src/main/java/org/spout/api/material/block/BlockFaces.java
@@ -0,0 +1,378 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material.block;
+
+import com.flowpowered.commons.bytebit.ByteBitMask;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Random;
+
+import gnu.trove.map.hash.TByteObjectHashMap;
+
+import org.spout.math.vector.Vector3f;
+
+/**
+ * Contains several BlockFace array constants and functions to operate on them
+ */
+public class BlockFaces implements Iterable, ByteBitMask {
+ private static final TByteObjectHashMap offsetHash = new TByteObjectHashMap<>();
+
+ static {
+ for (BlockFace face1 : new BlockFace[] {BlockFace.THIS, BlockFace.TOP,
+ BlockFace.BOTTOM}) {
+ for (BlockFace face2 : new BlockFace[] {BlockFace.THIS,
+ BlockFace.WEST, BlockFace.EAST}) {
+ for (BlockFace face3 : new BlockFace[] {BlockFace.THIS,
+ BlockFace.NORTH, BlockFace.SOUTH}) {
+ BlockFaces faces = new BlockFaces(face1, face2, face3);
+ Vector3f offset = faces.getOffset();
+ byte hash = getOffsetHash(offset);
+ offsetHash.put(hash, faces);
+ }
+ }
+ }
+ }
+
+ /**
+ * The [top-bottom] faces
+ */
+ public static final BlockFaces TB = new BlockFaces(BlockFace.TOP, BlockFace.BOTTOM);
+ /**
+ * The [bottom-top] faces
+ */
+ public static final BlockFaces BT = new BlockFaces(BlockFace.BOTTOM, BlockFace.TOP);
+ /**
+ * The [north-south] faces
+ */
+ public static final BlockFaces NS = new BlockFaces(BlockFace.NORTH, BlockFace.SOUTH);
+ /**
+ * The [east-west] faces
+ */
+ public static final BlockFaces EW = new BlockFaces(BlockFace.EAST, BlockFace.WEST);
+ /**
+ * The [north-east-south-west] faces
+ */
+ public static final BlockFaces NESW = new BlockFaces(BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST);
+ /**
+ * The [north-south-east-west] faces
+ */
+ public static final BlockFaces NSEW = new BlockFaces(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, BlockFace.WEST);
+ /**
+ * The [east-west-south-north] faces
+ */
+ public static final BlockFaces EWSN = new BlockFaces(BlockFace.EAST, BlockFace.WEST, BlockFace.SOUTH, BlockFace.NORTH);
+ /**
+ * The [north-south-west-east] faces
+ */
+ public static final BlockFaces NSWE = new BlockFaces(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST);
+ /**
+ * The [south-west-north-east] faces
+ */
+ public static final BlockFaces SWNE = new BlockFaces(BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST);
+ /**
+ * The [west-north-east-south] faces
+ */
+ public static final BlockFaces WNES = new BlockFaces(BlockFace.WEST, BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH);
+ /**
+ * The [west-south-east-north] faces
+ */
+ public static final BlockFaces WSEN = new BlockFaces(BlockFace.WEST, BlockFace.SOUTH, BlockFace.EAST, BlockFace.NORTH);
+ /**
+ * The [south-north-east-west] faces
+ */
+ public static final BlockFaces SNEW = new BlockFaces(BlockFace.SOUTH, BlockFace.NORTH, BlockFace.EAST, BlockFace.WEST);
+ /**
+ * The [west-east-south-north] faces
+ */
+ public static final BlockFaces WESN = new BlockFaces(BlockFace.WEST, BlockFace.EAST, BlockFace.SOUTH, BlockFace.NORTH);
+ /**
+ * The [south-north-west-east] faces
+ */
+ public static final BlockFaces SNWE = new BlockFaces(BlockFace.SOUTH, BlockFace.NORTH, BlockFace.WEST, BlockFace.EAST);
+ /**
+ * The [east-south-west-north] faces
+ */
+ public static final BlockFaces ESWN = new BlockFaces(BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST, BlockFace.NORTH);
+ /**
+ * The [east-west-north-south] faces
+ */
+ public static final BlockFaces EWNS = new BlockFaces(BlockFace.EAST, BlockFace.WEST, BlockFace.NORTH, BlockFace.SOUTH);
+ /**
+ * The [north-east-south-west-bottom] faces
+ */
+ public static final BlockFaces NESWB = NESW.append(BlockFace.BOTTOM);
+ /**
+ * The [east-west-south-north-bottom] faces
+ */
+ public static final BlockFaces EWSNB = EWSN.append(BlockFace.BOTTOM);
+ /**
+ * The [north-east-south-west-top] faces
+ */
+ public static final BlockFaces NESWT = NESW.append(BlockFace.TOP);
+ /**
+ * The [north-east-south-west-bottom-top] faces
+ */
+ public static final BlockFaces NESWBT = NESW.append(BT);
+ /**
+ * The [bottom-top-east-west-north-south] faces
+ */
+ public static final BlockFaces BTEWNS = BT.append(EWNS);
+ /**
+ * The [bottom-top-north-south-west-east] faces
+ */
+ public static final BlockFaces BTNSWE = BT.append(NSWE);
+ /**
+ * The [north-south-east-west-bottom] faces
+ */
+ public static final BlockFaces NSEWB = NSEW.append(BlockFace.BOTTOM);
+ /**
+ * The [north-south-west-east-bottom] faces
+ */
+ public static final BlockFaces NSWEB = NSWE.append(BlockFace.BOTTOM);
+ /**
+ * The [north-east-south-west-bottom-this] faces
+ */
+ public static final BlockFaces NESWBTHIS = NESWB.append(BlockFace.THIS);
+ /**
+ * The [top-bottom-north-south-east-west-this] faces
+ */
+ public static final BlockFaces ALL = new BlockFaces(BlockFace.values());
+ /**
+ * A constant containing no faces at all
+ */
+ public static final BlockFaces NONE = new BlockFaces();
+ private final byte mask;
+ private final BlockFace[] faces;
+ private final Vector3f offset;
+
+ public BlockFaces(BlockFace... blockfaces) {
+ this.faces = blockfaces;
+ byte mask = 0;
+ Vector3f offsetc = Vector3f.ZERO;
+ for (BlockFace face : this.faces) {
+ offsetc = offsetc.add(face.getOffset());
+ mask |= face.getMask();
+ }
+ offset = offsetc;
+ this.mask = mask;
+ }
+
+ @Override
+ public byte getMask() {
+ return this.mask;
+ }
+
+ public Vector3f getOffset() {
+ return offset;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return Arrays.asList(this.faces).iterator();
+ }
+
+ /**
+ * Gets the total amount of BlockFace objects contained in this constant
+ *
+ * @return the amount of BlockFace objects
+ */
+ public int size() {
+ return this.faces.length;
+ }
+
+ /**
+ * Appends another array of block faces to this BlockFaces object
+ *
+ * @param blockFaces to append
+ * @return a new BlockFaces object with the faces appended
+ */
+ public BlockFaces append(BlockFaces blockFaces) {
+ return this.append(blockFaces.faces);
+ }
+
+ /**
+ * Appends another array of block faces to this BlockFaces object
+ *
+ * @param blockFaces to append
+ * @return a new BlockFaces object with the faces appended
+ */
+ public BlockFaces append(BlockFace... blockFaces) {
+ BlockFace[] faces = new BlockFace[this.faces.length + blockFaces.length];
+ System.arraycopy(this.faces, 0, faces, 0, this.faces.length);
+ System.arraycopy(blockFaces, 0, faces, this.faces.length, blockFaces.length);
+ return new BlockFaces(faces);
+ }
+
+ /**
+ * Checks if this block face constant contains the face given
+ *
+ * @param face to look for
+ * @return True if found, False if not
+ */
+ public boolean contains(BlockFace face) {
+ for (BlockFace bface : this.faces) {
+ if (bface == face) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Gets the index of a face in this constant
+ *
+ * @param face to get the index of
+ * @param def value to return if not found
+ * @return the index in the constant, or the def value if not found
+ */
+ public int indexOf(BlockFace face, int def) {
+ for (int i = 0; i < this.faces.length; i++) {
+ if (this.faces[i] == face) {
+ return i;
+ }
+ }
+ return def;
+ }
+
+ /**
+ * Gets the previous BlockFace in this circular BlockFaces constant
This function calls previous using an offset of 1
+ *
+ * @param from the BlockFace to count
+ * @return the face at the offset
+ * @see BlockFaces.previous(BlockFace from, int offset);
+ */
+ public BlockFace previous(BlockFace from) {
+ return previous(from, 1);
+ }
+
+ /**
+ * Gets the previous BlockFace in this circular BlockFaces constant
This function calls next using a negative offset
+ *
+ * @param from the BlockFace to count
+ * @param offset index in this range
+ * @return the face at the offset
+ * @see BlockFaces.next(BlockFace from, int offset);
+ */
+ public BlockFace previous(BlockFace from, int offset) {
+ return this.next(from, -offset);
+ }
+
+ /**
+ * Gets the next BlockFace in this circular BlockFaces constant
This function calls next using an offset of 1
+ *
+ * @param from the BlockFace to count
+ * @return the face at the offset
+ * @see BlockFaces.next(BlockFace from, int offset);
+ */
+ public BlockFace next(BlockFace from) {
+ return next(from, 1);
+ }
+
+ /**
+ * Gets the next BlockFace in this circular BlockFaces constant
+ *
+ * For example:
BlockFaces.NESW.next(BlockFace.EAST, 2) == BlockFace.WEST
BlockFaces.NESW.next(BlockFace.WEST, 1) == BlockFace.NORTH
BlockFaces.NESW.next(BlockFace.SOUTH, -3) ==
+ * BlockFace.WEST
+ *
+ * @param from the BlockFace to count
+ * @param offset index in this range
+ * @return the face at the offset
+ */
+ public BlockFace next(BlockFace from, int offset) {
+ int index = this.indexOf(from, -1);
+ if (index == -1) {
+ throw new IllegalArgumentException("This BlockFaces constant does not contain the face specified");
+ }
+ index = (index + offset) % this.faces.length;
+ if (index < 0) {
+ index += this.faces.length;
+ }
+ return this.faces[index];
+ }
+
+ /**
+ * Gets a random BlockFace from this BlockFaces constant
+ *
+ * @param random to use
+ * @return a random BlockFace
+ */
+ public BlockFace random(Random random) {
+ return this.faces[random.nextInt(this.faces.length)];
+ }
+
+ /**
+ * Gets the face from this constant at the index given
If the index is out of range, the first or last element is returned
+ *
+ * @param index to get at
+ * @return the BlockFace
+ */
+ public BlockFace get(int index) {
+ if (index < 0) {
+ return this.faces[0];
+ } else if (index >= this.faces.length) {
+ return this.faces[this.faces.length - 1];
+ } else {
+ return this.faces[index];
+ }
+ }
+
+ /**
+ * Gets the face from this constant at the index given
If the index is out of range, the default is returned
+ *
+ * @param index to get at
+ * @param def if the index is out of range
+ * @return the BlockFace
+ */
+ public BlockFace get(int index, BlockFace def) {
+ if (index < 0 || index >= this.faces.length) {
+ return def;
+ }
+
+ return this.faces[index];
+ }
+
+ public BlockFace[] toArray() {
+ return Arrays.copyOf(this.faces, this.faces.length);
+ }
+
+ private static byte getOffsetHash(Vector3f offset) {
+ offset = offset.normalize();
+ offset = offset.round();
+ int x = offset.getFloorX();
+ int y = offset.getFloorY();
+ int z = offset.getFloorZ();
+ x += 1;
+ y += 1;
+ z += 1;
+ return (byte) (x | y << 2 | z << 4);
+ }
+
+ public static BlockFaces fromOffset(Vector3f offset) {
+ return offsetHash.get(getOffsetHash(offset));
+ }
+}
diff --git a/src/main/java/org/spout/api/material/block/BlockFullState.java b/src/main/java/org/spout/api/material/block/BlockFullState.java
new file mode 100644
index 0000000..ff8dd2d
--- /dev/null
+++ b/src/main/java/org/spout/api/material/block/BlockFullState.java
@@ -0,0 +1,160 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material.block;
+
+import com.flowpowered.commons.StringUtil;
+
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
+import org.spout.api.material.BlockMaterial;
+
+/**
+ * Represents a {@link Block}'s ID and Data values, but contains no location-specific information.
+ */
+public class BlockFullState implements Cloneable {
+ private final short id;
+ private final short data;
+
+ public BlockFullState(int idAndData) {
+ id = (short) (idAndData >> 16);
+ data = (short) (idAndData);
+ }
+
+ public BlockFullState(short id, short data) {
+ this.id = id;
+ this.data = data;
+ }
+
+ /**
+ * Id of the Block
+ *
+ * @return id
+ */
+ public final short getId() {
+ return id;
+ }
+
+ /**
+ * Data value of the Block
+ *
+ * @return data
+ */
+ public final short getData() {
+ return data;
+ }
+
+ /**
+ * Returns an Integer representation of the merged ID and data for this BlockFullState.
The id will be contained in the upper 16-bits. The data will be contained in the lower 16-bits.
+ *
+ * @return integer representation of ID and Data.
+ */
+ public int getPacked() {
+ return getPacked(id, data);
+ }
+
+ /**
+ * Returns an Integer representation of the merged ID and data.
The id will be contained in the upper 16-bits. The data will be contained in the lower 16-bits.
+ *
+ * @param id to pack.
+ * @param data to pack.
+ * @return integer representation of ID and Data.
+ */
+ public static int getPacked(short id, short data) {
+ return id << 16 | (data & 0xFFFF);
+ }
+
+ /**
+ * Returns an Integer representation of the ID and Data from a {@link BlockMaterial}.
The id will be contained in the upper 16-bits. The data will be contained in the lower 16-bits.
+ */
+ public static int getPacked(BlockMaterial m) {
+ return getPacked(m.getId(), m.getData());
+ }
+
+ /**
+ * Unpacks the ID of a Material or Block from a packed integer.
The integer being passed in must have the ID of the Material or Block contained in the upper 16-bits.
+ *
+ * @param packed integer
+ * @return id of the material or block
+ */
+ public static short getId(int packed) {
+ return (short) (packed >> 16);
+ }
+
+ /**
+ * Unpacks the Data of a material or block from a packed integer.
The integer being passed in must have the data of the Material or Block contained in the lower 16-bits.
+ *
+ * @param packed integer
+ * @return data of the material or block.
+ */
+ public static short getData(int packed) {
+ return (short) packed;
+ }
+
+ /**
+ * Looks up the BlockMaterial from a packed integer.
If the material does not exist in the {@link BlockMaterialRegistry} then {@link BasicAir} will be returned. If the material does exist, and
+ * it contains data, the Sub-Material will be returned.
+ *
+ * @return the material found.
+ */
+ public static BlockMaterial getMaterial(int packed) {
+ short id = getId(packed);
+ short data = getData(packed);
+ BlockMaterial mat = BlockMaterial.get(id);
+ if (mat == null) {
+ return BlockMaterial.AIR;
+ }
+ return mat.getSubMaterial(data);
+ }
+
+ @Override
+ public String toString() {
+ return StringUtil.toNamedString(this, this.id, this.data);
+ }
+
+ @Override
+ public int hashCode() {
+ return new HashCodeBuilder(77, 81).append(id).append(data).toHashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ } else if (!(o instanceof BlockFullState)) {
+ return false;
+ } else {
+ BlockFullState fullState = (BlockFullState) o;
+
+ return fullState.id == id && fullState.data == data;
+ }
+ }
+
+ @Override
+ public BlockFullState clone() {
+ return new BlockFullState(id, data);
+ }
+}
diff --git a/src/main/java/org/spout/api/material/block/BlockSnapshot.java b/src/main/java/org/spout/api/material/block/BlockSnapshot.java
new file mode 100644
index 0000000..7a051bd
--- /dev/null
+++ b/src/main/java/org/spout/api/material/block/BlockSnapshot.java
@@ -0,0 +1,150 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.material.block;
+
+import org.spout.api.geo.World;
+import org.spout.api.geo.cuboid.Block;
+import org.spout.api.material.BlockMaterial;
+import org.spout.api.material.Material;
+import org.spout.api.util.hashing.ShortPairHashed;
+
+/**
+ * Represents an immutable snapshot of the state of a block
+ */
+public class BlockSnapshot {
+ private final BlockMaterial material;
+ private final short data;
+ private final int x, y, z;
+ private final World world;
+
+ public BlockSnapshot(Block block) {
+ this(block, block.getMaterial(), block.getBlockData());
+ }
+
+ public BlockSnapshot(Block block, BlockMaterial material, short data) {
+ this(block.getWorld(), block.getX(), block.getY(), block.getZ(), material, data);
+ }
+
+ public BlockSnapshot(World world, int x, int y, int z, BlockMaterial material, short data) {
+ this.material = material;
+ this.data = data;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.world = world;
+ }
+
+ /**
+ * Gets the x-coordinate of this Block snapshot
+ *
+ * @return the x-coordinate
+ */
+ public int getX() {
+ return this.x;
+ }
+
+ /**
+ * Gets the y-coordinate of this Block snapshot
+ *
+ * @return the y-coordinate
+ */
+ public int getY() {
+ return this.y;
+ }
+
+ /**
+ * Gets the z-coordinate of this Block snapshot
+ *
+ * @return the z-coordinate
+ */
+ public int getZ() {
+ return this.z;
+ }
+
+ /**
+ * Gets the world this Block snapshot is in
+ *
+ * @return the World
+ */
+ public World getWorld() {
+ return this.world;
+ }
+
+ /**
+ * Gets which block corresponding to the snapshot
+ *
+ * @return the block
+ */
+ public Block getBlock() {
+ return this.world.getBlock(this.x, this.y, this.z);
+ }
+
+ /**
+ * Gets the block's material at the time of the snapshot
+ *
+ * @return the material
+ */
+ public BlockMaterial getMaterial() {
+ return this.material;
+ }
+
+ public short getData() {
+ return this.data;
+ }
+
+ @Override
+ public String toString() {
+ return "BlockSnapshot { material: " + this.material + " , data:" + this.data + ", x: " + x + ", y: " + y + ", z: " + z + "}";
+ }
+
+ @Override
+ public int hashCode() {
+ return ShortPairHashed.key(this.material.getId(), this.data);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BlockSnapshot) {
+ BlockSnapshot other = (BlockSnapshot) o;
+ return other.x == x && other.y == y && other.z == z && other.material.equals(material) && other.data == data;
+ }
+ return false;
+ }
+
+ public boolean isMaterial(Material... materials) {
+ if (this.material == null) {
+ for (Material material : materials) {
+ if (material == null) {
+ return true;
+ }
+ }
+ return false;
+ } else {
+ return this.material.isMaterial(materials);
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/model/mesh/CubeMeshFactory.java b/src/main/java/org/spout/api/model/mesh/CubeMeshFactory.java
new file mode 100644
index 0000000..c5383dc
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesh/CubeMeshFactory.java
@@ -0,0 +1,111 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.model.mesh;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+
+import org.spout.api.material.block.BlockFace;
+import org.spout.math.vector.Vector2f;
+import org.spout.math.vector.Vector3f;
+
+public class CubeMeshFactory {
+ public static OrientedMesh generateCubeMesh(Vector2f[][] uvs) {
+ ArrayList list = new ArrayList<>(12);
+
+ Vector3f vertex0 = new Vector3f(0, 0, 0);
+ Vector3f vertex1 = new Vector3f(0, 1, 0);
+ Vector3f vertex2 = new Vector3f(1, 1, 0);
+ Vector3f vertex3 = new Vector3f(1, 0, 0);
+ Vector3f vertex4 = new Vector3f(0, 0, 1);
+ Vector3f vertex5 = new Vector3f(0, 1, 1);
+ Vector3f vertex6 = new Vector3f(1, 1, 1);
+ Vector3f vertex7 = new Vector3f(1, 0, 1);
+
+ Vertex v1 = null, v2 = null, v3 = null, v4 = null;
+
+ /* 1--2
+ * /| /|
+ * 5--6 |
+ * | 0|-3 Y - Bottom < TOP
+ * |/ |/ |
+ * 4--7 O-- X - North < SOUTH
+ * /
+ * Z - East < WEST
+ */
+
+ v1 = Vertex.createVertexPositionNormaTexture0(vertex1, BlockFace.TOP.getOffset(), getUV(uvs, 0, 3));
+ v2 = Vertex.createVertexPositionNormaTexture0(vertex2, BlockFace.TOP.getOffset(), getUV(uvs, 0, 2));
+ v3 = Vertex.createVertexPositionNormaTexture0(vertex6, BlockFace.TOP.getOffset(), getUV(uvs, 0, 1));
+ v4 = Vertex.createVertexPositionNormaTexture0(vertex5, BlockFace.TOP.getOffset(), getUV(uvs, 0, 0));
+ list.add(new OrientedMeshFace(v1, v2, v3, new HashSet<>(Arrays.asList(BlockFace.TOP))));
+ list.add(new OrientedMeshFace(v3, v4, v1, new HashSet<>(Arrays.asList(BlockFace.TOP))));
+
+ v1 = Vertex.createVertexPositionNormaTexture0(vertex0, BlockFace.BOTTOM.getOffset(), getUV(uvs, 1, 0));
+ v2 = Vertex.createVertexPositionNormaTexture0(vertex4, BlockFace.BOTTOM.getOffset(), getUV(uvs, 1, 3));
+ v3 = Vertex.createVertexPositionNormaTexture0(vertex7, BlockFace.BOTTOM.getOffset(), getUV(uvs, 1, 2));
+ v4 = Vertex.createVertexPositionNormaTexture0(vertex3, BlockFace.BOTTOM.getOffset(), getUV(uvs, 1, 1));
+ list.add(new OrientedMeshFace(v1, v2, v3, new HashSet<>(Arrays.asList(BlockFace.BOTTOM))));
+ list.add(new OrientedMeshFace(v3, v4, v1, new HashSet<>(Arrays.asList(BlockFace.BOTTOM))));
+
+ v1 = Vertex.createVertexPositionNormaTexture0(vertex0, BlockFace.NORTH.getOffset(), getUV(uvs, 2, 1));
+ v2 = Vertex.createVertexPositionNormaTexture0(vertex1, BlockFace.NORTH.getOffset(), getUV(uvs, 2, 0));
+ v3 = Vertex.createVertexPositionNormaTexture0(vertex5, BlockFace.NORTH.getOffset(), getUV(uvs, 2, 3));
+ v4 = Vertex.createVertexPositionNormaTexture0(vertex4, BlockFace.NORTH.getOffset(), getUV(uvs, 2, 2));
+ list.add(new OrientedMeshFace(v1, v2, v3, new HashSet<>(Arrays.asList(BlockFace.NORTH))));
+ list.add(new OrientedMeshFace(v3, v4, v1, new HashSet<>(Arrays.asList(BlockFace.NORTH))));
+
+ v1 = Vertex.createVertexPositionNormaTexture0(vertex7, BlockFace.SOUTH.getOffset(), getUV(uvs, 3, 1));
+ v2 = Vertex.createVertexPositionNormaTexture0(vertex6, BlockFace.SOUTH.getOffset(), getUV(uvs, 3, 0));
+ v3 = Vertex.createVertexPositionNormaTexture0(vertex2, BlockFace.SOUTH.getOffset(), getUV(uvs, 3, 3));
+ v4 = Vertex.createVertexPositionNormaTexture0(vertex3, BlockFace.SOUTH.getOffset(), getUV(uvs, 3, 2));
+ list.add(new OrientedMeshFace(v1, v2, v3, new HashSet<>(Arrays.asList(BlockFace.SOUTH))));
+ list.add(new OrientedMeshFace(v3, v4, v1, new HashSet<>(Arrays.asList(BlockFace.SOUTH))));
+
+ v1 = Vertex.createVertexPositionNormaTexture0(vertex0, BlockFace.EAST.getOffset(), getUV(uvs, 4, 2));
+ v2 = Vertex.createVertexPositionNormaTexture0(vertex3, BlockFace.EAST.getOffset(), getUV(uvs, 4, 1));
+ v3 = Vertex.createVertexPositionNormaTexture0(vertex2, BlockFace.EAST.getOffset(), getUV(uvs, 4, 0));
+ v4 = Vertex.createVertexPositionNormaTexture0(vertex1, BlockFace.EAST.getOffset(), getUV(uvs, 4, 3));
+ list.add(new OrientedMeshFace(v1, v2, v3, new HashSet<>(Arrays.asList(BlockFace.EAST))));
+ list.add(new OrientedMeshFace(v3, v4, v1, new HashSet<>(Arrays.asList(BlockFace.EAST))));
+
+ v1 = Vertex.createVertexPositionNormaTexture0(vertex5, BlockFace.WEST.getOffset(), getUV(uvs, 5, 0));
+ v2 = Vertex.createVertexPositionNormaTexture0(vertex6, BlockFace.WEST.getOffset(), getUV(uvs, 5, 3));
+ v3 = Vertex.createVertexPositionNormaTexture0(vertex7, BlockFace.WEST.getOffset(), getUV(uvs, 5, 2));
+ v4 = Vertex.createVertexPositionNormaTexture0(vertex4, BlockFace.WEST.getOffset(), getUV(uvs, 5, 1));
+ list.add(new OrientedMeshFace(v1, v2, v3, new HashSet<>(Arrays.asList(BlockFace.WEST))));
+ list.add(new OrientedMeshFace(v3, v4, v1, new HashSet<>(Arrays.asList(BlockFace.WEST))));
+
+ return new OrientedMesh(list);
+ }
+
+ private static Vector2f getUV(Vector2f[][] uvs, int face, int vertex) {
+ int i = face % uvs.length; //Allow to render all face of a cube with a one face specified
+ return uvs[i][vertex % uvs[i].length];
+ }
+}
diff --git a/src/main/java/org/spout/api/model/mesh/Mesh.java b/src/main/java/org/spout/api/model/mesh/Mesh.java
new file mode 100644
index 0000000..5512954
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesh/Mesh.java
@@ -0,0 +1,30 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.model.mesh;
+
+public interface Mesh {
+}
diff --git a/src/main/java/org/spout/api/model/mesh/MeshFace.java b/src/main/java/org/spout/api/model/mesh/MeshFace.java
new file mode 100644
index 0000000..276d011
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesh/MeshFace.java
@@ -0,0 +1,83 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.model.mesh;
+
+import com.flowpowered.commons.StringUtil;
+
+import java.io.Serializable;
+import java.util.Iterator;
+
+import org.apache.commons.collections.iterators.ArrayIterator;
+import org.spout.math.vector.Vector2f;
+import org.spout.math.vector.Vector3f;
+
+/**
+ * Represents a Triangle for a model face
+ */
+public class MeshFace implements Iterable, Serializable {
+ private static final long serialVersionUID = 1L;
+ Vertex[] verts = new Vertex[3];
+
+ public MeshFace(Vertex v1, Vertex v2, Vertex v3) {
+ verts[0] = v1;
+ verts[1] = v2;
+ verts[2] = v3;
+ }
+
+ /**
+ * Recalculates the normals for this triangle. All points must be 0'd before this.
+ */
+ protected void doRecalculateNormals() {
+ Vector3f trinormal = verts[0].position.sub(verts[1].position).cross(verts[1].position.sub(verts[2].position)).normalize();
+ verts[0].normal = verts[0].normal.add(trinormal).normalize();
+ verts[1].normal = verts[1].normal.add(trinormal).normalize();
+ verts[2].normal = verts[2].normal.add(trinormal).normalize();
+ }
+
+ Vector3f[] getPositions() {
+ return new Vector3f[] {verts[0].position, verts[1].position, verts[2].position};
+ }
+
+ Vector3f[] getNormals() {
+ return new Vector3f[] {verts[0].normal, verts[1].normal, verts[2].normal};
+ }
+
+ Vector2f[] getUVs() {
+ return new Vector2f[] {verts[0].texCoord0, verts[1].texCoord0, verts[2].texCoord0};
+ }
+
+ @Override
+ public String toString() {
+ return StringUtil.toNamedString(this, verts[0], verts[1], verts[2]);
+ }
+
+ @SuppressWarnings ("unchecked")
+ @Override
+ public Iterator iterator() {
+ return new ArrayIterator(verts);
+ }
+}
diff --git a/src/main/java/org/spout/api/model/mesh/OrientedMesh.java b/src/main/java/org/spout/api/model/mesh/OrientedMesh.java
new file mode 100644
index 0000000..83418d3
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesh/OrientedMesh.java
@@ -0,0 +1,57 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.model.mesh;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.Iterator;
+import java.util.List;
+
+public class OrientedMesh implements Mesh, Iterable {
+ private static final long serialVersionUID = 1L;
+ protected final List meshFace;
+
+ public & Serializable> OrientedMesh(T meshFace) {
+ this.meshFace = meshFace;
+ }
+
+ @Override
+ public Iterator iterator() {
+ return meshFace.iterator();
+ }
+
+ // This is a workaround for SerializableTest. We know at compile-time, thanks to generics, that meshFace will be Serializable
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ }
+
+ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ }
+}
diff --git a/src/main/java/org/spout/api/model/mesh/OrientedMeshFace.java b/src/main/java/org/spout/api/model/mesh/OrientedMeshFace.java
new file mode 100644
index 0000000..0a64733
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesh/OrientedMeshFace.java
@@ -0,0 +1,114 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.model.mesh;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.spout.api.material.block.BlockFace;
+import org.spout.math.vector.Vector3f;
+
+/**
+ * Represents a Triangle for a model face
+ */
+public class OrientedMeshFace extends MeshFace {
+ public final static BlockFace[] shouldRender = new BlockFace[] {BlockFace.TOP, BlockFace.BOTTOM, BlockFace.NORTH, BlockFace.SOUTH, BlockFace.WEST, BlockFace.EAST};
+ private final static Map> faceMap = new HashMap<>();
+ private static final long serialVersionUID = 1L;
+
+ static {
+ /* 1--2
+ * /| /|
+ * 5--6 |
+ * | 0|-3 Y - Bottom < TOP
+ * |/ |/ |
+ * 4--7 O-- X - North < SOUTH
+ * /
+ * Z - East < WEST
+ */
+
+ //TODO : extract vector in variable
+
+ faceMap.put(BlockFace.TOP, new ArrayList<>(Arrays.asList(new Vector3f(-1, 1, -1).normalize(), new Vector3f(1, 1, -1).normalize(), new Vector3f(-1, 1, 1).normalize(), new Vector3f(1, 1, 1).normalize())));
+ faceMap.put(BlockFace.BOTTOM, new ArrayList<>(Arrays.asList(new Vector3f(-1, -1, -1).normalize(), new Vector3f(1, -1, -1).normalize(), new Vector3f(-1, -1, 1).normalize(), new Vector3f(1, -1, 1).normalize())));
+ faceMap.put(BlockFace.NORTH, new ArrayList<>(Arrays.asList(new Vector3f(-1, -1, -1).normalize(), new Vector3f(-1, 1, -1).normalize(), new Vector3f(-1, 1, 1).normalize(), new Vector3f(-1, -1, 1).normalize())));
+ faceMap.put(BlockFace.SOUTH, new ArrayList<>(Arrays.asList(new Vector3f(1, -1, -1).normalize(), new Vector3f(1, -1, 1).normalize(), new Vector3f(1, 1, -1).normalize(), new Vector3f(1, 1, 1).normalize())));
+ faceMap.put(BlockFace.WEST, new ArrayList<>(Arrays.asList(new Vector3f(-1, 1, 1).normalize(), new Vector3f(-1, -1, 1).normalize(), new Vector3f(1, -1, 1).normalize(), new Vector3f(1, 1, 1).normalize())));
+ faceMap.put(BlockFace.EAST, new ArrayList<>(Arrays.asList(new Vector3f(-1, -1, -1).normalize(), new Vector3f(-1, 1, -1).normalize(), new Vector3f(1, -1, -1).normalize(), new Vector3f(1, 1, -1).normalize())));
+ }
+
+ private boolean[] seeFromFace = new boolean[shouldRender.length];
+
+ public OrientedMeshFace(Vertex v1, Vertex v2, Vertex v3) {
+ super(v1, v2, v3);
+
+ // Calculate two vectors from the three points
+
+ Vector3f vector1 = verts[0].position.sub(verts[1].position);
+ Vector3f vector2 = verts[1].position.sub(verts[2].position);
+
+ // Take the cross product of the two vectors to get
+ // the normal vector which will be stored in out
+
+ Vector3f norm = vector1.cross(vector2).normalize();
+
+ for (int i = 0; i < shouldRender.length; i++) {
+ for (Vector3f edge : faceMap.get(shouldRender[i])) {
+ if (norm.distance(edge) < 1) {
+ seeFromFace[i] = true;
+ }
+ }
+ }
+ }
+
+ public OrientedMeshFace(Vertex v1, Vertex v2, Vertex v3, Set requiredFace) {
+ super(v1, v2, v3);
+
+ for (int i = 0; i < shouldRender.length; i++) {
+ seeFromFace[i] = requiredFace.contains(shouldRender[i]);
+ }
+ }
+
+ public boolean canRender(boolean toRender[]) {
+ /**
+ * For each face :
+ * - Look if a face is see by a block face and this block face isn't occluded
+ * - If this face is the face what you want to draw, send yes
+ * - If it's not the face that you want to drawn, send no, because this face will be rended by a other blockface
+ */
+ for (int i = 0; i < shouldRender.length; i++) {
+ if (seeFromFace[i] && toRender[i]) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/org/spout/api/model/mesh/Vertex.java b/src/main/java/org/spout/api/model/mesh/Vertex.java
new file mode 100644
index 0000000..a91bea2
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesh/Vertex.java
@@ -0,0 +1,162 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.model.mesh;
+
+import java.awt.Color;
+import java.io.Serializable;
+
+import com.flowpowered.commons.StringUtil;
+
+import org.spout.math.vector.Vector2f;
+import org.spout.math.vector.Vector3f;
+
+public class Vertex implements Serializable {
+ private static final long serialVersionUID = 1L;
+ public static final int SIZE_FLOAT = 4;
+ public static final int VERTEX_LAYER = 0;
+ public static final int COLOR_LAYER = 1;
+ public static final int NORMAL_LAYER = 2;
+ public static final int TEXTURE0_LAYER = 3;
+ public static final int TEXTURE1_LAYER = 4;
+ public Vector3f position;
+ public Color color;
+ public Vector3f normal;
+ public Vector2f texCoord0;
+ public Vector2f texCoord1;
+ public int id;
+
+ /**
+ * Create a vertex with a position
+ */
+ public static Vertex createVertexPosition(Vector3f position) {
+ return new Vertex(position, Vector3f.ZERO, Vector2f.ZERO, null, 0);
+ }
+
+ /**
+ * Create a vertex with a position and a normal
+ */
+ public static Vertex createVertexPositionNormal(Vector3f position, Vector3f normal) {
+ return new Vertex(position, normal, Vector2f.ZERO, null, 0);
+ }
+
+ /**
+ * Create a vertex with a position and a texture
+ */
+ public static Vertex createVertexPositionTexture0(Vector3f position, Vector2f texture) {
+ return new Vertex(position, Vector3f.ZERO, texture, null, 0);
+ }
+
+ /**
+ * Create a vertex with a position, a normal and a color
+ */
+ public static Vertex createVertexPositionNormalColor(Vector3f position, Vector3f normal, Color color) {
+ return new Vertex(position, normal, Vector2f.ZERO, color, 0);
+ }
+
+ /**
+ * Create a vertex with a position, a normal and a texture
+ */
+ public static Vertex createVertexPositionNormaTexture0(Vector3f position, Vector3f normal, Vector2f texture) {
+ return new Vertex(position, normal, texture, null, 0);
+ }
+
+ /**
+ * Create a vertex with a position, a normal, a texture and a color
+ */
+ public static Vertex createVertexPositionNormalTexture0Color(Vector3f position, Vector3f normal, Vector2f texture, Color color) {
+ return new Vertex(position, normal, texture, color, 0);
+ }
+
+ /**
+ * Create a vertex with a position, a normal, a texture and a vertice index
+ */
+ public static Vertex createVertexPositionNormalTexture0Index(Vector3f position, Vector3f normal, Vector2f texture, int id) {
+ return new Vertex(position, normal, texture, null, id);
+ }
+
+ /**
+ * Create a vertex with a position, a normal and a vertice index
+ */
+ public static Vertex createVertexPositionNormalIndex(Vector3f position, Vector3f normal, int id) {
+ return new Vertex(position, normal, Vector2f.ZERO, null, id);
+ }
+
+ /**
+ * Create a vertex with a position and a vertice index
+ */
+ public static Vertex createVertexPositionIndex(Vector3f position, int id) {
+ return new Vertex(position, Vector3f.ZERO, Vector2f.ZERO, null, id);
+ }
+
+ /**
+ * Create a vertex with a position, a texture and a vertice index
+ */
+ public static Vertex createVertexPositionTexture0Index(Vector3f position, Vector2f texture, int id) {
+ return new Vertex(position, Vector3f.ZERO, texture, null, id);
+ }
+
+ public Vertex(Vector3f position, Vector3f normal, Vector2f texture, Color color, int id) {
+ this.position = position;
+ this.normal = normal;
+ this.texCoord0 = texture;
+ this.color = color == null ? null : new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
+ this.id = id;
+ }
+
+ public Vertex(Vertex v) {
+ this.color = v.color == null ? null : new Color(v.color.getRed(), v.color.getGreen(), v.color.getBlue(), v.color.getAlpha());
+ this.position = v.position == null ? null : new Vector3f(v.position);
+ this.normal = v.normal == null ? null : new Vector3f(v.normal);
+ this.texCoord0 = v.texCoord0 == null ? null : new Vector2f(v.texCoord0);
+ this.texCoord1 = v.texCoord1 == null ? null : new Vector2f(v.texCoord1);
+ this.id = v.id;
+ }
+
+ public float[] toArray() {
+ return new float[] {position.getX(), position.getY(), position.getZ(), 1.0f,
+ color.getRed() / 255.0f, color.getBlue() / 255.0f, color.getGreen() / 255.0f, color.getAlpha() / 255.0f,
+ normal.getX(), normal.getY(), normal.getZ(),
+ texCoord0.getX(), texCoord0.getY(),
+ texCoord1.getX(), texCoord1.getY()
+ };
+ }
+
+ public int getStride() {
+ int stride = 0;
+ stride += SIZE_FLOAT * 4; //number of floats in a vector4
+ stride += SIZE_FLOAT * 4; //number of floats in a Color
+ stride += SIZE_FLOAT * 3; //number of floats in a normal
+ stride += SIZE_FLOAT * 2; //number of floats in a texcoord
+ stride += SIZE_FLOAT * 2; //number of floats in a texcoord
+ return stride;
+ }
+
+ @Override
+ public String toString() {
+ return StringUtil.toNamedString(this, position, normal, texCoord0);
+ }
+}
diff --git a/src/main/java/org/spout/api/model/mesher/ChunkMesher.java b/src/main/java/org/spout/api/model/mesher/ChunkMesher.java
new file mode 100644
index 0000000..5d02b86
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesher/ChunkMesher.java
@@ -0,0 +1,39 @@
+/**
+ * This file is part of Client, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2013 Spoutcraft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.spout.api.model.mesher;
+
+import org.spout.api.geo.cuboid.ChunkSnapshotGroup;
+
+/**
+ * Converts chunk snapshot groups to meshes for rendering.
+ */
+public interface ChunkMesher {
+ /**
+ * Converts the chunk snapshot group to a mesh.
+ *
+ * @param chunk The chunk snapshot group
+ * @return The mesh
+ */
+ public Mesh mesh(ChunkSnapshotGroup chunk);
+}
diff --git a/src/main/java/org/spout/api/model/mesher/Mesh.java b/src/main/java/org/spout/api/model/mesher/Mesh.java
new file mode 100644
index 0000000..6b55db3
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesher/Mesh.java
@@ -0,0 +1,212 @@
+/**
+ * This file is part of Client, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2013 Spoutcraft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.spout.api.model.mesher;
+
+import java.util.EnumMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import gnu.trove.list.TFloatList;
+import gnu.trove.list.TIntList;
+import gnu.trove.list.array.TFloatArrayList;
+import gnu.trove.list.array.TIntArrayList;
+
+import org.spout.renderer.data.VertexAttribute;
+import org.spout.renderer.data.VertexAttribute.DataType;
+import org.spout.renderer.data.VertexData;
+import org.spout.renderer.util.CausticUtil;
+
+/**
+ * Represents a standard mesh, with various attributes (positions, normals, texture coordinates and/or tangents). This mesh can be converted into {@link org.spout.renderer.data.VertexData for
+ * rendering}.
+ *
+ * @see org.spoutcraft.client.nterface.mesh.Mesh.MeshAttribute
+ */
+public class Mesh {
+ private final Map attributes = new EnumMap<>(MeshAttribute.class);
+ private final TIntList indices = new TIntArrayList();
+
+ /**
+ * Constructs a new mesh with the desired attributes.
+ *
+ * @param attributes The attributes
+ */
+ public Mesh(MeshAttribute... attributes) {
+ for (MeshAttribute attribute : attributes) {
+ this.attributes.put(attribute, new TFloatArrayList());
+ }
+ }
+
+ /**
+ * Returns true if the mesh has the attribute, false if not.
+ *
+ * @param attribute Whether or not the mesh has the attribute
+ * @return The attribute to check for
+ */
+ public boolean hasAttribute(MeshAttribute attribute) {
+ return attributes.containsKey(attribute);
+ }
+
+ /**
+ * Adds an attribute to the mesh, if not already present.
+ *
+ * @param attribute The attribute to add
+ */
+ public void addAttribute(MeshAttribute attribute) {
+ if (!hasAttribute(attribute)) {
+ attributes.put(attribute, new TFloatArrayList());
+ }
+ }
+
+ /**
+ * Returns the float list associated to the attribute in which to store the data. The components for each individual attribute point should be stored in their natural order inside the list, and
+ * each point after the other. Actual order of the points for rendering is decided by the indices list.
+ *
+ * @param attribute The attribute to get the float list for
+ * @return The float list for the attribute
+ */
+ public TFloatList getAttribute(MeshAttribute attribute) {
+ return attributes.get(attribute);
+ }
+
+ /**
+ * Removes the attributes from the mesh, deleting its data.
+ *
+ * @param attribute The attribute to remove
+ */
+ public void removeAttribute(MeshAttribute attribute) {
+ attributes.remove(attribute);
+ }
+
+ /**
+ * Returns the index list for the mesh, in which to store the indices that declares the triangle faces by winding order.
+ *
+ * @return The index list
+ */
+ public TIntList getIndices() {
+ return indices;
+ }
+
+ /**
+ * Returns true if all the attribute data lists and the indices list are empty.
+ *
+ * @return Whether or not this mesh is empty (no data for the attributes or indices)
+ */
+ public boolean isEmpty() {
+ for (TFloatList list : attributes.values()) {
+ if (!list.isEmpty()) {
+ return false;
+ }
+ }
+ return indices.isEmpty();
+ }
+
+ /**
+ * Builds the mesh into a {@link org.spout.renderer.data.VertexData} to be ready for rendering. If an attribute has no data, but can be automatically generated (see {@link
+ * org.spoutcraft.client.nterface.mesh.Mesh.MeshAttribute#generateDataIfMissing()}, it will be generated for the build. The generated data will be stored in the attribute float list.
+ *
+ * @return The vertex data for the built mesh
+ */
+ public VertexData build() {
+ final VertexData vertexData = new VertexData();
+ int i = 0;
+ for (Entry entry : attributes.entrySet()) {
+ MeshAttribute attribute = entry.getKey();
+ final VertexAttribute vertexAttribute = new VertexAttribute(attribute.getName(), DataType.FLOAT, attribute.getComponentCount());
+ final TFloatList data = entry.getValue();
+ if (data.isEmpty() && attribute.generateDataIfMissing()) {
+ switch (attribute) {
+ case NORMALS:
+ CausticUtil.generateNormals(attributes.get(MeshAttribute.POSITIONS), indices, data);
+ break;
+ case TANGENTS:
+ CausticUtil.generateTangents(attributes.get(MeshAttribute.POSITIONS), attributes.get(MeshAttribute.NORMALS), attributes.get(MeshAttribute.TEXTURE_COORDS), indices, data);
+ }
+ }
+ vertexAttribute.setData(data);
+ vertexData.addAttribute(i++, vertexAttribute);
+ }
+ vertexData.getIndices().addAll(indices);
+ return vertexData;
+ }
+
+ /**
+ * An enum of the various mesh attributes.
+ */
+ public static enum MeshAttribute {
+ // Enum ordering is important here, don't change
+ /**
+ * The positions attribute, has 3 components and cannot be automatically generated.
+ */
+ POSITIONS("positions", 3, false),
+ /**
+ * The normals attribute, has 3 components and can be automatically generated if the position data exists.
+ */
+ NORMALS("normals", 3, true),
+ /**
+ * The texture coordinates attribute, has 2 components and cannot be automatically generated.
+ */
+ TEXTURE_COORDS("textureCoords", 2, false),
+ /**
+ * The tangents attribute, has 4 components and can be automatically generated if the positions, normals and texture coordinates exist.
+ */
+ TANGENTS("tangents", 4, true);
+ private final String name;
+ private final int componentCount;
+ private final boolean generateIfDataMissing;
+
+ private MeshAttribute(String name, int componentCount, boolean generateIfDataMissing) {
+ this.name = name;
+ this.componentCount = componentCount;
+ this.generateIfDataMissing = generateIfDataMissing;
+ }
+
+ /**
+ * Returns the name of the attribute.
+ *
+ * @return The attribute name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the component count of the attribute.
+ *
+ * @return The component count
+ */
+ public int getComponentCount() {
+ return componentCount;
+ }
+
+ /**
+ * Returns true if the attribute data can be automatically generated when the required attributes are present.
+ *
+ * @return Whether or not the attribute data can be automatically generated
+ */
+ public boolean generateDataIfMissing() {
+ return generateIfDataMissing;
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/model/mesher/ParallelChunkMesher.java b/src/main/java/org/spout/api/model/mesher/ParallelChunkMesher.java
new file mode 100644
index 0000000..f365910
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesher/ParallelChunkMesher.java
@@ -0,0 +1,200 @@
+/**
+ * This file is part of Client, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2013 Spoutcraft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.spout.api.model.mesher;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ForkJoinPool;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.spout.api.geo.cuboid.ChunkSnapshotGroup;
+import org.spout.engine.render.SpoutRenderer;
+import org.spout.renderer.data.VertexData;
+import org.spout.renderer.gl.VertexArray;
+import org.spout.renderer.model.Model;
+
+/**
+ * Meshes chunks in parallel. Returns chunk models which may not be rendered when {@link org.spoutcraft.client.nterface.mesh.ParallelChunkMesher.ChunkModel#render()} is called, this is happens when
+ * the meshing is in progress. Parallelism is achieved using a {@link java.util.concurrent.ForkJoinPool} with default thread count. Chunks are meshed using the provided {@link
+ * org.spoutcraft.client.nterface.mesh.ChunkMesher}. An optional {@link org.spoutcraft.client.nterface.Interface} can be passed to the constructor for chunk culling.
+ *
+ * @see org.spoutcraft.client.nterface.mesh.ParallelChunkMesher.ChunkModel
+ */
+public class ParallelChunkMesher {
+ private final SpoutRenderer renderer;
+ private final ChunkMesher mesher;
+ private final ThreadPoolExecutor executor;
+
+ /**
+ * Constructs a new parallel chunk mesher from the actual mesher.
+ *
+ * @param mesher The chunk mesher
+ */
+ public ParallelChunkMesher(SpoutRenderer renderer, ChunkMesher mesher) {
+ this.renderer = renderer;
+ this.mesher = mesher;
+ this.executor = new ThreadPoolExecutor(4, 4,
+ 60L, TimeUnit.SECONDS,
+ new LinkedBlockingQueue());
+ executor.allowCoreThreadTimeOut(true);
+ }
+
+ /**
+ * Queues a chunk to be meshed, returning a chunk model which can be used normally. The chunk model will actually only renderer the chunk once meshing it complete.
+ *
+ * @param chunk The chunk to mesh
+ * @return The chunk's model
+ */
+ public ChunkModel queue(ChunkSnapshotGroup chunk) {
+ return new ChunkModel(executor.submit(new ChunkMeshTask(chunk)));
+ }
+
+ /**
+ * Shuts down the executor used for meshing, cancelling any meshing pending or active.
+ */
+ public void shutdown() {
+ executor.shutdownNow();
+ }
+
+ private class ChunkMeshTask implements Callable {
+ private final ChunkSnapshotGroup toMesh;
+
+ private ChunkMeshTask(ChunkSnapshotGroup toMesh) {
+ this.toMesh = toMesh;
+ }
+
+ @Override
+ public VertexData call() {
+ final Mesh mesh = mesher.mesh(toMesh);
+ if (mesh.isEmpty()) {
+ return null;
+ }
+ return mesh.build();
+ }
+ }
+
+ /**
+ * In the case that meshing is occurring and that the chunk is not renderable, a previous model can be rendered instead. To use this feature, set the previous model using {@link
+ * org.spoutcraft.client.nterface.mesh.ParallelChunkMesher.ChunkModel#setPrevious(org.spoutcraft.client.nterface.mesh.ParallelChunkMesher.ChunkModel)}. This previous model will be used until the
+ * mesh becomes available. At this point, the previous model will be destroyed, and the new one rendered. When a model isn't needed anymore, you must call {@link
+ * org.spoutcraft.client.nterface.mesh.ParallelChunkMesher.ChunkModel#destroy()} to dispose of it completely. This will also cancel the meshing if it's in progress, and destroy the previous model.
+ * The chunk can also be automatically culled by passing an optional {@link org.spoutcraft.client.nterface.Interface} to the {@link org.spoutcraft.client.nterface.mesh.ParallelChunkMesher}
+ * constructor.
+ */
+ public class ChunkModel extends Model {
+ private Future mesh;
+ private boolean complete = false;
+ private ChunkModel previous;
+
+ private ChunkModel(Future mesh) {
+ this.mesh = mesh;
+ }
+
+ @Override
+ public void render() {
+ // If we have not received the mesh and it's done
+ if (!complete && mesh.isDone()) {
+ // Get the mesh
+ final VertexData vertexData;
+ try {
+ vertexData = mesh.get();
+ } catch (Exception ex) {
+ throw new RuntimeException(ex);
+ }
+ mesh = null;
+ // If the chunk mesher returned a mesh. It may not return one if the chunk has no mesh (completely invisible)
+ if (vertexData != null) {
+ // Create the vertex array from the mesh
+ final VertexArray vertexArray = renderer.getGLFactory().createVertexArray();
+ vertexArray.setData(vertexData);
+ vertexArray.create();
+ // Set it for rendering
+ setVertexArray(vertexArray);
+ }
+ // Destroy and discard the previous model (if any), as it is now obsolete
+ if (previous != null) {
+ previous.destroy();
+ previous = null;
+ }
+ // Set the model as complete
+ complete = true;
+ }
+ if (!isVisible()) {
+ return;
+ }
+ // If we have a vertex array, we can render
+ if (complete) {
+ // Only render if the model has a vertex array and we're visible
+ if (getVertexArray() != null && isVisible()) {
+ super.render();
+ }
+ } else if (previous != null && isVisible()) {
+ // Else, fall back on the previous model if we have one and we're visible
+ previous.render();
+ }
+ }
+
+ private boolean isVisible() {
+ // It's hard to look right
+ // at the world baby
+ // But here's my frustum
+ // so cull me maybe?
+ return true;
+ // TODO frustrum
+ }
+
+ /**
+ * Sets the previous model to renderer until the updated one is ready.
+ *
+ * @param previous The previous model
+ */
+ public void setPrevious(ChunkModel previous) {
+ this.previous = previous;
+ }
+
+ /**
+ * Destroys the models, cancelling the meshing task if in progress, and the previous model (if any).
+ */
+ public void destroy() {
+ // If we have a vertex array, destroy it
+ if (complete) {
+ if (getVertexArray() != null) {
+ getVertexArray().destroy();
+ }
+ complete = false;
+ } else {
+ // Else, the mesh is still in progress, cancel that
+ mesh.cancel(false);
+ mesh = null;
+ // Also destroy and discard the previous model if we have one
+ if (previous != null) {
+ previous.destroy();
+ previous = null;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/model/mesher/StandardChunkMesher.java b/src/main/java/org/spout/api/model/mesher/StandardChunkMesher.java
new file mode 100644
index 0000000..402c60c
--- /dev/null
+++ b/src/main/java/org/spout/api/model/mesher/StandardChunkMesher.java
@@ -0,0 +1,147 @@
+/**
+ * This file is part of Client, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2013 Spoutcraft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.spout.api.model.mesher;
+
+import gnu.trove.list.TFloatList;
+import gnu.trove.list.TIntList;
+import org.spout.api.geo.cuboid.Chunk;
+
+import org.spout.api.geo.cuboid.ChunkSnapshotGroup;
+import org.spout.api.material.BlockMaterial;
+import org.spout.api.material.block.BlockFace;
+import org.spout.api.material.block.BlockFaces;
+import org.spout.api.model.mesher.Mesh.MeshAttribute;
+
+/**
+ * The standard chunk mesher. Voxels are meshed as blocks. Occludes any block not visible, including the edge blocks. Can mesh a chunk with 3n^2(n+2) block access operations, n being the size of the
+ * chunk.
+ */
+public class StandardChunkMesher implements ChunkMesher {
+ @Override
+ public Mesh mesh(ChunkSnapshotGroup chunk) {
+ // TODO: add textures
+ final Mesh mesh = new Mesh(MeshAttribute.POSITIONS, MeshAttribute.NORMALS);
+ final TFloatList positions = mesh.getAttribute(MeshAttribute.POSITIONS);
+ final TIntList indices = mesh.getIndices();
+ int index = 0;
+ // Mesh the faces on the x axis
+ for (int zz = 0; zz < Chunk.BLOCKS.SIZE; zz++) {
+ for (int yy = 0; yy < Chunk.BLOCKS.SIZE; yy++) {
+ BlockMaterial backMaterial = chunk.getBlock(-1, yy, zz);
+ for (int xx = 0; xx < Chunk.BLOCKS.SIZE + 1; xx++) {
+ final BlockMaterial frontMaterial = chunk.getBlock(xx, yy, zz);
+ final BlockFace face = getFace(backMaterial, frontMaterial, BlockFaces.NS);
+ if (face == BlockFace.NORTH) {
+ add(indices, index + 3, index + 2, index + 1, index + 2, index, index + 1);
+ } else if (face == BlockFace.SOUTH) {
+ add(indices, index + 3, index + 1, index + 2, index + 2, index + 1, index);
+ } else {
+ backMaterial = frontMaterial;
+ continue;
+ }
+ add(positions, xx, yy + 1, zz + 1);
+ add(positions, xx, yy + 1, zz);
+ add(positions, xx, yy, zz + 1);
+ add(positions, xx, yy, zz);
+ index += 4;
+ backMaterial = frontMaterial;
+ }
+ }
+ }
+ // Mesh the faces on the y axis
+ for (int xx = 0; xx < Chunk.BLOCKS.SIZE; xx++) {
+ for (int zz = 0; zz < Chunk.BLOCKS.SIZE; zz++) {
+ BlockMaterial backMaterial = chunk.getBlock(xx, -1, zz);
+ for (int yy = 0; yy < Chunk.BLOCKS.SIZE + 1; yy++) {
+ final BlockMaterial frontMaterial = chunk.getBlock(xx, yy, zz);
+ final BlockFace face = getFace(backMaterial, frontMaterial, BlockFaces.BT);
+ if (face == BlockFace.BOTTOM) {
+ add(indices, index + 3, index + 2, index + 1, index + 2, index, index + 1);
+ } else if (face == BlockFace.TOP) {
+ add(indices, index + 3, index + 1, index + 2, index + 2, index + 1, index);
+ } else {
+ backMaterial = frontMaterial;
+ continue;
+ }
+ add(positions, xx, yy, zz);
+ add(positions, xx + 1, yy, zz);
+ add(positions, xx, yy, zz + 1);
+ add(positions, xx + 1, yy, zz + 1);
+ index += 4;
+ backMaterial = frontMaterial;
+ }
+ }
+ }
+ // Mesh the faces on the z axis
+ for (int xx = 0; xx < Chunk.BLOCKS.SIZE; xx++) {
+ for (int yy = 0; yy < Chunk.BLOCKS.SIZE; yy++) {
+ BlockMaterial backMaterial = chunk.getBlock(xx, yy, -1);
+ for (int zz = 0; zz < Chunk.BLOCKS.SIZE + 1; zz++) {
+ final BlockMaterial frontMaterial = chunk.getBlock(xx, yy, zz);
+ final BlockFace face = getFace(backMaterial, frontMaterial, BlockFaces.EW);
+ if (face == BlockFace.EAST) {
+ add(indices, index + 3, index + 2, index + 1, index + 2, index, index + 1);
+ } else if (face == BlockFace.WEST) {
+ add(indices, index + 3, index + 1, index + 2, index + 2, index + 1, index);
+ } else {
+ backMaterial = frontMaterial;
+ continue;
+ }
+ add(positions, xx, yy + 1, zz);
+ add(positions, xx + 1, yy + 1, zz);
+ add(positions, xx, yy, zz);
+ add(positions, xx + 1, yy, zz);
+ index += 4;
+ backMaterial = frontMaterial;
+ }
+ }
+ }
+ return mesh;
+ }
+
+ private BlockFace getFace(BlockMaterial back, BlockMaterial front, BlockFaces axis) {
+ if (!back.isInvisible() && !front.isFaceRendered(axis.get(0), back)) {
+ return axis.get(1);
+ }
+ if (!front.isInvisible() && !back.isFaceRendered(axis.get(1), front)) {
+ return axis.get(0);
+ }
+ return null;
+ }
+
+ private static void add(TFloatList list, float x, float y, float z) {
+ list.add(x);
+ list.add(y);
+ list.add(z);
+ }
+
+ private static void add(TIntList list, int i0, int i1, int i2, int i3, int i4, int i5) {
+ list.add(i0);
+ list.add(i1);
+ list.add(i2);
+ list.add(i3);
+ list.add(i4);
+ list.add(i5);
+ }
+}
diff --git a/src/main/java/org/spout/api/render/Renderer.java b/src/main/java/org/spout/api/render/Renderer.java
new file mode 100644
index 0000000..3d09d42
--- /dev/null
+++ b/src/main/java/org/spout/api/render/Renderer.java
@@ -0,0 +1,22 @@
+package org.spout.api.render;
+
+import org.spout.math.vector.Vector2f;
+
+public interface Renderer {
+
+ // TODO: getGLVersion
+
+ /**
+ * Returns the resolution of the window, in pixels.
+ *
+ * @return the resolution of the window.
+ */
+ Vector2f getResolution();
+
+ /**
+ * Returns the aspect ratio of the client, in pixels. Ratio = (screen width / screen height)
+ *
+ * @return The ratio as a float
+ */
+ float getAspectRatio();
+}
diff --git a/src/main/java/org/spout/api/scheduler/IllegalTickSequenceException.java b/src/main/java/org/spout/api/scheduler/IllegalTickSequenceException.java
new file mode 100644
index 0000000..96041fe
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/IllegalTickSequenceException.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler;
+
+public class IllegalTickSequenceException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public IllegalTickSequenceException(int allowedStages, int restrictedStages, Thread t, int actualStage) {
+ super(getMessage(allowedStages, restrictedStages, t, actualStage));
+ }
+
+ public IllegalTickSequenceException(int allowedStages, int actualStage) {
+ super("Method called during (" + TickStage.getAllStages(actualStage) + ") when only (" + TickStage.getAllStages(allowedStages) + ") were allowed");
+ }
+
+ private static String getMessage(int allowedStages, int restrictedStages, Thread t, int actualStage) {
+ if (Thread.currentThread() != t) {
+ return "Method called by non-owning thread (" + Thread.currentThread() + ") during (" + TickStage.getAllStages(actualStage) + ") when only calls by (" + t + ") during (" + TickStage.getAllStages(allowedStages) + ") were allowed";
+ } else {
+ return "Method called during (" + TickStage.getAllStages(actualStage) + ") when only (" + TickStage.getAllStages(restrictedStages) + ") were allowed for owning thread " + t;
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/scheduler/Scheduler.java b/src/main/java/org/spout/api/scheduler/Scheduler.java
new file mode 100644
index 0000000..f318cc7
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/Scheduler.java
@@ -0,0 +1,37 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler;
+
+public interface Scheduler extends TaskManager {
+
+ /**
+ * Determines if the server is under heavy load.
The server is considered under heavy load if the previous tick went over time, or if the current tick has gone over time.
+ *
+ * @return true if the server is under heavy load
+ */
+ public boolean isServerOverloaded();
+}
diff --git a/src/main/java/org/spout/api/scheduler/SnapshotLock.java b/src/main/java/org/spout/api/scheduler/SnapshotLock.java
new file mode 100644
index 0000000..8c5e8cb
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/SnapshotLock.java
@@ -0,0 +1,64 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler;
+
+/**
+ * A class to allow non-pulsed threads to synchronize with the pulsed thread system
+ */
+public interface SnapshotLock {
+ /**
+ * Readlocks the stable snapshot.
+ *
+ * This method will prevent server ticks from completing, so any locks should be short
+ *
+ * @param plugin the plugin
+ */
+ public void readLock(Object plugin);
+
+ /**
+ * Attempts to readlock the stable snapshot and returns immediately
+ *
+ * This method will prevent server ticks from completing, so any locks should be short
+ *
+ * @param plugin the plugin
+ */
+ public boolean readTryLock(Object plugin);
+
+ /**
+ * Releases a previous readlock
+ *
+ * @param plugin the plugin
+ */
+ public void readUnlock(Object plugin);
+
+ /**
+ * Gets if the lock is read locked
+ *
+ * @return true if locked
+ */
+ public boolean isWriteLocked();
+}
diff --git a/src/main/java/org/spout/api/scheduler/Task.java b/src/main/java/org/spout/api/scheduler/Task.java
new file mode 100644
index 0000000..1a902e8
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/Task.java
@@ -0,0 +1,81 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler;
+
+import org.spout.api.geo.cuboid.Region;
+
+/**
+ * Represents a task being executed by the scheduler
+ */
+public interface Task {
+ /**
+ * Returns the taskId for the task
+ *
+ * @return Task id number
+ */
+ public int getTaskId();
+
+ /**
+ * Returns the Object that owns this task
+ *
+ * @return The Object that owns the task
+ */
+ public Object getOwner();
+
+ /**
+ * Returns true if the Task is a sync task
+ *
+ * @return true if the task is run by main thread
+ */
+ public boolean isSync();
+
+ /**
+ * Returns true if the Task is alive. Dead tasks are no longer being scheduled
+ *
+ * @return true if the task is alive
+ */
+ public boolean isAlive();
+
+ /**
+ * Returns true if the Task is executing.
+ *
+ * @return true if the task is executing
+ */
+ public boolean isExecuting();
+
+ /**
+ * Returns true if the task is a long lived async task
+ *
+ * @return true if the task is a long lived task
+ */
+ public boolean isLongLived();
+
+ /**
+ * Cancels current Task
+ */
+ public void cancel();
+}
diff --git a/src/main/java/org/spout/api/scheduler/TaskManager.java b/src/main/java/org/spout/api/scheduler/TaskManager.java
new file mode 100644
index 0000000..6c28857
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/TaskManager.java
@@ -0,0 +1,179 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler;
+
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+public interface TaskManager {
+ /**
+ * Schedules a once off task to occur as soon as possible This task will be executed by the main server thread.
+ *
+ * @param plugin the owner of the task
+ * @param task the task to execute
+ * @return the task
+ */
+ public Task scheduleSyncDelayedTask(Object plugin, Runnable task);
+
+ /**
+ * Schedules a once off task to occur as soon as possible This task will be executed by the main server thread.
+ *
+ * @param plugin the owner of the task
+ * @param task the task to execute
+ * @param priority the priority of the task
+ * @return the task
+ */
+ public Task scheduleSyncDelayedTask(Object plugin, Runnable task, TaskPriority priority);
+
+ /**
+ * Schedules a once off task to occur after a delay. This task will be executed by the main server thread
+ *
+ * @param plugin the owner of the task
+ * @param task the task to execute
+ * @param delay the delay, in ms, before the task starts
+ * @param priority the priority of the task
+ * @return the task
+ */
+ public Task scheduleSyncDelayedTask(Object plugin, Runnable task, long delay, TaskPriority priority);
+
+ /**
+ * Schedules a repeating task This task will be executed by the main server thread. The repeat will not be started if the task until the previous repeat has completed running.
+ *
+ * @param plugin the owner of the task
+ * @param task the task to execute
+ * @param delay the delay, in ms, before the task starts
+ * @param period the repeat period, in ms, of the task, or <= 0 to indicate a single shot task
+ * @param priority the priority of the task
+ * @return the task
+ */
+ public Task scheduleSyncRepeatingTask(Object plugin, Runnable task, long delay, long period, TaskPriority priority);
+
+ /**
+ * Schedules a once off short lived task to occur as soon as possible. This task will be executed by a thread managed by the scheduler
+ *
+ * @param plugin the owner of the task
+ * @param task the task to execute
+ * @return the task id of the task
+ */
+ public Task scheduleAsyncTask(Object plugin, Runnable task);
+
+ /**
+ * Schedules a once off task to occur as soon as possible. This task will be executed by a thread managed by the scheduler
+ *
+ * @param plugin the owner of the task
+ * @param task the task to execute
+ * @param longLife indicates that the thread is long lived
+ * @return the tas
+ */
+ public Task scheduleAsyncTask(Object plugin, Runnable task, boolean longLife);
+
+ /**
+ * Schedules a once off short lived task to occur after a delay. This task will be executed by a thread managed by the scheduler.
+ *
+ * @param plugin the owner of the task
+ * @param task the task to execute
+ * @param delay the delay, in ms, before the task starts
+ * @param priority the priority of the task
+ * @return the task
+ */
+ public Task scheduleAsyncDelayedTask(Object plugin, Runnable task, long delay, TaskPriority priority);
+
+ /**
+ * Schedules a once off task to occur after a delay. This task will be executed by a thread managed by the scheduler.
+ *
+ * @param plugin the owner of the task
+ * @param task the task to execute
+ * @param delay the delay, in ms, before the task starts
+ * @param priority the priority of the task
+ * @param longLife indicates that the thread is long lived
+ * @return the task
+ */
+ public Task scheduleAsyncDelayedTask(Object plugin, Runnable task, long delay, TaskPriority priority, boolean longLife);
+
+ /**
+ * Calls a method on the main thread and returns a Future object This task will be executed by the main server thread
+ *
+ * Note: The Future.get() methods must NOT be called from the main thread
Note 2: There is at least an average of 10ms latency until the isDone() method returns true
+ *
+ * @param plugin the owner of the task
+ * @param task the Callable to execute
+ * @param priority the priority of the task
+ * @return Future Future object related to the task
+ */
+ public Future callSyncMethod(Object plugin, Callable task, TaskPriority priority);
+
+ /**
+ * True if the task is an actively scheduled task
+ *
+ * @return actived scheduled
+ */
+ public boolean isQueued(int taskId);
+
+ /**
+ * Removes task from scheduler
+ */
+ public void cancelTask(int taskId);
+
+ /**
+ * Removes task from scheduler
+ */
+ public void cancelTask(Task task);
+
+ /**
+ * Removes all tasks associated with a particular object from the scheduler
+ */
+ public void cancelTasks(Object plugin);
+
+ /**
+ * Removes all tasks from the scheduler
+ */
+ public void cancelAllTasks();
+
+ /**
+ * Returns a list of all active workers.
+ *
+ * This list contains asynch tasks that are being executed by separate threads.
+ *
+ * @return Active workers
+ */
+ public List getActiveWorkers();
+
+ /**
+ * Returns a list of all pending tasks. The ordering of the tasks is not related to their order of execution.
+ *
+ * @return Active workers
+ */
+ public List getPendingTasks();
+
+ /**
+ * Gets the up time for the scheduler. This is the time since server started for the main schedulers and the age of the world for the Region based schedulers.
It is updated once per tick.
+ *
+ * @return the up time in milliseconds
+ */
+ public long getUpTime();
+}
diff --git a/src/main/java/org/spout/api/scheduler/TaskPriority.java b/src/main/java/org/spout/api/scheduler/TaskPriority.java
new file mode 100644
index 0000000..be2f6da
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/TaskPriority.java
@@ -0,0 +1,75 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler;
+
+public class TaskPriority {
+ /**
+ * Priority for tasks which may not be deferred
+ */
+ public static final TaskPriority CRITICAL = new TaskPriority(0);
+ /**
+ * Priority for tasks which can be deferred by up to 50ms when under load
+ */
+ public static final TaskPriority HIGHEST = new TaskPriority(50);
+ /**
+ * Priority for tasks which can be deferred by up to 150ms when under load
+ */
+ public static final TaskPriority HIGH = new TaskPriority(150);
+ /**
+ * Priority for tasks which can be deferred by up to 500ms when under load
+ */
+ public static final TaskPriority MEDIUM = new TaskPriority(500);
+ /**
+ * Priority for tasks which can be deferred by up to 500ms when under load
+ */
+ public static final TaskPriority NORMAL = MEDIUM;
+ /**
+ * Priority for tasks which can be deferred by up to 1.5s when under load
+ */
+ public static final TaskPriority LOW = new TaskPriority(1500);
+ /**
+ * Priority for tasks which can be deferred by up to 10s when under load
+ */
+ public static final TaskPriority LOWEST = new TaskPriority(10000);
+ private final long maxDeferred;
+
+ /**
+ * Creates a TaskPriority instance which sets the maximum time that a task can be deferred.
+ *
+ * @param maxDelay the maximum delay before
+ */
+ public TaskPriority(long maxDeferred) {
+ this.maxDeferred = maxDeferred;
+ }
+
+ /**
+ * Gets the maximum time that the task can be deferred.
+ */
+ public long getMaxDeferred() {
+ return maxDeferred;
+ }
+}
diff --git a/src/main/java/org/spout/api/scheduler/TickStage.java b/src/main/java/org/spout/api/scheduler/TickStage.java
new file mode 100644
index 0000000..5826f25
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/TickStage.java
@@ -0,0 +1,190 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler;
+
+/**
+ * Represents the various tick stages.
The exact bit fields used are subject to change
+ */
+public class TickStage {
+ /**
+ * All tasks submitted to the main thread are executed during TICKSTART.
This stage is single threaded
+ */
+ public final static int TICKSTART = 1;
+ /**
+ * This is the first stage of the execution of the tick
+ */
+ public final static int STAGE1 = 1 << 1;
+ /**
+ * This is the second and subsequent stages of the tick
+ */
+ public final static int STAGE2P = 1 << 2;
+ /**
+ * This is the final stage before entering the pre-snapshot stage.
This is for minor changes prior to the snapshot process.
+ */
+ public final static int PHYSICS = 1 << 3;
+ /**
+ * This is the final stage before entering the pre-snapshot stage.
This is for minor changes prior to the snapshot process.
+ */
+ public final static int GLOBAL_PHYSICS = 1 << 4;
+ /**
+ * This is the final stage before entering the pre-snapshot stage.
This is for minor changes prior to the snapshot process.
+ */
+ public final static int DYNAMIC_BLOCKS = 1 << 5;
+ /**
+ * This is the final stage before entering the pre-snapshot stage.
This is for minor changes prior to the snapshot process.
+ */
+ public final static int GLOBAL_DYNAMIC_BLOCKS = 1 << 6;
+ /**
+ * This is the final stage before entering the pre-snapshot stage.
This is for minor changes prior to the snapshot process.
+ */
+ public final static int LIGHTING = 1 << 7;
+ /**
+ * This is the final stage before entering the pre-snapshot stage.
This is for minor changes prior to the snapshot process.
+ */
+ public final static int FINALIZE = 1 << 8;
+ /**
+ * This stage occurs before the snapshot stage.
This is a MONITOR ONLY stage, no changes should be made during the stage.
+ */
+ public final static int PRESNAPSHOT = 1 << 9;
+ /**
+ * This is the snapshot copy stage.
All snapshots are updated to the equal to the live value.
+ */
+ public final static int SNAPSHOT = 1 << 10;
+ public final static int ALL_PHYSICS = PHYSICS | GLOBAL_PHYSICS;
+ public final static int ALL_DYNAMIC = DYNAMIC_BLOCKS | GLOBAL_DYNAMIC_BLOCKS;
+ public final static int ALL_PHYSICS_AND_DYNAMIC = ALL_PHYSICS | ALL_DYNAMIC;
+
+ public static int getStageInt() {
+ return stage;
+ }
+
+ public static String getStage(int num) {
+ switch (num) {
+ case 1:
+ return "TICKSTART";
+ case 1 << 1:
+ return "STAGE1";
+ case 1 << 2:
+ return "STAGE2P";
+ case 1 << 3:
+ return "PHYSICS";
+ case 1 << 4:
+ return "GLOBAL_PHYSICS";
+ case 1 << 5:
+ return "DYNAMIC_BLOCKS";
+ case 1 << 6:
+ return "GLOBAL_DYNAMIC_BLOCKS";
+ case 1 << 7:
+ return "LIGHTING";
+ case 1 << 8:
+ return "FINALIZE";
+ case 1 << 9:
+ return "PRESNAPSHOT";
+ case 1 << 10:
+ return "SNAPSHOT";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ public static String getAllStages(int num) {
+ int scan = 1;
+ boolean first = true;
+ StringBuilder sb = new StringBuilder();
+
+ while (scan != 0) {
+ int checkNum = num & scan;
+ if (checkNum != 0) {
+ if (first) {
+ first = false;
+ } else {
+ sb.append(", ");
+ }
+ sb.append(getStage(checkNum));
+ }
+ scan <<= 1;
+ }
+ return sb.toString();
+ }
+
+ private static int stage = TICKSTART;
+
+ /**
+ * Sets the current stage. This is not synchronised, so should only be called during the stable period between stages.
+ *
+ * @param stage the stage
+ */
+ public static void setStage(int stage) {
+ TickStage.stage = stage;
+ }
+
+ /**
+ * Checks if the current stages is one of the valid allowed stages.
+ *
+ * @param allowedStages the OR of all the allowed stages
+ */
+ public static void checkStage(int allowedStages) {
+ if (!testStage(allowedStages)) {
+ throw new IllegalTickSequenceException(allowedStages, stage);
+ }
+ }
+
+ /**
+ * Checks if the current stages is one of the valid allowed stages, but does not throw an exception.
+ *
+ * @param allowedStages the OR of all the allowed stages
+ * @return true if the current stage is one of the allowed stages
+ */
+ public static boolean testStage(int allowedStages) {
+ return (stage & allowedStages) != 0;
+ }
+
+ /**
+ * Checks if the current thread is the owner thread and the current stage is one of the restricted stages, or that the current stage is one of the open stages
+ *
+ * @param allowedStages the OR of all the open stages
+ * @param restrictedStages the OR of all restricted stages
+ * @param ownerThread the thread that has restricted access
+ */
+ public static void checkStage(int allowedStages, int restrictedStages, Thread ownerThread) {
+ if ((stage & allowedStages) == 0 && (((stage & restrictedStages) == 0) || Thread.currentThread() != ownerThread)) {
+ throw new IllegalTickSequenceException(allowedStages, restrictedStages, ownerThread, stage);
+ }
+ }
+
+ /**
+ * Checks if the current thread is the owner thread and the current stage is one of the restricted stages
+ *
+ * @param restrictedStages the OR of all restricted stages
+ * @param ownerThread the thread that has restricted access
+ */
+ public static void checkStage(int restrictedStages, Thread ownerThread) {
+ if (((stage & restrictedStages) == 0) || Thread.currentThread() != ownerThread) {
+ throw new IllegalTickSequenceException(restrictedStages, 0, ownerThread, stage);
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/scheduler/Timer.java b/src/main/java/org/spout/api/scheduler/Timer.java
new file mode 100644
index 0000000..c570fc3
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/Timer.java
@@ -0,0 +1,165 @@
+/**
+ * This file is part of Client, licensed under the MIT License (MIT).
+ *
+ * Copyright (c) 2013 Spoutcraft
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+package org.spout.api.scheduler;
+
+import java.util.Arrays;
+
+import org.apache.commons.lang3.SystemUtils;
+
+/**
+ * A time class. Calling the {@link #sync()} method at the end of each tick will cause the thread to sleep for the correct time delay between the ticks. {@link #start()} must be called just before the
+ * loop to start the timer. {@link #reset()} is used to reset the start time to the current time.
+ *
+ * Based on LWJGL's implementation of {@link org.lwjgl.opengl.Sync}.
+ */
+public class Timer {
+ // Time to sleep or yield before next tick
+ private long nextTick = -1;
+ // Last 10 running averages for sleeps and yields
+ private final RunAverages sleepDurations = new RunAverages(10, 1000 * 1000);
+ private final RunAverages yieldDurations = new RunAverages(10, (int) (-(getTime() - getTime()) * 1.333f));
+ // The target tps
+ private final int tps;
+
+ static {
+ // Makes windows thread sleeping more accurate
+ if (SystemUtils.IS_OS_WINDOWS) {
+ final Thread sleepingDaemon = new Thread() {
+ @Override
+ public void run() {
+ try {
+ Thread.sleep(Long.MAX_VALUE);
+ } catch (Exception ignored) {
+ }
+ }
+ };
+ sleepingDaemon.setName("Timer");
+ sleepingDaemon.setDaemon(true);
+ sleepingDaemon.start();
+ }
+ }
+
+ /**
+ * Constructs a new timer.
+ *
+ * @param tps The target tps
+ */
+ public Timer(int tps) {
+ this.tps = tps;
+ }
+
+ /**
+ * Returns the timer's target TPS.
+ * @return The tps
+ */
+ public int getTps() {
+ return tps;
+ }
+
+ /**
+ * Starts the timer.
+ */
+ public void start() {
+ nextTick = getTime();
+ }
+
+ /**
+ * Resets the timer.
+ */
+ public void reset() {
+ start();
+ }
+
+ /**
+ * An accurate sync method that will attempt to run at the tps. It should be called once every tick.
+ */
+ public void sync() {
+ if (nextTick < 0) {
+ throw new IllegalStateException("Timer was not started");
+ }
+ if (tps <= 0) {
+ return;
+ }
+ try {
+ // Sleep until the average sleep time is greater than the time remaining until next tick
+ for (long time1 = getTime(), time2; nextTick - time1 > sleepDurations.average(); time1 = time2) {
+ Thread.sleep(1);
+ // Update average sleep time
+ sleepDurations.add((time2 = getTime()) - time1);
+ }
+ // Slowly dampen sleep average if too high to avoid yielding too much
+ sleepDurations.dampen();
+ // Yield until the average yield time is greater than the time remaining until next tick
+ for (long time1 = getTime(), time2; nextTick - time1 > yieldDurations.average(); time1 = time2) {
+ Thread.yield();
+ // Update average yield time
+ yieldDurations.add((time2 = getTime()) - time1);
+ }
+ } catch (InterruptedException ignored) {
+ }
+ // Schedule next frame, drop frames if it's too late for next frame
+ nextTick = Math.max(nextTick + 1000000000 / tps, getTime());
+ }
+
+ // Get the system time in nanoseconds
+ private static long getTime() {
+ return System.nanoTime();
+ }
+
+ // Holds a number of run times for averaging
+ private static class RunAverages {
+ // Dampen threshold, 10ms
+ private static final long DAMPEN_THRESHOLD = 10000000;
+ // Dampen factor, don't alter this value
+ private static final float DAMPEN_FACTOR = 0.9f;
+ private final long[] values;
+ private int currentIndex = 0;
+
+ private RunAverages(int slotCount, long initialValue) {
+ values = new long[slotCount];
+ Arrays.fill(values, initialValue);
+ }
+
+ private void add(long value) {
+ currentIndex %= values.length;
+ values[currentIndex++] = value;
+ }
+
+ private long average() {
+ long sum = 0;
+ for (long slot : values) {
+ sum += slot;
+ }
+ return sum / values.length;
+ }
+
+ private void dampen() {
+ if (average() > DAMPEN_THRESHOLD) {
+ for (int i = 0; i < values.length; i++) {
+ values[i] *= DAMPEN_FACTOR;
+ }
+ }
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/scheduler/Worker.java b/src/main/java/org/spout/api/scheduler/Worker.java
new file mode 100644
index 0000000..1b0101b
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/Worker.java
@@ -0,0 +1,60 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler;
+
+/**
+ * Represents a worker thread for the scheduler. This gives information about the Thread object for the task, owner of the task and the taskId.
+ *
+ * Workers are used to execute async tasks.
+ */
+public interface Worker {
+ /**
+ * Returns the taskId for the task being executed by this worker
+ *
+ * @return Task id number
+ */
+ public int getTaskId();
+
+ /**
+ * Returns the Object that owns this task
+ *
+ * @return The Object that owns the task
+ */
+ public Object getOwner();
+
+ /**
+ * Attempts to cancel the task. This will trigger an interrupt for async tasks that are in progress.
+ */
+ public void cancel();
+
+ /**
+ * Gets the task associated with this worker
+ *
+ * @return the task
+ */
+ public Task getTask();
+}
diff --git a/src/main/java/org/spout/api/scheduler/tickable/BasicTickable.java b/src/main/java/org/spout/api/scheduler/tickable/BasicTickable.java
new file mode 100644
index 0000000..a238f5d
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/tickable/BasicTickable.java
@@ -0,0 +1,36 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler.tickable;
+
+public abstract class BasicTickable implements Tickable {
+ @Override
+ public final void tick(float dt) {
+ if (canTick()) {
+ onTick(dt);
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/scheduler/tickable/TickPriority.java b/src/main/java/org/spout/api/scheduler/tickable/TickPriority.java
new file mode 100644
index 0000000..eeb9fbd
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/tickable/TickPriority.java
@@ -0,0 +1,47 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler.tickable;
+
+/**
+ * Represents a {@link org.spout.api.component.Component}s priority
+ */
+public enum TickPriority {
+ LOWEST(0),
+ LOW(1),
+ NORMAL(2),
+ HIGH(3),
+ HIGHEST(4);
+ private final int index;
+
+ private TickPriority(int index) {
+ this.index = index;
+ }
+
+ public int getIndex() {
+ return index;
+ }
+}
diff --git a/src/main/java/org/spout/api/scheduler/tickable/Tickable.java b/src/main/java/org/spout/api/scheduler/tickable/Tickable.java
new file mode 100644
index 0000000..016a448
--- /dev/null
+++ b/src/main/java/org/spout/api/scheduler/tickable/Tickable.java
@@ -0,0 +1,55 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.scheduler.tickable;
+
+public interface Tickable {
+ /**
+ * Called each simulation tick.
Override this to perform logic upon ticking.
1 tick = 1/20 second
20 ticks = 1 second
1200 ticks = 1 minute
72000 ticks = 1
+ * hour
1728000 ticks = 1 day
+ *
+ * @param dt time since the last tick in seconds
+ */
+ public void onTick(float dt);
+
+ /**
+ * Whether or not this tickable can perform a tick
+ *
+ * @return true if it can tick, false if not
+ */
+ public boolean canTick();
+
+ /**
+ * Ticks the Tickable.
Call this to make the Tickable tick.
+ *
+ * Standard implementation is if(canTick()) { onTick(dt); }
+ *
+ * 1 tick = 1/20 second
20 ticks = 1 second
1200 ticks = 1 minute
72000 ticks = 1 hour
1728000 ticks = 1 day
+ *
+ * @param dt time since the last tick in seconds
+ */
+ public void tick(float dt);
+}
diff --git a/src/main/java/org/spout/api/store/BinaryFileStore.java b/src/main/java/org/spout/api/store/BinaryFileStore.java
new file mode 100644
index 0000000..a6380be
--- /dev/null
+++ b/src/main/java/org/spout/api/store/BinaryFileStore.java
@@ -0,0 +1,157 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.store;
+
+import com.flowpowered.commons.store.MemoryStore;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.EOFException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+/**
+ * This implements a SimpleStore that is stored in memory. The save and load methods can be used to write the map to a binary file.
+ */
+public class BinaryFileStore extends MemoryStore {
+ private File file;
+ private boolean dirty = true;
+
+ public BinaryFileStore(File file) {
+ super();
+ this.file = file;
+ }
+
+ public BinaryFileStore() {
+ this(null);
+ }
+
+ public synchronized void setFile(File file) {
+ this.file = file;
+ }
+
+ public synchronized File getFile() {
+ return file;
+ }
+
+ @Override
+ public synchronized boolean clear() {
+ dirty = true;
+ return super.clear();
+ }
+
+ @Override
+ public synchronized boolean save() {
+ if (!dirty) {
+ return true;
+ }
+
+ boolean saved = true;
+ DataOutputStream out = null;
+ try {
+ out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
+ Iterator> itr = super.getEntrySet().iterator();
+
+ while (itr.hasNext()) {
+ Entry next = itr.next();
+ out.writeInt(next.getValue());
+ out.writeUTF(next.getKey());
+ }
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ saved = false;
+ } finally {
+ try {
+ if (out != null) {
+ out.close();
+ }
+ } catch (IOException ioe) {
+ ioe.printStackTrace();
+ saved = false;
+ }
+ if (saved) {
+ dirty = false;
+ }
+ }
+ return saved;
+ }
+
+ @Override
+ public synchronized boolean load() {
+ boolean loaded = true;
+ DataInputStream in = null;
+ try {
+ in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
+
+ boolean eof = false;
+ while (!eof) {
+ try {
+ Integer id = in.readInt();
+ String key = in.readUTF();
+ set(key, id);
+ } catch (EOFException eofe) {
+ eof = true;
+ }
+ }
+ } catch (IOException ioe) {
+ loaded = false;
+ } finally {
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException ioe) {
+ loaded = false;
+ }
+ }
+ if (loaded) {
+ dirty = false;
+ }
+ return loaded;
+ }
+
+ @Override
+ public synchronized Integer remove(String key) {
+ Integer value = super.remove(key);
+ if (value != null) {
+ dirty = true;
+ }
+ return value;
+ }
+
+ @Override
+ public synchronized Integer set(String key, Integer value) {
+ dirty = true;
+ return super.set(key, value);
+ }
+}
diff --git a/src/main/java/org/spout/api/store/FlatFileStore.java b/src/main/java/org/spout/api/store/FlatFileStore.java
new file mode 100644
index 0000000..fd09092
--- /dev/null
+++ b/src/main/java/org/spout/api/store/FlatFileStore.java
@@ -0,0 +1,164 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.store;
+
+import com.flowpowered.commons.FileUtil;
+import com.flowpowered.commons.store.MemoryStore;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map.Entry;
+
+/**
+ * This implements a SimpleStore that is stored in memory. The save and load methods can be used to write the map to a File.
+ */
+public class FlatFileStore extends MemoryStore {
+ private final File file;
+ private boolean dirty = false;
+ private final Class> clazz; // preserve class, so parser knows what to do
+
+ public FlatFileStore(File file, Class> clazz) {
+ super();
+ this.clazz = clazz;
+ this.file = file;
+ if (file != null) {
+ if (!file.exists()) {
+ if (!FileUtil.createFile(file)) {
+ }
+ }
+ }
+ }
+
+ @Override
+ public synchronized boolean clear() {
+ dirty = true;
+ return super.clear();
+ }
+
+ @Override
+ public synchronized boolean save() {
+ if (!dirty) {
+ return true;
+ }
+
+ Collection strings = getStrings();
+ boolean saved = FileUtil.stringToFile(strings, file);
+ if (saved) {
+ dirty = false;
+ }
+
+ return saved;
+ }
+
+ @Override
+ public synchronized boolean load() {
+ Collection strings = FileUtil.fileToString(file);
+ if (strings == null) {
+ return false;
+ }
+ boolean loaded = processStrings(strings);
+ if (loaded) {
+ dirty = false;
+ }
+ return loaded;
+ }
+
+ @Override
+ public synchronized T remove(String key) {
+ T value = super.remove(key);
+ if (value != null) {
+ dirty = true;
+ }
+ return value;
+ }
+
+ @Override
+ public synchronized T set(String key, T value) {
+ dirty = true;
+ return super.set(key, value);
+ }
+
+ private synchronized Collection getStrings() {
+ Iterator> itr = super.getEntrySet().iterator();
+ ArrayList strings = new ArrayList<>(super.getSize());
+ while (itr.hasNext()) {
+ Entry entry = itr.next();
+ String encodedKey = encode(entry.getKey());
+ T value = entry.getValue();
+ strings.add(value + ":" + encodedKey);
+ }
+ return strings;
+ }
+
+ private boolean processStrings(Collection strings) {
+ super.clear();
+ for (String string : strings) {
+ String[] split = string.trim().split(":");
+ if (split.length != 2) {
+ return false;
+ }
+ T value;
+ try {
+ value = parse(split[0]);
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ String key = decode(split[1]);
+ set(key, value);
+ }
+ return true;
+ }
+
+ private static String encode(String key) {
+ String encoded = key;
+ encoded = encoded.replace("\\", "\\\\");
+ encoded = encoded.replace("\n", "\\n");
+ encoded = encoded.replace(":", "\\:");
+ return encoded;
+ }
+
+ private static String decode(String encoded) {
+ String key = encoded;
+ key = key.replace("\\:", ":");
+ key = key.replace("\\n", "\n");
+ key = key.replace("\\\\", "\\");
+ return encoded;
+ }
+
+ @SuppressWarnings ("unchecked")
+ private T parse(String string) {
+ if (clazz.equals(Integer.class)) {
+ return (T) (Object) Integer.parseInt(string);
+ } else if (clazz.equals(String.class)) {
+ return (T) string;
+ } else {
+ throw new IllegalArgumentException("Unable to parse clazzes of type " + clazz.getName());
+ }
+ }
+}
diff --git a/src/main/java/org/spout/api/util/SyncedMapEvent.java b/src/main/java/org/spout/api/util/SyncedMapEvent.java
new file mode 100644
index 0000000..f5ea73b
--- /dev/null
+++ b/src/main/java/org/spout/api/util/SyncedMapEvent.java
@@ -0,0 +1,62 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.util;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.commons.lang3.tuple.Pair;
+
+import com.flowpowered.events.object.ObjectEvent;
+
+/**
+ * Event called when modifications occur on a StringMap
+ */
+public class SyncedMapEvent extends ObjectEvent {
+ public static enum Action {
+ ADD,
+ SET,
+ REMOVE,
+ }
+
+ private final Action action;
+ private final List> modifiedElements;
+
+ public SyncedMapEvent(SyncedStringMap map, Action action, List> modifiedElements) {
+ super(map);
+ this.action = action;
+ this.modifiedElements = Collections.unmodifiableList(modifiedElements);
+ }
+
+ public Action getAction() {
+ return action;
+ }
+
+ public List> getModifiedElements() {
+ return modifiedElements;
+ }
+}
diff --git a/src/main/java/org/spout/api/util/SyncedMapRegistry.java b/src/main/java/org/spout/api/util/SyncedMapRegistry.java
new file mode 100644
index 0000000..14454a2
--- /dev/null
+++ b/src/main/java/org/spout/api/util/SyncedMapRegistry.java
@@ -0,0 +1,101 @@
+/*
+ * This file is part of Spout.
+ *
+ * Copyright (c) 2011 Spout LLC
+ * Spout is licensed under the Spout License Version 1.
+ *
+ * Spout is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation, either version 3 of the License, or (at your option)
+ * any later version.
+ *
+ * In addition, 180 days after any changes are published, you can use the
+ * software, incorporating those changes, under the terms of the MIT license,
+ * as described in the Spout License Version 1.
+ *
+ * Spout is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License,
+ * the MIT license and the Spout License Version 1 along with this program.
+ * If not, see for the GNU Lesser General Public
+ * License and see for the full license, including
+ * the MIT license.
+ */
+package org.spout.api.util;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import com.flowpowered.events.object.EventableBase;
+
+/**
+ * Represents a map for mapping Strings to unique ids.
+ *
+ * The class supports conversion of ids between maps and allocation of new unique ids for unknown Strings
+ *
+ * Conversions to and from parent/child maps are cached
+ */
+public final class SyncedMapRegistry extends EventableBase {
+ public static final byte REGISTRATION_MAP = -1;
+ protected static final SyncedStringMap STRING_MAP_REGISTRATION = new SyncedStringMap("REGISTRATION_MAP"); // This is a special case
+ protected static final ConcurrentMap> REGISTERED_MAPS = new ConcurrentHashMap<>();
+
+ public static SyncedStringMap get(String name) {
+ WeakReference ref = REGISTERED_MAPS.get(name);
+ if (ref != null) {
+ SyncedStringMap map = ref.get();
+ if (map == null) {
+ REGISTERED_MAPS.remove(name);
+ }
+ return map;
+ }
+ return null;
+ }
+
+ public static SyncedStringMap get(int id) {
+ if (id == REGISTRATION_MAP) {
+ return STRING_MAP_REGISTRATION;
+ }
+ String name = STRING_MAP_REGISTRATION.getString(id);
+ if (name != null) {
+ WeakReference ref = REGISTERED_MAPS.get(name);
+ if (ref != null) {
+ SyncedStringMap map = ref.get();
+ if (map == null) {
+ REGISTERED_MAPS.remove(name);
+ }
+ return map;
+ }
+ }
+ return null;
+ }
+
+ public static Collection