Add support for Events defined in Ruby

A slightly complicated and hacky change, because this requires working
around JRuby issue 2359.
This commit is contained in:
Major-
2016-02-12 16:29:57 +00:00
parent 8e52dfb121
commit 9a920d95ee
6 changed files with 189 additions and 35 deletions
+11 -13
View File
@@ -7,6 +7,7 @@ import java.util.Map;
import java.util.Queue;
import java.util.logging.Logger;
import com.google.common.base.Preconditions;
import org.apollo.Service;
import org.apollo.cache.IndexedFileSystem;
import org.apollo.cache.decoder.ItemDefinitionDecoder;
@@ -31,8 +32,6 @@ import org.apollo.game.scheduling.Scheduler;
import org.apollo.game.scheduling.impl.NpcMovementTask;
import org.apollo.util.NameUtil;
import com.google.common.base.Preconditions;
/**
* The world class is a singleton which contains objects like the {@link MobRepository} for players and NPCs. It should
* only contain things relevant to the in-game world and not classes which deal with I/O and such (these may be better
@@ -86,6 +85,11 @@ public final class World {
*/
private final MobRepository<Npc> npcRepository = new MobRepository<>(WorldConstants.MAXIMUM_NPCS);
/**
* The Queue of Npcs that have yet to be removed from the repository.
*/
private final Queue<Npc> oldNpcs = new ArrayDeque<>();
/**
* The {@link MobRepository} of {@link Player}s.
*/
@@ -100,11 +104,6 @@ public final class World {
* The Queue of Npcs that have yet to be added to the repository.
*/
private final Queue<Npc> queuedNpcs = new ArrayDeque<>();
/**
* The Queue of Npcs that have yet to be removed from the repository.
*/
private final Queue<Npc> oldNpcs = new ArrayDeque<>();
/**
* This world's {@link RegionRepository}.
@@ -209,8 +208,8 @@ public final class World {
releaseNumber = release;
SynchronousDecoder decoder = new SynchronousDecoder(new ItemDefinitionDecoder(fs),
new NpcDefinitionDecoder(fs), new GameObjectDecoder(fs, this),
EquipmentDefinitionParser.fromFile("data/equipment-" + release + "" + ".dat"));
new NpcDefinitionDecoder(fs), new GameObjectDecoder(fs, this),
EquipmentDefinitionParser.fromFile("data/equipment-" + release + "" + ".dat"));
decoder.block();
@@ -315,8 +314,7 @@ public final class World {
* @param npc The npc.
*/
public void unregister(final Npc npc) {
Preconditions.checkNotNull(npc, "Npc may not be null.");
Preconditions.checkNotNull(npc, "Npc must not be null.");
oldNpcs.add(npc);
}
@@ -362,7 +360,7 @@ public final class World {
npcMovement.addNpc(npc);
}
} else {
logger.warning("Failed to register npc, repository capacity reached: [count=" + npcRepository.size() + "]");
logger.warning("Failed to register npc (capacity reached): [count=" + npcRepository.size() + "]");
}
}
}
@@ -373,7 +371,7 @@ public final class World {
private void unregisterNpcs() {
while (!oldNpcs.isEmpty()) {
Npc npc = oldNpcs.poll();
Region region = regions.fromPosition(npc.getPosition());
region.removeEntity(npc);
@@ -0,0 +1,52 @@
package org.apollo.game.model.event;
/**
* An {@link Event} that wraps another {@link Event}.
*
* This is a workaround for JRuby issue <a href="https://github.com/jruby/jruby/issues/2359">2359</a>. This class
* should <strong>not</strong> be used in Java.
*
* @author Major
*/
public final class ProxyEvent extends Event {
/**
* The Event created by a Ruby plugin.
*/
private final Event event;
/**
* The name of the Ruby Event.
*/
private final String name;
/**
* Creates the ProxyEvent.
*
* @param name The name of the {@link Event} defined in Ruby.
* @param event The Event created by a Ruby plugin.
*/
public ProxyEvent(String name, Event event) {
this.name = name;
this.event = event;
}
/**
* Gets the name of the {@link Event} defined in Ruby.
*
* @return The name.
*/
public String getName() {
return name;
}
/**
* Gets the {@link Event} created in a Ruby plugin.
*
* @return The Ruby {@link Event}.
*/
public Event getRuby() {
return event;
}
}
@@ -0,0 +1,52 @@
package org.apollo.game.model.event;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* An {@link EventListener} for {@link ProxyEvent}s.
*
* This is a workaround for JRuby issue <a href="https://github.com/jruby/jruby/issues/2359">2359</a>.
*
* @author Major
*/
public final class ProxyEventListener implements EventListener<ProxyEvent> {
/**
* The {@link Map} from Event names to {@link List}s of {@link EventListener}s.
*/
private final Map<String, List<EventListener<Event>>> listeners = new HashMap<>();
/**
* Registers an {@link EventListener} to this proxy. This method is <strong>not</strong> type-safe, and must only
* be called from ruby.
*
* @param name The name of the Event. Must not be {@code null}.
* @param listener The {@link EventListener} to add. Must not be {@code null}.
*/
public void add(String name, EventListener<Event> listener) {
List<EventListener<Event>> listeners = this.listeners.computeIfAbsent(name, n -> new ArrayList<>(2));
listeners.add(listener);
}
@Override
@SuppressWarnings("unchecked")
public void handle(ProxyEvent event) {
List<EventListener<Event>> chain = listeners.get(event.getName());
if (chain != null) {
for (EventListener<Event> listener : chain) {
Event ruby = event.getRuby();
listener.handle(ruby);
if (ruby.terminated()) {
event.terminate();
break;
}
}
}
}
}
@@ -20,11 +20,6 @@ public final class RubyPluginEnvironment implements PluginEnvironment {
*/
private final ScriptingContainer container = new ScriptingContainer();
/**
* The World this RubyPluginEnvironment is for.
*/
private final World world;
/**
* Creates and bootstraps the Ruby plugin environment.
*
@@ -32,7 +27,7 @@ public final class RubyPluginEnvironment implements PluginEnvironment {
* @throws IOException If an I/O error occurs during bootstrapping.
*/
public RubyPluginEnvironment(World world) throws IOException {
this.world = world;
container.put("$world", world);
parseBootstrapper();
}
@@ -42,7 +37,7 @@ public final class RubyPluginEnvironment implements PluginEnvironment {
container.runScriptlet(is, name);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Error parsing scriptlet " + name + ".");
throw new RuntimeException("Error parsing scriptlet " + name + ".", e);
}
}
@@ -61,7 +56,6 @@ public final class RubyPluginEnvironment implements PluginEnvironment {
@Override
public void setContext(PluginContext context) {
container.put("$ctx", context);
container.put("$world", world);
}
}