package mhfc.net.common.eventhandler;
import java.util.EnumMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.function.operation.Operation;
import com.sk89q.worldedit.function.operation.RunContext;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.common.gameevent.TickEvent;
import mhfc.net.MHFCMain;
import mhfc.net.common.network.NetworkTracker;
import mhfc.net.common.util.services.IServiceAccess;
import mhfc.net.common.util.services.IServiceHandle;
import mhfc.net.common.util.services.IServiceKey;
import mhfc.net.common.util.services.IServicePhaseHandle;
import mhfc.net.common.util.services.Services;
public class MHFCTickHandler {
public static final MHFCTickHandler instance = new MHFCTickHandler();
public static void staticInit() {}
private static class OperationWrapper implements Runnable {
private Operation op;
private RunContext context;
private DoubleBufferRunnableRegistry registry;
private Runnable cancel;
public OperationWrapper(Operation op, DoubleBufferRunnableRegistry registry) {
this.op = Objects.requireNonNull(op);
this.context = new RunContext();
this.registry = Objects.requireNonNull(registry);
this.cancel = this::cancelOp;
}
protected void cancelOp() {
if (op != null) {
this.op.cancel();
}
}
@Override
public void run() {
try {
this.op = op.resume(context);
} catch (WorldEditException e) {
MHFCMain.logger().error("Exception when handling an operation", e);
return;
}
if (this.op != null) {
registry.register(this, cancel);
}
}
}
private static Map<TickPhase, IServiceKey<DoubleBufferRunnableRegistry>> jobQueue = new EnumMap<>(TickPhase.class);
static {
for (TickPhase phase : TickPhase.values()) {
boolean isServerTick = phase == TickPhase.SERVER_POST || phase == TickPhase.SERVER_PRE;
boolean isClientTick = phase == TickPhase.CLIENT_POST || phase == TickPhase.CLIENT_PRE;
IServiceAccess<DoubleBufferRunnableRegistry> phaseHandler = Services.instance
.registerService("tick " + phase, new IServiceHandle<DoubleBufferRunnableRegistry>() {
@Override
public void startup(DoubleBufferRunnableRegistry instance) {}
@Override
public void shutdown(DoubleBufferRunnableRegistry instance) {
instance.cancel();
}
}, DoubleBufferRunnableRegistry::new);
if (isServerTick) {
phaseHandler.addTo(MHFCMain.serverActivePhase, IServicePhaseHandle.noInit());
} else if (isClientTick) {
phaseHandler.addTo(NetworkTracker.clientConnectedPhase, IServicePhaseHandle.noInit());
} else {
phaseHandler.addTo(MHFCMain.preInitPhase, IServicePhaseHandle.noInit());
}
jobQueue.put(phase, phaseHandler);
}
}
private DoubleBufferRunnableRegistry getQueueFor(TickPhase phase) {
assert phase != null;
return jobQueue.get(phase).getService();
}
public void registerRunnable(TickPhase phase, Runnable run, Runnable cancel) {
Objects.requireNonNull(phase);
getQueueFor(phase).register(run, cancel);
}
public void registerOperation(TickPhase phase, final Operation op) {
registerOperation(phase, op, new RunContext());
}
/**
* Registers an operation to get called every time the {@link TickPhase} phase triggers. Then
* <code>op.resume(context)</code> will get called with the context given. If the result is not <code>null</code>,
* the returned {@link Operation} will get registered for the same phase and with the same context.
*
* @param phase
* the phase to tick in, non-null
* @param op
* the operation to call, <code>null</code> will be ignored.
* @param context
* the context to feed to the operation
*/
public void registerOperation(TickPhase phase, final Operation op, final RunContext context) {
Objects.requireNonNull(phase);
if (op == null) {
return;
}
DoubleBufferRunnableRegistry queue = getQueueFor(phase);
OperationWrapper runnable = new OperationWrapper(op, queue);
queue.register(runnable, runnable.cancel);
}
public Executor poolFor(final TickPhase phase) {
return command -> registerRunnable(phase, command, null);
}
@SubscribeEvent
public void onRenderTick(TickEvent.RenderTickEvent event) {
onTick(event);
}
@SubscribeEvent
public void onClientTick(TickEvent.ClientTickEvent event) {
onTick(event);
}
@SubscribeEvent
public void onServerTick(TickEvent.ServerTickEvent event) {
onTick(event);
}
@SubscribeEvent
public void onPlayerTick(TickEvent.PlayerTickEvent event) {
onTick(event);
}
private void onTick(TickEvent event) {
TickPhase phase = TickPhase.forEvent(event);
IServiceKey<DoubleBufferRunnableRegistry> key = jobQueue.get(phase);
key.getServiceProvider().getServiceFor(key).ifPresent(DoubleBufferRunnableRegistry::runAll);
}
}