mirror of
https://github.com/2006-Scape/apollo.git
synced 2026-07-03 00:38:21 +00:00
Optimise definition decoding for faster start-up.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package org.apollo.cache.decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.apollo.cache.IndexedFileSystem;
|
||||
@@ -13,47 +14,46 @@ import org.apollo.util.BufferUtil;
|
||||
*
|
||||
* @author Graham
|
||||
*/
|
||||
public final class ItemDefinitionDecoder {
|
||||
public final class ItemDefinitionDecoder implements Runnable {
|
||||
|
||||
/**
|
||||
* The {@link IndexedFileSystem}.
|
||||
* The IndexedFileSystem.
|
||||
*/
|
||||
private final IndexedFileSystem fs;
|
||||
|
||||
/**
|
||||
* Creates the item definition decoder.
|
||||
* Creates the ItemDefinitionDecoder.
|
||||
*
|
||||
* @param fs The indexed file system.
|
||||
* @param fs The {@link IndexedFileSystem}.
|
||||
*/
|
||||
public ItemDefinitionDecoder(IndexedFileSystem fs) {
|
||||
this.fs = fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the item definitions.
|
||||
*
|
||||
* @return The item definitions.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
public ItemDefinition[] decode() throws IOException {
|
||||
Archive config = fs.getArchive(0, 2);
|
||||
ByteBuffer data = config.getEntry("obj.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("obj.idx").getBuffer();
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Archive config = fs.getArchive(0, 2);
|
||||
ByteBuffer data = config.getEntry("obj.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("obj.idx").getBuffer();
|
||||
|
||||
int count = idx.getShort(), index = 2;
|
||||
int[] indices = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices[i] = index;
|
||||
index += idx.getShort();
|
||||
int count = idx.getShort(), index = 2;
|
||||
int[] indices = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices[i] = index;
|
||||
index += idx.getShort();
|
||||
}
|
||||
|
||||
ItemDefinition[] definitions = new ItemDefinition[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
data.position(indices[i]);
|
||||
definitions[i] = decode(i, data);
|
||||
}
|
||||
|
||||
ItemDefinition.init(definitions);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error decoding ItemDefinitions.", e);
|
||||
}
|
||||
|
||||
ItemDefinition[] defs = new ItemDefinition[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
data.position(indices[i]);
|
||||
defs[i] = decode(i, data);
|
||||
}
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -121,12 +121,12 @@ public final class MapFileDecoder {
|
||||
int count = buffer.capacity() / (3 * Short.BYTES + Byte.BYTES);
|
||||
|
||||
for (int times = 0; times < count; times++) {
|
||||
int packed = buffer.getShort() & 0xFFFF;
|
||||
int id = buffer.getShort() & 0xFFFF;
|
||||
int terrain = buffer.getShort() & 0xFFFF;
|
||||
int objects = buffer.getShort() & 0xFFFF;
|
||||
boolean members = buffer.get() == 1;
|
||||
|
||||
definitions.put(packed, new MapDefinition(packed, terrain, objects, members));
|
||||
definitions.put(id, new MapDefinition(id, terrain, objects, members));
|
||||
}
|
||||
|
||||
return definitions;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.apollo.cache.decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
@@ -14,47 +15,47 @@ import org.apollo.util.BufferUtil;
|
||||
*
|
||||
* @author Major
|
||||
*/
|
||||
public final class NpcDefinitionDecoder {
|
||||
public final class NpcDefinitionDecoder implements Runnable {
|
||||
|
||||
/**
|
||||
* The {@link IndexedFileSystem}.
|
||||
* The IndexedFileSystem.
|
||||
*/
|
||||
private final IndexedFileSystem fs;
|
||||
|
||||
/**
|
||||
* Creates the npc definition decoder.
|
||||
* Creates the NpcDefinitionDecoder.
|
||||
*
|
||||
* @param fs The indexed file system.
|
||||
* @param fs The {@link IndexedFileSystem}.
|
||||
*/
|
||||
public NpcDefinitionDecoder(IndexedFileSystem fs) {
|
||||
this.fs = fs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the npc definitions.
|
||||
*
|
||||
* @return An array of all parsed npc definitions.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
public NpcDefinition[] decode() throws IOException {
|
||||
Archive config = fs.getArchive(0, 2);
|
||||
ByteBuffer data = config.getEntry("npc.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("npc.idx").getBuffer();
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Archive config = fs.getArchive(0, 2);
|
||||
ByteBuffer data = config.getEntry("npc.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("npc.idx").getBuffer();
|
||||
|
||||
int count = idx.getShort(), index = 2;
|
||||
int[] indices = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices[i] = index;
|
||||
index += idx.getShort();
|
||||
int count = idx.getShort(), index = 2;
|
||||
int[] indices = new int[count];
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices[i] = index;
|
||||
index += idx.getShort();
|
||||
}
|
||||
|
||||
NpcDefinition[] definitions = new NpcDefinition[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
data.position(indices[i]);
|
||||
definitions[i] = decode(i, data);
|
||||
}
|
||||
|
||||
NpcDefinition.init(definitions);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error decoding NpcDefinitions.", e);
|
||||
}
|
||||
|
||||
NpcDefinition[] defs = new NpcDefinition[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
data.position(indices[i]);
|
||||
defs[i] = decode(i, data);
|
||||
}
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -75,8 +76,8 @@ public final class NpcDefinitionDecoder {
|
||||
} else if (opcode == 1) {
|
||||
int length = buffer.get() & 0xFF;
|
||||
int[] models = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
models[i] = buffer.getShort();
|
||||
for (int index = 0; index < length; index++) {
|
||||
models[index] = buffer.getShort();
|
||||
}
|
||||
} else if (opcode == 2) {
|
||||
definition.setName(BufferUtil.readString(buffer));
|
||||
@@ -92,24 +93,27 @@ public final class NpcDefinitionDecoder {
|
||||
definition
|
||||
.setWalkAnimations(buffer.getShort(), buffer.getShort(), buffer.getShort(), buffer.getShort());
|
||||
} else if (opcode >= 30 && opcode < 40) {
|
||||
String str = BufferUtil.readString(buffer);
|
||||
if (str.equals("hidden")) {
|
||||
str = null;
|
||||
String action = BufferUtil.readString(buffer);
|
||||
if (action.equals("hidden")) {
|
||||
action = null;
|
||||
}
|
||||
definition.setInteraction(opcode - 30, str);
|
||||
|
||||
definition.setInteraction(opcode - 30, action);
|
||||
} else if (opcode == 40) {
|
||||
int length = buffer.get() & 0xFF;
|
||||
int[] originalColours = new int[length];
|
||||
int[] replacementColours = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
originalColours[i] = buffer.getShort();
|
||||
replacementColours[i] = buffer.getShort();
|
||||
|
||||
for (int index = 0; index < length; index++) {
|
||||
originalColours[index] = buffer.getShort();
|
||||
replacementColours[index] = buffer.getShort();
|
||||
}
|
||||
} else if (opcode == 60) {
|
||||
int length = buffer.get() & 0xFF;
|
||||
int[] additionalModels = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
additionalModels[i] = buffer.getShort();
|
||||
|
||||
for (int index = 0; index < length; index++) {
|
||||
additionalModels[index] = buffer.getShort();
|
||||
}
|
||||
} else if (opcode >= 90 && opcode <= 92) {
|
||||
buffer.getShort(); // Dummy
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package org.apollo.cache.decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.apollo.cache.IndexedFileSystem;
|
||||
import org.apollo.cache.archive.Archive;
|
||||
import org.apollo.cache.def.ItemDefinition;
|
||||
import org.apollo.cache.def.ObjectDefinition;
|
||||
import org.apollo.util.BufferUtil;
|
||||
|
||||
@@ -13,15 +15,15 @@ import org.apollo.util.BufferUtil;
|
||||
*
|
||||
* @author Major
|
||||
*/
|
||||
public final class ObjectDefinitionDecoder {
|
||||
public final class ObjectDefinitionDecoder implements Runnable {
|
||||
|
||||
/**
|
||||
* The {@link IndexedFileSystem}.
|
||||
* The IndexedFileSystem.
|
||||
*/
|
||||
private final IndexedFileSystem fs;
|
||||
|
||||
/**
|
||||
* Creates the decoder.
|
||||
* Creates the ObjectDefinitionDecoder.
|
||||
*
|
||||
* @param fs The {@link IndexedFileSystem}.
|
||||
*/
|
||||
@@ -29,6 +31,32 @@ public final class ObjectDefinitionDecoder {
|
||||
this.fs = fs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
Archive config = fs.getArchive(0, 2);
|
||||
ByteBuffer data = config.getEntry("loc.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("loc.idx").getBuffer();
|
||||
|
||||
int count = idx.getShort(), index = 2;
|
||||
int[] indices = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices[i] = index;
|
||||
index += idx.getShort();
|
||||
}
|
||||
|
||||
ObjectDefinition[] definitions = new ObjectDefinition[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
data.position(indices[i]);
|
||||
definitions[i] = decode(i, data);
|
||||
}
|
||||
|
||||
ObjectDefinition.init(definitions);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error decoding ObjectDefinitions.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes data from the cache into an {@link ObjectDefinition}.
|
||||
*
|
||||
@@ -36,7 +64,7 @@ public final class ObjectDefinitionDecoder {
|
||||
* @param data The {@link ByteBuffer} containing the data.
|
||||
* @return The object definition.
|
||||
*/
|
||||
public ObjectDefinition decode(int id, ByteBuffer data) {
|
||||
private ObjectDefinition decode(int id, ByteBuffer data) {
|
||||
ObjectDefinition definition = new ObjectDefinition(id);
|
||||
while (true) {
|
||||
int opcode = data.get() & 0xFF;
|
||||
@@ -111,30 +139,4 @@ public final class ObjectDefinitionDecoder {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes all of the data into {@link ObjectDefinition}s.
|
||||
*
|
||||
* @return The definitions.
|
||||
* @throws IOException If an error occurs when decoding the archive or finding an entry.
|
||||
*/
|
||||
public ObjectDefinition[] decode() throws IOException {
|
||||
Archive config = fs.getArchive(0, 2);
|
||||
ByteBuffer data = config.getEntry("loc.dat").getBuffer();
|
||||
ByteBuffer idx = config.getEntry("loc.idx").getBuffer();
|
||||
|
||||
int count = idx.getShort(), index = 2;
|
||||
int[] indices = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
indices[i] = index;
|
||||
index += idx.getShort();
|
||||
}
|
||||
|
||||
ObjectDefinition[] defs = new ObjectDefinition[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
data.position(indices[i]);
|
||||
defs[i] = decode(i, data);
|
||||
}
|
||||
return defs;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -36,14 +36,14 @@ public final class NpcDefinition {
|
||||
* Initialises the class with the specified set of definitions.
|
||||
*
|
||||
* @param definitions The definitions.
|
||||
* @throws RuntimeException If there is an id mismatch.
|
||||
* @throws IllegalStateException If there is an id mismatch.
|
||||
*/
|
||||
public static void init(NpcDefinition[] definitions) {
|
||||
NpcDefinition.definitions = definitions;
|
||||
for (int id = 0; id < definitions.length; id++) {
|
||||
NpcDefinition def = definitions[id];
|
||||
if (def.getId() != id) {
|
||||
throw new RuntimeException("Npc definition id mismatch.");
|
||||
throw new IllegalStateException("Npc definition id mismatch.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+352
-622
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
package org.apollo.game.fs.decoder;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -11,6 +12,7 @@ import java.util.function.Predicate;
|
||||
import org.apollo.cache.IndexedFileSystem;
|
||||
import org.apollo.cache.decoder.MapFileDecoder;
|
||||
import org.apollo.cache.decoder.MapFileDecoder.MapDefinition;
|
||||
import org.apollo.cache.decoder.ObjectDefinitionDecoder;
|
||||
import org.apollo.cache.def.ObjectDefinition;
|
||||
import org.apollo.game.model.Position;
|
||||
import org.apollo.game.model.World;
|
||||
@@ -31,7 +33,7 @@ import com.google.common.collect.Iterables;
|
||||
* @author Ryley
|
||||
* @author Major
|
||||
*/
|
||||
public final class GameObjectDecoder {
|
||||
public final class GameObjectDecoder implements Runnable {
|
||||
|
||||
/**
|
||||
* A bit flag that indicates that the tile at the current Position is blocked.
|
||||
@@ -58,44 +60,51 @@ public final class GameObjectDecoder {
|
||||
*/
|
||||
private final RegionRepository regions;
|
||||
|
||||
/**
|
||||
* The World to place the objects in.
|
||||
*/
|
||||
private final World world;
|
||||
|
||||
/**
|
||||
* Creates the GameObjectDecoder.
|
||||
*
|
||||
* @param fs The {@link IndexedFileSystem}.
|
||||
* @param regions The {@link RegionRepository}.
|
||||
* @param world The {@link World} to place the objects in.
|
||||
*/
|
||||
public GameObjectDecoder(IndexedFileSystem fs, RegionRepository regions) {
|
||||
public GameObjectDecoder(IndexedFileSystem fs, World world) {
|
||||
this.fs = fs;
|
||||
this.regions = regions;
|
||||
this.world = world;
|
||||
regions = world.getRegionRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the GameObjects from their MapDefinitions.
|
||||
*
|
||||
* @param world The {@link World} containing the StaticGameObjects.
|
||||
* @return The decoded objects.
|
||||
* @throws IOException If there is an error decoding the {@link MapDefinition}s.
|
||||
*/
|
||||
public GameObject[] decode(World world) throws IOException {
|
||||
Map<Integer, MapDefinition> definitions = MapFileDecoder.decode(fs);
|
||||
@Override
|
||||
public void run() {
|
||||
ObjectDefinitionDecoder decoder = new ObjectDefinitionDecoder(fs);
|
||||
decoder.run();
|
||||
|
||||
for (Entry<Integer, MapDefinition> entry : definitions.entrySet()) {
|
||||
MapDefinition definition = entry.getValue();
|
||||
try {
|
||||
Map<Integer, MapDefinition> definitions = MapFileDecoder.decode(fs);
|
||||
|
||||
int packed = definition.getPackedCoordinates();
|
||||
int x = (packed >> 8 & 0xFF) * 64;
|
||||
int y = (packed & 0xFF) * 64;
|
||||
for (Entry<Integer, MapDefinition> entry : definitions.entrySet()) {
|
||||
MapDefinition definition = entry.getValue();
|
||||
|
||||
ByteBuffer objects = fs.getFile(4, definition.getObjectFile());
|
||||
ByteBuffer decompressed = ByteBuffer.wrap(CompressionUtil.degzip(objects));
|
||||
decodeObjects(world, decompressed, x, y);
|
||||
int packed = definition.getPackedCoordinates();
|
||||
int x = (packed >> 8 & 0xFF) * 64;
|
||||
int y = (packed & 0xFF) * 64;
|
||||
|
||||
ByteBuffer terrain = fs.getFile(4, definition.getTerrainFile());
|
||||
decompressed = ByteBuffer.wrap(CompressionUtil.degzip(terrain));
|
||||
decodeTerrain(decompressed, x, y);
|
||||
ByteBuffer objects = fs.getFile(4, definition.getObjectFile());
|
||||
ByteBuffer decompressed = ByteBuffer.wrap(CompressionUtil.degzip(objects));
|
||||
decodeObjects(world, decompressed, x, y);
|
||||
|
||||
ByteBuffer terrain = fs.getFile(4, definition.getTerrainFile());
|
||||
decompressed = ByteBuffer.wrap(CompressionUtil.degzip(terrain));
|
||||
decodeTerrain(decompressed, x, y);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error decoding StaticGameObjects.", e);
|
||||
}
|
||||
|
||||
return Iterables.toArray(objects, GameObject.class);
|
||||
objects.forEach(object -> regions.fromPosition(object.getPosition()).addEntity(object, false));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,24 +121,21 @@ public final class GameObjectDecoder {
|
||||
int x = position.getX(), y = position.getY(), height = position.getHeight();
|
||||
|
||||
CollisionMatrix matrix = region.getMatrix(height);
|
||||
|
||||
boolean block = false;
|
||||
|
||||
if (type == ObjectType.FLOOR_DECORATION.getValue() && definition.isInteractive()) {
|
||||
block = true;
|
||||
}
|
||||
|
||||
Predicate<Integer> walls = (value) -> value >= ObjectType.LENGTHWISE_WALL.getValue()
|
||||
// TODO figure out the other ObjectTypes and get rid of all the getValue() calls
|
||||
|
||||
Predicate<Integer> walls = value -> value >= ObjectType.LENGTHWISE_WALL.getValue()
|
||||
&& value <= ObjectType.RECTANGULAR_CORNER.getValue() || value == ObjectType.DIAGONAL_WALL.getValue();
|
||||
|
||||
Predicate<Integer> roofs = (value) -> value > ObjectType.DIAGONAL_INTERACTABLE.getValue()
|
||||
Predicate<Integer> roofs = value -> value > ObjectType.DIAGONAL_INTERACTABLE.getValue()
|
||||
&& value < ObjectType.FLOOR_DECORATION.getValue();
|
||||
|
||||
if (walls.test(type) || roofs.test(type)) {
|
||||
block = true;
|
||||
}
|
||||
|
||||
if (type == 10 && definition.isSolid()) {
|
||||
if (walls.test(type) || roofs.test(type) || type == ObjectType.INTERACTABLE.getValue() && definition.isSolid()) {
|
||||
block = true;
|
||||
}
|
||||
|
||||
@@ -144,11 +150,13 @@ public final class GameObjectDecoder {
|
||||
Position nextPosition = new Position(nextLocalX, nextLocalY);
|
||||
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;
|
||||
int nextY = nextPosition.getY() % Region.SIZE + dy;
|
||||
|
||||
if (nextX > 7) {
|
||||
nextX -= 7;
|
||||
}
|
||||
|
||||
if (nextY > 7) {
|
||||
nextY -= 7;
|
||||
}
|
||||
@@ -173,12 +181,11 @@ public final class GameObjectDecoder {
|
||||
Region region = regions.fromPosition(position);
|
||||
int x = position.getX(), y = position.getY(), height = position.getHeight();
|
||||
|
||||
CollisionMatrix current = region.getMatrix(height);
|
||||
|
||||
boolean block = false;
|
||||
if ((attributes & BLOCKED_TILE) != 0) {
|
||||
block = true;
|
||||
}
|
||||
|
||||
if ((attributes & BRIDGE_TILE) != 0) {
|
||||
if (height > 0) {
|
||||
block = true;
|
||||
@@ -188,7 +195,7 @@ public final class GameObjectDecoder {
|
||||
|
||||
if (block) {
|
||||
int localX = x % Region.SIZE, localY = y % Region.SIZE;
|
||||
current.block(localX, localY);
|
||||
region.getMatrix(height).block(localX, localY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,6 +256,7 @@ public final class GameObjectDecoder {
|
||||
int attributes = 0;
|
||||
while (true) {
|
||||
int attributeId = buffer.get() & 0xFF;
|
||||
|
||||
if (attributeId == 0) {
|
||||
decodeAttributes(attributes, position);
|
||||
break;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package org.apollo.game.fs.decoder;
|
||||
|
||||
import org.apollo.util.ThreadUtil;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A composite decoder that executes each child in parallel.
|
||||
*
|
||||
* @author Major
|
||||
*/
|
||||
public final class SynchronousDecoder {
|
||||
|
||||
/**
|
||||
* The time to wait before cancelling the decoding.
|
||||
*/
|
||||
private static final int TIMEOUT = 15_000;
|
||||
|
||||
/**
|
||||
* The Executor used to execute the Runnable(s).
|
||||
*/
|
||||
private final ExecutorService executor = Executors.newFixedThreadPool(ThreadUtil.AVAILABLE_PROCESSORS,
|
||||
ThreadUtil.create("SynchronousDecoder"));
|
||||
|
||||
/**
|
||||
* The List of Runnables.
|
||||
*/
|
||||
private final List<Runnable> runnables;
|
||||
|
||||
/**
|
||||
* Creates the SynchronousDecoder.
|
||||
*
|
||||
* @param runnables The {@link Runnable}s to execute.
|
||||
*/
|
||||
public SynchronousDecoder(Runnable... runnables) {
|
||||
this.runnables = Arrays.asList(runnables);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts this SynchronousDecoder.
|
||||
*
|
||||
* @throws InterruptedException If a decoder is still running after {@link #TIMEOUT} ms.
|
||||
*/
|
||||
public void block() throws InterruptedException {
|
||||
runnables.forEach(executor::submit);
|
||||
executor.shutdown();
|
||||
executor.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +1,83 @@
|
||||
package org.apollo.game.io;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
|
||||
import org.apollo.cache.def.EquipmentDefinition;
|
||||
|
||||
/**
|
||||
* A class that parses the {@code data/equipment-[release].dat} file to create an array of {@link EquipmentDefinition}s.
|
||||
* A class that parses the {@code data/equipment-[release].dat} file to create an array of {@link
|
||||
* EquipmentDefinition}s.
|
||||
*
|
||||
* @author Graham
|
||||
*/
|
||||
public final class EquipmentDefinitionParser {
|
||||
public final class EquipmentDefinitionParser implements Runnable {
|
||||
|
||||
/**
|
||||
* The input stream.
|
||||
* Creates an {@link EquipmentDefinitionParser} that reads from the file located at the specified {@code path}.
|
||||
*
|
||||
* @param path The path to the file.
|
||||
* @return The EquipmentDefinitionParser.
|
||||
* @throws UncheckedIOException If there is an error creating the {@link FileInputStream}.
|
||||
*/
|
||||
public static EquipmentDefinitionParser fromFile(String path) {
|
||||
try {
|
||||
return new EquipmentDefinitionParser(new BufferedInputStream(new FileInputStream(path)));
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error creating EquipmentDefinitionParser.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The InputStream.
|
||||
*/
|
||||
private final InputStream is;
|
||||
|
||||
/**
|
||||
* Creates the equipment definition parser.
|
||||
* Creates the EquipmentDefinitionParser.
|
||||
*
|
||||
* @param is The input stream.
|
||||
* @param is The {@link InputStream}.
|
||||
*/
|
||||
public EquipmentDefinitionParser(InputStream is) {
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the input stream.
|
||||
*
|
||||
* @return The equipment definition array.
|
||||
* @throws IOException If an I/O error occurs.
|
||||
*/
|
||||
public EquipmentDefinition[] parse() throws IOException {
|
||||
DataInputStream dis = new DataInputStream(is);
|
||||
@Override
|
||||
public void run() {
|
||||
try (DataInputStream in = new DataInputStream(is)) {
|
||||
int count = in.readShort() & 0xFFFF;
|
||||
EquipmentDefinition[] definitions = new EquipmentDefinition[count];
|
||||
|
||||
int count = dis.readShort() & 0xFFFF;
|
||||
EquipmentDefinition[] definitions = new EquipmentDefinition[count];
|
||||
for (int id = 0; id < count; id++) {
|
||||
int slot = in.readByte() & 0xFF;
|
||||
if (slot != 0xFF) {
|
||||
boolean twoHanded = in.readBoolean();
|
||||
boolean fullBody = in.readBoolean();
|
||||
boolean fullHat = in.readBoolean();
|
||||
boolean fullMask = in.readBoolean();
|
||||
int attack = in.readByte() & 0xFF;
|
||||
int strength = in.readByte() & 0xFF;
|
||||
int defence = in.readByte() & 0xFF;
|
||||
int ranged = in.readByte() & 0xFF;
|
||||
int magic = in.readByte() & 0xFF;
|
||||
|
||||
for (int id = 0; id < count; id++) {
|
||||
int slot = dis.readByte() & 0xFF;
|
||||
if (slot != 0xFF) {
|
||||
boolean twoHanded = dis.readBoolean();
|
||||
boolean fullBody = dis.readBoolean();
|
||||
boolean fullHat = dis.readBoolean();
|
||||
boolean fullMask = dis.readBoolean();
|
||||
int attack = dis.readByte() & 0xFF;
|
||||
int strength = dis.readByte() & 0xFF;
|
||||
int defence = dis.readByte() & 0xFF;
|
||||
int ranged = dis.readByte() & 0xFF;
|
||||
int magic = dis.readByte() & 0xFF;
|
||||
EquipmentDefinition definition = new EquipmentDefinition(id);
|
||||
definition.setLevels(attack, strength, defence, ranged, magic);
|
||||
definition.setSlot(slot);
|
||||
definition.setFlags(twoHanded, fullBody, fullHat, fullMask);
|
||||
|
||||
EquipmentDefinition definition = new EquipmentDefinition(id);
|
||||
definition.setLevels(attack, strength, defence, ranged, magic);
|
||||
definition.setSlot(slot);
|
||||
definition.setFlags(twoHanded, fullBody, fullHat, fullMask);
|
||||
|
||||
definitions[id] = definition;
|
||||
definitions[id] = definition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return definitions;
|
||||
EquipmentDefinition.init(definitions);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException("Error parsing EquipmentDefinitions.", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +1,18 @@
|
||||
package org.apollo.game.model;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apollo.Server;
|
||||
import org.apollo.Service;
|
||||
import org.apollo.cache.IndexedFileSystem;
|
||||
import org.apollo.cache.decoder.ItemDefinitionDecoder;
|
||||
import org.apollo.cache.decoder.NpcDefinitionDecoder;
|
||||
import org.apollo.cache.decoder.ObjectDefinitionDecoder;
|
||||
import org.apollo.cache.def.EquipmentDefinition;
|
||||
import org.apollo.cache.def.ItemDefinition;
|
||||
import org.apollo.cache.def.NpcDefinition;
|
||||
import org.apollo.cache.def.ObjectDefinition;
|
||||
import org.apollo.game.command.CommandDispatcher;
|
||||
import org.apollo.game.fs.decoder.GameObjectDecoder;
|
||||
import org.apollo.game.fs.decoder.SynchronousDecoder;
|
||||
import org.apollo.game.io.EquipmentDefinitionParser;
|
||||
import org.apollo.game.model.area.Region;
|
||||
import org.apollo.game.model.area.RegionRepository;
|
||||
@@ -27,7 +21,6 @@ import org.apollo.game.model.entity.EntityType;
|
||||
import org.apollo.game.model.entity.MobRepository;
|
||||
import org.apollo.game.model.entity.Npc;
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.game.model.entity.obj.GameObject;
|
||||
import org.apollo.game.model.event.Event;
|
||||
import org.apollo.game.model.event.EventListener;
|
||||
import org.apollo.game.model.event.EventListenerChainSet;
|
||||
@@ -87,11 +80,6 @@ public final class World {
|
||||
*/
|
||||
private final EventListenerChainSet events = new EventListenerChainSet();
|
||||
|
||||
/**
|
||||
* The ScheduledTask that moves Npcs.
|
||||
*/
|
||||
private NpcMovementTask npcMovement;
|
||||
|
||||
/**
|
||||
* The {@link MobRepository} of {@link Npc}s.
|
||||
*/
|
||||
@@ -107,32 +95,30 @@ public final class World {
|
||||
*/
|
||||
private final Map<Long, Player> players = new HashMap<>();
|
||||
|
||||
/**
|
||||
* The {@link PluginManager}.
|
||||
*/
|
||||
private PluginManager pluginManager;
|
||||
|
||||
/**
|
||||
* This world's {@link RegionRepository}.
|
||||
*/
|
||||
private final RegionRepository regions = RegionRepository.immutable();
|
||||
|
||||
/**
|
||||
* The release number (i.e. version) of this world.
|
||||
*/
|
||||
private int releaseNumber;
|
||||
|
||||
/**
|
||||
* The scheduler.
|
||||
*/
|
||||
private final Scheduler scheduler = new Scheduler();
|
||||
|
||||
/**
|
||||
* Creates the world.
|
||||
* The ScheduledTask that moves Npcs.
|
||||
*/
|
||||
public World() {
|
||||
private NpcMovementTask npcMovement;
|
||||
|
||||
}
|
||||
/**
|
||||
* The {@link PluginManager}.
|
||||
*/
|
||||
private PluginManager pluginManager;
|
||||
|
||||
/**
|
||||
* The release number (i.e. version) of this world.
|
||||
*/
|
||||
private int releaseNumber;
|
||||
|
||||
/**
|
||||
* Gets the command dispatcher.
|
||||
@@ -210,32 +196,11 @@ public final class World {
|
||||
public void init(int release, IndexedFileSystem fs, PluginManager manager) throws Exception {
|
||||
releaseNumber = release;
|
||||
|
||||
ItemDefinitionDecoder itemDecoder = new ItemDefinitionDecoder(fs);
|
||||
ItemDefinition[] items = itemDecoder.decode();
|
||||
ItemDefinition.init(items);
|
||||
logger.fine("Loaded " + items.length + " item definitions.");
|
||||
SynchronousDecoder decoder = new SynchronousDecoder(new ItemDefinitionDecoder(fs),
|
||||
new NpcDefinitionDecoder(fs), new GameObjectDecoder(fs, this),
|
||||
EquipmentDefinitionParser.fromFile("data/equipment-" + release + "" + ".dat"));
|
||||
|
||||
try (InputStream is = new BufferedInputStream(new FileInputStream("data/equipment-" + release + ".dat"))) {
|
||||
EquipmentDefinitionParser parser = new EquipmentDefinitionParser(is);
|
||||
EquipmentDefinition[] defs = parser.parse();
|
||||
EquipmentDefinition.init(defs);
|
||||
logger.fine("Loaded " + defs.length + " equipment definitions.");
|
||||
}
|
||||
|
||||
NpcDefinitionDecoder npcDecoder = new NpcDefinitionDecoder(fs);
|
||||
NpcDefinition[] npcs = npcDecoder.decode();
|
||||
NpcDefinition.init(npcs);
|
||||
logger.fine("Loaded " + npcs.length + " npc definitions.");
|
||||
|
||||
ObjectDefinitionDecoder objectDecoder = new ObjectDefinitionDecoder(fs);
|
||||
ObjectDefinition[] objectDefs = objectDecoder.decode();
|
||||
ObjectDefinition.init(objectDefs);
|
||||
logger.fine("Loaded " + objectDefs.length + " object definitions.");
|
||||
|
||||
GameObjectDecoder staticDecoder = new GameObjectDecoder(fs, regions);
|
||||
GameObject[] objects = staticDecoder.decode(this);
|
||||
placeEntities(objects);
|
||||
logger.fine("Loaded " + objects.length + " static objects.");
|
||||
decoder.block();
|
||||
|
||||
npcMovement = new NpcMovementTask(regions); // Must be exactly here because of ordering issues.
|
||||
scheduler.schedule(npcMovement);
|
||||
@@ -291,6 +256,7 @@ public final class World {
|
||||
} else {
|
||||
logger.warning("Failed to register npc, repository capacity reached: [count=" + npcRepository.size() + "]");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@@ -384,13 +350,4 @@ public final class World {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds entities to regions in the {@link RegionRepository}. By default, we do not notify listeners.
|
||||
*
|
||||
* @param entities The entities.
|
||||
*/
|
||||
private void placeEntities(Entity... entities) {
|
||||
Arrays.stream(entities).forEach(entity -> regions.fromPosition(entity.getPosition()).addEntity(entity, false));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,8 +21,13 @@ import org.apollo.game.sync.seg.SynchronizationSegment;
|
||||
public final class NpcSynchronizationTask extends SynchronizationTask {
|
||||
|
||||
/**
|
||||
* The maximum number of npcs to load per cycle. This prevents the update packet from becoming too large (the client
|
||||
* uses a 5000 byte buffer) and also stops old spec PCs from crashing when they login or teleport.
|
||||
* The maximum amount of local npcs.
|
||||
*/
|
||||
private static final int MAXIMUM_LOCAL_NPCS = 255;
|
||||
|
||||
/**
|
||||
* The maximum number of npcs to load per cycle. This prevents the update packet from becoming too large (the
|
||||
* client uses a 5000 byte buffer) and also stops old spec PCs from crashing when they login or teleport.
|
||||
*/
|
||||
private static final int NEW_NPCS_PER_CYCLE = 20;
|
||||
|
||||
@@ -42,41 +47,49 @@ public final class NpcSynchronizationTask extends SynchronizationTask {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<Npc> localNpcs = player.getLocalNpcList();
|
||||
List<Npc> locals = player.getLocalNpcList();
|
||||
List<SynchronizationSegment> segments = new ArrayList<>();
|
||||
int oldLocalNpcs = localNpcs.size();
|
||||
|
||||
int originalCount = locals.size();
|
||||
final Position playerPosition = player.getPosition();
|
||||
|
||||
for (Iterator<Npc> it = localNpcs.iterator(); it.hasNext();) {
|
||||
Npc npc = it.next();
|
||||
if (!npc.isActive() || npc.isTeleporting() || npc.getPosition().getLongestDelta(playerPosition) > player.getViewingDistance() || !npc.getPosition().isWithinDistance(playerPosition, player.getViewingDistance())) {
|
||||
it.remove();
|
||||
int distance = player.getViewingDistance();
|
||||
for (Iterator<Npc> iterator = locals.iterator(); iterator.hasNext(); ) {
|
||||
Npc npc = iterator.next();
|
||||
Position position = npc.getPosition();
|
||||
|
||||
if (!npc.isActive() || npc.isTeleporting() || position.getLongestDelta(playerPosition) > distance
|
||||
|| !position.isWithinDistance(playerPosition, distance)) {
|
||||
iterator.remove();
|
||||
|
||||
segments.add(new RemoveMobSegment());
|
||||
} else {
|
||||
segments.add(new MovementSegment(npc.getBlockSet(), npc.getDirections()));
|
||||
}
|
||||
}
|
||||
|
||||
int added = 0;
|
||||
int added = 0, count = locals.size();
|
||||
|
||||
for (Npc npc : player.getWorld().getNpcRepository()) {
|
||||
if (localNpcs.size() >= 255) {
|
||||
if (count >= MAXIMUM_LOCAL_NPCS) {
|
||||
player.flagExcessiveNpcs();
|
||||
break;
|
||||
} else if (added >= NEW_NPCS_PER_CYCLE) {
|
||||
break;
|
||||
}
|
||||
|
||||
Position npcPosition = npc.getPosition();
|
||||
if (npcPosition.isWithinDistance(playerPosition, player.getViewingDistance()) && !localNpcs.contains(npc)) {
|
||||
localNpcs.add(npc);
|
||||
Position position = npc.getPosition();
|
||||
if (position.isWithinDistance(playerPosition, distance) && !locals.contains(npc)) {
|
||||
locals.add(npc);
|
||||
count++;
|
||||
added++;
|
||||
|
||||
npc.turnTo(npc.getFacingPosition());
|
||||
segments.add(new AddNpcSegment(npc.getBlockSet(), npc.getIndex(), npcPosition, npc.getId()));
|
||||
segments.add(new AddNpcSegment(npc.getBlockSet(), npc.getIndex(), position, npc.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
player.send(new NpcSynchronizationMessage(playerPosition, segments, oldLocalNpcs));
|
||||
player.send(new NpcSynchronizationMessage(playerPosition, segments, originalCount));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,6 @@ import java.util.List;
|
||||
|
||||
import org.apollo.game.message.impl.PlayerSynchronizationMessage;
|
||||
import org.apollo.game.model.Position;
|
||||
import org.apollo.game.model.entity.MobRepository;
|
||||
import org.apollo.game.model.entity.Player;
|
||||
import org.apollo.game.sync.block.AppearanceBlock;
|
||||
import org.apollo.game.sync.block.ChatBlock;
|
||||
@@ -25,6 +24,11 @@ import org.apollo.game.sync.seg.TeleportSegment;
|
||||
*/
|
||||
public final class PlayerSynchronizationTask extends SynchronizationTask {
|
||||
|
||||
/**
|
||||
* The maximum amount of local players.
|
||||
*/
|
||||
private static final int MAXIMUM_LOCAL_PLAYERS = 255;
|
||||
|
||||
/**
|
||||
* The maximum number of players to load per cycle. This prevents the update packet from becoming too large (the
|
||||
* client uses a 5000 byte buffer) and also stops old spec PCs from crashing when they login or teleport.
|
||||
@@ -32,14 +36,14 @@ public final class PlayerSynchronizationTask extends SynchronizationTask {
|
||||
private static final int NEW_PLAYERS_PER_CYCLE = 20;
|
||||
|
||||
/**
|
||||
* The player.
|
||||
* The Player.
|
||||
*/
|
||||
private final Player player;
|
||||
|
||||
/**
|
||||
* Creates the {@link PlayerSynchronizationTask} for the specified player.
|
||||
* Creates the {@link PlayerSynchronizationTask} for the specified {@link Player}.
|
||||
*
|
||||
* @param player The player.
|
||||
* @param player The Player.
|
||||
*/
|
||||
public PlayerSynchronizationTask(Player player) {
|
||||
this.player = player;
|
||||
@@ -50,80 +54,80 @@ public final class PlayerSynchronizationTask extends SynchronizationTask {
|
||||
Position lastKnownRegion = player.getLastKnownRegion();
|
||||
boolean regionChanged = player.hasRegionChanged();
|
||||
|
||||
SynchronizationSegment segment;
|
||||
SynchronizationBlockSet blockSet = player.getBlockSet();
|
||||
|
||||
if (blockSet.contains(ChatBlock.class)) {
|
||||
blockSet = blockSet.clone();
|
||||
blockSet.remove(ChatBlock.class);
|
||||
}
|
||||
|
||||
if (player.isTeleporting() || player.hasRegionChanged()) {
|
||||
segment = new TeleportSegment(blockSet, player.getPosition());
|
||||
} else {
|
||||
segment = new MovementSegment(blockSet, player.getDirections());
|
||||
}
|
||||
Position position = player.getPosition();
|
||||
|
||||
SynchronizationSegment segment = (player.isTeleporting() || player.hasRegionChanged()) ?
|
||||
new TeleportSegment(blockSet, position) : new MovementSegment(blockSet, player.getDirections());
|
||||
|
||||
List<Player> localPlayers = player.getLocalPlayerList();
|
||||
int oldLocalPlayers = localPlayers.size();
|
||||
int oldCount = localPlayers.size();
|
||||
|
||||
List<SynchronizationSegment> segments = new ArrayList<>();
|
||||
int distance = player.getViewingDistance();
|
||||
|
||||
for (Iterator<Player> it = localPlayers.iterator(); it.hasNext(); ) {
|
||||
Player other = it.next();
|
||||
for (Iterator<Player> iterator = localPlayers.iterator(); iterator.hasNext(); ) {
|
||||
Player other = iterator.next();
|
||||
|
||||
if (removePlayer(other)) {
|
||||
it.remove();
|
||||
if (removeable(position, distance, other)) {
|
||||
iterator.remove();
|
||||
segments.add(new RemoveMobSegment());
|
||||
} else {
|
||||
segments.add(new MovementSegment(other.getBlockSet(), other.getDirections()));
|
||||
}
|
||||
}
|
||||
|
||||
int added = 0;
|
||||
int added = 0, count = localPlayers.size();
|
||||
|
||||
MobRepository<Player> repository = player.getWorld().getPlayerRepository();
|
||||
for (Player other : repository) {
|
||||
if (localPlayers.size() >= 255) {
|
||||
for (Player other : player.getWorld().getPlayerRepository()) {
|
||||
if (count >= MAXIMUM_LOCAL_PLAYERS) {
|
||||
player.flagExcessivePlayers();
|
||||
break;
|
||||
} else if (added >= NEW_PLAYERS_PER_CYCLE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (other != player && other.getPosition().isWithinDistance(player.getPosition(), player.getViewingDistance()) && !localPlayers.contains(other)) {
|
||||
Position local = other.getPosition();
|
||||
|
||||
if (other != player && local.isWithinDistance(position, distance) && !localPlayers.contains(other)) {
|
||||
localPlayers.add(other);
|
||||
count++;
|
||||
added++;
|
||||
|
||||
blockSet = other.getBlockSet();
|
||||
if (!blockSet.contains(AppearanceBlock.class)) {
|
||||
// TODO check if client has cached appearance
|
||||
if (!blockSet.contains(AppearanceBlock.class)) { // TODO check if client has cached appearance
|
||||
blockSet = blockSet.clone();
|
||||
blockSet.add(SynchronizationBlock.createAppearanceBlock(other));
|
||||
}
|
||||
|
||||
segments.add(new AddPlayerSegment(blockSet, other.getIndex(), other.getPosition()));
|
||||
segments.add(new AddPlayerSegment(blockSet, other.getIndex(), local));
|
||||
}
|
||||
}
|
||||
|
||||
PlayerSynchronizationMessage message = new PlayerSynchronizationMessage(lastKnownRegion, player.getPosition(),
|
||||
regionChanged, segment, oldLocalPlayers, segments);
|
||||
PlayerSynchronizationMessage message = new PlayerSynchronizationMessage(lastKnownRegion, position,
|
||||
regionChanged, segment, oldCount, segments);
|
||||
player.send(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the specified {@link Player} should be removed.
|
||||
*
|
||||
* @param position The {@link Position} of the Player being updated.
|
||||
* @param other The Player being tested.
|
||||
* @return {@code true} iff the specified Player should be removed.
|
||||
*/
|
||||
private boolean removePlayer(Player other) {
|
||||
private boolean removeable(Position position, int distance, Player other) {
|
||||
if (other.isTeleporting() || !other.isActive()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Position position = player.getPosition();
|
||||
Position otherPosition = other.getPosition();
|
||||
int distance = player.getViewingDistance();
|
||||
|
||||
return otherPosition.getLongestDelta(position) > distance || !otherPosition.isWithinDistance(position, distance);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ public final class PostPlayerSynchronizationTask extends SynchronizationTask {
|
||||
player.setTeleporting(false);
|
||||
player.setRegionChanged(false);
|
||||
player.resetBlockSet();
|
||||
|
||||
if (!player.isExcessivePlayersSet()) {
|
||||
player.incrementViewingDistance();
|
||||
} else {
|
||||
|
||||
@@ -29,6 +29,7 @@ public final class CompressionUtil {
|
||||
*/
|
||||
public static byte[] bzip2(byte[] uncompressed) throws IOException {
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
|
||||
try (BZip2CompressorOutputStream os = new BZip2CompressorOutputStream(bout, 1)) {
|
||||
os.write(uncompressed);
|
||||
os.finish();
|
||||
@@ -40,22 +41,6 @@ public final class CompressionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the specified array.
|
||||
*
|
||||
* @param uncompressed The uncompressed array.
|
||||
* @return The compressed array.
|
||||
* @throws IOException If there is an error compressing the array.
|
||||
*/
|
||||
public static byte[] gzip(byte[] uncompressed) throws IOException {
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
try (DeflaterOutputStream os = new GZIPOutputStream(bout)) {
|
||||
os.write(uncompressed);
|
||||
os.finish();
|
||||
return bout.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debzip2s the compressed array and places the result into the decompressed array.
|
||||
*
|
||||
@@ -90,27 +75,44 @@ public final class CompressionUtil {
|
||||
}
|
||||
|
||||
/**
|
||||
* Degzips the compressed {@link ByteBuffer} and returns the result as a byte array.
|
||||
* Degzips <strong>all</strong> of the datain the specified {@link ByteBuffer}.
|
||||
*
|
||||
* @param compressed The compressed buffer.
|
||||
* @return The decompressed array.
|
||||
* @throws IOException If there is an error decompressing the buffer.
|
||||
*/
|
||||
public static byte[] degzip(ByteBuffer compressed) throws IOException {
|
||||
byte[] data = new byte[compressed.remaining()];
|
||||
compressed.get(data);
|
||||
try (InputStream is = new GZIPInputStream(new ByteArrayInputStream(compressed.array()));
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
byte[] buffer = new byte[1024];
|
||||
|
||||
try (InputStream is = new GZIPInputStream(new ByteArrayInputStream(data)); ByteArrayOutputStream os = new ByteArrayOutputStream()) {
|
||||
while (true) {
|
||||
byte[] buf = new byte[1024];
|
||||
int read = is.read(buf, 0, buf.length);
|
||||
int read = is.read(buffer, 0, buffer.length);
|
||||
if (read == -1) {
|
||||
break;
|
||||
}
|
||||
os.write(buf, 0, read);
|
||||
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
return os.toByteArray();
|
||||
return out.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gzips the specified array.
|
||||
*
|
||||
* @param uncompressed The uncompressed array.
|
||||
* @return The compressed array.
|
||||
* @throws IOException If there is an error compressing the array.
|
||||
*/
|
||||
public static byte[] gzip(byte[] uncompressed) throws IOException {
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
|
||||
try (DeflaterOutputStream os = new GZIPOutputStream(bout)) {
|
||||
os.write(uncompressed);
|
||||
os.finish();
|
||||
return bout.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,20 +27,20 @@ public final class ThreadUtil {
|
||||
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 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);
|
||||
private static final UncaughtExceptionHandler DEFAULT_EXCEPTION_HANDLER =
|
||||
(thread, exception) -> LOGGER.log(Level.SEVERE, "Exception in thread " + thread.getName(), exception);
|
||||
|
||||
/**
|
||||
* Builds a {@link ThreadFactory} using the specified {@code String} name-format, normal thread priority and the
|
||||
* default {@link UncaughtExceptionHandler}.
|
||||
*
|
||||
* @see #DEFAULT_EXCEPTION_HANDLER
|
||||
*
|
||||
* @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}.
|
||||
* @param name The name-format used when creating threads. Must not be {@code null}.
|
||||
* @return The {@link ThreadFactory}. Will never be {@code null}.
|
||||
* @see #DEFAULT_EXCEPTION_HANDLER
|
||||
*/
|
||||
public static ThreadFactory create(String name) {
|
||||
return create(name, Thread.NORM_PRIORITY, DEFAULT_EXCEPTION_HANDLER);
|
||||
@@ -50,9 +50,9 @@ public final class ThreadUtil {
|
||||
* Builds a {@link ThreadFactory} using the specified {@code String} name-format, priority and the
|
||||
* {@link #DEFAULT_EXCEPTION_HANDLER}.
|
||||
*
|
||||
* @param name The name-format used when creating threads, may not be {@code null}.
|
||||
* @param name The name-format used when creating threads. Must not be {@code null}.
|
||||
* @param priority The priority used when creating threads.
|
||||
* @return A new {@link ThreadFactory} from the specified parameters, never {@code null}.
|
||||
* @return The {@link ThreadFactory}. Will never be {@code null}.
|
||||
*/
|
||||
public static ThreadFactory create(String name, int priority) {
|
||||
return create(name, priority, DEFAULT_EXCEPTION_HANDLER);
|
||||
@@ -65,10 +65,11 @@ public final class ThreadUtil {
|
||||
* @param name The name-format used when creating threads. Must not be {@code null}.
|
||||
* @param priority The priority used when creating threads.
|
||||
* @param handler The {@link UncaughtExceptionHandler} used when creating threads. Must not be {@code null}.
|
||||
* @return A new {@link ThreadFactory} using the specified parameters.
|
||||
* @return The {@link ThreadFactory}. Will never be {@code null}.
|
||||
*/
|
||||
public static ThreadFactory create(String name, int priority, UncaughtExceptionHandler handler) {
|
||||
Objects.requireNonNull(priority);
|
||||
Objects.requireNonNull(name, "ThreadFactory name must not be null.");
|
||||
Objects.requireNonNull(handler, "UncaughtExceptionHandler must not be null.");
|
||||
|
||||
ThreadFactoryBuilder builder = new ThreadFactoryBuilder();
|
||||
builder.setNameFormat(name);
|
||||
|
||||
Reference in New Issue
Block a user