From 94b8ef53958559a8a725f297cffae53446a9e093 Mon Sep 17 00:00:00 2001 From: simon Date: Fri, 21 Dec 2018 14:00:18 -0500 Subject: [PATCH] NPC Plugin rewrite. Plugin-level data can be stored in DB --- .../simon987/cubotplugin/CubotComPort.java | 2 +- .../java/net/simon987/npcplugin/Factory.java | 87 ++----- .../net/simon987/npcplugin/HarvesterNPC.java | 6 +- .../npcplugin/NonPlayerCharacter.java | 32 ++- .../net/simon987/npcplugin/NpcPlugin.java | 37 ++- .../npcplugin/RadioReceiverHardware.java | 13 +- .../net/simon987/npcplugin/RadioTower.java | 1 - .../net/simon987/npcplugin/Settlement.java | 219 ++++++++++++++++++ .../simon987/npcplugin/VaultDimension.java | 2 +- .../event/WorldCreationListener.java | 113 +-------- .../simon987/biomassplugin/WorldUtils.java | 3 - .../event/ObjectDeathListener.java | 3 - .../java/net/simon987/server/GameServer.java | 17 +- .../simon987/server/game/GameUniverse.java | 8 + .../server/game/objects/GameObject.java | 16 +- .../simon987/server/game/world/TileMap.java | 13 +- .../net/simon987/server/game/world/World.java | 18 +- .../game/world/WorldGenerationException.java | 7 + .../server/game/world/WorldGenerator.java | 10 +- .../simon987/server/plugin/PluginManager.java | 4 + .../simon987/server/plugin/ServerPlugin.java | 23 +- Server/src/main/resources/config.properties | 2 +- 22 files changed, 391 insertions(+), 245 deletions(-) create mode 100644 Plugin NPC/src/main/java/net/simon987/npcplugin/Settlement.java create mode 100644 Server/src/main/java/net/simon987/server/game/world/WorldGenerationException.java diff --git a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotComPort.java b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotComPort.java index a281dd7..c0eb4e6 100644 --- a/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotComPort.java +++ b/Plugin Cubot/src/main/java/net/simon987/cubotplugin/CubotComPort.java @@ -67,7 +67,7 @@ public class CubotComPort extends CubotHardwareModule { } else if (a == COMPORT_FRONT_PORT_OUT) { - if (cubot.spendEnergy(20)) { + if (cubot.spendEnergy(5)) { //Get object directly in front of the Cubot Point frontTile = cubot.getFrontTile(); //Todo will have to add getGameObjectsBlockingAt to enable Factory diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java index 5827ccc..0d82c14 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/Factory.java @@ -7,8 +7,6 @@ import org.bson.Document; import org.bson.types.ObjectId; import java.awt.*; -import java.util.ArrayList; -import java.util.List; /** * Game objects that regularly creates NonPlayerCharacters @@ -27,35 +25,17 @@ public class Factory extends Structure implements Updatable { */ private static final int NPC_CREATION_COOLDOWN = NonPlayerCharacter.LIFETIME / MAX_NPC_COUNT; - /** - * List of associated NonPlayerCharacters - */ - private ArrayList npcs = new ArrayList<>(); - /** * Number of ticks to wait until the Factory can spawn a new NPC */ private int cooldown = 0; - /** - * 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]; - - /** - * Factory are uninitialised until the first update() call - */ - private boolean initialised = false; - public Factory() { super(2, 2); } public Factory(Document document) { super(document, 2, 2); - - tmpNpcArray = ((ArrayList) document.get("npcs")).toArray(); } @Override @@ -70,66 +50,29 @@ public class Factory extends Structure implements Updatable { @Override public void update() { - if (!initialised) { + Settlement settlement = NpcPlugin.settlementMap.get(getWorld().getId()); - initialised = true; + if (cooldown == 0) { + if (settlement.getNpcs().size() < MAX_NPC_COUNT) { + Point p = getAdjacentTile(); - for (Object id : tmpNpcArray) { + if (p != null) { + NonPlayerCharacter npc = new HarvesterNPC(); + npc.setWorld(getWorld()); + npc.setObjectId(new ObjectId()); + npc.setX(p.x); + npc.setY(p.y); + getWorld().addObject(npc); + getWorld().incUpdatable(); - NonPlayerCharacter npc = (NonPlayerCharacter) GameServer.INSTANCE.getGameUniverse().getObject((ObjectId) id); - - if (npc != null) { - npc.setFactory(this); - npcs.add(npc); + settlement.addNpc(npc); } } - tmpNpcArray = null; + cooldown += NPC_CREATION_COOLDOWN; } else { - - if (cooldown == 0) { - if (npcs.size() < MAX_NPC_COUNT) { - Point p = getAdjacentTile(); - - if (p != null) { - NonPlayerCharacter npc = new HarvesterNPC(); - npc.setWorld(getWorld()); - npc.setObjectId(new ObjectId()); - 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--; - } + cooldown--; } } - - @Override - public Document mongoSerialise() { - Document dbObject = super.mongoSerialise(); - - List tmpNpcArray = new ArrayList<>(npcs.size()); - - for (NonPlayerCharacter npc : npcs) { - tmpNpcArray.add(npc.getObjectId()); - } - - dbObject.put("npcs", tmpNpcArray); - - return dbObject; - } - - ArrayList getNpcs() { - return npcs; - } } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvesterNPC.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvesterNPC.java index f15219f..d204f76 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvesterNPC.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/HarvesterNPC.java @@ -33,7 +33,7 @@ public class HarvesterNPC extends NonPlayerCharacter { super.update(); - if (getFactory() != null) { + if (getSettlement() != null) { if (getTask().checkCompleted()) { setTask(new HarvestTask()); @@ -54,8 +54,8 @@ public class HarvesterNPC extends NonPlayerCharacter { 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)); diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/NonPlayerCharacter.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/NonPlayerCharacter.java index a11c7d6..8ad7c65 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/NonPlayerCharacter.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/NonPlayerCharacter.java @@ -41,10 +41,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable */ private Action lastAction = Action.IDLE; - /** - * Factory that created this NPC - */ - private Factory factory; + private Settlement settlement; /** * If set to true, the NPC will be destroyed next tick if it is @@ -93,8 +90,8 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable age++; - //Destroy NPCs that are not linked with a Factory - if (factory == null) { + //Destroy NPCs that are not linked with a Settlement + if (settlement == null) { if (selfDestroyNextTick) { setDead(true); } @@ -147,7 +144,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable if (direction == Direction.NORTH) { - if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(), + if (Util.manhattanDist(settlement.getWorld().getX(), settlement.getWorld().getY(), getWorld().getX(), getWorld().getY() - 1) <= MAX_FACTORY_DISTANCE) { if (!moveTo(8, 0, 0)) { setDirection(Direction.NORTH); @@ -159,7 +156,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable } } else if (direction == Direction.EAST) { - if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(), + if (Util.manhattanDist(settlement.getWorld().getX(), settlement.getWorld().getY(), getWorld().getX() + 1, getWorld().getY()) <= MAX_FACTORY_DISTANCE) { if (!moveTo(15, 7, 0)) { setDirection(Direction.EAST); @@ -170,7 +167,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable return false; } } else if (direction == Direction.SOUTH) { - if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(), + if (Util.manhattanDist(settlement.getWorld().getX(), settlement.getWorld().getY(), getWorld().getX(), getWorld().getY() + 1) <= MAX_FACTORY_DISTANCE) { if (!moveTo(8, 15, 0)) { setDirection(Direction.SOUTH); @@ -181,7 +178,7 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable return false; } } else if (direction == Direction.WEST) { - if (Util.manhattanDist(factory.getWorld().getX(), factory.getWorld().getY(), + if (Util.manhattanDist(settlement.getWorld().getX(), settlement.getWorld().getY(), getWorld().getX() - 1, getWorld().getY()) <= MAX_FACTORY_DISTANCE) { if (!moveTo(0, 7, 0)) { setDirection(Direction.WEST); @@ -254,16 +251,15 @@ public abstract class NonPlayerCharacter extends GameObject implements Updatable return lastAction; } - public Factory getFactory() { - return factory; - } - - public void setFactory(Factory factory) { - this.factory = factory; - } - public int getAge() { return age; } + public Settlement getSettlement() { + return settlement; + } + + public void setSettlement(Settlement settlement) { + this.settlement = settlement; + } } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java index c884d2d..8d6f3ad 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/NpcPlugin.java @@ -11,15 +11,14 @@ import net.simon987.server.ServerConfiguration; import net.simon987.server.game.objects.GameRegistry; import net.simon987.server.logging.LogManager; import net.simon987.server.plugin.ServerPlugin; +import org.bson.Document; -import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; public class NpcPlugin extends ServerPlugin { - /** - * Radio tower cache - */ - private static ArrayList radioTowers; + public static Map settlementMap; @Override public void init(GameServer gameServer) { @@ -46,13 +45,35 @@ public class NpcPlugin extends ServerPlugin { registry.registerTile(TileVaultFloor.ID, TileVaultFloor.class); registry.registerTile(TileVaultWall.ID, TileVaultWall.class); - radioTowers = new ArrayList<>(32); + settlementMap = new ConcurrentHashMap<>(); LogManager.LOGGER.info("(NPC Plugin) Initialised NPC plugin"); } - public static ArrayList getRadioTowers() { - return radioTowers; + @Override + public Document mongoSerialise() { + Document document = super.mongoSerialise(); + + Document settlements = new Document(); + for (String world : settlementMap.keySet()) { + settlements.put(world, settlementMap.get(world).mongoSerialise()); + } + + document.put("settlement_map", settlements); + + return document; } + @Override + public void load(Document document) { + super.load(document); + + Document settlements = (Document) document.get("settlement_map"); + + for (String world : settlements.keySet()) { + settlementMap.put(world, new Settlement((Document) settlements.get(world))); + } + + LogManager.LOGGER.fine(String.format("(%s) Loaded %d settlements", name, settlementMap.size())); + } } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioReceiverHardware.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioReceiverHardware.java index cb0ab04..415d248 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioReceiverHardware.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioReceiverHardware.java @@ -8,6 +8,7 @@ import net.simon987.server.game.objects.ControllableUnit; import org.bson.Document; import java.util.ArrayList; +import java.util.List; public class RadioReceiverHardware extends HardwareModule { @@ -39,12 +40,14 @@ public class RadioReceiverHardware extends HardwareModule { //Find the nearest Radio Tower and query it cubot.setAction(Action.LISTENING); - ArrayList messages = new ArrayList<>(6); + List messages = new ArrayList<>(6); - ArrayList towers = new ArrayList<>(NpcPlugin.getRadioTowers()); //Avoid ConcurrentModificationException - for (RadioTower tower : towers) { - if (Util.manhattanDist(tower.getWorld().getX(), tower.getWorld().getY(), cubot.getWorld().getX(), - cubot.getWorld().getY()) <= RadioTower.MAX_RANGE) { + for (String world : NpcPlugin.settlementMap.keySet()) { + RadioTower tower = NpcPlugin.settlementMap.get(world).getRadioTower(); + + if (tower != null && Util.manhattanDist( + tower.getWorld().getX(), tower.getWorld().getY(), + cubot.getWorld().getX(), cubot.getWorld().getY()) <= RadioTower.MAX_RANGE) { //Tower is in range messages.addAll(tower.getMessages()); } diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioTower.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioTower.java index e867774..0a66b7a 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioTower.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/RadioTower.java @@ -24,7 +24,6 @@ public class RadioTower extends Structure implements MessageReceiver, Updatable public RadioTower(Document document) { super(document, 1, 1); - NpcPlugin.getRadioTowers().add(this); } @Override diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/Settlement.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/Settlement.java new file mode 100644 index 0000000..eb9ea9f --- /dev/null +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/Settlement.java @@ -0,0 +1,219 @@ +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 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 { + + System.out.println("SETTLING"); + + this.world = world; + this.difficultyLevel = DifficultyLevel.NORMAL; //TODO randomize ? + + 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(0); //todo cypherId ? + 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", "1234567"); //todo + + + List 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 getNpcs() { + return npcs; + } +} diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/VaultDimension.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/VaultDimension.java index a4c1f48..87a3cc1 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/VaultDimension.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/VaultDimension.java @@ -29,7 +29,7 @@ public class VaultDimension { public VaultDimension(VaultDoor vaultDoor) { - name = "v" + vaultDoor.getObjectId() + "-"; + name = "v" + vaultDoor.getObjectId(); /* * Creates a group of vault worlds and pieces them together with openings. diff --git a/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java index 8821bd4..609e727 100644 --- a/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java +++ b/Plugin NPC/src/main/java/net/simon987/npcplugin/event/WorldCreationListener.java @@ -1,17 +1,14 @@ package net.simon987.npcplugin.event; -import net.simon987.npcplugin.Factory; import net.simon987.npcplugin.NpcPlugin; -import net.simon987.npcplugin.RadioTower; -import net.simon987.npcplugin.VaultDoor; +import net.simon987.npcplugin.Settlement; import net.simon987.server.event.GameEvent; import net.simon987.server.event.GameEventListener; import net.simon987.server.event.WorldGenerationEvent; -import net.simon987.server.game.world.TilePlain; import net.simon987.server.game.world.World; -import org.bson.types.ObjectId; +import net.simon987.server.game.world.WorldGenerationException; +import net.simon987.server.logging.LogManager; -import java.awt.*; import java.util.Random; public class WorldCreationListener implements GameEventListener { @@ -37,105 +34,13 @@ public class WorldCreationListener implements GameEventListener { if (random.nextInt(FACTORY_SPAWN_RATE) == 0) { - World world = ((WorldGenerationEvent) event).getWorld(); + World world = (World) event.getSource(); - 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(); - -// 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.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. - - NpcPlugin.getRadioTowers().add(radioTower); - -// LogManager.LOGGER.info("Spawned RadioTower at (" + world.getX() + ", " + world.getY() + -// ") (" + p.x + ", " + p.y + ")"); - } - } - - //Also spawn a Vault in the same World - p = world.getRandomPassableTile(); - if (p != null) { - - VaultDoor vaultDoor = new VaultDoor(0); //todo cypherId ? - 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(); - -// LogManager.LOGGER.info("Spawned Vault Door at (" + world.getX() + ", " + world.getY() + -// ") (" + p.x + ", " + p.y + ")"); + try { + NpcPlugin.settlementMap.put(world.getId(), new Settlement(world)); + } catch (WorldGenerationException e) { + LogManager.LOGGER.fine(String.format("Exception during settlement generation: %s.", + e.getMessage())); } } } diff --git a/Plugin Plant/src/main/java/net/simon987/biomassplugin/WorldUtils.java b/Plugin Plant/src/main/java/net/simon987/biomassplugin/WorldUtils.java index 8d05d74..61dda4b 100644 --- a/Plugin Plant/src/main/java/net/simon987/biomassplugin/WorldUtils.java +++ b/Plugin Plant/src/main/java/net/simon987/biomassplugin/WorldUtils.java @@ -73,9 +73,6 @@ public class WorldUtils { } } -// LogManager.LOGGER.info("Generated " + biomassBlobs.size() + " biomassBlobs for World (" + world.getX() + ',' + -// world.getY() + ')'); - return biomassBlobs; } } diff --git a/Plugin Plant/src/main/java/net/simon987/biomassplugin/event/ObjectDeathListener.java b/Plugin Plant/src/main/java/net/simon987/biomassplugin/event/ObjectDeathListener.java index 3a5deed..023c66f 100644 --- a/Plugin Plant/src/main/java/net/simon987/biomassplugin/event/ObjectDeathListener.java +++ b/Plugin Plant/src/main/java/net/simon987/biomassplugin/event/ObjectDeathListener.java @@ -7,7 +7,6 @@ import net.simon987.server.event.GameEventListener; import net.simon987.server.event.ObjectDeathEvent; import net.simon987.server.game.objects.GameObject; import net.simon987.server.game.world.World; -import net.simon987.server.logging.LogManager; import org.bson.types.ObjectId; /** @@ -41,8 +40,6 @@ public class ObjectDeathListener implements GameEventListener { dyingHarvesterNPC.getX(), dyingHarvesterNPC.getY(), dyingHarvesterNPC.getWorld()); //Add it to the world game objects dyingHarvesterNPC.getWorld().addObject(newBiomassBlob); - LogManager.LOGGER.fine("Spawned biomass at (" + newBiomassBlob.getX() + - ", " + newBiomassBlob.getY() + ")"); } } } diff --git a/Server/src/main/java/net/simon987/server/GameServer.java b/Server/src/main/java/net/simon987/server/GameServer.java index 0996dde..4777c42 100644 --- a/Server/src/main/java/net/simon987/server/GameServer.java +++ b/Server/src/main/java/net/simon987/server/GameServer.java @@ -18,6 +18,7 @@ import net.simon987.server.game.objects.GameRegistry; import net.simon987.server.game.world.*; import net.simon987.server.logging.LogManager; import net.simon987.server.plugin.PluginManager; +import net.simon987.server.plugin.ServerPlugin; import net.simon987.server.user.User; import net.simon987.server.user.UserManager; import net.simon987.server.user.UserStatsHelper; @@ -221,11 +222,18 @@ public class GameServer implements Runnable { universe.addUser(user); } - //Load misc server info + //Load server & plugin data cursor = server.find().iterator(); if (cursor.hasNext()) { Document serverObj = cursor.next(); gameUniverse.setTime((long) serverObj.get("time")); + + 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() + @@ -267,6 +275,13 @@ public class GameServer implements Runnable { Document serverObj = new Document(); serverObj.put("time", gameUniverse.getTime()); + + 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. server.replaceOne(new Document("_id", "serverinfo"), serverObj, updateOptions); diff --git a/Server/src/main/java/net/simon987/server/game/GameUniverse.java b/Server/src/main/java/net/simon987/server/game/GameUniverse.java index 9248736..d3415a6 100644 --- a/Server/src/main/java/net/simon987/server/game/GameUniverse.java +++ b/Server/src/main/java/net/simon987/server/game/GameUniverse.java @@ -115,6 +115,14 @@ public class GameUniverse { } } + public World getWorld(String id, boolean createNew) { + + String[] tokens = id.split("-"); + + return getWorld(Integer.decode(tokens[1]), Integer.decode(tokens[2]), + createNew, tokens[0]); + } + public World getLoadedWorld(int x, int y, String dimension) { // Wrapping coordinates around cyclically x %= maxWidth; diff --git a/Server/src/main/java/net/simon987/server/game/objects/GameObject.java b/Server/src/main/java/net/simon987/server/game/objects/GameObject.java index 251e441..b463fa4 100755 --- a/Server/src/main/java/net/simon987/server/game/objects/GameObject.java +++ b/Server/src/main/java/net/simon987/server/game/objects/GameObject.java @@ -159,30 +159,30 @@ public abstract class GameObject implements JSONSerializable, MongoSerializable int count = 0; - if (!getWorld().isTileBlocked(getX() + 1, getY())) { + if (getWorld().getTileMap().isInBounds(x + 1, y) && !getWorld().isTileBlocked(x + 1, y)) { count++; } - if (!getWorld().isTileBlocked(getX(), getY() + 1)) { + if (getWorld().getTileMap().isInBounds(x, y + 1) && !getWorld().isTileBlocked(x, y + 1)) { count++; } - if (!getWorld().isTileBlocked(getX() - 1, getY())) { + if (getWorld().getTileMap().isInBounds(x - 1, y) && !getWorld().isTileBlocked(x - 1, y)) { count++; } - if (!getWorld().isTileBlocked(getX(), getY() - 1)) { + if (getWorld().getTileMap().isInBounds(x, y - 1) && !getWorld().isTileBlocked(x, y - 1)) { count++; } if (diagonals) { - if (!getWorld().isTileBlocked(getX() + 1, getY() + 1)) { + if (getWorld().getTileMap().isInBounds(x + 1, y + 1) && !getWorld().isTileBlocked(x + 1, y + 1)) { count++; } - if (!getWorld().isTileBlocked(getX() - 1, getY() + 1)) { + if (getWorld().getTileMap().isInBounds(x - 1, y + 1) && !getWorld().isTileBlocked(x - 1, y + 1)) { count++; } - if (!getWorld().isTileBlocked(getX() + 1, getY() - 1)) { + if (getWorld().getTileMap().isInBounds(x + 1, y - 1) && !getWorld().isTileBlocked(x + 1, y - 1)) { count++; } - if (!getWorld().isTileBlocked(getX() - 1, getY() - 1)) { + if (getWorld().getTileMap().isInBounds(x - 1, y - 1) && !getWorld().isTileBlocked(x - 1, y - 1)) { count++; } } diff --git a/Server/src/main/java/net/simon987/server/game/world/TileMap.java b/Server/src/main/java/net/simon987/server/game/world/TileMap.java index d64ba6f..5d91007 100755 --- a/Server/src/main/java/net/simon987/server/game/world/TileMap.java +++ b/Server/src/main/java/net/simon987/server/game/world/TileMap.java @@ -5,6 +5,7 @@ import net.simon987.server.GameServer; import net.simon987.server.game.objects.GameRegistry; import net.simon987.server.io.JSONSerializable; import net.simon987.server.io.MongoSerializable; +import net.simon987.server.logging.LogManager; import org.bson.Document; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -87,7 +88,8 @@ public class TileMap implements JSONSerializable, MongoSerializable { */ public int getTileIdAt(int x, int y) { try { - return tiles[x][y].getId(); + Tile tile = tiles[x][y]; + return tile == null ? -1 : tile.getId(); } catch (ArrayIndexOutOfBoundsException e) { return -1; } @@ -101,13 +103,18 @@ public class TileMap implements JSONSerializable, MongoSerializable { * @return the tile id at the specified position, null if out of bounds */ public Tile getTileAt(int x, int y) { - try { + if (isInBounds(x, y)) { return tiles[x][y]; - } catch (ArrayIndexOutOfBoundsException e) { + } else { + LogManager.LOGGER.warning(String.format("ArrayIndexOutOfBoundsException in TileMap::getTileAt(%d, %d)", x, y)); + Thread.dumpStack(); return null; } } + public boolean isInBounds(int x, int y) { + return x >= 0 && x < width && y >= 0 && y < width; + } public int getWidth() { diff --git a/Server/src/main/java/net/simon987/server/game/world/World.java b/Server/src/main/java/net/simon987/server/game/world/World.java index 3d74162..c3890da 100644 --- a/Server/src/main/java/net/simon987/server/game/world/World.java +++ b/Server/src/main/java/net/simon987/server/game/world/World.java @@ -76,7 +76,7 @@ public class World implements MongoSerializable { * @return long */ public static String idFromCoordinates(int x, int y, String dimension) { - return dimension + "0x" + Integer.toHexString(x) + "-" + "0x" + Integer.toHexString(y); + return dimension + "-0x" + Integer.toHexString(x) + "-" + "0x" + Integer.toHexString(y); } /** @@ -425,28 +425,28 @@ public class World implements MongoSerializable { if (rTile != null) { int adjacentTiles = 0; - if (!isTileBlocked(rTile.x, rTile.y - 1)) { + if (tileMap.isInBounds(rTile.x, rTile.y - 1) && !isTileBlocked(rTile.x, rTile.y - 1)) { adjacentTiles++; } - if (!isTileBlocked(rTile.x + 1, rTile.y)) { + if (tileMap.isInBounds(rTile.x + 1, rTile.y) && !isTileBlocked(rTile.x + 1, rTile.y)) { adjacentTiles++; } - if (!isTileBlocked(rTile.x, rTile.y + 1)) { + if (tileMap.isInBounds(rTile.x, rTile.y + 1) && !isTileBlocked(rTile.x, rTile.y + 1)) { adjacentTiles++; } - if (!isTileBlocked(rTile.x - 1, rTile.y)) { + if (tileMap.isInBounds(rTile.x - 1, rTile.y) && !isTileBlocked(rTile.x - 1, rTile.y)) { adjacentTiles++; } - if (!isTileBlocked(rTile.x + 1, rTile.y + 1)) { + if (tileMap.isInBounds(rTile.x + 1, rTile.y + 1) && !isTileBlocked(rTile.x + 1, rTile.y + 1)) { adjacentTiles++; } - if (!isTileBlocked(rTile.x - 1, rTile.y + 1)) { + if (tileMap.isInBounds(rTile.x - 1, rTile.y + 1) && !isTileBlocked(rTile.x - 1, rTile.y + 1)) { adjacentTiles++; } - if (!isTileBlocked(rTile.x + 1, rTile.y - 1)) { + if (tileMap.isInBounds(rTile.x + 1, rTile.y - 1) && !isTileBlocked(rTile.x + 1, rTile.y - 1)) { adjacentTiles++; } - if (!isTileBlocked(rTile.x - 1, rTile.y - 1)) { + if (tileMap.isInBounds(rTile.x - 1, rTile.y - 1) && !isTileBlocked(rTile.x - 1, rTile.y - 1)) { adjacentTiles++; } diff --git a/Server/src/main/java/net/simon987/server/game/world/WorldGenerationException.java b/Server/src/main/java/net/simon987/server/game/world/WorldGenerationException.java new file mode 100644 index 0000000..bac7ee8 --- /dev/null +++ b/Server/src/main/java/net/simon987/server/game/world/WorldGenerationException.java @@ -0,0 +1,7 @@ +package net.simon987.server.game.world; + +public class WorldGenerationException extends Exception { + public WorldGenerationException(String message) { + super(message); + } +} diff --git a/Server/src/main/java/net/simon987/server/game/world/WorldGenerator.java b/Server/src/main/java/net/simon987/server/game/world/WorldGenerator.java index dd51e53..1550e43 100755 --- a/Server/src/main/java/net/simon987/server/game/world/WorldGenerator.java +++ b/Server/src/main/java/net/simon987/server/game/world/WorldGenerator.java @@ -38,6 +38,8 @@ public class WorldGenerator { private int minCopperCount; private int maxCopperCount; + private String dimension; + private static final int DEFAULT_WORLD_SIZE = 16; /** @@ -48,6 +50,8 @@ public class WorldGenerator { public WorldGenerator(ServerConfiguration config) { + dimension = config.getString("new_user_dimension"); + centerPointCountMin = config.getInt("wg_centerPointCountMin"); centerPointCountMax = config.getInt("wg_centerPointCountMax"); wallPlainRatio = config.getInt("wg_wallPlainRatio"); @@ -88,9 +92,9 @@ public class WorldGenerator { /** * Generates an empty World */ - private static World generateEmptyWorld(int locX, int locY) { + private static World generateEmptyWorld(int locX, int locY, String dimension) { - return new World(locX, locY, new TileMap(DEFAULT_WORLD_SIZE, DEFAULT_WORLD_SIZE), "w-"); + return new World(locX, locY, new TileMap(DEFAULT_WORLD_SIZE, DEFAULT_WORLD_SIZE), dimension); } /** @@ -99,7 +103,7 @@ public class WorldGenerator { public World generateWorld(int locX, int locY) throws CancelledException { Random random = new Random(); - World world = generateEmptyWorld(locX, locY); + World world = generateEmptyWorld(locX, locY, dimension); centerPointsMap = new HashMap<>(16); diff --git a/Server/src/main/java/net/simon987/server/plugin/PluginManager.java b/Server/src/main/java/net/simon987/server/plugin/PluginManager.java index 2d51f8f..7a12e87 100644 --- a/Server/src/main/java/net/simon987/server/plugin/PluginManager.java +++ b/Server/src/main/java/net/simon987/server/plugin/PluginManager.java @@ -152,6 +152,10 @@ public class PluginManager { return null; } + public ServerPlugin getPluginByName(String name) { + return PluginManager.getPluginByName(name, loadedPlugins); + } + private boolean isLoaded(String name) { return getPluginByName(name, loadedPlugins) != null; } diff --git a/Server/src/main/java/net/simon987/server/plugin/ServerPlugin.java b/Server/src/main/java/net/simon987/server/plugin/ServerPlugin.java index 5b6af10..c6a2d8d 100644 --- a/Server/src/main/java/net/simon987/server/plugin/ServerPlugin.java +++ b/Server/src/main/java/net/simon987/server/plugin/ServerPlugin.java @@ -2,11 +2,14 @@ package net.simon987.server.plugin; import net.simon987.server.GameServer; import net.simon987.server.event.GameEventListener; +import net.simon987.server.io.MongoSerializable; +import net.simon987.server.logging.LogManager; +import org.bson.Document; import java.util.ArrayList; import java.util.List; -public abstract class ServerPlugin { +public abstract class ServerPlugin implements MongoSerializable { /** * Name of the plugin @@ -49,4 +52,22 @@ public abstract class ServerPlugin { public List getListeners() { return listeners; } + + @Override + public Document mongoSerialise() { + Document document = new Document(); + + document.put("version", version); + + return document; + } + + public void load(Document document) { + + LogManager.LOGGER.fine(String.format("(%s) Loading from database", name)); + if (!version.equals(document.getString("version"))) { + LogManager.LOGGER.warning(String.format("(%s) Version mismatch with database!" + + " This could cause problems. %s!=%s", name, version, document.getString("version"))); + } + } } diff --git a/Server/src/main/resources/config.properties b/Server/src/main/resources/config.properties index 5dd914a..1b44a00 100644 --- a/Server/src/main/resources/config.properties +++ b/Server/src/main/resources/config.properties @@ -48,7 +48,7 @@ user_timeout=100 #User creation new_user_worldX=32767 new_user_worldY=32767 -new_user_dimension=w- +new_user_dimension=w # Default user code new_user_code=; Welcome to Much Assembly required!\n\ ; You will find useful information on the game here: https://github.com/simon987/Much-Assembly-Required/wiki\n\