Added biomass respawn feature #22

This commit is contained in:
simon 2017-12-02 10:26:59 -05:00
parent 29cac77e79
commit be45979ed0
20 changed files with 309 additions and 189 deletions

View File

@ -2,6 +2,7 @@ package net.simon987.cubotplugin;
import net.simon987.cubotplugin.event.CpuInitialisationListener;
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;
@ -14,7 +15,7 @@ public class CubotPlugin extends ServerPlugin implements GameObjectDeserializer,
@Override
public void init() {
public void init(ServerConfiguration config) {
listeners.add(new CpuInitialisationListener());
listeners.add(new UserCreationListener());

View File

@ -1,6 +1,7 @@
package net.simon987.mischwplugin;
import net.simon987.mischwplugin.event.CpuInitialisationListener;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.assembly.CpuHardware;
import net.simon987.server.io.CpuHardwareDeserializer;
import net.simon987.server.logging.LogManager;
@ -11,7 +12,7 @@ public class MiscHWPlugin extends ServerPlugin implements CpuHardwareDeserialize
@Override
public void init() {
public void init(ServerConfiguration config) {
listeners.add(new CpuInitialisationListener());
LogManager.LOGGER.info("Initialised Misc Hardware Plugin");

View File

@ -1,6 +1,7 @@
package net.simon987.npcplugin;
import net.simon987.npcplugin.event.WorldUpdateListener;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.game.GameObject;
import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.logging.LogManager;
@ -11,7 +12,7 @@ public class NpcPlugin extends ServerPlugin implements GameObjectDeserializer {
@Override
public void init() {
public void init(ServerConfiguration configuration) {
listeners.add(new WorldUpdateListener());

View File

@ -35,19 +35,17 @@ public class WorldUpdateListener implements GameEventListener {
NonPlayerCharacter npc = new HarvesterNPC();
//todo set max iteration
Point p = null;
while (p == null) {
p = world.getRandomPassableTile();
Point p = world.getRandomPassableTile();
if (p != null) {
npc.setWorld(world);
npc.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
npc.setX(p.x);
npc.setY(p.y);
world.getGameObjects().add(npc);
}
npc.setWorld(world);
npc.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
npc.setX(p.x);
npc.setY(p.y);
world.getGameObjects().add(npc);
}

View File

@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>net.simon987.pluginplant</groupId>
<artifactId>plugin-plant</artifactId>
<artifactId>plugin-biomassBlob</artifactId>
<version>1.2a</version>
<build>
<plugins>

View File

@ -1,19 +1,20 @@
package net.simon987.plantplugin;
package net.simon987.biomassplugin;
import net.simon987.server.game.GameObject;
import net.simon987.server.game.InventoryHolder;
import org.json.simple.JSONObject;
public class Plant extends GameObject implements InventoryHolder {
public class BiomassBlob extends GameObject implements InventoryHolder {
private static final char MAP_INFO = 0x4000;
public static final int ID = 2;
/**
* Yield of the plant, in biomass units
* Yield of the blob, in biomass units
*/
private int biomassCount;
/**
* Style of the plant (Only visual)
* Style of the blob (Only visual)
*/
private int style;
@ -56,28 +57,28 @@ public class Plant extends GameObject implements InventoryHolder {
this.style = style;
}
public static Plant deserialize(JSONObject json) {
public static BiomassBlob deserialize(JSONObject json) {
Plant plant = new Plant();
BiomassBlob biomassBlob = new BiomassBlob();
plant.setObjectId((int) (long) json.get("id"));
plant.setX((int) (long) json.get("x"));
plant.setY((int) (long) json.get("y"));
plant.style = (int) (long) json.get("style");
plant.biomassCount = (int) (long) json.get("biomassCount");
biomassBlob.setObjectId((int) (long) json.get("id"));
biomassBlob.setX((int) (long) json.get("x"));
biomassBlob.setY((int) (long) json.get("y"));
biomassBlob.style = (int) (long) json.get("style");
biomassBlob.biomassCount = (int) (long) json.get("biomassCount");
return plant;
return biomassBlob;
}
/**
* Called when an object attempts to place an item in this Plant
* Called when an object attempts to place an item in this BiomassBlob
*
* @param item item id (see MarConstants.ITEM_*)
* @return Always returns false
*/
@Override
public boolean placeItem(int item) {
//Why would you want to place an item in a plant?
//Why would you want to place an item in a blob?
return false;
}
@ -87,7 +88,7 @@ public class Plant extends GameObject implements InventoryHolder {
}
/**
* Called when an object attempts to take an item from this Plant.
* 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 it reaches 0, the plant is deleted
*

View File

@ -0,0 +1,35 @@
package net.simon987.biomassplugin;
import net.simon987.biomassplugin.event.WorldCreationListener;
import net.simon987.biomassplugin.event.WorldUpdateListener;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.game.GameObject;
import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin;
import org.json.simple.JSONObject;
public class BiomassPlugin extends ServerPlugin implements GameObjectDeserializer {
@Override
public void init(ServerConfiguration config) {
listeners.add(new WorldCreationListener());
listeners.add(new WorldUpdateListener(config));
LogManager.LOGGER.info("Initialised Biomass plugin");
}
@Override
public GameObject deserializeObject(JSONObject object) {
int objType = (int) (long) object.get("type");
if (objType == BiomassBlob.ID) {
return BiomassBlob.deserialize(object);
}
return null;
}
}

View File

@ -0,0 +1,83 @@
package net.simon987.biomassplugin;
import net.simon987.server.GameServer;
import net.simon987.server.game.World;
import net.simon987.server.logging.LogManager;
import java.awt.*;
import java.util.ArrayList;
import java.util.Random;
public class WorldUtils {
/**
* Generate a list of biomass blobs for a world
*/
public static ArrayList<BiomassBlob> generateBlobs(World world, int minCount, int maxCount, int yield) {
System.out.println("Generating blobs...");
Random random = new Random();
int blobCount = random.nextInt(maxCount - minCount) + minCount;
ArrayList<BiomassBlob> biomassBlobs = new ArrayList<>(maxCount);
//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
int[][] tiles = world.getTileMap().getTiles();
int plainCount = 0;
for (int y = 0; y < World.WORLD_SIZE; y++) {
for (int x = 0; x < World.WORLD_SIZE; x++) {
if (tiles[x][y] == 0) {
plainCount++;
}
}
}
if (blobCount > plainCount) {
blobCount = plainCount;
}
outerLoop:
for (int i = 0; i < blobCount; i++) {
Point p = world.getTileMap().getRandomPlainTile();
if (p != null) {
//Don't block worlds
int counter = 0;
while (p.x == 0 || p.y == 0 || p.x == World.WORLD_SIZE - 1 || p.y == World.WORLD_SIZE - 1 ||
world.isTileBlocked(p.x, p.y)) {
p = world.getTileMap().getRandomPlainTile();
counter++;
if (counter > 25) {
continue outerLoop;
}
}
for (BiomassBlob biomassBlob : biomassBlobs) {
if (biomassBlob.getX() == p.x && biomassBlob.getY() == p.y) {
//There is already a blob here
continue outerLoop;
}
}
BiomassBlob biomassBlob = new BiomassBlob();
biomassBlob.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
biomassBlob.setStyle(0); //TODO: set style depending on difficulty level? or random? from config?
biomassBlob.setBiomassCount(yield);
biomassBlob.setX(p.x);
biomassBlob.setY(p.y);
biomassBlob.setWorld(world);
biomassBlobs.add(biomassBlob);
}
}
LogManager.LOGGER.info("Generated " + biomassBlobs.size() + " biomassBlobs for World (" + world.getX() + ',' +
world.getY() + ')');
return biomassBlobs;
}
}

View File

@ -0,0 +1,31 @@
package net.simon987.biomassplugin.event;
import net.simon987.biomassplugin.BiomassBlob;
import net.simon987.biomassplugin.WorldUtils;
import net.simon987.server.GameServer;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldGenerationEvent;
import java.util.ArrayList;
public class WorldCreationListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return WorldGenerationEvent.class;
}
@Override
public void handle(GameEvent event) {
int minCount = GameServer.INSTANCE.getConfig().getInt("minBiomassCount");
int maxCount = GameServer.INSTANCE.getConfig().getInt("maxBiomassCount");
int yield = GameServer.INSTANCE.getConfig().getInt("biomass_yield");
ArrayList<BiomassBlob> biomassBlobs = WorldUtils.generateBlobs(((WorldGenerationEvent) event).getWorld(),
minCount, maxCount, yield);
((WorldGenerationEvent) event).getWorld().getGameObjects().addAll(biomassBlobs);
}
}

View File

@ -0,0 +1,72 @@
package net.simon987.biomassplugin.event;
import net.simon987.biomassplugin.BiomassBlob;
import net.simon987.biomassplugin.WorldUtils;
import net.simon987.server.GameServer;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldUpdateEvent;
import net.simon987.server.game.World;
import java.util.ArrayList;
import java.util.HashMap;
public class WorldUpdateListener implements GameEventListener {
private HashMap<World, Long> worldWaitMap = new HashMap<>(200);
private int minBlobCount;
private int maxBlobCount;
private int blobYield;
private int waitTime;
private int blobThreshold;
public WorldUpdateListener(ServerConfiguration config) {
minBlobCount = config.getInt("minBiomassRespawnCount");
maxBlobCount = config.getInt("maxBiomassRespawnCount");
waitTime = config.getInt("biomassRespawnTime");
blobThreshold = config.getInt("biomassRespawnThreshold");
blobYield = config.getInt("biomass_yield");
}
@Override
public Class getListenedEventType() {
return WorldUpdateEvent.class;
}
@Override
public void handle(GameEvent event) {
World world = ((WorldUpdateEvent) event).getWorld();
//If there is less than the respawn threshold,
if (world.getGameObjects(BiomassBlob.class).size() < blobThreshold) {
//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<BiomassBlob> newBlobs = WorldUtils.generateBlobs(world, minBlobCount,
maxBlobCount, blobYield);
world.getGameObjects().addAll(newBlobs);
//Set the 'waitUntil' time to 0 to indicate that we are not waiting
worldWaitMap.replace(world, 0L);
}
}
}
}
}

View File

@ -1,31 +0,0 @@
package net.simon987.plantplugin;
import net.simon987.plantplugin.event.WorldCreationListener;
import net.simon987.server.game.GameObject;
import net.simon987.server.io.GameObjectDeserializer;
import net.simon987.server.logging.LogManager;
import net.simon987.server.plugin.ServerPlugin;
import org.json.simple.JSONObject;
public class PlantPlugin extends ServerPlugin implements GameObjectDeserializer {
@Override
public void init() {
listeners.add(new WorldCreationListener());
LogManager.LOGGER.info("Initialised Plant plugin");
}
@Override
public GameObject deserializeObject(JSONObject object) {
int objType = (int) (long) object.get("type");
if (objType == Plant.ID) {
return Plant.deserialize(object);
}
return null;
}
}

View File

@ -1,95 +0,0 @@
package net.simon987.plantplugin.event;
import net.simon987.plantplugin.Plant;
import net.simon987.server.GameServer;
import net.simon987.server.event.GameEvent;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.event.WorldGenerationEvent;
import net.simon987.server.game.World;
import net.simon987.server.logging.LogManager;
import java.awt.*;
import java.util.ArrayList;
import java.util.Random;
public class WorldCreationListener implements GameEventListener {
@Override
public Class getListenedEventType() {
return WorldGenerationEvent.class;
}
@Override
public void handle(GameEvent event) {
ArrayList<Plant> plants = generatePlants(((WorldGenerationEvent) event).getWorld());
((WorldGenerationEvent) event).getWorld().getGameObjects().addAll(plants);
}
/**
* Generate a list of plants for a world
*/
public ArrayList<Plant> generatePlants(World world) {
int minTreeCount = GameServer.INSTANCE.getConfig().getInt("minTreeCount");
int maxTreeCount = GameServer.INSTANCE.getConfig().getInt("maxTreeCount");
int plant_yield = GameServer.INSTANCE.getConfig().getInt("plant_yield");
Random random = new Random();
int treeCount = random.nextInt(maxTreeCount - minTreeCount) + minTreeCount;
ArrayList<Plant> plants = new ArrayList<>(maxTreeCount);
//Count number of plain tiles. If there is less plain tiles than desired amount of trees,
//set the desired amount of trees to the plain tile count
int[][] tiles = world.getTileMap().getTiles();
int plainCount = 0;
for (int y = 0; y < World.WORLD_SIZE; y++) {
for (int x = 0; x < World.WORLD_SIZE; x++) {
if (tiles[x][y] == 0) {
plainCount++;
}
}
}
if (treeCount > plainCount) {
treeCount = plainCount;
}
outerLoop:
for (int i = 0; i < treeCount; i++) {
Point p = world.getTileMap().getRandomPlainTile();
if (p != null) {
//Don't block worlds
while (p.x == 0 || p.y == 0 || p.x == World.WORLD_SIZE - 1 || p.y == World.WORLD_SIZE - 1) {
p = world.getTileMap().getRandomPlainTile();
}
for (Plant plant : plants) {
if (plant.getX() == p.x && plant.getY() == p.y) {
//There is already a plant here
continue outerLoop;
}
}
Plant plant = new Plant();
plant.setObjectId(GameServer.INSTANCE.getGameUniverse().getNextObjectId());
plant.setStyle(0); //TODO: set style depending on difficulty level? or random? from config?
plant.setBiomassCount(plant_yield);
plant.setX(p.x);
plant.setY(p.y);
plant.setWorld(world);
plants.add(plant);
}
}
LogManager.LOGGER.info("Generated " + plants.size() + " plants for World (" + world.getX() + ',' +
world.getY() + ')');
return plants;
}
}

View File

@ -1,3 +1,3 @@
classpath=net.simon987.plantplugin.PlantPlugin
name=Plant Plugin
classpath=net.simon987.biomassplugin.BiomassPlugin
name=Biomass Plugin
version=1.0

View File

@ -50,7 +50,7 @@ public class GameServer implements Runnable {
for (File pluginFile : pluginDirListing) {
if (pluginFile.getName().endsWith(".jar")) {
pluginManager.load(pluginFile);
pluginManager.load(pluginFile, config);
}
}

View File

@ -62,10 +62,31 @@ public class World implements JSONSerialisable {
return y;
}
/**
* Get all the game objects that are instances of the specified class
*/
public ArrayList getGameObjects(Class<? extends GameObject> clazz) {
ArrayList<GameObject> objects = new ArrayList<>(gameObjects.size());
for (GameObject object : gameObjects) {
if (object.getClass().equals(clazz)) {
objects.add(object);
}
}
return objects;
}
public ArrayList<GameObject> getGameObjects() {
return gameObjects;
}
/**
* Update this World and its GameObjects
* <br>
* The update is handled by plugins first
*/
public void update() {
//Dispatch update event
@ -75,11 +96,11 @@ public class World implements JSONSerialisable {
ArrayList<GameObject> gameObjects_ = new ArrayList<>(gameObjects);
for (GameObject object : gameObjects_) {
//Clean up dead objects
if (object.isDead()) {
gameObjects.remove(object);
LogManager.LOGGER.fine("Removed object " + object + " id: " + object.getObjectId());
}
if (object instanceof Updatable) {
} else if (object instanceof Updatable) {
((Updatable) object).update();
}
}
@ -198,7 +219,7 @@ public class World implements JSONSerialisable {
counter++;
//Prevent infinite loop
if (counter >= 2500) {
if (counter >= 1000) {
return null;
}

View File

@ -2,7 +2,6 @@ package net.simon987.server.game.pathfinding;
import net.simon987.server.assembly.Util;
import net.simon987.server.game.World;
import net.simon987.server.logging.LogManager;
import java.util.ArrayList;
import java.util.Collections;
@ -116,7 +115,7 @@ public class Pathfinder {
}
//Incomplete path
LogManager.LOGGER.fine("Incomplete path! " + counter);
// LogManager.LOGGER.fine("Incomplete path! " + counter);
return null;
}

View File

@ -1,5 +1,6 @@
package net.simon987.server.plugin;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.logging.LogManager;
import java.io.File;
@ -20,7 +21,7 @@ public class PluginManager {
this.plugins = new ArrayList<>(10);
}
public void load(File pluginFile) {
public void load(File pluginFile, ServerConfiguration config) {
LogManager.LOGGER.info("Loading plugin file " + pluginFile.getName());
@ -33,18 +34,18 @@ public class PluginManager {
if (configEntry != null) {
InputStream stream = zipFile.getInputStream(configEntry);
Properties config = new Properties();
config.load(stream);
Properties pluginConfig = new Properties();
pluginConfig.load(stream);
//Load the plugin
ClassLoader loader = URLClassLoader.newInstance(new URL[]{pluginFile.toURI().toURL()});
Class<?> aClass = Class.forName(config.getProperty("classpath"), true, loader);
Class<?> aClass = Class.forName(pluginConfig.getProperty("classpath"), true, loader);
Class<? extends ServerPlugin> pluginClass = aClass.asSubclass(ServerPlugin.class);
Constructor<? extends ServerPlugin> constructor = pluginClass.getConstructor();
ServerPlugin plugin = constructor.newInstance();
plugin.setName(config.getProperty("name"));
plugin.setVersion(config.getProperty("version"));
plugin.setName(pluginConfig.getProperty("name"));
plugin.setVersion(pluginConfig.getProperty("version"));
LogManager.LOGGER.info("Loaded " + plugin.name + " V" + plugin.version);
@ -52,7 +53,7 @@ public class PluginManager {
plugins.add(plugin);
//Init the plugin
plugin.init();
plugin.init(config);
} else {
LogManager.LOGGER.severe("Couldn't find plugin.properties in " + pluginFile.getName());

View File

@ -1,5 +1,6 @@
package net.simon987.server.plugin;
import net.simon987.server.ServerConfiguration;
import net.simon987.server.event.GameEventListener;
import net.simon987.server.io.JSONSerialisable;
import org.json.simple.JSONObject;
@ -26,7 +27,7 @@ public abstract class ServerPlugin implements JSONSerialisable {
/**
* Called when the plugin is loaded
*/
public abstract void init();
public abstract void init(ServerConfiguration config);
public String getName() {
return name;

View File

@ -145,9 +145,8 @@ public class SocketServer extends WebSocketServer {
@Override
public void onError(WebSocket conn, Exception ex) {
LogManager.LOGGER.severe("an error occured on connection " + conn.getRemoteSocketAddress() + ':' + ex);
LogManager.LOGGER.severe("an error occurred on connection " + conn + ": " + ex);
userManager.remove(userManager.getUser(conn));
conn.close();
ex.printStackTrace();
}

View File

@ -4,7 +4,7 @@ mysql_user=mar
mysql_pass=mar
# MySQL address
mysql_url=jdbc:mysql://localhost:3306/mar?useSSL=false
save_interval=10
save_interval=5
# Web server port
webSocket_port=8887
webSocket_host=0.0.0.0
@ -14,12 +14,12 @@ cert_path=certificates
# ----------------------------------------------
# Length of a tick in ms
tick_length=1000
tick_length=250
# Default offset of the origin (starting point of code execution) in words
org_offset=1024
org_offset=512
# Address of the stack bottom
stack_bottom=32768
# Size of the memory in bytes
stack_bottom=65536
# Size of the memory in words
memory_size=65536
# Initial location of new user's controlled unit
new_user_worldX = 32767
@ -33,16 +33,20 @@ new_user_code=; Welcome to Much Assembly required!\n\
# Default held item
new_user_item=0
# ----------------------------------------------
# Biomass units yield for a plant
plant_yield=2
# Grow time in ticks for a plant to grow
plant_grow_time=32
# Minimum tree count for the WorldGenerator
minTreeCount=3
# Maximum tree count for the WorldGenerator
maxTreeCount=10
# Biomass units for each blob
biomass_yield=2
# Minimum biomass blob count for the WorldGenerator
minBiomassCount=3
minBiomassRespawnCount=2
# Maximum biomass blob count for the WorldGenerator
maxBiomassCount=10
maxBiomassRespawnCount=6
# Maximum energy of the battery hardware in kJ
battery_max_energy=60000
# Time for biomass respawn in ticks
biomassRespawnTime=64
# Respawn timer will start when biomass count is below this number
biomassRespawnThreshold=1
# ----------------------------------------------
# Minimum center point count for the WorldGenerator
wg_centerPointCountMin=5
@ -60,6 +64,4 @@ wg_minCopperCount=0
wg_maxCopperCount=2
# ----------------------------------------------
# Maximum execution time of user code in ms
user_timeout=500
# Free CPU execution time in ms
user_free_execution_time=2
user_timeout=100