Compare commits

..

No commits in common. "master" and "v1.4a" have entirely different histories.

424 changed files with 3687 additions and 174396 deletions

5
.gitattributes vendored
View File

@ -1,5 +0,0 @@
Server/src/main/resources/static/css/bootstrap4-neon-glow.min linguist-vendored
Server/src/main/resources/static/css/bootstrap-grid.min linguist-vendored
Server/src/main/resources/static/css/bootstrap-reboot.min linguist-vendored
Server/src/main/resources/static/js/* linguist-vendored
Server/src/main/resources/static/js/ace/* linguist-vendored

5
.gitignore vendored
View File

@ -15,8 +15,3 @@ Server/Server.iml
target/* target/*
Server/Server.iml Server/Server.iml
Server/src/main/java/META-INF/MANIFEST.MF Server/src/main/java/META-INF/MANIFEST.MF
.settings
.project
.classpath
# VSCode Workspace
.vscode/

View File

@ -1,6 +1,7 @@
FROM maven:3.5-jdk-8 FROM alpine:3.7
RUN apk add --no-cache maven openjdk8
COPY /. /app/ COPY /. /app/
WORKDIR /app WORKDIR /app
RUN mvn package RUN mvn package \
WORKDIR /app/target && cp Server/src/main/resources/config.properties /app/
CMD ["java", "-jar", "/app/target/server-1.4a.jar"] CMD ["java", "-jar", "/app/target/server-1.2a.jar"]

View File

@ -1,10 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" /> <output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" /> <output-test url="file://$MODULE_DIR$/target/test-classes" />
@ -18,40 +13,11 @@
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Server" /> <orderEntry type="module" module-name="Server" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.6" level="project" /> <orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.8.1" level="project" /> <orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongodb-driver-sync:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:bson:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongodb-driver-core:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.1.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.sparkjava:spark-core:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.25" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-server:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:3.1.0" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-http:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-util:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-io:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-webapp:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-xml:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-servlet:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-security:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-server:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-common:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-client:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-client:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-servlet:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-api:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: com.sparkjava:spark-template-velocity:2.7.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.velocity:velocity:1.7" level="project" />
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.1" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.4" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-simple:1.7.25" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" /> <orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: junit:junit:4.10" level="project" /> <orderEntry type="library" name="Maven: junit:junit:4.10" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" /> <orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />

View File

@ -7,19 +7,19 @@
<parent> <parent>
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId> <artifactId>server_root</artifactId>
<version>1.4a</version> <version>1.2a</version>
</parent> </parent>
<groupId>net.simon987.plugincubot</groupId> <groupId>net.simon987.plugincubot</groupId>
<artifactId>plugin-cubot</artifactId> <artifactId>plugin-cubot</artifactId>
<version>1.4a</version> <version>1.2a</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server</artifactId> <artifactId>server</artifactId>
<version>1.4a</version> <version>1.2a</version>
</dependency> </dependency>

View File

@ -1,155 +1,56 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import net.simon987.cubotplugin.event.CubotWalkEvent; import com.mongodb.BasicDBObject;
import net.simon987.cubotplugin.event.DeathEvent; import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.IServerConfiguration;
import net.simon987.server.assembly.CPU;
import net.simon987.server.assembly.HardwareModule;
import net.simon987.server.assembly.Memory; import net.simon987.server.assembly.Memory;
import net.simon987.server.assembly.Status; import net.simon987.server.game.*;
import net.simon987.server.assembly.exception.CancelledException;
import net.simon987.server.event.GameEvent;
import net.simon987.server.game.item.Item;
import net.simon987.server.game.item.ItemVoid;
import net.simon987.server.game.objects.*;
import net.simon987.server.user.User; import net.simon987.server.user.User;
import org.bson.Document;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import java.awt.*; import java.util.ArrayList;
import java.util.List;
import java.util.*;
public class Cubot extends GameObject implements Updatable, ControllableUnit, MessageReceiver { public class Cubot extends GameObject implements Updatable, ControllableUnit, Programmable {
private static final char MAP_INFO = 0x0200; private static final char MAP_INFO = 0x0080;
public static final int ID = 1;
public static int TYPE_ID = 2;
private int hologram = 0;
private String hologramString = "";
private HologramMode hologramMode = HologramMode.CLEARED;
private HologramMode lastHologramMode = HologramMode.CLEARED;
private int hologramColor = 0;
/** /**
* Hit points * Hit points
*/ */
private int hp; private int hp;
/** private int heldItem;
* Maximum hit points
*/
private int maxHp;
/**
* Shield points
*/
private int shield;
/**
* Maximum shield points
*/
private int maxShield;
/**
* Action that was set during the current tick. It is set to IDLE by default
*/
private Action currentAction = Action.IDLE; private Action currentAction = Action.IDLE;
/**
* Action at the end of the last tick
*/
private Action lastAction = Action.IDLE; private Action lastAction = Action.IDLE;
/**
* Status bit field that was set during the current tick. It is set to 0 by default
* <br>See CubotStatus and addStatus() method
*/
private char currentStatus;
/**
* Status bit field at the end of the last tick
*/
private char lastStatus;
/**
* Buffer of keypress codes. It is not changed between ticks and it is reset when
* the player uploads their code
*/
private ArrayList<Integer> keyboardBuffer = new ArrayList<>(); private ArrayList<Integer> keyboardBuffer = new ArrayList<>();
/**
* Buffer of console messages (also called 'internal buffer') that was set during the current tick
*/
private ArrayList<char[]> consoleMessagesBuffer = new ArrayList<>(CONSOLE_BUFFER_MAX_SIZE); private ArrayList<char[]> consoleMessagesBuffer = new ArrayList<>(CONSOLE_BUFFER_MAX_SIZE);
/**
* Buffer of console messages (also called 'internal buffer') at the end of the last tick
*/
private ArrayList<char[]> lastConsoleMessagesBuffer = new ArrayList<>(CONSOLE_BUFFER_MAX_SIZE); private ArrayList<char[]> lastConsoleMessagesBuffer = new ArrayList<>(CONSOLE_BUFFER_MAX_SIZE);
/**
* Console mode that was set during the current tick. It is set to NORMAL by default
*/
private ConsoleMode consoleMode = ConsoleMode.NORMAL; private ConsoleMode consoleMode = ConsoleMode.NORMAL;
/**
* Console mode at the end of the last tick
*/
private ConsoleMode lastConsoleMode = ConsoleMode.NORMAL; private ConsoleMode lastConsoleMode = ConsoleMode.NORMAL;
/**
* User that controls this Cubot
*/
private User parent; private User parent;
/** private int energy;
* Maximum size of the console buffer (also called 'internal buffer') private int maxEnergy;
*/
public static final int CONSOLE_BUFFER_MAX_SIZE = 40;
/** private static final float SOLAR_PANEL_MULTIPLIER = 1;
* List of attached hardware, 'modules' private static final int CONSOLE_BUFFER_MAX_SIZE = 40;
*/
private Map<Integer, HardwareModule> hardwareAddresses = new HashMap<>();
private Map<Class<? extends HardwareModule>, Integer> hardwareModules = new HashMap<>();
/**
* Cubot's brain box
*/
private CPU cpu;
public enum ConsoleMode {
/**
* Used by the ComPort hardware - clears the console screen (client-side)
*/
CLEAR,
/**
* No specific client-side action
*/
NORMAL
}
public Cubot() { public Cubot() {
} }
public Cubot(Document document) {
super(document);
hp = document.getInteger("hp");
shield = document.getInteger("shield");
setDirection(Direction.getDirection(document.getInteger("direction")));
IServerConfiguration config = GameServer.INSTANCE.getConfig();
maxHp = config.getInt("cubot_max_hp");
maxShield = config.getInt("cubot_max_shield");
try {
cpu = CPU.deserialize((Document) document.get("cpu"), this);
ArrayList hardwareList = (ArrayList) document.get("hardware");
for (Object serialisedHw : hardwareList) {
HardwareModule hardware = GameServer.INSTANCE.getRegistry().deserializeHardware((Document) serialisedHw, this);
hardware.setCpu(cpu);
attachHardware(hardware, ((Document) serialisedHw).getInteger("address"));
}
} catch (CancelledException e) {
e.printStackTrace();
}
}
@Override @Override
public char getMapInfo() { public char getMapInfo() {
return MAP_INFO; return MAP_INFO;
@ -157,13 +58,14 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit, Me
@Override @Override
public void update() { public void update() {
storeEnergy((int) (SOLAR_PANEL_MULTIPLIER * GameServer.INSTANCE.getDayNightCycle().getSunIntensity()));
if (currentAction == Action.WALKING) { if (currentAction == Action.WALKING) {
if (spendEnergy(100)) { if (spendEnergy(100)) {
if (!incrementLocation()) { if (!incrementLocation()) {
//Couldn't walk //Couldn't walk
currentAction = Action.IDLE; currentAction = Action.IDLE;
}else{
GameServer.INSTANCE.getEventDispatcher().dispatch(new CubotWalkEvent(this));
} }
} else { } else {
currentAction = Action.IDLE; currentAction = Action.IDLE;
@ -179,6 +81,8 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit, Me
currentAction = Action.IDLE; currentAction = Action.IDLE;
//Same principle for hologram //Same principle for hologram
lastHologramMode = hologramMode;
hologramMode = HologramMode.CLEARED;
//And the console //And the console
lastConsoleMode = consoleMode; lastConsoleMode = consoleMode;
@ -186,118 +90,79 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit, Me
lastConsoleMessagesBuffer = new ArrayList<>(consoleMessagesBuffer); lastConsoleMessagesBuffer = new ArrayList<>(consoleMessagesBuffer);
consoleMessagesBuffer.clear(); consoleMessagesBuffer.clear();
//And the status..
lastStatus = currentStatus;
currentStatus = 0;
for (HardwareModule module : hardwareAddresses.values()) {
module.update();
}
} }
@Override @Override
public JSONObject jsonSerialise() { public JSONObject serialise() {
JSONObject json = super.jsonSerialise(); JSONObject json = new JSONObject();
json.put("i", getObjectId());
json.put("t", ID);
json.put("x", getX());
json.put("y", getY());
json.put("direction", getDirection().ordinal()); json.put("direction", getDirection().ordinal());
CubotInventory inv = (CubotInventory) getHardware(CubotInventory.class);
int heldItem = inv.getInventory().getOrDefault(inv.getPosition(), new ItemVoid()).getId();
json.put("heldItem", heldItem); json.put("heldItem", heldItem);
json.put("hp", hp); json.put("hp", hp);
json.put("shield", shield);
json.put("action", lastAction.ordinal()); json.put("action", lastAction.ordinal());
json.put("holo", hologram);
json.put("holoStr", hologramString);
json.put("holoMode", lastHologramMode.ordinal());
json.put("holoC", hologramColor);
json.put("energy", energy);
if (parent != null) { if (parent != null) {
json.put("parent", parent.getUsername()); //Only used client-side for now json.put("parent", parent.getUsername()); //Only used client-side for now
} }
for (HardwareModule module : hardwareAddresses.values()) {
JSONObject hwJson = module.jsonSerialise();
if (hwJson != null) {
json.put(module.getClass().getName(), hwJson);
}
}
return json; return json;
} }
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document dbObject = super.mongoSerialise(); BasicDBObject dbObject = new BasicDBObject();
dbObject.put("i", getObjectId());
dbObject.put("t", ID);
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("direction", getDirection().ordinal()); dbObject.put("direction", getDirection().ordinal());
dbObject.put("heldItem", heldItem);
dbObject.put("hp", hp); dbObject.put("hp", hp);
dbObject.put("shield", shield);
dbObject.put("action", lastAction.ordinal()); dbObject.put("action", lastAction.ordinal());
dbObject.put("holo", hologram);
dbObject.put("holoStr", hologramString);
dbObject.put("holoMode", lastHologramMode.ordinal());
dbObject.put("holoC", hologramColor);
dbObject.put("energy", energy);
if (parent != null) { if (parent != null) {
dbObject.put("parent", parent.getUsername()); //Only used client-side for now dbObject.put("parent", parent.getUsername()); //Only used client-side for now
} }
List<Document> hardwareList = new ArrayList<>();
for (Integer address : hardwareAddresses.keySet()) {
HardwareModule hardware = hardwareAddresses.get(address);
Document serialisedHw = hardware.mongoSerialise();
serialisedHw.put("address", address);
hardwareList.add(serialisedHw);
}
dbObject.put("hardware", hardwareList);
dbObject.put("cpu", cpu.mongoSerialise());
return dbObject; return dbObject;
} }
/** public static Cubot deserialize(DBObject obj) {
* Reset to 'factory settings', as it were when it was first created
*/ Cubot cubot = new Cubot();
private void reset() { cubot.setObjectId((long) obj.get("i"));
setDead(false); cubot.setX((int) obj.get("x"));
setHp(maxHp); cubot.setY((int) obj.get("y"));
setShield(0); cubot.hp = (int) obj.get("hp");
setEnergy(((CubotBattery) getHardware(CubotBattery.class)).getMaxEnergy()); cubot.setDirection(Direction.getDirection((int) obj.get("direction")));
clearKeyboardBuffer(); cubot.heldItem = (int) obj.get("heldItem");
consoleMessagesBuffer.clear(); cubot.energy = (int) obj.get("energy");
lastConsoleMessagesBuffer.clear(); cubot.maxEnergy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
currentStatus = 0;
lastStatus = 0; return cubot;
addStatus(CubotStatus.FACTORY_NEW);
for (HardwareModule module : hardwareAddresses.values()) {
module.reset();
}
} }
@Override public void setHeldItem(int heldItem) {
public boolean onDeadCallback() { this.heldItem = heldItem;
GameEvent event = new DeathEvent(this); }
GameServer.INSTANCE.getEventDispatcher().dispatch(event);
reset(); public int getHeldItem() {
return heldItem;
//Teleport to spawn point
this.getWorld().removeObject(this);
this.getWorld().decUpdatable();
IServerConfiguration config = GameServer.INSTANCE.getConfig();
Random random = new Random();
int spawnX = config.getInt("new_user_worldX") + random.nextInt(5);
int spawnY = config.getInt("new_user_worldY") + random.nextInt(5);
String dimension = config.getString("new_user_dimension");
this.setWorld(GameServer.INSTANCE.getGameUniverse().getWorld(spawnX, spawnY, true, dimension));
Point point = this.getWorld().getRandomPassableTile();
this.setX(point.x);
this.setY(point.y);
this.getWorld().addObject(this);
this.getWorld().incUpdatable();
return true;
} }
@Override @Override
@ -318,15 +183,14 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit, Me
this.currentAction = currentAction; this.currentAction = currentAction;
} }
public void setParent(User parent) {
this.parent = parent;
}
@Override
public User getParent() { public User getParent() {
return parent; return parent;
} }
public void setParent(User parent) {
this.parent = parent;
}
public Action getAction() { public Action getAction() {
return lastAction; return lastAction;
} }
@ -335,83 +199,51 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit, Me
return currentAction; return currentAction;
} }
public void setHologram(int hologram) {
this.hologram = hologram;
}
public void setHologramString(String hologramString) {
this.hologramString = hologramString;
}
public int getEnergy() { public int getEnergy() {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class); return energy;
return battery.getEnergy();
} }
public void setEnergy(int energy) { public void setEnergy(int energy) {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class); this.energy = energy;
battery.setEnergy(energy);
} }
public boolean spendEnergy(int amount) { public boolean spendEnergy(int spent) {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class); if (energy - spent < 0) {
if (battery.getEnergy() - amount < 0) {
return false; return false;
} else { } else {
battery.setEnergy(battery.getEnergy() - amount); energy -= spent;
return true; return true;
} }
} }
public void storeEnergy(int amount) { public void storeEnergy(int qty) {
energy = Math.min(energy + qty, maxEnergy);
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class);
battery.setEnergy(Math.min(battery.getEnergy() + amount, battery.getMaxEnergy()));
} }
public void setMaxEnergy(int maxEnergy) { public void setMaxEnergy(int maxEnergy) {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class); this.maxEnergy = maxEnergy;
battery.setMaxEnergy(maxEnergy);
} }
public int getMaxEnergy() { public int getMaxEnergy() {
CubotBattery battery = (CubotBattery) getHardware(CubotBattery.class); return maxEnergy;
return battery.getMaxEnergy();
}
public int getShield() {
return shield;
}
public void setShield(int shield) {
this.shield = shield;
}
public boolean chargeShield(int amount) {
amount = Math.min(amount, maxShield - shield);
int energySpent = amount * CubotShield.COST;
if(spendEnergy(energySpent)) {
shield += amount;
return true;
} else {
return false;
}
}
/**
* Damages shield by amount.
*
* Return damage that broke through the shield.
*/
public int damageShield(int amount) {
int after = shield - amount;
if(after < 0) {
shield = 0;
return -after;
}
shield = after;
return 0;
} }
@Override @Override
public Memory getFloppyData() { public Memory getFloppyData() {
CubotFloppyDrive drive = (CubotFloppyDrive) getHardware(CubotFloppyDrive.class); CubotFloppyDrive drive = ((CubotFloppyDrive) getParent().getCpu().getHardware(CubotFloppyDrive.DEFAULT_ADDRESS));
if (drive.getFloppy() != null) { if (drive.getFloppy() != null) {
return drive.getFloppy().getMemory(); return drive.getFloppy().getMemory();
@ -425,6 +257,22 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit, Me
return false; return false;
} }
public void setHologramMode(HologramMode hologramMode) {
this.hologramMode = hologramMode;
}
public enum HologramMode {
CLEARED,
HEX,
STRING,
DEC
}
public enum ConsoleMode {
CLEAR,
NORMAL
}
@Override @Override
public void setAction(Action action) { public void setAction(Action action) {
currentAction = action; currentAction = action;
@ -454,152 +302,7 @@ public class Cubot extends GameObject implements Updatable, ControllableUnit, Me
this.consoleMode = consoleMode; this.consoleMode = consoleMode;
} }
public void addStatus(CubotStatus status) { public void setHologramColor(int hologramColor) {
this.hologramColor = hologramColor;
currentStatus |= status.val;
}
public void removeStatus(CubotStatus status) {
currentStatus &= (~status.val);
}
public char getStatus() {
return lastStatus;
}
/**
* Currently has no effect
*/
@Override
public void setHealRate(int hp) {
//no op
}
@Override
public int getHp() {
return hp;
}
@Override
public void setHp(int hp) {
this.hp = hp;
}
@Override
public int getMaxHp() {
return maxHp;
}
@Override
public void setMaxHp(int hp) {
this.maxHp = hp;
}
public int getMaxShield() {
return maxShield;
}
public void setMaxShield(int maxShield) {
this.maxShield = maxShield;
}
@Override
public void heal(int amount) {
hp += amount;
//Can't heal above max
if (hp > maxHp) {
hp = maxHp;
}
}
@Override
public void damage(int amount) {
//Damage shield first
int hullDamage = damageShield(amount);
hp -= hullDamage;
if (hp <= 0) {
setDead(true);
}
}
@Override
public void attachHardware(HardwareModule hardware, int address) {
hardwareAddresses.put(address, hardware);
hardwareModules.put(hardware.getClass(), address);
}
@Override
public void detachHardware(int address) {
hardwareAddresses.remove(address);
Class<? extends HardwareModule> toRemove = null;
for (Class<? extends HardwareModule> clazz : hardwareModules.keySet()) {
if (hardwareModules.get(clazz) == address) {
toRemove = clazz;
}
}
hardwareModules.remove(toRemove);
}
@Override
public boolean hardwareInterrupt(int address, Status status) {
HardwareModule hardware = hardwareAddresses.get(address);
if (hardware != null) {
hardware.handleInterrupt(status);
return true;
} else {
return false;
}
}
@Override
public int hardwareQuery(int address) {
HardwareModule hardware = hardwareAddresses.get(address);
if (hardware != null) {
return hardware.getId();
} else {
return 0;
}
}
public HardwareModule getHardware(Class<? extends HardwareModule> clazz) {
return hardwareAddresses.get(hardwareModules.get(clazz));
}
public HardwareModule getHardware(int address) {
return hardwareAddresses.get(address);
}
@Override
public CPU getCpu() {
return cpu;
}
public void setCpu(CPU cpu) {
this.cpu = cpu;
}
@Override
public void giveItem(Item item) {
//Overwrite item at current position
((CubotInventory) getHardware(CubotInventory.class)).putItem(item);
}
@Override
public String toString() {
StringBuilder str = new StringBuilder(super.toString());
str.append("\nHardware: \n");
for (Integer i : hardwareAddresses.keySet()) {
str.append(String.format("%04X", i)).append(":").append(hardwareAddresses.get(i)).append("\n");
}
return str.toString();
} }
} }

View File

@ -1,13 +1,12 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.assembly.HardwareModule; import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
import org.json.simple.JSONObject;
public class CubotBattery extends HardwareModule { public class CubotBattery extends CpuHardware {
public static final int DEFAULT_ADDRESS = 0x000A; public static final int DEFAULT_ADDRESS = 0x000A;
@ -15,38 +14,13 @@ public class CubotBattery extends HardwareModule {
* Hardware ID (Should be unique) * Hardware ID (Should be unique)
*/ */
public static final char HWID = 0x000A; public static final char HWID = 0x000A;
/**
* Solar panel multiplier
* <br>TODO: Set this constant in dimension
*/
private static final float SOLAR_PANEL_MULTIPLIER = 1;
/**
* Energy units in kJ
*/
private int energy;
/**
* Maximum energy units in kJ
*/
private int maxEnergy;
private Cubot cubot;
private static final int BATTERY_POLL = 1; private static final int BATTERY_POLL = 1;
private static final int BATTERY_GET_MAX_CAPACITY = 2; private static final int BATTERY_GET_MAX_CAPACITY = 2;
public CubotBattery(ControllableUnit unit) { public CubotBattery(Cubot cubot) {
super(null, unit); this.cubot = cubot;
energy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
maxEnergy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
}
public CubotBattery(Document document, ControllableUnit cubot) {
super(document, cubot);
energy = document.getInteger("energy");
maxEnergy = document.getInteger("max_energy");
} }
@Override @Override
@ -55,12 +29,14 @@ public class CubotBattery extends HardwareModule {
int a = getCpu().getRegisterSet().getRegister("A").getValue(); int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == BATTERY_POLL) { if (a == BATTERY_POLL) {
getCpu().getRegisterSet().getRegister("B").setValue(energy); getCpu().getRegisterSet().getRegister("B").setValue(cubot.getEnergy());
} else if (a == BATTERY_GET_MAX_CAPACITY) { } else if (a == BATTERY_GET_MAX_CAPACITY) {
getCpu().getRegisterSet().getRegister("B").setValue(maxEnergy); getCpu().getRegisterSet().getRegister("B").setValue(cubot.getMaxEnergy());
} else if (a == 0xFFFF) {
cubot.setEnergy(cubot.getMaxEnergy());
} }
} }
@Override @Override
@ -69,52 +45,19 @@ public class CubotBattery extends HardwareModule {
} }
@Override @Override
public JSONObject jsonSerialise() { public BasicDBObject mongoSerialise() {
JSONObject json = new JSONObject();
json.put("energy", energy); BasicDBObject dbObject = new BasicDBObject();
return json; dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
} }
@Override
public JSONObject debugJsonSerialise() {
JSONObject json = jsonSerialise();
json.put("max_energy", maxEnergy); public static CubotBattery deserialize(DBObject obj) {
return new CubotBattery((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
return json;
} }
@Override
public Document mongoSerialise() {
Document document = super.mongoSerialise();
document.put("energy", energy);
document.put("max_energy", maxEnergy);
return document;
}
public int getEnergy() {
return energy;
}
public void setEnergy(int energy) {
this.energy = energy;
}
public int getMaxEnergy() {
return maxEnergy;
}
public void setMaxEnergy(int maxEnergy) {
this.maxEnergy = maxEnergy;
}
@Override
public void update() {
energy = Math.min(maxEnergy,
energy + (int) (SOLAR_PANEL_MULTIPLIER * GameServer.INSTANCE.getDayNightCycle().getSunIntensity()));
}
} }

View File

