mirror of
https://github.com/dmulloy2/ProtocolLib.git
synced 2024-11-27 21:26:17 +01:00
Ensure that the structure compiler is thread safe.
This commit is contained in:
parent
e6de9ae705
commit
46d9a6e975
@ -17,12 +17,17 @@
|
||||
|
||||
package com.comphenix.protocol.injector;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
|
||||
import net.minecraft.server.Packet;
|
||||
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
import com.comphenix.protocol.reflect.compiler.BackgroundCompiler;
|
||||
import com.comphenix.protocol.reflect.compiler.CompileListener;
|
||||
import com.comphenix.protocol.reflect.compiler.CompiledStructureModifier;
|
||||
|
||||
/**
|
||||
* Caches structure modifiers.
|
||||
@ -30,7 +35,10 @@ import com.comphenix.protocol.reflect.StructureModifier;
|
||||
*/
|
||||
public class StructureCache {
|
||||
// Structure modifiers
|
||||
private static Map<Integer, StructureModifier<Object>> structureModifiers = new HashMap<Integer, StructureModifier<Object>>();
|
||||
private static ConcurrentMap<Integer, StructureModifier<Object>> structureModifiers =
|
||||
new ConcurrentHashMap<Integer, StructureModifier<Object>>();
|
||||
|
||||
private static Set<Integer> compiling = new HashSet<Integer>();
|
||||
|
||||
/**
|
||||
* Creates an empty Minecraft packet of the given ID.
|
||||
@ -53,15 +61,51 @@ public class StructureCache {
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(int id) {
|
||||
// Compile structures by default
|
||||
return getStructure(id, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a cached structure modifier for the given packet id.
|
||||
* @param id - packet ID.
|
||||
* @param compile - whether or not to asynchronously compile the structure modifier.
|
||||
* @return A structure modifier.
|
||||
*/
|
||||
public static StructureModifier<Object> getStructure(int id, boolean compile) {
|
||||
|
||||
StructureModifier<Object> result = structureModifiers.get(id);
|
||||
|
||||
// Use the vanilla class definition
|
||||
// We don't want to create this for every lookup
|
||||
if (result == null) {
|
||||
result = new StructureModifier<Object>(
|
||||
// Use the vanilla class definition
|
||||
final StructureModifier<Object> value = new StructureModifier<Object>(
|
||||
MinecraftRegistry.getPacketClassFromID(id, true), Packet.class, true);
|
||||
|
||||
structureModifiers.put(id, result);
|
||||
result = structureModifiers.putIfAbsent(id, value);
|
||||
|
||||
// We may end up creating multiple modifiers, but we'll agree on which to use
|
||||
if (result == null) {
|
||||
result = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Automatically compile the structure modifier
|
||||
if (compile && !(result instanceof CompiledStructureModifier)) {
|
||||
// Compilation is many orders of magnitude slower than synchronization
|
||||
synchronized (compiling) {
|
||||
final int idCopy = id;
|
||||
final BackgroundCompiler compiler = BackgroundCompiler.getInstance();
|
||||
|
||||
if (!compiling.contains(id) && compiler != null) {
|
||||
compiler.scheduleCompilation(result, new CompileListener<Object>() {
|
||||
@Override
|
||||
public void onCompiled(StructureModifier<Object> compiledModifier) {
|
||||
structureModifiers.put(idCopy, compiledModifier);
|
||||
}
|
||||
});
|
||||
compiling.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -80,6 +80,27 @@ public class BackgroundCompiler {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void scheduleCompilation(final Map<Class, StructureModifier> cache, final Class key) {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final StructureModifier<Object> uncompiled = cache.get(key);
|
||||
|
||||
if (uncompiled != null) {
|
||||
scheduleCompilation(uncompiled, new CompileListener<Object>() {
|
||||
@Override
|
||||
public void onCompiled(StructureModifier<Object> compiledModifier) {
|
||||
// Update cache
|
||||
cache.put(key, compiledModifier);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the given structure modifier is eventually compiled.
|
||||
* @param uncompiled - structure modifier to compile.
|
||||
* @param listener - listener responsible for responding to the compilation.
|
||||
*/
|
||||
public <TKey> void scheduleCompilation(final StructureModifier<TKey> uncompiled, final CompileListener<TKey> listener) {
|
||||
|
||||
// Only schedule if we're enabled
|
||||
if (enabled && !shuttingDown) {
|
||||
|
||||
@ -92,11 +113,11 @@ public class BackgroundCompiler {
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
|
||||
StructureModifier<?> modifier = cache.get(key);
|
||||
StructureModifier<TKey> modifier = uncompiled;
|
||||
|
||||
// Update the cache!
|
||||
// Do our compilation
|
||||
modifier = compiler.compile(modifier);
|
||||
cache.put(key, modifier);
|
||||
listener.onCompiled(modifier);
|
||||
|
||||
// We'll also return the new structure modifier
|
||||
return modifier;
|
||||
|
@ -0,0 +1,17 @@
|
||||
package com.comphenix.protocol.reflect.compiler;
|
||||
|
||||
import com.comphenix.protocol.reflect.StructureModifier;
|
||||
|
||||
/**
|
||||
* Used to save the result of an compilation.
|
||||
*
|
||||
* @author Kristian
|
||||
* @param <TKey> - type of the structure modifier field.
|
||||
*/
|
||||
public interface CompileListener<TKey> {
|
||||
/**
|
||||
* Invoked when a structure modifier has been successfully compiled.
|
||||
* @param compiledModifier - the compiled structure modifier.
|
||||
*/
|
||||
public void onCompiled(StructureModifier<TKey> compiledModifier);
|
||||
}
|
Loading…
Reference in New Issue
Block a user