mirror of
https://github.com/simon987/Much-Assembly-Required.git
synced 2025-04-10 14:26:45 +00:00
Merge pull request #118 from sg495/master
Implemented selective loading/unloading of worlds.
This commit is contained in:
commit
59fd620e4a
@ -36,11 +36,20 @@ public class GameServer implements Runnable {
|
||||
|
||||
private DayNightCycle dayNightCycle;
|
||||
|
||||
private MongoClient mongo = null;
|
||||
|
||||
public GameServer() {
|
||||
|
||||
this.config = new ServerConfiguration("config.properties");
|
||||
|
||||
try{
|
||||
mongo = new MongoClient("localhost", 27017);
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
gameUniverse = new GameUniverse(config);
|
||||
gameUniverse.setMongo(mongo);
|
||||
pluginManager = new PluginManager();
|
||||
|
||||
maxExecutionTime = config.getInt("user_timeout");
|
||||
@ -169,105 +178,101 @@ public class GameServer implements Runnable {
|
||||
") updated");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void load() {
|
||||
|
||||
LogManager.LOGGER.info("Loading from MongoDB");
|
||||
MongoClient mongo;
|
||||
try {
|
||||
mongo = new MongoClient("localhost", 27017);
|
||||
LogManager.LOGGER.info("Loading all data from MongoDB");
|
||||
|
||||
DB db = mongo.getDB("mar");
|
||||
DB db = mongo.getDB("mar");
|
||||
|
||||
DBCollection worlds = db.getCollection("world");
|
||||
DBCollection users = db.getCollection("user");
|
||||
DBCollection server = db.getCollection("server");
|
||||
DBCollection worlds = db.getCollection("world");
|
||||
DBCollection users = db.getCollection("user");
|
||||
DBCollection server = db.getCollection("server");
|
||||
|
||||
//Load worlds
|
||||
DBCursor cursor = worlds.find();
|
||||
while (cursor.hasNext()) {
|
||||
GameServer.INSTANCE.getGameUniverse().getWorlds().add(World.deserialize(cursor.next()));
|
||||
}
|
||||
|
||||
//Load users
|
||||
cursor = users.find();
|
||||
while (cursor.hasNext()) {
|
||||
try {
|
||||
GameServer.INSTANCE.getGameUniverse().getUsers().add(User.deserialize(cursor.next()));
|
||||
} catch (CancelledException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//Load misc server info
|
||||
cursor = server.find();
|
||||
if (cursor.hasNext()) {
|
||||
DBObject serverObj = cursor.next();
|
||||
gameUniverse.setTime((long) serverObj.get("time"));
|
||||
gameUniverse.setNextObjectId((long) serverObj.get("nextObjectId"));
|
||||
}
|
||||
|
||||
LogManager.LOGGER.info("Done loading! W:" + GameServer.INSTANCE.getGameUniverse().getWorlds().size() +
|
||||
" | U:" + GameServer.INSTANCE.getGameUniverse().getUsers().size());
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
BasicDBObject whereQuery = new BasicDBObject();
|
||||
whereQuery.put("shouldUpdate", true);
|
||||
DBCursor cursor = worlds.find(whereQuery);
|
||||
GameUniverse universe = GameServer.INSTANCE.getGameUniverse();
|
||||
while (cursor.hasNext()) {
|
||||
World w = World.deserialize(cursor.next());
|
||||
universe.addWorld(w);
|
||||
}
|
||||
|
||||
//Load users
|
||||
cursor = users.find();
|
||||
while (cursor.hasNext()) {
|
||||
try {
|
||||
universe.getUsers().add(User.deserialize(cursor.next()));
|
||||
} catch (CancelledException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
//Load misc server info
|
||||
cursor = server.find();
|
||||
if (cursor.hasNext()) {
|
||||
DBObject serverObj = cursor.next();
|
||||
gameUniverse.setTime((long) serverObj.get("time"));
|
||||
gameUniverse.setNextObjectId((long) serverObj.get("nextObjectId"));
|
||||
}
|
||||
|
||||
LogManager.LOGGER.info("Done loading! W:" + GameServer.INSTANCE.getGameUniverse().getWorlds().size() +
|
||||
" | U:" + GameServer.INSTANCE.getGameUniverse().getUsers().size());
|
||||
}
|
||||
|
||||
private void save() {
|
||||
|
||||
LogManager.LOGGER.info("Saving to MongoDB | W:" + gameUniverse.getWorlds().size() + " | U:" + gameUniverse.getUsers().size());
|
||||
try{
|
||||
DB db = mongo.getDB("mar");
|
||||
|
||||
MongoClient mongo;
|
||||
try {
|
||||
mongo = new MongoClient("localhost", 27017);
|
||||
int unloaded_worlds = 0;
|
||||
|
||||
DB db = mongo.getDB("mar");
|
||||
DBCollection worlds = db.getCollection("world");
|
||||
DBCollection users = db.getCollection("user");
|
||||
DBCollection server = db.getCollection("server");
|
||||
|
||||
db.dropDatabase(); //Todo: Update database / keep history instead of overwriting
|
||||
List<DBObject> worldDocuments = new ArrayList<>();
|
||||
int perBatch = 35;
|
||||
int insertedWorlds = 0;
|
||||
GameUniverse universe = GameServer.INSTANCE.getGameUniverse();
|
||||
ArrayList<World> worlds_ = new ArrayList<>(universe.getWorlds());
|
||||
for (World w : worlds_) {
|
||||
LogManager.LOGGER.fine("Saving world "+w.getId()+" to mongodb");
|
||||
insertedWorlds++;
|
||||
worlds.save(w.mongoSerialise());
|
||||
|
||||
// If the world should unload, it is removed from the Universe after having been saved.
|
||||
if (w.shouldUnload()){
|
||||
unloaded_worlds++;
|
||||
LogManager.LOGGER.fine("Unloading world "+w.getId()+" from universe");
|
||||
universe.removeWorld(w);
|
||||
}
|
||||
}
|
||||
|
||||
DBCollection worlds = db.getCollection("world");
|
||||
DBCollection users = db.getCollection("user");
|
||||
DBCollection server = db.getCollection("server");
|
||||
List<DBObject> userDocuments = new ArrayList<>();
|
||||
int insertedUsers = 0;
|
||||
ArrayList<User> users_ = new ArrayList<>(GameServer.INSTANCE.getGameUniverse().getUsers());
|
||||
for (User u : users_) {
|
||||
|
||||
List<DBObject> worldDocuments = new ArrayList<>();
|
||||
int perBatch = 35;
|
||||
int insertedWorlds = 0;
|
||||
ArrayList<World> worlds_ = new ArrayList<>(GameServer.INSTANCE.getGameUniverse().getWorlds());
|
||||
for (World w : worlds_) {
|
||||
worldDocuments.add(w.mongoSerialise());
|
||||
insertedWorlds++;
|
||||
insertedUsers++;
|
||||
|
||||
if (worldDocuments.size() >= perBatch || insertedWorlds >= GameServer.INSTANCE.getGameUniverse().getWorlds().size()) {
|
||||
worlds.insert(worldDocuments);
|
||||
worldDocuments.clear();
|
||||
}
|
||||
}
|
||||
|
||||
List<DBObject> userDocuments = new ArrayList<>();
|
||||
int insertedUsers = 0;
|
||||
ArrayList<User> users_ = new ArrayList<>(GameServer.INSTANCE.getGameUniverse().getUsers());
|
||||
for (User u : users_) {
|
||||
if (!u.isGuest()) {
|
||||
users.save(u.mongoSerialise());
|
||||
}
|
||||
|
||||
insertedUsers++;
|
||||
}
|
||||
|
||||
if (!u.isGuest()) {
|
||||
userDocuments.add(u.mongoSerialise());
|
||||
}
|
||||
BasicDBObject serverObj = new BasicDBObject();
|
||||
serverObj.put("_id","serverinfo"); // a constant id ensures only one entry is kept and updated, instead of a new entry created every save.
|
||||
serverObj.put("time", gameUniverse.getTime());
|
||||
serverObj.put("nextObjectId", gameUniverse.getNextObjectId());
|
||||
server.save(serverObj);
|
||||
|
||||
if (userDocuments.size() >= perBatch || insertedUsers >= GameServer.INSTANCE.getGameUniverse().getUsers().size()) {
|
||||
users.insert(userDocuments);
|
||||
userDocuments.clear();
|
||||
}
|
||||
}
|
||||
|
||||
BasicDBObject serverObj = new BasicDBObject();
|
||||
serverObj.put("time", gameUniverse.getTime());
|
||||
serverObj.put("nextObjectId", gameUniverse.getNextObjectId());
|
||||
server.insert(serverObj);
|
||||
|
||||
mongo.close();
|
||||
|
||||
LogManager.LOGGER.info("Done!");
|
||||
LogManager.LOGGER.info(""+insertedWorlds+" worlds saved, "+unloaded_worlds+" unloaded");
|
||||
LogManager.LOGGER.info("Done!");
|
||||
} catch (Exception e) {
|
||||
LogManager.LOGGER.severe("Problem happened during save function");
|
||||
e.printStackTrace();
|
||||
|
@ -1,5 +1,6 @@
|
||||
package net.simon987.server.game;
|
||||
|
||||
import com.mongodb.*;
|
||||
import net.simon987.server.GameServer;
|
||||
import net.simon987.server.ServerConfiguration;
|
||||
import net.simon987.server.assembly.Assembler;
|
||||
@ -9,14 +10,21 @@ import net.simon987.server.assembly.exception.CancelledException;
|
||||
import net.simon987.server.logging.LogManager;
|
||||
import net.simon987.server.user.User;
|
||||
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Hashtable;
|
||||
|
||||
public class GameUniverse {
|
||||
|
||||
private ArrayList<World> worlds;
|
||||
//private ArrayList<World> worlds;
|
||||
private Hashtable<String,World> worlds;
|
||||
private ArrayList<User> users;
|
||||
private WorldGenerator worldGenerator;
|
||||
|
||||
private MongoClient mongo = null;
|
||||
|
||||
|
||||
private long time;
|
||||
|
||||
private long nextObjectId = 0;
|
||||
@ -25,63 +33,149 @@ public class GameUniverse {
|
||||
|
||||
public GameUniverse(ServerConfiguration config) {
|
||||
|
||||
worlds = new ArrayList<>(32);
|
||||
//worlds = new ArrayList<>(32);
|
||||
worlds = new Hashtable<String,World>(32);
|
||||
users = new ArrayList<>(16);
|
||||
|
||||
worldGenerator = new WorldGenerator(config);
|
||||
}
|
||||
|
||||
public void setMongo(MongoClient mongo){
|
||||
this.mongo = mongo;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public World getWorld(int x, int y, boolean createNew) {
|
||||
/**
|
||||
* Attempts loading a world from mongoDB by coordinates
|
||||
*
|
||||
* @param x the x coordinate of the world
|
||||
* @param y the y coordinate of the world
|
||||
*
|
||||
* @return World, null if not found
|
||||
*/
|
||||
private World loadWorld(int x, int y){
|
||||
|
||||
DB db = mongo.getDB("mar");
|
||||
DBCollection worlds = db.getCollection("world");
|
||||
|
||||
for (World world : worlds) {
|
||||
if (world.getX() == x && world.getY() == y) {
|
||||
return world;
|
||||
}
|
||||
BasicDBObject whereQuery = new BasicDBObject();
|
||||
whereQuery.put("_id", World.idFromCoordinates(x,y));
|
||||
DBCursor cursor = worlds.find(whereQuery);
|
||||
if (cursor.hasNext()) {
|
||||
World w = World.deserialize(cursor.next());
|
||||
return w;
|
||||
}
|
||||
|
||||
if (x >= 0 && x <= maxWidth && y >= 0 && y <= maxWidth) {
|
||||
if (createNew) {
|
||||
//World does not exist
|
||||
World world = createWorld(x, y);
|
||||
worlds.add(world);
|
||||
|
||||
return world;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
} else {
|
||||
else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public World createWorld(int x, int y) {
|
||||
/**
|
||||
* Get a world by coordinates, attempts to load from mongoDB if not found.
|
||||
*
|
||||
* @param x the x coordinate of the world
|
||||
* @param y the y coordinate of the world
|
||||
* @param createNew if true, a new world is created when a world with given coordinates is not found
|
||||
*
|
||||
* @return World, null if not found and not created.
|
||||
*/
|
||||
public World getWorld(int x, int y, boolean createNew) {
|
||||
|
||||
// Wrapping coordinates around cyclically
|
||||
x %= maxWidth+1;
|
||||
y %= maxWidth+1;
|
||||
|
||||
// Looks for a previously loaded world
|
||||
World world = getLoadedWorld(x,y);
|
||||
if (world != null){
|
||||
return world;
|
||||
}
|
||||
|
||||
// Tries loading the world from the database
|
||||
world = loadWorld(x,y);
|
||||
if (world != null){
|
||||
addWorld(world);
|
||||
LogManager.LOGGER.fine("Loaded world "+(World.idFromCoordinates(x,y))+" from mongodb.");
|
||||
return world;
|
||||
}
|
||||
|
||||
// World does not exist
|
||||
if (createNew) {
|
||||
// Creates a new world
|
||||
world = createWorld(x, y);
|
||||
addWorld(world);
|
||||
LogManager.LOGGER.fine("Created new world "+(World.idFromCoordinates(x,y))+".");
|
||||
return world;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public World getLoadedWorld(int x, int y) {
|
||||
// Wrapping coordinates around cyclically
|
||||
x %= maxWidth+1;
|
||||
y %= maxWidth+1;
|
||||
|
||||
return worlds.get(World.idFromCoordinates(x,y));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new or freshly loaded world to the universe (if not already present).
|
||||
*
|
||||
* @param world the world to be added
|
||||
*/
|
||||
public void addWorld(World world){
|
||||
World w = worlds.get(world.getId());
|
||||
if (w == null){
|
||||
world.setUniverse(this);
|
||||
worlds.put(world.getId(),world);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the world with given coordinates from the universe.
|
||||
*
|
||||
* @param x the x coordinate of the world to be removed
|
||||
* @param y the y coordinate of the world to be removed
|
||||
*/
|
||||
public void removeWorld(int x, int y){
|
||||
World w = worlds.remove(World.idFromCoordinates(x,y));
|
||||
if (w != null){
|
||||
w.setUniverse(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given world from the universe.
|
||||
*
|
||||
* @param world the world to be removed.
|
||||
*/
|
||||
public void removeWorld(World world){
|
||||
World w = worlds.remove(world.getId());
|
||||
if (w != null){
|
||||
w.setUniverse(null);
|
||||
}
|
||||
}
|
||||
|
||||
public World createWorld(int x, int y) {
|
||||
World world = null;
|
||||
try {
|
||||
world = worldGenerator.generateWorld(x, y);
|
||||
|
||||
|
||||
} catch (CancelledException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
public User getUser(String username) {
|
||||
|
||||
for (User user : users) {
|
||||
if (user.getUsername().equals(username)) {
|
||||
return user;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -142,7 +236,7 @@ public class GameUniverse {
|
||||
public GameObject getObject(long id) {
|
||||
|
||||
//
|
||||
for (World world : worlds) {
|
||||
for (World world : getWorlds()) {
|
||||
for (GameObject object : world.getGameObjects()) {
|
||||
if (object.getObjectId() == id) {
|
||||
return object;
|
||||
@ -160,7 +254,7 @@ public class GameUniverse {
|
||||
}
|
||||
|
||||
public ArrayList<World> getWorlds() {
|
||||
return worlds;
|
||||
return new ArrayList<World>(worlds.values());
|
||||
}
|
||||
|
||||
public ArrayList<User> getUsers() {
|
||||
|
@ -6,9 +6,11 @@ import com.mongodb.DBObject;
|
||||
import net.simon987.server.GameServer;
|
||||
import net.simon987.server.event.GameEvent;
|
||||
import net.simon987.server.event.WorldUpdateEvent;
|
||||
import net.simon987.server.game.GameUniverse;
|
||||
import net.simon987.server.game.pathfinding.Pathfinder;
|
||||
import net.simon987.server.io.MongoSerialisable;
|
||||
import org.json.simple.JSONObject;
|
||||
import net.simon987.server.logging.LogManager;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
@ -57,7 +59,28 @@ public class World implements MongoSerialisable {
|
||||
public boolean isTileBlocked(int x, int y) {
|
||||
|
||||
return getGameObjectsBlockingAt(x, y).size() > 0 || tileMap.getTileAt(x, y) == TileMap.WALL_TILE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the world's unique id from its coordinates.
|
||||
*
|
||||
* @param x the x coordinate of the world
|
||||
* @param y the y coordinate of the world
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public static String idFromCoordinates(int x, int y){
|
||||
return "w-"+"0x"+Integer.toHexString(x)+"-"+"0x"+Integer.toHexString(y);
|
||||
//return ((long)x)*(((long)maxWidth)+1)+((long)y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world's unique id, computed with idFromCoordinates.
|
||||
*
|
||||
* @return long
|
||||
*/
|
||||
public String getId(){
|
||||
return World.idFromCoordinates(x,y);
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
@ -124,6 +147,9 @@ public class World implements MongoSerialisable {
|
||||
objects.add(obj.mongoSerialise());
|
||||
}
|
||||
|
||||
|
||||
dbObject.put("_id", getId());
|
||||
|
||||
dbObject.put("objects", objects);
|
||||
dbObject.put("terrain", tileMap.mongoSerialise());
|
||||
|
||||
@ -131,7 +157,7 @@ public class World implements MongoSerialisable {
|
||||
dbObject.put("y", y);
|
||||
|
||||
dbObject.put("updatable", updatable);
|
||||
|
||||
dbObject.put("shouldUpdate",shouldUpdate());
|
||||
|
||||
return dbObject;
|
||||
}
|
||||
@ -330,4 +356,73 @@ public class World implements MongoSerialisable {
|
||||
public boolean shouldUpdate() {
|
||||
return updatable > 0;
|
||||
}
|
||||
|
||||
|
||||
private GameUniverse universe = null;
|
||||
|
||||
public void setUniverse(GameUniverse universe){
|
||||
this.universe = universe;
|
||||
}
|
||||
|
||||
public ArrayList<World> getNeighbouringLoadedWorlds(){
|
||||
ArrayList<World> neighbouringWorlds = new ArrayList<>();
|
||||
|
||||
if (universe == null){
|
||||
return neighbouringWorlds;
|
||||
}
|
||||
|
||||
for (int dx=-1; dx<=+1; dx+=2){
|
||||
World nw = universe.getLoadedWorld(x+dx,y);
|
||||
if (nw != null){
|
||||
neighbouringWorlds.add(nw);
|
||||
}
|
||||
}
|
||||
for (int dy=-1; dy<=+1; dy+=2){
|
||||
World nw = universe.getLoadedWorld(x,y+dy);
|
||||
if (nw != null){
|
||||
neighbouringWorlds.add(nw);
|
||||
}
|
||||
}
|
||||
|
||||
return neighbouringWorlds;
|
||||
}
|
||||
|
||||
public ArrayList<World> getNeighbouringExistingWorlds(){
|
||||
ArrayList<World> neighbouringWorlds = new ArrayList<>();
|
||||
|
||||
if (universe == null){
|
||||
return neighbouringWorlds;
|
||||
}
|
||||
|
||||
for (int dx=-1; dx<=+1; dx+=2){
|
||||
World nw = universe.getWorld(x+dx,y,false);
|
||||
if (nw != null){
|
||||
neighbouringWorlds.add(nw);
|
||||
}
|
||||
}
|
||||
for (int dy=-1; dy<=+1; dy+=2){
|
||||
World nw = universe.getWorld(x,y+dy,false);
|
||||
if (nw != null){
|
||||
neighbouringWorlds.add(nw);
|
||||
}
|
||||
}
|
||||
|
||||
return neighbouringWorlds;
|
||||
}
|
||||
|
||||
|
||||
public boolean canUnload(){
|
||||
return updatable==0;
|
||||
}
|
||||
|
||||
public boolean shouldUnload(){
|
||||
boolean res = canUnload();
|
||||
|
||||
for (World nw : getNeighbouringLoadedWorlds() ){
|
||||
res &= nw.canUnload();
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ public class User implements MongoSerialisable {
|
||||
|
||||
BasicDBObject dbObject = new BasicDBObject();
|
||||
|
||||
dbObject.put("_id", username); // a constant id ensures only one entry per user is kept and updated, instead of a new entry created every save for every user.
|
||||
dbObject.put("username", username);
|
||||
dbObject.put("code", userCode);
|
||||
dbObject.put("controlledUnit", controlledUnit.getObjectId());
|
||||
|
Loading…
x
Reference in New Issue
Block a user