@ -1,32 +1,31 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import net.simon987.server.assembly.HardwareModule; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit; import net.simon987.server.game.GameObject;
import net.simon987.server.game.objects.GameObject; import net.simon987.server.game.Programmable;
import net.simon987.server.game.objects.MessageReceiver;
import org.bson.Document;
import java.awt.*; import java.awt.*;
import java.util.ArrayList; import java.util.ArrayList;
public class CubotComPort extends HardwareModule { public class CubotComPort extends CpuHardware {
public static final char HWID = 0xD; public static final char HWID = 0xD;
public static final int DEFAULT_ADDRESS = 0xD; public static final int DEFAULT_ADDRESS = 0xD;
private Cubot cubot;
private static final int COMPORT_BUFFER_CLEAR = 0; private static final int COMPORT_BUFFER_CLEAR = 0;
private static final int COMPORT_POLL = 1; private static final int COMPORT_POLL = 1;
private static final int COMPORT_FRONT_PORT_OUT = 2; private static final int COMPORT_FRONT_PORT_OUT = 2;
private static final int COMPORT_SELF_OUT = 3; private static final int COMPORT_SELF_OUT = 3;
private static final int COMPORT_CONSOLE_CLEAR = 4; private static final int COMPORT_CONSOLE_CLEAR = 4;
public CubotComPort(ControllableUnit unit) { public CubotComPort(Cubot cubot) {
super(null, unit); this.cubot = cubot;
}
public CubotComPort(Document document, ControllableUnit cubot) {
super(document, cubot);
} }
private static final int MESSAGE_LENGTH = 8; private static final int MESSAGE_LENGTH = 8;
@ -38,23 +37,21 @@ public class CubotComPort extends HardwareModule {
if (a == COMPORT_BUFFER_CLEAR) { if (a == COMPORT_BUFFER_CLEAR) {
unit.getConsoleMessagesBuffer().clear(); cubot.getConsoleMessagesBuffer().clear();
} else if (a == COMPORT_CONSOLE_CLEAR) { } else if (a == COMPORT_CONSOLE_CLEAR) {
if (unit instanceof Cubot) { cubot.setConsoleMode(Cubot.ConsoleMode.CLEAR);
((Cubot) unit).setConsoleMode(Cubot.ConsoleMode.CLEAR);
}
} else if (a == COMPORT_POLL) { } else if (a == COMPORT_POLL) {
if (unit.spendEnergy(4)) { if (cubot.spendEnergy(4)) {
int x = getCpu().getRegisterSet().getRegister("X").getValue(); int x = getCpu().getRegisterSet().getRegister("X").getValue();
//Read all messages in the console buffer to memory at X //Read all messages in the console buffer to memory at X
for (char[] message : unit.getConsoleMessagesBuffer()) { for (char[] message : cubot.getConsoleMessagesBuffer()) {
if (x + MESSAGE_LENGTH >= getCpu().getMemory().getWords().length) { if (x + MESSAGE_LENGTH >= getCpu().getMemory().getWords().length) {
//todo set interrupt ? //todo set interrupt ?
getCpu().getStatus().setErrorFlag(true); getCpu().getStatus().setErrorFlag(true);
@ -64,19 +61,19 @@ public class CubotComPort extends HardwareModule {
} }
//Set B = number of messages //Set B = number of messages
getCpu().getRegisterSet().getRegister("B").setValue(unit.getConsoleMessagesBuffer().size()); getCpu().getRegisterSet().getRegister("B").setValue(cubot.getConsoleMessagesBuffer().size());
} }
} else if (a == COMPORT_FRONT_PORT_OUT) { } else if (a == COMPORT_FRONT_PORT_OUT) {
if (unit.spendEnergy(5)) { if (cubot.spendEnergy(20)) {
//Get object directly in front of the Cubot //Get object directly in front of the Cubot
Point frontTile = unit.getFrontTile(); Point frontTile = cubot.getFrontTile();
//Todo will have to add getGameObjectsBlockingAt to enable Factory //Todo will have to add getGameObjectsBlockingAt to enable Factory
ArrayList<GameObject> objects = unit.getWorld().getGameObjectsAt(frontTile.x, frontTile.y); ArrayList<GameObject> objects = cubot.getWorld().getGameObjectsAt(frontTile.x, frontTile.y);
if (objects.size() > 0 && objects.get(0) instanceof MessageReceiver) { if (objects.size() > 0 && objects.get(0) instanceof Programmable) {
int x = getCpu().getRegisterSet().getRegister("X").getValue(); int x = getCpu().getRegisterSet().getRegister("X").getValue();
@ -89,9 +86,9 @@ public class CubotComPort extends HardwareModule {
char[] message = new char[MESSAGE_LENGTH]; char[] message = new char[MESSAGE_LENGTH];
System.arraycopy(getCpu().getMemory().getWords(), x, message, 0, MESSAGE_LENGTH); System.arraycopy(getCpu().getMemory().getWords(), x, message, 0, MESSAGE_LENGTH);
//Send it to the MessageReceiver object //Send it to the Programmable object
getCpu().getRegisterSet().getRegister("B").setValue( getCpu().getRegisterSet().getRegister("B").setValue(
((MessageReceiver) objects.get(0)).sendMessage(message) ? 1 : 0); ((Programmable) objects.get(0)).sendMessage(message) ? 1 : 0);
return; return;
} }
} }
@ -101,7 +98,7 @@ public class CubotComPort extends HardwareModule {
} else if (a == COMPORT_SELF_OUT) { } else if (a == COMPORT_SELF_OUT) {
if (unit.spendEnergy(1)) { if (cubot.spendEnergy(1)) {
int x = getCpu().getRegisterSet().getRegister("X").getValue(); int x = getCpu().getRegisterSet().getRegister("X").getValue();
@ -114,17 +111,34 @@ public class CubotComPort extends HardwareModule {
//Get MESSAGE_LENGTH-word message pointed by X //Get MESSAGE_LENGTH-word message pointed by X
char[] message = new char[MESSAGE_LENGTH]; char[] message = new char[MESSAGE_LENGTH];
System.arraycopy(getCpu().getMemory().getWords(), x, message, 0, MESSAGE_LENGTH); System.arraycopy(getCpu().getMemory().getWords(), x, message, 0, MESSAGE_LENGTH);
getCpu().getRegisterSet().getRegister("B").setValue(unit.sendMessage(message) ? 1 : 0); getCpu().getRegisterSet().getRegister("B").setValue(cubot.sendMessage(message) ? 1 : 0);
return; return;
} }
} }
getCpu().getRegisterSet().getRegister("B").setValue(0); //Failed getCpu().getRegisterSet().getRegister("B").setValue(0); //Failed
} }
} }
@Override @Override
public char getId() { public char getId() {
return HWID; return HWID;
} }
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotComPort deserialize(DBObject obj) {
return new CubotComPort((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
} }

View File

@ -1,46 +0,0 @@
package net.simon987.cubotplugin;
import net.simon987.server.assembly.HardwareModule;
import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
public class CubotCore extends HardwareModule {
public static final int DEFAULT_ADDRESS = 0x000E;
/**
* Hardware ID (Should be unique)
*/
public static final char HWID = 0x000E;
private static final int CORE_STATUS_POLL = 1;
private static final int CORE_HULL_POLL = 2;
public CubotCore(ControllableUnit unit) {
super(null, unit);
}
public CubotCore(Document document, ControllableUnit unit) {
super(document, unit);
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == CORE_STATUS_POLL) {
if (unit instanceof Cubot) {
getCpu().getRegisterSet().getRegister("B").setValue(((Cubot) unit).getStatus());
}
} else if (a == CORE_HULL_POLL) {
getCpu().getRegisterSet().getRegister("B").setValue(unit.getHp());
}
}
@Override
public char getId() {
return HWID;
}
}

View File

@ -1,14 +1,14 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import net.simon987.server.assembly.HardwareModule; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.item.Item; import net.simon987.server.game.Action;
import net.simon987.server.game.objects.Action; import net.simon987.server.game.TileMap;
import net.simon987.server.game.objects.ControllableUnit;
import net.simon987.server.game.world.Tile;
import org.bson.Document;
public class CubotDrill extends HardwareModule { public class CubotDrill extends CpuHardware {
/** /**
* Hardware ID (Should be unique) * Hardware ID (Should be unique)
@ -20,12 +20,10 @@ public class CubotDrill extends HardwareModule {
private static final int DRILL_POLL = 1; private static final int DRILL_POLL = 1;
private static final int DRILL_GATHER = 2; // simplified gather private static final int DRILL_GATHER = 2; // simplified gather
public CubotDrill(ControllableUnit unit) { private Cubot cubot;
super(null, unit);
}
public CubotDrill(Document document, ControllableUnit cubot) { public CubotDrill(Cubot cubot) {
super(document, cubot); this.cubot = cubot;
} }
@Override @Override
@ -43,18 +41,38 @@ public class CubotDrill extends HardwareModule {
} else if (a == DRILL_GATHER) { } else if (a == DRILL_GATHER) {
if (unit.spendEnergy(1400)) { if (cubot.spendEnergy(1400)) {
if (unit.getCurrentAction() == Action.IDLE) { if (cubot.getCurrentAction() == Action.IDLE) {
int tile = cubot.getWorld().getTileMap().getTileAt(cubot.getX(), cubot.getY());
Tile tile = unit.getWorld().getTileMap().getTileAt(unit.getX(), unit.getY()); if (tile == TileMap.IRON_TILE) {
cubot.setHeldItem(TileMap.ITEM_IRON);
cubot.setCurrentAction(Action.DIGGING);
} else if (tile == TileMap.COPPER_TILE) {
cubot.setHeldItem(TileMap.ITEM_COPPER);
cubot.setCurrentAction(Action.DIGGING);
Item newItem = tile.drill();
if (newItem != null) {
unit.setCurrentAction(Action.DIGGING);
unit.giveItem(newItem);
} }
} }
} }
} }
} }
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotDrill deserialize(DBObject obj) {
return new CubotDrill((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
} }

View File

@ -1,10 +1,12 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
public class CubotFloppyDrive extends CubotHardwareModule { public class CubotFloppyDrive extends CpuHardware {
/** /**
* Hardware ID (Should be unique) * Hardware ID (Should be unique)
@ -17,24 +19,15 @@ public class CubotFloppyDrive extends CubotHardwareModule {
private static final int FLOPPY_READ_SECTOR = 2; private static final int FLOPPY_READ_SECTOR = 2;
private static final int FLOPPY_WRITE_SECTOR = 3; private static final int FLOPPY_WRITE_SECTOR = 3;
private Cubot cubot;
private FloppyDisk floppyDisk; private FloppyDisk floppyDisk;
public CubotFloppyDrive(Cubot cubot) { public CubotFloppyDrive(Cubot cubot) {
super(cubot); this.cubot = cubot;
floppyDisk = new FloppyDisk(); floppyDisk = new FloppyDisk();
} }
public CubotFloppyDrive(Document document, ControllableUnit cubot) {
super(document, cubot);
if (document.containsKey("floppy")) {
floppyDisk = new FloppyDisk((Document) document.get("floppy"));
} else {
floppyDisk = new FloppyDisk();
}
}
@Override @Override
public void handleInterrupt(Status status) { public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue(); int a = getCpu().getRegisterSet().getRegister("A").getValue();
@ -58,7 +51,7 @@ public class CubotFloppyDrive extends CubotHardwareModule {
int x = getCpu().getRegisterSet().getRegister("X").getValue(); int x = getCpu().getRegisterSet().getRegister("X").getValue();
int y = getCpu().getRegisterSet().getRegister("Y").getValue(); int y = getCpu().getRegisterSet().getRegister("Y").getValue();
floppyDisk.readSector(x, cubot.getCpu().getMemory(), y); floppyDisk.readSector(x, cubot.getParent().getCpu().getMemory(), y);
} }
} }
@ -73,7 +66,7 @@ public class CubotFloppyDrive extends CubotHardwareModule {
int x = getCpu().getRegisterSet().getRegister("X").getValue(); int x = getCpu().getRegisterSet().getRegister("X").getValue();
int y = getCpu().getRegisterSet().getRegister("Y").getValue(); int y = getCpu().getRegisterSet().getRegister("Y").getValue();
floppyDisk.writeSector(x, cubot.getCpu().getMemory(), y); floppyDisk.writeSector(x, cubot.getParent().getCpu().getMemory(), y);
} }
} }
} }
@ -85,6 +78,31 @@ public class CubotFloppyDrive extends CubotHardwareModule {
return HWID; return HWID;
} }
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
if (floppyDisk != null) {
dbObject.put("floppy", floppyDisk.mongoSerialise());
}
return dbObject;
}
public static CubotFloppyDrive deserialize(DBObject obj) {
CubotFloppyDrive drive = new CubotFloppyDrive((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
if (obj.containsField("floppy")) {
drive.floppyDisk = FloppyDisk.deserialise((DBObject) obj.get("floppy"));
}
return drive;
}
public FloppyDisk getFloppy() { public FloppyDisk getFloppy() {
return floppyDisk; return floppyDisk;

View File

@ -1,19 +0,0 @@
package net.simon987.cubotplugin;
import net.simon987.server.assembly.HardwareModule;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
public abstract class CubotHardwareModule extends HardwareModule {
protected Cubot cubot;
public CubotHardwareModule(Document document, ControllableUnit cubot) {
this.cubot = (Cubot) cubot;
}
public CubotHardwareModule(Cubot cubot) {
this.cubot = cubot;
}
}

View File

@ -1,19 +1,23 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import net.simon987.server.assembly.HardwareModule; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
import org.json.simple.JSONObject;
public class CubotHologram extends HardwareModule { public class CubotHologram extends CpuHardware {
/** /**
* Hardware ID (Should be unique) * Hardware ID (Should be unique)
*/ */
static final char HWID = 0x0009; static final char HWID = 0x0009;
public static final int DEFAULT_ADDRESS = 9; public static final int DEFAULT_ADDRESS = 9;
private Cubot cubot;
private static final int HOLO_CLEAR = 0; private static final int HOLO_CLEAR = 0;
private static final int HOLO_DISPLAY_HEX = 1; private static final int HOLO_DISPLAY_HEX = 1;
private static final int HOLO_DISPLAY_STRING = 2; private static final int HOLO_DISPLAY_STRING = 2;
@ -22,27 +26,8 @@ public class CubotHologram extends HardwareModule {
private static final int STR_MAX_LEN = 8; private static final int STR_MAX_LEN = 8;
private int displayValue = 0; public CubotHologram(Cubot cubot) {
private String displayString = ""; this.cubot = cubot;
private HologramMode mode = HologramMode.CLEARED;
private HologramMode lastMode = HologramMode.CLEARED;
/**
* Hologram color code. Format is handled by the client
*/
private int displayColor = 0;
public CubotHologram(ControllableUnit unit) {
super(null, unit);
}
public CubotHologram(Document document, ControllableUnit cubot) {
super(document, cubot);
displayValue = document.getInteger("value");
displayColor = document.getInteger("color");
displayString = document.getString("string");
mode = HologramMode.values()[document.getInteger("mode")];
} }
@Override @Override
@ -51,10 +36,11 @@ public class CubotHologram extends HardwareModule {
char a = getCpu().getRegisterSet().getRegister("A").getValue(); char a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == HOLO_CLEAR) { if (a == HOLO_CLEAR) {
mode = HologramMode.CLEARED; cubot.setHologramMode(Cubot.HologramMode.CLEARED);
} else if (a == HOLO_DISPLAY_HEX) { } else if (a == HOLO_DISPLAY_HEX) {
displayValue = getCpu().getRegisterSet().getRegister("B").getValue(); char b = getCpu().getRegisterSet().getRegister("B").getValue();
mode = HologramMode.HEX; cubot.setHologram(b);
cubot.setHologramMode(Cubot.HologramMode.HEX);
} else if (a == HOLO_DISPLAY_STRING) { } else if (a == HOLO_DISPLAY_STRING) {
char x = getCpu().getRegisterSet().getRegister("X").getValue(); char x = getCpu().getRegisterSet().getRegister("X").getValue();
//Display zero-terminated string starting at X (max 8 chars) //Display zero-terminated string starting at X (max 8 chars)
@ -72,20 +58,21 @@ public class CubotHologram extends HardwareModule {
} }
} }
displayString = holoString.toString(); cubot.setHologramString(holoString.toString());
mode = HologramMode.STRING; cubot.setHologramMode(Cubot.HologramMode.STRING);
} else if (a == HOLO_DISPLAY_DEC) { } else if (a == HOLO_DISPLAY_DEC) {
//Display decimal number //Display decimal number
displayValue = getCpu().getRegisterSet().getRegister("B").getValue(); char b = getCpu().getRegisterSet().getRegister("B").getValue();
mode = HologramMode.DEC; cubot.setHologram(b);
cubot.setHologramMode(Cubot.HologramMode.DEC);
} else if (a == HOLO_DISPLAY_COLOR) { } else if (a == HOLO_DISPLAY_COLOR) {
if (unit.spendEnergy(4)) { if (cubot.spendEnergy(4)) {
int b = getCpu().getRegisterSet().getRegister("B").getValue(); int b = getCpu().getRegisterSet().getRegister("B").getValue();
int c = getCpu().getRegisterSet().getRegister("C").getValue(); int c = getCpu().getRegisterSet().getRegister("C").getValue();
displayColor = (c | (b << 16)); //B:C cubot.setHologramColor((c | (b << 16))); //B:C
} }
} }
@ -96,69 +83,19 @@ public class CubotHologram extends HardwareModule {
return HWID; return HWID;
} }
@Override public static CubotHologram deserialize(DBObject obj) {
public Document mongoSerialise() { return new CubotHologram((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
Document document = super.mongoSerialise();
document.put("color", displayColor);
document.put("value", displayValue);
document.put("string", displayString);
document.put("mode", lastMode.ordinal());
return document;
} }
@Override @Override
public JSONObject debugJsonSerialise() { public BasicDBObject mongoSerialise() {
JSONObject json = jsonSerialise();
json.put("lastmode", mode); BasicDBObject dbObject = new BasicDBObject();
return json; dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
} }
@Override
public JSONObject jsonSerialise() {
JSONObject json = new JSONObject();
json.put("color", displayColor);
json.put("value", displayValue);
json.put("string", displayString);
json.put("mode", lastMode.ordinal());
return json;
}
private enum HologramMode {
/**
* Display nothing
*/
CLEARED,
/**
* Display value as hexadecimal in format 0x0000
*/
HEX,
/**
* Display string
*/
STRING,
/**
* Display value as decimal
*/
DEC
}
@Override
public void reset() {
displayValue = 0;
displayColor = 0;
displayString = "";
mode = HologramMode.CLEARED;
}
@Override
public void update() {
lastMode = mode;
mode = HologramMode.CLEARED;
}
} }

View File

@ -1,16 +1,12 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.assembly.HardwareModule; import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.item.Item;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
import java.util.HashMap; public class CubotInventory extends CpuHardware {
import java.util.Map;
public class CubotInventory extends HardwareModule {
/** /**
* Hardware ID (Should be unique) * Hardware ID (Should be unique)
@ -19,63 +15,13 @@ public class CubotInventory extends HardwareModule {
public static final int DEFAULT_ADDRESS = 6; public static final int DEFAULT_ADDRESS = 6;
private Cubot cubot;
private static final int INV_CLEAR = 0; private static final int INV_CLEAR = 0;
private static final int INV_POLL = 1; private static final int INV_POLL = 1;
private static final int INV_SEEK = 2;
private static final int INV_SCAN = 3;
private int inventorySize = 4; //TODO: load from config public CubotInventory(Cubot cubot) {
private Map<Integer, Item> inventory; this.cubot = cubot;
private int position = 0;
public CubotInventory(ControllableUnit unit) {
super(null, unit);
inventory = new HashMap<>();
}
public CubotInventory(Document document, ControllableUnit cubot) {
super(document, cubot);
position = document.getInteger("position");
inventorySize = document.getInteger("size");
inventory = new HashMap<>();
for (String i : ((Map<String, Document>) document.get("inventory")).keySet()) {
inventory.put(Integer.valueOf(i),
GameServer.INSTANCE.getRegistry().deserializeItem(((Map<String, Document>) document.get("inventory")).get(i)));
}
}
public void putItem(Item item) {
inventory.put(position, item);
}
private void scanItem() {
int x = getCpu().getRegisterSet().getRegister("X").getValue();
Item item = inventory.get(position);
item.digitize(unit.getCpu().getMemory(), x);
}
public Item clearItem() {
Item item = inventory.get(position);
item.clear(unit);
inventory.remove(position);
return item;
}
public int getPosition() {
return position;
}
public void setPosition(int inventoryPosition) {
this.position = inventoryPosition;
}
public Map<Integer, Item> getInventory() {
return inventory;
} }
@Override @Override
@ -89,54 +35,34 @@ public class CubotInventory extends HardwareModule {
int a = getCpu().getRegisterSet().getRegister("A").getValue(); int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == INV_POLL) { if (a == INV_POLL) {
Item item = inventory.get(position); getCpu().getRegisterSet().getRegister("B").setValue(cubot.getHeldItem());
char result;
if (item == null) {
result = 0;
} else {
result = item.poll();
}
getCpu().getRegisterSet().getRegister("B").setValue(result);
} else if (a == INV_CLEAR) { } else if (a == INV_CLEAR) {
if (unit.spendEnergy(100)) { if (cubot.getHeldItem() == 0x0001) {
clearItem(); int energy = GameServer.INSTANCE.getConfig().getInt("biomassEnergyValue");
} cubot.storeEnergy(energy);
} else if (a == INV_SEEK) { cubot.setHeldItem(0);
setPosition(getCpu().getRegisterSet().getRegister("X").getValue());
} else if (a == INV_SCAN) { } else if (cubot.spendEnergy(100)) {
if (unit.spendEnergy(200)) { cubot.setHeldItem(0);
scanItem();
clearItem();
} }
} }
} }
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document document = super.mongoSerialise();
document.put("position", position); BasicDBObject dbObject = new BasicDBObject();
document.put("size", inventorySize);
Document items = new Document(); dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
for (Integer i : inventory.keySet()) { return dbObject;
items.put(i.toString(), inventory.get(i).mongoSerialise());
}
document.put("inventory", items);
return document;
} }
@Override public static CubotInventory deserialize(DBObject obj) {
public String toString() { return new CubotInventory((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
String itemList = "";
for (Integer i : inventory.keySet()) {
itemList += i + ": " + inventory.get(i).getClass().getSimpleName() + ", ";
}
return String.format("{CubotInventory[%d/%d] @ %d [%s]}", inventory.size(), inventorySize, position, itemList);
} }
} }

View File

@ -1,10 +1,12 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
public class CubotKeyboard extends CubotHardwareModule { public class CubotKeyboard extends CpuHardware {
public static final int DEFAULT_ADDRESS = 4; public static final int DEFAULT_ADDRESS = 4;
@ -16,12 +18,10 @@ public class CubotKeyboard extends CubotHardwareModule {
*/ */
public static final char HWID = 0x0004; public static final char HWID = 0x0004;
public CubotKeyboard(Cubot cubot) { private Cubot cubot;
super(cubot);
}
public CubotKeyboard(Document document, ControllableUnit cubot) { public CubotKeyboard(Cubot cubot) {
super(document, cubot); this.cubot = cubot;
} }
@Override @Override
@ -51,4 +51,19 @@ public class CubotKeyboard extends CubotHardwareModule {
} }
} }
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotKeyboard deserialize(DBObject obj) {
return new CubotKeyboard((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
} }

View File

@ -1,15 +1,19 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.assembly.HardwareModule; import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.*; import net.simon987.server.game.Action;
import org.bson.Document; import net.simon987.server.game.Attackable;
import net.simon987.server.game.GameObject;
import net.simon987.server.game.InventoryHolder;
import java.awt.*; import java.awt.*;
import java.util.ArrayList; import java.util.ArrayList;
public class CubotLaser extends HardwareModule { public class CubotLaser extends CpuHardware {
/** /**
* Hardware ID (Should be unique) * Hardware ID (Should be unique)
@ -18,18 +22,16 @@ public class CubotLaser extends HardwareModule {
public static final int DEFAULT_ADDRESS = 2; public static final int DEFAULT_ADDRESS = 2;
private Cubot cubot;
private static final int LASER_WITHDRAW = 1; private static final int LASER_WITHDRAW = 1;
private static final int LASER_DEPOSIT = 2; private static final int LASER_DEPOSIT = 2;
private static final int LASER_ATTACK = 3; private static final int LASER_ATTACK = 3;
private static final int LASER_DAMAGE = 25; private static final int LASER_DAMAGE = 25;
public CubotLaser(ControllableUnit unit) { public CubotLaser(Cubot cubot) {
super(null, unit); this.cubot = cubot;
}
public CubotLaser(Document document, ControllableUnit cubot) {
super(document, cubot);
} }
@Override @Override
@ -47,19 +49,19 @@ public class CubotLaser extends HardwareModule {
if (a == LASER_WITHDRAW) { if (a == LASER_WITHDRAW) {
Point frontTile = unit.getFrontTile(); Point frontTile = cubot.getFrontTile();
ArrayList<GameObject> objects = unit.getWorld().getGameObjectsBlockingAt(frontTile.x, frontTile.y); ArrayList<GameObject> objects = cubot.getWorld().getGameObjectsBlockingAt(frontTile.x, frontTile.y);
if (unit.getCurrentAction() == Action.IDLE && objects.size() > 0) { if (cubot.getCurrentAction() == Action.IDLE && objects.size() > 0) {
//FIXME: Problem here if more than 1 object //FIXME: Problem here if more than 1 object
if (objects.get(0) instanceof InventoryHolder) { if (objects.get(0) instanceof InventoryHolder) {
if (((InventoryHolder) objects.get(0)).canTakeItem(b)) { if (((InventoryHolder) objects.get(0)).canTakeItem(b)) {
if (unit.spendEnergy(30)) { if (cubot.spendEnergy(30)) {
//Take the item //Take the item
((InventoryHolder) objects.get(0)).takeItem(b); ((InventoryHolder) objects.get(0)).takeItem(b);
unit.giveItem(GameServer.INSTANCE.getRegistry().makeItem(b));
unit.setCurrentAction(Action.WITHDRAWING); cubot.setHeldItem(b);
cubot.setCurrentAction(Action.WITHDRAWING);
} }
} }
} }
@ -70,23 +72,34 @@ public class CubotLaser extends HardwareModule {
// TODO // TODO
} else if (a == LASER_ATTACK) { } else if (a == LASER_ATTACK) {
if (unit.getCurrentAction() == Action.IDLE) { if (cubot.spendEnergy(70)) {
if (unit.spendEnergy(70)) {
//Get object directly in front of the Cubot //Get object directly in front of the Cubot
Point frontTile = unit.getFrontTile(); Point frontTile = cubot.getFrontTile();
ArrayList<GameObject> objects = unit.getWorld().getGameObjectsAt(frontTile.x, frontTile.y); ArrayList<GameObject> objects = cubot.getWorld().getGameObjectsAt(frontTile.x, frontTile.y);
//todo: Add option in config to allow PvP
if (objects.size() > 0 && objects.get(0) instanceof Attackable && !(objects.get(0) instanceof Cubot)) {
((Attackable) objects.get(0)).damage(LASER_DAMAGE);
}
if (objects.size() > 0 && objects.get(0) instanceof Attackable) {
((Attackable) objects.get(0)).damage(LASER_DAMAGE);
} }
unit.setCurrentAction(Action.ATTACKING);
} }
} }
} }
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotLaser deserialize(DBObject obj) {
return new CubotLaser((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
} }

View File

@ -1,16 +1,21 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import net.simon987.server.assembly.HardwareModule; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.Action; import net.simon987.server.game.Action;
import net.simon987.server.game.objects.ControllableUnit; import net.simon987.server.game.Direction;
import net.simon987.server.game.objects.Direction; import net.simon987.server.io.JSONSerialisable;
import org.bson.Document; import org.json.simple.JSONObject;
public class CubotLeg extends HardwareModule { public class CubotLeg extends CpuHardware implements JSONSerialisable {
public static final int DEFAULT_ADDRESS = 1; public static final int DEFAULT_ADDRESS = 1;
public static final String NAME = "Cubot Leg";
private static final int LEGS_SET_DIR = 1; private static final int LEGS_SET_DIR = 1;
private static final int LEGS_SET_DIR_AND_WALK = 2; private static final int LEGS_SET_DIR_AND_WALK = 2;
@ -19,12 +24,10 @@ public class CubotLeg extends HardwareModule {
*/ */
static final char HWID = 0x0001; static final char HWID = 0x0001;
public CubotLeg(ControllableUnit unit) { private Cubot cubot;
super(null, unit);
}
public CubotLeg(Document document, ControllableUnit unit) { public CubotLeg(Cubot cubot) {
super(document, unit); this.cubot = cubot;
} }
@Override @Override
@ -35,7 +38,7 @@ public class CubotLeg extends HardwareModule {
@Override @Override
public void handleInterrupt(Status status) { public void handleInterrupt(Status status) {
if (unit.getCurrentAction() == Action.IDLE) { if (cubot.getCurrentAction() == Action.IDLE) {
int a = getCpu().getRegisterSet().getRegister("A").getValue(); int a = getCpu().getRegisterSet().getRegister("A").getValue();
int b = getCpu().getRegisterSet().getRegister("B").getValue(); int b = getCpu().getRegisterSet().getRegister("B").getValue();
@ -45,8 +48,8 @@ public class CubotLeg extends HardwareModule {
Direction dir = Direction.getDirection(b); Direction dir = Direction.getDirection(b);
if (dir != null) { if (dir != null) {
if (unit.spendEnergy(20)) { if (cubot.spendEnergy(20)) {
unit.setDirection(Direction.getDirection(b)); cubot.setDirection(Direction.getDirection(b));
status.setErrorFlag(false); status.setErrorFlag(false);
} }
} else { } else {
@ -56,19 +59,46 @@ public class CubotLeg extends HardwareModule {
} else if (a == LEGS_SET_DIR_AND_WALK) { } else if (a == LEGS_SET_DIR_AND_WALK) {
if (unit.getMaxEnergy() >= 100) { if (cubot.getMaxEnergy() >= 100) {
Direction dir = Direction.getDirection(b); Direction dir = Direction.getDirection(b);
if (dir != null) { if (dir != null) {
unit.setDirection(Direction.getDirection(b)); cubot.setDirection(Direction.getDirection(b));
status.setErrorFlag(false); status.setErrorFlag(false);
} else { } else {
status.setErrorFlag(true); status.setErrorFlag(true);
} }
unit.setCurrentAction(Action.WALKING); cubot.setCurrentAction(Action.WALKING);
} }
} }
} }
} }
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
json.put("hwid", (int) HWID);
json.put("cubot", cubot.getObjectId());
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotLeg deserialize(DBObject obj) {
return new CubotLeg((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
} }

View File

@ -1,16 +1,21 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import net.simon987.server.assembly.HardwareModule; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Memory; import net.simon987.server.assembly.Memory;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit; import net.simon987.server.game.World;
import net.simon987.server.game.pathfinding.Node; import net.simon987.server.game.pathfinding.Node;
import net.simon987.server.game.pathfinding.Pathfinder; import net.simon987.server.game.pathfinding.Pathfinder;
import org.bson.Document; import net.simon987.server.io.JSONSerialisable;
import net.simon987.server.logging.LogManager;
import org.json.simple.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
public class CubotLidar extends HardwareModule { public class CubotLidar extends CpuHardware implements JSONSerialisable {
/** /**
* Hardware ID (Should be unique) * Hardware ID (Should be unique)
@ -19,19 +24,20 @@ public class CubotLidar extends HardwareModule {
public static final int DEFAULT_ADDRESS = 3; public static final int DEFAULT_ADDRESS = 3;
private Cubot cubot;
private static final int LIDAR_GET_POS = 1; private static final int LIDAR_GET_POS = 1;
private static final int LIDAR_GET_PATH = 2; private static final int LIDAR_GET_PATH = 2;
private static final int LIDAR_GET_MAP = 3; private static final int LIDAR_GET_MAP = 3;
private static final int LIDAR_GET_WORLD_POS = 4; private static final int LIDAR_GET_WORLD_POS = 4;
private static final int LIDAR_GET_WORLD_SIZE = 5;
public CubotLidar(ControllableUnit unit) { private static final int MEMORY_MAP_START = 0x0100;
super(null, unit); private static final int MEMORY_PATH_START = 0x0000;
public CubotLidar(Cubot cubot) {
this.cubot = cubot;
} }
public CubotLidar(Document document, ControllableUnit unit) {
super(document, unit);
}
@Override @Override
public char getId() { public char getId() {
@ -45,24 +51,23 @@ public class CubotLidar extends HardwareModule {
switch (a) { switch (a) {
case LIDAR_GET_POS: case LIDAR_GET_POS:
getCpu().getRegisterSet().getRegister("X").setValue(unit.getX()); getCpu().getRegisterSet().getRegister("X").setValue(cubot.getX());
getCpu().getRegisterSet().getRegister("Y").setValue(unit.getY()); getCpu().getRegisterSet().getRegister("Y").setValue(cubot.getY());
break; break;
case LIDAR_GET_PATH: case LIDAR_GET_PATH:
if (unit.spendEnergy(50)) { if (cubot.spendEnergy(50)) {
int c = getCpu().getRegisterSet().getRegister("C").getValue();
int b = getCpu().getRegisterSet().getRegister("B").getValue(); int b = getCpu().getRegisterSet().getRegister("B").getValue();
int destX = getCpu().getRegisterSet().getRegister("X").getValue(); int destX = getCpu().getRegisterSet().getRegister("X").getValue();
int destY = getCpu().getRegisterSet().getRegister("Y").getValue(); int destY = getCpu().getRegisterSet().getRegister("Y").getValue();
//Get path //Get path
ArrayList<Node> nodes = Pathfinder.findPath(unit.getWorld(), unit.getX(), unit.getY(), ArrayList<Node> nodes = Pathfinder.findPath(cubot.getWorld(), cubot.getX(), cubot.getY(),
destX, destY, b); destX, destY, b);
//Write to memory //Write to memory
Memory mem = getCpu().getMemory(); Memory mem = getCpu().getMemory();
int counter = c; int counter = MEMORY_PATH_START;
if (nodes != null) { if (nodes != null) {
@ -70,6 +75,7 @@ public class CubotLidar extends HardwareModule {
for (Node n : nodes) { for (Node n : nodes) {
//Store the path as a sequence of directions //Store the path as a sequence of directions
if (lastNode == null) { if (lastNode == null) {
lastNode = n; lastNode = n;
continue; continue;
@ -98,37 +104,57 @@ public class CubotLidar extends HardwareModule {
//Indicate invalid path 0xFFFF //Indicate invalid path 0xFFFF
mem.set(counter, 0xFFFF); mem.set(counter, 0xFFFF);
} }
LogManager.LOGGER.fine("DEBUG: path to" + destX + "," + destY);
} }
break; break;
case LIDAR_GET_MAP: case LIDAR_GET_MAP:
if (unit.spendEnergy(10)) { if (cubot.spendEnergy(10)) {
char[][] mapInfo = unit.getWorld().getMapInfo(); char[][] mapInfo = cubot.getWorld().getMapInfo();
//Write map data to the location specified by register X int i = MEMORY_MAP_START;
int i = getCpu().getRegisterSet().getRegister("X").getValue(); for (int y = 0; y < World.WORLD_SIZE; y++) {
for (int y = 0; y < unit.getWorld().getWorldSize(); y++) { for (int x = 0; x < World.WORLD_SIZE; x++) {
for (int x = 0; x < unit.getWorld().getWorldSize(); x++) {
getCpu().getMemory().set(i++, mapInfo[x][y]); getCpu().getMemory().set(i++, mapInfo[x][y]);
} }
} }
} }
break;
case LIDAR_GET_WORLD_SIZE:
getCpu().getRegisterSet().getRegister("X").setValue(unit.getWorld().getWorldSize());
getCpu().getRegisterSet().getRegister("Y").setValue(unit.getWorld().getWorldSize());
break; break;
case LIDAR_GET_WORLD_POS: case LIDAR_GET_WORLD_POS:
getCpu().getRegisterSet().getRegister("X").setValue(unit.getWorld().getX()); getCpu().getRegisterSet().getRegister("X").setValue(cubot.getWorld().getX());
getCpu().getRegisterSet().getRegister("Y").setValue(unit.getWorld().getY()); getCpu().getRegisterSet().getRegister("Y").setValue(cubot.getWorld().getY());
break; break;
default:
break;
} }
}
@Override
public JSONObject serialise() {
JSONObject json = new JSONObject();
json.put("hwid", (int) HWID);
json.put("cubot", cubot.getObjectId());
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId());
return dbObject;
}
public static CubotLidar deserialize(DBObject obj) {
return new CubotLidar((Cubot) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
} }
} }

View File

@ -1,44 +1,67 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import net.simon987.cubotplugin.event.*; import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.cubotplugin.event.CpuInitialisationListener;
import net.simon987.server.game.objects.GameRegistry; import net.simon987.cubotplugin.event.UserCreationListener;
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.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin; import net.simon987.server.plugin.ServerPlugin;
public class CubotPlugin extends ServerPlugin { public class CubotPlugin extends ServerPlugin implements GameObjectDeserializer, CpuHardwareDeserializer {
@Override @Override
public void init(GameServer gameServer) { public void init(ServerConfiguration config) {
listeners.add(new CpuInitialisationListener()); listeners.add(new CpuInitialisationListener());
listeners.add(new UserCreationListener()); listeners.add(new UserCreationListener());
//Debug commands
listeners.add(new ChargeShieldCommandListener());
listeners.add(new SetInventoryPosition());
listeners.add(new PutItemCommandListener());
listeners.add(new PopItemCommandListener());
//Leaderboard
listeners.add(new DeathListener());
listeners.add(new WalkListener());
GameRegistry registry = gameServer.getRegistry(); LogManager.LOGGER.info("Initialised Cubot plugin");
}
registry.registerGameObject(Cubot.class); @Override
public GameObject deserializeObject(DBObject object) {
registry.registerHardware(CubotLeg.class); int objType = (int) object.get("t");
registry.registerHardware(CubotLaser.class);
registry.registerHardware(CubotLidar.class);
registry.registerHardware(CubotDrill.class);
registry.registerHardware(CubotInventory.class);
registry.registerHardware(CubotKeyboard.class);
registry.registerHardware(CubotHologram.class);
registry.registerHardware(CubotBattery.class);
registry.registerHardware(CubotFloppyDrive.class);
registry.registerHardware(CubotComPort.class);
registry.registerHardware(CubotShield.class);
registry.registerHardware(CubotCore.class);
LogManager.LOGGER.info("(Cubot Plugin) Initialised Cubot plugin"); if (objType == Cubot.ID) {
return Cubot.deserialize(object);
}
return null;
}
@Override
public CpuHardware deserializeHardware(DBObject obj) {
int hwid = (int) obj.get("hwid");
switch (hwid) {
case CubotLeg.HWID:
return CubotLeg.deserialize(obj);
case CubotLaser.HWID:
return CubotLaser.deserialize(obj);
case CubotLidar.HWID:
return CubotLidar.deserialize(obj);
case CubotDrill.HWID:
return CubotDrill.deserialize(obj);
case CubotInventory.HWID:
return CubotInventory.deserialize(obj);
case CubotKeyboard.HWID:
return CubotKeyboard.deserialize(obj);
case CubotHologram.HWID:
return CubotHologram.deserialize(obj);
case CubotBattery.HWID:
return CubotBattery.deserialize(obj);
case CubotFloppyDrive.HWID:
return CubotFloppyDrive.deserialize(obj);
case CubotComPort.HWID:
return CubotComPort.deserialize(obj);
}
return null;
} }
} }

View File

@ -1,44 +0,0 @@
package net.simon987.cubotplugin;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
public class CubotShield extends CubotHardwareModule {
public static final char DEFAULT_ADDRESS = 0x000F;
static final char HWID = 0x000F;
private static final int SHIELD_CHARGE = 1;
private static final int SHIELD_POLL = 2;
public static final int COST = GameServer.INSTANCE.getConfig().getInt("shield_energy_cost");
public CubotShield(Cubot cubot) {
super(cubot);
}
public CubotShield(Document document, ControllableUnit cubot) {
super(document, cubot);
}
@Override
public char getId() {
return HWID;
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
// b = amount to charge
if(a == SHIELD_CHARGE) {
int b = getCpu().getRegisterSet().getRegister("B").getValue();
cubot.chargeShield(b);
} else if (a == SHIELD_POLL) {
int shield = cubot.getShield();
getCpu().getRegisterSet().getRegister("B").setValue(shield);
}
}
}

View File

@ -1,19 +0,0 @@
package net.simon987.cubotplugin;
/**
* Status of a Cubot (Special buff or debuff)
*/
public enum CubotStatus {
DEFAULT(0),
RADIATED(1),
DAMAGED(2),
FACTORY_NEW(3);
public char val;
CubotStatus(int val) {
this.val = (char) val;
}
}

View File

@ -1,16 +1,17 @@
package net.simon987.cubotplugin; package net.simon987.cubotplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.assembly.Memory; import net.simon987.server.assembly.Memory;
import net.simon987.server.io.MongoSerializable; import net.simon987.server.io.MongoSerialisable;
import org.bson.Document;
/** /**
* Represents a floppy disk that is inside a floppy drive. * Represents a floppy disk that is inside a floppy drive.
* Floppies contains 80 tracks with 18 sectors per track. * Floppies contains 80 tracks with 18 sectors per track.
* That's 1440 sectors of 512 words. (total 1,474,560 bytes / 737,280 words / 1.44Mb) * That's 1440 sectors of 512 words. (total 1,474,560 bytes / 737,280 words / 1.44Mb)
*/ */
public class FloppyDisk implements MongoSerializable { public class FloppyDisk implements MongoSerialisable {
/** /**
* Contents of the disk * Contents of the disk
@ -28,11 +29,6 @@ public class FloppyDisk implements MongoSerializable {
this.memory = new Memory(512 * 1440); this.memory = new Memory(512 * 1440);
} }
public FloppyDisk(Document document) {
this.rwHeadTrack = document.getInteger("rwHeadTrack");
this.memory = new Memory((Document) document.get("memory"));
}
/** /**
* Read 512 words from the specified sector to cpu memory at specified address * Read 512 words from the specified sector to cpu memory at specified address
* *
@ -88,8 +84,8 @@ public class FloppyDisk implements MongoSerializable {
} }
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document dbObject = new Document(); BasicDBObject dbObject = new BasicDBObject();
dbObject.put("rwHeadTrack", rwHeadTrack); dbObject.put("rwHeadTrack", rwHeadTrack);
dbObject.put("memory", memory.mongoSerialise()); dbObject.put("memory", memory.mongoSerialise());
@ -97,6 +93,16 @@ public class FloppyDisk implements MongoSerializable {
return dbObject; return dbObject;
} }
public static FloppyDisk deserialise(DBObject obj) {
FloppyDisk floppyDisk = new FloppyDisk();
floppyDisk.rwHeadTrack = (int) obj.get("rwHeadTrack");
floppyDisk.memory = Memory.deserialize((DBObject) obj.get("memory"));
return floppyDisk;
}
public Memory getMemory() { public Memory getMemory() {
return memory; return memory;
} }

View File

@ -1,48 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.cubotplugin.Cubot;
import net.simon987.server.GameServer;
import net.simon987.server.event.DebugCommandEvent;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.game.objects.GameObject;
/**
* Debug command to add shield points to a Cubot
*/
public class ChargeShieldCommandListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return DebugCommandEvent.class;
}
@Override
public void handle(GameEvent event) {
DebugCommandEvent e = (DebugCommandEvent) event;
if (e.getName().equals("chargeShield")) {
GameObject cubot = GameServer.INSTANCE.getGameUniverse().getObject(e.getObjectId("objectId"));
if (cubot != null) {
if (cubot instanceof Cubot) {
String hp = ((Cubot) cubot).getHp() + "/" + ((Cubot) cubot).getMaxHp();
int oldShield = ((Cubot) cubot).getShield();
((Cubot) cubot).chargeShield(e.getInt("amount"));
e.reply("Success: " + hp + " (" + oldShield + ") -> " + hp + "(" + ((Cubot) cubot).getShield() +
")");
} else {
e.reply("Object is not a Cubot");
}
} else {
e.reply("Object not found: " + e.getLong("objectId"));
}
}
}
}

View File

@ -5,6 +5,7 @@ import net.simon987.server.assembly.CPU;
import net.simon987.server.event.CpuInitialisationEvent; import net.simon987.server.event.CpuInitialisationEvent;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener; import net.simon987.server.event.GameEventListener;
import net.simon987.server.user.User;
public class CpuInitialisationListener implements GameEventListener { public class CpuInitialisationListener implements GameEventListener {
@Override @Override
@ -14,48 +15,42 @@ public class CpuInitialisationListener implements GameEventListener {
@Override @Override
public void handle(GameEvent event) { public void handle(GameEvent event) {
//LogManager.LOGGER.fine("(Plugin) Handled CPU Initialisation event (Cubot Plugin)");
CPU cpu = (CPU) event.getSource(); CPU cpu = (CPU) event.getSource();
Cubot cubot = (Cubot) ((CpuInitialisationEvent) event).getUnit(); User user = ((CpuInitialisationEvent) event).getUser();
cpu.setHardwareHost(cubot);
CubotLeg legHw = new CubotLeg(cubot); CubotLeg legHw = new CubotLeg((Cubot) user.getControlledUnit());
legHw.setCpu(cpu); legHw.setCpu(cpu);
CubotLaser laserHw = new CubotLaser(cubot); CubotLaser laserHw = new CubotLaser((Cubot) user.getControlledUnit());
laserHw.setCpu(cpu); laserHw.setCpu(cpu);
CubotLidar radarHw = new CubotLidar(cubot); CubotLidar radarHw = new CubotLidar((Cubot) user.getControlledUnit());
radarHw.setCpu(cpu); radarHw.setCpu(cpu);
CubotKeyboard keyboard = new CubotKeyboard(cubot); CubotKeyboard keyboard = new CubotKeyboard((Cubot) user.getControlledUnit());
keyboard.setCpu(cpu); keyboard.setCpu(cpu);
CubotDrill drillHw = new CubotDrill(cubot); CubotDrill drillHw = new CubotDrill((Cubot) user.getControlledUnit());
drillHw.setCpu(cpu); drillHw.setCpu(cpu);
CubotInventory invHw = new CubotInventory(cubot); CubotInventory invHw = new CubotInventory((Cubot) user.getControlledUnit());
invHw.setCpu(cpu); invHw.setCpu(cpu);
CubotHologram emoteHw = new CubotHologram(cubot); CubotHologram emoteHw = new CubotHologram((Cubot) user.getControlledUnit());
emoteHw.setCpu(cpu); emoteHw.setCpu(cpu);
CubotBattery batteryHw = new CubotBattery(cubot); CubotBattery batteryHw = new CubotBattery((Cubot) user.getControlledUnit());
batteryHw.setCpu(cpu); batteryHw.setCpu(cpu);
CubotFloppyDrive floppyHw = new CubotFloppyDrive(cubot); CubotFloppyDrive floppyHw = new CubotFloppyDrive((Cubot) user.getControlledUnit());
floppyHw.setCpu(cpu); floppyHw.setCpu(cpu);
CubotComPort comPortHw = new CubotComPort(cubot); CubotComPort comPortHw = new CubotComPort((Cubot) user.getControlledUnit());
comPortHw.setCpu(cpu); comPortHw.setCpu(cpu);
CubotCore coreHw = new CubotCore(cubot);
coreHw.setCpu(cpu);
CubotShield shieldHw = new CubotShield(cubot);
shieldHw.setCpu(cpu);
cubot.attachHardware(legHw, CubotLeg.DEFAULT_ADDRESS); cpu.attachHardware(legHw, CubotLeg.DEFAULT_ADDRESS);
cubot.attachHardware(laserHw, CubotLaser.DEFAULT_ADDRESS); cpu.attachHardware(laserHw, CubotLaser.DEFAULT_ADDRESS);
cubot.attachHardware(radarHw, CubotLidar.DEFAULT_ADDRESS); cpu.attachHardware(radarHw, CubotLidar.DEFAULT_ADDRESS);
cubot.attachHardware(keyboard, CubotKeyboard.DEFAULT_ADDRESS); cpu.attachHardware(keyboard, CubotKeyboard.DEFAULT_ADDRESS);
cubot.attachHardware(drillHw, CubotDrill.DEFAULT_ADDRESS); cpu.attachHardware(drillHw, CubotDrill.DEFAULT_ADDRESS);
cubot.attachHardware(invHw, CubotInventory.DEFAULT_ADDRESS); cpu.attachHardware(invHw, CubotInventory.DEFAULT_ADDRESS);
cubot.attachHardware(invHw, CubotInventory.DEFAULT_ADDRESS); cpu.attachHardware(invHw, CubotInventory.DEFAULT_ADDRESS);
cubot.attachHardware(emoteHw, CubotHologram.DEFAULT_ADDRESS); cpu.attachHardware(emoteHw, CubotHologram.DEFAULT_ADDRESS);
cubot.attachHardware(batteryHw, CubotBattery.DEFAULT_ADDRESS); cpu.attachHardware(batteryHw, CubotBattery.DEFAULT_ADDRESS);
cubot.attachHardware(floppyHw, CubotFloppyDrive.DEFAULT_ADDRESS); cpu.attachHardware(floppyHw, CubotFloppyDrive.DEFAULT_ADDRESS);
cubot.attachHardware(comPortHw, CubotComPort.DEFAULT_ADDRESS); cpu.attachHardware(comPortHw, CubotComPort.DEFAULT_ADDRESS);
cubot.attachHardware(coreHw, CubotCore.DEFAULT_ADDRESS);
cubot.attachHardware(shieldHw, CubotShield.DEFAULT_ADDRESS);
} }
} }

View File

@ -1,17 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.cubotplugin.Cubot;
import net.simon987.server.event.GameEvent;
public class CubotWalkEvent extends GameEvent {
public CubotWalkEvent(Cubot cubot) {
setSource(cubot);
}
@Override
public Cubot getSource() {
return (Cubot) super.getSource();
}
}

View File

@ -1,16 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.server.event.GameEvent;
import net.simon987.server.game.objects.GameObject;
public class DeathEvent extends GameEvent {
public DeathEvent(GameObject object) {
setSource(object);
}
@Override
public GameObject getSource() {
return (GameObject) super.getSource();
}
}

View File

@ -1,23 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.game.objects.ControllableUnit;
import net.simon987.server.game.objects.GameObject;
public class DeathListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return DeathEvent.class;
}
@Override
public void handle(GameEvent event) {
DeathEvent DeathEvent = (DeathEvent) event;
GameObject object = DeathEvent.getSource();
if (object instanceof ControllableUnit) {
((ControllableUnit) object).getParent().getStats().incrementStat("death", 1);
}
}
}

View File

@ -1,43 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.cubotplugin.Cubot;
import net.simon987.cubotplugin.CubotInventory;
import net.simon987.server.GameServer;
import net.simon987.server.event.DebugCommandEvent;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.game.objects.GameObject;
public class PopItemCommandListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return DebugCommandEvent.class;
}
@Override
public void handle(GameEvent event) {
DebugCommandEvent e = (DebugCommandEvent) event;
if (e.getName().equals("clearItem")) {
GameObject object = GameServer.INSTANCE.getGameUniverse().getObject(e.getObjectId("objectId"));
if (object != null) {
if (object instanceof Cubot) {
CubotInventory inventory = (CubotInventory) ((Cubot) object).getHardware(CubotInventory.class);
e.reply("Removed item from inventory: " + inventory.clearItem());
} else {
e.reply("Object is not a Cubot");
}
} else {
e.reply("Object not found: " + e.getLong("objectId"));
}
}
}
}

View File

@ -1,53 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.cubotplugin.Cubot;
import net.simon987.cubotplugin.CubotInventory;
import net.simon987.server.GameServer;
import net.simon987.server.event.DebugCommandEvent;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.game.item.Item;
import net.simon987.server.game.objects.GameObject;
import org.bson.Document;
public class PutItemCommandListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return DebugCommandEvent.class;
}
@Override
public void handle(GameEvent event) {
DebugCommandEvent e = (DebugCommandEvent) event;
if (e.getName().equals("putItem")) {
GameObject object = GameServer.INSTANCE.getGameUniverse().getObject(e.getObjectId("objectId"));
if (object != null) {
if (object instanceof Cubot) {
CubotInventory inventory = (CubotInventory) ((Cubot) object).getHardware(CubotInventory.class);
Item item = GameServer.INSTANCE.getRegistry().deserializeItem(Document.parse(e.getString("item")));
if (item != null) {
inventory.putItem(item);
e.reply("Set item to " + item.getClass().getSimpleName());
} else {
e.reply("Couldn't deserialize item");
}
} else {
e.reply("Object is not a Cubot");
}
} else {
e.reply("Object not found: " + e.getLong("objectId"));
}
}
}
}

View File

@ -1,45 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.cubotplugin.Cubot;
import net.simon987.cubotplugin.CubotInventory;
import net.simon987.server.GameServer;
import net.simon987.server.event.DebugCommandEvent;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.game.objects.GameObject;
public class SetInventoryPosition implements GameEventListener {
@Override
public Class getListenedEventType() {
return DebugCommandEvent.class;
}
@Override
public void handle(GameEvent event) {
DebugCommandEvent e = (DebugCommandEvent) event;
if (e.getName().equals("setInventoryPosition")) {
GameObject object = GameServer.INSTANCE.getGameUniverse().getObject(e.getObjectId("objectId"));
if (object != null) {
if (object instanceof Cubot) {
int position = e.getInt("position");
CubotInventory inventory = (CubotInventory) ((Cubot) object).getHardware(CubotInventory.class);
inventory.setPosition(position);
e.reply("Set inventory position to " + position);
} else {
e.reply("Object is not a Cubot");
}
} else {
e.reply("Object not found: " + e.getLong("objectId"));
}
}
}
}

View File

@ -1,19 +1,12 @@
package net.simon987.cubotplugin.event; package net.simon987.cubotplugin.event;
import net.simon987.cubotplugin.Cubot; import net.simon987.cubotplugin.Cubot;
import net.simon987.cubotplugin.CubotStatus;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.IServerConfiguration;
import net.simon987.server.assembly.Assembler;
import net.simon987.server.assembly.AssemblyResult;
import net.simon987.server.assembly.CPU;
import net.simon987.server.assembly.exception.CancelledException;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener; import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.UserCreationEvent; import net.simon987.server.event.UserCreationEvent;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import net.simon987.server.user.User; import net.simon987.server.user.User;
import org.bson.types.ObjectId;
import java.awt.*; import java.awt.*;
import java.util.Random; import java.util.Random;
@ -31,16 +24,13 @@ public class UserCreationListener implements GameEventListener {
User user = (User) event.getSource(); User user = (User) event.getSource();
Cubot cubot = new Cubot(); Cubot cubot = new Cubot();
cubot.addStatus(CubotStatus.FACTORY_NEW); cubot.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
cubot.setObjectId(new ObjectId());
IServerConfiguration config = GameServer.INSTANCE.getConfig();
Point point = null; Point point = null;
while (point == null || cubot.getWorld() == null) { while (point == null || cubot.getWorld() == null) {
int spawnX = config.getInt("new_user_worldX") + random.nextInt(5); int spawnX = GameServer.INSTANCE.getConfig().getInt("new_user_worldX") + random.nextInt(5);
int spawnY = config.getInt("new_user_worldY") + random.nextInt(5); int spawnY = GameServer.INSTANCE.getConfig().getInt("new_user_worldY") + random.nextInt(5);
String dimension = config.getString("new_user_dimension"); cubot.setWorld(GameServer.INSTANCE.getGameUniverse().getWorld(spawnX, spawnY, true));
cubot.setWorld(GameServer.INSTANCE.getGameUniverse().getWorld(spawnX, spawnY, true, dimension));
point = cubot.getWorld().getRandomPassableTile(); point = cubot.getWorld().getRandomPassableTile();
} }
@ -50,34 +40,15 @@ public class UserCreationListener implements GameEventListener {
cubot.getWorld().addObject(cubot); cubot.getWorld().addObject(cubot);
cubot.getWorld().incUpdatable(); cubot.getWorld().incUpdatable();
cubot.setHeldItem(GameServer.INSTANCE.getConfig().getInt("new_user_item"));
cubot.setEnergy(GameServer.INSTANCE.getConfig().getInt("battery_max_energy"));
cubot.setMaxEnergy(GameServer.INSTANCE.getConfig().getInt("battery_max_energy"));
cubot.setParent(user); cubot.setParent(user);
user.setControlledUnit(cubot); user.setControlledUnit(cubot);
//Create CPU
try {
cubot.setCpu(new CPU(GameServer.INSTANCE.getConfig(), cubot));
cubot.getCpu().setHardwareHost(cubot);
user.setUserCode(config.getString("new_user_code"));
//Compile user code
AssemblyResult ar = new Assembler(cubot.getCpu().getInstructionSet(), cubot.getCpu().getRegisterSet(),
GameServer.INSTANCE.getConfig()).parse(user.getUserCode());
cubot.getCpu().getMemory().clear();
//Write assembled code to mem
char[] assembledCode = ar.getWords();
cubot.getCpu().getMemory().write((char) ar.origin, assembledCode, 0, assembledCode.length);
cubot.getCpu().setCodeSectionOffset(ar.getCodeSectionOffset());
} catch (CancelledException e) {
e.printStackTrace();
}
cubot.setHp(config.getInt("cubot_max_hp"));
cubot.setMaxHp(config.getInt("cubot_max_hp"));
cubot.setMaxShield(config.getInt("cubot_max_shield"));
LogManager.LOGGER.fine("(Plugin) Handled User creation event (Cubot Plugin)"); LogManager.LOGGER.fine("(Plugin) Handled User creation event (Cubot Plugin)");
} }
} }

View File

@ -1,18 +0,0 @@
package net.simon987.cubotplugin.event;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
public class WalkListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return CubotWalkEvent.class;
}
@Override
public void handle(GameEvent event) {
CubotWalkEvent walkEvent = (CubotWalkEvent) event;
walkEvent.getSource().getParent().getStats().incrementStat("walkDistance", 1);
}
}

View File

@ -3,10 +3,17 @@ package net.simon987.cubotplugin;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class CubotTest { public class CubotTest {
@Test @Test
public void test(){ public void test(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
assertEquals(1, 1);
} }
} }

View File

@ -1,10 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" /> <output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" /> <output-test url="file://$MODULE_DIR$/target/test-classes" />
@ -17,40 +12,11 @@
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module" module-name="Server" /> <orderEntry type="module" module-name="Server" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.6" level="project" /> <orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.8.1" level="project" /> <orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongodb-driver-sync:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:bson:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongodb-driver-core:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.1.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.sparkjava:spark-core:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.25" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-server:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:3.1.0" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-http:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-util:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-io:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-webapp:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-xml:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-servlet:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-security:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-server:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-common:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-client:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-client:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-servlet:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-api:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: com.sparkjava:spark-template-velocity:2.7.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.velocity:velocity:1.7" level="project" />
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.1" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.4" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-simple:1.7.25" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" /> <orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: junit:junit:4.10" level="project" /> <orderEntry type="library" name="Maven: junit:junit:4.10" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" /> <orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />

View File

@ -7,18 +7,18 @@
<parent> <parent>
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId> <artifactId>server_root</artifactId>
<version>1.4a</version> <version>1.2a</version>
</parent> </parent>
<groupId>net.simon987.pluginmischw</groupId> <groupId>net.simon987.pluginmischw</groupId>
<artifactId>plugin-misc-hw</artifactId> <artifactId>plugin-misc-hw</artifactId>
<version>1.4a</version> <version>1.2a</version>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server</artifactId> <artifactId>server</artifactId>
<version>1.4a</version> <version>1.2a</version>
</dependency> </dependency>
<dependency> <dependency>

View File

@ -1,29 +1,17 @@
package net.simon987.mischwplugin; package net.simon987.mischwplugin;
import com.mongodb.BasicDBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.assembly.HardwareModule; import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.assembly.Util; import net.simon987.server.assembly.Util;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
/** public class Clock extends CpuHardware {
* Hardware to get game time
*/
public class Clock extends HardwareModule {
private static final char HWID = 0x0008; public static final char HWID = 0x0008;
public static final char DEFAULT_ADDRESS = 0x0008; public static final char DEFAULT_ADDRESS = 0x0008;
public Clock() {
}
public Clock(Document document, ControllableUnit unit) {
super(document, unit);
}
@Override @Override
public void handleInterrupt(Status status) { public void handleInterrupt(Status status) {
@ -40,12 +28,17 @@ public class Clock extends HardwareModule {
return HWID; return HWID;
} }
public static Clock deserialize() {
return new Clock();
}
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document dbObject = new Document(); BasicDBObject dbObject = new BasicDBObject();
dbObject.put("type", getClass().getCanonicalName());
dbObject.put("hwid", (int) HWID);
return dbObject; return dbObject;
} }

View File

@ -1,26 +1,34 @@
package net.simon987.mischwplugin; package net.simon987.mischwplugin;
import com.mongodb.DBObject;
import net.simon987.mischwplugin.event.CpuInitialisationListener; import net.simon987.mischwplugin.event.CpuInitialisationListener;
import net.simon987.server.GameServer; import net.simon987.server.ServerConfiguration;
import net.simon987.server.game.objects.GameRegistry; import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.io.CpuHardwareDeserializer;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin; import net.simon987.server.plugin.ServerPlugin;
/** public class MiscHWPlugin extends ServerPlugin implements CpuHardwareDeserializer {
* Plugin that adds miscellaneous hardware to the game
*/
public class MiscHWPlugin extends ServerPlugin {
@Override @Override
public void init(GameServer gameServer) { public void init(ServerConfiguration config) {
listeners.add(new CpuInitialisationListener()); listeners.add(new CpuInitialisationListener());
GameRegistry registry = gameServer.getRegistry(); LogManager.LOGGER.info("Initialised Misc Hardware Plugin");
}
registry.registerHardware(RandomNumberGenerator.class); @Override
registry.registerHardware(Clock.class); public CpuHardware deserializeHardware(DBObject hwJson) {
int hwid = (int) hwJson.get("hwid");
LogManager.LOGGER.info("(Mist HW Plugin) Initialised Misc Hardware Plugin"); switch (hwid) {
case RandomNumberGenerator.HWID:
return RandomNumberGenerator.deserialize();
case Clock.HWID:
return Clock.deserialize();
}
return null;
} }
} }

View File

@ -1,18 +1,14 @@
package net.simon987.mischwplugin; package net.simon987.mischwplugin;
import net.simon987.server.assembly.HardwareModule; import com.mongodb.BasicDBObject;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
import java.util.Random; import java.util.Random;
/** public class RandomNumberGenerator extends CpuHardware {
* Hardware to generate random numbers
*/
public class RandomNumberGenerator extends HardwareModule {
private static final char HWID = 0x0007; public static final char HWID = 0x0007;
public static final char DEFAULT_ADDRESS = 0x0007; public static final char DEFAULT_ADDRESS = 0x0007;
@ -22,11 +18,6 @@ public class RandomNumberGenerator extends HardwareModule {
random = new Random(); random = new Random();
} }
public RandomNumberGenerator(Document document, ControllableUnit unit) {
super(document, unit);
random = new Random();
}
@Override @Override
public void handleInterrupt(Status status) { public void handleInterrupt(Status status) {
@ -40,11 +31,16 @@ public class RandomNumberGenerator extends HardwareModule {
} }
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document dbObject = new Document(); BasicDBObject dbObject = new BasicDBObject();
dbObject.put("type", getClass().getCanonicalName());
dbObject.put("hwid", (int) HWID);
return dbObject; return dbObject;
} }
public static RandomNumberGenerator deserialize() {
return new RandomNumberGenerator();
}
} }

