parent
							
								
									b384d7a70a
								
							
						
					
					
						commit
						4d2fc55a29
					
				@ -1,16 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Access to an {@link EventBus} instance | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public interface BusAccess { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @return event bus | 
				
			||||
	 */ | 
				
			||||
	EventBus getEventBus(); | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,121 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.DelayedEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.DirectEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.NonConsumableEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.NotLoggedEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.SingleReceiverEvent; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * <p> | 
				
			||||
 * Event that can be handled by HANDLER, subscribing to the event bus. | 
				
			||||
 * </p> | 
				
			||||
 * <p> | 
				
			||||
 * Can be annotated as {@link SingleReceiverEvent} to be delivered once only, | 
				
			||||
 * and {@link DelayedEvent} or {@link DirectEvent} to specify default sending | 
				
			||||
 * mode. When marked as {@link NotLoggedEvent}, it will not appear in detailed | 
				
			||||
 * bus logging (useful for very frequent events, such as UpdateEvent). | 
				
			||||
 * </p> | 
				
			||||
 * <p> | 
				
			||||
 * Events annotated as {@link NonConsumableEvent} will throw an exception upon | 
				
			||||
 * an attempt to consume them. | 
				
			||||
 * </p> | 
				
			||||
 * <p> | 
				
			||||
 * Default sending mode (if not changed by annotations) is <i>queued</i> with | 
				
			||||
 * zero delay. | 
				
			||||
 * </p> | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 * @param <HANDLER> handler type | 
				
			||||
 */ | 
				
			||||
public abstract class BusEvent<HANDLER> { | 
				
			||||
	 | 
				
			||||
	private boolean consumed; | 
				
			||||
	private boolean served; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Ask handler to handle this message. | 
				
			||||
	 *  | 
				
			||||
	 * @param handler handler instance | 
				
			||||
	 */ | 
				
			||||
	protected abstract void handleBy(HANDLER handler); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Consume the event, so no other clients will receive it. | 
				
			||||
	 *  | 
				
			||||
	 * @throws UnsupportedOperationException if the {@link NonConsumableEvent} | 
				
			||||
	 *             annotation is present. | 
				
			||||
	 */ | 
				
			||||
	public final void consume() | 
				
			||||
	{ | 
				
			||||
		if (consumed) throw new IllegalStateException("Already consumed."); | 
				
			||||
		 | 
				
			||||
		if (getClass().isAnnotationPresent(NonConsumableEvent.class)) { | 
				
			||||
			throw new UnsupportedOperationException("Not consumable."); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		consumed = true; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Deliver to a handler using the handleBy method. | 
				
			||||
	 *  | 
				
			||||
	 * @param handler handler instance | 
				
			||||
	 */ | 
				
			||||
	final void deliverTo(HANDLER handler) | 
				
			||||
	{ | 
				
			||||
		handleBy(handler); | 
				
			||||
		 | 
				
			||||
		if (!served) { | 
				
			||||
			if (getClass().isAnnotationPresent(SingleReceiverEvent.class)) { | 
				
			||||
				consumed = true; | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			served = true; | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Check if the event is consumed. Consumed event is not served to other | 
				
			||||
	 * clients. | 
				
			||||
	 *  | 
				
			||||
	 * @return true if consumed | 
				
			||||
	 */ | 
				
			||||
	public final boolean isConsumed() | 
				
			||||
	{ | 
				
			||||
		return consumed; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @return true if the event was served to at least 1 client | 
				
			||||
	 */ | 
				
			||||
	final boolean wasServed() | 
				
			||||
	{ | 
				
			||||
		return served; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Clear "served" and "consumed" flags before dispatching. | 
				
			||||
	 */ | 
				
			||||
	final void clearFlags() | 
				
			||||
	{ | 
				
			||||
		served = false; | 
				
			||||
		consumed = false; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Called after all clients have received the event. | 
				
			||||
	 */ | 
				
			||||
	public void onDispatchComplete(EventBus bus) | 
				
			||||
	{ | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,399 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.lang.reflect.ParameterizedType; | 
				
			||||
import java.lang.reflect.Type; | 
				
			||||
import java.util.Collection; | 
				
			||||
import java.util.Collections; | 
				
			||||
import java.util.Set; | 
				
			||||
import java.util.concurrent.ConcurrentHashMap; | 
				
			||||
import java.util.concurrent.DelayQueue; | 
				
			||||
import java.util.concurrent.Delayed; | 
				
			||||
import java.util.concurrent.TimeUnit; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.eventbus.clients.DelegatingClient; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.DelayedEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.DirectEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.NotLoggedEvent; | 
				
			||||
import mightypork.gamecore.eventbus.events.Destroyable; | 
				
			||||
import mightypork.gamecore.logging.Log; | 
				
			||||
import mightypork.gamecore.util.Utils; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * An event bus, accommodating multiple EventChannels.<br> | 
				
			||||
 * Channel will be created when an event of type is first encountered. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
final public class EventBus implements Destroyable, BusAccess { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Queued event holder | 
				
			||||
	 */ | 
				
			||||
	private class DelayQueueEntry implements Delayed { | 
				
			||||
		 | 
				
			||||
		private final long due; | 
				
			||||
		private final BusEvent<?> evt; | 
				
			||||
		 | 
				
			||||
		 | 
				
			||||
		public DelayQueueEntry(double seconds, BusEvent<?> event) | 
				
			||||
		{ | 
				
			||||
			super(); | 
				
			||||
			this.due = System.currentTimeMillis() + (long) (seconds * 1000); | 
				
			||||
			this.evt = event; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		 | 
				
			||||
		@Override | 
				
			||||
		public int compareTo(Delayed o) | 
				
			||||
		{ | 
				
			||||
			return Long.valueOf(getDelay(TimeUnit.MILLISECONDS)).compareTo(o.getDelay(TimeUnit.MILLISECONDS)); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		 | 
				
			||||
		@Override | 
				
			||||
		public long getDelay(TimeUnit unit) | 
				
			||||
		{ | 
				
			||||
			return unit.convert(due - System.currentTimeMillis(), TimeUnit.MILLISECONDS); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		 | 
				
			||||
		public BusEvent<?> getEvent() | 
				
			||||
		{ | 
				
			||||
			return evt; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Thread handling queued events | 
				
			||||
	 */ | 
				
			||||
	private class QueuePollingThread extends Thread { | 
				
			||||
		 | 
				
			||||
		public volatile boolean stopped = false; | 
				
			||||
		 | 
				
			||||
		 | 
				
			||||
		public QueuePollingThread() | 
				
			||||
		{ | 
				
			||||
			super("Queue Polling Thread"); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		 | 
				
			||||
		@Override | 
				
			||||
		public void run() | 
				
			||||
		{ | 
				
			||||
			DelayQueueEntry evt; | 
				
			||||
			 | 
				
			||||
			while (!stopped) { | 
				
			||||
				evt = null; | 
				
			||||
				 | 
				
			||||
				try { | 
				
			||||
					evt = sendQueue.take(); | 
				
			||||
				} catch (final InterruptedException ignored) { | 
				
			||||
					//
 | 
				
			||||
				} | 
				
			||||
				 | 
				
			||||
				if (evt != null) { | 
				
			||||
					try { | 
				
			||||
						dispatch(evt.getEvent()); | 
				
			||||
					} catch (final Throwable t) { | 
				
			||||
						Log.w(logMark + "Error while dispatching event: ", t); | 
				
			||||
					} | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	static final String logMark = "(bus) "; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	private static Class<?> getEventListenerClass(BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		// BEHOLD, MAGIC!
 | 
				
			||||
		 | 
				
			||||
		final Type evtc = event.getClass().getGenericSuperclass(); | 
				
			||||
		 | 
				
			||||
		if (evtc instanceof ParameterizedType) { | 
				
			||||
			if (((ParameterizedType) evtc).getRawType() == BusEvent.class) { | 
				
			||||
				final Type[] types = ((ParameterizedType) evtc).getActualTypeArguments(); | 
				
			||||
				for (final Type genericType : types) { | 
				
			||||
					return (Class<?>) genericType; | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		throw new RuntimeException("Could not detect event listener type."); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	/** Log detailed messages (debug) */ | 
				
			||||
	public boolean detailedLogging = false; | 
				
			||||
	 | 
				
			||||
	/** Queue polling thread */ | 
				
			||||
	private final QueuePollingThread busThread; | 
				
			||||
	 | 
				
			||||
	/** Registered clients */ | 
				
			||||
	private final Set<Object> clients = Collections.newSetFromMap(new ConcurrentHashMap<Object, Boolean>()); | 
				
			||||
	 | 
				
			||||
	/** Whether the bus was destroyed */ | 
				
			||||
	private boolean dead = false; | 
				
			||||
	 | 
				
			||||
	/** Message channels */ | 
				
			||||
	private final Set<EventChannel<?, ?>> channels = Collections.newSetFromMap(new ConcurrentHashMap<EventChannel<?, ?>, Boolean>()); | 
				
			||||
	 | 
				
			||||
	/** Messages queued for delivery */ | 
				
			||||
	private final DelayQueue<DelayQueueEntry> sendQueue = new DelayQueue<>(); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Make a new bus and start it's queue thread. | 
				
			||||
	 */ | 
				
			||||
	public EventBus() | 
				
			||||
	{ | 
				
			||||
		busThread = new QueuePollingThread(); | 
				
			||||
		busThread.setDaemon(true); | 
				
			||||
		busThread.start(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Halt bus thread and reject any future events. | 
				
			||||
	 */ | 
				
			||||
	@Override | 
				
			||||
	public void destroy() | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		busThread.stopped = true; | 
				
			||||
		dead = true; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Send based on annotation | 
				
			||||
	 *  | 
				
			||||
	 * @param event event | 
				
			||||
	 */ | 
				
			||||
	public void send(BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		final DelayedEvent adelay = Utils.getAnnotation(event, DelayedEvent.class); | 
				
			||||
		if (adelay != null) { | 
				
			||||
			sendDelayed(event, adelay.delay()); | 
				
			||||
			return; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		if (Utils.hasAnnotation(event, DirectEvent.class)) { | 
				
			||||
			sendDirect(event); | 
				
			||||
			return; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		sendQueued(event); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Add event to a queue | 
				
			||||
	 *  | 
				
			||||
	 * @param event event | 
				
			||||
	 */ | 
				
			||||
	public void sendQueued(BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		sendDelayed(event, 0); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Add event to a queue, scheduled for given time. | 
				
			||||
	 *  | 
				
			||||
	 * @param event event | 
				
			||||
	 * @param delay delay before event is dispatched | 
				
			||||
	 */ | 
				
			||||
	public void sendDelayed(BusEvent<?> event, double delay) | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		final DelayQueueEntry dm = new DelayQueueEntry(delay, event); | 
				
			||||
		 | 
				
			||||
		if (shallLog(event)) { | 
				
			||||
			Log.f3(logMark + "Qu [" + Log.str(event) + "]" + (delay == 0 ? "" : (", delay: " + delay + "s"))); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		sendQueue.add(dm); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Send immediately.<br> | 
				
			||||
	 * Should be used for real-time events that require immediate response, such | 
				
			||||
	 * as timing events. | 
				
			||||
	 *  | 
				
			||||
	 * @param event event | 
				
			||||
	 */ | 
				
			||||
	public void sendDirect(BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		if (shallLog(event)) Log.f3(logMark + "Di [" + Log.str(event) + "]"); | 
				
			||||
		 | 
				
			||||
		dispatch(event); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public void sendDirectToChildren(DelegatingClient delegatingClient, BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		if (shallLog(event)) Log.f3(logMark + "Di->sub [" + Log.str(event) + "]"); | 
				
			||||
		 | 
				
			||||
		doDispatch(delegatingClient.getChildClients(), event); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Connect a client to the bus. The client will be connected to all current | 
				
			||||
	 * and future channels, until removed from the bus. | 
				
			||||
	 *  | 
				
			||||
	 * @param client the client | 
				
			||||
	 */ | 
				
			||||
	public void subscribe(Object client) | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		if (client == null) return; | 
				
			||||
		 | 
				
			||||
		clients.add(client); | 
				
			||||
		 | 
				
			||||
		if (detailedLogging) Log.f3(logMark + "Client joined: " + Log.str(client)); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Disconnect a client from the bus. | 
				
			||||
	 *  | 
				
			||||
	 * @param client the client | 
				
			||||
	 */ | 
				
			||||
	public void unsubscribe(Object client) | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		clients.remove(client); | 
				
			||||
		 | 
				
			||||
		if (detailedLogging) Log.f3(logMark + "Client left: " + Log.str(client)); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@SuppressWarnings("unchecked") | 
				
			||||
	private boolean addChannelForEvent(BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		try { | 
				
			||||
			if (detailedLogging) { | 
				
			||||
				Log.f3(logMark + "Setting up channel for new event type: " + Log.str(event.getClass())); | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			final Class<?> listener = getEventListenerClass(event); | 
				
			||||
			final EventChannel<?, ?> ch = EventChannel.create(event.getClass(), listener); | 
				
			||||
			 | 
				
			||||
			if (ch.canBroadcast(event)) { | 
				
			||||
				 | 
				
			||||
				channels.add(ch); | 
				
			||||
				//channels.flush();
 | 
				
			||||
				 | 
				
			||||
				if (detailedLogging) { | 
				
			||||
					Log.f3(logMark + "Created new channel: " + Log.str(event.getClass()) + " -> " + Log.str(listener)); | 
				
			||||
				} | 
				
			||||
				 | 
				
			||||
				return true; | 
				
			||||
				 | 
				
			||||
			} else { | 
				
			||||
				Log.w(logMark + "Could not create channel for event " + Log.str(event.getClass())); | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
		} catch (final Throwable t) { | 
				
			||||
			Log.w(logMark + "Error while trying to add channel for event.", t); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		return false; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Make sure the bus is not destroyed. | 
				
			||||
	 *  | 
				
			||||
	 * @throws IllegalStateException if the bus is dead. | 
				
			||||
	 */ | 
				
			||||
	private void assertLive() throws IllegalStateException | 
				
			||||
	{ | 
				
			||||
		if (dead) throw new IllegalStateException("EventBus is dead."); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Send immediately.<br> | 
				
			||||
	 * Should be used for real-time events that require immediate response, such | 
				
			||||
	 * as timing events. | 
				
			||||
	 *  | 
				
			||||
	 * @param event event | 
				
			||||
	 */ | 
				
			||||
	private synchronized void dispatch(BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		assertLive(); | 
				
			||||
		 | 
				
			||||
		doDispatch(clients, event); | 
				
			||||
		event.onDispatchComplete(this); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Send to a set of clients | 
				
			||||
	 *  | 
				
			||||
	 * @param clients clients | 
				
			||||
	 * @param event event | 
				
			||||
	 */ | 
				
			||||
	private synchronized void doDispatch(Collection<?> clients, BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		boolean accepted = false; | 
				
			||||
		 | 
				
			||||
		event.clearFlags(); | 
				
			||||
		 | 
				
			||||
		for (int i = 0; i < 2; i++) { // two tries.
 | 
				
			||||
		 | 
				
			||||
			for (final EventChannel<?, ?> b : channels) { | 
				
			||||
				if (b.canBroadcast(event)) { | 
				
			||||
					accepted = true; | 
				
			||||
					b.broadcast(event, clients); | 
				
			||||
				} | 
				
			||||
				 | 
				
			||||
				if (event.isConsumed()) break; | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			if (!accepted) if (addChannelForEvent(event)) continue; | 
				
			||||
			 | 
				
			||||
			break; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		if (!accepted) Log.e(logMark + "Not accepted by any channel: " + Log.str(event)); | 
				
			||||
		if (!event.wasServed() && shallLog(event)) Log.w(logMark + "Not delivered: " + Log.str(event)); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	private boolean shallLog(BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		if (!detailedLogging) return false; | 
				
			||||
		if (Utils.hasAnnotation(event, NotLoggedEvent.class)) return false; | 
				
			||||
		 | 
				
			||||
		return true; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public EventBus getEventBus() | 
				
			||||
	{ | 
				
			||||
		return this; // just for compatibility use-case
 | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,207 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.Collection; | 
				
			||||
import java.util.HashSet; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.eventbus.clients.DelegatingClient; | 
				
			||||
import mightypork.gamecore.eventbus.clients.ToggleableClient; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.NonRejectableEvent; | 
				
			||||
import mightypork.gamecore.logging.Log; | 
				
			||||
import mightypork.gamecore.util.Utils; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Event delivery channel, module of {@link EventBus} | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 * @param <EVENT> event type | 
				
			||||
 * @param <CLIENT> client (subscriber) type | 
				
			||||
 */ | 
				
			||||
class EventChannel<EVENT extends BusEvent<CLIENT>, CLIENT> { | 
				
			||||
	 | 
				
			||||
	private final Class<CLIENT> clientClass; | 
				
			||||
	private final Class<EVENT> eventClass; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Create a channel | 
				
			||||
	 *  | 
				
			||||
	 * @param eventClass event class
 | 
				
			||||
	 * @param clientClass client class
 | 
				
			||||
	 */ | 
				
			||||
	public EventChannel(Class<EVENT> eventClass, Class<CLIENT> clientClass) | 
				
			||||
	{ | 
				
			||||
		 | 
				
			||||
		if (eventClass == null || clientClass == null) { | 
				
			||||
			throw new NullPointerException("Null Event or Client class."); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		this.clientClass = clientClass; | 
				
			||||
		this.eventClass = eventClass; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Try to broadcast a event.<br> | 
				
			||||
	 * If event is of wrong type, <code>false</code> is returned. | 
				
			||||
	 *  | 
				
			||||
	 * @param event a event to be sent | 
				
			||||
	 * @param clients collection of clients | 
				
			||||
	 */ | 
				
			||||
	public void broadcast(BusEvent<?> event, Collection<?> clients) | 
				
			||||
	{ | 
				
			||||
		if (!canBroadcast(event)) return; | 
				
			||||
		 | 
				
			||||
		doBroadcast(eventClass.cast(event), clients, new HashSet<>()); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Send the event | 
				
			||||
	 *  | 
				
			||||
	 * @param event sent event | 
				
			||||
	 * @param clients subscribing clients | 
				
			||||
	 * @param processed clients already processed | 
				
			||||
	 */ | 
				
			||||
	private void doBroadcast(final EVENT event, final Collection<?> clients, final Collection<Object> processed) | 
				
			||||
	{ | 
				
			||||
		for (final Object client : clients) { | 
				
			||||
			 | 
				
			||||
			// exclude obvious non-clients
 | 
				
			||||
			if (!isClientValid(client)) { | 
				
			||||
				continue; | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			// avoid executing more times
 | 
				
			||||
			if (processed.contains(client)) { | 
				
			||||
				Log.w(EventBus.logMark + "Client already served: " + Log.str(client)); | 
				
			||||
				continue; | 
				
			||||
			} | 
				
			||||
			processed.add(client); | 
				
			||||
			 | 
				
			||||
			final boolean must_deliver = Utils.hasAnnotation(event, NonRejectableEvent.class); | 
				
			||||
			 | 
				
			||||
			// opt-out
 | 
				
			||||
			if (client instanceof ToggleableClient) { | 
				
			||||
				if (!must_deliver && !((ToggleableClient) client).isListening()) continue; | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			sendTo(client, event); | 
				
			||||
			 | 
				
			||||
			if (event.isConsumed()) return; | 
				
			||||
			 | 
				
			||||
			// pass on to delegated clients
 | 
				
			||||
			if (client instanceof DelegatingClient) { | 
				
			||||
				if (must_deliver || ((DelegatingClient) client).doesDelegate()) { | 
				
			||||
					 | 
				
			||||
					final Collection<?> children = ((DelegatingClient) client).getChildClients(); | 
				
			||||
					 | 
				
			||||
					if (children != null && children.size() > 0) { | 
				
			||||
						doBroadcast(event, children, processed); | 
				
			||||
					} | 
				
			||||
					 | 
				
			||||
				} | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Send an event to a client. | 
				
			||||
	 *  | 
				
			||||
	 * @param client target client | 
				
			||||
	 * @param event event to send | 
				
			||||
	 */ | 
				
			||||
	@SuppressWarnings("unchecked") | 
				
			||||
	private void sendTo(Object client, EVENT event) | 
				
			||||
	{ | 
				
			||||
		if (isClientOfChannelType(client)) { | 
				
			||||
			((BusEvent<CLIENT>) event).deliverTo((CLIENT) client); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Check if the given event can be broadcasted by this channel | 
				
			||||
	 *  | 
				
			||||
	 * @param event event object | 
				
			||||
	 * @return can be broadcasted | 
				
			||||
	 */ | 
				
			||||
	public boolean canBroadcast(BusEvent<?> event) | 
				
			||||
	{ | 
				
			||||
		return event != null && eventClass.isInstance(event); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Create an instance for given types | 
				
			||||
	 *  | 
				
			||||
	 * @param eventClass event class
 | 
				
			||||
	 * @param clientClass client class
 | 
				
			||||
	 * @return the broadcaster | 
				
			||||
	 */ | 
				
			||||
	public static <F_EVENT extends BusEvent<F_CLIENT>, F_CLIENT> EventChannel<F_EVENT, F_CLIENT> create(Class<F_EVENT> eventClass, Class<F_CLIENT> clientClass) | 
				
			||||
	{ | 
				
			||||
		return new EventChannel<>(eventClass, clientClass); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Check if client is of channel type | 
				
			||||
	 *  | 
				
			||||
	 * @param client client | 
				
			||||
	 * @return is of type | 
				
			||||
	 */ | 
				
			||||
	private boolean isClientOfChannelType(Object client) | 
				
			||||
	{ | 
				
			||||
		return clientClass.isInstance(client); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Check if the channel is compatible with given | 
				
			||||
	 *  | 
				
			||||
	 * @param client client | 
				
			||||
	 * @return is supported | 
				
			||||
	 */ | 
				
			||||
	public boolean isClientValid(Object client) | 
				
			||||
	{ | 
				
			||||
		return isClientOfChannelType(client) || (client instanceof DelegatingClient); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public int hashCode() | 
				
			||||
	{ | 
				
			||||
		final int prime = 13; | 
				
			||||
		int result = 1; | 
				
			||||
		result = prime * result + ((clientClass == null) ? 0 : clientClass.hashCode()); | 
				
			||||
		result = prime * result + ((eventClass == null) ? 0 : eventClass.hashCode()); | 
				
			||||
		return result; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public boolean equals(Object obj) | 
				
			||||
	{ | 
				
			||||
		if (this == obj) return true; | 
				
			||||
		if (obj == null) return false; | 
				
			||||
		if (!(obj instanceof EventChannel)) return false; | 
				
			||||
		final EventChannel<?, ?> other = (EventChannel<?, ?>) obj; | 
				
			||||
		if (clientClass == null) { | 
				
			||||
			if (other.clientClass != null) return false; | 
				
			||||
		} else if (!clientClass.equals(other.clientClass)) return false; | 
				
			||||
		if (eventClass == null) { | 
				
			||||
			if (other.eventClass != null) return false; | 
				
			||||
		} else if (!eventClass.equals(other.eventClass)) return false; | 
				
			||||
		return true; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public String toString() | 
				
			||||
	{ | 
				
			||||
		return "{ " + Log.str(eventClass) + " => " + Log.str(clientClass) + " }"; | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,115 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.clients; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.Collection; | 
				
			||||
import java.util.LinkedHashSet; | 
				
			||||
import java.util.Set; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.eventbus.BusAccess; | 
				
			||||
import mightypork.gamecore.eventbus.EventBus; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Client that can be attached to the {@link EventBus}, or added as a child | 
				
			||||
 * client to another {@link DelegatingClient} | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public abstract class BusNode implements BusAccess, ClientHub { | 
				
			||||
	 | 
				
			||||
	private final BusAccess busAccess; | 
				
			||||
	 | 
				
			||||
	private final Set<Object> clients = new LinkedHashSet<>(); | 
				
			||||
	private boolean listening = true; | 
				
			||||
	private boolean delegating = true; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @param busAccess access to bus | 
				
			||||
	 */ | 
				
			||||
	public BusNode(BusAccess busAccess) | 
				
			||||
	{ | 
				
			||||
		this.busAccess = busAccess; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public Collection<Object> getChildClients() | 
				
			||||
	{ | 
				
			||||
		return clients; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public boolean doesDelegate() | 
				
			||||
	{ | 
				
			||||
		return delegating; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public boolean isListening() | 
				
			||||
	{ | 
				
			||||
		return listening; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Add a child subscriber to the {@link EventBus}.<br> | 
				
			||||
	 *  | 
				
			||||
	 * @param client | 
				
			||||
	 */ | 
				
			||||
	@Override | 
				
			||||
	public void addChildClient(Object client) | 
				
			||||
	{ | 
				
			||||
		if (client instanceof RootBusNode) { | 
				
			||||
			throw new IllegalArgumentException("Cannot nest RootBusNode."); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		clients.add(client); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Remove a child subscriber | 
				
			||||
	 *  | 
				
			||||
	 * @param client subscriber to remove | 
				
			||||
	 */ | 
				
			||||
	@Override | 
				
			||||
	public void removeChildClient(Object client) | 
				
			||||
	{ | 
				
			||||
		if (client != null) { | 
				
			||||
			clients.remove(client); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Set whether events should be received. | 
				
			||||
	 *  | 
				
			||||
	 * @param listening receive events | 
				
			||||
	 */ | 
				
			||||
	public void setListening(boolean listening) | 
				
			||||
	{ | 
				
			||||
		this.listening = listening; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Set whether events should be passed on to child nodes | 
				
			||||
	 *  | 
				
			||||
	 * @param delegating | 
				
			||||
	 */ | 
				
			||||
	public void setDelegating(boolean delegating) | 
				
			||||
	{ | 
				
			||||
		this.delegating = delegating; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public EventBus getEventBus() | 
				
			||||
	{ | 
				
			||||
		return busAccess.getEventBus(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,42 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.clients; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.eventbus.EventBus; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Common methods for client hubs (ie delegating vlient implementations) | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public interface ClientHub extends DelegatingClient, ToggleableClient { | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public boolean doesDelegate(); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public Collection<Object> getChildClients(); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public boolean isListening(); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Add a child subscriber to the {@link EventBus}.<br> | 
				
			||||
	 *  | 
				
			||||
	 * @param client | 
				
			||||
	 */ | 
				
			||||
	public void addChildClient(Object client); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Remove a child subscriber | 
				
			||||
	 *  | 
				
			||||
	 * @param client subscriber to remove | 
				
			||||
	 */ | 
				
			||||
	void removeChildClient(Object client); | 
				
			||||
} | 
				
			||||
@ -1,21 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.clients; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.ArrayList; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Array-list with varargs constructor | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public class ClientList extends ArrayList<Object> { | 
				
			||||
	 | 
				
			||||
	public ClientList(Object... clients) | 
				
			||||
	{ | 
				
			||||
		for (final Object c : clients) { | 
				
			||||
			super.add(c); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,27 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.clients; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Client containing child clients. According to the contract, if the collection | 
				
			||||
 * of clients is ordered, the clients will be served in that order. In any case, | 
				
			||||
 * the {@link DelegatingClient} itself will be served beforehand. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public interface DelegatingClient { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @return collection of child clients. Can not be null. | 
				
			||||
	 */ | 
				
			||||
	public Collection<?> getChildClients(); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @return true if delegating is active | 
				
			||||
	 */ | 
				
			||||
	public boolean doesDelegate(); | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,51 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.clients; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.Collection; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.gui.Enableable; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Basic delegating client | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public class DelegatingList extends ClientList implements DelegatingClient, Enableable { | 
				
			||||
	 | 
				
			||||
	private boolean enabled = true; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public DelegatingList(Object... clients) | 
				
			||||
	{ | 
				
			||||
		super(clients); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public Collection<?> getChildClients() | 
				
			||||
	{ | 
				
			||||
		return this; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public boolean doesDelegate() | 
				
			||||
	{ | 
				
			||||
		return isEnabled(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void setEnabled(boolean yes) | 
				
			||||
	{ | 
				
			||||
		enabled = yes; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public boolean isEnabled() | 
				
			||||
	{ | 
				
			||||
		return enabled; | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,45 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.clients; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.eventbus.BusAccess; | 
				
			||||
import mightypork.gamecore.eventbus.events.Destroyable; | 
				
			||||
import mightypork.gamecore.util.annot.DefaultImpl; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Bus node that should be directly attached to the bus. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public abstract class RootBusNode extends BusNode implements Destroyable { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @param busAccess access to bus | 
				
			||||
	 */ | 
				
			||||
	public RootBusNode(BusAccess busAccess) | 
				
			||||
	{ | 
				
			||||
		super(busAccess); | 
				
			||||
		 | 
				
			||||
		getEventBus().subscribe(this); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public final void destroy() | 
				
			||||
	{ | 
				
			||||
		deinit(); | 
				
			||||
		 | 
				
			||||
		getEventBus().unsubscribe(this); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Deinitialize the subsystem<br> | 
				
			||||
	 * (called during destruction) | 
				
			||||
	 */ | 
				
			||||
	@DefaultImpl | 
				
			||||
	protected void deinit() | 
				
			||||
	{ | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,16 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.clients; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Client that can toggle receiving messages. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public interface ToggleableClient { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @return true if the client wants to receive messages | 
				
			||||
	 */ | 
				
			||||
	public boolean isListening(); | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,22 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.event_flags; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.lang.annotation.*; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Event that should be queued with given delay (default: 0); | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
@Target(ElementType.TYPE) | 
				
			||||
@Inherited | 
				
			||||
@Documented | 
				
			||||
public @interface DelayedEvent { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @return event dispatch delay [seconds] | 
				
			||||
	 */ | 
				
			||||
	double delay() default 0; | 
				
			||||
} | 
				
			||||
@ -1,16 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.event_flags; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.lang.annotation.*; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Event that should not be queued. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
@Target(ElementType.TYPE) | 
				
			||||
@Inherited | 
				
			||||
@Documented | 
				
			||||
public @interface DirectEvent {} | 
				
			||||
@ -1,21 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.event_flags; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.lang.annotation.Documented; | 
				
			||||
import java.lang.annotation.ElementType; | 
				
			||||
import java.lang.annotation.Retention; | 
				
			||||
import java.lang.annotation.RetentionPolicy; | 
				
			||||
import java.lang.annotation.Target; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Event that cannot be consumed | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
@Documented | 
				
			||||
@Target(ElementType.TYPE) | 
				
			||||
public @interface NonConsumableEvent { | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,21 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.event_flags; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.lang.annotation.Documented; | 
				
			||||
import java.lang.annotation.ElementType; | 
				
			||||
import java.lang.annotation.Retention; | 
				
			||||
import java.lang.annotation.RetentionPolicy; | 
				
			||||
import java.lang.annotation.Target; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Event that is forcibly delivered to all clients (bypass Toggleable etc) | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
@Documented | 
				
			||||
@Target(ElementType.TYPE) | 
				
			||||
public @interface NonRejectableEvent { | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,17 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.event_flags; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.lang.annotation.*; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Event that's not worth logging, unless there was an error with it.<br> | 
				
			||||
 * Useful for common events that would otherwise clutter the log. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
@Target(ElementType.TYPE) | 
				
			||||
@Inherited | 
				
			||||
@Documented | 
				
			||||
public @interface NotLoggedEvent {} | 
				
			||||
@ -1,16 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.event_flags; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.lang.annotation.*; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Handled only by the first client, then discarded. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
@Target(ElementType.TYPE) | 
				
			||||
@Inherited | 
				
			||||
@Documented | 
				
			||||
public @interface SingleReceiverEvent {} | 
				
			||||
@ -1,24 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.events; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.eventbus.BusEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.DirectEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.NonConsumableEvent; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Invoke destroy() method of all subscribers. Used to deinit a system. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@DirectEvent | 
				
			||||
@NonConsumableEvent | 
				
			||||
public class DestroyEvent extends BusEvent<Destroyable> { | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void handleBy(Destroyable handler) | 
				
			||||
	{ | 
				
			||||
		handler.destroy(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,15 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.events; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Object that can be destroyed (free resources etc) | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public interface Destroyable { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Destroy this object | 
				
			||||
	 */ | 
				
			||||
	public void destroy(); | 
				
			||||
} | 
				
			||||
@ -1,38 +0,0 @@ | 
				
			||||
package mightypork.gamecore.eventbus.events; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.eventbus.BusEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.DirectEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.NonConsumableEvent; | 
				
			||||
import mightypork.gamecore.eventbus.event_flags.NotLoggedEvent; | 
				
			||||
import mightypork.gamecore.util.math.timing.Updateable; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Delta timing update event. Not logged. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@NotLoggedEvent | 
				
			||||
@DirectEvent | 
				
			||||
@NonConsumableEvent | 
				
			||||
public class UpdateEvent extends BusEvent<Updateable> { | 
				
			||||
	 | 
				
			||||
	private final double deltaTime; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @param deltaTime time since last update (sec) | 
				
			||||
	 */ | 
				
			||||
	public UpdateEvent(double deltaTime) | 
				
			||||
	{ | 
				
			||||
		this.deltaTime = deltaTime; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void handleBy(Updateable handler) | 
				
			||||
	{ | 
				
			||||
		handler.update(deltaTime); | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,25 +0,0 @@ | 
				
			||||
package mightypork.gamecore.gui; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Can be enabled or disabled.<br> | 
				
			||||
 * Implementations should take appropriate action (ie. stop listening to events, | 
				
			||||
 * updating etc.) | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public interface Enableable { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Change enabled state | 
				
			||||
	 *  | 
				
			||||
	 * @param yes enabled | 
				
			||||
	 */ | 
				
			||||
	public void setEnabled(boolean yes); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @return true if enabled | 
				
			||||
	 */ | 
				
			||||
	public boolean isEnabled(); | 
				
			||||
} | 
				
			||||
@ -1,385 +0,0 @@ | 
				
			||||
package mightypork.gamecore.logging; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.io.File; | 
				
			||||
import java.io.PrintWriter; | 
				
			||||
import java.io.StringWriter; | 
				
			||||
import java.util.HashMap; | 
				
			||||
import java.util.logging.Level; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.logging.monitors.LogMonitor; | 
				
			||||
import mightypork.gamecore.logging.monitors.LogToSysoutMonitor; | 
				
			||||
import mightypork.gamecore.logging.writers.ArchivingLog; | 
				
			||||
import mightypork.gamecore.logging.writers.LogWriter; | 
				
			||||
import mightypork.gamecore.logging.writers.SimpleLog; | 
				
			||||
import mightypork.gamecore.util.annot.FactoryMethod; | 
				
			||||
import mightypork.gamecore.util.strings.StringUtils; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * A log. | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public class Log { | 
				
			||||
	 | 
				
			||||
	private static LogWriter main = null; | 
				
			||||
	private static boolean enabled = true; | 
				
			||||
	private static final LogToSysoutMonitor sysoMonitor = new LogToSysoutMonitor(); | 
				
			||||
	private static final long start_ms = System.currentTimeMillis(); | 
				
			||||
	 | 
				
			||||
	private static HashMap<String, SimpleLog> logs = new HashMap<>(); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Create a logger. If another with the name already exists, it'll be | 
				
			||||
	 * retrieved instead of creating a new one. | 
				
			||||
	 *  | 
				
			||||
	 * @param logName log name (used for filename, should be application-unique) | 
				
			||||
	 * @param logFile log file; old logs will be kept here too. | 
				
			||||
	 * @param oldLogsCount number of old logs to keep, -1 infinite, 0 none. | 
				
			||||
	 * @return the created Log instance | 
				
			||||
	 */ | 
				
			||||
	@FactoryMethod | 
				
			||||
	public static synchronized LogWriter create(String logName, File logFile, int oldLogsCount) | 
				
			||||
	{ | 
				
			||||
		if (logs.containsKey(logName)) return logs.get(logName); | 
				
			||||
		 | 
				
			||||
		final ArchivingLog log = new ArchivingLog(logName, logFile, oldLogsCount); | 
				
			||||
		log.init(); | 
				
			||||
		 | 
				
			||||
		logs.put(logName, log); | 
				
			||||
		 | 
				
			||||
		return log; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Create a logger. If another with the name already exists, it'll be | 
				
			||||
	 * retrieved instead of creating a new one. | 
				
			||||
	 *  | 
				
			||||
	 * @param logName log name (used for filename, must be application-unique) | 
				
			||||
	 * @param logFile log file; old logs will be kept here too. | 
				
			||||
	 * @return the created Log instance | 
				
			||||
	 */ | 
				
			||||
	@FactoryMethod | 
				
			||||
	public static synchronized LogWriter create(String logName, File logFile) | 
				
			||||
	{ | 
				
			||||
		if (logs.containsKey(logName)) return logs.get(logName); | 
				
			||||
		 | 
				
			||||
		final SimpleLog log = new SimpleLog(logName, logFile); | 
				
			||||
		log.init(); | 
				
			||||
		 | 
				
			||||
		logs.put(logName, log); | 
				
			||||
		 | 
				
			||||
		return log; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static void setMainLogger(LogWriter log) | 
				
			||||
	{ | 
				
			||||
		main = log; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static void addMonitor(LogMonitor mon) | 
				
			||||
	{ | 
				
			||||
		assertInited(); | 
				
			||||
		 | 
				
			||||
		main.addMonitor(mon); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static void removeMonitor(LogMonitor mon) | 
				
			||||
	{ | 
				
			||||
		assertInited(); | 
				
			||||
		 | 
				
			||||
		main.removeMonitor(mon); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	private static void assertInited() | 
				
			||||
	{ | 
				
			||||
		if (main == null) throw new IllegalStateException("Main logger not initialized."); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log a message | 
				
			||||
	 *  | 
				
			||||
	 * @param level message level | 
				
			||||
	 * @param msg message text | 
				
			||||
	 */ | 
				
			||||
	public static void log(Level level, String msg) | 
				
			||||
	{ | 
				
			||||
		if (enabled) { | 
				
			||||
			sysoMonitor.onMessageLogged(level, formatMessage(level, msg, null, start_ms)); | 
				
			||||
			 | 
				
			||||
			if (main != null) { | 
				
			||||
				main.log(level, msg); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log a message | 
				
			||||
	 *  | 
				
			||||
	 * @param level message level | 
				
			||||
	 * @param msg message text | 
				
			||||
	 * @param t thrown exception | 
				
			||||
	 */ | 
				
			||||
	public static void log(Level level, String msg, Throwable t) | 
				
			||||
	{ | 
				
			||||
		if (enabled) { | 
				
			||||
			sysoMonitor.onMessageLogged(level, formatMessage(level, msg, t, start_ms)); | 
				
			||||
			 | 
				
			||||
			if (main != null) { | 
				
			||||
				main.log(level, msg, t); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log FINE message | 
				
			||||
	 *  | 
				
			||||
	 * @param msg message | 
				
			||||
	 */ | 
				
			||||
	public static void f1(String msg) | 
				
			||||
	{ | 
				
			||||
		log(Level.FINE, msg); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log FINER message | 
				
			||||
	 *  | 
				
			||||
	 * @param msg message | 
				
			||||
	 */ | 
				
			||||
	public static void f2(String msg) | 
				
			||||
	{ | 
				
			||||
		log(Level.FINER, msg); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log FINEST message | 
				
			||||
	 *  | 
				
			||||
	 * @param msg message | 
				
			||||
	 */ | 
				
			||||
	public static void f3(String msg) | 
				
			||||
	{ | 
				
			||||
		log(Level.FINEST, msg); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log INFO message | 
				
			||||
	 *  | 
				
			||||
	 * @param msg message | 
				
			||||
	 */ | 
				
			||||
	public static void i(String msg) | 
				
			||||
	{ | 
				
			||||
		log(Level.INFO, msg); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log WARNING message (less severe than ERROR) | 
				
			||||
	 *  | 
				
			||||
	 * @param msg message | 
				
			||||
	 */ | 
				
			||||
	public static void w(String msg) | 
				
			||||
	{ | 
				
			||||
		log(Level.WARNING, msg); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log ERROR message | 
				
			||||
	 *  | 
				
			||||
	 * @param msg message | 
				
			||||
	 */ | 
				
			||||
	public static void e(String msg) | 
				
			||||
	{ | 
				
			||||
		log(Level.SEVERE, msg); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log warning message with exception | 
				
			||||
	 *  | 
				
			||||
	 * @param msg message | 
				
			||||
	 * @param thrown thrown exception | 
				
			||||
	 */ | 
				
			||||
	public static void w(String msg, Throwable thrown) | 
				
			||||
	{ | 
				
			||||
		log(Level.WARNING, msg, thrown); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log exception thrown as warning | 
				
			||||
	 *  | 
				
			||||
	 * @param thrown thrown exception | 
				
			||||
	 */ | 
				
			||||
	public static void w(Throwable thrown) | 
				
			||||
	{ | 
				
			||||
		log(Level.WARNING, null, thrown); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log error message | 
				
			||||
	 *  | 
				
			||||
	 * @param msg message | 
				
			||||
	 * @param thrown thrown exception | 
				
			||||
	 */ | 
				
			||||
	public static void e(String msg, Throwable thrown) | 
				
			||||
	{ | 
				
			||||
		log(Level.SEVERE, msg, thrown); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log exception thrown as error | 
				
			||||
	 *  | 
				
			||||
	 * @param thrown thrown exception | 
				
			||||
	 */ | 
				
			||||
	public static void e(Throwable thrown) | 
				
			||||
	{ | 
				
			||||
		log(Level.SEVERE, null, thrown); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static void enable(boolean flag) | 
				
			||||
	{ | 
				
			||||
		enabled = flag; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static void setSysoutLevel(Level level) | 
				
			||||
	{ | 
				
			||||
		sysoMonitor.setLevel(level); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static void setLevel(Level level) | 
				
			||||
	{ | 
				
			||||
		assertInited(); | 
				
			||||
		 | 
				
			||||
		main.setLevel(level); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Get stack trace from throwable | 
				
			||||
	 *  | 
				
			||||
	 * @param t | 
				
			||||
	 * @return trace | 
				
			||||
	 */ | 
				
			||||
	public static String getStackTrace(Throwable t) | 
				
			||||
	{ | 
				
			||||
		final StringWriter sw = new StringWriter(); | 
				
			||||
		final PrintWriter pw = new PrintWriter(sw, true); | 
				
			||||
		t.printStackTrace(pw); | 
				
			||||
		pw.flush(); | 
				
			||||
		sw.flush(); | 
				
			||||
		return sw.toString(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static String formatMessage(Level level, String message, Throwable throwable, long start_ms) | 
				
			||||
	{ | 
				
			||||
		if (message == null) message = ""; | 
				
			||||
		 | 
				
			||||
		final String nl = System.getProperty("line.separator"); | 
				
			||||
		 | 
				
			||||
		if (message.length() > 0) { | 
				
			||||
			if (message.equals("\n")) { | 
				
			||||
				return nl; | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
			if (message.charAt(0) == '\n') { | 
				
			||||
				message = nl + message.substring(1); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		final long time_ms = (System.currentTimeMillis() - start_ms); | 
				
			||||
		final double time_s = time_ms / 1000D; | 
				
			||||
		final String time = String.format("%6.2f ", time_s); | 
				
			||||
		final String time_blank = StringUtils.repeat(" ", time.length()); | 
				
			||||
		 | 
				
			||||
		String prefix = "[ ? ]"; | 
				
			||||
		 | 
				
			||||
		if (level == Level.FINE) { | 
				
			||||
			prefix = "[ # ] "; | 
				
			||||
		} else if (level == Level.FINER) { | 
				
			||||
			prefix = "[ - ] "; | 
				
			||||
		} else if (level == Level.FINEST) { | 
				
			||||
			prefix = "[   ] "; | 
				
			||||
		} else if (level == Level.INFO) { | 
				
			||||
			prefix = "[ i ] "; | 
				
			||||
		} else if (level == Level.SEVERE) { | 
				
			||||
			prefix = "[!E!] "; | 
				
			||||
		} else if (level == Level.WARNING) { | 
				
			||||
			prefix = "[!W!] "; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		message = time + prefix + message.replaceAll("\n", nl + time_blank + prefix) + nl; | 
				
			||||
		 | 
				
			||||
		if (throwable != null) { | 
				
			||||
			message += getStackTrace(throwable); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		return message; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static String str(Object o) | 
				
			||||
	{ | 
				
			||||
		 | 
				
			||||
		if (o == null) return "<null>"; | 
				
			||||
		 | 
				
			||||
		boolean hasToString = false; | 
				
			||||
		 | 
				
			||||
		try { | 
				
			||||
			hasToString = (o.getClass().getMethod("toString").getDeclaringClass() != Object.class); | 
				
			||||
		} catch (final Throwable t) { | 
				
			||||
			// oh well..
 | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		if (hasToString) { | 
				
			||||
			return o.toString(); | 
				
			||||
		} else { | 
				
			||||
			 | 
				
			||||
			return str(o.getClass()); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static String str(Class<?> cls) | 
				
			||||
	{ | 
				
			||||
		final LogAlias ln = cls.getAnnotation(LogAlias.class); | 
				
			||||
		if (ln != null) { | 
				
			||||
			return ln.name(); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		String name = cls.getName(); | 
				
			||||
		 | 
				
			||||
		String sep = ""; | 
				
			||||
		 | 
				
			||||
		if (name.contains("$")) { | 
				
			||||
			name = name.substring(name.lastIndexOf("$") + 1); | 
				
			||||
			sep = "$"; | 
				
			||||
		} else { | 
				
			||||
			name = name.substring(name.lastIndexOf(".") + 1); | 
				
			||||
			sep = "."; | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		final Class<?> enclosing = cls.getEnclosingClass(); | 
				
			||||
		 | 
				
			||||
		return (enclosing == null ? "" : str(enclosing) + sep) + name; | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,21 +0,0 @@ | 
				
			||||
package mightypork.gamecore.logging; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.lang.annotation.Documented; | 
				
			||||
import java.lang.annotation.Inherited; | 
				
			||||
import java.lang.annotation.Retention; | 
				
			||||
import java.lang.annotation.RetentionPolicy; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Specify pretty name to be used when logging (eg. <code>Log.str()</code>) | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
@Retention(RetentionPolicy.RUNTIME) | 
				
			||||
@Inherited | 
				
			||||
@Documented | 
				
			||||
public @interface LogAlias { | 
				
			||||
	 | 
				
			||||
	String name(); | 
				
			||||
} | 
				
			||||
@ -1,39 +0,0 @@ | 
				
			||||
package mightypork.gamecore.logging.monitors; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.logging.Level; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
public abstract class BaseLogMonitor implements LogMonitor { | 
				
			||||
	 | 
				
			||||
	private boolean enabled = true; | 
				
			||||
	private Level accepted = Level.ALL; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void onMessageLogged(Level level, String message) | 
				
			||||
	{ | 
				
			||||
		if (!enabled) return; | 
				
			||||
		if (accepted.intValue() > level.intValue()) return; | 
				
			||||
		 | 
				
			||||
		logMessage(level, message); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	protected abstract void logMessage(Level level, String message); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void setLevel(Level level) | 
				
			||||
	{ | 
				
			||||
		this.accepted = level; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void enable(boolean flag) | 
				
			||||
	{ | 
				
			||||
		this.enabled = flag; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,17 +0,0 @@ | 
				
			||||
package mightypork.gamecore.logging.monitors; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.logging.Level; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
public interface LogMonitor { | 
				
			||||
	 | 
				
			||||
	void onMessageLogged(Level level, String message); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	void setLevel(Level level); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	void enable(boolean flag); | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,19 +0,0 @@ | 
				
			||||
package mightypork.gamecore.logging.monitors; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.logging.Level; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
public class LogToSysoutMonitor extends BaseLogMonitor { | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	protected void logMessage(Level level, String message) | 
				
			||||
	{ | 
				
			||||
		if (level == Level.SEVERE || level == Level.WARNING) { | 
				
			||||
			System.err.print(message); | 
				
			||||
		} else { | 
				
			||||
			System.out.print(message); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,129 +0,0 @@ | 
				
			||||
package mightypork.gamecore.logging.writers; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.io.File; | 
				
			||||
import java.io.FileFilter; | 
				
			||||
import java.text.SimpleDateFormat; | 
				
			||||
import java.util.Collections; | 
				
			||||
import java.util.Comparator; | 
				
			||||
import java.util.Date; | 
				
			||||
import java.util.List; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.util.files.FileUtils; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Logger that cleans directory & archives old logs | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 * @copy (c) 2014 | 
				
			||||
 */ | 
				
			||||
public class ArchivingLog extends SimpleLog { | 
				
			||||
	 | 
				
			||||
	/** Number of old logs to keep */ | 
				
			||||
	private final int logs_to_keep; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log | 
				
			||||
	 *  | 
				
			||||
	 * @param name log name | 
				
			||||
	 * @param file log file (in log directory) | 
				
			||||
	 * @param oldLogCount number of old log files to keep: -1 all, 0 none. | 
				
			||||
	 */ | 
				
			||||
	public ArchivingLog(String name, File file, int oldLogCount) | 
				
			||||
	{ | 
				
			||||
		super(name, file); | 
				
			||||
		this.logs_to_keep = oldLogCount; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log, not keeping 5 last log files (default); | 
				
			||||
	 *  | 
				
			||||
	 * @param name log name | 
				
			||||
	 * @param file log file (in log directory) | 
				
			||||
	 */ | 
				
			||||
	public ArchivingLog(String name, File file) | 
				
			||||
	{ | 
				
			||||
		super(name, file); | 
				
			||||
		this.logs_to_keep = 5; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void init() | 
				
			||||
	{ | 
				
			||||
		cleanLoggingDirectory(); | 
				
			||||
		 | 
				
			||||
		super.init(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	private void cleanLoggingDirectory() | 
				
			||||
	{ | 
				
			||||
		if (logs_to_keep == 0) return; // overwrite
 | 
				
			||||
		 | 
				
			||||
		final File log_file = getFile(); | 
				
			||||
		final File log_dir = log_file.getParentFile(); | 
				
			||||
		final String fname = FileUtils.getBasename(log_file.toString()); | 
				
			||||
		 | 
				
			||||
		// move old file
 | 
				
			||||
		for (final File f : FileUtils.listDirectory(log_dir)) { | 
				
			||||
			if (!f.isFile()) continue; | 
				
			||||
			if (f.equals(getFile())) { | 
				
			||||
				 | 
				
			||||
				final Date d = new Date(f.lastModified()); | 
				
			||||
				final String fbase = fname + '_' + (new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")).format(d); | 
				
			||||
				final String suff = "." + getSuffix(); | 
				
			||||
				String cntStr = ""; | 
				
			||||
				File f2; | 
				
			||||
				 | 
				
			||||
				for (int cnt = 0; (f2 = new File(log_dir, fbase + cntStr + suff)).exists(); cntStr = "_" + (++cnt)) {} | 
				
			||||
				 | 
				
			||||
				if (!f.renameTo(f2)) throw new RuntimeException("Could not move log file."); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		if (logs_to_keep == -1) return; // keep all
 | 
				
			||||
		 | 
				
			||||
		final List<File> oldLogs = FileUtils.listDirectory(log_dir, new FileFilter() { | 
				
			||||
			 | 
				
			||||
			@Override | 
				
			||||
			public boolean accept(File f) | 
				
			||||
			{ | 
				
			||||
				if (f.isDirectory()) return false; | 
				
			||||
				if (!f.getName().endsWith(getSuffix())) return false; | 
				
			||||
				if (!f.getName().startsWith(fname)) return false; | 
				
			||||
				 | 
				
			||||
				return true; | 
				
			||||
			} | 
				
			||||
			 | 
				
			||||
		}); | 
				
			||||
		 | 
				
			||||
		Collections.sort(oldLogs, new Comparator<File>() { | 
				
			||||
			 | 
				
			||||
			@Override | 
				
			||||
			public int compare(File o1, File o2) | 
				
			||||
			{ | 
				
			||||
				return o1.getName().compareTo(o2.getName()); | 
				
			||||
			} | 
				
			||||
		}); | 
				
			||||
		 | 
				
			||||
		// playing with fireee
 | 
				
			||||
		for (int i = 0; i < oldLogs.size() - logs_to_keep; i++) { | 
				
			||||
			if (!oldLogs.get(i).delete()) { | 
				
			||||
				throw new RuntimeException("Could not delete old log file."); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * @return log filename suffix | 
				
			||||
	 */ | 
				
			||||
	private String getSuffix() | 
				
			||||
	{ | 
				
			||||
		return FileUtils.getExtension(getFile()); | 
				
			||||
	} | 
				
			||||
} | 
				
			||||
@ -1,72 +0,0 @@ | 
				
			||||
package mightypork.gamecore.logging.writers; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.util.logging.Level; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.logging.monitors.LogMonitor; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Log interface
 | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public interface LogWriter { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Prepare logs for logging | 
				
			||||
	 */ | 
				
			||||
	void init(); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Add log monitor | 
				
			||||
	 *  | 
				
			||||
	 * @param mon monitor | 
				
			||||
	 */ | 
				
			||||
	void addMonitor(LogMonitor mon); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Remove a monitor | 
				
			||||
	 *  | 
				
			||||
	 * @param removed monitor to remove | 
				
			||||
	 */ | 
				
			||||
	void removeMonitor(LogMonitor removed); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Set logging level | 
				
			||||
	 *  | 
				
			||||
	 * @param level | 
				
			||||
	 */ | 
				
			||||
	void setLevel(Level level); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Enable logging. | 
				
			||||
	 *  | 
				
			||||
	 * @param flag do enable logging | 
				
			||||
	 */ | 
				
			||||
	void enable(boolean flag); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log a message | 
				
			||||
	 *  | 
				
			||||
	 * @param level message level | 
				
			||||
	 * @param msg message text | 
				
			||||
	 */ | 
				
			||||
	void log(Level level, String msg); | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log a message | 
				
			||||
	 *  | 
				
			||||
	 * @param level message level | 
				
			||||
	 * @param msg message text | 
				
			||||
	 * @param t thrown exception | 
				
			||||
	 */ | 
				
			||||
	void log(Level level, String msg, Throwable t); | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,166 +0,0 @@ | 
				
			||||
package mightypork.gamecore.logging.writers; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import java.io.File; | 
				
			||||
import java.text.SimpleDateFormat; | 
				
			||||
import java.util.Date; | 
				
			||||
import java.util.HashSet; | 
				
			||||
import java.util.logging.FileHandler; | 
				
			||||
import java.util.logging.Formatter; | 
				
			||||
import java.util.logging.Level; | 
				
			||||
import java.util.logging.LogRecord; | 
				
			||||
import java.util.logging.Logger; | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.logging.Log; | 
				
			||||
import mightypork.gamecore.logging.monitors.LogMonitor; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
/** | 
				
			||||
 * Basic logger | 
				
			||||
 *  | 
				
			||||
 * @author Ondřej Hruška (MightyPork) | 
				
			||||
 */ | 
				
			||||
public class SimpleLog implements LogWriter { | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Log file formatter. | 
				
			||||
	 */ | 
				
			||||
	class LogFormatter extends Formatter { | 
				
			||||
		 | 
				
			||||
		@Override | 
				
			||||
		public String format(LogRecord record) | 
				
			||||
		{ | 
				
			||||
			return Log.formatMessage(record.getLevel(), record.getMessage(), record.getThrown(), started_ms); | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	/** Log file */ | 
				
			||||
	private final File file; | 
				
			||||
	 | 
				
			||||
	/** Log name */ | 
				
			||||
	private final String name; | 
				
			||||
	 | 
				
			||||
	/** Logger instance. */ | 
				
			||||
	private Logger logger; | 
				
			||||
	 | 
				
			||||
	private boolean enabled = true; | 
				
			||||
	private final HashSet<LogMonitor> monitors = new HashSet<>(); | 
				
			||||
	private final long started_ms; | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public SimpleLog(String name, File file) | 
				
			||||
	{ | 
				
			||||
		this.name = name; | 
				
			||||
		this.file = file; | 
				
			||||
		this.started_ms = System.currentTimeMillis(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void init() | 
				
			||||
	{ | 
				
			||||
		logger = Logger.getLogger(getName()); | 
				
			||||
		 | 
				
			||||
		FileHandler handler = null; | 
				
			||||
		try { | 
				
			||||
			handler = new FileHandler(getFile().getPath()); | 
				
			||||
		} catch (final Throwable t) { | 
				
			||||
			throw new RuntimeException("Failed to init log.", t); | 
				
			||||
		} | 
				
			||||
		 | 
				
			||||
		handler.setFormatter(new LogFormatter()); | 
				
			||||
		logger.addHandler(handler); | 
				
			||||
		logger.setUseParentHandlers(false); | 
				
			||||
		logger.setLevel(Level.ALL); | 
				
			||||
		 | 
				
			||||
		printHeader(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	protected void printHeader() | 
				
			||||
	{ | 
				
			||||
		final String stamp = (new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()); | 
				
			||||
		log(Level.INFO, "Logger \"" + getName() + "\" initialized.\n" + stamp); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Add log monitor | 
				
			||||
	 *  | 
				
			||||
	 * @param mon monitor | 
				
			||||
	 */ | 
				
			||||
	@Override | 
				
			||||
	public synchronized void addMonitor(LogMonitor mon) | 
				
			||||
	{ | 
				
			||||
		monitors.add(mon); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	/** | 
				
			||||
	 * Remove a monitor | 
				
			||||
	 *  | 
				
			||||
	 * @param removed monitor to remove | 
				
			||||
	 */ | 
				
			||||
	@Override | 
				
			||||
	public synchronized void removeMonitor(LogMonitor removed) | 
				
			||||
	{ | 
				
			||||
		monitors.remove(removed); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void setLevel(Level level) | 
				
			||||
	{ | 
				
			||||
		logger.setLevel(level); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void enable(boolean flag) | 
				
			||||
	{ | 
				
			||||
		enabled = flag; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public File getFile() | 
				
			||||
	{ | 
				
			||||
		return file; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public String getName() | 
				
			||||
	{ | 
				
			||||
		return name; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void log(Level level, String msg) | 
				
			||||
	{ | 
				
			||||
		if (enabled) { | 
				
			||||
			logger.log(level, msg); | 
				
			||||
			 | 
				
			||||
			final String fmt = Log.formatMessage(level, msg, null, started_ms); | 
				
			||||
			 | 
				
			||||
			for (final LogMonitor mon : monitors) { | 
				
			||||
				mon.onMessageLogged(level, fmt); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	@Override | 
				
			||||
	public void log(Level level, String msg, Throwable t) | 
				
			||||
	{ | 
				
			||||
		if (enabled) { | 
				
			||||
			logger.log(level, msg, t); | 
				
			||||
			 | 
				
			||||
			final String fmt = Log.formatMessage(level, msg, t, started_ms); | 
				
			||||
			 | 
				
			||||
			for (final LogMonitor mon : monitors) { | 
				
			||||
				mon.onMessageLogged(level, fmt); | 
				
			||||
			} | 
				
			||||
		} | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
@ -1,32 +0,0 @@ | 
				
			||||
package mightypork.gamecore.resources; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
import mightypork.gamecore.util.math.Calc; | 
				
			||||
 | 
				
			||||
 | 
				
			||||
public class Profiler { | 
				
			||||
	 | 
				
			||||
	public static long begin() | 
				
			||||
	{ | 
				
			||||
		return System.currentTimeMillis(); | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static double end(long begun) | 
				
			||||
	{ | 
				
			||||
		return endLong(begun) / 1000D; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static long endLong(long begun) | 
				
			||||
	{ | 
				
			||||
		return System.currentTimeMillis() - begun; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
	 | 
				
			||||
	public static String endStr(long begun) | 
				
			||||
	{ | 
				
			||||
		return Calc.toString(end(begun)) + " s"; | 
				
			||||
	} | 
				
			||||
	 | 
				
			||||
} | 
				
			||||
Some files were not shown because too many files have changed in this diff Show More
					Loading…
					
					
				
		Reference in new issue