package mhfc.net.common.eventhandler; import java.util.Collection; import java.util.HashSet; import java.util.Objects; import java.util.Vector; import java.util.concurrent.BlockingQueue; import java.util.concurrent.PriorityBlockingQueue; import java.util.concurrent.atomic.AtomicLong; import cpw.mods.fml.common.FMLCommonHandler; import cpw.mods.fml.common.eventhandler.SubscribeEvent; import cpw.mods.fml.common.gameevent.TickEvent; import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent; import cpw.mods.fml.common.gameevent.TickEvent.Phase; import cpw.mods.fml.common.gameevent.TickEvent.ServerTickEvent; @Deprecated public class MHFCJobHandler { private static final class JobEntry implements Comparable<JobEntry> { public final long procTime; public final DelayedJob job; public JobEntry(long procTime, DelayedJob job) { this.procTime = procTime; this.job = Objects.requireNonNull(job); } @Override public int compareTo(JobEntry o) { return Long.compare(this.procTime, o.procTime); } @Override public boolean equals(Object obj) { if (obj instanceof DelayedJob) { return job.equals(obj); } if (obj instanceof JobEntry) { JobEntry o = (JobEntry) obj; return this.job.equals(o.job) && this.procTime == o.procTime; } return false; } } public static final int ticksPerSecond = 20; public static final MHFCJobHandler instance = new MHFCJobHandler(); public static MHFCJobHandler instance() { return instance; } private BlockingQueue<JobEntry> jobQueue = new PriorityBlockingQueue<>(20); private Collection<DelayedJob> thisTick = new Vector<DelayedJob>(); private Collection<DelayedJob> listOfRemoves = new HashSet<DelayedJob>(); private volatile AtomicLong now = new AtomicLong(0); // See http://stackoverflow.com/q/11167566/ private Object lock = new Object(); private MHFCJobHandler() {} private void doRemoves() { synchronized (listOfRemoves) { this.thisTick.removeAll(listOfRemoves); this.jobQueue.removeAll(listOfRemoves); listOfRemoves.clear(); } } private void tick(TickEvent tick) { synchronized (this.lock) { doRemoves(); if (tick.phase == Phase.START && !jobQueue.isEmpty()) { thisTick.clear(); long timeNow = now.incrementAndGet(); assert (!(jobQueue.peek().procTime < timeNow)); while (jobQueue.peek().procTime == timeNow) { DelayedJob job = jobQueue.poll().job; thisTick.add(job); job.executeJob(Phase.START); } } else { for (DelayedJob job : thisTick) { job.executeJob(Phase.END); } } } } @SubscribeEvent public void receiveTick(ClientTickEvent tick) { tick(tick); } @SubscribeEvent public void receiveTick(ServerTickEvent tick) { // Only if we really are on a server if (!FMLCommonHandler.instance().getSide().isServer()) { return; } tick(tick); } /** * Insert a new job into the Handler. If the delay is less than or equal to zero, an * {@link IllegalArgumentException} is thrown. If the job is <code>null</code> an {@link NullPointerException} is * thrown. * * @param job * the job to insert * @param delay * the delay after which to execute the job * @return a time-point at which the job is scheduled to be executed, the difference between the received timepoint * and the current time can be polled with {@link #getDelay(DelayedJob)} */ public long insert(DelayedJob job, long delay) { if (delay <= 0) { throw new IllegalArgumentException("Delay must be positive: " + delay); } Objects.requireNonNull(job); synchronized (this.lock) { long jobTime = now.get() + delay; jobQueue.add(new JobEntry(jobTime, job)); return jobTime; } } public void remove(DelayedJob job) { Objects.requireNonNull(job); synchronized (listOfRemoves) { listOfRemoves.add(job); } } public boolean containsJob(DelayedJob job) { Objects.requireNonNull(job); return jobQueue.contains(job); } /** * Can be used to poll how much time is left until a job that was previously registered with * {@link #insert(DelayedJob, long)} is due. Given the return value of the {@link #insert(DelayedJob, long)} method, * this tells how long (in ticks) until the job is due. * * @param timepoint * the return value of {@link #insert(DelayedJob, long)} * @return the amount of ticks until the job is due. */ public long getDelay(long timepoint) { return timepoint - now.get(); } }