Resolve merge conflict.

This commit is contained in:
Major-
2015-04-15 18:36:00 +01:00
546 changed files with 2638 additions and 2465 deletions
+4 -3
View File
@@ -13,12 +13,12 @@ class OpenDoorAction < DistancedAction
def executeAction def executeAction
mob.turn_to(@door.position) mob.turn_to(@door.position)
DoorUtil::toggle(@door, mob) DoorUtil::toggle(@door)
stop stop
end end
def equals(other) def equals(other)
return (get_class == other.get_class && @door_object == other.door_object) return (get_class == other.get_class && @door == other.door)
end end
end end
@@ -28,6 +28,7 @@ on :message, :first_object_action do |player, message|
if DoorUtil::is_door?(message.id) if DoorUtil::is_door?(message.id)
puts "Player: #{player.position}, door: #{message.position}" puts "Player: #{player.position}, door: #{message.position}"
door = DoorUtil::get_door_object(message.position, message.id) door = DoorUtil::get_door_object(message.position, message.id)
player.start_action(OpenDoorAction.new(player, door)) unless door.nil? DoorUtil::toggle(door) unless door.nil?
# player.start_action(OpenDoorAction.new(player, door)) unless door.nil?
end end
end end
+2 -2
View File
@@ -46,7 +46,7 @@ module DoorUtil
end end
# Toggles the given door. # Toggles the given door.
def self.toggle(door, player) def self.toggle(door)
position = door.position position = door.position
region = $world.region_repository.from_position(position) region = $world.region_repository.from_position(position)
region.remove_entity(door) region.remove_entity(door)
@@ -59,7 +59,7 @@ module DoorUtil
else else
toggled_position = translate_door_position(door) toggled_position = translate_door_position(door)
toggled_orientation = translate_door_orientation(door) toggled_orientation = translate_door_orientation(door)
toggled_door = DynamicGameObject.createPublic(door.id, toggled_position, door.type, toggled_orientation) toggled_door = DynamicGameObject.createPublic($world, door.id, toggled_position, door.type, toggled_orientation)
toggled_region = $world.region_repository.from_position(toggled_position) toggled_region = $world.region_repository.from_position(toggled_position)
toggled_region.add_entity(toggled_door) toggled_region.add_entity(toggled_door)
+66 -67
View File
@@ -1,78 +1,77 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd"> http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>org.apollo</artifactId> <artifactId>org.apollo</artifactId>
<version>1.0</version> <version>1.0</version>
<groupId>apollo</groupId> <groupId>apollo</groupId>
<build> <build>
<sourceDirectory>src</sourceDirectory> <sourceDirectory>src</sourceDirectory>
<testSourceDirectory>test</testSourceDirectory> <testSourceDirectory>test</testSourceDirectory>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version> <version>3.1</version>
<configuration> <configuration>
<source>1.8</source> <source>1.8</source>
<target>1.8</target> <target>1.8</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.codehaus.mojo</groupId> <groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId> <artifactId>exec-maven-plugin</artifactId>
<version>1.3.2</version> <version>1.3.2</version>
<configuration> <configuration>
<mainClass>org.apollo.Server</mainClass> <mainClass>org.apollo.Server</mainClass>
<arguments> <arguments>
<argument>-server</argument> <argument>-server</argument>
<argument>-Xmx750M</argument> <argument>-Xmx750M</argument>
</arguments> </arguments>
</configuration> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId> <artifactId>commons-compress</artifactId>
<version>1.9</version> <version>1.9</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jruby</groupId> <groupId>org.jruby</groupId>
<artifactId>jruby-complete</artifactId> <artifactId>jruby-complete</artifactId>
<version>1.7.19</version> <version>1.7.19</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>18.0</version> <version>18.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>
<version>4.0.26.Final</version> <version>4.0.27.Final</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.lambdaworks</groupId> <groupId>com.lambdaworks</groupId>
<artifactId>scrypt</artifactId> <artifactId>scrypt</artifactId>
<version>1.4.0</version> <version>1.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.mchange</groupId> <groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId> <artifactId>c3p0</artifactId>
<version>0.9.5</version> <version>0.9.5</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>
+145 -163
View File
@@ -31,58 +31,49 @@ public final class IsaacRandom {
private static final int GOLDEN_RATIO = 0x9e3779b9; private static final int GOLDEN_RATIO = 0x9e3779b9;
/** /**
* The log of the size of the result and memory arrays. * The log of the size of the result and state arrays.
*/ */
private static final int LOG_SIZE = 8; private static final int LOG_SIZE = Long.BYTES;
/** /**
* The size of the result and memory arrays. * The size of the result and states arrays.
*/ */
private static final int SIZE = 1 << LOG_SIZE; private static final int SIZE = 1 << LOG_SIZE;
/** /**
* A mask for pseudorandom lookup. * A mask for pseudo-random lookup.
*/ */
private static int MASK = SIZE - 1 << 2; private static int MASK = SIZE - 1 << 2;
/** /**
* The accumulator. * The results given to the user.
*/ */
private int a; private final int[] results = new int[SIZE];
/**
* The last result.
*/
private int b;
/**
* The counter.
*/
private int c;
/**
* The count through the results in the results array.
*/
private int count;
/** /**
* The internal state. * The internal state.
*/ */
private int[] mem; private final int[] state = new int[SIZE];
/** /**
* The results given to the user. * The count through the results in the results array.
*/ */
private int[] rsl; private int count = SIZE;
/** /**
* Creates the random number generator without an initial seed. * The accumulator.
*/ */
public IsaacRandom() { private int accumulator;
mem = new int[SIZE];
rsl = new int[SIZE]; /**
init(false); * The last result.
} */
private int last;
/**
* The counter.
*/
private int counter;
/** /**
* Creates the random number generator with the specified seed. * Creates the random number generator with the specified seed.
@@ -90,20 +81,75 @@ public final class IsaacRandom {
* @param seed The seed. * @param seed The seed.
*/ */
public IsaacRandom(int[] seed) { public IsaacRandom(int[] seed) {
mem = new int[SIZE]; int length = Math.min(seed.length, results.length);
rsl = new int[SIZE]; System.arraycopy(seed, 0, results, 0, length);
for (int i = 0; i < seed.length; ++i) { init();
rsl[i] = seed[i];
}
init(true);
} }
/** /**
* Initialises this random number generator. * Generates 256 results.
*
* @param hasSeed Set to {@code true} if a seed was passed to the constructor.
*/ */
private void init(boolean hasSeed) { private void isaac() {
int i, j, x, y;
last += ++counter;
for (i = 0, j = SIZE / 2; i < SIZE / 2;) {
x = state[i];
accumulator ^= accumulator << 13;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator >>> 6;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator << 2;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator >>> 16;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
}
for (j = 0; j < SIZE / 2;) {
x = state[i];
accumulator ^= accumulator << 13;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator >>> 6;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator << 2;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
x = state[i];
accumulator ^= accumulator >>> 16;
accumulator += state[j++];
state[i] = y = state[(x & MASK) >> 2] + accumulator + last;
results[i++] = last = state[(y >> LOG_SIZE & MASK) >> 2] + x;
}
}
/**
* Initializes this random number generator.
*/
private void init() {
int i; int i;
int a, b, c, d, e, f, g, h; int a, b, c, d, e, f, g, h;
a = b = c = d = e = f = g = h = GOLDEN_RATIO; a = b = c = d = e = f = g = h = GOLDEN_RATIO;
@@ -136,16 +182,15 @@ public final class IsaacRandom {
} }
for (i = 0; i < SIZE; i += 8) { /* fill in mem[] with messy stuff */ for (i = 0; i < SIZE; i += 8) { /* fill in mem[] with messy stuff */
if (hasSeed) { a += results[i];
a += rsl[i]; b += results[i + 1];
b += rsl[i + 1]; c += results[i + 2];
c += rsl[i + 2]; d += results[i + 3];
d += rsl[i + 3]; e += results[i + 4];
e += rsl[i + 4]; f += results[i + 5];
f += rsl[i + 5]; g += results[i + 6];
g += rsl[i + 6]; h += results[i + 7];
h += rsl[i + 7];
}
a ^= b << 11; a ^= b << 11;
d += a; d += a;
b += c; b += c;
@@ -170,123 +215,60 @@ public final class IsaacRandom {
h ^= a >>> 9; h ^= a >>> 9;
c += h; c += h;
a += b; a += b;
mem[i] = a; state[i] = a;
mem[i + 1] = b; state[i + 1] = b;
mem[i + 2] = c; state[i + 2] = c;
mem[i + 3] = d; state[i + 3] = d;
mem[i + 4] = e; state[i + 4] = e;
mem[i + 5] = f; state[i + 5] = f;
mem[i + 6] = g; state[i + 6] = g;
mem[i + 7] = h; state[i + 7] = h;
} }
if (hasSeed) { /* second pass makes all of seed affect all of mem */ for (i = 0; i < SIZE; i += 8) {
for (i = 0; i < SIZE; i += 8) { a += state[i];
a += mem[i]; b += state[i + 1];
b += mem[i + 1]; c += state[i + 2];
c += mem[i + 2]; d += state[i + 3];
d += mem[i + 3]; e += state[i + 4];
e += mem[i + 4]; f += state[i + 5];
f += mem[i + 5]; g += state[i + 6];
g += mem[i + 6]; h += state[i + 7];
h += mem[i + 7]; a ^= b << 11;
a ^= b << 11; d += a;
d += a; b += c;
b += c; b ^= c >>> 2;
b ^= c >>> 2; e += b;
e += b; c += d;
c += d; c ^= d << 8;
c ^= d << 8; f += c;
f += c; d += e;
d += e; d ^= e >>> 16;
d ^= e >>> 16; g += d;
g += d; e += f;
e += f; e ^= f << 10;
e ^= f << 10; h += e;
h += e; f += g;
f += g; f ^= g >>> 4;
f ^= g >>> 4; a += f;
a += f; g += h;
g += h; g ^= h << 8;
g ^= h << 8; b += g;
b += g; h += a;
h += a; h ^= a >>> 9;
h ^= a >>> 9; c += h;
c += h; a += b;
a += b; state[i] = a;
mem[i] = a; state[i + 1] = b;
mem[i + 1] = b; state[i + 2] = c;
mem[i + 2] = c; state[i + 3] = d;
mem[i + 3] = d; state[i + 4] = e;
mem[i + 4] = e; state[i + 5] = f;
mem[i + 5] = f; state[i + 6] = g;
mem[i + 6] = g; state[i + 7] = h;
mem[i + 7] = h;
}
} }
isaac(); isaac();
count = SIZE;
}
/**
* Generates 256 results.
*/
private void isaac() {
int i, j, x, y;
b += ++c;
for (i = 0, j = SIZE / 2; i < SIZE / 2;) {
x = mem[i];
a ^= a << 13;
a += mem[j++];
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
rsl[i++] = b = mem[(y >> LOG_SIZE & MASK) >> 2] + x;
x = mem[i];
a ^= a >>> 6;
a += mem[j++];
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
rsl[i++] = b = mem[(y >> LOG_SIZE & MASK) >> 2] + x;
x = mem[i];
a ^= a << 2;
a += mem[j++];
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
rsl[i++] = b = mem[(y >> LOG_SIZE & MASK) >> 2] + x;
x = mem[i];
a ^= a >>> 16;
a += mem[j++];
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
rsl[i++] = b = mem[(y >> LOG_SIZE & MASK) >> 2] + x;
}
for (j = 0; j < SIZE / 2;) {
x = mem[i];
a ^= a << 13;
a += mem[j++];
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
rsl[i++] = b = mem[(y >> LOG_SIZE & MASK) >> 2] + x;
x = mem[i];
a ^= a >>> 6;
a += mem[j++];
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
rsl[i++] = b = mem[(y >> LOG_SIZE & MASK) >> 2] + x;
x = mem[i];
a ^= a << 2;
a += mem[j++];
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
rsl[i++] = b = mem[(y >> LOG_SIZE & MASK) >> 2] + x;
x = mem[i];
a ^= a >>> 16;
a += mem[j++];
mem[i] = y = mem[(x & MASK) >> 2] + a + b;
rsl[i++] = b = mem[(y >> LOG_SIZE & MASK) >> 2] + x;
}
} }
/** /**
@@ -299,7 +281,7 @@ public final class IsaacRandom {
isaac(); isaac();
count = SIZE - 1; count = SIZE - 1;
} }
return rsl[count]; return results[count];
} }
} }
+16 -13
View File
@@ -10,6 +10,7 @@ import io.netty.channel.socket.nio.NioServerSocketChannel;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -25,6 +26,8 @@ import org.apollo.net.release.r317.Release317;
import org.apollo.util.plugin.PluginContext; import org.apollo.util.plugin.PluginContext;
import org.apollo.util.plugin.PluginManager; import org.apollo.util.plugin.PluginManager;
import com.google.common.base.Stopwatch;
/** /**
* The core class of the Apollo server. * The core class of the Apollo server.
* *
@@ -43,8 +46,9 @@ public final class Server {
* @param args The command-line arguments passed to the application. * @param args The command-line arguments passed to the application.
*/ */
public static void main(String[] args) { public static void main(String[] args) {
Stopwatch stopwatch = Stopwatch.createStarted();
try { try {
long start = System.currentTimeMillis();
Server server = new Server(); Server server = new Server();
server.init(args.length == 1 ? args[0] : Release317.class.getName()); server.init(args.length == 1 ? args[0] : Release317.class.getName());
@@ -53,10 +57,11 @@ public final class Server {
SocketAddress jaggrab = new InetSocketAddress(NetworkConstants.JAGGRAB_PORT); SocketAddress jaggrab = new InetSocketAddress(NetworkConstants.JAGGRAB_PORT);
server.bind(service, http, jaggrab); server.bind(service, http, jaggrab);
logger.fine("Starting apollo took " + (System.currentTimeMillis() - start) + " ms.");
} catch (Throwable t) { } catch (Throwable t) {
logger.log(Level.SEVERE, "Error whilst starting server.", t); logger.log(Level.SEVERE, "Error whilst starting server.", t);
} }
logger.fine("Starting apollo took " + stopwatch.elapsed(TimeUnit.MILLISECONDS) + " ms.");
} }
/** /**
@@ -81,10 +86,8 @@ public final class Server {
/** /**
* Creates the Apollo server. * Creates the Apollo server.
*
* @throws Exception If an error occurs whilst creating services.
*/ */
public Server() throws Exception { public Server() {
logger.info("Starting Apollo..."); logger.info("Starting Apollo...");
} }
@@ -98,14 +101,14 @@ public final class Server {
public void bind(SocketAddress serviceAddress, SocketAddress httpAddress, SocketAddress jagGrabAddress) { public void bind(SocketAddress serviceAddress, SocketAddress httpAddress, SocketAddress jagGrabAddress) {
try { try {
logger.fine("Binding service listener to address: " + serviceAddress + "..."); logger.fine("Binding service listener to address: " + serviceAddress + "...");
serviceBootstrap.bind(serviceAddress).syncUninterruptibly(); serviceBootstrap.bind(serviceAddress).sync();
logger.fine("Binding HTTP listener to address: " + httpAddress + "..."); logger.fine("Binding HTTP listener to address: " + httpAddress + "...");
httpBootstrap.bind(httpAddress).syncUninterruptibly(); httpBootstrap.bind(httpAddress).sync();
logger.fine("Binding JAGGRAB listener to address: " + jagGrabAddress + "..."); logger.fine("Binding JAGGRAB listener to address: " + jagGrabAddress + "...");
jagGrabBootstrap.bind(jagGrabAddress).syncUninterruptibly(); jagGrabBootstrap.bind(jagGrabAddress).sync();
} catch (Exception e) { } catch (InterruptedException e) {
logger.log(Level.SEVERE, "Binding to a port failed: ensure apollo isn't already running.", e); logger.log(Level.SEVERE, "Binding to a port failed: ensure apollo isn't already running.", e);
System.exit(1); System.exit(1);
} }
@@ -122,8 +125,9 @@ public final class Server {
public void init(String releaseClassName) throws Exception { public void init(String releaseClassName) throws Exception {
Class<?> clazz = Class.forName(releaseClassName); Class<?> clazz = Class.forName(releaseClassName);
Release release = (Release) clazz.newInstance(); Release release = (Release) clazz.newInstance();
int releaseNo = release.getReleaseNumber();
logger.info("Initialized release #" + release.getReleaseNumber() + "."); logger.info("Initialized release #" + releaseNo + ".");
serviceBootstrap.group(loopGroup); serviceBootstrap.group(loopGroup);
httpBootstrap.group(loopGroup); httpBootstrap.group(loopGroup);
@@ -131,7 +135,8 @@ public final class Server {
World world = new World(); World world = new World();
ServiceManager serviceManager = new ServiceManager(world); ServiceManager serviceManager = new ServiceManager(world);
ServerContext context = new ServerContext(release, serviceManager); IndexedFileSystem fs = new IndexedFileSystem(Paths.get("data/fs", Integer.toString(releaseNo)), true);
ServerContext context = new ServerContext(release, serviceManager, fs);
ApolloHandler handler = new ApolloHandler(context); ApolloHandler handler = new ApolloHandler(context);
ChannelInitializer<SocketChannel> serviceInitializer = new ServiceChannelInitializer(handler); ChannelInitializer<SocketChannel> serviceInitializer = new ServiceChannelInitializer(handler);
@@ -149,8 +154,6 @@ public final class Server {
PluginManager manager = new PluginManager(world, new PluginContext(context)); PluginManager manager = new PluginManager(world, new PluginContext(context));
serviceManager.startAll(); serviceManager.startAll();
int releaseNo = release.getReleaseNumber();
IndexedFileSystem fs = new IndexedFileSystem(Paths.get("data/fs", Integer.toString(releaseNo)), true);
world.init(releaseNo, fs, manager); world.init(releaseNo, fs, manager);
} }
+22 -3
View File
@@ -1,5 +1,8 @@
package org.apollo; package org.apollo;
import java.util.Objects;
import org.apollo.fs.IndexedFileSystem;
import org.apollo.net.release.Release; import org.apollo.net.release.Release;
/** /**
@@ -21,16 +24,23 @@ public final class ServerContext {
*/ */
private final ServiceManager serviceManager; private final ServiceManager serviceManager;
/**
* The IndexedFileSystem.
*/
private final IndexedFileSystem fileSystem;
/** /**
* Creates a new server context. * Creates a new server context.
* *
* @param release The current release. * @param release The current release.
* @param serviceManager The service manager. * @param serviceManager The service manager.
* @param fileSystem The indexed file system.
*/ */
ServerContext(Release release, ServiceManager serviceManager) { protected ServerContext(Release release, ServiceManager serviceManager, IndexedFileSystem fileSystem) {
this.release = release; this.release = Objects.requireNonNull(release);
this.serviceManager = serviceManager; this.serviceManager = Objects.requireNonNull(serviceManager);
this.serviceManager.setContext(this); this.serviceManager.setContext(this);
this.fileSystem = Objects.requireNonNull(fileSystem);
} }
/** /**
@@ -42,6 +52,15 @@ public final class ServerContext {
return release; return release;
} }
/**
* Gets the IndexeFileSystem
*
* @return The IndexedFileSystem.
*/
public IndexedFileSystem getFileSystem() {
return fileSystem;
}
/** /**
* Gets a service. This method is shorthand for {@code getServiceManager().getService(...)}. * Gets a service. This method is shorthand for {@code getServiceManager().getService(...)}.
* *
+1 -1
View File
@@ -27,7 +27,7 @@ public final class ServiceManager {
/** /**
* The service map. * The service map.
*/ */
private Map<Class<? extends Service>, Service> services = new HashMap<>(); private final Map<Class<? extends Service>, Service> services = new HashMap<>();
/** /**
* Creates and initializes the {@link ServiceManager}. * Creates and initializes the {@link ServiceManager}.
+24 -1
View File
@@ -7,6 +7,7 @@ import java.io.RandomAccessFile;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
@@ -24,6 +25,11 @@ public final class IndexedFileSystem implements Closeable {
*/ */
private ByteBuffer crcTable; private ByteBuffer crcTable;
/**
* The {@link #crcTable} represented as an {@code int} array.
*/
private int[] crcs;
/** /**
* The data file. * The data file.
*/ */
@@ -32,7 +38,7 @@ public final class IndexedFileSystem implements Closeable {
/** /**
* The index files. * The index files.
*/ */
private RandomAccessFile[] indices = new RandomAccessFile[256]; private final RandomAccessFile[] indices = new RandomAccessFile[256];
/** /**
* Read only flag. * Read only flag.
@@ -143,6 +149,23 @@ public final class IndexedFileSystem implements Closeable {
throw new IllegalStateException("Cannot get CRC table from a writable file system."); throw new IllegalStateException("Cannot get CRC table from a writable file system.");
} }
/**
* Gets the CRC table as an {@code int} array.
*
* @return The CRC table as an {@code int} array.
* @throws IOException If there is an error accessing files to create the table.
*/
public int[] getCrcs() throws IOException {
if (crcs != null) {
return crcs;
}
ByteBuffer buffer = getCrcTable();
crcs = new int[(buffer.remaining() / Integer.BYTES) - 1];
Arrays.setAll(crcs, crc -> buffer.getInt());
return crcs;
}
/** /**
* Gets a file. * Gets a file.
* *
@@ -133,7 +133,7 @@ public final class GameObjectDecoder {
if (block) { if (block) {
for (int dx = 0; dx < definition.getWidth(); dx++) { for (int dx = 0; dx < definition.getWidth(); dx++) {
for (int dy = 0; dy < definition.getLength(); dy++) { for (int dy = 0; dy < definition.getLength(); dy++) {
int localX = (x % Region.SIZE) + dx, localY = (y % Region.SIZE) + dy; int localX = x % Region.SIZE + dx, localY = y % Region.SIZE + dy;
if (localX > 7 || localY > 7) { if (localX > 7 || localY > 7) {
int nextLocalX = localX > 7 ? x + localX - 7 : x + localX; int nextLocalX = localX > 7 ? x + localX - 7 : x + localX;
@@ -141,11 +141,13 @@ public final class GameObjectDecoder {
Position nextPosition = new Position(nextLocalX, nextLocalY); Position nextPosition = new Position(nextLocalX, nextLocalY);
Region next = regions.fromPosition(nextPosition); Region next = regions.fromPosition(nextPosition);
int nextX = (nextPosition.getX() % Region.SIZE) + dx, nextY = (nextPosition.getY() % Region.SIZE) + dy; int nextX = nextPosition.getX() % Region.SIZE + dx, nextY = nextPosition.getY() % Region.SIZE + dy;
if (nextX > 7) if (nextX > 7) {
nextX -= 7; nextX -= 7;
if (nextY > 7) }
if (nextY > 7) {
nextY -= 7; nextY -= 7;
}
next.getMatrix(height).block(nextX, nextY); next.getMatrix(height).block(nextX, nextY);
continue; continue;
@@ -181,7 +183,7 @@ public final class GameObjectDecoder {
} }
if (block) { if (block) {
int localX = (x % Region.SIZE), localY = (y % Region.SIZE); int localX = x % Region.SIZE, localY = y % Region.SIZE;
current.block(localX, localY); current.block(localX, localY);
} }
} }
@@ -209,7 +211,7 @@ public final class GameObjectDecoder {
int localY = packed & 0x3F; int localY = packed & 0x3F;
int localX = packed >> 6 & 0x3F; int localX = packed >> 6 & 0x3F;
int height = (packed >> 12) & 0x3; int height = packed >> 12 & 0x3;
int attributes = buffer.get() & 0xFF; int attributes = buffer.get() & 0xFF;
int type = attributes >> 2; int type = attributes >> 2;
@@ -121,17 +121,13 @@ public final class NpcDefinitionDecoder {
} else if (opcode == 102 || opcode == 103) { } else if (opcode == 102 || opcode == 103) {
buffer.getShort(); buffer.getShort();
} else if (opcode == 106) { } else if (opcode == 106) {
@SuppressWarnings("unused") wrap(buffer.getShort());
int morphVariableBitsIndex = wrap(buffer.getShort()); wrap(buffer.getShort());
@SuppressWarnings("unused")
int morphismCount = wrap(buffer.getShort());
int count = buffer.get() & 0xFF; int count = buffer.get() & 0xFF;
int[] morphisms = new int[count + 1]; int[] morphisms = new int[count + 1];
Arrays.setAll(morphisms, index -> wrap(buffer.getShort())); Arrays.setAll(morphisms, index -> wrap(buffer.getShort()));
} else if (opcode == 107) { } else if (opcode == 107) {
@SuppressWarnings("unused")
boolean clickable = false;
} }
} }
} }
+2 -3
View File
@@ -20,12 +20,11 @@ import org.apollo.io.MessageHandlerChainSetParser;
import org.apollo.login.LoginService; import org.apollo.login.LoginService;
import org.apollo.net.session.GameSession; import org.apollo.net.session.GameSession;
import org.apollo.util.MobRepository; import org.apollo.util.MobRepository;
import org.apollo.util.ThreadUtil;
import org.apollo.util.xml.XmlNode; import org.apollo.util.xml.XmlNode;
import org.apollo.util.xml.XmlParser; import org.apollo.util.xml.XmlParser;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/** /**
* The {@link GameService} class schedules and manages the execution of the {@link GamePulseHandler} class. * The {@link GameService} class schedules and manages the execution of the {@link GamePulseHandler} class.
* *
@@ -52,7 +51,7 @@ public final class GameService extends Service {
/** /**
* The scheduled executor service. * The scheduled executor service.
*/ */
private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setNameFormat("GameService").build()); private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor(ThreadUtil.build("GameService"));
/** /**
* The {@link ClientSynchronizer}. * The {@link ClientSynchronizer}.
@@ -30,7 +30,7 @@ public final class MessageHandlerChainSet {
public <M extends Message> boolean notify(Player player, M message) { public <M extends Message> boolean notify(Player player, M message) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
MessageHandlerChain<M> chain = (MessageHandlerChain<M>) chains.get(message.getClass()); MessageHandlerChain<M> chain = (MessageHandlerChain<M>) chains.get(message.getClass());
return (chain == null) || chain.notify(player, message); return chain == null || chain.notify(player, message);
} }
/** /**
@@ -98,8 +98,7 @@ public final class EquipItemHandler extends MessageHandler<ItemOptionMessage> {
return; return;
} }
if (definition.getSlot() == EquipmentConstants.SHIELD && weapon != null if (definition.getSlot() == EquipmentConstants.SHIELD && weapon != null && EquipmentDefinition.lookup(weapon.getId()).isTwoHanded()) {
&& EquipmentDefinition.lookup(weapon.getId()).isTwoHanded()) {
equipment.set(EquipmentConstants.SHIELD, inventory.reset(inventorySlot)); equipment.set(EquipmentConstants.SHIELD, inventory.reset(inventorySlot));
inventory.add(equipment.reset(EquipmentConstants.WEAPON)); inventory.add(equipment.reset(EquipmentConstants.WEAPON));
return; return;
@@ -27,8 +27,7 @@ public final class ItemOnObjectVerificationHandler extends MessageHandler<ItemOn
@Override @Override
public void handle(Player player, ItemOnObjectMessage message) { public void handle(Player player, ItemOnObjectMessage message) {
if (message.getInterfaceId() != SynchronizationInventoryListener.INVENTORY_ID if (message.getInterfaceId() != SynchronizationInventoryListener.INVENTORY_ID && message.getInterfaceId() != BankConstants.SIDEBAR_INVENTORY_ID) {
&& message.getInterfaceId() != BankConstants.SIDEBAR_INVENTORY_ID) {
message.terminate(); message.terminate();
return; return;
} }
@@ -34,7 +34,7 @@ public final class ForwardPrivateChatMessage extends Message {
*/ */
public ForwardPrivateChatMessage(String username, PrivilegeLevel level, byte[] message) { public ForwardPrivateChatMessage(String username, PrivilegeLevel level, byte[] message) {
this.username = username; this.username = username;
this.privilege = level; privilege = level;
this.message = message; this.message = message;
} }
@@ -14,9 +14,9 @@ import org.apollo.game.model.area.RegionCoordinates;
public final class GroupedRegionUpdateMessage extends Message { public final class GroupedRegionUpdateMessage extends Message {
/** /**
* The Position of the Player. * The last known region Position of the Player.
*/ */
private final Position player; private final Position lastKnownRegion;
/** /**
* The Position of the Region being updated. * The Position of the Region being updated.
@@ -31,13 +31,13 @@ public final class GroupedRegionUpdateMessage extends Message {
/** /**
* Creates the GroupedRegionUpdateMessage. * Creates the GroupedRegionUpdateMessage.
* *
* @param player The {@link Position} of the Player. * @param lastKnownRegion The last known region {@link Position} of the Player.
* @param coordinates The {@link RegionCoordinates} of the Region being updated. * @param coordinates The {@link RegionCoordinates} of the Region being updated.
* @param messages The {@link List} of {@link RegionUpdateMessage}s. * @param messages The {@link List} of {@link RegionUpdateMessage}s.
*/ */
public GroupedRegionUpdateMessage(Position player, RegionCoordinates coordinates, List<RegionUpdateMessage> messages) { public GroupedRegionUpdateMessage(Position lastKnownRegion, RegionCoordinates coordinates, List<RegionUpdateMessage> messages) {
this.player = player; this.lastKnownRegion = lastKnownRegion;
this.region = new Position(coordinates.getAbsoluteX(), coordinates.getAbsoluteY()); region = new Position(coordinates.getAbsoluteX(), coordinates.getAbsoluteY());
this.messages = messages; this.messages = messages;
} }
@@ -46,8 +46,8 @@ public final class GroupedRegionUpdateMessage extends Message {
* *
* @return The Position. * @return The Position.
*/ */
public Position getPlayerPosition() { public Position getLastKnownRegion() {
return player; return lastKnownRegion;
} }
/** /**
@@ -33,7 +33,7 @@ public final class ItemOnObjectMessage extends InventoryItemMessage {
public ItemOnObjectMessage(int interfaceId, int itemId, int itemSlot, int objectId, int x, int y) { public ItemOnObjectMessage(int interfaceId, int itemId, int itemSlot, int objectId, int x, int y) {
super(0, interfaceId, itemId, itemSlot); super(0, interfaceId, itemId, itemSlot);
this.objectId = objectId; this.objectId = objectId;
this.position = new Position(x, y); position = new Position(x, y);
} }
/** /**
@@ -33,8 +33,8 @@ public final class RemoveObjectMessage extends RegionUpdateMessage {
*/ */
public RemoveObjectMessage(GameObject object, int positionOffset) { public RemoveObjectMessage(GameObject object, int positionOffset) {
this.positionOffset = positionOffset; this.positionOffset = positionOffset;
this.type = object.getType(); type = object.getType();
this.orientation = object.getOrientation(); orientation = object.getOrientation();
} }
@Override @Override
@@ -37,10 +37,10 @@ public final class SendObjectMessage extends RegionUpdateMessage {
* @param positionOffset The offset of the object's position from the region's central position. * @param positionOffset The offset of the object's position from the region's central position.
*/ */
public SendObjectMessage(GameObject object, int positionOffset) { public SendObjectMessage(GameObject object, int positionOffset) {
this.id = object.getId(); id = object.getId();
this.positionOffset = positionOffset; this.positionOffset = positionOffset;
this.type = object.getType(); type = object.getType();
this.orientation = object.getOrientation(); orientation = object.getOrientation();
} }
@Override @Override
@@ -44,7 +44,7 @@ public final class SetPlayerActionMessage extends Message {
public SetPlayerActionMessage(String text, int slot, boolean primaryInteraction) { public SetPlayerActionMessage(String text, int slot, boolean primaryInteraction) {
this.text = text; this.text = text;
this.slot = slot; this.slot = slot;
this.primaryAction = primaryInteraction; primaryAction = primaryInteraction;
} }
/** /**
@@ -15,7 +15,7 @@ public final class WalkMessage extends Message {
/** /**
* The running flag. * The running flag.
*/ */
private boolean run; private final boolean run;
/** /**
* The steps. * The steps.
+1 -1
View File
@@ -47,7 +47,7 @@ public final class Item {
Preconditions.checkArgument(amount >= 0, "Amount cannot be negative."); Preconditions.checkArgument(amount >= 0, "Amount cannot be negative.");
this.id = id; this.id = id;
this.amount = amount; this.amount = amount;
this.definition = ItemDefinition.lookup(id); definition = ItemDefinition.lookup(id);
} }
@Override @Override
+1 -1
View File
@@ -192,7 +192,7 @@ public final class Position {
* @return The y coordinate. * @return The y coordinate.
*/ */
public int getY() { public int getY() {
return (packed >> 15) & 0x7FFF; return packed >> 15 & 0x7FFF;
} }
@Override @Override
+4 -4
View File
@@ -80,7 +80,7 @@ public final class World {
/** /**
* The command dispatcher. * The command dispatcher.
*/ */
private CommandDispatcher commandDispatcher = new CommandDispatcher(); private final CommandDispatcher commandDispatcher = new CommandDispatcher();
/** /**
* The EventListenerChainSet for this World. * The EventListenerChainSet for this World.
@@ -208,7 +208,7 @@ public final class World {
* @throws Exception If any definitions could not be loaded or there was a failure when loading plugins. * @throws Exception If any definitions could not be loaded or there was a failure when loading plugins.
*/ */
public void init(int release, IndexedFileSystem fs, PluginManager manager) throws Exception { public void init(int release, IndexedFileSystem fs, PluginManager manager) throws Exception {
this.releaseNumber = release; releaseNumber = release;
ItemDefinitionDecoder itemDecoder = new ItemDefinitionDecoder(fs); ItemDefinitionDecoder itemDecoder = new ItemDefinitionDecoder(fs);
ItemDefinition[] items = itemDecoder.decode(); ItemDefinition[] items = itemDecoder.decode();
@@ -385,12 +385,12 @@ public final class World {
} }
/** /**
* Adds entities to regions in the {@link RegionRepository}. * Adds entities to regions in the {@link RegionRepository}. By default, we do not notify listeners.
* *
* @param entities The entities. * @param entities The entities.
*/ */
private void placeEntities(Entity... entities) { private void placeEntities(Entity... entities) {
Arrays.stream(entities).forEach(entity -> regions.fromPosition(entity.getPosition()).addEntity(entity)); Arrays.stream(entities).forEach(entity -> regions.fromPosition(entity.getPosition()).addEntity(entity, false));
} }
} }
+258 -236
View File
@@ -31,280 +31,302 @@ import com.google.common.collect.ImmutableSet;
*/ */
public final class Region { public final class Region {
/** /**
* A {@link RegionListener} for {@link UpdateOperation}s. * A {@link RegionListener} for {@link UpdateOperation}s.
* *
* @author Major * @author Major
*/ */
private static final class UpdateRegionListener implements RegionListener { private static final class UpdateRegionListener implements RegionListener {
@Override @Override
public void execute(Region region, Entity entity, EntityUpdateType update) { public void execute(Region region, Entity entity, EntityUpdateType update) {
EntityType type = entity.getEntityType(); EntityType type = entity.getEntityType();
if (type != EntityType.PLAYER && type != EntityType.NPC && (type != EntityType.STATIC_OBJECT || update == EntityUpdateType.REMOVE)) { if (type != EntityType.PLAYER && type != EntityType.NPC) {
region.record(entity, update); region.record(entity, update);
} }
} }
} }
/** /**
* The width and length of a Region, in tiles. * The width and length of a Region, in tiles.
*/ */
public static final int SIZE = 8; public static final int SIZE = 8;
static final long start = System.currentTimeMillis(); static final long start = System.currentTimeMillis();
/** /**
* The default size of newly-created sets, to reduce memory usage. * The default size of newly-created sets, to reduce memory usage.
*/ */
private static final int DEFAULT_SET_SIZE = 2; private static final int DEFAULT_SET_SIZE = 2;
/** /**
* The RegionCoordinates of this Region. * The RegionCoordinates of this Region.
*/ */
private final RegionCoordinates coordinates; private final RegionCoordinates coordinates;
/** /**
* The Map of Positions to Entities in that Position. * The Map of Positions to Entities in that Position.
*/ */
private final Map<Position, Set<Entity>> entities = new HashMap<>(); private final Map<Position, Set<Entity>> entities = new HashMap<>();
/** /**
* A List of RegionListeners registered to this Region. * A List of RegionListeners registered to this Region.
*/ */
private final List<RegionListener> listeners = new ArrayList<>(); private final List<RegionListener> listeners = new ArrayList<>();
/** /**
* The CollisionMatrix. * The CollisionMatrix.
*/ */
private final CollisionMatrix[] matrices = CollisionMatrix.createMatrices(Position.HEIGHT_LEVELS, SIZE, SIZE); private final CollisionMatrix[] matrices = CollisionMatrix.createMatrices(Position.HEIGHT_LEVELS, SIZE, SIZE);
/** /**
* The Set containing RegionUpdateMessages which can be sent to add every non-Mob Entity in this Region. * The Set containing RegionUpdateMessages which can be sent to add every non-Mob Entity in this Region.
*/ */
private final List<List<RegionUpdateMessage>> snapshots = new ArrayList<>(Position.HEIGHT_LEVELS); private final List<Map<Entity, RegionUpdateMessage>> snapshots = new ArrayList<>(Position.HEIGHT_LEVELS);
/** /**
* The Set containing UpdateOperations. * The Set containing UpdateOperations.
*/ */
private final List<List<RegionUpdateMessage>> updates = new ArrayList<>(Position.HEIGHT_LEVELS); private final List<List<RegionUpdateMessage>> updates = new ArrayList<>(Position.HEIGHT_LEVELS);
/** /**
* Creates a new Region. * Creates a new Region.
* *
* @param x The x coordinate of the Region. * @param x The x coordinate of the Region.
* @param y The y coordinate of the Region. * @param y The y coordinate of the Region.
*/ */
public Region(int x, int y) { public Region(int x, int y) {
this(new RegionCoordinates(x, y)); this(new RegionCoordinates(x, y));
} }
/** /**
* Creates a new Region with the specified {@link RegionCoordinates}. * Creates a new Region with the specified {@link RegionCoordinates}.
* *
* @param coordinates The coordinates. * @param coordinates The coordinates.
*/ */
public Region(RegionCoordinates coordinates) { public Region(RegionCoordinates coordinates) {
this.coordinates = coordinates; this.coordinates = coordinates;
listeners.add(new UpdateRegionListener()); listeners.add(new UpdateRegionListener());
for (int height = 0; height < Position.HEIGHT_LEVELS; height++) { for (int height = 0; height < Position.HEIGHT_LEVELS; height++) {
snapshots.add(new ArrayList<>()); snapshots.add(new HashMap<>());
updates.add(new ArrayList<>(DEFAULT_SET_SIZE)); updates.add(new ArrayList<>(DEFAULT_SET_SIZE));
} }
} }
/** /**
* Adds a {@link Entity} from to Region. Note that this does not spawn the Entity, or do any other action other than * Adds a {@link Entity} to the Region. Note that this does not spawn the Entity, or do any other action other than
* register it to this Region. * register it to this Region.
* *
* @param entity The Entity. * @param entity The Entity.
* @throws IllegalArgumentException If the Entity does not belong in this Region. * @param notify A flag indicating whether the {@link RegionListener}s for this Region should be notified.
*/ * @throws IllegalArgumentException If the Entity does not belong in this Region.
public void addEntity(Entity entity) { */
Position position = entity.getPosition(); public void addEntity(Entity entity, boolean notify) {
checkPosition(position); Position position = entity.getPosition();
checkPosition(position);
Set<Entity> local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE)); Set<Entity> local = entities.computeIfAbsent(position, key -> new HashSet<>(DEFAULT_SET_SIZE));
local.add(entity); local.add(entity);
if ((System.currentTimeMillis() - start) / 1000 > 10 && (entity instanceof GameObject)) { if ((System.currentTimeMillis() - start) / 1000 > 10 && (entity instanceof GameObject)) {
System.out.println("Adding entity " + entity + " to " + entity.getPosition()); System.out.println("Adding entity " + entity + " to " + entity.getPosition());
} }
notifyListeners(entity, EntityUpdateType.ADD); if (notify) {
} notifyListeners(entity, EntityUpdateType.ADD);
}
}
/** /**
* Checks if this Region contains the specified Entity. * Adds a {@link Entity} to the Region. Note that this does not spawn the Entity, or do any other action other than
* <p> * register it to this Region.
* This method operates in constant time. * <p/>
* * By default, this method notifies RegionListeners for this region of the addition.
* @param entity The Entity. *
* @return {@code true} if this Region contains the Entity, otherwise {@code false}. * @param entity The Entity.
*/ * @throws IllegalArgumentException If the Entity does not belong in this Region.
public boolean contains(Entity entity) { */
Position position = entity.getPosition(); public void addEntity(Entity entity) {
Set<Entity> local = entities.get(position); addEntity(entity, true);
}
return local != null && local.contains(entity); /**
} * Checks if this Region contains the specified Entity.
* <p/>
* This method operates in constant time.
*
* @param entity The Entity.
* @return {@code true} if this Region contains the Entity, otherwise {@code false}.
*/
/** public boolean contains(Entity entity) {
* Gets this Region's {@link RegionCoordinates}. Position position = entity.getPosition();
* Set<Entity> local = entities.get(position);
* @return The Region coordinates.
*/
public RegionCoordinates getCoordinates() {
return coordinates;
}
/** return local != null && local.contains(entity);
* Gets a shallow copy of the {@link Set} of {@link Entity} objects at the specified {@link Position}. The returned }
* type will be immutable.
*
* @param position The position containing the entities.
* @return The list.
*/
public Set<Entity> getEntities(Position position) {
Set<Entity> set = entities.get(position);
return (set == null) ? ImmutableSet.of() : ImmutableSet.copyOf(set);
}
/** /**
* Gets a shallow copy of the {@link Set} of {@link Entity}s with the specified {@link EntityType}(s). The returned * Gets this Region's {@link RegionCoordinates}.
* type will be immutable. Type will be inferred from the call, so ensure that the Entity type and the reference *
* correspond, or this method will fail at runtime. * @return The Region coordinates.
* */
* @param position The {@link Position} containing the entities. public RegionCoordinates getCoordinates() {
* @param types The {@link EntityType}s. return coordinates;
* @return The set of entities. }
*/
public <T extends Entity> Set<T> getEntities(Position position, EntityType... types) {
Set<Entity> local = entities.get(position);
if (local == null) {
return ImmutableSet.of();
}
Set<EntityType> set = new HashSet<>(Arrays.asList(types)); /**
@SuppressWarnings("unchecked") * Gets a shallow copy of the {@link Set} of {@link Entity} objects at the specified {@link Position}. The returned
Set<T> filtered = (Set<T>) local.stream().filter(entity -> set.contains(entity.getEntityType())).collect(Collectors.toSet()); * type will be immutable.
return ImmutableSet.copyOf(filtered); *
} * @param position The position containing the entities.
* @return The list.
*/
public Set<Entity> getEntities(Position position) {
Set<Entity> set = entities.get(position);
return (set == null) ? ImmutableSet.of() : ImmutableSet.copyOf(set);
}
/** /**
* Gets the {@link CollisionMatrix} at the specified height level. * Gets a shallow copy of the {@link Set} of {@link Entity}s with the specified {@link EntityType}(s). The returned
* * type will be immutable. Type will be inferred from the call, so ensure that the Entity type and the reference
* @param height The height level. * correspond, or this method will fail at runtime.
* @return The CollisionMatrix. *
*/ * @param position The {@link Position} containing the entities.
public CollisionMatrix getMatrix(int height) { * @param types The {@link EntityType}s.
Preconditions.checkElementIndex(height, matrices.length, "Matrix height level must be [0, " + matrices.length + ")."); * @return The set of entities.
return matrices[height]; */
} public <T extends Entity> Set<T> getEntities(Position position, EntityType... types) {
Set<Entity> local = entities.get(position);
if (local == null) {
return ImmutableSet.of();
}
/** Set<EntityType> set = new HashSet<>(Arrays.asList(types));
* Gets a {@link Set} containing {@link RegionUpdateMessage}s that add every {@link Entity} in this Region. @SuppressWarnings("unchecked")
* Set<T> filtered = (Set<T>) local.stream().filter(entity -> set.contains(entity.getEntityType())).collect(Collectors.toSet());
* @param height The height level to get the Set of RegionUpdateMessages for. return ImmutableSet.copyOf(filtered);
* @return The Set of RegionUpdateMessages. }
*/
public List<RegionUpdateMessage> getSnapshot(int height) {
List<RegionUpdateMessage> copy = new ArrayList<>(snapshots.get(height));
Collections.sort(copy);
return ImmutableList.copyOf(copy);
}
/** /**
* Gets the updates that have occurred in the last tick in this Region, as a {@link Set} of * Gets the {@link CollisionMatrix} at the specified height level.
* {@link RegionUpdateMessage}s. *
* * @param height The height level.
* @param height The height level to get the Set of RegionUpdateMessages for. * @return The CollisionMatrix.
* @return The Set of RegionUpdateMessages. */
*/ public CollisionMatrix getMatrix(int height) {
public List<RegionUpdateMessage> getUpdates(int height) { Preconditions.checkElementIndex(height, matrices.length, "Matrix height level must be [0, " + matrices.length + ").");
List<RegionUpdateMessage> original = this.updates.get(height); return matrices[height];
List<RegionUpdateMessage> updates = new ArrayList<>(original); }
original.clear();
Collections.sort(updates); /**
return ImmutableList.copyOf(updates); * Gets a {@link Set} containing {@link RegionUpdateMessage}s that add every {@link Entity} in this Region.
} *
* @param height The height level to get the Set of RegionUpdateMessages for.
* @return The Set of RegionUpdateMessages.
*/
public List<RegionUpdateMessage> getSnapshot(int height) {
List<RegionUpdateMessage> copy = new ArrayList<>(snapshots.get(height).values());
Collections.sort(copy);
return ImmutableList.copyOf(copy);
}
/** /**
* Notifies the {@link RegionListener}s registered to this Region that an update has occurred. * Gets the updates that have occurred in the last tick in this Region, as a {@link Set} of
* * {@link RegionUpdateMessage}s.
* @param entity The {@link Entity} that was updated. *
* @param type The {@link EntityUpdateType} that occurred. * @param height The height level to get the Set of RegionUpdateMessages for.
*/ * @return The Set of RegionUpdateMessages.
public void notifyListeners(Entity entity, EntityUpdateType type) { */
listeners.forEach(listener -> listener.execute(this, entity, type)); public List<RegionUpdateMessage> getUpdates(int height) {
} List<RegionUpdateMessage> original = this.updates.get(height);
List<RegionUpdateMessage> updates = new ArrayList<>(original);
original.clear();
/** Collections.sort(updates);
* Removes a {@link Entity} from this Region. return ImmutableList.copyOf(updates);
* }
* @param entity The Entity.
* @throws IllegalArgumentException If the Entity does not belong in this Region, or if it was never added.
*/
public void removeEntity(Entity entity) {
Position position = entity.getPosition();
checkPosition(position);
Set<Entity> local = entities.get(position); /**
* Notifies the {@link RegionListener}s registered to this Region that an update has occurred.
*
* @param entity The {@link Entity} that was updated.
* @param type The {@link EntityUpdateType} that occurred.
*/
public void notifyListeners(Entity entity, EntityUpdateType type) {
listeners.forEach(listener -> listener.execute(this, entity, type));
}
if (local == null || !local.remove(entity)) { /**
throw new IllegalArgumentException("Entity belongs in this Region (" + this + ") but does not exist."); * Removes a {@link Entity} from this Region.
} *
* @param entity The Entity.
* @throws IllegalArgumentException If the Entity does not belong in this Region, or if it was never added.
*/
public void removeEntity(Entity entity) {
Position position = entity.getPosition();
checkPosition(position);
notifyListeners(entity, EntityUpdateType.REMOVE); Set<Entity> local = entities.get(position);
}
@Override if (local == null || !local.remove(entity)) {
public String toString() { throw new IllegalArgumentException("Entity belongs in this Region (" + this + ") but does not exist.");
return MoreObjects.toStringHelper(this).add("coordinates", coordinates).toString(); }
}
/** notifyListeners(entity, EntityUpdateType.REMOVE);
* Returns whether or not an Entity of the specified {@link EntityType type} can traverse the tile at the specified }
* coordinate pair.
*
* @param position The {@link Position} of the tile.
* @param entity The {@link EntityType}.
* @param direction The {@link Direction} the Entity is approaching from.
* @return {@code true} if the tile at the specified coordinate pair is traversable, {@code false} if not.
*/
public boolean traversable(Position position, EntityType entity, Direction direction) {
CollisionMatrix matrix = matrices[position.getHeight()];
int x = position.getX(), y = position.getY();
return !matrix.untraversable(x % SIZE, y % SIZE, entity, direction); @Override
} public String toString() {
return MoreObjects.toStringHelper(this).add("coordinates", coordinates).toString();
}
/** /**
* Checks that the specified {@link Position} is included in this Region. * Returns whether or not an Entity of the specified {@link EntityType type} can traverse the tile at the specified
* * coordinate pair.
* @param position The position. *
* @throws IllegalArgumentException If the specified position is not included in this Region. * @param position The {@link Position} of the tile.
*/ * @param entity The {@link EntityType}.
private void checkPosition(Position position) { * @param direction The {@link Direction} the Entity is approaching from.
Preconditions.checkArgument(coordinates.equals(RegionCoordinates.fromPosition(position)), "Position is not included in this Region."); * @return {@code true} if the tile at the specified coordinate pair is traversable, {@code false} if not.
} */
public boolean traversable(Position position, EntityType entity, Direction direction) {
CollisionMatrix matrix = matrices[position.getHeight()];
int x = position.getX(), y = position.getY();
/** return !matrix.untraversable(x % SIZE, y % SIZE, entity, direction);
* Records the specified {@link Entity} as being updated this pulse. }
*
* @param entity The Entity.
* @param type The {@link EntityUpdateType}.
* @throws UnsupportedOperationException If the specified Entity cannot be operated on in this manner.
*/
private void record(Entity entity, EntityUpdateType type) {
RegionUpdateMessage message = entity.toUpdateOperation(this, type).toMessage();
int height = entity.getPosition().getHeight();
updates.get(height).add(message); /**
snapshots.get(height).add(message); * Checks that the specified {@link Position} is included in this Region.
} *
* @param position The position.
* @throws IllegalArgumentException If the specified position is not included in this Region.
*/
private void checkPosition(Position position) {
Preconditions.checkArgument(coordinates.equals(RegionCoordinates.fromPosition(position)), "Position is not included in this Region.");
}
/**
* Records the specified {@link Entity} as being updated this pulse.
*
* @param entity The Entity.
* @param type The {@link EntityUpdateType}.
* @throws UnsupportedOperationException If the specified Entity cannot be operated on in this manner.
*/
private void record(Entity entity, EntityUpdateType type) {
RegionUpdateMessage message = entity.toUpdateOperation(this, type).toMessage();
int height = entity.getPosition().getHeight();
updates.get(height).add(message);
snapshots.get(height).remove(entity);
if ((entity.getEntityType() == EntityType.STATIC_OBJECT && type == EntityUpdateType.REMOVE) ||
(entity.getEntityType() != EntityType.STATIC_OBJECT && type == EntityUpdateType.ADD)) {
snapshots.get(height).put(entity, message);
}
}
} }
@@ -56,7 +56,7 @@ public enum CollisionFlag {
* @return The array of CollisionFlags. * @return The array of CollisionFlags.
*/ */
public static CollisionFlag[] forType(EntityType type) { public static CollisionFlag[] forType(EntityType type) {
return (type == EntityType.PROJECTILE) ? projectiles() : mobs(); return type == EntityType.PROJECTILE ? projectiles() : mobs();
} }
/** /**
@@ -95,7 +95,7 @@ public abstract class UpdateOperation<E extends Entity> {
Preconditions.checkArgument(dx >= 0 && dx < Region.SIZE, position + " not in expected Region of " + region + "."); Preconditions.checkArgument(dx >= 0 && dx < Region.SIZE, position + " not in expected Region of " + region + ".");
Preconditions.checkArgument(dy >= 0 && dy < Region.SIZE, position + " not in expected Region of " + region + "."); Preconditions.checkArgument(dy >= 0 && dy < Region.SIZE, position + " not in expected Region of " + region + ".");
return (dx << 4) | dy; return dx << 4 | dy;
} }
} }
@@ -67,7 +67,7 @@ public final class EquipmentDefinition {
/** /**
* The array of skill requirement levels. * The array of skill requirement levels.
*/ */
private int[] levels = { 1, 1, 1, 1, 1, 1, 1 }; private final int[] levels = { 1, 1, 1, 1, 1, 1, 1 };
/** /**
* The slot this equipment goes into. * The slot this equipment goes into.
@@ -114,7 +114,7 @@ public final class ItemDefinition {
/** /**
* The ground actions array. * The ground actions array.
*/ */
private String[] groundActions = new String[5]; private final String[] groundActions = new String[5];
/** /**
* The item's id. * The item's id.
@@ -124,7 +124,7 @@ public final class ItemDefinition {
/** /**
* The inventory actions array. * The inventory actions array.
*/ */
private String[] inventoryActions = new String[5]; private final String[] inventoryActions = new String[5];
/** /**
* A flag indicating if this item is members only. * A flag indicating if this item is members only.
@@ -80,7 +80,7 @@ public final class NpcDefinition {
/** /**
* An array of interaction options. * An array of interaction options.
*/ */
private String[] interactions = new String[5]; private final String[] interactions = new String[5];
/** /**
* The name of the npc. * The name of the npc.
+1 -1
View File
@@ -535,7 +535,7 @@ public abstract class Mob extends Entity {
* @param position The position to face. * @param position The position to face.
*/ */
public final void turnTo(Position position) { public final void turnTo(Position position) {
this.facingPosition = position; facingPosition = position;
blockSet.add(SynchronizationBlock.createTurnToPositionBlock(position)); blockSet.add(SynchronizationBlock.createTurnToPositionBlock(position));
} }
+2 -2
View File
@@ -88,7 +88,7 @@ public final class Player extends Mob {
/** /**
* This player's credentials. * This player's credentials.
*/ */
private PlayerCredentials credentials; private final PlayerCredentials credentials;
/** /**
* A flag which indicates there are npcs that couldn't be added. * A flag which indicates there are npcs that couldn't be added.
@@ -870,7 +870,7 @@ public final class Player extends Mob {
* @param brightness The screen brightness. * @param brightness The screen brightness.
*/ */
public void setScreenBrightness(ScreenBrightness brightness) { public void setScreenBrightness(ScreenBrightness brightness) {
this.screenBrightness = brightness; screenBrightness = brightness;
} }
/** /**
@@ -223,7 +223,7 @@ public final class SkillSet {
continue; continue;
} }
current += (current < max ? 1 : -1); current += current < max ? 1 : -1;
setSkill(id, new Skill(skills[id].getExperience(), current, max)); setSkill(id, new Skill(skills[id].getExperience(), current, max));
} }
} }
@@ -67,7 +67,7 @@ public final class AttributeMap {
/** /**
* The map of attribute names to attributes. * The map of attribute names to attributes.
*/ */
private Map<String, Attribute<?>> attributes = new HashMap<>(DEFAULT_MAP_SIZE); private final Map<String, Attribute<?>> attributes = new HashMap<>(DEFAULT_MAP_SIZE);
/** /**
* Gets the {@link Attribute} with the specified name. * Gets the {@link Attribute} with the specified name.
@@ -30,13 +30,13 @@ public final class NumericalAttribute extends Attribute<Number> {
@Override @Override
public byte[] encode() { public byte[] encode() {
long encoded = (type == AttributeType.DOUBLE) ? Double.doubleToLongBits((double) value) : (long) value; long encoded = type == AttributeType.DOUBLE ? Double.doubleToLongBits((double) value) : (long) value;
return Longs.toByteArray(encoded); return Longs.toByteArray(encoded);
} }
@Override @Override
public String toString() { public String toString() {
return (type == AttributeType.DOUBLE) ? Double.toString((double) value) : Long.toString((long) value); return type == AttributeType.DOUBLE ? Double.toString((double) value) : Long.toString((long) value);
} }
} }
@@ -44,7 +44,7 @@ public final class DynamicGameObject extends GameObject {
@Override @Override
public int hashCode() { public int hashCode() {
Player player = get(); Player player = get();
return (player == null) ? 0 : player.hashCode(); return player == null ? 0 : player.hashCode();
} }
} }
@@ -35,7 +35,7 @@ public abstract class GameObject extends Entity {
*/ */
public GameObject(World world, int id, Position position, int type, int orientation) { public GameObject(World world, int id, Position position, int type, int orientation) {
super(world, position); super(world, position);
this.packed = id << 8 | (type & 0x3F) << 2 | orientation & 0x3; packed = id << 8 | (type & 0x3F) << 2 | orientation & 0x3;
} }
@Override @Override
@@ -81,7 +81,7 @@ public abstract class GameObject extends Entity {
* @return The type. * @return The type.
*/ */
public int getType() { public int getType() {
return (packed >> 2) & 0x3F; return packed >> 2 & 0x3F;
} }
@Override @Override
@@ -88,7 +88,7 @@ public final class SimplePathfindingAlgorithm extends PathfindingAlgorithm {
} }
Position last = new Position(x, y, height); Position last = new Position(x, y, height);
if (!start.equals(last) && dy != 0 && traversable(last, boundaries, (dy > 0) ? Direction.SOUTH : Direction.NORTH)) { if (!start.equals(last) && dy != 0 && traversable(last, boundaries, dy > 0 ? Direction.SOUTH : Direction.NORTH)) {
return addVertical(last, target, positions); return addVertical(last, target, positions);
} }
@@ -132,7 +132,7 @@ public final class SimplePathfindingAlgorithm extends PathfindingAlgorithm {
} }
Position last = new Position(x, y, height); Position last = new Position(x, y, height);
if (!last.equals(target) && dx != 0 && traversable(last, boundaries, (dx > 0) ? Direction.WEST : Direction.EAST)) { if (!last.equals(target) && dx != 0 && traversable(last, boundaries, dx > 0 ? Direction.WEST : Direction.EAST)) {
return addHorizontal(last, target, positions); return addHorizontal(last, target, positions);
} }
@@ -24,7 +24,7 @@ public final class EventListenerChainSet {
public <E extends Event> boolean notify(E event) { public <E extends Event> boolean notify(E event) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
EventListenerChain<E> chain = (EventListenerChain<E>) chains.get(event.getClass()); EventListenerChain<E> chain = (EventListenerChain<E>) chains.get(event.getClass());
return (chain == null) || chain.notify(event); return chain == null || chain.notify(event);
} }
/** /**
@@ -6,9 +6,6 @@ import org.apollo.game.model.event.Event;
/** /**
* An {@link Event} created when a Mob's Position is being updated. * An {@link Event} created when a Mob's Position is being updated.
* <p>
* This Event intentionally ignores the result of execution - it should not be possible for a plugin to prevent this
* Event from happening, only to listen for it.
* *
* @author Major * @author Major
*/ */
@@ -51,7 +51,7 @@ public final class InterfaceSet {
/** /**
* A map of open interfaces. * A map of open interfaces.
*/ */
private Map<InterfaceType, Integer> interfaces = new HashMap<>(); private final Map<InterfaceType, Integer> interfaces = new HashMap<>();
/** /**
* The current listener. * The current listener.
@@ -150,7 +150,7 @@ public final class InterfaceSet {
public void openDialogue(DialogueListener listener, int dialogueId) { public void openDialogue(DialogueListener listener, int dialogueId) {
closeAndNotify(); closeAndNotify();
this.dialogueListener = Optional.ofNullable(listener); dialogueListener = Optional.ofNullable(listener);
this.listener = Optional.ofNullable(listener); this.listener = Optional.ofNullable(listener);
interfaces.put(InterfaceType.DIALOGUE, dialogueId); interfaces.put(InterfaceType.DIALOGUE, dialogueId);
@@ -175,7 +175,7 @@ public final class InterfaceSet {
public void openDialogueOverlay(DialogueListener listener, int dialogueId) { public void openDialogueOverlay(DialogueListener listener, int dialogueId) {
closeAndNotify(); closeAndNotify();
this.dialogueListener = Optional.ofNullable(listener); dialogueListener = Optional.ofNullable(listener);
this.listener = Optional.ofNullable(listener); this.listener = Optional.ofNullable(listener);
interfaces.put(InterfaceType.DIALOGUE, dialogueId); interfaces.put(InterfaceType.DIALOGUE, dialogueId);
@@ -18,7 +18,8 @@ public interface DialogueListener extends InterfaceListener {
* </p> * </p>
* *
* @param button The button interface id. * @param button The button interface id.
* @return {@code true} if the {@link MessageHandlerChain} should be broken, {@code false} if it should be continued. * @return {@code true} if the {@link MessageHandlerChain} should be broken, {@code false} if it should be
* continued.
*/ */
public boolean buttonClicked(int button); public boolean buttonClicked(int button);
+1 -1
View File
@@ -540,7 +540,7 @@ public final class Inventory {
int removed = Math.min(amount, itemAmount); int removed = Math.min(amount, itemAmount);
int remainder = itemAmount - removed; int remainder = itemAmount - removed;
set(slot, (remainder > 0) ? new Item(item.getId(), remainder) : null); set(slot, remainder > 0 ? new Item(item.getId(), remainder) : null);
return removed; return removed;
} }
} }
@@ -82,8 +82,8 @@ public final class NpcMovementTask extends ScheduledTask {
int currentX = current.getX(), currentY = current.getY(); int currentX = current.getX(), currentY = current.getY();
boolean negativeX = RANDOM.nextBoolean(), negativeY = RANDOM.nextBoolean(); boolean negativeX = RANDOM.nextBoolean(), negativeY = RANDOM.nextBoolean();
int x = RANDOM.nextInt(negativeX ? (currentX - min.getX()) : (max.getX() - currentX)); int x = RANDOM.nextInt(negativeX ? currentX - min.getX() : max.getX() - currentX);
int y = RANDOM.nextInt(negativeY ? (currentY - min.getY()) : (max.getY() - currentY)); int y = RANDOM.nextInt(negativeY ? currentY - min.getY() : max.getY() - currentY);
int dx = negativeX ? -x : x; int dx = negativeX ? -x : x;
int dy = negativeY ? -y : y; int dy = negativeY ? -y : y;
@@ -6,7 +6,6 @@ import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Phaser; import java.util.concurrent.Phaser;
import java.util.concurrent.ThreadFactory;
import org.apollo.game.GameService; import org.apollo.game.GameService;
import org.apollo.game.message.impl.RegionUpdateMessage; import org.apollo.game.message.impl.RegionUpdateMessage;
@@ -22,8 +21,7 @@ import org.apollo.game.sync.task.PreNpcSynchronizationTask;
import org.apollo.game.sync.task.PrePlayerSynchronizationTask; import org.apollo.game.sync.task.PrePlayerSynchronizationTask;
import org.apollo.game.sync.task.SynchronizationTask; import org.apollo.game.sync.task.SynchronizationTask;
import org.apollo.util.MobRepository; import org.apollo.util.MobRepository;
import org.apollo.util.ThreadUtil;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/** /**
* An implementation of {@link ClientSynchronizer} which runs in a thread pool. A {@link Phaser} is used to ensure that * An implementation of {@link ClientSynchronizer} which runs in a thread pool. A {@link Phaser} is used to ensure that
@@ -49,12 +47,10 @@ public final class ParallelClientSynchronizer extends ClientSynchronizer {
/** /**
* Creates the parallel client synchronizer backed by a thread pool with a number of threads equal to the number of * Creates the parallel client synchronizer backed by a thread pool with a number of threads equal to the number of
* processing cores available (this is found by the {@link Runtime#availableProcessors()} method. * processing cores available (this is found by the {@link ThreadUtil#AVAILABLE_PROCESSORS} method.
*/ */
public ParallelClientSynchronizer() { public ParallelClientSynchronizer() {
int processors = Runtime.getRuntime().availableProcessors(); executor = Executors.newFixedThreadPool(ThreadUtil.AVAILABLE_PROCESSORS, ThreadUtil.build("ClientSynchronizer"));
ThreadFactory factory = new ThreadFactoryBuilder().setNameFormat("ClientSynchronizer").build();
executor = Executors.newFixedThreadPool(processors, factory);
} }
@Override @Override
@@ -49,8 +49,7 @@ public final class NpcSynchronizationTask extends SynchronizationTask {
for (Iterator<Npc> it = localNpcs.iterator(); it.hasNext();) { for (Iterator<Npc> it = localNpcs.iterator(); it.hasNext();) {
Npc npc = it.next(); Npc npc = it.next();
if (!npc.isActive() || npc.isTeleporting() if (!npc.isActive() || npc.isTeleporting() || npc.getPosition().getLongestDelta(playerPosition) > player.getViewingDistance()) {
|| npc.getPosition().getLongestDelta(playerPosition) > player.getViewingDistance()) {
it.remove(); it.remove();
segments.add(new RemoveMobSegment()); segments.add(new RemoveMobSegment());
} else { } else {
@@ -69,8 +68,7 @@ public final class NpcSynchronizationTask extends SynchronizationTask {
} }
Position npcPosition = npc.getPosition(); Position npcPosition = npc.getPosition();
if (npcPosition.isWithinDistance(playerPosition, player.getViewingDistance()) && !localNpcs.contains(npc) if (npcPosition.isWithinDistance(playerPosition, player.getViewingDistance()) && !localNpcs.contains(npc) && npcPosition.getHeight() == playerPosition.getHeight()) {
&& npcPosition.getHeight() == playerPosition.getHeight()) {
localNpcs.add(npc); localNpcs.add(npc);
added++; added++;
npc.turnTo(npc.getFacingPosition()); npc.turnTo(npc.getFacingPosition());
@@ -80,8 +80,7 @@ public final class PlayerSynchronizationTask extends SynchronizationTask {
int added = 0; int added = 0;
MobRepository<Player> repository = player.getWorld().getPlayerRepository(); MobRepository<Player> repository = player.getWorld().getPlayerRepository();
for (Iterator<Player> it = repository.iterator(); it.hasNext();) { for (Player other : repository) {
Player other = it.next();
if (localPlayers.size() >= 255) { if (localPlayers.size() >= 255) {
player.flagExcessivePlayers(); player.flagExcessivePlayers();
break; break;
@@ -181,7 +181,7 @@ public final class PrePlayerSynchronizationTask extends SynchronizationTask {
player.send(new ClearRegionMessage(position, coordinates)); player.send(new ClearRegionMessage(position, coordinates));
} }
Optional<GroupedRegionUpdateMessage> message = toUpdateMessage(local, position, coordinates, repository); Optional<GroupedRegionUpdateMessage> message = toUpdateMessage(local, player.getLastKnownRegion(), coordinates, repository);
if (message.isPresent()) { if (message.isPresent()) {
messages.add(message.get()); messages.add(message.get());
} }
@@ -208,7 +208,7 @@ public final class PrePlayerSynchronizationTask extends SynchronizationTask {
int deltaX = current.getLocalX(last); int deltaX = current.getLocalX(last);
int deltaY = current.getLocalY(last); int deltaY = current.getLocalY(last);
return deltaX <= Position.MAX_DISTANCE || deltaX >= (VIEWPORT_WIDTH - Position.MAX_DISTANCE - 1) || deltaY <= Position.MAX_DISTANCE || deltaY >= (VIEWPORT_WIDTH - Position.MAX_DISTANCE - 1); return deltaX <= Position.MAX_DISTANCE || deltaX >= VIEWPORT_WIDTH - Position.MAX_DISTANCE - 1 || deltaY <= Position.MAX_DISTANCE || deltaY >= VIEWPORT_WIDTH - Position.MAX_DISTANCE - 1;
} }
/** /**
@@ -216,12 +216,12 @@ public final class PrePlayerSynchronizationTask extends SynchronizationTask {
* {@link Optional#empty()} if no update message is required. * {@link Optional#empty()} if no update message is required.
* *
* @param mode The RegionUpdateMode for the Message. * @param mode The RegionUpdateMode for the Message.
* @param position The {@link Position} of the Player. * @param lastKnownRegion The last known region {@link Position} of the Player.
* @param coordinates The {@link RegionCoordinates} of the {@link Region}. * @param coordinates The {@link RegionCoordinates} of the {@link Region}.
* @param repository The {@link RegionRepository} containing the Regions. * @param repository The {@link RegionRepository} containing the Regions.
* @return The Optional containing the GroupedRegionUpdateMessage. * @return The Optional containing the GroupedRegionUpdateMessage.
*/ */
private Optional<GroupedRegionUpdateMessage> toUpdateMessage(RegionUpdateMode mode, Position position, RegionCoordinates coordinates, RegionRepository repository) { private Optional<GroupedRegionUpdateMessage> toUpdateMessage(RegionUpdateMode mode, Position lastKnownRegion, RegionCoordinates coordinates, RegionRepository repository) {
List<RegionUpdateMessage> messages; List<RegionUpdateMessage> messages;
/* /*
@@ -237,7 +237,7 @@ public final class PrePlayerSynchronizationTask extends SynchronizationTask {
messages = updates.get(coordinates); messages = updates.get(coordinates);
if (messages == null) { if (messages == null) {
synchronized (updates) { synchronized (updates) {
messages = updates.computeIfAbsent(coordinates, coords -> repository.get(coords).getUpdates(position.getHeight())); messages = updates.computeIfAbsent(coordinates, coords -> repository.get(coords).getUpdates(lastKnownRegion.getHeight()));
} }
} }
@@ -246,7 +246,7 @@ public final class PrePlayerSynchronizationTask extends SynchronizationTask {
messages = snapshots.get(coordinates); messages = snapshots.get(coordinates);
if (messages == null) { if (messages == null) {
synchronized (snapshots) { synchronized (snapshots) {
messages = snapshots.computeIfAbsent(coordinates, coords -> repository.get(coords).getSnapshot(position.getHeight())); messages = snapshots.computeIfAbsent(coordinates, coords -> repository.get(coords).getSnapshot(lastKnownRegion.getHeight()));
} }
} }
@@ -255,7 +255,7 @@ public final class PrePlayerSynchronizationTask extends SynchronizationTask {
throw new IllegalArgumentException("Unrecognised RegionUpdateMode " + mode + "."); throw new IllegalArgumentException("Unrecognised RegionUpdateMode " + mode + ".");
} }
return messages.isEmpty() ? Optional.empty() : Optional.of(new GroupedRegionUpdateMessage(position, coordinates, messages)); return messages.isEmpty() ? Optional.empty() : Optional.of(new GroupedRegionUpdateMessage(lastKnownRegion, coordinates, messages));
} }
} }
@@ -107,7 +107,7 @@ public final class BinaryPlayerSerializer extends PlayerSerializer {
int y = in.readUnsignedShort(); int y = in.readUnsignedShort();
int height = in.readUnsignedByte(); int height = in.readUnsignedByte();
Gender gender = (in.readUnsignedByte() == Gender.MALE.toInteger()) ? Gender.MALE : Gender.FEMALE; Gender gender = in.readUnsignedByte() == Gender.MALE.toInteger() ? Gender.MALE : Gender.FEMALE;
int[] style = new int[7]; int[] style = new int[7];
for (int slot = 0; slot < style.length; slot++) { for (int slot = 0; slot < style.length; slot++) {
style[slot] = in.readUnsignedByte(); style[slot] = in.readUnsignedByte();
+39 -13
View File
@@ -3,6 +3,7 @@ package org.apollo.login;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.Arrays;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@@ -16,12 +17,11 @@ import org.apollo.net.codec.login.LoginRequest;
import org.apollo.net.release.Release; import org.apollo.net.release.Release;
import org.apollo.net.session.GameSession; import org.apollo.net.session.GameSession;
import org.apollo.net.session.LoginSession; import org.apollo.net.session.LoginSession;
import org.apollo.util.ThreadUtil;
import org.apollo.util.xml.XmlNode; import org.apollo.util.xml.XmlNode;
import org.apollo.util.xml.XmlParser; import org.apollo.util.xml.XmlParser;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/** /**
* The {@link LoginService} manages {@link LoginRequest}s. * The {@link LoginService} manages {@link LoginRequest}s.
* *
@@ -33,7 +33,7 @@ public final class LoginService extends Service {
/** /**
* The {@link ExecutorService} to which workers are submitted. * The {@link ExecutorService} to which workers are submitted.
*/ */
private final ExecutorService executor = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("LoginService").build()); private final ExecutorService executor = Executors.newCachedThreadPool(ThreadUtil.build("LoginService"));
/** /**
* The current {@link PlayerSerializer}. * The current {@link PlayerSerializer}.
@@ -79,9 +79,6 @@ public final class LoginService extends Service {
this.serializer = (PlayerSerializer) clazz.getConstructor(World.class).newInstance(world); this.serializer = (PlayerSerializer) clazz.getConstructor(World.class).newInstance(world);
} }
/**
* Starts the login service.
*/
@Override @Override
public void start() { public void start() {
@@ -92,15 +89,44 @@ public final class LoginService extends Service {
* *
* @param session The session submitting this request. * @param session The session submitting this request.
* @param request The login request. * @param request The login request.
* @throws IOException If some I/O exception occurs.
*/ */
public void submitLoadRequest(LoginSession session, LoginRequest request) { public void submitLoadRequest(LoginSession session, LoginRequest request) throws IOException {
Release release = session.getRelease(); int response = LoginConstants.STATUS_OK;
if (release.getReleaseNumber() != request.getReleaseNumber()) {
// TODO check archive 0 CRCs if (requiresUpdate(session, request)) {
session.handlePlayerLoaderResponse(request, new PlayerLoaderResponse(LoginConstants.STATUS_GAME_UPDATED)); response = LoginConstants.STATUS_GAME_UPDATED;
} else {
executor.submit(new PlayerLoaderWorker(serializer, session, request));
} }
if (response == LoginConstants.STATUS_OK) {
executor.submit(new PlayerLoaderWorker(serializer, session, request));
} else {
session.handlePlayerLoaderResponse(request, new PlayerLoaderResponse(response));
}
}
/**
* Checks if an update is required whenever a {@link Player} submits a login request.
*
* @param session The login session.
* @param request The login request.
* @return {@code true} if an update is required, otherwise return {@code false}.
* @throws IOException If some I/O exception occurs.
*/
private boolean requiresUpdate(LoginSession session, LoginRequest request) throws IOException {
Release release = getContext().getRelease();
if (release.getReleaseNumber() != request.getReleaseNumber()) {
return true;
}
int[] clientCrcs = request.getArchiveCrcs();
int[] serverCrcs = getContext().getFileSystem().getCrcs();
if (Arrays.equals(clientCrcs, serverCrcs)) {
return false;
}
return true;
} }
/** /**
+6 -5
View File
@@ -66,13 +66,14 @@ public final class ApolloHandler extends ChannelInboundHandlerAdapter {
} }
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object message) { public void channelRead(ChannelHandlerContext ctx, Object message) throws Exception {
try { try {
Attribute<Session> attribute = ctx.attr(NetworkConstants.SESSION_KEY); Channel channel = ctx.channel();
Attribute<Session> attribute = channel.attr(NetworkConstants.SESSION_KEY);
Session session = attribute.get(); Session session = attribute.get();
if (message instanceof HttpRequest || message instanceof JagGrabRequest) { if (message instanceof HttpRequest || message instanceof JagGrabRequest) {
session = new UpdateSession(ctx.channel(), serverContext); session = new UpdateSession(channel, serverContext);
} }
if (session != null) { if (session != null) {
@@ -86,11 +87,11 @@ public final class ApolloHandler extends ChannelInboundHandlerAdapter {
switch (handshakeMessage.getServiceId()) { switch (handshakeMessage.getServiceId()) {
case HandshakeConstants.SERVICE_GAME: case HandshakeConstants.SERVICE_GAME:
attribute.set(new LoginSession(ctx, serverContext)); attribute.set(new LoginSession(channel, serverContext));
break; break;
case HandshakeConstants.SERVICE_UPDATE: case HandshakeConstants.SERVICE_UPDATE:
attribute.set(new UpdateSession(ctx.channel(), serverContext)); attribute.set(new UpdateSession(channel, serverContext));
break; break;
} }
} }
@@ -19,8 +19,8 @@ public final class FlaggedMouseEventMessageDecoder extends MessageDecoder<Flagge
int read; int read;
if (reader.getLength() == 2) { if (reader.getLength() == 2) {
read = (int) reader.getUnsigned(DataType.SHORT); read = (int) reader.getUnsigned(DataType.SHORT);
int clicks = (read >> 12); int clicks = read >> 12;
int dX = (read >> 6) & 0x3f; int dX = read >> 6 & 0x3f;
int dY = read & 0x3f; int dY = read & 0x3f;
return new FlaggedMouseEventMessage(clicks, dX, dY, true); return new FlaggedMouseEventMessage(clicks, dX, dY, true);
} else if (reader.getLength() == 3) { } else if (reader.getLength() == 3) {
@@ -29,7 +29,7 @@ public final class FlaggedMouseEventMessageDecoder extends MessageDecoder<Flagge
read = (int) reader.getUnsigned(DataType.INT) & ~0xc0000000; read = (int) reader.getUnsigned(DataType.INT) & ~0xc0000000;
} }
int clicks = (read >> 19); int clicks = read >> 19;
int x = (read & 0x7f) % 765; int x = (read & 0x7f) % 765;
int y = (read & 0x7f) / 765; int y = (read & 0x7f) / 765;
@@ -35,11 +35,11 @@ public final class GroupedRegionUpdateMessageEncoder extends MessageEncoder<Grou
@Override @Override
public GamePacket encode(GroupedRegionUpdateMessage message) { public GamePacket encode(GroupedRegionUpdateMessage message) {
GamePacketBuilder builder = new GamePacketBuilder(60, PacketType.VARIABLE_SHORT); GamePacketBuilder builder = new GamePacketBuilder(60, PacketType.VARIABLE_SHORT);
Position player = message.getPlayerPosition(), region = message.getRegionPosition(); Position lastKnownRegion = message.getLastKnownRegion(), region = message.getRegionPosition();
builder.put(DataType.BYTE, player.getLocalY(region)); builder.put(DataType.BYTE, region.getLocalY(lastKnownRegion));
System.out.println("Grum: local x: " + player.getLocalX(region) + ", local y: " + player.getLocalY(region)); builder.put(DataType.BYTE, DataTransformation.NEGATE, region.getLocalX(lastKnownRegion));
builder.put(DataType.BYTE, DataTransformation.NEGATE, player.getLocalX(region)); System.out.println("Grum: local x: " + lastKnownRegion.getLocalX(region) + ", local y: " + lastKnownRegion.getLocalY(region));
for (RegionUpdateMessage update : message.getMessages()) { for (RegionUpdateMessage update : message.getMessages()) {
System.out.println("==== Sending " + update + " as part of grum"); System.out.println("==== Sending " + update + " as part of grum");
@@ -19,9 +19,9 @@ public final class MouseClickedMessageDecoder extends MessageDecoder<MouseClicke
int value = (int) reader.getUnsigned(DataType.INT); int value = (int) reader.getUnsigned(DataType.INT);
long delay = (value >> 20) * 50; long delay = (value >> 20) * 50;
boolean right = ((value >> 19) & 0x1) == 1; boolean right = (value >> 19 & 0x1) == 1;
int cords = (value & 0x3FFFF); int cords = value & 0x3FFFF;
int x = cords % 765; int x = cords % 765;
int y = cords / 765; int y = cords / 765;
@@ -20,8 +20,8 @@ public final class FlaggedMouseEventMessageDecoder extends MessageDecoder<Flagge
if (reader.getLength() == 2) { if (reader.getLength() == 2) {
read = (int) reader.getUnsigned(DataType.SHORT); read = (int) reader.getUnsigned(DataType.SHORT);
clicks = (read >> 12); clicks = read >> 12;
x = (read >> 6) & 0x3f; x = read >> 6 & 0x3f;
y = read & 0x3f; y = read & 0x3f;
return new FlaggedMouseEventMessage(clicks, x, y, true); return new FlaggedMouseEventMessage(clicks, x, y, true);
} else if (reader.getLength() == 3) { } else if (reader.getLength() == 3) {
@@ -30,7 +30,7 @@ public final class FlaggedMouseEventMessageDecoder extends MessageDecoder<Flagge
read = (int) reader.getUnsigned(DataType.INT) & ~0xc0000000; read = (int) reader.getUnsigned(DataType.INT) & ~0xc0000000;
} }
clicks = (read >> 19); clicks = read >> 19;
x = (read & 0x7f) % 765; x = (read & 0x7f) % 765;
y = (read & 0x7f) / 765; y = (read & 0x7f) / 765;
return new FlaggedMouseEventMessage(clicks, x, y, false); return new FlaggedMouseEventMessage(clicks, x, y, false);
@@ -39,7 +39,7 @@ public final class GroupedRegionUpdateMessageEncoder extends MessageEncoder<Grou
@Override @Override
public GamePacket encode(GroupedRegionUpdateMessage message) { public GamePacket encode(GroupedRegionUpdateMessage message) {
GamePacketBuilder builder = new GamePacketBuilder(183, PacketType.VARIABLE_SHORT); GamePacketBuilder builder = new GamePacketBuilder(183, PacketType.VARIABLE_SHORT);
Position base = message.getPlayerPosition(), region = message.getRegionPosition(); Position base = message.getLastKnownRegion(), region = message.getRegionPosition();
builder.put(DataType.BYTE, region.getLocalX(base)); builder.put(DataType.BYTE, region.getLocalX(base));
builder.put(DataType.BYTE, DataTransformation.ADD, region.getLocalY(base)); builder.put(DataType.BYTE, DataTransformation.ADD, region.getLocalY(base));
@@ -19,9 +19,9 @@ public final class MouseClickedMessageDecoder extends MessageDecoder<MouseClicke
int value = (int) reader.getUnsigned(DataType.INT); int value = (int) reader.getUnsigned(DataType.INT);
long delay = (value >> 20) * 50; long delay = (value >> 20) * 50;
boolean right = ((value >> 19) & 0x1) == 1; boolean right = (value >> 19 & 0x1) == 1;
int cords = (value & 0x3FFFF); int cords = value & 0x3FFFF;
int x = cords % 765; int x = cords % 765;
int y = cords / 765; int y = cords / 765;
+3 -3
View File
@@ -16,7 +16,6 @@ import org.apollo.game.message.Message;
import org.apollo.game.message.MessageHandlerChainSet; import org.apollo.game.message.MessageHandlerChainSet;
import org.apollo.game.message.impl.LogoutMessage; import org.apollo.game.message.impl.LogoutMessage;
import org.apollo.game.model.entity.Player; import org.apollo.game.model.entity.Player;
import org.apollo.util.CollectionUtil;
/** /**
* A game session. * A game session.
@@ -84,13 +83,14 @@ public final class GameSession extends Session {
* @param chainSet The {@link MessageHandlerChainSet} * @param chainSet The {@link MessageHandlerChainSet}
*/ */
public void handlePendingMessages(MessageHandlerChainSet chainSet) { public void handlePendingMessages(MessageHandlerChainSet chainSet) {
CollectionUtil.pollAll(messageQueue, message -> { Message message;
while ((message = messageQueue.poll()) != null) {
try { try {
chainSet.notify(player, message); chainSet.notify(player, message);
} catch (Exception reason) { } catch (Exception reason) {
logger.log(Level.SEVERE, "Uncaught exception thrown while handling message: " + message, reason); logger.log(Level.SEVERE, "Uncaught exception thrown while handling message: " + message, reason);
} }
}); }
} }
/** /**
+17 -32
View File
@@ -3,8 +3,8 @@ package org.apollo.net.session;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import java.io.IOException;
import java.util.Optional; import java.util.Optional;
import org.apollo.ServerContext; import org.apollo.ServerContext;
@@ -13,7 +13,6 @@ import org.apollo.game.model.World.RegistrationStatus;
import org.apollo.game.model.entity.Player; import org.apollo.game.model.entity.Player;
import org.apollo.io.player.PlayerLoaderResponse; import org.apollo.io.player.PlayerLoaderResponse;
import org.apollo.login.LoginService; import org.apollo.login.LoginService;
import org.apollo.net.ApolloHandler;
import org.apollo.net.NetworkConstants; import org.apollo.net.NetworkConstants;
import org.apollo.net.codec.game.GameMessageDecoder; import org.apollo.net.codec.game.GameMessageDecoder;
import org.apollo.net.codec.game.GameMessageEncoder; import org.apollo.net.codec.game.GameMessageEncoder;
@@ -32,26 +31,20 @@ import org.apollo.security.IsaacRandomPair;
*/ */
public final class LoginSession extends Session { public final class LoginSession extends Session {
/**
* The context of the {@link ApolloHandler}.
*/
private final ChannelHandlerContext channelContext;
/** /**
* The server context. * The server context.
*/ */
private final ServerContext serverContext; private final ServerContext context;
/** /**
* Creates a login session for the specified channel. * Creates a login session for the specified channel.
* *
* @param ctx The context of the {@link ApolloHandler}. * @param channel The channel.
* @param serverContext The server context. * @param context The server context.
*/ */
public LoginSession(ChannelHandlerContext ctx, ServerContext serverContext) { public LoginSession(Channel channel, ServerContext context) {
super(ctx.channel()); super(channel);
this.channelContext = ctx; this.context = context;
this.serverContext = serverContext;
} }
@Override @Override
@@ -59,22 +52,14 @@ public final class LoginSession extends Session {
} }
/**
* Gets the release.
*
* @return The release.
*/
public Release getRelease() {
return serverContext.getRelease();
}
/** /**
* Handles a login request. * Handles a login request.
* *
* @param request The login request. * @param request The login request.
* @throws IOException If some I/O exception occurs.
*/ */
private void handleLoginRequest(LoginRequest request) { private void handleLoginRequest(LoginRequest request) throws IOException {
LoginService loginService = serverContext.getService(LoginService.class); LoginService loginService = context.getService(LoginService.class);
loginService.submitLoadRequest(this, request); loginService.submitLoadRequest(this, request);
} }
@@ -85,7 +70,7 @@ public final class LoginSession extends Session {
* @param response The response. * @param response The response.
*/ */
public void handlePlayerLoaderResponse(LoginRequest request, PlayerLoaderResponse response) { public void handlePlayerLoaderResponse(LoginRequest request, PlayerLoaderResponse response) {
GameService service = serverContext.getService(GameService.class); GameService service = context.getService(GameService.class);
Channel channel = getChannel(); Channel channel = getChannel();
Optional<Player> optional = response.getPlayer(); Optional<Player> optional = response.getPlayer();
@@ -96,14 +81,14 @@ public final class LoginSession extends Session {
Player player = optional.get(); Player player = optional.get();
rights = player.getPrivilegeLevel().toInteger(); rights = player.getPrivilegeLevel().toInteger();
GameSession session = new GameSession(channel, serverContext, player); GameSession session = new GameSession(channel, context, player);
RegistrationStatus registration = service.registerPlayer(player, session); RegistrationStatus registration = service.registerPlayer(player, session);
if (registration != RegistrationStatus.OK) { if (registration != RegistrationStatus.OK) {
optional = Optional.empty(); optional = Optional.empty();
rights = 0; rights = 0;
status = (registration == RegistrationStatus.ALREADY_ONLINE) ? LoginConstants.STATUS_ACCOUNT_ONLINE : LoginConstants.STATUS_SERVER_FULL; status = registration == RegistrationStatus.ALREADY_ONLINE ? LoginConstants.STATUS_ACCOUNT_ONLINE : LoginConstants.STATUS_SERVER_FULL;
} }
} }
@@ -113,18 +98,18 @@ public final class LoginSession extends Session {
if (optional.isPresent()) { if (optional.isPresent()) {
IsaacRandomPair randomPair = request.getRandomPair(); IsaacRandomPair randomPair = request.getRandomPair();
Release release = serverContext.getRelease(); Release release = context.getRelease();
channel.pipeline().addFirst("messageEncoder", new GameMessageEncoder(release)); channel.pipeline().addFirst("messageEncoder", new GameMessageEncoder(release));
channel.pipeline().addBefore("messageEncoder", "gameEncoder", new GamePacketEncoder(randomPair.getEncodingRandom())); channel.pipeline().addBefore("messageEncoder", "gameEncoder", new GamePacketEncoder(randomPair.getEncodingRandom()));
channel.pipeline().addBefore("handler", "gameDecoder", new GamePacketDecoder(randomPair.getDecodingRandom(), serverContext.getRelease())); channel.pipeline().addBefore("handler", "gameDecoder", new GamePacketDecoder(randomPair.getDecodingRandom(), context.getRelease()));
channel.pipeline().addAfter("gameDecoder", "messageDecoder", new GameMessageDecoder(release)); channel.pipeline().addAfter("gameDecoder", "messageDecoder", new GameMessageDecoder(release));
channel.pipeline().remove("loginDecoder"); channel.pipeline().remove("loginDecoder");
channel.pipeline().remove("loginEncoder"); channel.pipeline().remove("loginEncoder");
channelContext.attr(NetworkConstants.SESSION_KEY).set(optional.get().getSession()); channel.attr(NetworkConstants.SESSION_KEY).set(optional.get().getSession());
} else { } else {
future.addListener(ChannelFutureListener.CLOSE); future.addListener(ChannelFutureListener.CLOSE);
} }
@@ -135,7 +120,7 @@ public final class LoginSession extends Session {
} }
@Override @Override
public void messageReceived(Object message) { public void messageReceived(Object message) throws Exception {
if (message.getClass() == LoginRequest.class) { if (message.getClass() == LoginRequest.class) {
handleLoginRequest((LoginRequest) message); handleLoginRequest((LoginRequest) message);
} }
+2 -1
View File
@@ -42,7 +42,8 @@ public abstract class Session {
* Processes a message received from the channel. * Processes a message received from the channel.
* *
* @param message The message. * @param message The message.
* @throws Exception If some error occurs.
*/ */
public abstract void messageReceived(Object message); public abstract void messageReceived(Object message) throws Exception;
} }
+2 -3
View File
@@ -31,12 +31,11 @@ public final class RsaKeyGenerator {
do { do {
p = BigInteger.probablePrime(BIT_COUNT / 2, random); p = BigInteger.probablePrime(BIT_COUNT / 2, random);
q = BigInteger.probablePrime(BIT_COUNT / 2, random); q = BigInteger.probablePrime(BIT_COUNT / 2, random);
phi = (p.subtract(BigInteger.ONE)).multiply(q.subtract(BigInteger.ONE)); phi = p.subtract(BigInteger.ONE).multiply(q.subtract(BigInteger.ONE));
modulus = p.multiply(q); modulus = p.multiply(q);
privateKey = publicKey.modInverse(phi); privateKey = publicKey.modInverse(phi);
} while (modulus.bitLength() != BIT_COUNT || privateKey.bitLength() != BIT_COUNT } while (modulus.bitLength() != BIT_COUNT || privateKey.bitLength() != BIT_COUNT || !phi.gcd(publicKey).equals(BigInteger.ONE));
|| !phi.gcd(publicKey).equals(BigInteger.ONE));
System.out.println("modulus: " + modulus); System.out.println("modulus: " + modulus);
System.out.println("public key: " + publicKey); System.out.println("public key: " + publicKey);
+10 -19
View File
@@ -23,27 +23,18 @@ public final class TextUtil {
* @return The string with correct capitalization. * @return The string with correct capitalization.
*/ */
public static String capitalize(String str) { public static String capitalize(String str) {
char[] chars = str.toCharArray(); boolean capitalize = true;
boolean sentenceStart = true; StringBuilder bldr = new StringBuilder(str);
for (int i = 0; i < chars.length; i++) { for (int index = 0, length = str.length(); index < length; index++) {
char c = chars[i]; char character = bldr.charAt(index);
if (sentenceStart) { if (character == '.' || character == '!' || character == '?') {
if (c >= 'a' && c <= 'z') { capitalize = true;
chars[i] -= 0x20; } else if (capitalize && !Character.isWhitespace(character)) {
sentenceStart = false; bldr.setCharAt(index, Character.toUpperCase(character));
} else if (c >= 'A' && c <= 'Z') { capitalize = false;
sentenceStart = false;
}
} else {
if (c >= 'A' && c <= 'Z') {
chars[i] += 0x20;
}
}
if (c == '.' || c == '!' || c == '?') {
sentenceStart = true;
} }
} }
return new String(chars, 0, chars.length); return bldr.toString();
} }
/** /**
+136
View File
@@ -0,0 +1,136 @@
package org.apollo.util;
import static com.google.common.base.Preconditions.checkArgument;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Objects;
import java.util.concurrent.ThreadFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
/**
* A static utility class which provides ease of use functionality for {@link Thread}s
*
* @author Ryley Kimmel <ryley.kimmel@live.com>
*/
public final class ThreadUtil {
/**
* Returns the amount of available processors available to the Java virtual machine.
*/
public static final int AVAILABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
/**
* A {@link Logger} used to debug messages to the console.
*/
private static final Logger LOGGER = Logger.getLogger(ThreadUtil.class.getSimpleName());
/**
* The default {@link UncaughtExceptionHandler} which raises an error from the logger with the exception and name of
* the specified thread the exception occurred in.
*/
private static final UncaughtExceptionHandler DEFAULT_EXCEPTION_HANDLER = (thread, exception) -> LOGGER.log(Level.SEVERE, "Exception occured in thread " + thread.getName(), exception);
/**
* Builds a {@link ThreadFactory} using the specified {@code String} name-format, {@link ThreadPriority} and
* {@link UncaughtExceptionHandler}.
*
* @param name The name-format used when creating threads, may not be {@code null}.
* @param priority The priority used when creating threads, may not be {@code null}.
* @param handler The {@link UncaughtExceptionHandler} used when creating threads, may not be {@code null}.
* @return A new {@link ThreadFactory} from the specified parameters, never {@code null}.
*/
public static ThreadFactory build(String name, ThreadPriority priority, UncaughtExceptionHandler handler) {
Objects.requireNonNull(priority);
ThreadFactoryBuilder bldr = new ThreadFactoryBuilder();
bldr.setNameFormat(name);
bldr.setPriority(priority.getValue());
bldr.setUncaughtExceptionHandler(handler);
return bldr.build();
}
/**
* Builds a {@link ThreadFactory} using the specified {@code String} name-format, {@link ThreadPriority} and the
* default {@link UncaughtExceptionHandler}.
*
* @param name The name-format used when creating threads, may not be {@code null}.
* @param priority The priority used when creating threads, may not be {@code null}.
* @return A new {@link ThreadFactory} from the specified parameters, never {@code null}.
* @see {@link #DEFAULT_EXCEPTION_HANDLER}
*/
public static ThreadFactory build(String name, ThreadPriority priority) {
return build(name, priority, DEFAULT_EXCEPTION_HANDLER);
}
/**
* Builds a {@link ThreadFactory} using the specified {@code String} name-format, normal thread priority and the
* default {@link UncaughtExceptionHandler}.
*
* @param name The name-format used when creating threads, may not be {@code null}.
* @return A new {@link ThreadFactory} from the specified parameters, never {@code null}.
* @see {@link #DEFAULT_EXCEPTION_HANDLER}
* @see {@link ThreadPriority#NORMAL_PRIORITY}
*/
public static ThreadFactory build(String name) {
return build(name, ThreadPriority.NORMAL_PRIORITY, DEFAULT_EXCEPTION_HANDLER);
}
/**
* An enumeration representing the priority of a {@link Thread}.
*
* @author Ryley Kimmel <ryley.kimmel@live.com>
*/
public enum ThreadPriority {
/**
* Represents the minimum priority of a thread.
*/
MINIMUM_PRIORITY(1),
/**
* Represents the normal priority of a thread.
*/
NORMAL_PRIORITY(5),
/**
* Represents the maximum priority of a thread.
*/
MAXIMUM_PRIORITY(10);
/**
* The value of this thread priority.
*/
private final int value;
/**
* Constructs a new {@link ThreadPriority} with the specified value.
*
* @param value The value of this thread priority, must be within the bounds of {@link Thread#MIN_PRIORITY} and
* {@link Thread#MAX_PRIORITY}.
*/
private ThreadPriority(int value) {
// fail-fast for invalid priority values
checkArgument(value >= Thread.MIN_PRIORITY, "Thread priority (%s) must be >= %s", value, Thread.MIN_PRIORITY);
checkArgument(value <= Thread.MAX_PRIORITY, "Thread priority (%s) must be <= %s", value, Thread.MAX_PRIORITY);
this.value = value;
}
/**
* Returns the value of this thread priority.
*/
public final int getValue() {
return value;
}
}
/**
* Prevents the default-public constructor to discourage instantiation of this class.
*/
private ThreadUtil() {
}
}
@@ -29,7 +29,7 @@ public final class PluginManager {
/** /**
* A set of all author names. * A set of all author names.
*/ */
private SortedSet<String> authors = new TreeSet<>(); private final SortedSet<String> authors = new TreeSet<>();
/** /**
* The plugin context. * The plugin context.
@@ -164,8 +164,7 @@ public final class PluginManager {
* @throws DependencyException If a dependency error occurs. * @throws DependencyException If a dependency error occurs.
* @throws IOException If an I/O error occurs. * @throws IOException If an I/O error occurs.
*/ */
private void start(PluginEnvironment env, PluginMetaData plugin, Map<String, PluginMetaData> plugins, private void start(PluginEnvironment env, PluginMetaData plugin, Map<String, PluginMetaData> plugins, Set<PluginMetaData> started) throws DependencyException, IOException {
Set<PluginMetaData> started) throws DependencyException, IOException {
// TODO check for cyclic dependencies! this way just won't cut it, we need an exception // TODO check for cyclic dependencies! this way just won't cut it, we need an exception
if (started.contains(plugin)) { if (started.contains(plugin)) {
return; return;
+1 -1
View File
@@ -74,7 +74,7 @@ public final class XmlParser {
/** /**
* The stack of nodes, used when traversing the document and going through child nodes. * The stack of nodes, used when traversing the document and going through child nodes.
*/ */
private Stack<XmlNode> nodeStack = new Stack<>(); private final Stack<XmlNode> nodeStack = new Stack<>();
/** /**
* The current root node. * The current root node.