/*******************************************************************************
* Copyright (c) 2015
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.logic.timer;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import jsettlers.common.map.MapLoadException;
import jsettlers.logic.constants.MatchConstants;
import jsettlers.network.client.interfaces.IGameClock;
import jsettlers.network.synchronic.timer.INetworkTimerable;
public final class RescheduleTimer implements INetworkTimerable, Serializable {
private static final long serialVersionUID = -1962430988827211391L;
private static final int FUTURE_TIME = 32000;
private static final short TIME_SLICE = 25; // ms
private static final int TIME_SLOTS = FUTURE_TIME / TIME_SLICE;
private static RescheduleTimer uniIns;
@SuppressWarnings("unchecked")
private final ArrayList<IScheduledTimerable> timerables[] = new ArrayList[TIME_SLOTS];
private int currTimeSlot = 0;
protected RescheduleTimer() {
for (int i = 0; i < TIME_SLOTS; i++) {
timerables[i] = new ArrayList<IScheduledTimerable>();
}
}
public static synchronized void stopAndClear() {
if (uniIns != null) {
if (MatchConstants.clock() != null) {
MatchConstants.clock().remove(uniIns);
}
uniIns = null;
try {
Thread.sleep(100); // stopping takes some time
} catch (InterruptedException e) {
}
}
}
/**
* Schedules the given {@link IScheduledTimerable} in max delay milliseconds.
*
* @param t
* @param delay
*/
public static void add(IScheduledTimerable t, int delay) {
get().addTimerable(t, delay);
}
private void addTimerable(IScheduledTimerable t, int delay) {
if (delay <= 0) {
return; // don't schedule if requested delay is negative or zero
}
int delaySlots = delay / TIME_SLICE;
delaySlots = delaySlots > 0 ? delaySlots : 1; // ensure at least one slot delay
assert delaySlots < TIME_SLOTS : "SCHEDULED TO FAR IN THE FUTURE! " + delay + "ms";
timerables[(currTimeSlot + delaySlots) % TIME_SLOTS].add(t);
}
private static synchronized RescheduleTimer get() {
if (uniIns == null) {
uniIns = new RescheduleTimer();
}
return uniIns;
}
@Override
public void timerEvent() {
ArrayList<IScheduledTimerable> queue = timerables[currTimeSlot];
for (IScheduledTimerable curr : queue) {
if (uniIns != this) { // fast stop when stopAndClear() is called.
return;
}
try {
int delay = curr.timerEvent();
addTimerable(curr, delay);
} catch (Throwable t) {
System.err.println("RescheduleTimer catched: ");
t.printStackTrace();
try {
curr.kill();
} catch (Throwable t2) {
System.err.println("RescheduleTimer had trouble killing bad timerable!");
t2.printStackTrace();
}
}
}
queue.clear();
currTimeSlot = (currTimeSlot + 1) % TIME_SLOTS;
}
public static void loadFrom(ObjectInputStream ois) throws MapLoadException {
try {
stopAndClear();
uniIns = (RescheduleTimer) ois.readObject();
} catch (Throwable t) {
throw new MapLoadException(t);
}
}
public static void saveTo(ObjectOutputStream oos) throws IOException {
oos.writeObject(uniIns);
oos.flush();
}
public static void schedule(IGameClock gameClock) {
gameClock.schedule(get(), TIME_SLICE);
}
}