View File

@ -6,7 +6,6 @@ import net.simon987.server.assembly.CPU;
import net.simon987.server.event.CpuInitialisationEvent; import net.simon987.server.event.CpuInitialisationEvent;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener; import net.simon987.server.event.GameEventListener;
import net.simon987.server.game.objects.HardwareHost;
public class CpuInitialisationListener implements GameEventListener { public class CpuInitialisationListener implements GameEventListener {
@ -19,15 +18,13 @@ public class CpuInitialisationListener implements GameEventListener {
public void handle(GameEvent event) { public void handle(GameEvent event) {
CPU cpu = (CPU) event.getSource(); CPU cpu = (CPU) event.getSource();
HardwareHost cubot = (HardwareHost) ((CpuInitialisationEvent) event).getUnit();
cpu.setHardwareHost(cubot);
RandomNumberGenerator rngHW = new RandomNumberGenerator(); RandomNumberGenerator rngHW = new RandomNumberGenerator();
rngHW.setCpu(cpu); rngHW.setCpu(cpu);
Clock clock = new Clock(); Clock clock = new Clock();
clock.setCpu(cpu); clock.setCpu(cpu);
cpu.getHardwareHost().attachHardware(rngHW, RandomNumberGenerator.DEFAULT_ADDRESS); cpu.attachHardware(rngHW, RandomNumberGenerator.DEFAULT_ADDRESS);
cpu.getHardwareHost().attachHardware(clock, Clock.DEFAULT_ADDRESS); cpu.attachHardware(clock, Clock.DEFAULT_ADDRESS);
} }
} }

View File

@ -1,10 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" /> <output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" /> <output-test url="file://$MODULE_DIR$/target/test-classes" />
@ -20,39 +15,10 @@
<orderEntry type="library" name="Maven: junit:junit:4.10" level="project" /> <orderEntry type="library" name="Maven: junit:junit:4.10" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" /> <orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />
<orderEntry type="module" module-name="Server" /> <orderEntry type="module" module-name="Server" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.6" level="project" /> <orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.8.1" level="project" /> <orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongodb-driver-sync:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:bson:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongodb-driver-core:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.1.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.sparkjava:spark-core:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.25" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-server:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:3.1.0" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-http:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-util:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-io:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-webapp:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-xml:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-servlet:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-security:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-server:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-common:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-client:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-client:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-servlet:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-api:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: com.sparkjava:spark-template-velocity:2.7.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.velocity:velocity:1.7" level="project" />
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.1" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.4" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-simple:1.7.25" level="project" />
</component> </component>
</module> </module>

View File

@ -7,12 +7,12 @@
<parent> <parent>
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId> <artifactId>server_root</artifactId>
<version>1.4a</version> <version>1.2a</version>
</parent> </parent>
<groupId>net.simon987.pluginnpc</groupId> <groupId>net.simon987.pluginnpc</groupId>
<artifactId>plugin-npc</artifactId> <artifactId>plugin-npc</artifactId>
<version>1.4a</version> <version>1.2a</version>
<dependencies> <dependencies>
@ -25,7 +25,7 @@
<dependency> <dependency>
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server</artifactId> <artifactId>server</artifactId>
<version>1.4a</version> <version>1.2a</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,162 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.Util;
import net.simon987.server.game.objects.Attackable;
import net.simon987.server.game.objects.GameObject;
import net.simon987.server.game.objects.Rechargeable;
import net.simon987.server.game.objects.Updatable;
import org.bson.Document;
import org.json.simple.JSONObject;
import java.util.ArrayList;
/**
* Game object that deals damage to nearby objects and gives them energy
*/
public class ElectricBox extends GameObject implements Updatable, Attackable {
private static final char MAP_INFO = 0x0301;
/**
* Hit points
*/
private int hp;
/**
* Maximum hit points
*/
private static final int maxHp = GameServer.INSTANCE.getConfig().getInt("electric_box_hp");
/**
* Number of hit points dealt to nearby objects each tick
*/
private static final int damageDealt = GameServer.INSTANCE.getConfig().getInt("electric_box_damage");
/**
* Number of energy points given to nearby objects each tick
*/
private static final int energyGiven = GameServer.INSTANCE.getConfig().getInt("electric_box_energy_given");
/**
* List of nearby objects. Is updated every tick
*/
private ArrayList<Attackable> nearObjects = new ArrayList<>();
public ElectricBox() {
hp = maxHp;
}
public ElectricBox(Document document) {
super(document);
hp = document.getInteger("hp");
}
/**
* Currently has no effect
*/
@Override
public void setHealRate(int hp) {
//no op
}
@Override
public int getHp() {
return hp;
}
@Override
public void setHp(int hp) {
this.hp = hp;
}
@Override
public int getMaxHp() {
return hp;
}
/**
* Currently has no effect
*/
@Override
public void setMaxHp(int hp) {
//No op
}
/**
* Currently has no effect
*/
@Override
public void heal(int amount) {
//No op
}
@Override
public void damage(int amount) {
hp -= amount;
//YOU ARE DEAD
if (hp <= 0) {
setDead(true);
}
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
/**
* Updates the current list nearby objects
* <br>An object is considered 'nearby' if its Manhattan distance is {@literal <= @} 1 and is Attackable
*/
private void updateNearObjects() {
nearObjects.clear();
for (GameObject object : getWorld().getGameObjects()) {
if (object != this && object instanceof Attackable && Util.manhattanDist(object.getX(), object.getY(),
getX(), getY()) <= 1) {
nearObjects.add((Attackable) object);
}
}
}
/**
* Called every tick
*/
@Override
public void update() {
updateNearObjects();
for (Attackable obj : nearObjects) {
obj.damage(damageDealt);
if (obj instanceof Rechargeable) {
((Rechargeable) obj).storeEnergy(energyGiven);
}
}
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = super.jsonSerialise();
json.put("hp", hp);
return json;
}
@Override
public Document mongoSerialise() {
Document dbObject = super.mongoSerialise();
dbObject.put("hp", getHp());
return dbObject;
}
@Override
public boolean onDeadCallback() {
getWorld().decUpdatable();
return false;
}
}

View File

@ -1,38 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.GameServer;
import net.simon987.server.game.objects.Action;
public class ExecuteCpuTask extends NPCTask {
private static final int MAX_EXEC_TIME = GameServer.INSTANCE.getConfig().getInt("npc_exec_time");
@Override
public boolean checkCompleted() {
return false;
}
@Override
public void tick(NonPlayerCharacter npc) {
HackedNPC hNpc = (HackedNPC) npc;
//Execute code
int timeout = Math.min(hNpc.getEnergy(), MAX_EXEC_TIME);
hNpc.getCpu().reset();
int cost = hNpc.getCpu().execute(timeout);
hNpc.spendEnergy(cost);
if (hNpc.getCurrentAction() == Action.WALKING) {
if (hNpc.spendEnergy(100)) {
if (hNpc.incrementLocation()) {
//Couldn't walk
hNpc.setCurrentAction(Action.IDLE);
}
} else {
hNpc.setCurrentAction(Action.IDLE);
}
}
}
}

View File

@ -1,157 +1,210 @@
package net.simon987.npcplugin; package net.simon987.npcplugin;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.game.objects.MessageReceiver; import net.simon987.server.game.GameObject;
import net.simon987.server.game.objects.Structure; import net.simon987.server.game.Updatable;
import net.simon987.server.game.objects.Updatable; import org.json.simple.JSONArray;
import org.bson.Document; import org.json.simple.JSONObject;
import org.bson.types.ObjectId;
import java.awt.*; import java.awt.*;
import java.util.Arrays; import java.util.ArrayList;
/** public class Factory extends GameObject implements Updatable {
* Game objects that regularly creates NonPlayerCharacters
*/
public class Factory extends Structure implements Updatable, MessageReceiver {
private static final int MAP_INFO = 0x0401; private static final int MAP_INFO = 0x0200;
static final int ID = 3;
/**
* Maximum number of NonPlayerCharacters assigned to this Factory
*/
private static final int MAX_NPC_COUNT = GameServer.INSTANCE.getConfig().getInt("factory_max_npc_count"); private static final int MAX_NPC_COUNT = GameServer.INSTANCE.getConfig().getInt("factory_max_npc_count");
/**
* Number of ticks to wait after creating a NonPlayerCharacter
*/
private static final int NPC_CREATION_COOLDOWN = NonPlayerCharacter.LIFETIME / MAX_NPC_COUNT; private static final int NPC_CREATION_COOLDOWN = NonPlayerCharacter.LIFETIME / MAX_NPC_COUNT;
private ArrayList<NonPlayerCharacter> npcs = new ArrayList<>();
/** /**
* Number of ticks to wait until the Factory can spawn a new NPC * Number of ticks to wait until the Factory can spawn a new NPC
*/ */
private int cooldown = 0; private int cooldown = 0;
private boolean locked = true; /**
* Temporary NPC objectId array. The Factory links the NPCs to itself when initialised,
* at the first call of update().
*/
private Object[] tmpNpcArray = new Object[0];
/** /**
* If non-null, the next spawned NPC will be a HackedNPC and the program will be * Factory are uninitialised until the first update() call
* injected in its memory
*/ */
private char[] program; private boolean initialised = false;
private int programIndex = 0;
private static final int PROGRAM_SIZE = GameServer.INSTANCE.getConfig().getInt("factory_program_size");
public Factory() {
super(2, 2);
}
public Factory(Document document) {
super(document, 2, 2);
}
@Override @Override
public char getMapInfo() { public char getMapInfo() {
return MAP_INFO; return MAP_INFO;
} }
/**
* Called every tick
* <br>The fist time this is called, NPCs retrieved from the database are linked to the Factory
*/
@Override @Override
public void update() { public void update() {
Settlement settlement = NpcPlugin.settlementMap.get(getWorld().getId()); if (!initialised) {
if (settlement == null) { initialised = true;
//Only happens when server is killed during save function
getWorld().decUpdatable();
setDead(true);
return;
}
if (cooldown == 0) { for (Object id : tmpNpcArray) {
if (settlement.getNpcs().size() < MAX_NPC_COUNT) {
Point p = getAdjacentTile();
if (p != null) { NonPlayerCharacter npc = (NonPlayerCharacter) GameServer.INSTANCE.getGameUniverse().getObject((int) (long) id);
NonPlayerCharacter npc = spawnNPC(p);
settlement.addNpc(npc);
getWorld().addObject(npc); if (npc != null) {
getWorld().incUpdatable(); npc.setFactory(this);
npcs.add(npc);
} }
} }
cooldown += NPC_CREATION_COOLDOWN;
} else { } else {
cooldown--;
if (cooldown == 0) {
if (npcs.size() < MAX_NPC_COUNT) {
Point p = getAdjacentTile();
if (p != null) {
NonPlayerCharacter npc = new HarvesterNPC();
npc.setWorld(getWorld());
npc.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
npc.setX(p.x);
npc.setY(p.y);
getWorld().addObject(npc);
getWorld().incUpdatable();
npc.setFactory(this);
npcs.add(npc);
}
}
cooldown += NPC_CREATION_COOLDOWN;
} else {
cooldown--;
}
} }
} }
private NonPlayerCharacter spawnNPC(Point p) {
NonPlayerCharacter npc;
if (programIndex == 0) {
npc = spawnRandomNpc(p);
} else {
npc = spawnHackedNpc(p);
}
return npc;
}
private NonPlayerCharacter spawnRandomNpc(Point p) {
NonPlayerCharacter npc;
npc = new HarvesterNPC();
npc.setWorld(getWorld());
npc.setObjectId(new ObjectId());
npc.setX(p.x);
npc.setY(p.y);
return npc;
}
private NonPlayerCharacter spawnHackedNpc(Point p) {
NonPlayerCharacter npc;
npc = new HackedNPC(program);
npc.setWorld(getWorld());
npc.setObjectId(new ObjectId());
npc.setX(p.x);
npc.setY(p.y);
this.locked = true;
this.programIndex = 0;
return npc;
}
@Override @Override
public boolean sendMessage(char[] message) { public boolean isAt(int x, int y) {
if (locked) { /*
Settlement settlement = NpcPlugin.settlementMap.get(getWorld().getId()); * Object is 2x2 tiles, the (x,y) coordinates of the object being
* at top-left.
* # .
* . .
*/
return (x == getX() + 1 || x == getX()) && (y == getY() + 1 || y == getY());
}
if (Arrays.equals(settlement.getPassword(), message)) { @Override
this.locked = false; public JSONObject serialise() {
return true; JSONObject json = new JSONObject();
}
} else if (programIndex <= PROGRAM_SIZE) {
if (programIndex == 0) { json.put("i", getObjectId());
program = new char[PROGRAM_SIZE]; json.put("x", getX());
} json.put("y", getY());
json.put("t", ID);
System.arraycopy(message, 0, program, programIndex, message.length); JSONArray tmpNpcArray = new JSONArray();
programIndex += message.length;
return true; for (NonPlayerCharacter npc : npcs) {
tmpNpcArray.add(npc.getObjectId());
} }
return true; json.put("n", tmpNpcArray);
return json;
}
@Override
public BasicDBObject mongoSerialise() {
BasicDBObject dbObject = new BasicDBObject();
dbObject.put("i", getObjectId());
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("t", ID);
BasicDBList tmpNpcArray = new BasicDBList();
for (NonPlayerCharacter npc : npcs) {
tmpNpcArray.add(npc.getObjectId());
}
dbObject.put("n", tmpNpcArray);
return dbObject;
}
public static Factory deserialise(DBObject obj) {
Factory factory = new Factory();
factory.setObjectId((long) obj.get("i"));
factory.setX((int) obj.get("x"));
factory.setY((int) obj.get("y"));
factory.tmpNpcArray = ((BasicDBList) obj.get("n")).toArray();
return factory;
}
/**
* Get the first non-blocked tile that is directly adjacent to the factory, starting from the north-east corner
* going clockwise.
*
* @return The coordinates of the first non-blocked tile, null otherwise.
*/
public Point getAdjacentTile() {
/*
* (2,0)
* (2,1)
* (1,2)
* (0,2)
* (-1,1)
* (-1,0)
* (0,-1)
* (1,-1)
*/
if (!getWorld().isTileBlocked(getX() + 2, getY())) {
return new Point(getX() + 2, getY());
} else if (!getWorld().isTileBlocked(getX() + 2, getY() + 1)) {
return new Point(getX() + 2, getY() + 1);
} else if (!getWorld().isTileBlocked(getX() + 1, getY() + 2)) {
return new Point(getX() + 1, getY() + 2);
} else if (!getWorld().isTileBlocked(getX(), getY() + 2)) {
return new Point(getX(), getY() + 2);
} else if (!getWorld().isTileBlocked(getX() + -1, getY() + 1)) {
return new Point(getX() + -1, 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 if (!getWorld().isTileBlocked(getX() + 1, getY() + -1)) {
return new Point(getX() + 1, getY() + -1);
} else {
return null;
}
}
ArrayList<NonPlayerCharacter> getNpcs() {
return npcs;
} }
} }

View File

@ -1,339 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.*;
import net.simon987.server.event.ObjectDeathEvent;
import net.simon987.server.game.item.Item;
import net.simon987.server.game.item.ItemVoid;
import net.simon987.server.game.objects.Action;
import net.simon987.server.game.objects.ControllableUnit;
import net.simon987.server.game.objects.Direction;
import net.simon987.server.logging.LogManager;
import net.simon987.server.user.User;
import org.bson.Document;
import org.json.simple.JSONObject;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HackedNPC extends NonPlayerCharacter implements ControllableUnit {
private static final int MEM_SIZE = GameServer.INSTANCE.getConfig().getInt("hacked_npc_mem_size");
private static final boolean DIE_ON_NO_ENERGY = GameServer.INSTANCE.getConfig().getInt("hacked_npc_die_on_no_energy") != 0;
private CPU cpu;
/**
* List of attached hardware, 'modules'
*/
private Map<Integer, HardwareModule> hardwareAddresses = new HashMap<>();
private Map<Class<? extends HardwareModule>, Integer> hardwareModules = new HashMap<>();
private Action currentAction = Action.IDLE;
private Action lastAction = Action.IDLE;
private ArrayList<char[]> consoleMessagesBuffer = new ArrayList<>(30); //todo load from conf
private ArrayList<char[]> lastConsoleMessagesBuffer = new ArrayList<>(30);
HackedNPC(char[] program) {
cpu = new CPU();
cpu.setMemory(new Memory(MEM_SIZE));
cpu.setHardwareHost(this);
cpu.getMemory().write(cpu.getCodeSectionOffset(), program, 0, program.length);
for (Object serialisedHw : (List) NpcPlugin.DEFAULT_HACKED_NPC.get("hardware")) {
HardwareModule hardware = GameServer.INSTANCE.getRegistry().deserializeHardware((Document) serialisedHw, this);
hardware.setCpu(cpu);
attachHardware(hardware, ((Document) serialisedHw).getInteger("address"));
}
setTask(new ExecuteCpuTask());
}
public HackedNPC(Document document) {
super(document);
setHp(document.getInteger("hp"));
setDirection(Direction.getDirection(document.getInteger("direction")));
cpu = new CPU();
cpu.setHardwareHost(this);
cpu.setMemory(new Memory((Document) document.get("memory")));
cpu.setRegisterSet(RegisterSet.deserialize((Document) document.get("registerSet")));
ArrayList hardwareList = (ArrayList) document.get("hardware");
for (Object serialisedHw : hardwareList) {
HardwareModule hardware = GameServer.INSTANCE.getRegistry().deserializeHardware((Document) serialisedHw, this);
hardware.setCpu(cpu);
attachHardware(hardware, ((Document) serialisedHw).getInteger("address"));
}
setTask(new ExecuteCpuTask());
}
@Override
public void update() {
super.update();
lastAction = currentAction;
currentAction = Action.IDLE;
lastConsoleMessagesBuffer = new ArrayList<>(consoleMessagesBuffer);
consoleMessagesBuffer.clear();
for (HardwareModule module : hardwareAddresses.values()) {
module.update();
}
//Self-destroy when age limit is reached
if (getAge() >= NonPlayerCharacter.LIFETIME) {
setDead(true);
}
//Don't bother calling checkCompleted()
getTask().tick(this);
}
@Override
public void setKeyboardBuffer(ArrayList<Integer> kbBuffer) {
LogManager.LOGGER.warning("Something went wrong here: Hacked NPC has no keyboard module" +
"@HackedNPC::setKeyBoardBuffer()");
Thread.dumpStack();
}
@Override
public void setParent(User user) {
LogManager.LOGGER.warning("Something went wrong here: Hacked NPC has no parent" +
"@HackedNPC::setParent()");
Thread.dumpStack();
}
@Override
public User getParent() {
LogManager.LOGGER.warning("Something went wrong here: Hacked NPC has no parent" +
"@HackedNPC::getParent()");
Thread.dumpStack();
return null;
}
@Override
public ArrayList<Integer> getKeyboardBuffer() {
LogManager.LOGGER.warning("Something went wrong here: Hacked NPC has no keyboard module" +
"@HackedNPC::getKeyBoardBuffer()");
Thread.dumpStack();
return null;
}
@Override
public Memory getFloppyData() {
LogManager.LOGGER.warning("Something went wrong here: Hacked NPC has floppy data." +
"@HackedNPC::getFloppyData()");
Thread.dumpStack();
return null;
}
@Override
public void setAction(Action action) {
currentAction = action;
}
@Override
public ArrayList<char[]> getConsoleMessagesBuffer() {
return lastConsoleMessagesBuffer;
}
@Override
public int getConsoleMode() {
LogManager.LOGGER.warning("Something went wrong here: Hacked NPC has no console UI." +
"@HackedNPC::getConsoleMode()");
Thread.dumpStack();
return 0;
}
@Override
public CPU getCpu() {
return cpu;
}
@Override
public void giveItem(Item item) {
//Overwrite item at current position
((NpcInventory) getHardware(NpcInventory.class)).putItem(item);
}
@Override
public void attachHardware(HardwareModule hardware, int address) {
hardwareAddresses.put(address, hardware);
hardwareModules.put(hardware.getClass(), address);
}
@Override
public void detachHardware(int address) {
hardwareAddresses.remove(address);
Class<? extends HardwareModule> toRemove = null;
for (Class<? extends HardwareModule> clazz : hardwareModules.keySet()) {
if (hardwareModules.get(clazz) == address) {
toRemove = clazz;
}
}
hardwareModules.remove(toRemove);
}
@Override
public boolean hardwareInterrupt(int address, Status status) {
HardwareModule hardware = hardwareAddresses.get(address);
if (hardware != null) {
hardware.handleInterrupt(status);
return true;
} else {
return false;
}
}
@Override
public int hardwareQuery(int address) {
HardwareModule hardware = hardwareAddresses.get(address);
if (hardware != null) {
return hardware.getId();
} else {
return 0;
}
}
public int getEnergy() {
NpcBattery battery = (NpcBattery) getHardware(NpcBattery.class);
return battery.getEnergy();
}
public void setEnergy(int energy) {
NpcBattery battery = (NpcBattery) getHardware(NpcBattery.class);
battery.setEnergy(energy);
if (energy == 0 && DIE_ON_NO_ENERGY) {
setDead(true);
}
}
public boolean spendEnergy(int amount) {
NpcBattery battery = (NpcBattery) getHardware(NpcBattery.class);
if (battery.getEnergy() - amount < 0) {
if (DIE_ON_NO_ENERGY) {
setDead(true);
}
return false;
} else {
battery.setEnergy(battery.getEnergy() - amount);
return true;
}
}
@Override
public Document mongoSerialise() {
Document dbObject = super.mongoSerialise();
dbObject.put("direction", getDirection().ordinal());
dbObject.put("hp", getHp());
dbObject.put("action", lastAction.ordinal());
List<Document> hardwareList = new ArrayList<>();
for (Integer address : hardwareAddresses.keySet()) {
HardwareModule hardware = hardwareAddresses.get(address);
Document serialisedHw = hardware.mongoSerialise();
serialisedHw.put("address", address);
hardwareList.add(serialisedHw);
}
dbObject.put("hardware", hardwareList);
dbObject.put("memory", cpu.getMemory().mongoSerialise());
dbObject.put("registerSet", cpu.getRegisterSet().mongoSerialise());
return dbObject;
}
public void storeEnergy(int amount) {
NpcBattery battery = (NpcBattery) getHardware(NpcBattery.class);
battery.setEnergy(Math.min(battery.getEnergy() + amount, battery.getMaxEnergy()));
}
private HardwareModule getHardware(Class<? extends HardwareModule> clazz) {
return hardwareAddresses.get(hardwareModules.get(clazz));
}
public void setMaxEnergy(int maxEnergy) {
NpcBattery battery = (NpcBattery) getHardware(NpcBattery.class);
battery.setMaxEnergy(maxEnergy);
}
public int getMaxEnergy() {
NpcBattery battery = (NpcBattery) getHardware(NpcBattery.class);
return battery.getMaxEnergy();
}
@Override
public boolean sendMessage(char[] message) {
return false;
}
@Override
public void setCurrentAction(Action action) {
currentAction = action;
}
@Override
public Action getCurrentAction() {
return currentAction;
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = super.jsonSerialise();
for (HardwareModule module : hardwareAddresses.values()) {
JSONObject hwJson = module.jsonSerialise();
if (hwJson != null) {
json.put(module.getClass().getName(), hwJson);
}
}
json.put("direction", getDirection().ordinal());
NpcInventory inv = (NpcInventory) getHardware(NpcInventory.class);
Item item = inv.getItem();
json.put("heldItem", item == null ? new ItemVoid().getId() : item.getId());
json.put("hp", getHp());
json.put("action", lastAction.ordinal());
return json;
}
@Override
public boolean onDeadCallback() {
getWorld().decUpdatable();
if (getSettlement() != null && getSettlement().getNpcs() != null) {
getSettlement().getNpcs().remove(this);
}
GameServer.INSTANCE.getEventDispatcher().dispatch(new ObjectDeathEvent(this));
return false;
}
@Override
public JSONObject debugJsonSerialise() {
return jsonSerialise();
}
}

View File

@ -2,39 +2,27 @@ package net.simon987.npcplugin;
import net.simon987.server.assembly.Util; import net.simon987.server.assembly.Util;
import net.simon987.server.game.objects.Direction; import net.simon987.server.game.Direction;
import net.simon987.server.game.objects.GameObject; import net.simon987.server.game.GameObject;
import net.simon987.server.game.objects.InventoryHolder; import net.simon987.server.game.InventoryHolder;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Random; import java.util.Random;
/**
* Find Biomass, move towards it, collect it, repeat
*/
public class HarvestTask extends NPCTask { public class HarvestTask extends NPCTask {
private Random random; private Random random;
/**
* Number of ticks to wait before continuing
*/
private int pause; private int pause;
/**
* Direction of the next world to visit (randomly chosen)
*/
private Direction nextWorldDirection = null;
public HarvestTask() { public HarvestTask() {
random = new Random(); random = new Random();
pause = 0; pause = 0;
} }
/** private Direction nextWorldDirection = null;
* This task never finishes
*/
@Override @Override
public boolean checkCompleted() { public boolean checkCompleted() {
return false; return false;
@ -45,7 +33,9 @@ public class HarvestTask extends NPCTask {
if (pause == 0) { if (pause == 0) {
//Get biomass //Get biomass
ArrayList<GameObject> biomass = npc.getWorld().findGameObjects("net.simon987.biomassplugin.BiomassBlob"); /* todo replace by some sort of .collect call with object
id (See https://github.com/simon987/Much-Assembly-Required/pull/66)*/
ArrayList<GameObject> biomass = npc.getWorld().findObjects(0x4000);
//Get closest one //Get closest one
int minDist = Integer.MAX_VALUE; int minDist = Integer.MAX_VALUE;
@ -103,5 +93,8 @@ public class HarvestTask extends NPCTask {
} else { } else {
pause--; pause--;
} }
} }
} }

View File

@ -1,17 +1,21 @@
package net.simon987.npcplugin; package net.simon987.npcplugin;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.event.ObjectDeathEvent; import net.simon987.server.event.ObjectDeathEvent;
import net.simon987.server.game.objects.Direction; import net.simon987.server.game.Direction;
import org.bson.Document;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
public class HarvesterNPC extends NonPlayerCharacter { public class HarvesterNPC extends NonPlayerCharacter {
public static final int ID = 10;
public static final int MAX_HEALTH = GameServer.INSTANCE.getConfig().getInt("harvester_hp_max"); public static final int MAX_HEALTH = GameServer.INSTANCE.getConfig().getInt("harvester_hp_max");
public static final int HEAL_RATE = GameServer.INSTANCE.getConfig().getInt("harvester_regen"); public static final int HEAL_RATE = GameServer.INSTANCE.getConfig().getInt("harvester_regen");
public HarvesterNPC() { public HarvesterNPC() {
setTask(new HarvestTask()); setTask(new HarvestTask());
@ -20,19 +24,12 @@ public class HarvesterNPC extends NonPlayerCharacter {
setHealRate(HEAL_RATE); setHealRate(HEAL_RATE);
} }
public HarvesterNPC(Document document) {
super(document);
setTask(new HarvestTask());
setDirection(Direction.getDirection(document.getInteger("direction")));
}
@Override @Override
public void update() { public void update() {
super.update(); super.update();
if (getSettlement() != null) { if (getFactory() != null) {
if (getTask().checkCompleted()) { if (getTask().checkCompleted()) {
setTask(new HarvestTask()); setTask(new HarvestTask());
@ -49,47 +46,60 @@ public class HarvesterNPC extends NonPlayerCharacter {
} }
@Override @Override
public boolean onDeadCallback() { public void onDeadCallback() {
getWorld().decUpdatable(); if (getFactory() != null && getFactory().getNpcs() != null) {
getFactory().getNpcs().remove(this);
if (getSettlement() != null && getSettlement().getNpcs() != null) {
getSettlement().getNpcs().remove(this);
} }
GameServer.INSTANCE.getEventDispatcher().dispatch(new ObjectDeathEvent(this)); GameServer.INSTANCE.getEventDispatcher().dispatch(
new ObjectDeathEvent(this, ID));
return false;
} }
@Override @Override
public JSONObject jsonSerialise() { public JSONObject serialise() {
JSONObject json = super.jsonSerialise(); JSONObject json = super.serialise();
json.put("i", getObjectId());
json.put("x", getX());
json.put("y", getY());
json.put("direction", getDirection().ordinal()); json.put("direction", getDirection().ordinal());
json.put("hp", getHp()); json.put("hp", getHp());
json.put("energy", energy);
json.put("action", getAction().ordinal()); json.put("action", getAction().ordinal());
json.put("t", ID);
return json; return json;
} }
@Override @Override
public JSONObject debugJsonSerialise() { public BasicDBObject mongoSerialise() {
JSONObject json = jsonSerialise(); BasicDBObject dbObject = new BasicDBObject();
json.put("taskCompleted", getTask().checkCompleted());
return json;
}
@Override
public Document mongoSerialise() {
Document dbObject = super.mongoSerialise();
dbObject.put("i", getObjectId());
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("direction", getDirection().ordinal()); dbObject.put("direction", getDirection().ordinal());
dbObject.put("hp", getHp()); dbObject.put("hp", getHp());
// dbObject.put("energy", energy);
dbObject.put("action", getAction().ordinal()); dbObject.put("action", getAction().ordinal());
dbObject.put("t", ID);
return dbObject; return dbObject;
} }
public static HarvesterNPC deserialize(DBObject obj) {
HarvesterNPC npc = new HarvesterNPC();
npc.setObjectId((long) obj.get("i"));
npc.setX((int) obj.get("x"));
npc.setY((int) obj.get("y"));
npc.setHp((int) obj.get("hp"));
npc.setDirection(Direction.getDirection((int) obj.get("direction")));
// npc.energy = (int) obj.get("energy");
// npc.maxEnergy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
return npc;
}
} }

View File

@ -2,46 +2,46 @@ package net.simon987.npcplugin;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.assembly.Util; import net.simon987.server.assembly.Util;
import net.simon987.server.game.objects.*; import net.simon987.server.game.*;
import net.simon987.server.game.pathfinding.Node; import net.simon987.server.game.pathfinding.Node;
import net.simon987.server.game.pathfinding.Pathfinder; import net.simon987.server.game.pathfinding.Pathfinder;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import org.bson.Document;
import java.util.ArrayList; import java.util.ArrayList;
/**
* Game object that actively interacts with the game world by doing tasks
*/
public abstract class NonPlayerCharacter extends GameObject implements Updatable, Attackable { public abstract class NonPlayerCharacter extends GameObject implements Updatable, Attackable {
private static final char MAP_INFO = 0x0501; private static final int MAP_INFO = 0x0040;
/**
* Maximum distance to travel from its factory, in Worlds
*/
private static final int MAX_FACTORY_DISTANCE = GameServer.INSTANCE.getConfig().getInt("npc_max_factory_distance"); private static final int MAX_FACTORY_DISTANCE = GameServer.INSTANCE.getConfig().getInt("npc_max_factory_distance");
/**
* Number of ticks to live
*/
public static final int LIFETIME = GameServer.INSTANCE.getConfig().getInt("npc_lifetime"); public static final int LIFETIME = GameServer.INSTANCE.getConfig().getInt("npc_lifetime");
// Set these just in case they aren't overridden in the subclass // Set these just in case they aren't overridden in the subclass
public static final int HP_MAX_DEFAULT = 100; public static final int HP_MAX_DEFAULT = 100;
public static final int HP_REGEN_RATE_DEFAULT = 0; public static final int HP_REGEN_RATE_DEFAULT = 0;
//Unused
int energy;
int maxEnergy;
/** /**
* Current task * Current task
*/ */
private NPCTask task; private NPCTask task;
/**
* Action at the end of the last tick
*/
private Action lastAction = Action.IDLE; private Action lastAction = Action.IDLE;
private Settlement settlement; /**
* Factory that created this NPC
*/
private Factory factory;
/**
* If set to true, the NPC will be destroyed next tick if it is
* not linked to a Factory
*/
private boolean selfDestroyNextTick = false;
/** /**
* Age of the npc, in ticks * Age of the npc, in ticks
@ -63,17 +63,6 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable
*/ */
private int maxHp = HP_MAX_DEFAULT; private int maxHp = HP_MAX_DEFAULT;
public NonPlayerCharacter() {
}
public NonPlayerCharacter(Document document) {
super(document);
hp = document.getInteger("hp");
setDirection(Direction.getDirection(document.getInteger("direction")));
}
@Override @Override
public char getMapInfo() { public char getMapInfo() {
return MAP_INFO; return MAP_INFO;
@ -84,9 +73,13 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable
age++; age++;
//Destroy NPCs that are not linked with a Settlement //Destroy NPCs that are not linked with a Factory
if (settlement == null) { if (factory == null) {
setDead(true); if (selfDestroyNextTick) {
setDead(true);
}
selfDestroyNextTick = true;
} }
//Heal the NPC //Heal the NPC
@ -134,7 +127,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable
if (direction == Direction.NORTH) { if (direction == Direction.NORTH) {
if (Util.manhattanDist(settlement.getWorld().getX(), settlement.getWorld().getY(), if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(),
getWorld().getX(), getWorld().getY() - 1) <= MAX_FACTORY_DISTANCE) { getWorld().getX(), getWorld().getY() - 1) <= MAX_FACTORY_DISTANCE) {
if (!moveTo(8, 0, 0)) { if (!moveTo(8, 0, 0)) {
setDirection(Direction.NORTH); setDirection(Direction.NORTH);
@ -146,7 +139,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable
} }
} else if (direction == Direction.EAST) { } else if (direction == Direction.EAST) {
if (Util.manhattanDist(settlement.getWorld().getX(), settlement.getWorld().getY(), if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(),
getWorld().getX() + 1, getWorld().getY()) <= MAX_FACTORY_DISTANCE) { getWorld().getX() + 1, getWorld().getY()) <= MAX_FACTORY_DISTANCE) {
if (!moveTo(15, 7, 0)) { if (!moveTo(15, 7, 0)) {
setDirection(Direction.EAST); setDirection(Direction.EAST);
@ -157,7 +150,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable
return false; return false;
} }
} else if (direction == Direction.SOUTH) { } else if (direction == Direction.SOUTH) {
if (Util.manhattanDist(settlement.getWorld().getX(), settlement.getWorld().getY(), if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(),
getWorld().getX(), getWorld().getY() + 1) <= MAX_FACTORY_DISTANCE) { getWorld().getX(), getWorld().getY() + 1) <= MAX_FACTORY_DISTANCE) {
if (!moveTo(8, 15, 0)) { if (!moveTo(8, 15, 0)) {
setDirection(Direction.SOUTH); setDirection(Direction.SOUTH);
@ -168,7 +161,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable
return false; return false;
} }
} else if (direction == Direction.WEST) { } else if (direction == Direction.WEST) {
if (Util.manhattanDist(settlement.getWorld().getX(), settlement.getWorld().getY(), if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(),
getWorld().getX() - 1, getWorld().getY()) <= MAX_FACTORY_DISTANCE) { getWorld().getX() - 1, getWorld().getY()) <= MAX_FACTORY_DISTANCE) {
if (!moveTo(0, 7, 0)) { if (!moveTo(0, 7, 0)) {
setDirection(Direction.WEST); setDirection(Direction.WEST);
@ -241,15 +234,15 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable
return lastAction; return lastAction;
} }
public Factory getFactory() {
return factory;
}
public void setFactory(Factory factory) {
this.factory = factory;
}
public int getAge() { public int getAge() {
return age; return age;
} }
public Settlement getSettlement() {
return settlement;
}
public void setSettlement(Settlement settlement) {
this.settlement = settlement;
}
} }

View File

@ -1,109 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.HardwareModule;
import net.simon987.server.assembly.Status;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
import org.json.simple.JSONObject;
public class NpcBattery extends HardwareModule {
public static final int DEFAULT_ADDRESS = 0x010A;
/**
* Hardware ID (Should be unique)
*/
public static final char HWID = 0x010A;
/**
* Energy units in kJ
*/
private int energy;
/**
* Maximum energy units in kJ
*/
private int maxEnergy;
private static final int BATTERY_POLL = 1;
private static final int BATTERY_GET_MAX_CAPACITY = 2;
public NpcBattery(ControllableUnit unit) {
super(null, unit);
energy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
maxEnergy = GameServer.INSTANCE.getConfig().getInt("battery_max_energy");
}
public NpcBattery(Document document, ControllableUnit cubot) {
super(document, cubot);
energy = document.getInteger("energy");
maxEnergy = document.getInteger("max_energy");
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == BATTERY_POLL) {
getCpu().getRegisterSet().getRegister("B").setValue(unit.getEnergy());
} else if (a == BATTERY_GET_MAX_CAPACITY) {
getCpu().getRegisterSet().getRegister("B").setValue(unit.getMaxEnergy());
}
}
@Override
public char getId() {
return HWID;
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = new JSONObject();
json.put("energy", energy);
return json;
}
@Override
public JSONObject debugJsonSerialise() {
JSONObject json = jsonSerialise();
json.put("max_energy", maxEnergy);
return json;
}
@Override
public Document mongoSerialise() {
Document document = super.mongoSerialise();
document.put("energy", energy);
document.put("max_energy", maxEnergy);
return document;
}
public int getEnergy() {
return energy;
}
public void setEnergy(int energy) {
this.energy = energy;
}
public int getMaxEnergy() {
return maxEnergy;
}
public void setMaxEnergy(int maxEnergy) {
this.maxEnergy = maxEnergy;
}
}

View File

@ -1,109 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.HardwareModule;
import net.simon987.server.assembly.Status;
import net.simon987.server.game.item.Item;
import net.simon987.server.game.objects.ControllableUnit;
import org.bson.Document;
public class NpcInventory extends HardwareModule {
/**
* Hardware ID (Should be unique)
*/
static final char HWID = 0x0106;
public static final int DEFAULT_ADDRESS = 0x0106;
private static final int INV_CLEAR = 0;
private static final int INV_POLL = 1;
private static final int INV_SCAN = 3;
private Item item;
public NpcInventory(ControllableUnit unit) {
super(null, unit);
}
public NpcInventory(Document document, ControllableUnit cubot) {
super(document, cubot);
Document itemDoc = (Document) document.get("item");
if (itemDoc != null) {
item = GameServer.INSTANCE.getRegistry().deserializeItem(itemDoc);
}
}
public void putItem(Item item) {
this.item = item;
}
private void scanItem() {
int x = getCpu().getRegisterSet().getRegister("X").getValue();
item.digitize(unit.getCpu().getMemory(), x);
}
public Item clearItem() {
Item oldItem = item;
item.clear(unit);
item = null;
return oldItem;
}
@Override
public char getId() {
return HWID;
}
public Item getItem() {
return item;
}
@Override
public void handleInterrupt(Status status) {
int a = getCpu().getRegisterSet().getRegister("A").getValue();
if (a == INV_POLL) {
char result;
if (item == null) {
result = 0;
} else {
result = item.poll();
}
getCpu().getRegisterSet().getRegister("B").setValue(result);
} else if (a == INV_CLEAR) {
if (unit.spendEnergy(100)) {
clearItem();
}
} else if (a == INV_SCAN) {
if (unit.spendEnergy(200)) {
scanItem();
clearItem();
}
}
}
@Override
public Document mongoSerialise() {
Document document = super.mongoSerialise();
if (item != null) {
document.put("item", item.mongoSerialise());
} else {
document.put("item", null);
}
return document;
}
@Override
public String toString() {
return String.format("{NpcInventory [%s]}", item);
}
}

View File

@ -1,93 +1,65 @@
package net.simon987.npcplugin; package net.simon987.npcplugin;
import com.mongodb.DBObject;
import net.simon987.npcplugin.event.CpuInitialisationListener; import net.simon987.npcplugin.event.CpuInitialisationListener;
import net.simon987.npcplugin.event.VaultCompleteListener;
import net.simon987.npcplugin.event.VaultWorldUpdateListener;
import net.simon987.npcplugin.event.WorldCreationListener; import net.simon987.npcplugin.event.WorldCreationListener;
import net.simon987.npcplugin.world.TileVaultFloor; import net.simon987.server.ServerConfiguration;
import net.simon987.npcplugin.world.TileVaultWall; import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.GameServer; import net.simon987.server.game.GameObject;
import net.simon987.server.IServerConfiguration; import net.simon987.server.io.CpuHardwareDeserializer;
import net.simon987.server.game.objects.GameRegistry; import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin; import net.simon987.server.plugin.ServerPlugin;
import org.bson.Document;
import java.io.InputStream; import java.util.ArrayList;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
public class NpcPlugin extends ServerPlugin { public class NpcPlugin extends ServerPlugin implements GameObjectDeserializer, CpuHardwareDeserializer {
public static Map<String, Settlement> settlementMap; /**
* Radio tower cache
public static Document DEFAULT_HACKED_NPC; */
private static ArrayList<RadioTower> radioTowers;
@Override @Override
public void init(GameServer gameServer) { public void init(ServerConfiguration configuration) {
IServerConfiguration configuration = gameServer.getConfig(); listeners.add(new WorldCreationListener());
GameRegistry registry = gameServer.getRegistry();
listeners.add(new WorldCreationListener(configuration.getInt("settlement_spawn_rate")));
listeners.add(new CpuInitialisationListener()); listeners.add(new CpuInitialisationListener());
listeners.add(new VaultWorldUpdateListener(configuration));
listeners.add(new VaultCompleteListener());
registry.registerGameObject(HarvesterNPC.class); radioTowers = new ArrayList<>(32);
registry.registerGameObject(Factory.class);
registry.registerGameObject(RadioTower.class);
registry.registerGameObject(VaultDoor.class);
registry.registerGameObject(Obstacle.class);
registry.registerGameObject(ElectricBox.class);
registry.registerGameObject(Portal.class);
registry.registerGameObject(VaultExitPortal.class);
registry.registerGameObject(HackedNPC.class);
registry.registerHardware(RadioReceiverHardware.class); LogManager.LOGGER.info("Initialised NPC plugin");
registry.registerHardware(NpcBattery.class);
registry.registerHardware(NpcInventory.class);
registry.registerTile(TileVaultFloor.ID, TileVaultFloor.class);
registry.registerTile(TileVaultWall.ID, TileVaultWall.class);
settlementMap = new ConcurrentHashMap<>();
LogManager.LOGGER.fine("(NPC Plugin) Loading default HackedNPC settings from" +
" defaultHackedCubotHardware.json");
InputStream is = getClass().getClassLoader().getResourceAsStream("defaultHackedCubotHardware.json");
Scanner scanner = new Scanner(is).useDelimiter("\\A");
String json = scanner.next();
DEFAULT_HACKED_NPC = Document.parse(json);
LogManager.LOGGER.info("(NPC Plugin) Initialised NPC plugin");
} }
@Override @Override
public Document mongoSerialise() { public GameObject deserializeObject(DBObject obj) {
Document document = super.mongoSerialise();
Document settlements = new Document(); int objType = (int) obj.get("t");
for (String world : settlementMap.keySet()) {
settlements.put(world, settlementMap.get(world).mongoSerialise()); if (objType == HarvesterNPC.ID) {
return HarvesterNPC.deserialize(obj);
} else if (objType == Factory.ID) {
return Factory.deserialise(obj);
} else if (objType == RadioTower.ID) {
return RadioTower.deserialize(obj);
} }
document.put("settlement_map", settlements); return null;
return document;
} }
@Override @Override
public void load(Document document) { public CpuHardware deserializeHardware(DBObject obj) {
super.load(document); int hwid = (int) obj.get("hwid");
Document settlements = (Document) document.get("settlement_map"); switch (hwid) {
case RadioReceiverHardware.HWID:
for (String world : settlements.keySet()) { return RadioReceiverHardware.deserialize(obj);
settlementMap.put(world, new Settlement((Document) settlements.get(world)));
} }
LogManager.LOGGER.fine(String.format("(%s) Loaded %d settlements", name, settlementMap.size())); return null;
}
public static ArrayList<RadioTower> getRadioTowers() {
return radioTowers;
} }
} }

View File

@ -1,113 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.game.objects.Attackable;
import net.simon987.server.game.objects.GameObject;
import org.bson.Document;
import org.json.simple.JSONObject;
/**
* Generic game object that blocks the path.
*/
public class Obstacle extends GameObject implements Attackable {
public static final int MAP_INFO = 0x0701;
/**
* Style of the obstacle. Will tell the client which sprite to display
*/
private int style = 0;
/**
* Current health of the npc
*/
private int hp;
/**
* Maximum health of the npc
*/
private int maxHp;
public Obstacle(int hp) {
this.hp = hp;
this.maxHp = hp;
}
public Obstacle(Document document) {
super(document);
style = document.getInteger("style");
}
@Override
public void setHealRate(int hp) {
//No op
}
@Override
public void heal(int amount) {
//No op
}
@Override
public int getHp() {
return hp;
}
@Override
public void setHp(int hp) {
this.hp = hp;
}
@Override
public int getMaxHp() {
return maxHp;
}
@Override
public void setMaxHp(int hp) {
this.maxHp = hp;
this.hp = hp;
}
@Override
public void damage(int amount) {
hp -= amount;
//YOU ARE DEAD
if (hp <= 0) {
setDead(true);
}
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
public int getStyle() {
return style;
}
public void setStyle(int style) {
this.style = style;
}
@Override
public Document mongoSerialise() {
Document dbObject = super.mongoSerialise();
dbObject.put("hp", hp);
dbObject.put("style", style);
return dbObject;
}
@Override
public JSONObject jsonSerialise() {
JSONObject json = super.jsonSerialise();
json.put("hp", hp);
json.put("style", style);
return json;
}
}

View File

@ -1,89 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.GameServer;
import net.simon987.server.game.objects.Enterable;
import net.simon987.server.game.objects.GameObject;
import net.simon987.server.game.objects.Structure;
import net.simon987.server.game.objects.Updatable;
import net.simon987.server.game.world.Location;
import net.simon987.server.game.world.World;
import org.bson.Document;
public class Portal extends Structure implements Enterable {
/**
* Destination location
*/
private Location destination;
public static final int MAP_INFO = 0x0801;
public Portal() {
super(1, 1);
}
public Portal(Document document) {
super(document, 1, 1);
destination = new Location(
document.getInteger("dstWorldX"),
document.getInteger("dstWorldY"),
document.getString("dstDimension"),
document.getInteger("dstX"),
document.getInteger("dstY"));
setX(document.getInteger("x"));
setY(document.getInteger("y"));
}
/**
* Called when an object attempts to walk directly into a Enterable object
*
* @param object The game object that attempted to enter
* @return true if successful, false to block the object
*/
@Override
public boolean enter(GameObject object) {
World world = GameServer.INSTANCE.getGameUniverse().getWorld(destination.worldX, destination.worldY, false, destination.dimension);
if (object instanceof Updatable) {
object.getWorld().decUpdatable();
world.incUpdatable();
}
object.getWorld().removeObject(object);
object.setWorld(world);
world.addObject(object);
object.setX(destination.x);
object.setY(destination.y);
return true;
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public Document mongoSerialise() {
Document dbObject = super.mongoSerialise();
dbObject.put("dstWorldX", destination.worldX);
dbObject.put("dstWorldY", destination.worldY);
dbObject.put("dstX", destination.x);
dbObject.put("dstY", destination.y);
dbObject.put("dstDimension", destination.dimension);
return dbObject;
}
public Location getDestination() {
return destination;
}
public void setDestination(Location destination) {
this.destination = destination;
}
}

View File

@ -1,16 +1,17 @@
package net.simon987.npcplugin; package net.simon987.npcplugin;
import net.simon987.server.assembly.HardwareModule; import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.assembly.Status; import net.simon987.server.assembly.Status;
import net.simon987.server.assembly.Util; import net.simon987.server.assembly.Util;
import net.simon987.server.game.objects.Action; import net.simon987.server.game.Action;
import net.simon987.server.game.objects.ControllableUnit; import net.simon987.server.game.ControllableUnit;
import org.bson.Document;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
public class RadioReceiverHardware extends HardwareModule { public class RadioReceiverHardware extends CpuHardware {
public static final char HWID = 0xC; //12 public static final char HWID = 0xC; //12
@ -24,12 +25,6 @@ public class RadioReceiverHardware extends HardwareModule {
this.cubot = cubot; this.cubot = cubot;
} }
public RadioReceiverHardware(Document document, ControllableUnit unit) {
super(document, unit);
this.cubot = unit;
}
@Override @Override
public void handleInterrupt(Status status) { public void handleInterrupt(Status status) {
int x = getCpu().getRegisterSet().getRegister("X").getValue(); int x = getCpu().getRegisterSet().getRegister("X").getValue();
@ -40,14 +35,12 @@ public class RadioReceiverHardware extends HardwareModule {
//Find the nearest Radio Tower and query it //Find the nearest Radio Tower and query it
cubot.setAction(Action.LISTENING); cubot.setAction(Action.LISTENING);
List<char[]> messages = new ArrayList<>(6); ArrayList<char[]> messages = new ArrayList<>(6);
for (String world : NpcPlugin.settlementMap.keySet()) { ArrayList<RadioTower> towers = new ArrayList<>(NpcPlugin.getRadioTowers()); //Avoid ConcurrentModificationException
RadioTower tower = NpcPlugin.settlementMap.get(world).getRadioTower(); for (RadioTower tower : towers) {
if (Util.manhattanDist(tower.getWorld().getX(), tower.getWorld().getY(), cubot.getWorld().getX(),
if (tower != null && Util.manhattanDist( cubot.getWorld().getY()) <= RadioTower.MAX_RANGE) {
tower.getWorld().getX(), tower.getWorld().getY(),
cubot.getWorld().getX(), cubot.getWorld().getY()) <= RadioTower.MAX_RANGE) {
//Tower is in range //Tower is in range
messages.addAll(tower.getMessages()); messages.addAll(tower.getMessages());
} }
@ -74,13 +67,17 @@ public class RadioReceiverHardware extends HardwareModule {
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document dbObject = new Document(); BasicDBObject dbObject = new BasicDBObject();
dbObject.put("type", getClass().getCanonicalName()); dbObject.put("hwid", (int) HWID);
dbObject.put("cubot", cubot.getObjectId()); dbObject.put("cubot", cubot.getObjectId());
return dbObject; return dbObject;
} }
public static RadioReceiverHardware deserialize(DBObject obj) {
return new RadioReceiverHardware((ControllableUnit) GameServer.INSTANCE.getGameUniverse().getObject((long) obj.get("cubot")));
}
} }

View File

@ -1,36 +1,31 @@
package net.simon987.npcplugin; package net.simon987.npcplugin;
import net.simon987.server.GameServer; import com.mongodb.BasicDBObject;
import net.simon987.server.game.objects.MessageReceiver; import com.mongodb.DBObject;
import net.simon987.server.game.objects.Structure; import net.simon987.server.game.GameObject;
import net.simon987.server.game.objects.Updatable; import net.simon987.server.game.Programmable;
import org.bson.Document; import net.simon987.server.game.Updatable;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import java.awt.*;
import java.util.ArrayList; import java.util.ArrayList;
public class RadioTower extends Structure implements MessageReceiver, Updatable { public class RadioTower extends GameObject implements Programmable, Updatable {
private static final int MAP_INFO = 0x0901; private static final int MAP_INFO = 0x1000;
public static final int MAX_RANGE = GameServer.INSTANCE.getConfig().getInt("radio_tower_range"); public static final int ID = 4;
public static final int MAX_RANGE = 3; //todo load from config
private static final int MAX_MESSAGES = 16; private static final int MAX_MESSAGES = 16;
public RadioTower() {
super(1, 1);
}
public RadioTower(Document document) {
super(document, 1, 1);
}
@Override @Override
public char getMapInfo() { public char getMapInfo() {
return MAP_INFO; return MAP_INFO;
} }
/** /**
* Messages from the current tick * Messages from the current tick
*/ */
@ -58,22 +53,68 @@ public class RadioTower extends Structure implements MessageReceiver, Updatable
} }
} }
ArrayList<char[]> getMessages() { @Override
return lastMessages; 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;
} }
@Override @Override
public JSONObject debugJsonSerialise() { public BasicDBObject mongoSerialise() {
JSONObject json = super.debugJsonSerialise(); BasicDBObject dbObject = new BasicDBObject();
JSONArray messages = new JSONArray(); dbObject.put("i", getObjectId());
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("t", ID);
for (char[] message : this.messages) { return dbObject;
messages.add(new String(message)); }
public static RadioTower deserialize(DBObject obj) {
RadioTower tower = new RadioTower();
tower.setObjectId((long) obj.get("i"));
tower.setX((int) obj.get("x"));
tower.setY((int) obj.get("y"));
NpcPlugin.getRadioTowers().add(tower);
return tower;
}
public ArrayList<char[]> 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;
} }
json.put("messages", messages);
return json;
} }
} }

View File

@ -1,222 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.GameServer;
import net.simon987.server.game.world.TilePlain;
import net.simon987.server.game.world.World;
import net.simon987.server.game.world.WorldGenerationException;
import net.simon987.server.io.MongoSerializable;
import org.bson.Document;
import org.bson.types.ObjectId;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
public class Settlement implements MongoSerializable {
private Factory factory = null;
private RadioTower radioTower = null;
private VaultDoor vaultDoor = null;
private World world;
private DifficultyLevel difficultyLevel;
private List<NonPlayerCharacter> npcs = new ArrayList<>();
private char[] password;
public Settlement(Document document) {
world = GameServer.INSTANCE.getGameUniverse().getWorld(document.getString("world"), false);
ObjectId radioTowerId = document.getObjectId("radio_tower");
if (radioTowerId != null) {
radioTower = (RadioTower) GameServer.INSTANCE.getGameUniverse().getObject(radioTowerId);
}
ObjectId vaultDoorId = document.getObjectId("vault_door");
if (vaultDoorId != null) {
vaultDoor = (VaultDoor) GameServer.INSTANCE.getGameUniverse().getObject(vaultDoorId);
}
ObjectId factoryId = document.getObjectId("factory");
factory = (Factory) GameServer.INSTANCE.getGameUniverse().getObject(factoryId);
difficultyLevel = DifficultyLevel.values()[document.getInteger("difficulty_level")];
Object[] npcArray = ((ArrayList) document.get("npcs")).toArray();
for (Object id : npcArray) {
NonPlayerCharacter npc = (NonPlayerCharacter) GameServer.INSTANCE.getGameUniverse().getObject((ObjectId) id);
if (npc != null) {
addNpc(npc);
}
}
password = document.getString("password").toCharArray();
}
public Settlement(World world) throws WorldGenerationException {
this.world = world;
this.difficultyLevel = DifficultyLevel.NORMAL; //TODO randomize ?
this.password = "12345678".toCharArray();
outerLoopFactory:
for (int x = 2; x < 12; x++) {
for (int y = 2; y < 12; y++) {
if ((!world.isTileBlocked(x, y) && !world.isTileBlocked(x + 1, y) &&
!world.isTileBlocked(x, y + 1) && !world.isTileBlocked(x + 1, y + 1))) {
Factory factory = new Factory();
factory.setWorld(world);
factory.setObjectId(new ObjectId());
factory.setX(x);
factory.setY(y);
if (factory.getAdjacentTile() == null) {
//Factory has no non-blocked adjacent tiles
continue;
}
world.addObject(factory);
world.incUpdatable();
this.factory = factory;
break outerLoopFactory;
}
}
}
if (factory == null) {
throw new WorldGenerationException("Could not place Factory");
}
//Also spawn a radio tower in the same World
Point p = world.getRandomTileWithAdjacent(8, TilePlain.ID);
if (p != null) {
while (p.x == 0 || p.x == world.getWorldSize() - 1 || p.y == world.getWorldSize() - 1 || p.y == 0) {
p = world.getRandomPassableTile();
if (p == null) {
//World is full
return;
}
}
RadioTower radioTower = new RadioTower();
radioTower.setWorld(world);
radioTower.setObjectId(new ObjectId());
radioTower.setX(p.x);
radioTower.setY(p.y);
if (radioTower.getAdjacentTile() != null) {
//Radio Tower has adjacent tiles
world.addObject(radioTower);
world.incUpdatable(); //In case the Factory couldn't be spawned.
this.radioTower = radioTower;
}
}
//Also spawn a Vault in the same World
p = world.getRandomPassableTile();
if (p != null) {
VaultDoor vaultDoor = new VaultDoor();
vaultDoor.setWorld(world);
int counter = 700;
while (p.x == 0 || p.x == world.getWorldSize() - 1 || p.y == world.getWorldSize() - 1 || p.y == 0
|| vaultDoor.getAdjacentTileCount(true) < 8) {
p = world.getRandomPassableTile();
if (p == null) {
//World is full
return;
}
vaultDoor.setX(p.x);
vaultDoor.setY(p.y);
counter--;
if (counter <= 0) {
//Reached maximum amount of retries
return;
}
}
vaultDoor.setObjectId(new ObjectId());
world.addObject(vaultDoor);
world.incUpdatable(); //In case the Factory & Radio Tower couldn't be spawned.
vaultDoor.setWorld(world);
vaultDoor.initialize();
this.vaultDoor = vaultDoor;
}
}
public void addNpc(NonPlayerCharacter npc) {
npcs.add(npc);
npc.setSettlement(this);
}
@Override
public Document mongoSerialise() {
Document document = new Document();
document.put("world", world.getId());
if (radioTower != null) {
document.put("radio_tower", radioTower.getObjectId());
}
if (vaultDoor != null) {
document.put("vault_door", vaultDoor.getObjectId());
}
document.put("factory", factory.getObjectId());
document.put("difficulty_level", difficultyLevel.ordinal());
document.put("password", String.valueOf(password));
List<ObjectId> npcArray = new ArrayList<>(npcs.size());
for (NonPlayerCharacter npc : npcs) {
npcArray.add(npc.getObjectId());
}
document.put("npcs", npcArray);
return document;
}
public enum DifficultyLevel {
NORMAL(0);
public int cypherId;
DifficultyLevel(int cypherId) {
this.cypherId = cypherId;
}
}
public Factory getFactory() {
return factory;
}
public RadioTower getRadioTower() {
return radioTower;
}
public VaultDoor getVaultDoor() {
return vaultDoor;
}
public World getWorld() {
return world;
}
public List<NonPlayerCharacter> getNpcs() {
return npcs;
}
public char[] getPassword() {
return password;
}
}

View File

@ -1,261 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.npcplugin.world.TileVaultFloor;
import net.simon987.server.GameServer;
import net.simon987.server.IServerConfiguration;
import net.simon987.server.game.objects.Direction;
import net.simon987.server.game.world.Location;
import net.simon987.server.game.world.World;
import net.simon987.server.logging.LogManager;
import org.bson.types.ObjectId;
import java.awt.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Random;
public class VaultDimension {
/**
* Name of the dimension
*/
private String name;
private World homeWorld;
private int homeX;
private int homeY;
public VaultDimension(VaultDoor vaultDoor) {
name = "v" + vaultDoor.getObjectId();
/*
* Creates a group of vault worlds and pieces them together with openings.
* For a set number of passes, a random number of vault worlds are added to each world in the
* previous 'layer' of worlds in a random direction. Openings are added to allow movement from a
* layer to the next, meaning that adjacent worlds are not necessarily connected, and one would
* necessarily need to travel through 5 openings to reach the 6th layer, even when that layer is
* less than 5 worlds away from the origin/home vault world (the one containing the exit door).
*
* 1. Create home world (layer 0)
* 2. For each world in the current layer, attach a random number of new worlds
* 3. Repeat the same step for the newly added layer
* 4. Choose a random world from the last layer and create the vault box there (objective)
* 5. Create an exit portal in the home world
*
* This process is done in 2 passes, in the first pass, worlds are defined
* as a set of coordinates + a list of opening directions, then they are actually generated
*/
IServerConfiguration config = GameServer.INSTANCE.getConfig();
int minLayerCount = config.getInt("vault_wg_min_layer_count");
int maxLayerCount = config.getInt("vault_wg_max_layer_count");
int minAttachedWorld = config.getInt("vault_wg_min_attached_world");
int maxAttachedWorld = Math.min(config.getInt("vault_wg_max_attached_world"), 4);
int minElectricBoxCount = config.getInt("vault_wg_min_electric_box_count");
int maxElectricBoxCount = config.getInt("vault_wg_max_electric_box_count");
HashMap<Integer, ArrayList<WorldBluePrint>> worldLayers = new HashMap<>();
VaultWorldGenerator generator = new VaultWorldGenerator();
Random random = new Random();
int layerCount = random.nextInt(maxLayerCount - minLayerCount) + minLayerCount;
//1. Create home world
WorldBluePrint homeWorldBluePrint = new WorldBluePrint();
homeWorldBluePrint.coords.x = 0x7FFF;
homeWorldBluePrint.coords.y = 0x7FFF;
worldLayers.put(0, new ArrayList<>());
worldLayers.get(0).add(homeWorldBluePrint);
//2. For each world in the current layer, attach a random number of new worlds
for (int i = 1; i <= layerCount; i++) {
worldLayers.put(i, new ArrayList<>());
for (WorldBluePrint world : worldLayers.get(i - 1)) {
int attachedWorlds;
if (i == 1) {
attachedWorlds = 4; // The home world should have 4 attached worlds
} else {
attachedWorlds = random.nextInt(maxAttachedWorld - minAttachedWorld) + minAttachedWorld;
}
for (int j = 0; j < attachedWorlds; j++) {
int randDirIndex = random.nextInt(4);
//Try 4 directions (wrap around 0..3)
for (int attemptCount = 0; attemptCount < 4; attemptCount++) {
Direction randomDirection = Direction.getDirection(randDirIndex);
//Don't attach a world at the same spot twice
if (!worldExists(world.coordinatesOf(randomDirection), worldLayers)) {
WorldBluePrint attachedWorld = world.attachWorld(randomDirection);
worldLayers.get(i).add(attachedWorld);
}
randDirIndex = (randDirIndex + 1) % 4;
}
}
}
}
ArrayList<World> lastLayerWorlds = new ArrayList<>();
//Generate worlds
for (Integer key : worldLayers.keySet()) {
ArrayList<WorldBluePrint> layer = worldLayers.get(key);
for (WorldBluePrint bp : layer) {
World vWorld = generator.generateVaultWorld(bp.coords.x, bp.coords.y, bp.openings, name);
GameServer.INSTANCE.getGameUniverse().addWorld(vWorld);
ArrayList<ElectricBox> newBoxes = VaultWorldUtils.generateElectricBoxes(vWorld, minElectricBoxCount,
maxElectricBoxCount);
for (ElectricBox blob : newBoxes) {
vWorld.addObject(blob);
vWorld.incUpdatable();
}
if (key == layerCount) {
lastLayerWorlds.add(vWorld);
}
if (key == 0) {
this.homeWorld = vWorld;
}
}
}
Point exitCoords = vaultDoor.getAdjacentTile();
Location exitLocation = new Location(vaultDoor.getWorld().getX(), vaultDoor.getWorld().getY(), vaultDoor
.getWorld().getDimension(), exitCoords.x, exitCoords.y);
//4. Choose a random world from the last layer and create the vault box there (objective)
World objectiveWorld = lastLayerWorlds.get(random.nextInt(lastLayerWorlds.size()));
Point exitPortalPt = objectiveWorld.getRandomTileWithAdjacent(8, TileVaultFloor.ID);
if (exitPortalPt != null) {
VaultExitPortal exitPortal = new VaultExitPortal();
exitPortal.setDestination(exitLocation);
exitPortal.setX(exitPortalPt.x);
exitPortal.setY(exitPortalPt.y);
exitPortal.setWorld(objectiveWorld);
exitPortal.setObjectId(new ObjectId());
objectiveWorld.addObject(exitPortal);
// LogManager.LOGGER.severe("Objective: " + objectiveWorld.getId());
} else {
LogManager.LOGGER.severe("FIXME: Couldn't create exit portal for world " + homeWorld.getId());
}
//5. Create an exit portal in the home World
Point homePortalPt = homeWorld.getRandomTileWithAdjacent(8, TileVaultFloor.ID);
if (homePortalPt != null) {
Portal homePortal = new Portal();
homePortal.setDestination(exitLocation);
homePortal.setX(homePortalPt.x);
homePortal.setY(homePortalPt.y);
homePortal.setWorld(homeWorld);
homePortal.setObjectId(new ObjectId());
homeWorld.addObject(homePortal);
Point entryCoords = homePortal.getAdjacentTile();
homeX = entryCoords.x;
homeY = entryCoords.y;
} else {
LogManager.LOGGER.severe("FIXME: Couldn't create home exit portal for world " + homeWorld.getId());
}
}
private boolean worldExists(Point coords, HashMap<Integer, ArrayList<WorldBluePrint>> worldLayers) {
//Auto-generated by IntelliJ Idea
return worldLayers.values().stream().flatMap(Collection::stream).anyMatch(bp -> bp.coords.equals(coords));
}
World getHomeWorld() {
return homeWorld;
}
public int getHomeX() {
return homeX;
}
public int getHomeY() {
return homeY;
}
/**
* Helper class to plan the layout of a vault dimension
*/
private class WorldBluePrint {
ArrayList<Direction> openings = new ArrayList<>(4);
/**
* Coordinates of the world
*/
Point coords = new Point();
/**
* Update the blueprint's openings to allow traveling to the newly attached world
*
* @param direction direction of the world to attach (relative to this one)
* @return The blueprint of the attached world
*/
WorldBluePrint attachWorld(Direction direction) {
WorldBluePrint attachedWorld = new WorldBluePrint();
switch (direction) {
case NORTH:
openings.add(Direction.NORTH);
attachedWorld.openings.add(Direction.SOUTH);
break;
case EAST:
openings.add(Direction.EAST);
attachedWorld.openings.add(Direction.WEST);
break;
case SOUTH:
openings.add(Direction.SOUTH);
attachedWorld.openings.add(Direction.NORTH);
break;
case WEST:
openings.add(Direction.WEST);
attachedWorld.openings.add(Direction.EAST);
break;
default:
break;
}
attachedWorld.coords.x = coords.x + direction.dX;
attachedWorld.coords.y = coords.y + direction.dY;
return attachedWorld;
}
/**
* Get the coordinates of a world that would be attached to this world
*
* @param direction direction of the attached world
*/
Point coordinatesOf(Direction direction) {
return new Point(coords.x + direction.dX, coords.y + direction.dY);
}
}
}

View File

@ -1,164 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.GameServer;
import net.simon987.server.game.objects.*;
import net.simon987.server.game.world.World;
import net.simon987.server.logging.LogManager;
import org.bson.Document;
import java.util.Arrays;
public class VaultDoor extends Structure implements MessageReceiver, Enterable, Updatable {
private static final int MAP_INFO = 0x0B00;
/**
* Whether or not the vault door is opened
*/
private boolean open = false;
private int homeX;
private int homeY;
private World homeWorld;
/**
* Number of ticks to remain the door open
*/
private int OPEN_TIME = GameServer.INSTANCE.getConfig().getInt("vault_door_open_time");
private int openedTimer = 0;
public VaultDoor() {
super(1, 1);
}
public VaultDoor(Document document) {
super(document, 1, 1);
setX(document.getInteger("x"));
setY(document.getInteger("y"));
if (document.containsKey("homeX") && document.containsKey("homeY")) {
homeX = document.getInteger("homeX");
homeY = document.getInteger("homeY");
}
}
@Override
public void update() {
if (open){
if (openedTimer <= 0) {
//Door was open for OPEN_TIME, close it
open = false;
openedTimer = 0;
LogManager.LOGGER.fine("Closed Vault door ID: " + getObjectId());
} else {
openedTimer--;
}
}
}
@Override
public boolean sendMessage(char[] message) {
Settlement settlement = NpcPlugin.settlementMap.get(getWorld().getId());
System.out.println("message: " + new String(message));
System.out.println("password: " + new String(settlement.getPassword()));
if (Arrays.equals(message, settlement.getPassword())) {
if (!open) {
openVault();
} else {
keepVaultOpen();
}
return true;
} else {
return false;
}
}
private void openVault() {
open = true;
openedTimer = OPEN_TIME;
LogManager.LOGGER.fine("Opened Vault door ID: " + getObjectId());
}
private void keepVaultOpen() {
open = true;
openedTimer = OPEN_TIME;
}
@Override
public boolean enter(GameObject object) {
// LogManager.LOGGER.fine("VAULT enter " + open);
if (open) {
object.getWorld().decUpdatable();
object.getWorld().removeObject(object);
homeWorld.incUpdatable();
homeWorld.addObject(object);
object.setWorld(homeWorld);
object.setX(homeX);
object.setY(homeY);
return true;
} else {
return false;
}
}
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public Document mongoSerialise() {
Document dbObject = super.mongoSerialise();
dbObject.put("homeX", getHomeX());
dbObject.put("homeY", getHomeY());
return dbObject;
}
@Override
public void initialize() {
//Get or generate vault world
homeWorld = GameServer.INSTANCE.getGameUniverse().getWorld(0x7FFF, 0x7FFF,
false, "v" + getObjectId() + "-");
if (homeWorld == null) {
VaultDimension vaultDimension = new VaultDimension(this);
homeWorld = vaultDimension.getHomeWorld();
homeX = vaultDimension.getHomeX();
homeY = vaultDimension.getHomeY();
}
}
public int getHomeX() {
return homeX;
}
public void setHomeX(int homeX) {
this.homeX = homeX;
}
public int getHomeY() {
return homeY;
}
public void setHomeY(int homeY) {
this.homeY = homeY;
}
}

View File

@ -1,35 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.game.objects.GameObject;
import net.simon987.server.game.world.Location;
import org.bson.Document;
/**
* Final exit portal located in the 'last' World of a Vault dimension
*/
public class VaultExitPortal extends Portal {
public VaultExitPortal() {
}
public VaultExitPortal(Document document) {
super(document);
setDestination(new Location(
document.getInteger("dstWorldX"),
document.getInteger("dstWorldY"),
document.getString("dstDimension"),
document.getInteger("dstX"),
document.getInteger("dstY")));
setX(document.getInteger("x"));
setY(document.getInteger("y"));
}
@Override
public boolean enter(GameObject object) {
//TODO: Trigger vault complete event instead
return super.enter(object);
}
}

View File

@ -1,265 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.server.game.objects.Direction;
import net.simon987.server.game.world.TileMap;
import net.simon987.server.game.world.TileVoid;
import net.simon987.server.game.world.World;
import java.awt.*;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Random;
public class VaultWorldGenerator {
public World generateVaultWorld(int worldX, int worldY, ArrayList<Direction> openings, String dimension) {
// LogManager.LOGGER.info("Generating vault World");
/*
* Openings are always at the same spot (marked by '?')
* 1. Rectangles of random size are generated
* 2. Openings are generated (if another vault world is connected on this direction)
* 3. They are connected with tunnels from bottom to top,
* by their roomCenter, alternating between horizontal first
* tunnels and vertical first
* 4. Tiles adjacent to floor tiles are set to wall tiles
* Example map:
*
* # # # # # # # - - - # ? ? # - - - - - - - - - -
* # o o o o o # - - - # ? ? # - - - - - - - - - -
* # o o o o o # # # # # # o # - - - - - - - - - -
* # o o o o o o o o o o o o # - - - - - - - - - -
* # o o o o o # # # # # # # # # # # - - - - - - -
* # o o o o o # - - - - # o o o o # - - - - - - -
* # o o o o o # # # # # # o o o o # - - - - - - -
* # # o o o o o o o o o o o o o o # - - - - - - -
* - # o # # # # # # # # # o o o o # - - - - - - -
* - # o # - - - - - - - # # # # # # - - - - - - -
* # # o # - - - - - - - - - - - - - - - - - # # #
* ? ? o # # # # # # # # # # # # # # # # # # # ? ?
* ? ? o o o o o o o o o o o o o o o o o o o o ? ?
* # # # # # # # # # # # # # # # # # # o # # # # #
* - - - - - - - - - - - - - - - - - # o # - - - -
* - # # # # # # # # # - - - - - - - # o # - - - -
* - # o o o o o o o # - - - # # # # # o # # # # -
* - # o o o o o o o # # # # # o o o o o o o o # -
* - # o o o o o o o o o o o # o o o o o o o o # -
* - # o o o o o o o # # # o # o o o o o o o o # -
* - # o o o o o o o # - # o # # # # # # # # # # -
* - # o o o o o o o # # # o # - - - - - - - - - -
* - # # # # # # # # # # ? ? # - - - - - - - - - -
* - - - - - - - - - - # ? ? # - - - - - - - - - -
*/
Random random = new Random();
int worldSize = 20;
int floorTile = 4;
int wallTile = 5;
int minRoomCount = 5;
int maxRoomCount = 8;
int minRoomWidth = 4;
int minRoomHeight = 4;
int maxRoomWidth = 8;
int maxRoomHeight = 8;
ArrayList<Point> roomCenters = new ArrayList<>(maxRoomCount + 4);
ArrayList<Rectangle> rooms = new ArrayList<>(maxRoomCount);
World world = new World(worldX, worldY, new TileMap(worldSize, worldSize), dimension);
TileMap map = world.getTileMap();
//Generate rectangles of random size
int roomCount = random.nextInt(maxRoomCount - minRoomCount) + minRoomCount;
for (int i = 0; i < roomCount; i++) {
int roomWidth = random.nextInt(maxRoomWidth - minRoomWidth) + minRoomWidth;
int roomHeight = random.nextInt(maxRoomHeight - minRoomHeight) + minRoomHeight;
int attempts = 35;
Point roomCorner;
Rectangle room = null;
//Try to find a spot that doesn't intersect another room
attemptsLoop:
while (true) {
if (attempts < 0) {
roomCorner = null;
break;
}
roomCorner = new Point(random.nextInt((worldSize - roomWidth) - 2) + 2,
random.nextInt((worldSize - roomHeight) - 2) + 2);
room = new Rectangle(roomCorner.x, roomCorner.y, roomWidth, roomHeight);
for (Rectangle otherRoom : rooms) {
if (otherRoom.intersects(room)) {
attempts--;
continue attemptsLoop;
}
}
break;
}
if (roomCorner != null) {
for (int x = roomCorner.x; x < roomCorner.x + roomWidth; x++) {
for (int y = roomCorner.y; y < roomCorner.y + roomHeight; y++) {
map.setTileAt(floorTile, x, y);
}
}
rooms.add(room);
roomCenters.add(new Point(roomCorner.x + roomWidth / 2, roomCorner.y + roomHeight / 2));
} else {
break;
}
}
//Create openings
for (Direction opening : openings) {
switch (opening) {
case NORTH:
map.setTileAt(floorTile, worldSize / 2, 0);
map.setTileAt(floorTile, worldSize / 2, 1);
map.setTileAt(floorTile, worldSize / 2 - 1, 0);
map.setTileAt(floorTile, worldSize / 2 - 1, 1);
roomCenters.add(new Point(worldSize / 2, 1));
break;
case EAST:
map.setTileAt(floorTile, worldSize - 1, worldSize / 2);
map.setTileAt(floorTile, worldSize - 1, worldSize / 2 - 1);
map.setTileAt(floorTile, worldSize - 2, worldSize / 2);
map.setTileAt(floorTile, worldSize - 2, worldSize / 2 - 1);
roomCenters.add(new Point(worldSize - 2, worldSize / 2 - 1));
break;
case SOUTH:
map.setTileAt(floorTile, worldSize / 2, worldSize - 1);
map.setTileAt(floorTile, worldSize / 2, worldSize - 2);
map.setTileAt(floorTile, worldSize / 2 - 1, worldSize - 1);
map.setTileAt(floorTile, worldSize / 2 - 1, worldSize - 2);
roomCenters.add(new Point(worldSize / 2, worldSize - 2));
break;
case WEST:
map.setTileAt(floorTile, 0, worldSize / 2);
map.setTileAt(floorTile, 0, worldSize / 2 - 1);
map.setTileAt(floorTile, 1, worldSize / 2);
map.setTileAt(floorTile, 1, worldSize / 2 - 1);
roomCenters.add(new Point(1, worldSize / 2 - 1));
break;
default:
break;
}
}
//Connect rooms together, from bottom to top
roomCenters.sort(new RoomCenterComparator());
boolean xFirst = true; //Start the tunnel horizontally
for (int i = 0; i < roomCenters.size() - 1; i++) {
//Note to self: I wouldn't bother trying to understand what's in this for loop,
//If this needs to be modified just trash it and start over
if (xFirst) {
if (roomCenters.get(i + 1).x - roomCenters.get(i).x < 0) {
for (int x = roomCenters.get(i).x; x > roomCenters.get(i + 1).x; x--) {
map.setTileAt(floorTile, x, roomCenters.get(i).y);
}
} else {
for (int x = roomCenters.get(i).x; x < roomCenters.get(i + 1).x; x++) {
map.setTileAt(floorTile, x, roomCenters.get(i).y);
}
}
if (roomCenters.get(i + 1).y - roomCenters.get(i).y < 0) {
for (int y = roomCenters.get(i).y; y > roomCenters.get(i + 1).y; y--) {
map.setTileAt(floorTile, roomCenters.get(i + 1).x, y);
}
} else {
for (int y = roomCenters.get(i).y; y < roomCenters.get(i + 1).y; y++) {
map.setTileAt(floorTile, roomCenters.get(i + 1).x, y);
}
}
} else {
if (roomCenters.get(i + 1).x - roomCenters.get(i).x < 0) {
for (int x = roomCenters.get(i).x; x > roomCenters.get(i + 1).x; x--) {
map.setTileAt(floorTile, x, roomCenters.get(i + 1).y);
}
} else {
for (int x = roomCenters.get(i).x; x < roomCenters.get(i + 1).x; x++) {
map.setTileAt(floorTile, x, roomCenters.get(i + 1).y);
}
}
if (roomCenters.get(i + 1).y - roomCenters.get(i).y < 0) {
for (int y = roomCenters.get(i).y; y > roomCenters.get(i + 1).y; y--) {
map.setTileAt(floorTile, roomCenters.get(i).x, y);
}
} else {
for (int y = roomCenters.get(i).y; y < roomCenters.get(i + 1).y; y++) {
map.setTileAt(floorTile, roomCenters.get(i).x, y);
}
}
}
xFirst = !xFirst;
}
//Tiles adjacent to floor tiles are set to wall tiles
for (int x = 0; x < worldSize; x++) {
for (int y = 0; y < worldSize; y++) {
if (map.getTileIdAt(x, y) != floorTile && hasTileAdjacent(x, y, map, floorTile)) {
map.setTileAt(wallTile, x, y);
}
}
}
//Set other tiles to 'void'
for (int x = 0; x < worldSize; x++) {
for (int y = 0; y < worldSize; y++) {
if (map.getTileIdAt(x, y) != floorTile && map.getTileIdAt(x, y) != wallTile) {
map.setTileAt(new TileVoid(), x, y);
}
}
}
return world;
}
private boolean hasTileAdjacent(int x, int y, TileMap map, int tile) {
for (int dX = -1; dX <= 1; dX++) {
for (int dY = -1; dY <= 1; dY++) {
if (map.getTileIdAt(x + dX, y + dY) == tile) {
return true;
}
}
}
return false;
}
private class RoomCenterComparator implements Comparator<Point> {
@Override
public int compare(Point o1, Point o2) {
return o1.y - o2.y;
}
}
}

View File

@ -1,76 +0,0 @@
package net.simon987.npcplugin;
import net.simon987.npcplugin.world.TileVaultFloor;
import net.simon987.server.game.world.TileMap;
import net.simon987.server.game.world.World;
import org.bson.types.ObjectId;
import java.awt.*;
import java.util.ArrayList;
import java.util.Random;
public class VaultWorldUtils {
public static ArrayList<ElectricBox> generateElectricBoxes(World world, int minCount, int maxCount) {
Random random = new Random();
int boxesCount = random.nextInt(maxCount - minCount) + minCount;
ArrayList<ElectricBox> electricBoxes = new ArrayList<>(boxesCount);
//Count number of floor tiles. If there is less plain tiles than desired amount of boxes,
//set the desired amount of blobs to the plain tile count
TileMap m = world.getTileMap();
int floorCount = 0;
for (int y = 0; y < world.getWorldSize(); y++) {
for (int x = 0; x < world.getWorldSize(); x++) {
if (m.getTileIdAt(x, y) == TileVaultFloor.ID) {
floorCount++;
}
}
}
if (boxesCount > floorCount) {
boxesCount = floorCount;
}
outerLoop:
for (int i = 0; i < boxesCount; i++) {
Point p = m.getRandomTile(TileVaultFloor.ID);
if (p != null) {
//Don't block worlds
int counter = 0;
while (p.x == 0 || p.y == 0 || p.x == world.getWorldSize() - 1 || p.y == world.getWorldSize() - 1 ||
world.getGameObjectsAt(p.x, p.y).size() != 0) {
p = m.getRandomTile(TileVaultFloor.ID);
counter++;
if (counter > 25) {
continue outerLoop;
}
}
for (ElectricBox box : electricBoxes) {
if (box.getX() == p.x && box.getY() == p.y) {
//There is already a box here
continue outerLoop;
}
}
ElectricBox box = new ElectricBox();
box.setObjectId(new ObjectId());
box.setX(p.x);
box.setY(p.y);
box.setWorld(world);
electricBoxes.add(box);
}
}
return electricBoxes;
}
}

View File

@ -5,8 +5,7 @@ import net.simon987.server.assembly.CPU;
import net.simon987.server.event.CpuInitialisationEvent; import net.simon987.server.event.CpuInitialisationEvent;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener; import net.simon987.server.event.GameEventListener;
import net.simon987.server.game.objects.ControllableUnit; import net.simon987.server.user.User;
import net.simon987.server.game.objects.HardwareHost;
public class CpuInitialisationListener implements GameEventListener { public class CpuInitialisationListener implements GameEventListener {
@Override @Override
@ -18,12 +17,11 @@ public class CpuInitialisationListener implements GameEventListener {
@Override @Override
public void handle(GameEvent event) { public void handle(GameEvent event) {
CPU cpu = (CPU) event.getSource(); CPU cpu = (CPU) event.getSource();
ControllableUnit controllableUnit = ((CpuInitialisationEvent) event).getUnit(); User user = ((CpuInitialisationEvent) event).getUser();
cpu.setHardwareHost((HardwareHost) controllableUnit);
RadioReceiverHardware radioHw = new RadioReceiverHardware(controllableUnit); RadioReceiverHardware radioHw = new RadioReceiverHardware(user.getControlledUnit());
radioHw.setCpu(cpu); radioHw.setCpu(cpu);
cpu.getHardwareHost().attachHardware(radioHw, RadioReceiverHardware.DEFAULT_ADDRESS); cpu.attachHardware(radioHw, RadioReceiverHardware.DEFAULT_ADDRESS);
} }
} }

View File

@ -1,26 +0,0 @@
package net.simon987.npcplugin.event;
import net.simon987.npcplugin.VaultExitPortal;
import net.simon987.server.event.GameEvent;
import net.simon987.server.game.objects.GameObject;
public class VaultCompleteEvent extends GameEvent {
private VaultExitPortal portal;
public VaultCompleteEvent(GameObject object, VaultExitPortal portal) {
//TODO: Add completion time?
setSource(object);
this.portal = portal;
}
@Override
public GameObject getSource() {
return (GameObject) super.getSource();
}
public VaultExitPortal getPortal() {
return portal;
}
}

View File

@ -1,27 +0,0 @@
package net.simon987.npcplugin.event;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.game.objects.ControllableUnit;
import net.simon987.server.game.objects.GameObject;
import net.simon987.server.logging.LogManager;
public class VaultCompleteListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return VaultCompleteEvent.class;
}
@Override
public void handle(GameEvent event) {
VaultCompleteEvent vaultCompleteEvent = (VaultCompleteEvent) event;
GameObject object = vaultCompleteEvent.getSource();
if (object instanceof ControllableUnit) {
LogManager.LOGGER.info(((ControllableUnit) object).getParent().getUsername() + " Completed vault " +
object.getWorld().getDimension());
((ControllableUnit) object).getParent().getStats().addToStringSet("completedVaults",
vaultCompleteEvent.getPortal().getWorld().getDimension());
}
}
}

View File

@ -1,86 +0,0 @@
package net.simon987.npcplugin.event;
import net.simon987.npcplugin.ElectricBox;
import net.simon987.npcplugin.VaultWorldUtils;
import net.simon987.server.GameServer;
import net.simon987.server.IServerConfiguration;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldUpdateEvent;
import net.simon987.server.game.world.World;
import java.util.ArrayList;
import java.util.HashMap;
public class VaultWorldUpdateListener implements GameEventListener {
/**
* Map of worlds and their time to wait until next respawn event
*/
private HashMap<World, Long> worldWaitMap = new HashMap<>(200);
/**
* Lower bound of ElectricBox to be created on a respawn event
*/
private static int minElectricBoxCount;
/**
* Upper bound of ElectricBox to be created on a respawn event
*/
private static int maxElectricBoxCount;
/**
* Number of game ticks to wait after the threshold has been met
*/
private static int waitTime;
/**
* Threshold before starting the
*/
private static int electricBoxThreshold;
public VaultWorldUpdateListener(IServerConfiguration config) {
minElectricBoxCount = config.getInt("min_electric_box_respawn_count");
maxElectricBoxCount = config.getInt("max_electric_box_respawn_count");
waitTime = config.getInt("electric_box_respawnTime");
electricBoxThreshold = config.getInt("min_electric_box_count");
}
@Override
public Class getListenedEventType() {
return WorldUpdateEvent.class;
}
@Override
public void handle(GameEvent event) {
//TODO: Move this and the Biomass UpdateListener to a 'RespawnManager' kind of deal
World world = ((WorldUpdateEvent) event).getWorld();
if (world.getDimension().startsWith("v")) {
//If there is less than the respawn threshold,
if (world.findObjects(ElectricBox.class).size() < electricBoxThreshold) {
//Set a timer for respawn_time ticks
if (!worldWaitMap.containsKey(world) || worldWaitMap.get(world) == 0L) {
worldWaitMap.put(world, GameServer.INSTANCE.getGameUniverse().getTime() + waitTime);
} else {
long waitUntil = worldWaitMap.get(world);
if (GameServer.INSTANCE.getGameUniverse().getTime() >= waitUntil) {
//If the timer was set less than respawn_time ticks ago, respawn the blobs
ArrayList<ElectricBox> newBoxes = VaultWorldUtils.generateElectricBoxes(world, minElectricBoxCount,
maxElectricBoxCount);
for (ElectricBox blob : newBoxes) {
world.addObject(blob);
world.incUpdatable();
}
//Set the 'waitUntil' time to 0 to indicate that we are not waiting
worldWaitMap.replace(world, 0L);
}
}
}
}
}
}

View File

@ -1,14 +1,16 @@
package net.simon987.npcplugin.event; package net.simon987.npcplugin.event;
import net.simon987.npcplugin.Factory;
import net.simon987.npcplugin.NpcPlugin; import net.simon987.npcplugin.NpcPlugin;
import net.simon987.npcplugin.Settlement; import net.simon987.npcplugin.RadioTower;
import net.simon987.server.GameServer;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener; import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldGenerationEvent; import net.simon987.server.event.WorldGenerationEvent;
import net.simon987.server.game.world.World; import net.simon987.server.game.World;
import net.simon987.server.game.world.WorldGenerationException;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import java.awt.*;
import java.util.Random; import java.util.Random;
public class WorldCreationListener implements GameEventListener { public class WorldCreationListener implements GameEventListener {
@ -16,14 +18,10 @@ public class WorldCreationListener implements GameEventListener {
/** /**
* Spawn rate. Higher = rarer: A factory will be spawn about every FACTORY_SPAWN_RATE generated Worlds * Spawn rate. Higher = rarer: A factory will be spawn about every FACTORY_SPAWN_RATE generated Worlds
*/ */
private static int FACTORY_SPAWN_RATE = 0; private static final int FACTORY_SPAWN_RATE = 35;
private Random random = new Random(); private Random random = new Random();
public WorldCreationListener(int factorySpawnRate) {
FACTORY_SPAWN_RATE = factorySpawnRate;
}
@Override @Override
public Class getListenedEventType() { public Class getListenedEventType() {
return WorldGenerationEvent.class; return WorldGenerationEvent.class;
@ -34,14 +32,66 @@ public class WorldCreationListener implements GameEventListener {
if (random.nextInt(FACTORY_SPAWN_RATE) == 0) { if (random.nextInt(FACTORY_SPAWN_RATE) == 0) {
World world = (World) event.getSource(); World world = ((WorldGenerationEvent) event).getWorld();
try { outerLoopFactory:
Settlement settlement = new Settlement(world); for (int x = 2; x < 12; x++) {
NpcPlugin.settlementMap.put(world.getId(), settlement); for (int y = 2; y < 12; y++) {
} catch (WorldGenerationException e) {
LogManager.LOGGER.fine(String.format("Exception during settlement generation: %s.", if ((!world.isTileBlocked(x, y) && !world.isTileBlocked(x + 1, y) &&
e.getMessage())); !world.isTileBlocked(x, y + 1) && !world.isTileBlocked(x + 1, y + 1))) {
Factory factory = new Factory();
factory.setWorld(world);
factory.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
factory.setX(x);
factory.setY(y);
if (factory.getAdjacentTile() == null) {
//Factory has no non-blocked adjacent tiles
continue;
}
world.addObject(factory);
world.incUpdatable();
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.addObject(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 + ")");
}
} }
} }
} }

View File

@ -1,13 +0,0 @@
package net.simon987.npcplugin.world;
import net.simon987.server.game.world.Tile;
public class TileVaultFloor extends Tile {
public static final int ID = 4;
@Override
public int getId() {
return ID;
}
}

View File

@ -1,26 +0,0 @@
package net.simon987.npcplugin.world;
import net.simon987.server.game.objects.GameObject;
import net.simon987.server.game.world.Tile;
public class TileVaultWall extends Tile {
public static final int ID = 5;
@Override
public int getId() {
return ID;
}
@Override
public boolean walk(GameObject object) {
return false; //always blocked
}
@Override
public boolean isBlocked() {
return true;
}
}

View File

@ -1,62 +0,0 @@
{
"hardware": [
{
"type": "net.simon987.cubotplugin.CubotLeg",
"address": 1
},
{
"type": "net.simon987.cubotplugin.CubotLaser",
"address": 2
},
{
"type": "net.simon987.cubotplugin.CubotLidar",
"address": 3
},
{
"type": "net.simon987.cubotplugin.CubotDrill",
"address": 5
},
{
"type": "net.simon987.npcplugin.NpcInventory",
"item": null,
"address": 6
},
{
"type": "net.simon987.mischwplugin.RandomNumberGenerator",
"address": 7
},
{
"type": "net.simon987.mischwplugin.Clock",
"address": 8
},
{
"type": "net.simon987.cubotplugin.CubotHologram",
"color": 0,
"value": 0,
"string": "",
"mode": 0,
"address": 9
},
{
"type": "net.simon987.npcplugin.NpcBattery",
"energy": 60000,
"max_energy": 60000,
"address": 262
},
{
"type": "net.simon987.npcplugin.RadioReceiverHardware",
"cubot": {
"$oid": "5c1d43e40d3d2530aba636df"
},
"address": 12
},
{
"type": "net.simon987.cubotplugin.CubotComPort",
"address": 13
},
{
"type": "net.simon987.cubotplugin.CubotCore",
"address": 14
}
]
}

View File

@ -1,4 +1,3 @@
classpath=net.simon987.npcplugin.NpcPlugin classpath=net.simon987.npcplugin.NpcPlugin
name=NPC Plugin name=NPC Plugin
version=1.1 version=1.0
depend=Cubot Plugin

View File

@ -1,10 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4"> <module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="Spring" name="Spring">
<configuration />
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8"> <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" /> <output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" /> <output-test url="file://$MODULE_DIR$/target/test-classes" />
@ -20,39 +15,10 @@
<orderEntry type="library" name="Maven: junit:junit:4.10" level="project" /> <orderEntry type="library" name="Maven: junit:junit:4.10" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" /> <orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.1" level="project" />
<orderEntry type="module" module-name="Server" /> <orderEntry type="module" module-name="Server" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.6" level="project" /> <orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.8.1" level="project" /> <orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongodb-driver-sync:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:bson:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongodb-driver-core:3.9.1" level="project" /> <orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
<orderEntry type="library" name="Maven: org.springframework.security:spring-security-core:5.1.5.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-aop:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-beans:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-context:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-jcl:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-expression:5.1.6.RELEASE" level="project" />
<orderEntry type="library" name="Maven: com.sparkjava:spark-core:2.8.0" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.25" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-server:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: javax.servlet:javax.servlet-api:3.1.0" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-http:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-util:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-io:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-webapp:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-xml:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-servlet:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-security:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-server:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-common:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-client:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty:jetty-client:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-servlet:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: org.eclipse.jetty.websocket:websocket-api:9.4.12.v20180830" level="project" />
<orderEntry type="library" name="Maven: com.sparkjava:spark-template-velocity:2.7.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.velocity:velocity:1.7" level="project" />
<orderEntry type="library" name="Maven: commons-collections:commons-collections:3.2.1" level="project" />
<orderEntry type="library" name="Maven: commons-lang:commons-lang:2.4" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-simple:1.7.25" level="project" />
</component> </component>
</module> </module>

View File

@ -4,15 +4,17 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<parent> <parent>
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId> <artifactId>server_root</artifactId>
<version>1.4a</version> <version>1.2a</version>
</parent> </parent>
<groupId>net.simon987.pluginplant</groupId> <groupId>net.simon987.pluginplant</groupId>
<artifactId>plugin-biomassBlob</artifactId> <artifactId>plugin-biomassBlob</artifactId>
<version>1.4a</version> <version>1.2a</version>
<dependencies> <dependencies>
<dependency> <dependency>
@ -24,7 +26,7 @@
<dependency> <dependency>
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server</artifactId> <artifactId>server</artifactId>
<version>1.4a</version> <version>1.2a</version>
</dependency> </dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,28 +1,26 @@
package net.simon987.biomassplugin; package net.simon987.biomassplugin;
import net.simon987.server.game.item.Item; import com.mongodb.BasicDBObject;
import net.simon987.server.game.objects.GameObject; import com.mongodb.DBObject;
import net.simon987.server.game.objects.InventoryHolder; import net.simon987.server.game.GameObject;
import org.bson.Document; import net.simon987.server.game.InventoryHolder;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
public class BiomassBlob extends GameObject implements InventoryHolder { public class BiomassBlob extends GameObject implements InventoryHolder {
private static final char MAP_INFO = 0x0101; private static final char MAP_INFO = 0x4000;
public static final int ID = 2;
/** /**
* Yield of the blob, in biomass units * Yield of the blob, in biomass units
*/ */
private int biomassCount; private int biomassCount;
/**
* Style of the blob (Only visual)
*/
// private int style;
public BiomassBlob() { private static final int ITM_BIOMASS = 1;
}
public BiomassBlob(Document document) {
super(document);
biomassCount = document.getInteger("biomassCount");
}
@Override @Override
public char getMapInfo() { public char getMapInfo() {
@ -30,21 +28,30 @@ public class BiomassBlob extends GameObject implements InventoryHolder {
} }
@Override @Override
public JSONObject jsonSerialise() { public JSONObject serialise() {
JSONObject json = super.jsonSerialise(); JSONObject json = new JSONObject();
json.put("t", ID);
json.put("i", getObjectId());
json.put("x", getX());
json.put("y", getY());
json.put("b", biomassCount); json.put("b", biomassCount);
// json.put("style", style);
return json; return json;
} }
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document dbObject = super.mongoSerialise(); BasicDBObject dbObject = new BasicDBObject();
dbObject.put("biomassCount", biomassCount); dbObject.put("t", ID);
dbObject.put("i", getObjectId());
dbObject.put("x", getX());
dbObject.put("y", getY());
dbObject.put("b", biomassCount);
return dbObject; return dbObject;
@ -58,31 +65,55 @@ public class BiomassBlob extends GameObject implements InventoryHolder {
this.biomassCount = biomassCount; this.biomassCount = biomassCount;
} }
// public int getStyle() {
// return style;
// }
//
// public void setStyle(int style) {
// this.style = style;
// }
public static BiomassBlob deserialize(DBObject obj) {
BiomassBlob biomassBlob = new BiomassBlob();
biomassBlob.setObjectId((long) obj.get("i"));
biomassBlob.setX((int) obj.get("x"));
biomassBlob.setY((int) obj.get("y"));
// biomassBlob.style = (int) json.get("style");
biomassBlob.biomassCount = (int) obj.get("b");
return biomassBlob;
}
/** /**
* Called when an object attempts to place an item in this BiomassBlob * Called when an object attempts to place an item in this BiomassBlob
* *
* @param item item id (see MarConstants.ITEM_*)
* @return Always returns false * @return Always returns false
*/ */
@Override @Override
public boolean placeItem(Item item) { public boolean placeItem(int item) {
//Why would you want to place an item in a blob? //Why would you want to place an item in a blob?
return false; return false;
} }
@Override @Override
public boolean canTakeItem(int itemId) { public boolean canTakeItem(int item) {
return itemId == ItemBiomass.ID && biomassCount >= 1; return item == ITM_BIOMASS && biomassCount >= 1;
} }
/** /**
* Called when an object attempts to take an item from this BiomassBlob. * Called when an object attempts to take an item from this BiomassBlob.
* If the object requests biomass, it will be subtracted from biomassCount, and * If the object requests biomass, it will be subtracted from biomassCount, and
* if it reaches 0, the plant is deleted * if it reaches 0, the plant is deleted
*
* @param item item id (see MarConstants.ITEM_*)
*/ */
@Override @Override
public void takeItem(int itemId) { public void takeItem(int item) {
if (itemId == ItemBiomass.ID) { if (item == ITM_BIOMASS) {
if (biomassCount > 1) { if (biomassCount > 1) {
biomassCount--; biomassCount--;
} else { } else {
@ -90,5 +121,6 @@ public class BiomassBlob extends GameObject implements InventoryHolder {
setDead(true); setDead(true);
} }
} }
} }
} }

View File

@ -1,31 +1,37 @@
package net.simon987.biomassplugin; package net.simon987.biomassplugin;
import com.mongodb.DBObject;
import net.simon987.biomassplugin.event.ObjectDeathListener; import net.simon987.biomassplugin.event.ObjectDeathListener;
import net.simon987.biomassplugin.event.WorldCreationListener; import net.simon987.biomassplugin.event.WorldCreationListener;
import net.simon987.biomassplugin.event.WorldUpdateListener; import net.simon987.biomassplugin.event.WorldUpdateListener;
import net.simon987.server.GameServer; import net.simon987.server.ServerConfiguration;
import net.simon987.server.IServerConfiguration; import net.simon987.server.game.GameObject;
import net.simon987.server.game.objects.GameRegistry; import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin; import net.simon987.server.plugin.ServerPlugin;
public class BiomassPlugin extends ServerPlugin implements GameObjectDeserializer {
public class BiomassPlugin extends ServerPlugin {
@Override @Override
public void init(GameServer gameServer) { public void init(ServerConfiguration config) {
IServerConfiguration config = gameServer.getConfig();
GameRegistry registry = gameServer.getRegistry();
listeners.add(new WorldCreationListener()); listeners.add(new WorldCreationListener());
listeners.add(new WorldUpdateListener(config)); listeners.add(new WorldUpdateListener(config));
listeners.add(new ObjectDeathListener(config)); listeners.add(new ObjectDeathListener(config));
registry.registerGameObject(BiomassBlob.class); LogManager.LOGGER.info("Initialised Biomass plugin");
registry.registerItem(ItemBiomass.ID, ItemBiomass.class); }
LogManager.LOGGER.info("(BiomassPlugin) Initialised Biomass plugin"); @Override
public GameObject deserializeObject(DBObject object) {
int objType = (int) object.get("t");
if (objType == BiomassBlob.ID) {
return BiomassBlob.deserialize(object);
}
return null;
} }
} }

View File

@ -1,39 +0,0 @@
package net.simon987.biomassplugin;
import net.simon987.server.GameServer;
import net.simon987.server.game.item.Item;
import net.simon987.server.game.objects.ControllableUnit;
import net.simon987.server.game.objects.Rechargeable;
import org.bson.Document;
public class ItemBiomass extends Item {
public static final int ID = 0x0001;
private static final int energy = GameServer.INSTANCE.getConfig().getInt("biomassEnergyValue");
@Override
public int getId() {
return ID;
}
public ItemBiomass() {
super(null);
}
public ItemBiomass(Document document) {
super(document);
}
@Override
public void clear(ControllableUnit unit) {
if (unit instanceof Rechargeable) {
((Rechargeable) unit).storeEnergy(energy);
}
}
@Override
public char poll() {
return ID;
}
}

View File

@ -1,9 +1,8 @@
package net.simon987.biomassplugin; package net.simon987.biomassplugin;
import net.simon987.server.game.world.TileMap; import net.simon987.server.GameServer;
import net.simon987.server.game.world.TilePlain; import net.simon987.server.game.World;
import net.simon987.server.game.world.World; import net.simon987.server.logging.LogManager;
import org.bson.types.ObjectId;
import java.awt.*; import java.awt.*;
import java.util.ArrayList; import java.util.ArrayList;
@ -22,12 +21,12 @@ public class WorldUtils {
//Count number of plain tiles. If there is less plain tiles than desired amount of blobs, //Count number of plain tiles. If there is less plain tiles than desired amount of blobs,
//set the desired amount of blobs to the plain tile count //set the desired amount of blobs to the plain tile count
TileMap m = world.getTileMap(); int[][] tiles = world.getTileMap().getTiles();
int plainCount = 0; int plainCount = 0;
for (int y = 0; y < world.getWorldSize(); y++) { for (int y = 0; y < World.WORLD_SIZE; y++) {
for (int x = 0; x < world.getWorldSize(); x++) { for (int x = 0; x < World.WORLD_SIZE; x++) {
if (m.getTileIdAt(x, y) == TilePlain.ID) { if (tiles[x][y] == 0) {
plainCount++; plainCount++;
} }
} }
@ -40,14 +39,14 @@ public class WorldUtils {
outerLoop: outerLoop:
for (int i = 0; i < blobCount; i++) { for (int i = 0; i < blobCount; i++) {
Point p = m.getRandomTile(TilePlain.ID); Point p = world.getTileMap().getRandomPlainTile();
if (p != null) { if (p != null) {
//Don't block worlds //Don't block worlds
int counter = 0; int counter = 0;
while (p.x == 0 || p.y == 0 || p.x == world.getWorldSize() - 1 || p.y == world.getWorldSize() - 1 || while (p.x == 0 || p.y == 0 || p.x == World.WORLD_SIZE - 1 || p.y == World.WORLD_SIZE - 1 ||
world.getGameObjectsAt(p.x, p.y).size() != 0) { world.getGameObjectsAt(p.x, p.y).size() != 0) {
p = m.getRandomTile(TilePlain.ID); p = world.getTileMap().getRandomPlainTile();
counter++; counter++;
if (counter > 25) { if (counter > 25) {
@ -63,7 +62,8 @@ public class WorldUtils {
} }
BiomassBlob biomassBlob = new BiomassBlob(); BiomassBlob biomassBlob = new BiomassBlob();
biomassBlob.setObjectId(new ObjectId()); biomassBlob.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
// biomassBlob.setStyle(0); //TODO: set style depending on difficulty level? or random? from config?
biomassBlob.setBiomassCount(yield); biomassBlob.setBiomassCount(yield);
biomassBlob.setX(p.x); biomassBlob.setX(p.x);
biomassBlob.setY(p.y); biomassBlob.setY(p.y);
@ -73,6 +73,9 @@ public class WorldUtils {
} }
} }
LogManager.LOGGER.info("Generated " + biomassBlobs.size() + " biomassBlobs for World (" + world.getX() + ',' +
world.getY() + ')');
return biomassBlobs; return biomassBlobs;
} }
} }

View File

@ -1,13 +1,14 @@
package net.simon987.biomassplugin.event; package net.simon987.biomassplugin.event;
import net.simon987.biomassplugin.BiomassBlob; import net.simon987.biomassplugin.BiomassBlob;
import net.simon987.server.IServerConfiguration; import net.simon987.server.GameServer;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener; import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.ObjectDeathEvent; import net.simon987.server.event.ObjectDeathEvent;
import net.simon987.server.game.objects.GameObject; import net.simon987.server.game.GameObject;
import net.simon987.server.game.world.World; import net.simon987.server.game.World;
import org.bson.types.ObjectId; import net.simon987.server.logging.LogManager;
/** /**
* Handles ObjectDeathEvent events * Handles ObjectDeathEvent events
@ -16,8 +17,9 @@ public class ObjectDeathListener implements GameEventListener {
private int biomassDropCount; private int biomassDropCount;
public ObjectDeathListener(IServerConfiguration config) { public ObjectDeathListener(ServerConfiguration config) {
biomassDropCount = config.getInt("harvester_biomass_drop_count"); biomassDropCount = config.getInt("harvester_biomass_drop_count");
} }
@Override @Override
@ -27,19 +29,22 @@ public class ObjectDeathListener implements GameEventListener {
@Override @Override
public void handle(GameEvent event) { public void handle(GameEvent event) {
// TODO: setup enum with all GameObject type IDs
if (event.getSource().getClass().getCanonicalName().equals("net.simon987.npcplugin.HarvesterNPC")) { if (((ObjectDeathEvent) event).getSourceObjectId() == 10) {
//An HarvesterNPC ObjectDeathEvent is received //An HarvesterNPC ObjectDeathEvent is received
GameObject dyingHarvesterNPC = (GameObject)event.getSource(); GameObject dyingHarvesterNPC = (GameObject)event.getSource();
//Don't spawn biomass on World border //Don't spawn biomass on World border
if (dyingHarvesterNPC.getX() != 0 && dyingHarvesterNPC.getX() != dyingHarvesterNPC.getWorld().getWorldSize() - 1 && if (dyingHarvesterNPC.getX() != 0 && dyingHarvesterNPC.getX() != World.WORLD_SIZE - 1 &&
dyingHarvesterNPC.getY() != 0 && dyingHarvesterNPC.getY() != dyingHarvesterNPC.getWorld().getWorldSize() - 1) { dyingHarvesterNPC.getY() != 0 && dyingHarvesterNPC.getY() != World.WORLD_SIZE - 1) {
//Create a new biomass //Create a new biomass
BiomassBlob newBiomassBlob = createBiomassBlobAt( BiomassBlob newBiomassBlob = createBiomassBlobAt(
dyingHarvesterNPC.getX(), dyingHarvesterNPC.getY(), dyingHarvesterNPC.getWorld()); dyingHarvesterNPC.getX(), dyingHarvesterNPC.getY(), dyingHarvesterNPC.getWorld());
//Add it to the world game objects //Add it to the world game objects
dyingHarvesterNPC.getWorld().addObject(newBiomassBlob); dyingHarvesterNPC.getWorld().addObject(newBiomassBlob);
LogManager.LOGGER.fine("Spawned biomass at (" + newBiomassBlob.getX() +
", " + newBiomassBlob.getY() + ")");
} }
} }
} }
@ -54,7 +59,8 @@ public class ObjectDeathListener implements GameEventListener {
private BiomassBlob createBiomassBlobAt(int x, int y, World world) { private BiomassBlob createBiomassBlobAt(int x, int y, World world) {
BiomassBlob biomassBlob = new BiomassBlob(); BiomassBlob biomassBlob = new BiomassBlob();
biomassBlob.setObjectId(new ObjectId()); biomassBlob.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
// biomassBlob.setStyle(0); //TODO: set style depending on difficulty level? or random? from config?
biomassBlob.setBiomassCount(biomassDropCount); biomassBlob.setBiomassCount(biomassDropCount);
biomassBlob.setX(x); biomassBlob.setX(x);
biomassBlob.setY(y); biomassBlob.setY(y);

View File

@ -3,11 +3,11 @@ package net.simon987.biomassplugin.event;
import net.simon987.biomassplugin.BiomassBlob; import net.simon987.biomassplugin.BiomassBlob;
import net.simon987.biomassplugin.WorldUtils; import net.simon987.biomassplugin.WorldUtils;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.IServerConfiguration; import net.simon987.server.ServerConfiguration;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener; import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldUpdateEvent; import net.simon987.server.event.WorldUpdateEvent;
import net.simon987.server.game.world.World; import net.simon987.server.game.World;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -23,7 +23,7 @@ public class WorldUpdateListener implements GameEventListener {
private static int waitTime; private static int waitTime;
private static int blobThreshold; private static int blobThreshold;
public WorldUpdateListener(IServerConfiguration config) { public WorldUpdateListener(ServerConfiguration config) {
minBlobCount = config.getInt("minBiomassRespawnCount"); minBlobCount = config.getInt("minBiomassRespawnCount");
maxBlobCount = config.getInt("maxBiomassRespawnCount"); maxBlobCount = config.getInt("maxBiomassRespawnCount");
@ -44,32 +44,31 @@ public class WorldUpdateListener implements GameEventListener {
World world = ((WorldUpdateEvent) event).getWorld(); World world = ((WorldUpdateEvent) event).getWorld();
if (world.getDimension().startsWith("w")) { //If there is less than the respawn threshold,
//If there is less than the respawn threshold, if (world.findObjects(BiomassBlob.class).size() < blobThreshold) {
if (world.findObjects(BiomassBlob.class).size() < blobThreshold) {
//Set a timer for respawn_time ticks //Set a timer for respawn_time ticks
if (!worldWaitMap.containsKey(world) || worldWaitMap.get(world) == 0L) { if (!worldWaitMap.containsKey(world) || worldWaitMap.get(world) == 0L) {
worldWaitMap.put(world, GameServer.INSTANCE.getGameUniverse().getTime() + waitTime); worldWaitMap.put(world, GameServer.INSTANCE.getGameUniverse().getTime() + waitTime);
} else { } else {
long waitUntil = worldWaitMap.get(world); long waitUntil = worldWaitMap.get(world);
if (GameServer.INSTANCE.getGameUniverse().getTime() >= waitUntil) { if (GameServer.INSTANCE.getGameUniverse().getTime() >= waitUntil) {
//If the timer was set less than respawn_time ticks ago, respawn the blobs //If the timer was set less than respawn_time ticks ago, respawn the blobs
ArrayList<BiomassBlob> newBlobs = WorldUtils.generateBlobs(world, minBlobCount, ArrayList<BiomassBlob> newBlobs = WorldUtils.generateBlobs(world, minBlobCount,
maxBlobCount, blobYield); maxBlobCount, blobYield);
for (BiomassBlob blob : newBlobs) { for (BiomassBlob blob : newBlobs) {
world.addObject(blob); world.addObject(blob);
world.incUpdatable();
}
//Set the 'waitUntil' time to 0 to indicate that we are not waiting
worldWaitMap.replace(world, 0L);
} }
//Set the 'waitUntil' time to 0 to indicate that we are not waiting
worldWaitMap.replace(world, 0L);
} }
} }
} }
} }
} }

View File

@ -1,4 +1,3 @@
classpath=net.simon987.biomassplugin.BiomassPlugin classpath=net.simon987.biomassplugin.BiomassPlugin
name=Biomass Plugin name=Biomass Plugin
version=1.0 version=1.0
depend=NPC Plugin

View File

@ -1,31 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>net.simon987.server</groupId>
<artifactId>server_root</artifactId>
<version>1.4a</version>
</parent>
<groupId>net.simon987.pluginradioactivecloud</groupId>
<artifactId>plugin-radioactiveCloud</artifactId>
<version>1.4a</version>
<dependencies>
<dependency>
<groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>net.simon987.server</groupId>
<artifactId>server</artifactId>
<version>1.4a</version>
</dependency>
</dependencies>
</project>

View File

@ -1,32 +0,0 @@
package net.simon987.pluginradioactivecloud;
import net.simon987.server.GameServer;
import net.simon987.server.game.objects.ControllableUnit;
import net.simon987.server.game.objects.Enterable;
import net.simon987.server.game.objects.GameObject;
import net.simon987.server.game.objects.Radioactive;
public class RadioactiveCloud extends GameObject implements Radioactive, Enterable {
private final static int CORRUPTION_BLOCK_SIZE =
GameServer.INSTANCE.getConfig().getInt("radioactive_cloud_corruption_block_size");
/**
* Called when an object attempts to walk directly into a Enterable object
*
* @param object The game object that attempted to enter
* @return true if successful, false to block the object
*/
@Override
public boolean enter(GameObject object) {
if (object instanceof ControllableUnit) {
((ControllableUnit) object).getCpu().getMemory().corrupt(CORRUPTION_BLOCK_SIZE);
}
return true;
}
@Override
public char getMapInfo() {
return 0;
}
}

View File

@ -1,14 +0,0 @@
package net.simon987.pluginradioactivecloud;
import net.simon987.server.GameServer;
import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin;
public class RadioactiveCloudPlugin extends ServerPlugin {
@Override
public void init(GameServer gameServer) {
LogManager.LOGGER.info("(Radioactive cloud Plugin) Initialised Radioactive cloud plugin.");
}
}

View File

@ -1,26 +0,0 @@
package net.simon987.pluginradioactivecloud;
import net.simon987.server.GameServer;
import net.simon987.server.game.objects.ControllableUnit;
import net.simon987.server.game.objects.Enterable;
import net.simon987.server.game.objects.GameObject;
import net.simon987.server.game.objects.Radioactive;
public class RadioactiveObstacle extends GameObject implements Radioactive, Enterable {
private final static int corruptionBlockSize = GameServer.INSTANCE.getConfig().getInt("radioactive_obstacle_corruption_block_size");
private final static int MAP_INFO = 0x0A01; //10
@Override
public char getMapInfo() {
return MAP_INFO;
}
@Override
public boolean enter(GameObject object) {
if (object instanceof ControllableUnit) {
((ControllableUnit) object).getCpu().getMemory().corrupt(corruptionBlockSize);
}
return false;
}
}

View File

@ -1,3 +0,0 @@
classpath=net.simon987.pluginradioactivecloud.RadioactiveCloudPlugin
name=Radioactive cloud Plugin
version=1.4

134
README.md
View File

@ -1,40 +1,26 @@
# Much-Assembly-Required # [Live demo](https://muchassemblyrequired.com)
Program the 8086-like microprocessor of a robot in a grid-based multiplayer world. The game is web based so no installation is required.
[![CodeFactor](https://www.codefactor.io/repository/github/simon987/much-assembly-required/badge)](https://www.codefactor.io/repository/github/simon987/much-assembly-required) In its current state, players can walk around the game universe and collect Biomass blobs & Iron/copper ore using the online code editor.
[![Build Status](https://ci.simon987.net/buildStatus/icon?job=Much-Assembly-Required)](https://ci.simon987.net/job/Much-Assembly-Required/)
**Much Assembly Required** allows you to program the 8086-like microprocessor of a robot in a grid-based multiplayer world. The game is web based so no installation is required. In its current state, players can walk around the game universe and collect Biomass blobs and Iron/copper ore using the online code editor.
![screenshot from 2017-11-12 13-01-43](https://user-images.githubusercontent.com/7120851/32701793-e5d07e98-c7a9-11e7-9931-f8db7b287994.png) ![screenshot from 2017-11-12 13-01-43](https://user-images.githubusercontent.com/7120851/32701793-e5d07e98-c7a9-11e7-9931-f8db7b287994.png)
Wiki: [GitHub](https://github.com/simon987/Much-Assembly-Required/wiki) Wiki: [GitHub](https://github.com/simon987/Much-Assembly-Required/wiki)
Chat: [Slack](https://join.slack.com/t/muchassemblyrequired/shared_invite/enQtMjY3Mjc1OTUwNjEwLWRjMjRkZTg2N2EyNWRjN2YyMDc0YzIyMTUyYzFiNTBmMTU3OGQ1ZjA0MWY0M2IyYjUxZTA4NjRkMWVkNDk2NzY) Chat: [Slack](https://join.slack.com/t/muchassemblyrequired/shared_invite/enQtMjY3Mjc1OTUwNjEwLTkyOTIwOTA5OGY4MDVlMGI4NzM5YzlhMWJiMGY1OWE2NjUxODQ1NWQ1YTcxMTA1NGZkYzNjYzMyM2E1ODdmNzg)
## VS Code Extensions
- [Much Assembly Required (Upload on Save)](https://marketplace.visualstudio.com/items?itemName=tomhodder.much-assembly-required-upload-on-save) by tomhodder
- [Much Assembly Required Language Support](https://marketplace.visualstudio.com/items?itemName=PJB3005.much-assembly-required-language-support) by PJB3005
# Deploying the server # Deploying the server
## Linux Note: You can find the frontend [here](https://github.com/simon987/Much-Assembly-Required-Frontend)
**Installing tools**
On Ubuntu 16.04: ## Linux (Ubuntu 16.04)
```bash ```bash
sudo apt install git maven openjdk-8-jdk mongodb # Install tools
``` sudo apt install git maven openjdk-8-jdk
On Arch:
``` bash
sudo pacman -S git maven jdk8-opendjk
yay -S mongodb-bin
# Don't forget to start mongodb
sudo systemctl start mongodb.service
```
*If needed, visit [yay installation](https://github.com/Jguer/yay#installation) and [troubleshooting mongodb](https://wiki.archlinux.org/index.php/MongoDB#Troubleshooting).*
**Deploying server**
``` bash
# Obtain source files # Obtain source files
git clone https://github.com/simon987/Much-Assembly-Required.git git clone https://github.com/simon987/Much-Assembly-Required.git
@ -44,19 +30,19 @@ mvn package
# Run # Run
cd target cd target
java -jar server-1.4a.jar java -jar server-1.2a.jar
``` ```
## Windows (tested on Windows 10) ## Windows (tested on Windows 10)
Installation instructions: Installation instructions:
1. Download the JDK from [here](http://www.oracle.com/technetwork/java/javase/downloads/index.html). 1. Download the JDK from [here](http://www.oracle.com/technetwork/java/javase/downloads/index.html).
Install the JDK and update your PATH and JAVA_HOME environment variables. Install the JDK and update your PATH and JAVA_HOME enviroment variables.
2. Download Maven from [here](https://maven.apache.org/). 2. Download Maven from [here](https://maven.apache.org/).
Install Maven (following the README) and update your PATH environment variable. Install Maven (following the README) and update your PATH enviroment variable.
3. Download Mongo DB Community from [here](https://www.mongodb.com/download-center#community). 3. Download Mongo DB Community from [here](https://www.mongodb.com/download-center#community).
Install Mongo DB following the instructions [here](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/). Install Mongo DB following the instructions [here](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-windows/).
Update your PATH environment variable. Update your PATH enviroment variable.
Building instructions: Building instructions:
```batch ```batch
@ -75,69 +61,10 @@ mongod
```batch ```batch
:: Runs the MAR server :: Runs the MAR server
cd Much-Assembly-Required\target cd Much-Assembly-Required\target
java -jar server-1.4a.jar java -jar server-1.2a.jar
``` ```
3. Run the frontend, following the instructions that you can find [here](https://github.com/simon987/Much-Assembly-Required-Frontend).
## macOS (tested on Sierra 10.12.6)
**Installation**
1. Install [Maven3](https://maven.apache.org/)
-Add Maven bin to your path
```bash
export PATH=/path/to/maven/bin.:$PATH
```
2. Install [MongoDB](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/?_ga=2.201359831.774868398.1539369140-197602459.1539369140).
-Via Brew:
```bash
#Update brew
brew update
#Install mongodb
brew install mongodb
#Install latest development release
brew install mongodb --devel
```
-Via .tar.gz
```bash
#Extract files:
tar -zxvf mongodb-osx-ssl-x86_64-4.0.3.tgz
#Ensure binaries are in your path
export PATH=<mongodb-install-directory>/bin:$PATH
```
If you do not wish to use the default data directory (/data/db), follow the steps for running MongoDB in the install doc.
**Deploying Server**
1. Begin MongoDB service
```bash
#If brew:
#Launch on login
brew services start mongodb
#Or, if you don't want/need a background service you can just run:
mongod --config /usr/local/etc/mongod.conf
#If binary:
mongod
#Optional, set data directory path:
mongod --dbpath <path to data directory>
```
2. Deploy server:
```bash
# Obtain source files
git clone https://github.com/simon987/Much-Assembly-Required.git
# Build
cd Much-Assembly-Required
mvn package
# Run
cd target
java -jar server-1.4a.jar
```
## Docker ## Docker
### Requirements ### Requirements
@ -152,25 +79,8 @@ application's directory:
`docker-compose up` `docker-compose up`
Make sure to change `mongo_address` in `config.properties` to `mongodb`. This will start MySQL and then build and run this application. It will
be available via http://localhost.
Note that there is currently no frontend web application serving the
## Vagrant WebSocket feed served by the `Server` application!
### Requirements
1. [Vagrant](https://www.vagrantup.com/downloads.html)
2. [VirtualBox](https://www.virtualbox.org/wiki/Downloads)
### Installation
When vagrant is installed, you can build and start this application by running the following
command inside this application's directory:
`vagrant up`
# Running
Once the server is running, you should be able to connect to `http://localhost:4567` with your browser
## VS Code Extensions
- [Much Assembly Required (Upload on Save)](https://marketplace.visualstudio.com/items?itemName=tomhodder.much-assembly-required-upload-on-save) by tomhodder
- [Much Assembly Required Language Support](https://marketplace.visualstudio.com/items?itemName=PJB3005.much-assembly-required-language-support) by PJB3005

23
Server/Server.iml Normal file
View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: org.java-websocket:Java-WebSocket:1.3.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" name="Maven: mysql:mysql-connector-java:5.1.42" level="project" />
<orderEntry type="library" name="Maven: com.googlecode.json-simple:json-simple:1.1.1" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-text:1.2" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.7" level="project" />
<orderEntry type="library" name="Maven: org.mongodb:mongo-java-driver:2.10.1" level="project" />
</component>
</module>

View File

@ -24,6 +24,10 @@
<resources> <resources>
<resource> <resource>
<directory>../Server/src/main/resources</directory> <directory>../Server/src/main/resources</directory>
<filtering>true</filtering>
<includes>
<include>config.properties</include>
</includes>
</resource> </resource>
</resources> </resources>
</configuration> </configuration>
@ -77,7 +81,6 @@
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<version>2.7.2</version> <version>2.7.2</version>
<configuration> <configuration>
<forkMode>never</forkMode>
<workingDirectory>./src/main/resources</workingDirectory> <workingDirectory>./src/main/resources</workingDirectory>
</configuration> </configuration>
</plugin> </plugin>
@ -86,15 +89,25 @@
<groupId>net.simon987.server</groupId> <groupId>net.simon987.server</groupId>
<artifactId>server</artifactId> <artifactId>server</artifactId>
<version>1.4a</version> <version>1.2a</version>
<dependencies> <dependencies>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.3.6</version>
</dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<version>4.13.1</version> <version>4.12</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.42</version>
</dependency>
<dependency> <dependency>
<groupId>com.googlecode.json-simple</groupId> <groupId>com.googlecode.json-simple</groupId>
<artifactId>json-simple</artifactId> <artifactId>json-simple</artifactId>
@ -103,33 +116,12 @@
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId> <artifactId>commons-text</artifactId>
<version>1.6</version> <version>1.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.mongodb</groupId> <groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-sync</artifactId> <artifactId>mongo-java-driver</artifactId>
<version>3.9.1</version> <version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-core -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
<version>5.1.11.RELEASE</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-core</artifactId>
<version>2.8.0</version>
</dependency>
<dependency>
<groupId>com.sparkjava</groupId>
<artifactId>spark-template-velocity</artifactId>
<version>2.7.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.25</version>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -1,29 +1,21 @@
package net.simon987.server; package net.simon987.server;
import com.mongodb.MongoClientException;
import com.mongodb.client.*; import com.mongodb.*;
import com.mongodb.client.model.ReplaceOptions; import net.simon987.server.assembly.exception.CancelledException;
import net.simon987.server.crypto.CryptoProvider;
import net.simon987.server.crypto.SecretKeyGenerator;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventDispatcher; import net.simon987.server.event.GameEventDispatcher;
import net.simon987.server.event.TickEvent; import net.simon987.server.event.TickEvent;
import net.simon987.server.game.DayNightCycle;
import net.simon987.server.game.GameUniverse; import net.simon987.server.game.GameUniverse;
import net.simon987.server.game.debug.*; import net.simon987.server.game.World;
import net.simon987.server.game.item.ItemCopper;
import net.simon987.server.game.item.ItemIron;
import net.simon987.server.game.objects.GameRegistry;
import net.simon987.server.game.world.*;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.PluginManager; import net.simon987.server.plugin.PluginManager;
import net.simon987.server.plugin.ServerPlugin;
import net.simon987.server.user.User; import net.simon987.server.user.User;
import net.simon987.server.user.UserManager; import net.simon987.server.webserver.SocketServer;
import net.simon987.server.user.UserStatsHelper;
import net.simon987.server.websocket.SocketServer;
import org.bson.Document;
import java.util.ArrayList; import java.io.File;
import java.net.UnknownHostException;
public class GameServer implements Runnable { public class GameServer implements Runnable {
@ -33,7 +25,7 @@ public class GameServer implements Runnable {
private GameEventDispatcher eventDispatcher; private GameEventDispatcher eventDispatcher;
private PluginManager pluginManager; private PluginManager pluginManager;
private IServerConfiguration config; private ServerConfiguration config;
private SocketServer socketServer; private SocketServer socketServer;
@ -41,80 +33,48 @@ public class GameServer implements Runnable {
private DayNightCycle dayNightCycle; private DayNightCycle dayNightCycle;
private CryptoProvider cryptoProvider; private MongoClient mongo = null;
private MongoClient mongo;
private UserManager userManager;
private UserStatsHelper userStatsHelper;
private GameRegistry gameRegistry;
private String secretKey;
public GameServer() { public GameServer() {
this.config = new ServerConfiguration("config.properties"); this.config = new ServerConfiguration("config.properties");
String connString = String.format("mongodb://%s:%d", try{
config.getString("mongo_address"), config.getInt("mongo_port")); mongo = new MongoClient("localhost", 27017);
mongo = MongoClients.create(connString); } catch (UnknownHostException e) {
MongoDatabase db = mongo.getDatabase(config.getString("mongo_dbname")); e.printStackTrace();
}
MongoCollection<Document> userCollection = db.getCollection("user");
userManager = new UserManager(userCollection);
userStatsHelper = new UserStatsHelper(userCollection);
gameUniverse = new GameUniverse(config); gameUniverse = new GameUniverse(config);
gameUniverse.setMongo(mongo); gameUniverse.setMongo(mongo);
gameRegistry = new GameRegistry(); pluginManager = new PluginManager();
pluginManager = new PluginManager(this);
maxExecutionTime = config.getInt("user_timeout"); maxExecutionTime = config.getInt("user_timeout");
cryptoProvider = new CryptoProvider();
dayNightCycle = new DayNightCycle(); dayNightCycle = new DayNightCycle();
SecretKeyGenerator keyGenerator = new SecretKeyGenerator(); //Load all plugins in plugins folder, if it doesn't exist, create it
secretKey = config.getString("secret_key"); File pluginDir = new File("plugins/");
if (secretKey == null) { File[] pluginDirListing = pluginDir.listFiles();
secretKey = keyGenerator.generate();
config.setString("secret_key", secretKey);
}
if (!pluginManager.loadInFolder("plugins/")) { if (pluginDirListing != null) {
System.exit(-1); for (File pluginFile : pluginDirListing) {
if (pluginFile.getName().endsWith(".jar")) {
pluginManager.load(pluginFile, config);
}
}
} else {
if (!pluginDir.mkdir()) {
LogManager.LOGGER.severe("Couldn't create plugin directory");
}
} }
eventDispatcher = new GameEventDispatcher(pluginManager); eventDispatcher = new GameEventDispatcher(pluginManager);
eventDispatcher.getListeners().add(dayNightCycle); eventDispatcher.getListeners().add(dayNightCycle);
//Debug command Listeners
eventDispatcher.getListeners().add(new ComPortMsgCommandListener());
eventDispatcher.getListeners().add(new CreateWorldCommandListener());
eventDispatcher.getListeners().add(new KillAllCommandListener());
eventDispatcher.getListeners().add(new MoveObjCommandListener());
eventDispatcher.getListeners().add(new ObjInfoCommandListener());
eventDispatcher.getListeners().add(new SetTileAtCommandListener());
eventDispatcher.getListeners().add(new SpawnObjCommandListener());
eventDispatcher.getListeners().add(new TpObjectCommandListener());
eventDispatcher.getListeners().add(new UserInfoCommandListener());
eventDispatcher.getListeners().add(new HealObjCommandListener());
eventDispatcher.getListeners().add(new DamageObjCommandListener());
eventDispatcher.getListeners().add(new SetEnergyCommandListener());
eventDispatcher.getListeners().add(new SaveGameCommandListener());
gameRegistry.registerItem(ItemCopper.ID, ItemCopper.class);
gameRegistry.registerItem(ItemIron.ID, ItemIron.class);
gameRegistry.registerTile(TileVoid.ID, TileVoid.class);
gameRegistry.registerTile(TilePlain.ID, TilePlain.class);
gameRegistry.registerTile(TileWall.ID, TileWall.class);
gameRegistry.registerTile(TileCopper.ID, TileCopper.class);
gameRegistry.registerTile(TileIron.ID, TileIron.class);
gameRegistry.registerTile(TileFluid.ID, TileFluid.class);
} }
public GameUniverse getGameUniverse() { public GameUniverse getGameUniverse() {
@ -125,10 +85,6 @@ public class GameServer implements Runnable {
return eventDispatcher; return eventDispatcher;
} }
public CryptoProvider getCryptoProvider() {
return cryptoProvider;
}
@Override @Override
public void run() { public void run() {
LogManager.LOGGER.info("(G) Started game loop"); LogManager.LOGGER.info("(G) Started game loop");
@ -148,6 +104,8 @@ public class GameServer implements Runnable {
uTime = System.currentTimeMillis() - startTime; uTime = System.currentTimeMillis() - startTime;
waitTime = config.getInt("tick_length") - uTime; waitTime = config.getInt("tick_length") - uTime;
LogManager.LOGGER.info("Wait time : " + waitTime + "ms | Update time: " + uTime + "ms | " + (int) (((double) uTime / waitTime) * 100) + "% load");
try { try {
if (waitTime >= 0) { if (waitTime >= 0) {
Thread.sleep(waitTime); Thread.sleep(waitTime);
@ -155,7 +113,10 @@ public class GameServer implements Runnable {
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
private void tick() { private void tick() {
@ -165,30 +126,33 @@ public class GameServer implements Runnable {
GameEvent event = new TickEvent(gameUniverse.getTime()); GameEvent event = new TickEvent(gameUniverse.getTime());
eventDispatcher.dispatch(event); //Ignore cancellation eventDispatcher.dispatch(event); //Ignore cancellation
//Process user code //Process user code
for (User user : gameUniverse.getUsers()) { for (User user : gameUniverse.getUsers()) {
if (user.getControlledUnit() != null && user.getControlledUnit().getCpu() != null) { if (user.getCpu() != null) {
try { try {
int timeout = Math.min(user.getControlledUnit().getEnergy(), maxExecutionTime); int timeout = Math.min(user.getControlledUnit().getEnergy(), maxExecutionTime);
user.getControlledUnit().getCpu().reset(); user.getCpu().reset();
int cost = user.getControlledUnit().getCpu().execute(timeout); int cost = user.getCpu().execute(timeout);
user.getControlledUnit().spendEnergy(cost); user.getControlledUnit().spendEnergy(cost);
user.addTime(cost);
} catch (Exception e) { } catch (Exception e) {
LogManager.LOGGER.severe("Error executing " + user.getUsername() + "'s code"); LogManager.LOGGER.severe("Error executing " + user.getUsername() + "'s code");
e.printStackTrace(); e.printStackTrace();
} }
} }
} }
//Process each worlds //Process each worlds
int updatedWorlds = 0;
for (World world : gameUniverse.getWorlds()) { for (World world : gameUniverse.getWorlds()) {
if (world.shouldUpdate()) { if (world.shouldUpdate()) {
world.update(); world.update();
updatedWorlds++;
} }
} }
@ -198,120 +162,104 @@ public class GameServer implements Runnable {
} }
socketServer.tick(); socketServer.tick();
LogManager.LOGGER.info("Processed " + gameUniverse.getWorldCount() + " worlds (" + updatedWorlds +
") updated");
} }
void load() { void load() {
LogManager.LOGGER.info("Loading all data from MongoDB"); LogManager.LOGGER.info("Loading all data from MongoDB");
MongoDatabase db = mongo.getDatabase(config.getString("mongo_dbname")); DB db = mongo.getDB("mar");
MongoCollection<Document> worlds = db.getCollection("world"); DBCollection worlds = db.getCollection("world");
MongoCollection<Document> server = db.getCollection("server"); DBCollection users = db.getCollection("user");
DBCollection server = db.getCollection("server");
Document whereQuery = new Document(); BasicDBObject whereQuery = new BasicDBObject();
whereQuery.put("shouldUpdate", true); whereQuery.put("shouldUpdate", true);
MongoCursor<Document> cursor = worlds.find(whereQuery).iterator(); DBCursor cursor = worlds.find(whereQuery);
GameUniverse universe = GameServer.INSTANCE.getGameUniverse(); GameUniverse universe = GameServer.INSTANCE.getGameUniverse();
while (cursor.hasNext()) { while (cursor.hasNext()) {
World w = World.deserialize(cursor.next()); World w = World.deserialize(cursor.next());
universe.addWorld(w); universe.addWorld(w);
} }
//Load users //Load users
ArrayList<User> userList = userManager.getUsers(); cursor = users.find();
for (User user : userList) { while (cursor.hasNext()) {
universe.addUser(user); try {
universe.addUser(User.deserialize(cursor.next()));
} catch (CancelledException e) {
e.printStackTrace();
}
} }
//Load server & plugin data //Load misc server info
cursor = server.find().iterator(); cursor = server.find();
if (cursor.hasNext()) { if (cursor.hasNext()) {
Document serverObj = cursor.next(); DBObject serverObj = cursor.next();
gameUniverse.setTime((long) serverObj.get("time")); gameUniverse.setTime((long) serverObj.get("time"));
gameUniverse.setNextObjectId((long) serverObj.get("nextObjectId"));
Document plugins = (Document) serverObj.get("plugins");
for (String pluginName : plugins.keySet()) {
ServerPlugin plugin = pluginManager.getPluginByName(pluginName);
plugin.load((Document) plugins.get(pluginName));
}
} }
LogManager.LOGGER.info("Done loading! W:" + GameServer.INSTANCE.getGameUniverse().getWorldCount() + LogManager.LOGGER.info("Done loading! W:" + GameServer.INSTANCE.getGameUniverse().getWorldCount() +
" | U:" + GameServer.INSTANCE.getGameUniverse().getUserCount()); " | U:" + GameServer.INSTANCE.getGameUniverse().getUserCount());
} }
public void save() { private void save() {
LogManager.LOGGER.info("Saving to MongoDB | W:" + gameUniverse.getWorldCount() + " | U:" + gameUniverse.getUserCount()); LogManager.LOGGER.info("Saving to MongoDB | W:" + gameUniverse.getWorldCount() + " | U:" + gameUniverse.getUserCount());
try{
DB db = mongo.getDB("mar");
ClientSession session = null; int unloaded_worlds = 0;
try {
try {
session = mongo.startSession();
session.startTransaction();
} catch (MongoClientException e) {
LogManager.LOGGER.fine("Could not create mongoDB session, will not use transaction feature. " +
"(This message can be safely ignored)");
}
MongoDatabase db = mongo.getDatabase(config.getString("mongo_dbname")); DBCollection worlds = db.getCollection("world");
ReplaceOptions updateOptions = new ReplaceOptions(); DBCollection users = db.getCollection("user");
updateOptions.upsert(true); DBCollection server = db.getCollection("server");
int unloaded_worlds = 0; int insertedWorlds = 0;
GameUniverse universe = GameServer.INSTANCE.getGameUniverse();
MongoCollection<Document> worlds = db.getCollection("world");
MongoCollection<Document> users = db.getCollection("user");
MongoCollection<Document> server = db.getCollection("server");
int insertedWorlds = 0;
GameUniverse universe = GameServer.INSTANCE.getGameUniverse();
for (World w : universe.getWorlds()) { for (World w : universe.getWorlds()) {
// LogManager.LOGGER.fine("Saving world "+w.getId()+" to mongodb");
insertedWorlds++; insertedWorlds++;
worlds.replaceOne(new Document("_id", w.getId()), w.mongoSerialise(), updateOptions); worlds.save(w.mongoSerialise());
//If the world should unload, it is removed from the Universe after having been saved. // If the world should unload, it is removed from the Universe after having been saved.
if (w.shouldUnload()) { if (w.shouldUnload()){
unloaded_worlds++; unloaded_worlds++;
// LogManager.LOGGER.fine("Unloading world "+w.getId()+" from universe");
universe.removeWorld(w); universe.removeWorld(w);
} }
} }
for (User u : GameServer.INSTANCE.getGameUniverse().getUsers()) { for (User u : GameServer.INSTANCE.getGameUniverse().getUsers()) {
if (!u.isGuest()) {
users.replaceOne(new Document("_id", u.getUsername()), u.mongoSerialise(), updateOptions);
}
}
Document serverObj = new Document(); if (!u.isGuest()) {
serverObj.put("time", gameUniverse.getTime()); users.save(u.mongoSerialise());
}
Document plugins = new Document(); }
for (ServerPlugin plugin : pluginManager.getPlugins()) {
plugins.put(plugin.getName(), plugin.mongoSerialise());
}
serverObj.put("plugins", plugins);
//A constant id ensures only one entry is kept and updated, instead of a new entry created every save. BasicDBObject serverObj = new BasicDBObject();
server.replaceOne(new Document("_id", "serverinfo"), serverObj, updateOptions); serverObj.put("_id","serverinfo"); // a constant id ensures only one entry is kept and updated, instead of a new entry created every save.
if (session != null) { serverObj.put("time", gameUniverse.getTime());
session.commitTransaction(); serverObj.put("nextObjectId", gameUniverse.getNextObjectId());
} server.save(serverObj);
LogManager.LOGGER.info("" + insertedWorlds + " worlds saved, " + unloaded_worlds + " unloaded"); LogManager.LOGGER.info(""+insertedWorlds+" worlds saved, "+unloaded_worlds+" unloaded");
LogManager.LOGGER.info("Done!");
} catch (Exception e) { } catch (Exception e) {
LogManager.LOGGER.severe("Problem happened during save function"); LogManager.LOGGER.severe("Problem happened during save function");
e.printStackTrace(); e.printStackTrace();
if (session != null) {
session.commitTransaction();
}
} }
} }
public IServerConfiguration getConfig() { public ServerConfiguration getConfig() {
return config; return config;
} }
@ -326,25 +274,4 @@ public class GameServer implements Runnable {
public DayNightCycle getDayNightCycle() { public DayNightCycle getDayNightCycle() {
return dayNightCycle; return dayNightCycle;
} }
public UserManager getUserManager() {
return userManager;
}
public UserStatsHelper getUserStatsHelper() {
return userStatsHelper;
}
public GameRegistry getRegistry() {
return gameRegistry;
}
public String getSecretKey() {
return secretKey;
}
public void setSecretKey(String secretKey) {
this.secretKey = secretKey;
config.setString("secret_key", secretKey);
}
} }

View File

@ -1,12 +0,0 @@
package net.simon987.server;
public interface IServerConfiguration {
int getInt(String key);
String getString(String key);
void setInt(String key, int value);
void setString(String key, String value);
}

View File

@ -1,25 +1,26 @@
package net.simon987.server; package net.simon987.server;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import net.simon987.server.web.WebServer; import net.simon987.server.webserver.SocketServer;
import spark.Spark;
import java.net.InetSocketAddress;
public class Main { public class Main {
public static void main(String[] args) { public static void main(String[] args) {
ServerConfiguration config = new ServerConfiguration("config.properties"); ServerConfiguration config = new ServerConfiguration("config.properties");
LogManager.initialize(config); LogManager.initialize(config);
//Load
GameServer.INSTANCE.load(); GameServer.INSTANCE.load();
//Web server SocketServer socketServer = new SocketServer(new InetSocketAddress(config.getString("webSocket_host"),
WebServer webServer = new WebServer(GameServer.INSTANCE.getConfig()); config.getInt("webSocket_port")), config);
Spark.awaitInitialization(); GameServer.INSTANCE.setSocketServer(socketServer);
GameServer.INSTANCE.setSocketServer(webServer.getSocketServer());
(new Thread(socketServer)).start();
(new Thread(GameServer.INSTANCE)).start(); (new Thread(GameServer.INSTANCE)).start();
} }
} }

View File

@ -3,28 +3,26 @@ package net.simon987.server;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import java.io.*; import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties; import java.util.Properties;
/** /**
* Wrapper for Java Properties class * Wrapper for Java Property
*/ */
public class ServerConfiguration implements IServerConfiguration { public class ServerConfiguration {
/**
* Properties
*/
private Properties properties; private Properties properties;
private String fileName;
public ServerConfiguration() {
this.properties = new Properties();
}
public ServerConfiguration(String fileName) {
this.fileName = fileName;
public ServerConfiguration(String file) {
try { try {
properties = new Properties(); properties = new Properties();
InputStream is = new FileInputStream(this.fileName); InputStream is = new FileInputStream("config.properties");
properties.load(is); properties.load(is);
} catch (IOException e) { } catch (IOException e) {
@ -32,17 +30,6 @@ public class ServerConfiguration implements IServerConfiguration {
} }
} }
private void saveConfig() {
try {
OutputStream os = new FileOutputStream(this.fileName);
properties.store(os, "");
} catch (IOException e) {
LogManager.LOGGER.severe("Problem saving server configuration: " + e.getMessage());
}
}
public int getInt(String key) { public int getInt(String key) {
return Integer.valueOf((String) properties.get(key)); return Integer.valueOf((String) properties.get(key));
@ -53,14 +40,4 @@ public class ServerConfiguration implements IServerConfiguration {
return (String) properties.get(key); return (String) properties.get(key);
} }
public void setInt(String key, int value) {
properties.setProperty(key, String.valueOf(value));
saveConfig();
}
public void setString(String key, String value) {
properties.setProperty(key, value);
saveConfig();
}
} }

View File

@ -1,6 +1,6 @@
package net.simon987.server.assembly; package net.simon987.server.assembly;
import net.simon987.server.IServerConfiguration; import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.exception.*; import net.simon987.server.assembly.exception.*;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.text.StringEscapeUtils;
@ -18,22 +18,18 @@ import java.util.regex.Pattern;
*/ */
public class Assembler { public class Assembler {
private IServerConfiguration config; private ServerConfiguration config;
private InstructionSet instructionSet; private InstructionSet instructionSet;
private RegisterSet registerSet; private RegisterSet registerSet;
private static int MEM_SIZE; private static final int MEM_SIZE = 0x10000; // Size in words todo load from config
private static String labelPattern = "^\\s*[a-zA-Z_]\\w*:";
private static Pattern commentPattern = Pattern.compile("\"[^\"]*\"|(;)");
public Assembler(InstructionSet instructionSet, RegisterSet registerSet, IServerConfiguration config) { public Assembler(InstructionSet instructionSet, RegisterSet registerSet, ServerConfiguration config) {
this.instructionSet = instructionSet; this.instructionSet = instructionSet;
this.registerSet = registerSet; this.registerSet = registerSet;
this.config = config; this.config = config;
Assembler.MEM_SIZE = config.getInt("memory_size");
} }
/** /**
@ -43,17 +39,11 @@ public class Assembler {
* @return The line without its comment part * @return The line without its comment part
*/ */
private static String removeComment(String line) { private static String removeComment(String line) {
if (line.indexOf(';') != -1) {
Matcher m = commentPattern.matcher(line); return line.substring(0, line.indexOf(';'));
} else {
while (m.find()) { return line;
try {
return line.substring(0, m.start(1));
} catch (IndexOutOfBoundsException ignored) {
}
} }
return line;
} }
/** /**
@ -64,13 +54,14 @@ public class Assembler {
*/ */
private static String removeLabel(String line) { private static String removeLabel(String line) {
return line.replaceAll(labelPattern, ""); return line.replaceAll("^\\s*\\b\\w*\\b:", "");
} }
/** /**
* Check for and save the origin * Check for and save the origin
* *
* @param line Current line. Assuming that the comments and labels are removed * @param line Current line. Assuming that the comments & labels are removed
* @param result Current line number * @param result Current line number
*/ */
private static void checkForORGInstruction(String line, AssemblyResult result, int currentLine) private static void checkForORGInstruction(String line, AssemblyResult result, int currentLine)
@ -106,7 +97,7 @@ public class Assembler {
line = removeComment(line); line = removeComment(line);
//Check for labels //Check for labels
Pattern pattern = Pattern.compile(labelPattern); Pattern pattern = Pattern.compile("^\\s*\\b\\w*\\b:");
Matcher matcher = pattern.matcher(line); Matcher matcher = pattern.matcher(line);
if (matcher.find()) { if (matcher.find()) {
@ -130,7 +121,7 @@ public class Assembler {
/** /**
* Parse the DW instruction (Define word). Handles DUP operator * Parse the DW instruction (Define word). Handles DUP operator
* *
* @param line Current line. assuming that comments and labels are removed * @param line Current line. assuming that comments & labels are removed
* @param currentLine Current line number * @param currentLine Current line number
* @param labels Map of labels * @param labels Map of labels
* @return Encoded instruction, null if the line is not a DW instruction * @return Encoded instruction, null if the line is not a DW instruction
@ -148,7 +139,7 @@ public class Assembler {
try { try {
//Special thanks to https://stackoverflow.com/questions/1757065/ //Special thanks to https://stackoverflow.com/questions/1757065/
String[] values = line.substring(2).split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1); String[] values = line.substring(2, line.length()).split(",(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)", -1);
for (String value : values) { for (String value : values) {
@ -164,14 +155,7 @@ public class Assembler {
//Unescape the string //Unescape the string
String string = value.substring(1, value.length() - 1); String string = value.substring(1, value.length() - 1);
string = StringEscapeUtils.unescapeJava(string);
try {
string = StringEscapeUtils.unescapeJava(string);
} catch (IllegalArgumentException e) {
throw new InvalidOperandException(
"Invalid string operand \"" + string + "\": " + e.getMessage(),
currentLine);
}
out.write(string.getBytes(StandardCharsets.UTF_16BE)); out.write(string.getBytes(StandardCharsets.UTF_16BE));
} else if (labels != null && labels.containsKey(value)) { } else if (labels != null && labels.containsKey(value)) {
@ -220,17 +204,6 @@ public class Assembler {
} }
/**
* Parse the DW instruction (Define word). Handles DUP operator
*
* @param line Current line. assuming that comments and labels are removed
* @param currentLine Current line number
* @return Encoded instruction, null if the line is not a DW instruction
*/
private static byte[] parseDWInstruction(String line, int currentLine) throws AssemblyException {
return parseDWInstruction(line, null, currentLine);
}
/** /**
* Parse the dup operator * Parse the dup operator
* *
@ -283,7 +256,18 @@ public class Assembler {
} }
/** /**
* Check for and handle section declarations (.text and .data) * Parse the DW instruction (Define word). Handles DUP operator
*
* @param line Current line. assuming that comments & labels are removed
* @param currentLine Current line number
* @return Encoded instruction, null if the line is not a DW instruction
*/
private static byte[] parseDWInstruction(String line, int currentLine) throws AssemblyException {
return parseDWInstruction(line, null, currentLine);
}
/**
* Check for and handle section declarations (.text & .data)
* *
* @param line Current line * @param line Current line
*/ */
@ -294,14 +278,14 @@ public class Assembler {
if (tokens[0].toUpperCase().equals(".TEXT")) { if (tokens[0].toUpperCase().equals(".TEXT")) {
result.defineSection(Section.TEXT, currentLine, currentOffset); result.defineSecton(Section.TEXT, currentLine, currentOffset);
throw new PseudoInstructionException(currentLine); throw new PseudoInstructionException(currentLine);
} else if (tokens[0].toUpperCase().equals(".DATA")) { } else if (tokens[0].toUpperCase().equals(".DATA")) {
LogManager.LOGGER.fine("DEBUG: .data @" + currentLine); LogManager.LOGGER.fine("DEBUG: .data @" + currentLine);
result.defineSection(Section.DATA, currentLine, currentOffset); result.defineSecton(Section.DATA, currentLine, currentOffset);
throw new PseudoInstructionException(currentLine); throw new PseudoInstructionException(currentLine);
} }
} }
@ -318,7 +302,7 @@ public class Assembler {
/* the EQU pseudo instruction is equivalent to the #define compiler directive in C/C++ /* the EQU pseudo instruction is equivalent to the #define compiler directive in C/C++
* usage: constant_name EQU <immediate_value> * usage: constant_name EQU <immediate_value>
* A constant treated the same way as a label. * A constant treated the same way as a label.
*/ */
line = line.trim(); line = line.trim();
String[] tokens = line.split("\\s+"); String[] tokens = line.split("\\s+");
@ -362,47 +346,7 @@ public class Assembler {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
//Pass 1: Get code origin //Pass 1: Get code origin
getCodeOrigin(lines, result); for (currentLine = 0; currentLine < lines.length; currentLine++) {
//Pass 2: Save label names and location
saveLabelNamesAndLocation(lines, result);
//Pass 3: encode instructions
encodeInstructions(lines, result, out);
//If the code contains OffsetOverFlowException(s), don't bother writing the assembled bytes to memory
boolean writeToMemory = true;
for (Exception e : result.exceptions) {
if (e instanceof OffsetOverflowException) {
writeToMemory = false;
break;
}
}
if (writeToMemory) {
result.bytes = out.toByteArray();
} else {
result.bytes = new byte[0];
LogManager.LOGGER.fine("Skipping writing assembled bytes to memory. (OffsetOverflowException)");
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
LogManager.LOGGER.info("Assembled " + result.bytes.length + " bytes (" + result.exceptions.size() + " errors)");
for (AssemblyException e : result.exceptions) {
LogManager.LOGGER.severe(e.getMessage() + '@' + e.getLine());
}
LogManager.LOGGER.info('\n' + Util.toHex(result.bytes));
return result;
}
private void getCodeOrigin(String[] lines, AssemblyResult result) {
for (int currentLine = 0; currentLine < lines.length; currentLine++) {
try { try {
checkForORGInstruction(lines[currentLine], result, currentLine); checkForORGInstruction(lines[currentLine], result, currentLine);
@ -413,11 +357,10 @@ public class Assembler {
//Ignore error //Ignore error
} }
} }
}
private void saveLabelNamesAndLocation(String[] lines, AssemblyResult result) { //Pass 2: Save label names and location
int currentOffset = 0; int currentOffset = 0;
for (int currentLine = 0; currentLine < lines.length; currentLine++) { for (currentLine = 0; currentLine < lines.length; currentLine++) {
try { try {
checkForLabel(lines[currentLine], result, (char)currentOffset); checkForLabel(lines[currentLine], result, (char)currentOffset);
@ -435,11 +378,11 @@ public class Assembler {
} }
} }
}
private void encodeInstructions(String[] lines, AssemblyResult result, ByteArrayOutputStream out) {
int currentOffset = 0; //Pass 3: encode instructions
for (int currentLine = 0; currentLine < lines.length; currentLine++) { currentOffset = 0;
for (currentLine = 0; currentLine < lines.length; currentLine++) {
String line = lines[currentLine]; String line = lines[currentLine];
@ -480,6 +423,36 @@ public class Assembler {
ioE.printStackTrace(); ioE.printStackTrace();
} }
} }
//If the code contains OffsetOverFlowException(s), don't bother writing the assembled bytes to memory
boolean writeToMemory = true;
for (Exception e : result.exceptions) {
if (e instanceof OffsetOverflowException) {
writeToMemory = false;
}
}
if (writeToMemory) {
result.bytes = out.toByteArray();
} else {
result.bytes = new byte[0];
LogManager.LOGGER.fine("Skipping writing assembled bytes to memory. (OffsetOverflowException)");
}
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
LogManager.LOGGER.info("Assembled " + result.bytes.length + " bytes (" + result.exceptions.size() + " errors)");
for (AssemblyException e : result.exceptions) {
LogManager.LOGGER.severe(e.getMessage() + '@' + e.getLine());
}
LogManager.LOGGER.info('\n' + Util.toHex(result.bytes));
return result;
} }
/** /**
@ -555,13 +528,13 @@ public class Assembler {
if (instructionSet.get(mnemonic) == null) { if (instructionSet.get(mnemonic) == null) {
throw new InvalidMnemonicException(mnemonic, currentLine); throw new InvalidMnemonicException(mnemonic, currentLine);
} }
//Check operands and encode instruction //Check operands and encode instruction
final int beginIndex = line.indexOf(mnemonic) + mnemonic.length();
if (line.contains(",")) { if (line.contains(",")) {
//2 operands //2 operands
String strO1 = line.substring(beginIndex, line.indexOf(',')); String strO1 = line.substring(line.indexOf(mnemonic) + mnemonic.length(), line.indexOf(','));
String strO2 = line.substring(line.indexOf(',')); String strO2 = line.substring(line.indexOf(','));
Operand o1, o2; Operand o1, o2;
@ -581,7 +554,7 @@ public class Assembler {
} else if (tokens.length > 1) { } else if (tokens.length > 1) {
//1 operand //1 operand
String strO1 = line.substring(beginIndex); String strO1 = line.substring(line.indexOf(mnemonic) + mnemonic.length());
Operand o1; Operand o1;
if (assumeLabels) { if (assumeLabels) {
@ -602,5 +575,6 @@ public class Assembler {
} }
return out.toByteArray(); return out.toByteArray();
} }
} }

View File

@ -1,6 +1,6 @@
package net.simon987.server.assembly; package net.simon987.server.assembly;
import net.simon987.server.IServerConfiguration; import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.exception.AssemblyException; import net.simon987.server.assembly.exception.AssemblyException;
import net.simon987.server.assembly.exception.DuplicateSectionException; import net.simon987.server.assembly.exception.DuplicateSectionException;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
@ -59,7 +59,7 @@ public class AssemblyResult {
*/ */
private boolean dataSectionSet = false; private boolean dataSectionSet = false;
AssemblyResult(IServerConfiguration config) { AssemblyResult(ServerConfiguration config) {
origin = config.getInt("org_offset"); origin = config.getInt("org_offset");
} }
@ -71,7 +71,7 @@ public class AssemblyResult {
* @param currentLine Line number of the section declaration * @param currentLine Line number of the section declaration
* @throws DuplicateSectionException when a section is defined twice * @throws DuplicateSectionException when a section is defined twice
*/ */
void defineSection(Section section, int currentLine, int currentOffset) throws DuplicateSectionException { void defineSecton(Section section, int currentLine, int currentOffset) throws DuplicateSectionException {
if (section == Section.TEXT) { if (section == Section.TEXT) {
//Code section //Code section

View File

@ -1,23 +1,26 @@
package net.simon987.server.assembly; package net.simon987.server.assembly;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.IServerConfiguration; import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.exception.CancelledException; import net.simon987.server.assembly.exception.CancelledException;
import net.simon987.server.assembly.instruction.*; import net.simon987.server.assembly.instruction.*;
import net.simon987.server.event.CpuInitialisationEvent; import net.simon987.server.event.CpuInitialisationEvent;
import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEvent;
import net.simon987.server.game.objects.ControllableUnit; import net.simon987.server.io.MongoSerialisable;
import net.simon987.server.game.objects.HardwareHost;
import net.simon987.server.io.MongoSerializable;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import org.bson.Document; import net.simon987.server.user.User;
import java.util.HashMap;
/** /**
* CPU: Central Processing Unit. A CPU is capable of reading bytes from * CPU: Central Processing Unit. A CPU is capable of reading bytes from
* a Memory object and execute them. A CPU object holds registers objects and * a Memory object and execute them. A CPU object holds registers objects &
* a Memory object. * a Memory object.
*/ */
public class CPU implements MongoSerializable { public class CPU implements MongoSerialisable {
/** /**
* *
@ -51,56 +54,25 @@ public class CPU implements MongoSerializable {
private int ip; private int ip;
/** /**
* Hardware is connected to the hardwareHost * List of attached hardware, 'modules'
*/ */
private HardwareHost hardwareHost; private HashMap<Integer, CpuHardware> attachedHardware;
private ServerConfiguration config;
private int registerSetSize; private int registerSetSize;
private static final char EXECUTION_COST_ADDR = 0x0050; private static final char EXECUTION_COST_ADDR = 0x0050;
private static final char EXECUTED_INS_ADDR = 0x0051; private static final char EXECUTED_INS_ADDR = 0x0051;
public CPU() {
instructionSet = new DefaultInstructionSet();
registerSet = new DefaultRegisterSet();
codeSectionOffset = GameServer.INSTANCE.getConfig().getInt("org_offset");
instructionSet.add(new JmpInstruction(this));
instructionSet.add(new JnzInstruction(this));
instructionSet.add(new JzInstruction(this));
instructionSet.add(new JgInstruction(this));
instructionSet.add(new JgeInstruction(this));
instructionSet.add(new JleInstruction(this));
instructionSet.add(new JlInstruction(this));
instructionSet.add(new PushInstruction(this));
instructionSet.add(new PopInstruction(this));
instructionSet.add(new CallInstruction(this));
instructionSet.add(new RetInstruction(this));
instructionSet.add(new MulInstruction(this));
instructionSet.add(new DivInstruction(this));
instructionSet.add(new JnsInstruction(this));
instructionSet.add(new JsInstruction(this));
instructionSet.add(new HwiInstruction(this));
instructionSet.add(new HwqInstruction(this));
instructionSet.add(new XchgInstruction(this));
instructionSet.add(new JcInstruction(this));
instructionSet.add(new JncInstruction(this));
instructionSet.add(new JnoInstruction(this));
instructionSet.add(new JoInstruction(this));
instructionSet.add(new PushfInstruction(this));
instructionSet.add(new PopfInstruction(this));
instructionSet.add(new JnaInstruction(this));
instructionSet.add(new JaInstruction(this));
status = new Status();
}
/** /**
* Creates a new CPU * Creates a new CPU
*/ */
public CPU(IServerConfiguration config, ControllableUnit unit) throws CancelledException { public CPU(ServerConfiguration config, User user) throws CancelledException {
this.config = config;
instructionSet = new DefaultInstructionSet(); instructionSet = new DefaultInstructionSet();
registerSet = new DefaultRegisterSet(); registerSet = new DefaultRegisterSet();
attachedHardware = new HashMap<>();
codeSectionOffset = config.getInt("org_offset"); codeSectionOffset = config.getInt("org_offset");
instructionSet.add(new JmpInstruction(this)); instructionSet.add(new JmpInstruction(this));
@ -133,7 +105,7 @@ public class CPU implements MongoSerializable {
status = new Status(); status = new Status();
memory = new Memory(config.getInt("memory_size")); memory = new Memory(config.getInt("memory_size"));
GameEvent event = new CpuInitialisationEvent(this, unit); GameEvent event = new CpuInitialisationEvent(this, user);
GameServer.INSTANCE.getEventDispatcher().dispatch(event); GameServer.INSTANCE.getEventDispatcher().dispatch(event);
if (event.isCancelled()) { if (event.isCancelled()) {
throw new CancelledException(); throw new CancelledException();
@ -188,7 +160,7 @@ public class CPU implements MongoSerializable {
} }
int elapsed = (int) (System.currentTimeMillis() - startTime); int elapsed = (int) (System.currentTimeMillis() - startTime);
// LogManager.LOGGER.fine(counter + " instruction in " + elapsed + "ms : " + (double) counter / (elapsed / 1000) / 1000000 + "MHz"); LogManager.LOGGER.fine(counter + " instruction in " + elapsed + "ms : " + (double) counter / (elapsed / 1000) / 1000000 + "MHz");
//Write execution cost and instruction count to memory //Write execution cost and instruction count to memory
@ -201,17 +173,111 @@ public class CPU implements MongoSerializable {
public void executeInstruction(Instruction instruction, int source, int destination) { public void executeInstruction(Instruction instruction, int source, int destination) {
//Execute the instruction //Execute the instruction
if (source == 0) { if (source == 0) {
//No operand (assuming that destination is also null) //No operand (assuming that destination is also null)
ip++; ip++;
instruction.execute(status); instruction.execute(status);
} else if (source == Operand.IMMEDIATE_VALUE) { } else if (source == Operand.IMMEDIATE_VALUE) {
executeImmediateValue(instruction, source, destination); ip++;
int sourceValue = memory.get(ip);
if (destination == 0) {
//Single operand
ip++;
instruction.execute(sourceValue, status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value too
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with 2" +
"immediate values as operands"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate
ip += 2;
instruction.execute(memory, memory.get(ip - 1), sourceValue, status);
} else if (destination <= registerSetSize) {
//Destination is a register
ip++;
instruction.execute(registerSet, destination, sourceValue, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), sourceValue, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1),
sourceValue, status);
}
} else if (source == Operand.IMMEDIATE_VALUE_MEM) { } else if (source == Operand.IMMEDIATE_VALUE_MEM) {
executeImmediateValueMem(instruction, source, destination); //Source is [x]
ip++;
int sourceValue = memory.get(memory.get(ip));
if (destination == 0) {
//Single operand
ip++;
instruction.execute(memory, memory.get(ip - 1), status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with an" +
"immediate values as dst operand"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate too
ip += 2;
instruction.execute(memory, memory.get(ip - 1), sourceValue, status);
} else if (destination <= registerSetSize) {
//Destination is a register
ip++;
instruction.execute(registerSet, destination, sourceValue, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), sourceValue, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1), sourceValue, status);
}
} else if (source <= registerSetSize) { } else if (source <= registerSetSize) {
executeSourceIsRegister(instruction, source, destination); //Source is a register
if (destination == 0) {
//Single operand
ip++;
instruction.execute(registerSet, source, status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with an" +
"immediate values as dst operand"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate
ip += 2;
instruction.execute(memory, memory.get(ip - 1), registerSet, source, status);
} else if (destination <= registerSetSize) {
//Destination is a register too
ip++;
instruction.execute(registerSet, destination, registerSet, source, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), registerSet, source, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1),
registerSet, source, status);
}
} else if (source <= registerSetSize * 2) { } else if (source <= registerSetSize * 2) {
//Source is [reg] //Source is [reg]
if (destination == 0) { if (destination == 0) {
@ -281,129 +347,48 @@ public class CPU implements MongoSerializable {
} }
} }
private void executeSourceIsRegister(Instruction instruction, int source, int destination) {
//Source is a register
if (destination == 0) {
//Single operand
ip++;
instruction.execute(registerSet, source, status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with an" +
"immediate values as dst operand"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate
ip += 2;
instruction.execute(memory, memory.get(ip - 1), registerSet, source, status);
} else if (destination <= registerSetSize) {
//Destination is a register too
ip++;
instruction.execute(registerSet, destination, registerSet, source, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), registerSet, source, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1),
registerSet, source, status);
}
}
private void executeImmediateValue(Instruction instruction, int source, int destination) {
ip++;
int sourceValue = memory.get(ip);
if (destination == 0) {
//Single operand
ip++;
instruction.execute(sourceValue, status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value too
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with 2" +
"immediate values as operands"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate
ip += 2;
instruction.execute(memory, memory.get(ip - 1), sourceValue, status);
} else if (destination <= registerSetSize) {
//Destination is a register
ip++;
instruction.execute(registerSet, destination, sourceValue, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), sourceValue, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1),
sourceValue, status);
}
}
private void executeImmediateValueMem(Instruction instruction, int source, int destination) {
//Source is [x]
ip++;
int sourceValue = memory.get(memory.get(ip));
if (destination == 0) {
//Single operand
ip++;
instruction.execute(memory, memory.get(ip - 1), status);
} else if (destination == Operand.IMMEDIATE_VALUE) {
//Destination is an immediate value
//this shouldn't happen
LogManager.LOGGER.severe("Trying to execute an instruction with an" +
"immediate values as dst operand"); //todo remove debug info
} else if (destination == Operand.IMMEDIATE_VALUE_MEM) {
//Destination is memory immediate too
ip += 2;
instruction.execute(memory, memory.get(ip - 1), sourceValue, status);
} else if (destination <= registerSetSize) {
//Destination is a register
ip++;
instruction.execute(registerSet, destination, sourceValue, status);
} else if (destination <= registerSetSize * 2) {
//Destination is [reg]
ip++;
instruction.execute(memory, registerSet.get(destination - registerSetSize), sourceValue, status);
} else {
//Assuming that destination is [reg + x]
ip += 2;
instruction.execute(memory, registerSet.get(destination - registerSetSize - registerSetSize) + memory.get(ip - 1), sourceValue, status);
}
}
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document dbObject = new Document(); BasicDBObject dbObject = new BasicDBObject();
dbObject.put("memory", memory.mongoSerialise()); dbObject.put("memory", memory.mongoSerialise());
dbObject.put("registerSet", registerSet.mongoSerialise()); dbObject.put("registerSet", registerSet.mongoSerialise());
dbObject.put("codeSegmentOffset", codeSectionOffset); dbObject.put("codeSegmentOffset", codeSectionOffset);
BasicDBList hardwareList = new BasicDBList();
for (Integer address : attachedHardware.keySet()) {
CpuHardware hardware = attachedHardware.get(address);
BasicDBObject serialisedHw = hardware.mongoSerialise();
serialisedHw.put("address", address);
hardwareList.add(serialisedHw);
}
dbObject.put("hardware", hardwareList);
return dbObject; return dbObject;
} }
public static CPU deserialize(Document obj, ControllableUnit unit) throws CancelledException { public static CPU deserialize(DBObject obj, User user) throws CancelledException {
CPU cpu = new CPU(GameServer.INSTANCE.getConfig(), unit); CPU cpu = new CPU(GameServer.INSTANCE.getConfig(), user);
cpu.codeSectionOffset = obj.getInteger("codeSegmentOffset"); cpu.codeSectionOffset = (int) obj.get("codeSegmentOffset");
cpu.memory = new Memory((Document) obj.get("memory")); BasicDBList hardwareList = (BasicDBList) obj.get("hardware");
cpu.registerSet = RegisterSet.deserialize((Document) obj.get("registerSet"));
for (Object serialisedHw : hardwareList) {
CpuHardware hardware = CpuHardware.deserialize((DBObject) serialisedHw);
hardware.setCpu(cpu);
cpu.attachHardware(hardware, (int) ((BasicDBObject) serialisedHw).get("address"));
}
cpu.memory = Memory.deserialize((DBObject) obj.get("memory"));
cpu.registerSet = RegisterSet.deserialize((DBObject) obj.get("registerSet"));
return cpu; return cpu;
@ -421,14 +406,6 @@ public class CPU implements MongoSerializable {
return memory; return memory;
} }
public void setMemory(Memory memory) {
this.memory = memory;
}
public void setRegisterSet(RegisterSet registerSet) {
this.registerSet = registerSet;
}
public Status getStatus() { public Status getStatus() {
return status; return status;
} }
@ -441,29 +418,56 @@ public class CPU implements MongoSerializable {
this.ip = ip; this.ip = ip;
} }
public int getCodeSectionOffset() {
return codeSectionOffset;
}
public void setCodeSectionOffset(int codeSectionOffset) { public void setCodeSectionOffset(int codeSectionOffset) {
this.codeSectionOffset = codeSectionOffset; this.codeSectionOffset = codeSectionOffset;
} }
public void attachHardware(CpuHardware hardware, int address) {
attachedHardware.put(address, hardware);
}
public void detachHardware(int address) {
attachedHardware.remove(address);
}
public boolean hardwareInterrupt(int address) {
CpuHardware hardware = attachedHardware.get(address);
if (hardware != null) {
hardware.handleInterrupt(status);
return true;
} else {
return false;
}
}
public void hardwareQuery(int address) {
CpuHardware hardware = attachedHardware.get(address);
if (hardware != null) {
registerSet.getRegister("B").setValue(hardware.getId());
} else {
registerSet.getRegister("B").setValue(0);
}
}
@Override @Override
public String toString() { public String toString() {
String str = registerSet.toString(); String str = "------------------------\n";
str += registerSet.toString();
str += status.toString(); str += status.toString();
str += "------------------------\n";
return str; return str;
} }
public HardwareHost getHardwareHost() { public CpuHardware getHardware(int address) {
return hardwareHost;
return attachedHardware.get(address);
} }
public void setHardwareHost(HardwareHost hardwareHost) {
this.hardwareHost = hardwareHost;
}
} }

View File

@ -0,0 +1,45 @@
package net.simon987.server.assembly;
import com.mongodb.DBObject;
import net.simon987.server.GameServer;
import net.simon987.server.io.CpuHardwareDeserializer;
import net.simon987.server.io.MongoSerialisable;
import net.simon987.server.plugin.ServerPlugin;
public abstract class CpuHardware implements MongoSerialisable {
CPU cpu;
/**
* Handle an HWI instruction
*/
public abstract void handleInterrupt(Status status);
public CPU getCpu() {
return cpu;
}
public void setCpu(CPU cpu) {
this.cpu = cpu;
}
public abstract char getId();
public static CpuHardware deserialize(DBObject obj) {
for (ServerPlugin plugin : GameServer.INSTANCE.getPluginManager().getPlugins()) {
if (plugin instanceof CpuHardwareDeserializer) {
CpuHardware hw = ((CpuHardwareDeserializer) plugin).deserializeHardware(obj);
if (hw != null) {
return hw;
}
}
}
return null;
}
}

View File

@ -1,9 +1,9 @@
package net.simon987.server.assembly; package net.simon987.server.assembly;
import net.simon987.server.assembly.instruction.*; import net.simon987.server.assembly.instruction.*;
import net.simon987.server.logging.LogManager;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
/** /**
* Default instruction set for the CPU * Default instruction set for the CPU
@ -13,12 +13,7 @@ public class DefaultInstructionSet implements InstructionSet {
/** /**
* Map of instructions, stored in opcode : Instruction format * Map of instructions, stored in opcode : Instruction format
*/ */
private Map<Integer, Instruction> instructionMap = new HashMap<>(32); private HashMap<Integer, Instruction> instructionMap = new HashMap<>(32);
/**
* Map of aliasses, stored in mnemonic : Instruction format
*/
private Map<String, Instruction> aliasesMap = new HashMap<>(16);
private Instruction defaultInstruction; private Instruction defaultInstruction;
@ -37,6 +32,7 @@ public class DefaultInstructionSet implements InstructionSet {
add(new AndInstruction()); add(new AndInstruction());
add(new OrInstruction()); add(new OrInstruction());
add(new ShlInstruction()); add(new ShlInstruction());
add(new SalInstruction()); //Alias is added
add(new ShrInstruction()); add(new ShrInstruction());
add(new XorInstruction()); add(new XorInstruction());
add(new TestInstruction()); add(new TestInstruction());
@ -50,40 +46,6 @@ public class DefaultInstructionSet implements InstructionSet {
add(new SarInstruction()); add(new SarInstruction());
add(new IncInstruction()); add(new IncInstruction());
add(new DecInstruction()); add(new DecInstruction());
// 'abstract' instruction
add(new SetccInstruction());
// aliases
add(new SalInstruction());
// Setcc family
add(new SetaeInstruction());
add(new SetaInstruction());
add(new SetbeInstruction());
add(new SetbInstruction());
add(new SetcInstruction());
add(new SeteInstruction());
add(new SetgeInstruction());
add(new SetgInstruction());
add(new SetleInstruction());
add(new SetlInstruction());
add(new SetnaeInstruction());
add(new SetnaInstruction());
add(new SetnbeInstruction());
add(new SetnbInstruction());
add(new SetncInstruction());
add(new SetneInstruction());
add(new SetngeInstruction());
add(new SetngInstruction());
add(new SetnleInstruction());
add(new SetnlInstruction());
add(new SetnoInstruction());
add(new SetnsInstruction());
add(new SetnzInstruction());
add(new SetoInstruction());
add(new SetsInstruction());
add(new SetzInstruction());
} }
/** /**
@ -104,24 +66,7 @@ public class DefaultInstructionSet implements InstructionSet {
return defaultInstruction; return defaultInstruction;
} }
} }
/**
* Get an instruction from its mnemonic
*/
@Override
public Instruction get(String mnemonic) {
for (Instruction ins : instructionMap.values()) {
if (ins.getMnemonic().equalsIgnoreCase(mnemonic)) {
return ins;
}
}
Instruction aliasedInstruction = aliasesMap.get(mnemonic.toLowerCase());
if (aliasedInstruction != null) {
return aliasedInstruction;
}
return null;
}
/** /**
* Add a new instruction to the instructionSet * Add a new instruction to the instructionSet
* *
@ -132,13 +77,27 @@ public class DefaultInstructionSet implements InstructionSet {
instructionMap.put(opcode, instruction); instructionMap.put(opcode, instruction);
} }
@Override
public Instruction get(String mnemonic) {
for (Instruction ins : instructionMap.values()) {
if (ins.getMnemonic().equalsIgnoreCase(mnemonic)) {
return ins;
}
}
return null;
}
@Override @Override
public void add(Instruction instruction) { public void add(Instruction instruction) {
Instruction aliasedInstruction = instructionMap.get(instruction.getOpCode()); if (instructionMap.containsKey(instruction.getOpCode())) {
if (aliasedInstruction != null) { LogManager.LOGGER.fine(instruction.getMnemonic() + " instruction is an alias for " +
aliasesMap.put(instruction.getMnemonic(), instruction); instructionMap.get(instruction.getOpCode()).getMnemonic());
} else { } else {
instructionMap.put(instruction.getOpCode(), instruction); instructionMap.put(instruction.getOpCode(), instruction);
} }
} }
} }

View File

@ -1,70 +0,0 @@
package net.simon987.server.assembly;
import net.simon987.server.game.objects.ControllableUnit;
import net.simon987.server.io.JSONSerializable;
import net.simon987.server.io.MongoSerializable;
import org.bson.Document;
import org.json.simple.JSONObject;
public abstract class HardwareModule implements MongoSerializable, JSONSerializable {
private CPU cpu;
protected ControllableUnit unit;
public HardwareModule() {
}
public HardwareModule(Document document, ControllableUnit unit) {
this.unit = unit;
}
/**
* Handle an HWI instruction
*/
public abstract void handleInterrupt(Status status);
protected CPU getCpu() {
return cpu;
}
public void setCpu(CPU cpu) {
this.cpu = cpu;
}
public abstract char getId();
@Override
public String toString() {
JSONObject hwJson = jsonSerialise();
return String.format("{%s: {%s}}", getClass().getSimpleName(), hwJson == null ? "" : hwJson);
}
public void reset() {
}
public void update() {
}
@Override
public JSONObject jsonSerialise() {
return null;
}
@Override
public JSONObject debugJsonSerialise() {
return null;
}
@Override
public Document mongoSerialise() {
Document document = new Document();
document.put("type", getClass().getCanonicalName());
return document;
}
}

View File

@ -1,6 +1,6 @@
package net.simon987.server.assembly; package net.simon987.server.assembly;
import net.simon987.server.assembly.exception.AssemblyException;
import net.simon987.server.assembly.exception.IllegalOperandException; import net.simon987.server.assembly.exception.IllegalOperandException;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@ -102,7 +102,7 @@ public abstract class Instruction {
* @param o2 Source operand * @param o2 Source operand
* @return true if valid * @return true if valid
*/ */
public boolean operandsValid(Operand o1, Operand o2) { private static boolean operandsValid(Operand o1, Operand o2) throws IllegalOperandException {
return o1.getType() != OperandType.IMMEDIATE16; return o1.getType() != OperandType.IMMEDIATE16;
} }
@ -112,7 +112,7 @@ public abstract class Instruction {
* @param o1 source operand * @param o1 source operand
* @return true if the specified operand can be used with this instruction * @return true if the specified operand can be used with this instruction
*/ */
public boolean operandValid(Operand o1) { private static boolean operandValid(Operand o1) {
return true; return true;
} }
@ -124,7 +124,7 @@ public abstract class Instruction {
return false; return false;
} }
public String getMnemonic() { String getMnemonic() {
return mnemonic; return mnemonic;
} }
@ -133,7 +133,7 @@ public abstract class Instruction {
* *
* @param out encoded bytes will be written here * @param out encoded bytes will be written here
*/ */
public void encode(ByteArrayOutputStream out, int currentLine) throws AssemblyException { void encode(ByteArrayOutputStream out, int currentLine) throws IllegalOperandException {
if (!noOperandsValid()) { if (!noOperandsValid()) {
throw new IllegalOperandException("This instruction must have operand(s)!", currentLine); throw new IllegalOperandException("This instruction must have operand(s)!", currentLine);
@ -147,8 +147,8 @@ public abstract class Instruction {
} }
} }
public void encode(ByteArrayOutputStream out, Operand o1, Operand o2, int currentLine) void encode(ByteArrayOutputStream out, Operand o1, Operand o2, int currentLine)
throws AssemblyException { throws IllegalOperandException {
MachineCode code = new MachineCode(); MachineCode code = new MachineCode();
code.writeOpcode(opCode); code.writeOpcode(opCode);
@ -182,8 +182,8 @@ public abstract class Instruction {
} }
} }
public void encode(ByteArrayOutputStream out, Operand o1, int currentLine) void encode(ByteArrayOutputStream out, Operand o1, int currentLine)
throws AssemblyException { throws IllegalOperandException {
MachineCode code = new MachineCode(); MachineCode code = new MachineCode();
code.writeOpcode(opCode); code.writeOpcode(opCode);
@ -203,12 +203,14 @@ public abstract class Instruction {
//Destination bits are left blank //Destination bits are left blank
//System.out.println("o1: " + o1.getType());
for (byte b : code.bytes()) { for (byte b : code.bytes()) {
out.write(b); out.write(b);
} }
} }
public int getOpCode() { int getOpCode() {
return opCode; return opCode;
} }
} }

View File

@ -10,7 +10,7 @@ import java.util.ArrayList;
* Represents an encoded instruction. this class is used to easily * Represents an encoded instruction. this class is used to easily
* write to an 16bit value. * write to an 16bit value.
*/ */
public class MachineCode { class MachineCode {
/** /**
* Value of the initial 2-byte instruction * Value of the initial 2-byte instruction
@ -27,7 +27,7 @@ public class MachineCode {
* *
* @param opCode signed 6-bit integer (value 0-63) * @param opCode signed 6-bit integer (value 0-63)
*/ */
public void writeOpcode(int opCode) { void writeOpcode(int opCode) {
if (opCode < 0 || opCode > 63) { if (opCode < 0 || opCode > 63) {
LogManager.LOGGER.severe("Couldn't write the opCode for instruction :" + opCode); LogManager.LOGGER.severe("Couldn't write the opCode for instruction :" + opCode);
@ -44,7 +44,7 @@ public class MachineCode {
* *
* @param src signed 5-bit integer (value 0-31) * @param src signed 5-bit integer (value 0-31)
*/ */
public void writeSourceOperand(int src) { void writeSourceOperand(int src) {
if (src < 0 || src > 31) { if (src < 0 || src > 31) {
LogManager.LOGGER.severe("Couldn't write the scr operand for instruction :" + src); LogManager.LOGGER.severe("Couldn't write the scr operand for instruction :" + src);
@ -62,7 +62,7 @@ public class MachineCode {
* *
* @param dst signed 5-bit integer (value 0-31) * @param dst signed 5-bit integer (value 0-31)
*/ */
public void writeDestinationOperand(int dst) { void writeDestinationOperand(int dst) {
if (dst < 0 || dst > 31) { if (dst < 0 || dst > 31) {
LogManager.LOGGER.severe("Couldn't write the dst operand for instruction :" + dst); LogManager.LOGGER.severe("Couldn't write the dst operand for instruction :" + dst);
} else { } else {
@ -73,20 +73,22 @@ public class MachineCode {
} }
} }
public void appendWord(char word) { void appendWord(char word) {
additionalWords.add(word); additionalWords.add(word);
} }
/** /**
* Get the bytes of the code * Get the bytes of the code
*/ */
public byte[] bytes() { byte[] bytes() {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(value >> 8); out.write(value >> 8);
out.write(value); out.write(value);
for (Character s : additionalWords) { for (Character s : additionalWords) {
out.write(s >> 8); out.write(s >> 8);
out.write(s); out.write(s);

View File

@ -1,10 +1,11 @@
package net.simon987.server.assembly; package net.simon987.server.assembly;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import net.simon987.server.GameServer; import net.simon987.server.GameServer;
import net.simon987.server.io.MongoSerializable; import net.simon987.server.io.MongoSerialisable;
import net.simon987.server.logging.LogManager; import net.simon987.server.logging.LogManager;
import org.bson.Document;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
@ -12,7 +13,6 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.Arrays; import java.util.Arrays;
import java.util.Base64; import java.util.Base64;
import java.util.Random;
import java.util.zip.Deflater; import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream; import java.util.zip.DeflaterOutputStream;
import java.util.zip.Inflater; import java.util.zip.Inflater;
@ -21,7 +21,7 @@ import java.util.zip.InflaterOutputStream;
/** /**
* Represents the available memory for a CPU in the game universe * Represents the available memory for a CPU in the game universe
*/ */
public class Memory implements Target, MongoSerializable { public class Memory implements Target, MongoSerialisable {
/** /**
@ -38,31 +38,6 @@ public class Memory implements Target, MongoSerializable {
words = new char[size]; words = new char[size];
} }
public Memory(Document document) {
String zipBytesStr = (String) document.get("zipBytes");
if (zipBytesStr != null) {
byte[] compressedBytes = Base64.getDecoder().decode((String) document.get("zipBytes"));
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Inflater decompressor = new Inflater(true);
InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(baos, decompressor);
inflaterOutputStream.write(compressedBytes);
inflaterOutputStream.close();
setBytes(baos.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
} else {
LogManager.LOGGER.severe("Memory was manually deleted");
words = new char[GameServer.INSTANCE.getConfig().getInt("memory_size")];
}
}
/** /**
* Get the value at an address * Get the value at an address
* *
@ -112,32 +87,6 @@ public class Memory implements Target, MongoSerializable {
words[address] = (char) value; words[address] = (char) value;
} }
/**
* Configurably corrupt memory
*
* @param blockSize Block size (in words) in which to randomly flip one bit
*/
public void corrupt(int blockSize) {
Random rand = new Random();
// Increment offset by blockSize
for (int offset = 0; offset < words.length; offset += blockSize) {
// Calculate address to corrupt by adding a random value between 0 to (blocksize-1) to offset
int address = rand.nextInt(blockSize) + offset;
// Checking here avoids having a protected area at the end of the address space
if(address < words.length) {
// Calculate bitmask by left-shifting 1 by a random value between 0 and 15
int bitmask = 1 << rand.nextInt(16);
// Flip the bit with XOR
words[address] ^= bitmask;
}
}
}
/** /**
* Fill the memory with 0s * Fill the memory with 0s
*/ */
@ -157,9 +106,9 @@ public class Memory implements Target, MongoSerializable {
} }
@Override @Override
public Document mongoSerialise() { public BasicDBObject mongoSerialise() {
Document dbObject = new Document(); BasicDBObject dbObject = new BasicDBObject();
try { try {
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
@ -178,6 +127,35 @@ public class Memory implements Target, MongoSerializable {
return dbObject; return dbObject;
} }
public static Memory deserialize(DBObject obj) {
Memory memory = new Memory(0);
String zipBytesStr = (String) obj.get("zipBytes");
if (zipBytesStr != null) {
byte[] compressedBytes = Base64.getDecoder().decode((String) obj.get("zipBytes"));
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Inflater decompressor = new Inflater(true);
InflaterOutputStream inflaterOutputStream = new InflaterOutputStream(baos, decompressor);
inflaterOutputStream.write(compressedBytes);
inflaterOutputStream.close();
memory.setBytes(baos.toByteArray());
} catch (IOException e) {
e.printStackTrace();
}
} else {
LogManager.LOGGER.severe("Memory was manually deleted");
memory = new Memory(GameServer.INSTANCE.getConfig().getInt("memory_size"));
}
return memory;
}
public void setBytes(byte[] bytes) { public void setBytes(byte[] bytes) {
this.words = new char[bytes.length / 2]; this.words = new char[bytes.length / 2];
ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).asCharBuffer().get(this.words); ByteBuffer.wrap(bytes).order(ByteOrder.BIG_ENDIAN).asCharBuffer().get(this.words);

Some files were not shown because too many files have changed in this diff Show More