From ef7f573256c65ab37b63783a60c84a1b3fe4ffd3 Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 29 Dec 2017 21:49:42 -0500 Subject: [PATCH] Added basic Radio Tower functionality #32 Keypress buffer is cleared on code upload --- .../net/simon987/cubotplugin/ComPort.java | 84 +++++++++++++++ .../java/net/simon987/cubotplugin/Cubot.java | 5 + .../net/simon987/cubotplugin/CubotLaser.java | 2 - .../net/simon987/cubotplugin/CubotPlugin.java | 2 + .../event/CpuInitialisationListener.java | 6 +- .../java/net/simon987/npcplugin/Factory.java | 2 +- .../net/simon987/npcplugin/NpcPlugin.java | 40 ++++++- .../npcplugin/RadioReceiverHardware.java | 78 ++++++++++++++ .../net/simon987/npcplugin/RadioTower.java | 101 +++++++++++++++++- .../event/CpuInitialisationListener.java | 27 +++++ .../event/WorldCreationListener.java | 34 ++++++ .../java/net/simon987/server/game/Action.java | 3 +- .../server/game/ControllableUnit.java | 5 + .../simon987/server/game/Programmable.java | 7 ++ .../server/webserver/CodeUploadHandler.java | 7 ++ 15 files changed, 391 insertions(+), 12 deletions(-) create mode 100644 Plugin Cubot/src/main/java/net/simon987/cubotplugin/ComPort.java create mode 100644 Plugin NPC/src/main/java/net/simon987/npcplugin/RadioReceiverHardware.java create mode 100644 Plugin NPC/src/main/java/net/simon987/npcplugin/event/CpuInitialisationListener.java create mode 100644 Server/src/main/java/net/simon987/server/game/Programmable.java diff --git a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/ComPort.java b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/ComPort.java new file mode 100644 index 0000000..dbb55d7 --- /dev/null +++ b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/ComPort.java @@ -0,0 +1,84 @@ +package net.simon987.cubotplugin; + +import net.simon987.server.GameServer; +import net.simon987.server.assembly.CpuHardware; +import net.simon987.server.assembly.Status; +import net.simon987.server.game.GameObject; +import net.simon987.server.game.Programmable; +import org.json.simple.JSONObject; + +import java.awt.*; +import java.util.ArrayList; + +public class ComPort extends CpuHardware { + + public static final char HWID = 0xD; + public static final int DEFAULT_ADDRESS = 0xD; + + private Cubot cubot; + + private static final int POLL = 1; + + private static final int OUT = 2; + + public ComPort(Cubot cubot) { + this.cubot = cubot; + } + + private static final int MESSAGE_LENGTH = 8; + + @Override + public void handleInterrupt(Status status) { + + int a = getCpu().getRegisterSet().getRegister("A").getValue(); + + if (a == POLL) { + + /* No-op */ + + } else if (a == OUT) { + + //Get object directly in front of the Cubot + Point frontTile = cubot.getFrontTile(); + ArrayList objects = cubot.getWorld().getGameObjectsAt(frontTile.x, frontTile.y); + + if (objects.size() > 0 && objects.get(0) instanceof Programmable) { + + int x = getCpu().getRegisterSet().getRegister("X").getValue(); + + if (x + MESSAGE_LENGTH >= getCpu().getMemory().getWords().length) { + //todo set interrupt ? + getCpu().getStatus().setErrorFlag(true); + } else { + + //Get MESSAGE_LENGTH-word message pointed by X + char[] message = new char[MESSAGE_LENGTH]; + System.arraycopy(getCpu().getMemory().getWords(), x, message, 0, MESSAGE_LENGTH); + + //Send it to the Programmable object + ((Programmable) objects.get(0)).sendMessage(message); + } + } + } + + + } + + @Override + public char getId() { + return HWID; + } + + @Override + public JSONObject serialise() { + JSONObject json = new JSONObject(); + json.put("hwid", (int) HWID); + json.put("cubot", cubot.getObjectId()); + + return json; + } + + public static ComPort deserialize(JSONObject json) { + return new ComPort((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((int) (long) json.get("cubot"))); + } +} diff --git a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/Cubot.java b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/Cubot.java index 499e00e..1526c28 100644 --- a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/Cubot.java +++ b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/Cubot.java @@ -225,4 +225,9 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit { HEX, STRING } + + @Override + public void setAction(Action action) { + currentAction = action; + } } diff --git a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotLaser.java b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotLaser.java index 341f59b..f336b67 100644 --- a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotLaser.java +++ b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotLaser.java @@ -62,8 +62,6 @@ public class CubotLaser extends CpuHardware { } } } - } else { - System.out.println("\n\n\n\n\n It did it"); } diff --git a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotPlugin.java b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotPlugin.java index 8060d11..8c025b7 100644 --- a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotPlugin.java +++ b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotPlugin.java @@ -58,6 +58,8 @@ public class CubotPlugin extends ServerPlugin implements GameObjectDeserializer, return CubotBattery.deserialize(hwJson); case CubotFloppyDrive.HWID: return CubotFloppyDrive.deserialize(hwJson); + case ComPort.HWID: + return ComPort.deserialize(hwJson); } return null; diff --git a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/event/CpuInitialisationListener.java b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/event/CpuInitialisationListener.java index 0bf3106..a33884d 100644 --- a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/event/CpuInitialisationListener.java +++ b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/event/CpuInitialisationListener.java @@ -5,7 +5,6 @@ import net.simon987.server.assembly.CPU; import net.simon987.server.event.CpuInitialisationEvent; import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEventListener; -import net.simon987.server.logging.LogManager; import net.simon987.server.user.User; public class CpuInitialisationListener implements GameEventListener { @@ -16,7 +15,7 @@ public class CpuInitialisationListener implements GameEventListener { @Override public void handle(GameEvent event) { - LogManager.LOGGER.fine("(Plugin) Handled CPU Initialisation event (Cubot Plugin)"); + //LogManager.LOGGER.fine("(Plugin) Handled CPU Initialisation event (Cubot Plugin)"); CPU cpu = (CPU) event.getSource(); User user = ((CpuInitialisationEvent) event).getUser(); @@ -39,6 +38,8 @@ public class CpuInitialisationListener implements GameEventListener { batteryHw.setCpu(cpu); CubotFloppyDrive floppyHw = new CubotFloppyDrive((Cubot) user.getControlledUnit()); floppyHw.setCpu(cpu); + ComPort comPortHw = new ComPort((Cubot) user.getControlledUnit()); + comPortHw.setCpu(cpu); cpu.attachHardware(legHw, CubotLeg.DEFAULT_ADDRESS); cpu.attachHardware(laserHw, CubotLaser.DEFAULT_ADDRESS); @@ -50,5 +51,6 @@ public class CpuInitialisationListener implements GameEventListener { cpu.attachHardware(emoteHw, CubotHologram.DEFAULT_ADDRESS); cpu.attachHardware(batteryHw, CubotBattery.DEFAULT_ADDRESS); cpu.attachHardware(floppyHw, CubotFloppyDrive.DEFAULT_ADDRESS); + cpu.attachHardware(comPortHw, ComPort.DEFAULT_ADDRESS); } } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java index 85deb75..da8b5fd 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java @@ -124,7 +124,7 @@ public class Factory extends GameObject implements Updatable { factory.setX((int) (long) json.get("x")); factory.setY((int) (long) json.get("y")); - factory.tmpNpcArray = (Object[]) ((JSONArray) json.get("n")).toArray(); + factory.tmpNpcArray = ((JSONArray) json.get("n")).toArray(); return factory; } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java index 36e7da6..a2d80d1 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java @@ -1,35 +1,65 @@ package net.simon987.npcplugin; +import net.simon987.npcplugin.event.CpuInitialisationListener; import net.simon987.npcplugin.event.WorldCreationListener; import net.simon987.server.ServerConfiguration; +import net.simon987.server.assembly.CpuHardware; import net.simon987.server.game.GameObject; +import net.simon987.server.io.CpuHardwareDeserializer; import net.simon987.server.io.GameObjectDeserializer; import net.simon987.server.logging.LogManager; import net.simon987.server.plugin.ServerPlugin; import org.json.simple.JSONObject; -public class NpcPlugin extends ServerPlugin implements GameObjectDeserializer { +import java.util.ArrayList; +public class NpcPlugin extends ServerPlugin implements GameObjectDeserializer, CpuHardwareDeserializer { + + /** + * Radio tower cache + */ + private static ArrayList radioTowers; @Override public void init(ServerConfiguration configuration) { listeners.add(new WorldCreationListener()); + listeners.add(new CpuInitialisationListener()); + + radioTowers = new ArrayList<>(32); LogManager.LOGGER.info("Initialised NPC plugin"); } @Override - public GameObject deserializeObject(JSONObject object) { + public GameObject deserializeObject(JSONObject json) { - int objType = (int) (long) object.get("t"); + int objType = (int) (long) json.get("t"); if (objType == HarvesterNPC.ID) { - return HarvesterNPC.deserialize(object); + return HarvesterNPC.deserialize(json); } else if (objType == Factory.ID) { - return Factory.deserialise(object); + return Factory.deserialise(json); + } else if (objType == RadioTower.ID) { + return RadioTower.deserialize(json); } return null; } + + @Override + public CpuHardware deserializeHardware(JSONObject hwJson) { + int hwid = (int) (long) hwJson.get("hwid"); + + switch (hwid) { + case RadioReceiverHardware.HWID: + return RadioReceiverHardware.deserialize(hwJson); + } + + return null; + } + + public static ArrayList getRadioTowers() { + return radioTowers; + } } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioReceiverHardware.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioReceiverHardware.java new file mode 100644 index 0000000..a859cf0 --- /dev/null +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioReceiverHardware.java @@ -0,0 +1,78 @@ +package net.simon987.npcplugin; + +import net.simon987.server.GameServer; +import net.simon987.server.assembly.CpuHardware; +import net.simon987.server.assembly.Status; +import net.simon987.server.assembly.Util; +import net.simon987.server.game.Action; +import net.simon987.server.game.ControllableUnit; +import org.json.simple.JSONObject; + +import java.util.ArrayList; + +public class RadioReceiverHardware extends CpuHardware { + + public static final char HWID = 0xC; //12 + + private static final int LISTEN = 1; + + public static final int DEFAULT_ADDRESS = 0xC; + + private ControllableUnit cubot; + + public RadioReceiverHardware(ControllableUnit cubot) { + this.cubot = cubot; + } + + @Override + public void handleInterrupt(Status status) { + int x = getCpu().getRegisterSet().getRegister("X").getValue(); + int a = getCpu().getRegisterSet().getRegister("A").getValue(); + + if (a == LISTEN) { + + //Find the nearest Radio Tower and query it + cubot.setAction(Action.LISTENING); + + ArrayList messages = new ArrayList<>(6); + + ArrayList towers = new ArrayList<>(NpcPlugin.getRadioTowers()); //Avoid ConcurrentModificationException + for (RadioTower tower : towers) { + if (Util.manhattanDist(tower.getX(), tower.getY(), cubot.getX(), cubot.getY()) <= RadioTower.MAX_RANGE) { + //Tower is in range + messages.addAll(tower.getMessages()); + } + } + + //Write messages to memory + int offset = 0; + + for (char[] message : messages) { + + getCpu().getMemory().write(x + offset, message, 0, message.length); + offset += message.length; + } + + //Write the amount of messages received to B + getCpu().getRegisterSet().getRegister("B").setValue(messages.size()); + } + } + + @Override + public char getId() { + return HWID; + } + + @Override + public JSONObject serialise() { + JSONObject json = new JSONObject(); + json.put("hwid", (int) HWID); + json.put("cubot", cubot.getObjectId()); + + return json; + } + + public static RadioReceiverHardware deserialize(JSONObject json) { + return new RadioReceiverHardware((ControllableUnit) GameServer.INSTANCE.getGameUniverse().getObject((int) (long) json.get("cubot"))); + } +} diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioTower.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioTower.java index b225e96..54ee1d1 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioTower.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioTower.java @@ -1,4 +1,103 @@ package net.simon987.npcplugin; -public class RadioTower { +import net.simon987.server.game.GameObject; +import net.simon987.server.game.Programmable; +import net.simon987.server.game.Updatable; +import org.json.simple.JSONObject; + +import java.awt.*; +import java.util.ArrayList; + +public class RadioTower extends GameObject implements Programmable, Updatable { + + private static final int MAP_INFO = 0x1000; + + public static final int ID = 4; + + public static final int MAX_RANGE = 3; //todo load from config + + private static final int MAX_MESSAGES = 16; + + @Override + public char getMapInfo() { + return MAP_INFO; + } + + + /** + * Messages from the current tick + */ + private ArrayList messages = new ArrayList<>(4); + + /** + * Messages from the last tick + */ + private ArrayList lastMessages = new ArrayList<>(4); + + @Override + public void update() { + lastMessages = new ArrayList<>(messages); + messages.clear(); + } + + @Override + public void sendMessage(char[] message) { + + if (message.length < MAX_MESSAGES) { + messages.add(message); + } + } + + @Override + public JSONObject serialise() { + + JSONObject json = new JSONObject(); + + json.put("i", getObjectId()); + json.put("x", getX()); + json.put("y", getY()); + json.put("t", ID); + + return json; + + } + + public static RadioTower deserialize(JSONObject json) { + + RadioTower tower = new RadioTower(); + tower.setObjectId((long) json.get("i")); + tower.setX((int) (long) json.get("x")); + tower.setY((int) (long) json.get("y")); + + NpcPlugin.getRadioTowers().add(tower); + + return tower; + } + + + public ArrayList getMessages() { + return lastMessages; + } + + /** + * Get the first directly adjacent tile (starting east, going clockwise) + */ + public Point getAdjacentTile() { + + if (!getWorld().isTileBlocked(getX() + 1, getY())) { + return new Point(getX() + 1, getY()); + + } else if (!getWorld().isTileBlocked(getX(), getY() + 1)) { + return new Point(getX(), getY() + 1); + + } else if (!getWorld().isTileBlocked(getX() - 1, getY())) { + return new Point(getX() - 1, getY()); + + } else if (!getWorld().isTileBlocked(getX(), getY() - 1)) { + return new Point(getX(), getY() - 1); + } else { + return null; + } + + } } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/event/CpuInitialisationListener.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/CpuInitialisationListener.java new file mode 100644 index 0000000..f6bf356 --- /dev/null +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/CpuInitialisationListener.java @@ -0,0 +1,27 @@ +package net.simon987.npcplugin.event; + +import net.simon987.npcplugin.RadioReceiverHardware; +import net.simon987.server.assembly.CPU; +import net.simon987.server.event.CpuInitialisationEvent; +import net.simon987.server.event.GameEvent; +import net.simon987.server.event.GameEventListener; +import net.simon987.server.user.User; + +public class CpuInitialisationListener implements GameEventListener { + @Override + public Class getListenedEventType() { + return CpuInitialisationEvent.class; + } + + + @Override + public void handle(GameEvent event) { + CPU cpu = (CPU) event.getSource(); + User user = ((CpuInitialisationEvent) event).getUser(); + + RadioReceiverHardware radioHw = new RadioReceiverHardware(user.getControlledUnit()); + radioHw.setCpu(cpu); + + cpu.attachHardware(radioHw, RadioReceiverHardware.DEFAULT_ADDRESS); + } +} diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java index 6601bbf..608a40d 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java @@ -1,6 +1,8 @@ package net.simon987.npcplugin.event; import net.simon987.npcplugin.Factory; +import net.simon987.npcplugin.NpcPlugin; +import net.simon987.npcplugin.RadioTower; import net.simon987.server.GameServer; import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEventListener; @@ -8,6 +10,7 @@ import net.simon987.server.event.WorldGenerationEvent; import net.simon987.server.game.World; import net.simon987.server.logging.LogManager; +import java.awt.*; import java.util.Random; public class WorldCreationListener implements GameEventListener { @@ -31,6 +34,7 @@ public class WorldCreationListener implements GameEventListener { World world = ((WorldGenerationEvent) event).getWorld(); + outerLoopFactory: for (int x = 2; x < 12; x++) { for (int y = 2; y < 12; y++) { @@ -54,10 +58,40 @@ public class WorldCreationListener implements GameEventListener { LogManager.LOGGER.info("Spawned Factory at (" + world.getX() + ", " + world.getY() + ") (" + x + ", " + y + ")"); + break outerLoopFactory; + } + } + } + //Also spawn a radio tower in the same World + Point p = world.getRandomPassableTile(); + if (p != null) { + while (p.x == 0 || p.x == World.WORLD_SIZE - 1 || p.y == World.WORLD_SIZE - 1 || p.y == 0) { + p = world.getRandomPassableTile(); + + if (p == null) { + //World is full return; } } + + RadioTower radioTower = new RadioTower(); + + radioTower.setWorld(world); + radioTower.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId()); + radioTower.setX(p.x); + radioTower.setY(p.y); + + if (radioTower.getAdjacentTile() != null) { + //Radio Tower has adjacent tiles + world.getGameObjects().add(radioTower); + world.incUpdatable(); //In case the Factory couldn't be spawned. + + NpcPlugin.getRadioTowers().add(radioTower); + + LogManager.LOGGER.info("Spawned RadioTower at (" + world.getX() + ", " + world.getY() + + ") (" + p.x + ", " + p.y + ")"); + } } } } diff --git a/Server/src/main/java/net/simon987/server/game/Action.java b/Server/src/main/java/net/simon987/server/game/Action.java index 2b35c92..f9f037f 100644 --- a/Server/src/main/java/net/simon987/server/game/Action.java +++ b/Server/src/main/java/net/simon987/server/game/Action.java @@ -5,6 +5,7 @@ public enum Action { DIGGING, WALKING, WITHDRAWING, - DEPOSITING + DEPOSITING, + LISTENING } diff --git a/Server/src/main/java/net/simon987/server/game/ControllableUnit.java b/Server/src/main/java/net/simon987/server/game/ControllableUnit.java index 51a97f1..0478316 100644 --- a/Server/src/main/java/net/simon987/server/game/ControllableUnit.java +++ b/Server/src/main/java/net/simon987/server/game/ControllableUnit.java @@ -21,4 +21,9 @@ public interface ControllableUnit { int getEnergy(); + int getX(); + + int getY(); + + void setAction(Action listening); } diff --git a/Server/src/main/java/net/simon987/server/game/Programmable.java b/Server/src/main/java/net/simon987/server/game/Programmable.java new file mode 100644 index 0000000..3508480 --- /dev/null +++ b/Server/src/main/java/net/simon987/server/game/Programmable.java @@ -0,0 +1,7 @@ +package net.simon987.server.game; + +public interface Programmable { + + void sendMessage(char[] message); + +} diff --git a/Server/src/main/java/net/simon987/server/webserver/CodeUploadHandler.java b/Server/src/main/java/net/simon987/server/webserver/CodeUploadHandler.java index 520f6cd..422b32f 100644 --- a/Server/src/main/java/net/simon987/server/webserver/CodeUploadHandler.java +++ b/Server/src/main/java/net/simon987/server/webserver/CodeUploadHandler.java @@ -33,6 +33,13 @@ public class CodeUploadHandler implements MessageHandler { user.getUser().getCpu().getMemory().write((char) ar.origin, assembledCode, 0, assembledCode.length); user.getUser().getCpu().setCodeSegmentOffset(ar.getCodeSegmentOffset()); + //Clear keyboard buffer + if (user.getUser().getControlledUnit() != null && + user.getUser().getControlledUnit().getKeyboardBuffer() != null) { + user.getUser().getControlledUnit().getKeyboardBuffer().clear(); + } + + JSONObject response = new JSONObject(); response.put("t", "codeResponse"); response.put("bytes", ar.bytes.length);