diff --git a/pswgcommon b/pswgcommon
index 44dc9210..fff4daa9 160000
--- a/pswgcommon
+++ b/pswgcommon
@@ -1 +1 @@
-Subproject commit 44dc921082dc6a2a9e63941a0130033fcc81945c
+Subproject commit fff4daa98e4270aa45e06c94c3ff542fac5224ab
diff --git a/src/main/java/com/projectswg/holocore/ProjectSWG.java b/src/main/java/com/projectswg/holocore/ProjectSWG.java
index 8a929029..05e71aea 100644
--- a/src/main/java/com/projectswg/holocore/ProjectSWG.java
+++ b/src/main/java/com/projectswg/holocore/ProjectSWG.java
@@ -32,7 +32,6 @@ import com.projectswg.common.data.encodables.galaxy.Galaxy.GalaxyStatus;
import com.projectswg.holocore.intents.support.data.control.ServerStatusIntent;
import com.projectswg.holocore.resources.support.data.client_info.ServerFactory;
import com.projectswg.holocore.resources.support.data.control.ServerStatus;
-import com.projectswg.holocore.resources.support.data.server_info.DataManager;
import com.projectswg.holocore.resources.support.data.server_info.mongodb.PswgDatabase;
import com.projectswg.holocore.services.gameplay.GameplayManager;
import com.projectswg.holocore.services.support.SupportManager;
@@ -111,7 +110,6 @@ public class ProjectSWG {
return -1;
}
setupDatabase(arguments);
- DataManager.initialize();
Thread.currentThread().setPriority(10);
initializeServerFactory();
setupGalaxy(arguments);
@@ -145,7 +143,6 @@ public class ProjectSWG {
}
private static void shutdownStaticClasses() {
- DataManager.terminate();
ScheduledUtilities.shutdown();
}
@@ -177,16 +174,16 @@ public class ProjectSWG {
private static void setupGalaxy(CommandLine arguments) {
GALAXY.setId(1);
- GALAXY.setName(PswgDatabase.config().getString(ProjectSWG.class, "galaxyName", "Holocore"));
+ GALAXY.setName(PswgDatabase.INSTANCE.getConfig().getString(ProjectSWG.class, "galaxyName", "Holocore"));
GALAXY.setAddress("");
GALAXY.setPopulation(0);
GALAXY.setZoneOffset(OffsetTime.now().getOffset());
GALAXY.setZonePort(0);
GALAXY.setPingPort(0);
GALAXY.setStatus(GalaxyStatus.DOWN);
- GALAXY.setMaxCharacters(PswgDatabase.config().getInt(ProjectSWG.class, "galaxyMaxCharacters", 2));
- GALAXY.setOnlinePlayerLimit(PswgDatabase.config().getInt(ProjectSWG.class, "galaxyMaxOnline", 3000));
- GALAXY.setOnlineFreeTrialLimit(PswgDatabase.config().getInt(ProjectSWG.class, "galaxyMaxOnline", 3000));
+ GALAXY.setMaxCharacters(PswgDatabase.INSTANCE.getConfig().getInt(ProjectSWG.class, "galaxyMaxCharacters", 2));
+ GALAXY.setOnlinePlayerLimit(PswgDatabase.INSTANCE.getConfig().getInt(ProjectSWG.class, "galaxyMaxOnline", 3000));
+ GALAXY.setOnlineFreeTrialLimit(PswgDatabase.INSTANCE.getConfig().getInt(ProjectSWG.class, "galaxyMaxOnline", 3000));
GALAXY.setRecommended(true);
try {
GALAXY.setAdminServerPort(Integer.parseInt(arguments.getOptionValue("admin-port", "-1")));
@@ -199,9 +196,9 @@ public class ProjectSWG {
private static void setupDatabase(CommandLine arguments) {
String dbStr = arguments.getOptionValue("database", "mongodb://localhost");
- String db = arguments.getOptionValue("database", "nge");
+ String db = arguments.getOptionValue("dbName", "nge");
- PswgDatabase.initialize(dbStr, db);
+ PswgDatabase.INSTANCE.initialize(dbStr, db);
}
private static Options createArgumentOptions() {
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/crafting/resource/galactic/GalacticResourceSpawn.java b/src/main/java/com/projectswg/holocore/resources/gameplay/crafting/resource/galactic/GalacticResourceSpawn.java
index 667de0e6..2c8b2ab0 100644
--- a/src/main/java/com/projectswg/holocore/resources/gameplay/crafting/resource/galactic/GalacticResourceSpawn.java
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/crafting/resource/galactic/GalacticResourceSpawn.java
@@ -31,8 +31,6 @@ import com.projectswg.common.data.encodables.mongo.MongoPersistable;
import com.projectswg.common.data.location.Terrain;
import com.projectswg.common.network.NetBufferStream;
import com.projectswg.common.persistable.Persistable;
-import com.projectswg.holocore.resources.support.data.config.ConfigFile;
-import com.projectswg.holocore.resources.support.data.server_info.DataManager;
import com.projectswg.holocore.resources.support.data.server_info.mongodb.PswgDatabase;
import java.time.Instant;
@@ -145,19 +143,19 @@ public class GalacticResourceSpawn implements Persistable, MongoPersistable {
}
private int getMinSpawnTime() {
- return PswgDatabase.config().getInt(this, "resourceMinSpawnTime", 7);
+ return PswgDatabase.INSTANCE.getConfig().getInt(this, "resourceMinSpawnTime", 7);
}
private int getMaxSpawnTime() {
- return PswgDatabase.config().getInt(this, "resourceMaxSpawnTime", 21);
+ return PswgDatabase.INSTANCE.getConfig().getInt(this, "resourceMaxSpawnTime", 21);
}
private int getMinRadius() {
- return PswgDatabase.config().getInt(this, "resourceMinSpawnRadius", 200);
+ return PswgDatabase.INSTANCE.getConfig().getInt(this, "resourceMinSpawnRadius", 200);
}
private int getMaxRadius() {
- return PswgDatabase.config().getInt(this, "resourceMaxSpawnRadius", 500);
+ return PswgDatabase.INSTANCE.getConfig().getInt(this, "resourceMaxSpawnRadius", 500);
}
private int calculateRandomMaxConcentration(Random random, int min) {
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/crafting/resource/galactic/GalacticResourceSpawner.java b/src/main/java/com/projectswg/holocore/resources/gameplay/crafting/resource/galactic/GalacticResourceSpawner.java
index 1f817404..229356ae 100644
--- a/src/main/java/com/projectswg/holocore/resources/gameplay/crafting/resource/galactic/GalacticResourceSpawner.java
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/crafting/resource/galactic/GalacticResourceSpawner.java
@@ -29,13 +29,9 @@ package com.projectswg.holocore.resources.gameplay.crafting.resource.galactic;
import com.projectswg.common.data.location.Terrain;
import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.storage.GalacticResourceContainer;
import com.projectswg.holocore.resources.gameplay.crafting.resource.raw.RawResource;
-import com.projectswg.holocore.resources.gameplay.crafting.resource.raw.RawResourceContainer;
import com.projectswg.holocore.resources.support.data.namegen.SWGNameGenerator;
-import com.projectswg.holocore.resources.support.data.server_info.DataManager;
import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
import com.projectswg.holocore.resources.support.data.server_info.mongodb.PswgDatabase;
-import me.joshlarson.jlcommon.log.Log;
-import me.joshlarson.jlcommon.log.log_wrapper.ConsoleLogWrapper;
import java.util.List;
import java.util.Random;
@@ -69,20 +65,6 @@ public class GalacticResourceSpawner {
this.resourceIdMax = new AtomicLong(0);
}
- public static void main(String [] args) {
- Log.addWrapper(new ConsoleLogWrapper());
- DataManager.initialize();
- RawResourceContainer container = new RawResourceContainer();
- container.loadResources();
- for (RawResource rawResource : container.getResources()) {
- GalacticResourceContainer.getContainer().addRawResource(rawResource);
- }
- GalacticResourceSpawner spawner = new GalacticResourceSpawner();
- spawner.initialize();
- spawner.terminate();
- DataManager.terminate();
- }
-
public void initialize() {
loadResources();
updateAllResources();
@@ -100,7 +82,7 @@ public class GalacticResourceSpawner {
private void loadResources() {
long startTime = StandardLog.onStartLoad("galactic resources");
int resourceCount = 0;
- for (GalacticResource resource : PswgDatabase.resources().getResources()) {
+ for (GalacticResource resource : PswgDatabase.INSTANCE.getResources().getResources()) {
GalacticResourceContainer.getContainer().addGalacticResource(resource);
if (resource.getId() > resourceIdMax.get())
resourceIdMax.set(resource.getId());
@@ -112,7 +94,7 @@ public class GalacticResourceSpawner {
private void saveResources() {
GalacticResourceLoader loader = new GalacticResourceLoader();
loader.saveResources(GalacticResourceContainer.getContainer().getAllResources());
- PswgDatabase.resources().addResources(GalacticResourceContainer.getContainer().getAllResources());
+ PswgDatabase.INSTANCE.getResources().setResources(GalacticResourceContainer.getContainer().getAllResources());
}
private void updateUnusedPools() {
diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/world/travel/TravelHelper.java b/src/main/java/com/projectswg/holocore/resources/gameplay/world/travel/TravelHelper.java
index ddca572c..6fdc243e 100644
--- a/src/main/java/com/projectswg/holocore/resources/gameplay/world/travel/TravelHelper.java
+++ b/src/main/java/com/projectswg/holocore/resources/gameplay/world/travel/TravelHelper.java
@@ -186,8 +186,8 @@ public class TravelHelper {
}
private void createGalaxyTravels() {
- long groundTime = PswgDatabase.config().getInt(this, "shuttleGroundTime", 120);
- long airTime = PswgDatabase.config().getInt(this, "shuttleAirTime", 60);
+ long groundTime = PswgDatabase.INSTANCE.getConfig().getInt(this, "shuttleGroundTime", 120);
+ long airTime = PswgDatabase.INSTANCE.getConfig().getInt(this, "shuttleAirTime", 60);
createGalaxyTravel(SpecificObject.SO_TRANSPORT_SHUTTLE.getTemplate(), 17, groundTime, airTime);
createGalaxyTravel(SpecificObject.SO_TRANSPORT_STARPORT.getTemplate(), 21, groundTime, airTime);
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/ConfigWatcher.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/ConfigWatcher.java
deleted file mode 100644
index ca092576..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/ConfigWatcher.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/***********************************************************************************
- * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of Holocore. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * Holocore is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * Holocore is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
-package com.projectswg.holocore.resources.support.data.server_info;
-
-import com.projectswg.common.data.info.Config;
-import com.projectswg.holocore.intents.support.data.config.ConfigChangedIntent;
-import com.projectswg.holocore.resources.support.data.config.ConfigFile;
-import me.joshlarson.jlcommon.concurrency.BasicThread;
-import me.joshlarson.jlcommon.log.Log;
-
-import java.io.IOException;
-import java.nio.file.*;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public final class ConfigWatcher {
-
- private static final String CFGPATH = "cfg/";
-
- private final Map configMap;
- private final BasicThread watcherThread;
- private final AtomicBoolean running;
-
- private WatchService watcher;
-
- public ConfigWatcher(Map configMap) {
- this.configMap = configMap;
- this.watcherThread = new BasicThread("config-watcher", this::watch);
- this.running = new AtomicBoolean(false);
-
- try {
- this.watcher = FileSystems.getDefault().newWatchService();
- Paths.get(CFGPATH).register(watcher, StandardWatchEventKinds.ENTRY_MODIFY);
- } catch (IOException e) {
- this.watcher = null;
- }
- }
-
- public void start() {
- running.set(true);
- watcherThread.start();
- }
-
- public void stop() {
- running.set(false);
- watcherThread.stop(true);
- watcherThread.awaitTermination(1000);
- }
-
- private void watch() {
- if (watcher == null) {
- Log.e("WatcherService is null!");
- return;
- }
- WatchKey key;
-
- Log.i("ConfigWatcher started");
- try {
- while (running.get()) {
- key = watcher.take(); // We're stuck here until a change is made.
- if (key == null)
- break;
-
- for (WatchEvent> event : key.pollEvents()) {
- processEvents(event);
- key.reset();
- }
- }
- } catch (Exception e) {
- // Ignored
- } finally {
- try {
- watcher.close();
- } catch (Exception e) {
- Log.e(e);
- }
- }
- Log.i("ConfigWatcher shut down");
- }
-
- private void processEvents(WatchEvent> event) {
- if (event.kind() == StandardWatchEventKinds.OVERFLOW)
- return;
-
- @SuppressWarnings("unchecked")
- Path cfgPath = ((WatchEvent) event).context();
- ConfigFile cfgFile = ConfigFile.configFileForName(CFGPATH + cfgPath);
- if (cfgFile == null) {
- Log.w("Unknown config file: %s", cfgPath);
- return;
- }
- Config cfg = configMap.get(cfgFile);
- if (cfg == null) {
- Log.w("Unknown config type: %s", cfgFile);
- return;
- }
-
- for (Entry entry : cfg.load().entrySet()) {
- new ConfigChangedIntent(cfgFile, entry.getKey(), entry.getValue(), cfg.getString(entry.getKey(), null)).broadcast();
- }
- }
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/DataManager.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/DataManager.java
deleted file mode 100644
index 4883c772..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/DataManager.java
+++ /dev/null
@@ -1,139 +0,0 @@
-/***********************************************************************************
- * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of Holocore. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * Holocore is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * Holocore is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
-package com.projectswg.holocore.resources.support.data.server_info;
-
-import com.projectswg.common.data.info.Config;
-import com.projectswg.common.data.info.RelationalServerFactory;
-import com.projectswg.holocore.resources.support.data.config.ConfigFile;
-import me.joshlarson.jlcommon.log.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.EnumMap;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-public class DataManager {
-
- private static final Object instanceLock = new Object();
- private static DataManager instance = null;
-
- private final Map configs;
- private final ConfigWatcher watcher;
- private final AtomicBoolean initialized;
-
- private DataManager() {
- this.configs = new EnumMap<>(ConfigFile.class);
- this.watcher = new ConfigWatcher(configs);
- this.initialized = new AtomicBoolean(false);
- }
-
- private void initializeImpl() {
- if (initialized.getAndSet(true))
- return;
- initializeConfig();
- RelationalServerFactory.setBasePath("serverdata/");
- }
-
- private void terminateImpl() {
- if (!initialized.getAndSet(false))
- return;
- watcher.stop();
- configs.clear();
- }
-
- private Config getConfigImpl(ConfigFile file) {
- return configs.get(file);
- }
-
- private void initializeConfig() {
- assert configs.isEmpty() : "double initialize";
- for (ConfigFile file : ConfigFile.values()) {
- File f = new File(file.getFilename());
- if (!createConfig(f)) {
- Log.w("ConfigFile could not be loaded! " + file.getFilename());
- } else {
- configs.put(file, new Config(f));
- }
- }
- watcher.start();
- }
-
- private boolean createConfig(File file) {
- if (file.exists())
- return file.isFile();
- try {
- File parent = file.getParentFile();
- if (parent != null && !parent.exists() && !parent.mkdirs()) {
- Log.e("Failed to create parent directories for config: " + file.getCanonicalPath());
- return false;
- }
- if (!file.createNewFile()) {
- Log.e("Failed to create new config: " + file.getCanonicalPath());
- return false;
- }
- } catch (IOException e) {
- Log.e(e);
- }
- return file.exists();
- }
-
- /**
- * Gets the config object associated with a certain file, or NULL if the file failed to load on startup
- *
- * @param file the file to get the config for
- * @return the config object associated with the file, or NULL if the config failed to load
- */
- public static Config getConfig(ConfigFile file) {
- return getInstance().getConfigImpl(file);
- }
-
- public static void initialize() {
- synchronized (instanceLock) {
- if (instance != null)
- return;
- instance = new DataManager();
- instance.initializeImpl();
- }
- }
-
- public static void terminate() {
- synchronized (instanceLock) {
- if (instance == null)
- return;
- instance.terminateImpl();
- instance = null;
- }
- }
-
- private static DataManager getInstance() {
- synchronized (instanceLock) {
- return instance;
- }
- }
-
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgConfigDatabase.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgConfigDatabase.java
deleted file mode 100644
index bec66d75..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgConfigDatabase.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/***********************************************************************************
- * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of Holocore. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * Holocore is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * Holocore is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
-
-package com.projectswg.holocore.resources.support.data.server_info.mongodb;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.model.Filters;
-import com.mongodb.client.model.IndexOptions;
-import com.mongodb.client.model.Indexes;
-import me.joshlarson.jlcommon.log.Log;
-import org.bson.Document;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-public class PswgConfigDatabase implements PswgDatabase {
-
- private MongoCollection collection;
-
- PswgConfigDatabase() {
- this.collection = null;
- }
-
- @Override
- public void open(MongoCollection collection) {
- this.collection = collection;
- collection.createIndex(Indexes.ascending("package"), new IndexOptions().unique(true));
- }
-
- @NotNull
- public MongoCollection getCollection() {
- return collection;
- }
-
- public String getString(Object o, String key, String def) {
- for (Document config : getConfigurations(o)) {
- if (config.containsKey(key))
- return config.getString(key);
- }
- return def;
- }
-
- public boolean getBoolean(Object o, String key, boolean def) {
- for (Document config : getConfigurations(o)) {
- if (config.containsKey(key))
- return config.getBoolean(key);
- }
- return def;
- }
-
- public int getInt(Object o, String key, int def) {
- for (Document config : getConfigurations(o)) {
- if (config.containsKey(key))
- return config.getInteger(key);
- }
- return def;
- }
-
- public double getDouble(Object o, String key, double def) {
- for (Document config : getConfigurations(o)) {
- if (config.containsKey(key))
- return config.getDouble(key);
- }
- return def;
- }
-
- public double getLong(Object o, String key, long def) {
- for (Document config : getConfigurations(o)) {
- if (config.containsKey(key))
- return config.getLong(key);
- }
- return def;
- }
-
- private List getConfigurations(Object o) {
- String packageKey = o instanceof Class ? ((Class>) o).getPackageName() : Objects.requireNonNull(o).getClass().getPackageName();
- if (packageKey.startsWith("com.projectswg.holocore."))
- packageKey = packageKey.substring(24);
- else if (packageKey.startsWith("com.projectswg.holocore"))
- packageKey = packageKey.substring(23);
- else
- throw new IllegalArgumentException("package lookup object does not belong to holocore");
-
- if (packageKey.startsWith("intents."))
- throw new IllegalArgumentException("intents should not be querying configs");
-
- if (packageKey.startsWith("resources.") || packageKey.startsWith("services."))
- packageKey = packageKey.substring(packageKey.indexOf('.')+1);
-
- List configs = new ArrayList<>();
- if (collection == null)
- return configs;
- while (!packageKey.isEmpty()) {
- Document doc = collection.find(Filters.eq("package", packageKey)).first();
- if (doc != null)
- configs.add(doc);
-
- int lastDot = packageKey.lastIndexOf('.');
- packageKey = lastDot == -1 ? "" : packageKey.substring(0, lastDot);
- }
- return configs;
- }
-
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgConfigDatabase.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgConfigDatabase.kt
new file mode 100644
index 00000000..4cee894c
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgConfigDatabase.kt
@@ -0,0 +1,110 @@
+/***********************************************************************************
+ * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see //www.gnu.org/licenses/>. *
+ */
+
+package com.projectswg.holocore.resources.support.data.server_info.mongodb
+
+import com.mongodb.client.MongoCollection
+import com.mongodb.client.model.Filters
+import com.mongodb.client.model.IndexOptions
+import com.mongodb.client.model.Indexes
+import org.bson.Document
+import java.util.*
+
+class PswgConfigDatabase(private val collection: MongoCollection){
+
+ init {
+ collection.createIndex(Indexes.ascending("package"), IndexOptions().unique(true))
+ }
+
+ fun getString(o: Any, key: String, def: String): String {
+ for (config in getConfigurations(o)) {
+ if (config.containsKey(key))
+ return config.getString(key)
+ }
+ return def
+ }
+
+ fun getBoolean(o: Any, key: String, def: Boolean): Boolean {
+ for (config in getConfigurations(o)) {
+ if (config.containsKey(key))
+ return config.getBoolean(key)!!
+ }
+ return def
+ }
+
+ fun getInt(o: Any, key: String, def: Int): Int {
+ for (config in getConfigurations(o)) {
+ if (config.containsKey(key))
+ return config.getInteger(key)!!
+ }
+ return def
+ }
+
+ fun getDouble(o: Any, key: String, def: Double): Double {
+ for (config in getConfigurations(o)) {
+ if (config.containsKey(key))
+ return config.getDouble(key)!!
+ }
+ return def
+ }
+
+ fun getLong(o: Any, key: String, def: Long): Double {
+ for (config in getConfigurations(o)) {
+ if (config.containsKey(key))
+ return config.getLong(key)!!.toDouble()
+ }
+ return def.toDouble()
+ }
+
+ private fun getConfigurations(o: Any): List {
+ var packageKey = if (o is Class<*>) o.packageName else o.javaClass.packageName
+ require(packageKey.startsWith("com.projectswg.holocore")) { "packageKey must be a part of holocore, was: $packageKey" }
+
+ packageKey = packageKey.removePrefix("com.projectswg.holocore")
+ packageKey = packageKey.removePrefix(".")
+
+ if (packageKey.startsWith("intents."))
+ throw IllegalArgumentException("intents should not be querying configs")
+
+ if (packageKey.startsWith("resources.") || packageKey.startsWith("services."))
+ packageKey = packageKey.substringAfter('.')
+
+ val configs = ArrayList()
+
+ while (packageKey.isNotEmpty()) {
+ val doc = collection.find(Filters.eq("package", packageKey)).first()
+ if (doc != null)
+ configs.add(doc)
+
+ if (!packageKey.contains('.'))
+ break
+ packageKey = packageKey.substringBeforeLast('.')
+ }
+ return configs
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabase.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabase.java
deleted file mode 100644
index 16e3ecdf..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabase.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.projectswg.holocore.resources.support.data.server_info.mongodb;
-
-import com.mongodb.client.MongoClient;
-import com.mongodb.client.MongoClients;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoDatabase;
-import me.joshlarson.jlcommon.log.Log;
-import org.bson.Document;
-
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.Logger;
-
-public interface PswgDatabase {
-
- default void open(MongoCollection collection) {
-
- }
-
- static void initialize(String connectionString, String databaseName) {
- setupMongoLogging();
- MongoClient client = MongoClients.create(connectionString);
- MongoDatabase database = client.getDatabase(databaseName);
- for (PswgDatabaseImpl db : PswgDatabaseImpl.values()) {
- db.open(database);
- }
- }
-
- static PswgConfigDatabase config() {
- return (PswgConfigDatabase) PswgDatabaseImpl.CONFIG.getImplementation();
- }
-
- static PswgUserDatabase users() {
- return (PswgUserDatabase) PswgDatabaseImpl.USERS.getImplementation();
- }
-
- static PswgObjectDatabase objects() {
- return (PswgObjectDatabase) PswgDatabaseImpl.OBJECTS.getImplementation();
- }
-
- static PswgResourceDatabase resources() {
- return (PswgResourceDatabase) PswgDatabaseImpl.RESOURCES.getImplementation();
- }
-
- private static void setupMongoLogging() {
- Logger mongoLogger = Logger.getLogger("com.mongodb");
- while (mongoLogger != null) {
- for (Handler handler : mongoLogger.getHandlers()) {
- mongoLogger.removeHandler(handler);
- }
- if (mongoLogger.getParent() != null)
- mongoLogger = mongoLogger.getParent();
- else
- break;
- }
- if (mongoLogger != null) {
- mongoLogger.addHandler(new Handler() {
- public void publish(LogRecord record) {
- Level level = record.getLevel();
- if (level.equals(Level.INFO))
- Log.i("MongoDB: %s", record.getMessage());
- else if (level.equals(Level.WARNING))
- Log.w("MongoDB: %s", record.getMessage());
- else if (level.equals(Level.SEVERE))
- Log.e("MongoDB: %s", record.getMessage());
- else
- Log.t("MongoDB: %s", record.getMessage());
- }
- public void flush() { }
- public void close() throws SecurityException { }
- });
- }
- }
-
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabase.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabase.kt
new file mode 100644
index 00000000..2c2e2793
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabase.kt
@@ -0,0 +1,64 @@
+package com.projectswg.holocore.resources.support.data.server_info.mongodb
+
+import com.mongodb.WriteConcern
+import com.mongodb.client.MongoClients
+import me.joshlarson.jlcommon.log.Log
+import org.jetbrains.annotations.NotNull
+import java.util.logging.Handler
+import java.util.logging.Level
+import java.util.logging.LogRecord
+import java.util.logging.Logger
+
+object PswgDatabase {
+
+ var config: PswgConfigDatabase? = null
+ @NotNull get() = field!!
+ private set
+ var users: PswgUserDatabase? = null
+ @NotNull get() = field!!
+ private set
+ var objects: PswgObjectDatabase? = null
+ @NotNull get() = field!!
+ private set
+ var resources: PswgResourceDatabase? = null
+ @NotNull get() = field!!
+ private set
+
+ fun initialize(connectionString: String, databaseName: String) {
+ setupMongoLogging()
+ val client = MongoClients.create(connectionString)
+ val database = client.getDatabase(databaseName)
+
+ config = PswgConfigDatabase(database.getCollection("config").withWriteConcern(WriteConcern.ACKNOWLEDGED))
+ users = PswgUserDatabase(database.getCollection("users").withWriteConcern(WriteConcern.ACKNOWLEDGED))
+ objects = PswgObjectDatabase(database.getCollection("objects").withWriteConcern(WriteConcern.JOURNALED))
+ resources = PswgResourceDatabase(database.getCollection("resources").withWriteConcern(WriteConcern.ACKNOWLEDGED))
+ }
+
+ private fun setupMongoLogging() {
+ var mongoLogger: Logger? = Logger.getLogger("com.mongodb")
+ while (mongoLogger != null) {
+ for (handler in mongoLogger.handlers) {
+ mongoLogger.removeHandler(handler)
+ }
+ if (mongoLogger.parent != null)
+ mongoLogger = mongoLogger.parent
+ else
+ break
+ }
+ mongoLogger?.addHandler(object : Handler() {
+ override fun publish(record: LogRecord) {
+ when (record.level) {
+ Level.INFO -> Log.i("MongoDB: %s", record.message)
+ Level.WARNING -> Log.w("MongoDB: %s", record.message)
+ Level.SEVERE -> Log.e("MongoDB: %s", record.message)
+ else -> Log.t("MongoDB: %s", record.message)
+ }
+ }
+
+ override fun flush() {}
+ override fun close() {}
+ })
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabaseImpl.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabaseImpl.java
deleted file mode 100644
index 5b3e181c..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgDatabaseImpl.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/***********************************************************************************
- * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of Holocore. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * Holocore is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * Holocore is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
-
-package com.projectswg.holocore.resources.support.data.server_info.mongodb;
-
-import com.mongodb.WriteConcern;
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.MongoDatabase;
-import org.bson.Document;
-
-enum PswgDatabaseImpl {
- CONFIG ("config", WriteConcern.ACKNOWLEDGED, new PswgConfigDatabase()),
- USERS ("users", WriteConcern.ACKNOWLEDGED, new PswgUserDatabase()),
- OBJECTS ("objects", WriteConcern.JOURNALED, new PswgObjectDatabase()),
- RESOURCES ("resources", WriteConcern.ACKNOWLEDGED, new PswgResourceDatabase());
-
- private final String collectionName;
- private final WriteConcern writeConcern;
- private final PswgDatabase implementation;
-
- PswgDatabaseImpl(String collectionName, WriteConcern writeConcern, PswgDatabase implementation) {
- this.collectionName = collectionName;
- this.writeConcern = writeConcern;
- this.implementation = implementation;
- }
-
- void open(MongoDatabase database) {
- MongoCollection collection = database.getCollection(collectionName).withWriteConcern(writeConcern);
- implementation.open(collection);
- }
-
- PswgDatabase getImplementation() {
- return implementation;
- }
-
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgObjectDatabase.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgObjectDatabase.java
deleted file mode 100644
index fa536a48..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgObjectDatabase.java
+++ /dev/null
@@ -1,102 +0,0 @@
-/***********************************************************************************
- * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of Holocore. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * Holocore is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * Holocore is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
-
-package com.projectswg.holocore.resources.support.data.server_info.mongodb;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.model.*;
-import com.projectswg.common.data.encodables.mongo.MongoData;
-import com.projectswg.holocore.resources.support.data.persistable.SWGObjectFactory;
-import com.projectswg.holocore.resources.support.objects.swg.SWGObject;
-import org.bson.Document;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.regex.Pattern;
-import java.util.stream.Collectors;
-
-public class PswgObjectDatabase implements PswgDatabase {
-
- private MongoCollection collection;
-
- PswgObjectDatabase() {
- this.collection = null;
- }
-
- @Override
- public void open(MongoCollection collection) {
- this.collection = collection;
- collection.createIndex(Indexes.ascending("id"), new IndexOptions().unique(true));
- }
-
- @NotNull
- public MongoCollection getCollection() {
- return collection;
- }
-
- public void addObject(@NotNull SWGObject obj) {
- collection.replaceOne(Filters.eq("id", obj.getObjectId()), SWGObjectFactory.save(obj, new MongoData()).toDocument(), new ReplaceOptions().upsert(true));
- }
-
- public void addObjects(@NotNull Collection objects) {
- collection.bulkWrite(objects.stream()
- .map(obj -> new ReplaceOneModel<>(
- Filters.eq("id", obj.getObjectId()),
- SWGObjectFactory.save(obj).toDocument(),
- new ReplaceOptions().upsert(true)
- ))
- .collect(Collectors.toList()),
- new BulkWriteOptions().ordered(false));
- }
-
- public boolean removeObject(long id) {
- return collection.deleteOne(Filters.eq("id", id)).getDeletedCount() > 0;
- }
-
- public int getCharacterCount(String account) {
- return (int) collection.countDocuments(Filters.eq("account", account));
- }
-
- public boolean isCharacter(String firstName) {
- return collection.countDocuments(Filters.and(
- Filters.regex("template", "object/creature/player/shared_.+\\.iff"),
- Filters.regex("base3.objectName", Pattern.compile(Pattern.quote(firstName) + "( .+|$)", Pattern.CASE_INSENSITIVE))
- )) > 0;
- }
-
- public long clearObjects() {
- return collection.deleteMany(Filters.exists("_id")).getDeletedCount();
- }
-
- @NotNull
- public List getObjects() {
- return collection.find().map(MongoData::new).into(new ArrayList<>());
- }
-
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgObjectDatabase.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgObjectDatabase.kt
new file mode 100644
index 00000000..5b7be130
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgObjectDatabase.kt
@@ -0,0 +1,87 @@
+/***********************************************************************************
+ * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see //www.gnu.org/licenses/>. *
+ */
+
+package com.projectswg.holocore.resources.support.data.server_info.mongodb
+
+import com.mongodb.client.MongoCollection
+import com.mongodb.client.model.*
+import com.projectswg.common.data.encodables.mongo.MongoData
+import com.projectswg.holocore.resources.support.data.persistable.SWGObjectFactory
+import com.projectswg.holocore.resources.support.objects.swg.SWGObject
+import org.bson.Document
+import java.util.*
+import java.util.regex.Pattern
+import java.util.stream.Collectors.toList
+
+class PswgObjectDatabase(private val collection: MongoCollection) {
+
+ val objects: List
+ get() = collection.find().map { MongoData(it) }.into(ArrayList())
+
+ init {
+ collection.createIndex(Indexes.ascending("id"), IndexOptions().unique(true))
+ }
+
+ fun addObject(obj: SWGObject) {
+ collection.replaceOne(Filters.eq("id", obj.objectId), SWGObjectFactory.save(obj, MongoData()).toDocument(), ReplaceOptions().upsert(true))
+ }
+
+ fun addObjects(objects: Collection) {
+ if (objects.isEmpty())
+ return
+ collection.bulkWrite(objects.stream()
+ .map { obj ->
+ ReplaceOneModel(
+ Filters.eq("id", obj.objectId),
+ SWGObjectFactory.save(obj).toDocument(),
+ ReplaceOptions().upsert(true)
+ )
+ }
+ .collect(toList()),
+ BulkWriteOptions().ordered(false))
+ }
+
+ fun removeObject(id: Long): Boolean {
+ return collection.deleteOne(Filters.eq("id", id)).deletedCount > 0
+ }
+
+ fun getCharacterCount(account: String): Int {
+ return collection.countDocuments(Filters.eq("account", account)).toInt()
+ }
+
+ fun isCharacter(firstName: String): Boolean {
+ return collection.countDocuments(Filters.and(
+ Filters.regex("template", "object/creature/player/shared_.+\\.iff"),
+ Filters.regex("base3.objectName", Pattern.compile(Pattern.quote(firstName) + "( .+|$)", Pattern.CASE_INSENSITIVE))
+ )) > 0
+ }
+
+ fun clearObjects(): Long {
+ return collection.deleteMany(Filters.exists("_id")).deletedCount
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgResourceDatabase.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgResourceDatabase.java
deleted file mode 100644
index 33af0187..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgResourceDatabase.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/***********************************************************************************
- * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of Holocore. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * Holocore is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * Holocore is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
-
-package com.projectswg.holocore.resources.support.data.server_info.mongodb;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.model.*;
-import com.projectswg.common.data.encodables.mongo.MongoData;
-import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.GalacticResource;
-import org.bson.Document;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import static java.util.stream.Collectors.toList;
-
-public class PswgResourceDatabase implements PswgDatabase {
-
- private MongoCollection collection;
-
- PswgResourceDatabase() {
- this.collection = null;
- }
-
- @Override
- public void open(MongoCollection collection) {
- this.collection = collection;
- collection.createIndex(Indexes.ascending("id"), new IndexOptions().unique(true));
- }
-
- public void addResource(@NotNull GalacticResource resource) {
- collection.replaceOne(Filters.eq("id", resource.getId()), MongoData.store(resource).toDocument(), new ReplaceOptions().upsert(true));
- }
-
- public void addResources(@NotNull Collection resources) {
- collection.bulkWrite(resources.stream()
- .map(resource -> new ReplaceOneModel<>(
- Filters.eq("id", resource.getId()),
- MongoData.store(resource).toDocument(),
- new ReplaceOptions().upsert(true)
- ))
- .collect(toList()),
- new BulkWriteOptions().ordered(false));
- }
-
- @NotNull
- public List getResources() {
- return collection.find().map(MongoData::new).map(data -> MongoData.create(data, GalacticResource::new)).into(new ArrayList<>());
- }
-
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgResourceDatabase.kt b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgResourceDatabase.kt
new file mode 100644
index 00000000..1afbf8b4
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgResourceDatabase.kt
@@ -0,0 +1,59 @@
+/***********************************************************************************
+ * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see //www.gnu.org/licenses/>. *
+ */
+
+package com.projectswg.holocore.resources.support.data.server_info.mongodb
+
+import com.mongodb.client.MongoCollection
+import com.mongodb.client.model.*
+import com.projectswg.common.data.encodables.mongo.MongoData
+import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.GalacticResource
+import org.bson.Document
+import java.util.*
+import java.util.stream.Collectors.toList
+
+class PswgResourceDatabase(private val collection: MongoCollection) {
+
+ var resources: List
+ get() = collection.find().map { MongoData.create(it) { GalacticResource() } }.into(ArrayList())
+ set(value) {
+ collection.bulkWrite(value.stream()
+ .map { resource ->
+ ReplaceOneModel(
+ Filters.eq("id", resource.id), // match the resource id
+ MongoData.store(resource).toDocument(), // store the resource into a mongodb document
+ ReplaceOptions().upsert(true) // replace any matches with the new resource
+ )
+ }
+ .collect(toList()),
+ BulkWriteOptions().ordered(false))
+ }
+
+ init {
+ collection.createIndex(Indexes.ascending("id"), IndexOptions().unique(true))
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgUserDatabase.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgUserDatabase.java
deleted file mode 100644
index 5d1a72ae..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgUserDatabase.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/***********************************************************************************
- * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of Holocore. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * Holocore is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * Holocore is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
-
-package com.projectswg.holocore.resources.support.data.server_info.mongodb;
-
-import com.mongodb.client.MongoCollection;
-import com.mongodb.client.model.Filters;
-import com.mongodb.client.model.IndexOptions;
-import com.mongodb.client.model.Indexes;
-import org.bson.Document;
-import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
-
-public class PswgUserDatabase implements PswgDatabase {
-
- private MongoCollection collection;
-
- PswgUserDatabase() {
- this.collection = null;
- }
-
- @Override
- public void open(MongoCollection collection) {
- this.collection = collection;
- collection.createIndex(Indexes.ascending("username"), new IndexOptions().unique(true));
- }
-
- @Nullable
- public UserMetadata getUser(@NotNull String username) {
- return collection.find(Filters.eq("username", username)).map(UserMetadata::new).first();
- }
-
- public static class UserMetadata {
-
- private final String accountId;
- private final String username;
- private final String password;
- private final String accessLevel;
- private final boolean banned;
-
- public UserMetadata(Document doc) {
- this.accountId = doc.getObjectId("_id").toHexString();
- this.username = doc.getString("username");
- this.password = doc.getString("password");
- this.accessLevel = doc.getString("accessLevel");
- this.banned = doc.getBoolean("banned");
- }
-
- public String getAccountId() {
- return accountId;
- }
-
- public String getUsername() {
- return username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public String getAccessLevel() {
- return accessLevel;
- }
-
- public boolean isBanned() {
- return banned;
- }
-
- }
-
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/network/AdminNetworkClient.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgUserDatabase.kt
similarity index 50%
rename from src/main/java/com/projectswg/holocore/resources/support/global/network/AdminNetworkClient.java
rename to src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgUserDatabase.kt
index 3187a054..498ea366 100644
--- a/src/main/java/com/projectswg/holocore/resources/support/global/network/AdminNetworkClient.java
+++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/mongodb/PswgUserDatabase.kt
@@ -1,52 +1,63 @@
/***********************************************************************************
- * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
- * *
+ * Copyright (c) 2019 /// Project SWG /// www.projectswg.com *
+ * *
* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
* Our goal is to create an emulator which will provide a server for players to *
* continue playing a game similar to the one they used to play. We are basing *
* it on the final publish of the game prior to end-game events. *
- * *
+ * *
* This file is part of Holocore. *
- * *
+ * *
* --------------------------------------------------------------------------------*
- * *
+ * *
* Holocore is free software: you can redistribute it and/or modify *
* it under the terms of the GNU Affero General Public License as *
* published by the Free Software Foundation, either version 3 of the *
* License, or (at your option) any later version. *
- * *
+ * *
* Holocore is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU Affero General Public License for more details. *
- * *
+ * *
* You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
+ * along with Holocore. If not, see //www.gnu.org/licenses/>. *
+ */
-package com.projectswg.holocore.resources.support.global.network;
+package com.projectswg.holocore.resources.support.data.server_info.mongodb
-import com.projectswg.common.network.packets.SWGPacket;
-import me.joshlarson.jlcommon.concurrency.ThreadPool;
+import com.mongodb.client.MongoCollection
+import com.mongodb.client.model.Filters
+import com.mongodb.client.model.IndexOptions
+import com.mongodb.client.model.Indexes
+import org.bson.Document
-import javax.net.ssl.SSLContext;
-import java.nio.channels.SocketChannel;
-import java.util.Collection;
-
-public class AdminNetworkClient extends NetworkClient {
+class PswgUserDatabase(private val collection: MongoCollection) {
- public AdminNetworkClient(SocketChannel socket, SSLContext sslContext, ThreadPool securityExecutor, Collection flushList) {
- super(socket, sslContext, securityExecutor, flushList);
+ init {
+ collection.createIndex(Indexes.ascending("username"), IndexOptions().unique(true))
}
- @Override
- protected boolean allowInbound(SWGPacket packet) {
- return true;
+ fun getUser(username: String): UserMetadata? {
+ return collection
+ .find(Filters.eq("username", username))
+ .map { UserMetadata(it) }
+ .first()
}
- @Override
- protected boolean allowOutbound(SWGPacket packet) {
- return true;
- }
+}
+
+class UserMetadata(doc: Document) {
+
+ val accountId: String = doc.getObjectId("_id").toHexString()
+ val username: String = doc.getString("username")
+ val password: String = doc.getString("password")
+ val accessLevel: String = doc.getString("accessLevel")
+ val isBanned: Boolean = doc.getBoolean("banned")
+
+ override fun toString(): String = "UserMetadata[username=$username accessLevel=$accessLevel banned=$isBanned]"
+ override fun equals(other: Any?): Boolean = if (other is UserMetadata) accountId == other.accountId else false
+ override fun hashCode(): Int = accountId.hashCode()
+
}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/network/NetworkClient.java b/src/main/java/com/projectswg/holocore/resources/support/global/network/NetworkClient.java
deleted file mode 100644
index 71b695d6..00000000
--- a/src/main/java/com/projectswg/holocore/resources/support/global/network/NetworkClient.java
+++ /dev/null
@@ -1,326 +0,0 @@
-/***********************************************************************************
- * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of Holocore. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * Holocore is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * Holocore is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with Holocore. If not, see . *
- ***********************************************************************************/
-package com.projectswg.holocore.resources.support.global.network;
-
-import com.projectswg.common.network.NetBuffer;
-import com.projectswg.common.network.NetworkProtocol;
-import com.projectswg.common.network.packets.SWGPacket;
-import com.projectswg.common.network.packets.swg.ErrorMessage;
-import com.projectswg.common.network.packets.swg.admin.AdminPacket;
-import com.projectswg.common.network.packets.swg.holo.HoloConnectionStarted;
-import com.projectswg.common.network.packets.swg.holo.HoloConnectionStopped;
-import com.projectswg.common.network.packets.swg.holo.HoloConnectionStopped.ConnectionStoppedReason;
-import com.projectswg.common.network.packets.swg.holo.HoloSetProtocolVersion;
-import com.projectswg.holocore.intents.support.global.network.ConnectionClosedIntent;
-import com.projectswg.holocore.intents.support.global.network.ConnectionOpenedIntent;
-import com.projectswg.holocore.intents.support.global.network.InboundPacketIntent;
-import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
-import com.projectswg.holocore.resources.support.global.player.Player;
-import me.joshlarson.jlcommon.concurrency.ThreadPool;
-import me.joshlarson.jlcommon.control.IntentChain;
-import me.joshlarson.jlcommon.log.Log;
-import me.joshlarson.jlcommon.network.SSLEngineWrapper.SSLClosedException;
-import me.joshlarson.jlcommon.network.TCPServer.SecureTCPSession;
-import org.jetbrains.annotations.NotNull;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLEngine;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.nio.BufferOverflowException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ClosedChannelException;
-import java.nio.channels.SocketChannel;
-import java.util.Collection;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-public class NetworkClient extends SecureTCPSession {
-
- private static final String[] ENABLED_CIPHERS = new String[] {
- "TLS_RSA_WITH_AES_256_GCM_SHA384",
- "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"
- };
- private static final int INBOUND_BUFFER = 1024;
- private static final int OUTBOUND_BUFFER = 8192;
-
- private final Collection flushList;
- private final Queue outboundPackets;
- private final IntentChain intentChain;
- private final AtomicBoolean connected;
- private final AtomicReference status;
- private final ByteBuffer outboundBuffer;
- private final NetBuffer buffer;
- private final Player player;
-
- public NetworkClient(SocketChannel socket, SSLContext sslContext, ThreadPool securityExecutor, Collection flushList) {
- super(socket, createEngine(sslContext), 1024, securityExecutor::execute);
- //noinspection AssignmentOrReturnOfFieldWithMutableType
- this.flushList = flushList;
- this.outboundPackets = new ConcurrentLinkedQueue<>();
-
- this.intentChain = new IntentChain();
- this.connected = new AtomicBoolean(true);
- this.status = new AtomicReference<>(SessionStatus.DISCONNECTED);
- this.player = new Player(getSessionId(), (InetSocketAddress) getRemoteAddress(), this::addToOutbound);
- this.outboundBuffer = ByteBuffer.allocate(OUTBOUND_BUFFER);
- this.buffer = NetBuffer.allocateDirect(INBOUND_BUFFER);
- }
-
- @Override
- public void close() {
- if (connected.getAndSet(false)) {
- addToOutbound(new HoloConnectionStopped(ConnectionStoppedReason.OTHER_SIDE_TERMINATED));
- try {
- flushPipeline();
- } catch (Throwable t) {
- // Super must be called - this was just a best effort to flush
- }
- super.close();
- }
- }
-
- public void close(ConnectionStoppedReason reason) {
- if (connected.getAndSet(false)) {
- addToOutbound(new HoloConnectionStopped(reason));
- try {
- flushPipeline();
- } catch (Throwable t) {
- // Super must be called - this was just a best effort to flush
- }
- super.close();
- }
- }
-
- public void addToOutbound(SWGPacket p) {
- if (allowOutbound(p)) {
- outboundPackets.add(NetworkProtocol.encode(p).getBuffer());
- if (outboundPackets.size() >= 128)
- flush();
- }
- }
-
- public void flush() {
- if (outboundPackets.isEmpty())
- return;
- if (!connected.get())
- return;
- try {
- flushPipeline();
- } catch (SSLClosedException | ClosedChannelException e) {
- close();
- } catch (NetworkClientException e) {
- StandardLog.onPlayerError(this, player, e.getMessage());
- close(ConnectionStoppedReason.NETWORK);
- } catch (IOException e) {
- StandardLog.onPlayerError(this, player, "lost connection. %s: %s", e.getClass().getName(), e.getMessage());
- close();
- } catch (Throwable t) {
- StandardLog.onPlayerError(this, player, "encountered a network exception. %s: %s", t.getClass().getName(), t.getMessage());
- close();
- }
- }
-
- private void flushPipeline() throws IOException {
- synchronized (outboundBuffer) {
- flushToBuffer();
- flushToSecurity();
- }
- }
-
- private void flushToBuffer() throws IOException {
- ByteBuffer packet;
- while ((packet = outboundPackets.poll()) != null) {
- if (packet.remaining() > outboundBuffer.remaining())
- flushToSecurity();
-
- if (packet.remaining() > outboundBuffer.remaining()) {
- if (outboundBuffer.position() > 0) {
- // Failed to flush earlier and there's nowhere else to put the data
- throw new NetworkClientException("failed to flush data to network security");
- }
- int packetSize = packet.remaining();
- writeToChannel(packet);
- if (packet.hasRemaining())
- throw new NetworkClientException("failed to flush data to network security - packet too big: " + packetSize);
- } else {
- outboundBuffer.put(packet);
- }
- }
- }
-
- private void flushToSecurity() throws IOException {
- if (outboundBuffer.position() > 0) {
- outboundBuffer.flip();
- try {
- writeToChannel(outboundBuffer);
- } catch (BufferOverflowException e) {
- throw new NetworkClientException("failed to flush data to network security - buffer overflow");
- }
- outboundBuffer.compact();
- assert outboundBuffer.position() == 0;
- }
- }
-
- @Override
- public String toString() {
- return "NetworkClient[" + getRemoteAddress() + ']';
- }
-
- @Override
- protected void onConnected() {
- StandardLog.onPlayerTrace(this, player, "connected");
- flushList.add(this);
- status.set(SessionStatus.CONNECTING);
- intentChain.broadcastAfter(new ConnectionOpenedIntent(player));
- }
-
- @Override
- protected void onDisconnected() {
- StandardLog.onPlayerTrace(this, player, "disconnected");
- flushList.remove(this);
- intentChain.broadcastAfter(new ConnectionClosedIntent(player, ConnectionStoppedReason.OTHER_SIDE_TERMINATED));
- }
-
- @Override
- protected void onIncomingData(@NotNull ByteBuffer data) {
- synchronized (buffer) {
- if (data.remaining() > buffer.remaining()) {
- StandardLog.onPlayerError(this, player, "Possible hack attempt detected with buffer overflow. Closing connection to %s", getRemoteAddress());
- close(ConnectionStoppedReason.APPLICATION);
- return;
- }
- try {
- buffer.add(data);
- buffer.flip();
- while (NetworkProtocol.canDecode(buffer)) {
- SWGPacket p = NetworkProtocol.decode(buffer);
- if (p == null || !allowInbound(p))
- continue;
- p.setSocketAddress(getRemoteAddress());
- processPacket(p);
- intentChain.broadcastAfter(new InboundPacketIntent(player, p));
- }
- buffer.compact();
- } catch (HolocoreSessionException e) {
- onSessionError(e);
- } catch (IOException e) {
- Log.w("Failed to process inbound packets. IOException: %s", e.getMessage());
- close();
- }
- }
- }
-
- @Override
- protected void onError(Throwable t) {
- StandardLog.onPlayerError(this, player, "encountered exception in networking. %s: %s", t.getClass().getName(), t.getMessage());
- }
-
- protected boolean allowInbound(SWGPacket packet) {
- return !(packet instanceof AdminPacket);
- }
-
- protected boolean allowOutbound(SWGPacket packet) {
- return !(packet instanceof AdminPacket);
- }
-
- private void onSessionError(HolocoreSessionException e) {
- switch (e.getReason()) {
- case NO_PROTOCOL:
- addToOutbound(new ErrorMessage("Network Manager", "Upgrade your launcher!", false));
- addToOutbound(new HoloConnectionStopped(ConnectionStoppedReason.INVALID_PROTOCOL));
- break;
- case PROTOCOL_INVALID:
- addToOutbound(new HoloConnectionStopped(ConnectionStoppedReason.INVALID_PROTOCOL));
- break;
- }
- }
-
- private void processPacket(SWGPacket p) throws HolocoreSessionException {
- switch (p.getPacketType()) {
- case HOLO_SET_PROTOCOL_VERSION:
- if (!((HoloSetProtocolVersion) p).getProtocol().equals(NetworkProtocol.VERSION))
- throw new HolocoreSessionException(SessionExceptionReason.PROTOCOL_INVALID);
-
- status.set(SessionStatus.CONNECTED);
- addToOutbound(new HoloConnectionStarted());
- break;
- case HOLO_CONNECTION_STOPPED:
- close(ConnectionStoppedReason.OTHER_SIDE_TERMINATED);
- break;
- default:
- if (status.get() != SessionStatus.CONNECTED)
- throw new HolocoreSessionException(SessionExceptionReason.NO_PROTOCOL);
- break;
- }
- }
-
- private static SSLEngine createEngine(SSLContext sslContext) {
- SSLEngine engine = sslContext.createSSLEngine();
- engine.setUseClientMode(false);
- engine.setNeedClientAuth(false);
- engine.setEnabledCipherSuites(ENABLED_CIPHERS);
- return engine;
- }
-
- private enum SessionStatus {
- DISCONNECTED,
- CONNECTING,
- CONNECTED
-
- }
-
- private enum SessionExceptionReason {
- NO_PROTOCOL,
- PROTOCOL_INVALID
- }
-
- private static class HolocoreSessionException extends Exception {
-
- private final SessionExceptionReason reason;
-
- public HolocoreSessionException(SessionExceptionReason reason) {
- this.reason = reason;
- }
-
- public SessionExceptionReason getReason() {
- return reason;
- }
-
- }
-
- private static class NetworkClientException extends IOException {
-
- public NetworkClientException(String message) {
- super(message);
- }
-
- }
-
-}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/network/NetworkClient.kt b/src/main/java/com/projectswg/holocore/resources/support/global/network/NetworkClient.kt
new file mode 100644
index 00000000..ca18b1a7
--- /dev/null
+++ b/src/main/java/com/projectswg/holocore/resources/support/global/network/NetworkClient.kt
@@ -0,0 +1,220 @@
+/***********************************************************************************
+ * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of Holocore. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * Holocore is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * Holocore is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with Holocore. If not, see //www.gnu.org/licenses/>. *
+ */
+package com.projectswg.holocore.resources.support.global.network
+
+import com.projectswg.common.network.NetBuffer
+import com.projectswg.common.network.NetworkProtocol
+import com.projectswg.common.network.packets.SWGPacket
+import com.projectswg.common.network.packets.swg.ErrorMessage
+import com.projectswg.common.network.packets.swg.admin.AdminPacket
+import com.projectswg.common.network.packets.swg.holo.HoloConnectionStarted
+import com.projectswg.common.network.packets.swg.holo.HoloConnectionStopped
+import com.projectswg.common.network.packets.swg.holo.HoloConnectionStopped.ConnectionStoppedReason
+import com.projectswg.common.network.packets.swg.holo.HoloSetProtocolVersion
+import com.projectswg.holocore.intents.support.global.network.ConnectionClosedIntent
+import com.projectswg.holocore.intents.support.global.network.ConnectionOpenedIntent
+import com.projectswg.holocore.intents.support.global.network.InboundPacketIntent
+import com.projectswg.holocore.resources.support.data.server_info.StandardLog
+import com.projectswg.holocore.resources.support.global.player.AccessLevel
+import com.projectswg.holocore.resources.support.global.player.Player
+import me.joshlarson.jlcommon.concurrency.Delay
+import me.joshlarson.jlcommon.control.IntentChain
+import me.joshlarson.jlcommon.log.Log
+import java.io.IOException
+import java.net.InetSocketAddress
+import java.net.SocketAddress
+import java.nio.ByteBuffer
+import java.nio.channels.SocketChannel
+import java.util.concurrent.atomic.AtomicBoolean
+import java.util.concurrent.atomic.AtomicLong
+import java.util.concurrent.atomic.AtomicReference
+import java.util.function.Consumer
+
+class NetworkClient(private val socket: SocketChannel) {
+
+ private val remoteAddress: SocketAddress? = try { socket.remoteAddress } catch (e: IOException) { null }
+ private val inboundBuffer: NetBuffer
+ private val intentChain: IntentChain
+ private val connected: AtomicBoolean
+ private val status: AtomicReference
+ val player: Player
+
+ val id: Long
+ get() = player.networkId
+
+ init {
+ this.inboundBuffer = NetBuffer.allocate(INBOUND_BUFFER_SIZE)
+
+ this.intentChain = IntentChain()
+ this.connected = AtomicBoolean(true)
+ this.status = AtomicReference(SessionStatus.DISCONNECTED)
+ this.player = Player(SESSION_ID.getAndIncrement(), remoteAddress as InetSocketAddress?, Consumer { this.addToOutbound(it) })
+
+ onConnecting()
+ }
+
+ @JvmOverloads
+ fun close(reason: ConnectionStoppedReason = ConnectionStoppedReason.OTHER_SIDE_TERMINATED) {
+ if (connected.getAndSet(false)) {
+ try {
+ socket.write(NetworkProtocol.encode(HoloConnectionStopped(reason)).buffer)
+ } catch (e: IOException) {
+ // Ignored - just give it a best effort
+ }
+ try {
+ socket.close()
+ } catch (e: IOException) {
+ // Ignored
+ }
+
+ onDisconnected()
+ }
+ }
+
+ override fun toString(): String {
+ return "NetworkClient[$remoteAddress]"
+ }
+
+ fun addToInbound(data: ByteBuffer) {
+ synchronized(inboundBuffer) {
+ if (data.remaining() > inboundBuffer.remaining()) {
+ StandardLog.onPlayerError(this, player, "Possible hack attempt detected with buffer overflow. Closing connection to $remoteAddress")
+ close(ConnectionStoppedReason.APPLICATION)
+ return
+ }
+ try {
+ inboundBuffer.add(data)
+ inboundBuffer.flip()
+ while (NetworkProtocol.canDecode(inboundBuffer)) {
+ val p = NetworkProtocol.decode(inboundBuffer)
+ if (p == null || !allowInbound(p))
+ continue
+ p.socketAddress = remoteAddress
+ processPacket(p)
+ intentChain.broadcastAfter(InboundPacketIntent(player, p))
+ }
+ inboundBuffer.compact()
+ } catch (e: HolocoreSessionException) {
+ onSessionError(e)
+ } catch (e: IOException) {
+ Log.w("Failed to process inbound packets. IOException: %s", e.message)
+ close()
+ }
+
+ }
+ }
+
+ private fun addToOutbound(p: SWGPacket) {
+ if (allowOutbound(p) && connected.get()) {
+ synchronized (socket) {
+ try {
+ val buffer = NetworkProtocol.encode(p).buffer
+ while (connected.get() && buffer.hasRemaining()) {
+ socket.write(buffer)
+ if (buffer.hasRemaining())
+ Delay.sleepMilli(1)
+ }
+ } catch (e: IOException) {
+ StandardLog.onPlayerError(this, player, "failed to write network data. ${e.javaClass.name}: ${e.message}")
+ close()
+ }
+ }
+ }
+ }
+
+ private fun onConnecting() {
+ StandardLog.onPlayerTrace(this, player, "connecting")
+ status.set(SessionStatus.CONNECTING)
+ intentChain.broadcastAfter(ConnectionOpenedIntent(player))
+ }
+
+ private fun onConnected() {
+ StandardLog.onPlayerTrace(this, player, "connected")
+ status.set(SessionStatus.CONNECTED)
+ addToOutbound(HoloConnectionStarted())
+ }
+
+ private fun onDisconnected() {
+ StandardLog.onPlayerTrace(this, player, "disconnected")
+ intentChain.broadcastAfter(ConnectionClosedIntent(player, ConnectionStoppedReason.OTHER_SIDE_TERMINATED))
+ }
+
+ private fun allowInbound(packet: SWGPacket): Boolean {
+ return packet !is AdminPacket || player.accessLevel > AccessLevel.WARDEN
+ }
+
+ private fun allowOutbound(packet: SWGPacket): Boolean {
+ return packet !is AdminPacket || player.accessLevel > AccessLevel.WARDEN
+ }
+
+ private fun onSessionError(e: HolocoreSessionException) {
+ when (e.reason) {
+ SessionExceptionReason.NO_PROTOCOL -> {
+ addToOutbound(ErrorMessage("Network Manager", "Upgrade your launcher!", false))
+ addToOutbound(HoloConnectionStopped(ConnectionStoppedReason.INVALID_PROTOCOL))
+ }
+ SessionExceptionReason.PROTOCOL_INVALID -> addToOutbound(HoloConnectionStopped(ConnectionStoppedReason.INVALID_PROTOCOL))
+ }
+ }
+
+ @Throws(HolocoreSessionException::class)
+ private fun processPacket(p: SWGPacket) {
+ when (p) {
+ is HoloSetProtocolVersion -> {
+ if (p.protocol == NetworkProtocol.VERSION)
+ onConnected()
+ else
+ throw HolocoreSessionException(SessionExceptionReason.PROTOCOL_INVALID)
+ }
+ is HoloConnectionStopped -> close(ConnectionStoppedReason.OTHER_SIDE_TERMINATED)
+ else -> {
+ if (status.get() != SessionStatus.CONNECTED)
+ throw HolocoreSessionException(SessionExceptionReason.NO_PROTOCOL)
+ }
+ }
+ }
+
+ private enum class SessionStatus {
+ DISCONNECTED,
+ CONNECTING,
+ CONNECTED
+
+ }
+
+ private enum class SessionExceptionReason {
+ NO_PROTOCOL,
+ PROTOCOL_INVALID
+ }
+
+ private class HolocoreSessionException(val reason: SessionExceptionReason) : Exception()
+
+ companion object {
+ private val SESSION_ID = AtomicLong(1)
+ private const val INBOUND_BUFFER_SIZE = 4096
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/zone/ZoneRequester.java b/src/main/java/com/projectswg/holocore/resources/support/global/zone/ZoneRequester.java
index b5835998..ce366256 100644
--- a/src/main/java/com/projectswg/holocore/resources/support/global/zone/ZoneRequester.java
+++ b/src/main/java/com/projectswg/holocore/resources/support/global/zone/ZoneRequester.java
@@ -87,7 +87,7 @@ public class ZoneRequester {
}
private boolean isSafeZone() {
- return PswgDatabase.config().getBoolean(this, "safeZoneIn", false);
+ return PswgDatabase.INSTANCE.getConfig().getBoolean(this, "safeZoneIn", false);
}
private void sendClientFatal(Player player, String message) {
diff --git a/src/main/java/com/projectswg/holocore/resources/support/npc/ai/NpcCombatMode.java b/src/main/java/com/projectswg/holocore/resources/support/npc/ai/NpcCombatMode.java
index c6a7e22e..18c4a355 100644
--- a/src/main/java/com/projectswg/holocore/resources/support/npc/ai/NpcCombatMode.java
+++ b/src/main/java/com/projectswg/holocore/resources/support/npc/ai/NpcCombatMode.java
@@ -37,7 +37,7 @@ public class NpcCombatMode extends NpcMode {
this.returnLocation = new AtomicReference<>(null);
this.targets = new CopyOnWriteArraySet<>();
this.iteration = new AtomicLong(0);
- this.runSpeed = PswgDatabase.config().getDouble(this, "npcRunSpeed", 9);
+ this.runSpeed = PswgDatabase.INSTANCE.getConfig().getDouble(this, "npcRunSpeed", 9);
}
@Override
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/combat/loot/GrantLootService.java b/src/main/java/com/projectswg/holocore/services/gameplay/combat/loot/GrantLootService.java
index 2321af8e..791d6bb6 100644
--- a/src/main/java/com/projectswg/holocore/services/gameplay/combat/loot/GrantLootService.java
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/combat/loot/GrantLootService.java
@@ -70,7 +70,7 @@ public final class GrantLootService extends Service {
public GrantLootService() {
this.lootRestrictions = new ConcurrentHashMap<>();
this.executor = new ScheduledThreadPool(1, "grant-loot-service");
- this.lootRange = PswgDatabase.config().getInt(this, "lootRange", 64);
+ this.lootRange = PswgDatabase.INSTANCE.getConfig().getInt(this, "lootRange", 64);
}
@Override
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/combat/loot/LootGenerationService.java b/src/main/java/com/projectswg/holocore/services/gameplay/combat/loot/LootGenerationService.java
index 01afa8c5..fcee61fc 100644
--- a/src/main/java/com/projectswg/holocore/services/gameplay/combat/loot/LootGenerationService.java
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/combat/loot/LootGenerationService.java
@@ -88,9 +88,9 @@ public final class LootGenerationService extends Service {
boolean cashGenerated = false;
boolean lootGenerated = false;
- if (PswgDatabase.config().getBoolean(this, "cashLoot", true))
+ if (PswgDatabase.INSTANCE.getConfig().getBoolean(this, "cashLoot", true))
cashGenerated = generateCreditChip(loot, lootInventory, corpse.getDifficulty(), corpse.getLevel());
- if (PswgDatabase.config().getBoolean(this, "itemLoot", true))
+ if (PswgDatabase.INSTANCE.getConfig().getBoolean(this, "itemLoot", true))
lootGenerated = generateLoot(loot, killer, lootInventory);
if (!cashGenerated && !lootGenerated)
@@ -209,25 +209,25 @@ public final class LootGenerationService extends Service {
ThreadLocalRandom random = ThreadLocalRandom.current();
- double range = PswgDatabase.config().getDouble(this, "lootCashHumanRange", 0.05);
+ double range = PswgDatabase.INSTANCE.getConfig().getDouble(this, "lootCashHumanRange", 0.05);
double cashLootRoll = random.nextDouble();
int credits;
switch (difficulty) {
default:
case NORMAL:
- if (cashLootRoll > PswgDatabase.config().getDouble(this, "lootCashHumanNormalChance", 0.60))
+ if (cashLootRoll > PswgDatabase.INSTANCE.getConfig().getDouble(this, "lootCashHumanNormalChance", 0.60))
return false;
- credits = PswgDatabase.config().getInt(this, "lootCashHumanNormal", 2);
+ credits = PswgDatabase.INSTANCE.getConfig().getInt(this, "lootCashHumanNormal", 2);
break;
case ELITE:
- if (cashLootRoll > PswgDatabase.config().getDouble(this, "lootCashHumanElitechance", 0.80))
+ if (cashLootRoll > PswgDatabase.INSTANCE.getConfig().getDouble(this, "lootCashHumanElitechance", 0.80))
return false;
- credits = PswgDatabase.config().getInt(this, "lootCashHumanElite", 5);
+ credits = PswgDatabase.INSTANCE.getConfig().getInt(this, "lootCashHumanElite", 5);
break;
case BOSS:
// bosses always drop cash loot, so no need to check
- credits = PswgDatabase.config().getInt(this, "lootCashHumanBoss", 9);
+ credits = PswgDatabase.INSTANCE.getConfig().getInt(this, "lootCashHumanBoss", 9);
break;
}
credits *= combatLevel;
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/ExperienceLevelService.java b/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/ExperienceLevelService.java
index ae384183..5f448bae 100644
--- a/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/ExperienceLevelService.java
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/player/experience/ExperienceLevelService.java
@@ -24,7 +24,7 @@ public class ExperienceLevelService extends Service {
private final double xpMultiplier;
public ExperienceLevelService() {
- xpMultiplier = PswgDatabase.config().getDouble(this, "xpMultiplier", 1);
+ xpMultiplier = PswgDatabase.INSTANCE.getConfig().getDouble(this, "xpMultiplier", 1);
}
@IntentHandler
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/world/travel/PlayerMountService.java b/src/main/java/com/projectswg/holocore/services/gameplay/world/travel/PlayerMountService.java
index 1b63bd2f..91cbbb2f 100644
--- a/src/main/java/com/projectswg/holocore/services/gameplay/world/travel/PlayerMountService.java
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/world/travel/PlayerMountService.java
@@ -1,475 +1,475 @@
-/***********************************************************************************
- * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
- * *
- * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
- * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
- * Our goal is to create an emulator which will provide a server for players to *
- * continue playing a game similar to the one they used to play. We are basing *
- * it on the final publish of the game prior to end-game events. *
- * *
- * This file is part of PSWGCommon. *
- * *
- * --------------------------------------------------------------------------------*
- * *
- * PSWGCommon is free software: you can redistribute it and/or modify *
- * it under the terms of the GNU Affero General Public License as *
- * published by the Free Software Foundation, either version 3 of the *
- * License, or (at your option) any later version. *
- * *
- * PSWGCommon is distributed in the hope that it will be useful, *
- * but WITHOUT ANY WARRANTY; without even the implied warranty of *
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
- * GNU Affero General Public License for more details. *
- * *
- * You should have received a copy of the GNU Affero General Public License *
- * along with PSWGCommon. If not, see . *
- ***********************************************************************************/
-package com.projectswg.holocore.services.gameplay.world.travel;
-
-import com.projectswg.common.data.encodables.tangible.Posture;
-import com.projectswg.common.network.packets.swg.zone.PlayClientEffectObjectMessage;
-import com.projectswg.holocore.intents.gameplay.combat.CreatureIncapacitatedIntent;
-import com.projectswg.holocore.intents.gameplay.combat.CreatureKilledIntent;
-import com.projectswg.holocore.intents.gameplay.combat.buffs.BuffIntent;
-import com.projectswg.holocore.intents.gameplay.world.travel.pet.*;
-import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent;
-import com.projectswg.holocore.intents.support.global.command.ExecuteCommandIntent;
-import com.projectswg.holocore.intents.support.global.network.CloseConnectionIntent;
-import com.projectswg.holocore.intents.support.global.zone.PlayerEventIntent;
-import com.projectswg.holocore.intents.support.global.zone.PlayerTransformedIntent;
-import com.projectswg.holocore.intents.support.objects.swg.DestroyObjectIntent;
-import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent;
-import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
-import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader;
-import com.projectswg.holocore.resources.support.data.server_info.loader.VehicleLoader.VehicleInfo;
-import com.projectswg.holocore.resources.support.data.server_info.mongodb.PswgDatabase;
-import com.projectswg.holocore.resources.support.global.network.DisconnectReason;
-import com.projectswg.holocore.resources.support.objects.ObjectCreator;
-import com.projectswg.holocore.resources.support.objects.swg.SWGObject;
-import com.projectswg.holocore.resources.support.objects.swg.ServerAttribute;
-import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject;
-import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureState;
-import com.projectswg.holocore.resources.support.objects.swg.group.GroupObject;
-import com.projectswg.holocore.resources.support.objects.swg.intangible.IntangibleObject;
-import com.projectswg.holocore.resources.support.objects.swg.tangible.OptionFlag;
-import com.projectswg.holocore.services.support.objects.ObjectStorageService.ObjectLookup;
-import me.joshlarson.jlcommon.control.IntentHandler;
-import me.joshlarson.jlcommon.control.Service;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-public class PlayerMountService extends Service {
-
- // TODO no-vehicle zones (does not affect creature mounts)
-
- private final Map> calledMounts;
-
- public PlayerMountService() {
- this.calledMounts = new ConcurrentHashMap<>();
- }
-
- @Override
- public boolean stop() {
- globallyStorePets(); // Don't want mounts out and about when server starts up again
-
- return true;
- }
-
- String mobileForVehicleDeed(String deedTemplate) {
- assert deedTemplate.startsWith("object/tangible/deed/") : "invalid vehicle deed";
- return deedTemplate.replace("_deed", "").replace("tangible/deed", "mobile");
- }
-
- String pcdForVehicleDeed(String deedTemplate) {
- assert deedTemplate.startsWith("object/tangible/deed/") : "invalid vehicle deed";
- return deedTemplate.replace("tangible/deed/vehicle_deed", "intangible/vehicle").replace("deed", "pcd");
- }
-
- @IntentHandler
- private void handleCreatureIncapacitatedIntent(CreatureIncapacitatedIntent cii) {
- CreatureObject player = cii.getIncappee();
- if (player.isPlayer())
- exitMount(player);
- }
-
- @IntentHandler
- private void handleCreatureKilledIntent(CreatureKilledIntent cki) {
- CreatureObject player = cki.getCorpse();
- if (player.isPlayer())
- exitMount(player);
- }
-
- @IntentHandler
- private void handleExecuteCommandIntent(ExecuteCommandIntent eci) {
- String cmdName = eci.getCommand().getName();
- SWGObject target = eci.getTarget();
-
- if (!(target instanceof CreatureObject))
- return;
-
- if (cmdName.equals("mount"))
- enterMount(eci.getSource(), (CreatureObject) target);
- else if (cmdName.equals("dismount"))
- exitMount(eci.getSource(), (CreatureObject) target);
- }
-
- @IntentHandler
- private void handlePlayerTransformedIntent(PlayerTransformedIntent pti) {
- CreatureObject player = pti.getPlayer();
- Set mounts = calledMounts.get(player);
- if (mounts == null)
- return;
-
- for (Mount m : mounts) {
- if (!player.getAware().contains(m.getMount()))
- storeMount(player, m.getMount(), m.getPetControlDevice());
- }
- }
-
- @IntentHandler
- private void handleMount(MountIntent pmi) {
- enterMount(pmi.getCreature(), pmi.getPet());
- }
-
- @IntentHandler
- private void handleDismount(DismountIntent pmi) {
- exitMount(pmi.getCreature(), pmi.getPet());
- }
-
- @IntentHandler
- private void handlePetDeviceCall(PetDeviceCallIntent pci) {
- callMount(pci.getCreature(), pci.getControlDevice());
- }
-
- @IntentHandler
- private void handlePetStore(StoreMountIntent psi) {
- CreatureObject creature = psi.getCreature();
- CreatureObject mount = psi.getPet();
-
- Collection mounts = calledMounts.get(creature);
- if (mounts != null) {
- for (Mount p : mounts) {
- if (p.getMount() == mount) {
- storeMount(creature, mount, p.getPetControlDevice());
- return;
- }
- }
- }
- SystemMessageIntent.broadcastPersonal(psi.getCreature().getOwner(), "Could not find mount to store!");
- }
-
- @IntentHandler
- private void handlePetDeviceStore(PetDeviceStoreIntent psi) {
- CreatureObject creature = psi.getCreature();
- IntangibleObject pcd = psi.getControlDevice();
-
- Collection mounts = calledMounts.get(creature);
- if (mounts != null) {
- for (Mount mount : mounts) {
- if (mount.getPetControlDevice() == pcd) {
- storeMount(creature, mount.getMount(), pcd);
- return;
- }
- }
- }
- pcd.setCount(IntangibleObject.COUNT_PCD_STORED);
- }
-
- @IntentHandler
- private void handleVehicleDeedGenerate(VehicleDeedGenerateIntent vdgi) {
- if (!vdgi.getDeed().getTemplate().startsWith("object/tangible/deed/vehicle_deed/")) {
- SystemMessageIntent.broadcastPersonal(vdgi.getCreature().getOwner(), "Invalid vehicle deed!");
- return;
- }
- generateVehicle(vdgi.getCreature(), vdgi.getDeed());
- }
-
- @IntentHandler
- private void handlePlayerEventIntent(PlayerEventIntent pei) {
- CreatureObject creature = pei.getPlayer().getCreatureObject();
- if (creature == null)
- return;
- switch (pei.getEvent()) {
- case PE_LOGGED_OUT:
- case PE_DISAPPEAR:
- case PE_DESTROYED:
- case PE_SERVER_KICKED:
- storeMounts(creature);
- break;
- default:
- break;
- }
- }
-
- private void generateVehicle(CreatureObject creator, SWGObject deed) {
- String pcdTemplate = pcdForVehicleDeed(deed.getTemplate());
- VehicleInfo vehicleInfo = DataLoader.vehicles().getVehicleFromPcdIff(pcdTemplate);
- if (vehicleInfo == null) {
- StandardLog.onPlayerError(this, creator, "Unknown vehicle created from deed: %s", deed.getTemplate());
- return;
- }
- IntangibleObject vehicleControlDevice = (IntangibleObject) ObjectCreator.createObjectFromTemplate(pcdTemplate);
-
- DestroyObjectIntent.broadcast(deed);
-
- vehicleControlDevice.setServerAttribute(ServerAttribute.PCD_PET_TEMPLATE, vehicleInfo.getObjectTemplate());
- vehicleControlDevice.setCount(IntangibleObject.COUNT_PCD_STORED);
- vehicleControlDevice.moveToContainer(creator.getDatapad());
- ObjectCreatedIntent.broadcast(vehicleControlDevice);
-
- callMount(creator, vehicleControlDevice); // Once generated, the vehicle is called
- }
-
- private void callMount(CreatureObject player, IntangibleObject mountControlDevice) {
- if (player.getParent() != null || player.isInCombat()) {
- return;
- }
- if (player.getDatapad() != mountControlDevice.getParent()) {
- StandardLog.onPlayerError(this, player, "disconnecting - attempted to call another player's mount [%s]", mountControlDevice);
- CloseConnectionIntent.broadcast(player.getOwner(), DisconnectReason.SUSPECTED_HACK);
- return;
- }
-
- String template = mountControlDevice.getServerTextAttribute(ServerAttribute.PCD_PET_TEMPLATE);
- assert template != null : "mount control device doesn't have mount template attribute";
- CreatureObject mount = (CreatureObject) ObjectCreator.createObjectFromTemplate(template);
- mount.systemMove(null, player.getLocation());
- mount.addOptionFlags(OptionFlag.MOUNT); // The mount won't appear properly if this isn't set
- mount.setPvpFaction(player.getPvpFaction());
- mount.setOwnerId(player.getObjectId()); // Client crash if this isn't set before making anyone aware
-
- // TODO after combat there's a delay
- // TODO update faction status on mount if necessary
-
- Mount mountRecord = new Mount(mountControlDevice, mount);
- Set mounts = calledMounts.computeIfAbsent(player, c -> new CopyOnWriteArraySet<>());
- if (mountControlDevice.getCount() == IntangibleObject.COUNT_PCD_CALLED || !mounts.add(mountRecord)) {
- StandardLog.onPlayerTrace(this, player, "already called mount %s", mount);
- return;
- }
- if (mounts.size() > getMountLimit()) {
- mounts.remove(mountRecord);
- StandardLog.onPlayerTrace(this, player, "hit mount limit of %d", getMountLimit());
- return;
- }
- mountControlDevice.setCount(IntangibleObject.COUNT_PCD_CALLED);
- VehicleInfo vehicleInfo = DataLoader.vehicles().getVehicleFromIff(mount.getTemplate());
- if (vehicleInfo != null) {
- mount.setRunSpeed(vehicleInfo.getSpeed());
- mount.setWalkSpeed(vehicleInfo.getMinSpeed());
- mount.setTurnScale(vehicleInfo.getTurnRateMax());
- mount.setAccelScale(vehicleInfo.getAccelMax());
- mount.putCustomization("/private/index_speed_max", (int) (vehicleInfo.getSpeed() * 10d));
- mount.putCustomization("/private/index_speed_min", (int) (vehicleInfo.getMinSpeed() * 10d));
- mount.putCustomization("/private/index_turn_rate_min", vehicleInfo.getTurnRate());
- mount.putCustomization("/private/index_turn_rate_max", vehicleInfo.getTurnRateMax());
- mount.putCustomization("/private/index_accel_min", (int) (vehicleInfo.getAccelMin() * 10d));
- mount.putCustomization("/private/index_accel_max", (int) (vehicleInfo.getAccelMax() * 10d));
- mount.putCustomization("/private/index_decel", (int) (vehicleInfo.getDecel() * 10d));
- mount.putCustomization("/private/index_damp_roll", (int) (vehicleInfo.getDampingRoll() * 10d));
- mount.putCustomization("/private/index_damp_pitch", (int) (vehicleInfo.getDampingPitch() * 10d));
- mount.putCustomization("/private/index_damp_height", (int) (vehicleInfo.getDampingHeight() * 10d));
- mount.putCustomization("/private/index_glide", (int) (vehicleInfo.getGlide() * 10d));
- mount.putCustomization("/private/index_banking", (int) vehicleInfo.getBankingAngle());
- mount.putCustomization("/private/index_hover_height", (int) (vehicleInfo.getHoverHeight() * 10d));
- mount.putCustomization("/private/index_auto_level", (int) (vehicleInfo.getAutoLevel() * 100d));
- mount.putCustomization("/private/index_strafe", vehicleInfo.isStrafe() ? 1 : 0);
- }
-
- ObjectCreatedIntent.broadcast(mount);
- StandardLog.onPlayerTrace(this, player, "called mount %s at %s %s", mount, mount.getTerrain(), mount.getLocation().getPosition());
- cleanupCalledMounts();
- }
-
- private void enterMount(CreatureObject player, CreatureObject mount) {
- if (!isMountable(mount) || mount.getParent() != null) {
- StandardLog.onPlayerTrace(this, player, "attempted to mount %s when it's not mountable", mount);
- return;
- }
-
- if (player.getParent() != null || player.getPosture() != Posture.UPRIGHT)
- return;
-
- if (player.getObjectId() == mount.getOwnerId()) {
- player.moveToSlot(mount, "rider", mount.getArrangementId(player));
- } else if (mount.getSlottedObject("rider") != null) {
- GroupObject group = (GroupObject) ObjectLookup.getObjectById(player.getGroupId());
- if (group == null || !group.getGroupMembers().values().contains(mount.getOwnerId())) {
- StandardLog.onPlayerTrace(this, player, "attempted to mount %s when not in the same group as the owner", mount);
- return;
- }
- boolean added = false;
- for (int i = 1; i <= 7; i++) {
- if (!mount.hasSlot("rider" + i)) {
- StandardLog.onPlayerTrace(this, player, "attempted to mount %s when no slots remain", mount);
- return;
- }
- if (mount.getSlottedObject("rider" + i) == null) {
- player.moveToSlot(mount, "rider" + i, mount.getArrangementId(player));
- added = true;
- break;
- }
- }
- if (!added) {
- StandardLog.onPlayerTrace(this, player, "attempted to mount %s when no slots remain", mount);
- return;
- }
- } else {
- return;
- }
-
- player.setStatesBitmask(CreatureState.RIDING_MOUNT);
- mount.setStatesBitmask(CreatureState.MOUNTED_CREATURE);
- mount.setPosture(Posture.DRIVING_VEHICLE);
-
- VehicleInfo vehicleInfo = DataLoader.vehicles().getVehicleFromIff(mount.getTemplate());
- if (vehicleInfo != null) {
- if (!vehicleInfo.getPlayerBuff().isEmpty())
- BuffIntent.broadcast(vehicleInfo.getPlayerBuff(), player, player, false);
- if (!vehicleInfo.getVehicleBuff().isEmpty())
- BuffIntent.broadcast(vehicleInfo.getVehicleBuff(), player, mount, false);
- if (!vehicleInfo.getBuffClientEffect().isEmpty())
- player.sendObservers(new PlayClientEffectObjectMessage(vehicleInfo.getBuffClientEffect(), "", mount.getObjectId(), ""));
- }
-
- player.inheritMovement(mount);
- StandardLog.onPlayerEvent(this, player, "mounted %s", mount);
- }
-
- private void exitMount(CreatureObject player) {
- if (!player.isStatesBitmask(CreatureState.RIDING_MOUNT))
- return;
- SWGObject parent = player.getParent();
- if (!(parent instanceof CreatureObject))
- return;
- CreatureObject mount = (CreatureObject) parent;
- if (isMountable(mount) && mount.isStatesBitmask(CreatureState.MOUNTED_CREATURE))
- exitMount(player, mount);
- }
-
- private void exitMount(CreatureObject player, CreatureObject mount) {
- if (!isMountable(mount)) {
- StandardLog.onPlayerTrace(this, player, "attempted to dismount %s when it's not mountable", mount);
- return;
- }
-
-
- VehicleInfo vehicleInfo = DataLoader.vehicles().getVehicleFromIff(mount.getTemplate());
- if (player.getParent() == mount)
- dismount(player, mount, vehicleInfo);
-
- if (mount.getSlottedObject("rider") == null) {
- for (SWGObject child : mount.getSlottedObjects()) {
- assert child instanceof CreatureObject;
- dismount((CreatureObject) child, mount, vehicleInfo);
- }
- if (vehicleInfo != null && !vehicleInfo.getVehicleBuff().isEmpty())
- BuffIntent.broadcast(vehicleInfo.getVehicleBuff(), player, mount, true);
- mount.clearStatesBitmask(CreatureState.MOUNTED_CREATURE);
- mount.setPosture(Posture.UPRIGHT);
- }
- }
-
- private void storeMount(@NotNull CreatureObject player, @NotNull CreatureObject mount, @NotNull IntangibleObject mountControlDevice) {
- if (player.isInCombat())
- return;
-
- if (isMountable(mount) && mount.getOwnerId() == player.getObjectId()) {
- // Dismount anyone riding the mount about to be stored
- for (SWGObject rider : mount.getSlottedObjects()) {
- assert rider instanceof CreatureObject;
- exitMount((CreatureObject) rider, mount);
- }
- assert mount.getSlottedObjects().isEmpty();
-
- // Remove the record of the mount
- Collection mounts = calledMounts.get(player);
- if (mounts != null)
- mounts.remove(new Mount(mountControlDevice, mount));
-
- // Destroy the mount
- mountControlDevice.setCount(IntangibleObject.COUNT_PCD_STORED);
- player.broadcast(new DestroyObjectIntent(mount));
- StandardLog.onPlayerTrace(this, player, "stored mount %s at %s %s", mount, mount.getTerrain(), mount.getLocation().getPosition());
- }
- cleanupCalledMounts();
- }
-
- private void storeMounts(CreatureObject player) {
- Collection mounts = calledMounts.remove(player);
- if (mounts == null)
- return;
-
- for (Mount mount : mounts) {
- storeMount(player, mount.getMount(), mount.getPetControlDevice());
- }
- }
-
- /**
- * Stores all called mounts by all players
- */
- private void globallyStorePets() {
- calledMounts.keySet().forEach(this::storeMounts);
- }
-
- /**
- * Removes mount records where there are no mounts called
- */
- private void cleanupCalledMounts() {
- calledMounts.entrySet().removeIf(e -> e.getValue().isEmpty());
- }
-
- private void dismount(CreatureObject player, CreatureObject mount, VehicleInfo vehicleInfo) {
- assert player.getParent() == mount;
- player.clearStatesBitmask(CreatureState.RIDING_MOUNT);
- player.moveToContainer(null, mount.getLocation());
- player.resetMovement();
- if (vehicleInfo != null && !vehicleInfo.getPlayerBuff().isEmpty())
- BuffIntent.broadcast(vehicleInfo.getPlayerBuff(), player, mount, true);
- StandardLog.onPlayerEvent(this, player, "dismounted %s", mount);
- }
-
- private int getMountLimit() {
- return PswgDatabase.config().getInt(this, "mountLimit", 1);
- }
-
- private static boolean isMountable(CreatureObject mount) {
- return mount.hasOptionFlags(OptionFlag.MOUNT);
- }
-
- private static class Mount {
-
- private final IntangibleObject mountControlDevice;
- private final CreatureObject mount;
-
- private Mount(IntangibleObject mountControlDevice, CreatureObject mount) {
- this.mountControlDevice = mountControlDevice;
- this.mount = mount;
- }
-
- public IntangibleObject getPetControlDevice() {
- return mountControlDevice;
- }
-
- public CreatureObject getMount() {
- return mount;
- }
-
- @Override
- public int hashCode() {
- return mountControlDevice.hashCode();
- }
-
- @Override
- public boolean equals(Object o) {
- return o instanceof Mount && ((Mount) o).mountControlDevice.equals(mountControlDevice);
- }
-
- }
-
-}
+/***********************************************************************************
+ * Copyright (c) 2018 /// Project SWG /// www.projectswg.com *
+ * *
+ * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on *
+ * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. *
+ * Our goal is to create an emulator which will provide a server for players to *
+ * continue playing a game similar to the one they used to play. We are basing *
+ * it on the final publish of the game prior to end-game events. *
+ * *
+ * This file is part of PSWGCommon. *
+ * *
+ * --------------------------------------------------------------------------------*
+ * *
+ * PSWGCommon is free software: you can redistribute it and/or modify *
+ * it under the terms of the GNU Affero General Public License as *
+ * published by the Free Software Foundation, either version 3 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * PSWGCommon is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU Affero General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU Affero General Public License *
+ * along with PSWGCommon. If not, see . *
+ ***********************************************************************************/
+package com.projectswg.holocore.services.gameplay.world.travel;
+
+import com.projectswg.common.data.encodables.tangible.Posture;
+import com.projectswg.common.network.packets.swg.zone.PlayClientEffectObjectMessage;
+import com.projectswg.holocore.intents.gameplay.combat.CreatureIncapacitatedIntent;
+import com.projectswg.holocore.intents.gameplay.combat.CreatureKilledIntent;
+import com.projectswg.holocore.intents.gameplay.combat.buffs.BuffIntent;
+import com.projectswg.holocore.intents.gameplay.world.travel.pet.*;
+import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent;
+import com.projectswg.holocore.intents.support.global.command.ExecuteCommandIntent;
+import com.projectswg.holocore.intents.support.global.network.CloseConnectionIntent;
+import com.projectswg.holocore.intents.support.global.zone.PlayerEventIntent;
+import com.projectswg.holocore.intents.support.global.zone.PlayerTransformedIntent;
+import com.projectswg.holocore.intents.support.objects.swg.DestroyObjectIntent;
+import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent;
+import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
+import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader;
+import com.projectswg.holocore.resources.support.data.server_info.loader.VehicleLoader.VehicleInfo;
+import com.projectswg.holocore.resources.support.data.server_info.mongodb.PswgDatabase;
+import com.projectswg.holocore.resources.support.global.network.DisconnectReason;
+import com.projectswg.holocore.resources.support.objects.ObjectCreator;
+import com.projectswg.holocore.resources.support.objects.swg.SWGObject;
+import com.projectswg.holocore.resources.support.objects.swg.ServerAttribute;
+import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject;
+import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureState;
+import com.projectswg.holocore.resources.support.objects.swg.group.GroupObject;
+import com.projectswg.holocore.resources.support.objects.swg.intangible.IntangibleObject;
+import com.projectswg.holocore.resources.support.objects.swg.tangible.OptionFlag;
+import com.projectswg.holocore.services.support.objects.ObjectStorageService.ObjectLookup;
+import me.joshlarson.jlcommon.control.IntentHandler;
+import me.joshlarson.jlcommon.control.Service;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+public class PlayerMountService extends Service {
+
+ // TODO no-vehicle zones (does not affect creature mounts)
+
+ private final Map> calledMounts;
+
+ public PlayerMountService() {
+ this.calledMounts = new ConcurrentHashMap<>();
+ }
+
+ @Override
+ public boolean stop() {
+ globallyStorePets(); // Don't want mounts out and about when server starts up again
+
+ return true;
+ }
+
+ String mobileForVehicleDeed(String deedTemplate) {
+ assert deedTemplate.startsWith("object/tangible/deed/") : "invalid vehicle deed";
+ return deedTemplate.replace("_deed", "").replace("tangible/deed", "mobile");
+ }
+
+ String pcdForVehicleDeed(String deedTemplate) {
+ assert deedTemplate.startsWith("object/tangible/deed/") : "invalid vehicle deed";
+ return deedTemplate.replace("tangible/deed/vehicle_deed", "intangible/vehicle").replace("deed", "pcd");
+ }
+
+ @IntentHandler
+ private void handleCreatureIncapacitatedIntent(CreatureIncapacitatedIntent cii) {
+ CreatureObject player = cii.getIncappee();
+ if (player.isPlayer())
+ exitMount(player);
+ }
+
+ @IntentHandler
+ private void handleCreatureKilledIntent(CreatureKilledIntent cki) {
+ CreatureObject player = cki.getCorpse();
+ if (player.isPlayer())
+ exitMount(player);
+ }
+
+ @IntentHandler
+ private void handleExecuteCommandIntent(ExecuteCommandIntent eci) {
+ String cmdName = eci.getCommand().getName();
+ SWGObject target = eci.getTarget();
+
+ if (!(target instanceof CreatureObject))
+ return;
+
+ if (cmdName.equals("mount"))
+ enterMount(eci.getSource(), (CreatureObject) target);
+ else if (cmdName.equals("dismount"))
+ exitMount(eci.getSource(), (CreatureObject) target);
+ }
+
+ @IntentHandler
+ private void handlePlayerTransformedIntent(PlayerTransformedIntent pti) {
+ CreatureObject player = pti.getPlayer();
+ Set mounts = calledMounts.get(player);
+ if (mounts == null)
+ return;
+
+ for (Mount m : mounts) {
+ if (!player.getAware().contains(m.getMount()))
+ storeMount(player, m.getMount(), m.getPetControlDevice());
+ }
+ }
+
+ @IntentHandler
+ private void handleMount(MountIntent pmi) {
+ enterMount(pmi.getCreature(), pmi.getPet());
+ }
+
+ @IntentHandler
+ private void handleDismount(DismountIntent pmi) {
+ exitMount(pmi.getCreature(), pmi.getPet());
+ }
+
+ @IntentHandler
+ private void handlePetDeviceCall(PetDeviceCallIntent pci) {
+ callMount(pci.getCreature(), pci.getControlDevice());
+ }
+
+ @IntentHandler
+ private void handlePetStore(StoreMountIntent psi) {
+ CreatureObject creature = psi.getCreature();
+ CreatureObject mount = psi.getPet();
+
+ Collection mounts = calledMounts.get(creature);
+ if (mounts != null) {
+ for (Mount p : mounts) {
+ if (p.getMount() == mount) {
+ storeMount(creature, mount, p.getPetControlDevice());
+ return;
+ }
+ }
+ }
+ SystemMessageIntent.broadcastPersonal(psi.getCreature().getOwner(), "Could not find mount to store!");
+ }
+
+ @IntentHandler
+ private void handlePetDeviceStore(PetDeviceStoreIntent psi) {
+ CreatureObject creature = psi.getCreature();
+ IntangibleObject pcd = psi.getControlDevice();
+
+ Collection mounts = calledMounts.get(creature);
+ if (mounts != null) {
+ for (Mount mount : mounts) {
+ if (mount.getPetControlDevice() == pcd) {
+ storeMount(creature, mount.getMount(), pcd);
+ return;
+ }
+ }
+ }
+ pcd.setCount(IntangibleObject.COUNT_PCD_STORED);
+ }
+
+ @IntentHandler
+ private void handleVehicleDeedGenerate(VehicleDeedGenerateIntent vdgi) {
+ if (!vdgi.getDeed().getTemplate().startsWith("object/tangible/deed/vehicle_deed/")) {
+ SystemMessageIntent.broadcastPersonal(vdgi.getCreature().getOwner(), "Invalid vehicle deed!");
+ return;
+ }
+ generateVehicle(vdgi.getCreature(), vdgi.getDeed());
+ }
+
+ @IntentHandler
+ private void handlePlayerEventIntent(PlayerEventIntent pei) {
+ CreatureObject creature = pei.getPlayer().getCreatureObject();
+ if (creature == null)
+ return;
+ switch (pei.getEvent()) {
+ case PE_LOGGED_OUT:
+ case PE_DISAPPEAR:
+ case PE_DESTROYED:
+ case PE_SERVER_KICKED:
+ storeMounts(creature);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void generateVehicle(CreatureObject creator, SWGObject deed) {
+ String pcdTemplate = pcdForVehicleDeed(deed.getTemplate());
+ VehicleInfo vehicleInfo = DataLoader.vehicles().getVehicleFromPcdIff(pcdTemplate);
+ if (vehicleInfo == null) {
+ StandardLog.onPlayerError(this, creator, "Unknown vehicle created from deed: %s", deed.getTemplate());
+ return;
+ }
+ IntangibleObject vehicleControlDevice = (IntangibleObject) ObjectCreator.createObjectFromTemplate(pcdTemplate);
+
+ DestroyObjectIntent.broadcast(deed);
+
+ vehicleControlDevice.setServerAttribute(ServerAttribute.PCD_PET_TEMPLATE, vehicleInfo.getObjectTemplate());
+ vehicleControlDevice.setCount(IntangibleObject.COUNT_PCD_STORED);
+ vehicleControlDevice.moveToContainer(creator.getDatapad());
+ ObjectCreatedIntent.broadcast(vehicleControlDevice);
+
+ callMount(creator, vehicleControlDevice); // Once generated, the vehicle is called
+ }
+
+ private void callMount(CreatureObject player, IntangibleObject mountControlDevice) {
+ if (player.getParent() != null || player.isInCombat()) {
+ return;
+ }
+ if (player.getDatapad() != mountControlDevice.getParent()) {
+ StandardLog.onPlayerError(this, player, "disconnecting - attempted to call another player's mount [%s]", mountControlDevice);
+ CloseConnectionIntent.broadcast(player.getOwner(), DisconnectReason.SUSPECTED_HACK);
+ return;
+ }
+
+ String template = mountControlDevice.getServerTextAttribute(ServerAttribute.PCD_PET_TEMPLATE);
+ assert template != null : "mount control device doesn't have mount template attribute";
+ CreatureObject mount = (CreatureObject) ObjectCreator.createObjectFromTemplate(template);
+ mount.systemMove(null, player.getLocation());
+ mount.addOptionFlags(OptionFlag.MOUNT); // The mount won't appear properly if this isn't set
+ mount.setPvpFaction(player.getPvpFaction());
+ mount.setOwnerId(player.getObjectId()); // Client crash if this isn't set before making anyone aware
+
+ // TODO after combat there's a delay
+ // TODO update faction status on mount if necessary
+
+ Mount mountRecord = new Mount(mountControlDevice, mount);
+ Set mounts = calledMounts.computeIfAbsent(player, c -> new CopyOnWriteArraySet<>());
+ if (mountControlDevice.getCount() == IntangibleObject.COUNT_PCD_CALLED || !mounts.add(mountRecord)) {
+ StandardLog.onPlayerTrace(this, player, "already called mount %s", mount);
+ return;
+ }
+ if (mounts.size() > getMountLimit()) {
+ mounts.remove(mountRecord);
+ StandardLog.onPlayerTrace(this, player, "hit mount limit of %d", getMountLimit());
+ return;
+ }
+ mountControlDevice.setCount(IntangibleObject.COUNT_PCD_CALLED);
+ VehicleInfo vehicleInfo = DataLoader.vehicles().getVehicleFromIff(mount.getTemplate());
+ if (vehicleInfo != null) {
+ mount.setRunSpeed(vehicleInfo.getSpeed());
+ mount.setWalkSpeed(vehicleInfo.getMinSpeed());
+ mount.setTurnScale(vehicleInfo.getTurnRateMax());
+ mount.setAccelScale(vehicleInfo.getAccelMax());
+ mount.putCustomization("/private/index_speed_max", (int) (vehicleInfo.getSpeed() * 10d));
+ mount.putCustomization("/private/index_speed_min", (int) (vehicleInfo.getMinSpeed() * 10d));
+ mount.putCustomization("/private/index_turn_rate_min", vehicleInfo.getTurnRate());
+ mount.putCustomization("/private/index_turn_rate_max", vehicleInfo.getTurnRateMax());
+ mount.putCustomization("/private/index_accel_min", (int) (vehicleInfo.getAccelMin() * 10d));
+ mount.putCustomization("/private/index_accel_max", (int) (vehicleInfo.getAccelMax() * 10d));
+ mount.putCustomization("/private/index_decel", (int) (vehicleInfo.getDecel() * 10d));
+ mount.putCustomization("/private/index_damp_roll", (int) (vehicleInfo.getDampingRoll() * 10d));
+ mount.putCustomization("/private/index_damp_pitch", (int) (vehicleInfo.getDampingPitch() * 10d));
+ mount.putCustomization("/private/index_damp_height", (int) (vehicleInfo.getDampingHeight() * 10d));
+ mount.putCustomization("/private/index_glide", (int) (vehicleInfo.getGlide() * 10d));
+ mount.putCustomization("/private/index_banking", (int) vehicleInfo.getBankingAngle());
+ mount.putCustomization("/private/index_hover_height", (int) (vehicleInfo.getHoverHeight() * 10d));
+ mount.putCustomization("/private/index_auto_level", (int) (vehicleInfo.getAutoLevel() * 100d));
+ mount.putCustomization("/private/index_strafe", vehicleInfo.isStrafe() ? 1 : 0);
+ }
+
+ ObjectCreatedIntent.broadcast(mount);
+ StandardLog.onPlayerTrace(this, player, "called mount %s at %s %s", mount, mount.getTerrain(), mount.getLocation().getPosition());
+ cleanupCalledMounts();
+ }
+
+ private void enterMount(CreatureObject player, CreatureObject mount) {
+ if (!isMountable(mount) || mount.getParent() != null) {
+ StandardLog.onPlayerTrace(this, player, "attempted to mount %s when it's not mountable", mount);
+ return;
+ }
+
+ if (player.getParent() != null || player.getPosture() != Posture.UPRIGHT)
+ return;
+
+ if (player.getObjectId() == mount.getOwnerId()) {
+ player.moveToSlot(mount, "rider", mount.getArrangementId(player));
+ } else if (mount.getSlottedObject("rider") != null) {
+ GroupObject group = (GroupObject) ObjectLookup.getObjectById(player.getGroupId());
+ if (group == null || !group.getGroupMembers().values().contains(mount.getOwnerId())) {
+ StandardLog.onPlayerTrace(this, player, "attempted to mount %s when not in the same group as the owner", mount);
+ return;
+ }
+ boolean added = false;
+ for (int i = 1; i <= 7; i++) {
+ if (!mount.hasSlot("rider" + i)) {
+ StandardLog.onPlayerTrace(this, player, "attempted to mount %s when no slots remain", mount);
+ return;
+ }
+ if (mount.getSlottedObject("rider" + i) == null) {
+ player.moveToSlot(mount, "rider" + i, mount.getArrangementId(player));
+ added = true;
+ break;
+ }
+ }
+ if (!added) {
+ StandardLog.onPlayerTrace(this, player, "attempted to mount %s when no slots remain", mount);
+ return;
+ }
+ } else {
+ return;
+ }
+
+ player.setStatesBitmask(CreatureState.RIDING_MOUNT);
+ mount.setStatesBitmask(CreatureState.MOUNTED_CREATURE);
+ mount.setPosture(Posture.DRIVING_VEHICLE);
+
+ VehicleInfo vehicleInfo = DataLoader.vehicles().getVehicleFromIff(mount.getTemplate());
+ if (vehicleInfo != null) {
+ if (!vehicleInfo.getPlayerBuff().isEmpty())
+ BuffIntent.broadcast(vehicleInfo.getPlayerBuff(), player, player, false);
+ if (!vehicleInfo.getVehicleBuff().isEmpty())
+ BuffIntent.broadcast(vehicleInfo.getVehicleBuff(), player, mount, false);
+ if (!vehicleInfo.getBuffClientEffect().isEmpty())
+ player.sendObservers(new PlayClientEffectObjectMessage(vehicleInfo.getBuffClientEffect(), "", mount.getObjectId(), ""));
+ }
+
+ player.inheritMovement(mount);
+ StandardLog.onPlayerEvent(this, player, "mounted %s", mount);
+ }
+
+ private void exitMount(CreatureObject player) {
+ if (!player.isStatesBitmask(CreatureState.RIDING_MOUNT))
+ return;
+ SWGObject parent = player.getParent();
+ if (!(parent instanceof CreatureObject))
+ return;
+ CreatureObject mount = (CreatureObject) parent;
+ if (isMountable(mount) && mount.isStatesBitmask(CreatureState.MOUNTED_CREATURE))
+ exitMount(player, mount);
+ }
+
+ private void exitMount(CreatureObject player, CreatureObject mount) {
+ if (!isMountable(mount)) {
+ StandardLog.onPlayerTrace(this, player, "attempted to dismount %s when it's not mountable", mount);
+ return;
+ }
+
+
+ VehicleInfo vehicleInfo = DataLoader.vehicles().getVehicleFromIff(mount.getTemplate());
+ if (player.getParent() == mount)
+ dismount(player, mount, vehicleInfo);
+
+ if (mount.getSlottedObject("rider") == null) {
+ for (SWGObject child : mount.getSlottedObjects()) {
+ assert child instanceof CreatureObject;
+ dismount((CreatureObject) child, mount, vehicleInfo);
+ }
+ if (vehicleInfo != null && !vehicleInfo.getVehicleBuff().isEmpty())
+ BuffIntent.broadcast(vehicleInfo.getVehicleBuff(), player, mount, true);
+ mount.clearStatesBitmask(CreatureState.MOUNTED_CREATURE);
+ mount.setPosture(Posture.UPRIGHT);
+ }
+ }
+
+ private void storeMount(@NotNull CreatureObject player, @NotNull CreatureObject mount, @NotNull IntangibleObject mountControlDevice) {
+ if (player.isInCombat())
+ return;
+
+ if (isMountable(mount) && mount.getOwnerId() == player.getObjectId()) {
+ // Dismount anyone riding the mount about to be stored
+ for (SWGObject rider : mount.getSlottedObjects()) {
+ assert rider instanceof CreatureObject;
+ exitMount((CreatureObject) rider, mount);
+ }
+ assert mount.getSlottedObjects().isEmpty();
+
+ // Remove the record of the mount
+ Collection mounts = calledMounts.get(player);
+ if (mounts != null)
+ mounts.remove(new Mount(mountControlDevice, mount));
+
+ // Destroy the mount
+ mountControlDevice.setCount(IntangibleObject.COUNT_PCD_STORED);
+ player.broadcast(new DestroyObjectIntent(mount));
+ StandardLog.onPlayerTrace(this, player, "stored mount %s at %s %s", mount, mount.getTerrain(), mount.getLocation().getPosition());
+ }
+ cleanupCalledMounts();
+ }
+
+ private void storeMounts(CreatureObject player) {
+ Collection mounts = calledMounts.remove(player);
+ if (mounts == null)
+ return;
+
+ for (Mount mount : mounts) {
+ storeMount(player, mount.getMount(), mount.getPetControlDevice());
+ }
+ }
+
+ /**
+ * Stores all called mounts by all players
+ */
+ private void globallyStorePets() {
+ calledMounts.keySet().forEach(this::storeMounts);
+ }
+
+ /**
+ * Removes mount records where there are no mounts called
+ */
+ private void cleanupCalledMounts() {
+ calledMounts.entrySet().removeIf(e -> e.getValue().isEmpty());
+ }
+
+ private void dismount(CreatureObject player, CreatureObject mount, VehicleInfo vehicleInfo) {
+ assert player.getParent() == mount;
+ player.clearStatesBitmask(CreatureState.RIDING_MOUNT);
+ player.moveToContainer(null, mount.getLocation());
+ player.resetMovement();
+ if (vehicleInfo != null && !vehicleInfo.getPlayerBuff().isEmpty())
+ BuffIntent.broadcast(vehicleInfo.getPlayerBuff(), player, mount, true);
+ StandardLog.onPlayerEvent(this, player, "dismounted %s", mount);
+ }
+
+ private int getMountLimit() {
+ return PswgDatabase.INSTANCE.getConfig().getInt(this, "mountLimit", 1);
+ }
+
+ private static boolean isMountable(CreatureObject mount) {
+ return mount.hasOptionFlags(OptionFlag.MOUNT);
+ }
+
+ private static class Mount {
+
+ private final IntangibleObject mountControlDevice;
+ private final CreatureObject mount;
+
+ private Mount(IntangibleObject mountControlDevice, CreatureObject mount) {
+ this.mountControlDevice = mountControlDevice;
+ this.mount = mount;
+ }
+
+ public IntangibleObject getPetControlDevice() {
+ return mountControlDevice;
+ }
+
+ public CreatureObject getMount() {
+ return mount;
+ }
+
+ @Override
+ public int hashCode() {
+ return mountControlDevice.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return o instanceof Mount && ((Mount) o).mountControlDevice.equals(mountControlDevice);
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/world/travel/TravelService.java b/src/main/java/com/projectswg/holocore/services/gameplay/world/travel/TravelService.java
index 7cad934e..7c756e07 100644
--- a/src/main/java/com/projectswg/holocore/services/gameplay/world/travel/TravelService.java
+++ b/src/main/java/com/projectswg/holocore/services/gameplay/world/travel/TravelService.java
@@ -261,7 +261,7 @@ public class TravelService extends Service {
}
private double getTicketPriceFactor() {
- return PswgDatabase.config().getDouble(this, "ticketPriceFactor", 1);
+ return PswgDatabase.INSTANCE.getConfig().getDouble(this, "ticketPriceFactor", 1);
}
private void sendTravelMessage(CreatureObject creature, String message) {
diff --git a/src/main/java/com/projectswg/holocore/services/support/data/PacketRecordingService.java b/src/main/java/com/projectswg/holocore/services/support/data/PacketRecordingService.java
index a7a9bfa1..ff26a945 100644
--- a/src/main/java/com/projectswg/holocore/services/support/data/PacketRecordingService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/data/PacketRecordingService.java
@@ -36,7 +36,7 @@ public class PacketRecordingService extends Service {
}
private boolean isPacketDebug() {
- return PswgDatabase.config().getBoolean(this, "packetLogging", false);
+ return PswgDatabase.INSTANCE.getConfig().getBoolean(this, "packetLogging", false);
}
}
diff --git a/src/main/java/com/projectswg/holocore/services/support/data/ServerDataService.java b/src/main/java/com/projectswg/holocore/services/support/data/ServerDataService.java
index 1f1987ae..e7a54267 100644
--- a/src/main/java/com/projectswg/holocore/services/support/data/ServerDataService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/data/ServerDataService.java
@@ -12,8 +12,8 @@ public class ServerDataService extends Service {
@Override
public boolean initialize() {
- if (PswgDatabase.config().getBoolean(this, "wipeObjects", false)) {
- Log.d("Cleared %d objects", PswgDatabase.objects().clearObjects());
+ if (PswgDatabase.INSTANCE.getConfig().getBoolean(this, "wipeObjects", false)) {
+ Log.d("Cleared %d objects", PswgDatabase.INSTANCE.getObjects().clearObjects());
}
return super.initialize();
diff --git a/src/main/java/com/projectswg/holocore/services/support/data/dev/DeveloperService.java b/src/main/java/com/projectswg/holocore/services/support/data/dev/DeveloperService.java
index 67e550b8..2f8f57d1 100644
--- a/src/main/java/com/projectswg/holocore/services/support/data/dev/DeveloperService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/data/dev/DeveloperService.java
@@ -47,7 +47,7 @@ public class DeveloperService extends Service {
public boolean start() {
setupDeveloperArea();
- if (PswgDatabase.config().getBoolean(this, "characterBuilder", false))
+ if (PswgDatabase.INSTANCE.getConfig().getBoolean(this, "characterBuilder", false))
setupCharacterBuilders();
return super.start();
diff --git a/src/main/java/com/projectswg/holocore/services/support/global/chat/ChatSpatialService.java b/src/main/java/com/projectswg/holocore/services/support/global/chat/ChatSpatialService.java
index 587b75f2..c07c0daf 100644
--- a/src/main/java/com/projectswg/holocore/services/support/global/chat/ChatSpatialService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/global/chat/ChatSpatialService.java
@@ -14,7 +14,7 @@ public class ChatSpatialService extends Service {
private final int chatRange;
public ChatSpatialService() {
- this.chatRange = PswgDatabase.config().getInt(this, "spatialChatRange", 128);
+ this.chatRange = PswgDatabase.INSTANCE.getConfig().getInt(this, "spatialChatRange", 128);
}
@IntentHandler
diff --git a/src/main/java/com/projectswg/holocore/services/support/global/health/ServerHealthService.java b/src/main/java/com/projectswg/holocore/services/support/global/health/ServerHealthService.java
index 718ae31b..b8da9173 100644
--- a/src/main/java/com/projectswg/holocore/services/support/global/health/ServerHealthService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/global/health/ServerHealthService.java
@@ -32,7 +32,7 @@ public class ServerHealthService extends Service {
this.previousGcTime = new AtomicLong(0);
this.completedInitialIntents = new AtomicBoolean(true);
- if (PswgDatabase.config().getBoolean(this, "performanceLog", false)) {
+ if (PswgDatabase.INSTANCE.getConfig().getBoolean(this, "performanceLog", false)) {
this.performanceOutput = new BasicLogStream(new File("log/performance.txt"));
executor.start();
performanceOutput.log("%s\t%s\t%s\t%s\t%s\t%s", "cpu", "memory-used", "memory-max", "gc-collectionRate", "gc-time", "intents");
diff --git a/src/main/java/com/projectswg/holocore/services/support/global/network/NetworkClientService.java b/src/main/java/com/projectswg/holocore/services/support/global/network/NetworkClientService.java
index 7f627ee4..2b1e1079 100644
--- a/src/main/java/com/projectswg/holocore/services/support/global/network/NetworkClientService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/global/network/NetworkClientService.java
@@ -32,118 +32,82 @@ import com.projectswg.holocore.ProjectSWG;
import com.projectswg.holocore.ProjectSWG.CoreException;
import com.projectswg.holocore.intents.support.global.network.CloseConnectionIntent;
import com.projectswg.holocore.intents.support.global.network.ConnectionClosedIntent;
+import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
import com.projectswg.holocore.resources.support.data.server_info.mongodb.PswgDatabase;
-import com.projectswg.holocore.resources.support.global.network.AdminNetworkClient;
import com.projectswg.holocore.resources.support.global.network.NetworkClient;
import com.projectswg.holocore.resources.support.global.network.UDPServer;
import com.projectswg.holocore.resources.support.global.network.UDPServer.UDPPacket;
-import me.joshlarson.jlcommon.concurrency.ScheduledThreadPool;
-import me.joshlarson.jlcommon.concurrency.ThreadPool;
+import com.projectswg.holocore.resources.support.global.player.Player;
+import me.joshlarson.jlcommon.concurrency.BasicThread;
import me.joshlarson.jlcommon.control.IntentHandler;
import me.joshlarson.jlcommon.control.Service;
import me.joshlarson.jlcommon.log.Log;
-import me.joshlarson.jlcommon.network.TCPServer;
-import javax.net.ssl.KeyManagerFactory;
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.TrustManagerFactory;
-import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.io.InputStream;
-import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
-import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.Selector;
+import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
-import java.security.KeyStore;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
public class NetworkClientService extends Service {
- private final ThreadPool securityExecutor;
- private final TCPServer tcpServer;
- private final TCPServer adminServer;
- private final ScheduledThreadPool networkFlushPeriodic;
- private final List clients;
+ private static final int INBOUND_BUFFER_SIZE = 4096;
+
+ private final ServerSocketChannel tcpServer;
+ private final BasicThread acceptThreadPool;
+ private final Map clients;
+ private final ByteBuffer inboundBuffer;
private final UDPServer udpServer;
- private final SSLContext sslContext;
- private final int flushRate;
+ private volatile boolean operational;
public NetworkClientService() {
- this.flushRate = getFlushRate();
- this.securityExecutor = new ThreadPool(3, "network-client-security-%d");
- this.sslContext = initializeSecurity();
- this.networkFlushPeriodic = new ScheduledThreadPool(1, "network-client-flush");
- this.clients = new CopyOnWriteArrayList<>();
+ this.acceptThreadPool = new BasicThread("network-client-accept", this::acceptLoop);
+ this.clients = new ConcurrentHashMap<>();
+ this.inboundBuffer = ByteBuffer.allocate(INBOUND_BUFFER_SIZE);
+ this.operational = true;
{
int bindPort = getBindPort();
- int bufferSize = getBufferSize();
- tcpServer = TCPServer.builder()
- .setAddr(new InetSocketAddress((InetAddress) null, bindPort))
- .setBufferSize(bufferSize)
- .setSessionCreator(this::createStandardClient)
- .createTCPServer();
try {
- udpServer = new UDPServer(bindPort, bufferSize);
- } catch (SocketException e) {
- throw new CoreException("Socket Exception on UDP bind: " + e);
+ tcpServer = ServerSocketChannel.open();
+ tcpServer.bind(new InetSocketAddress(bindPort), 64);
+ tcpServer.configureBlocking(false);
+ udpServer = new UDPServer(bindPort, 32);
+ } catch (IOException e) {
+ throw new CoreException("Failed to start networking", e);
}
udpServer.setCallback(this::onUdpPacket);
}
- {
- int adminServerPort = ProjectSWG.getGalaxy().getAdminServerPort();
- if (adminServerPort > 0) {
- InetSocketAddress localhost = new InetSocketAddress(InetAddress.getLoopbackAddress(), adminServerPort);
- adminServer = TCPServer.builder()
- .setAddr(localhost)
- .setBufferSize(1024)
- .setSessionCreator(this::createAdminClient)
- .createTCPServer();
- } else {
- adminServer = null;
- }
- }
}
@Override
public boolean start() {
- securityExecutor.start();
- networkFlushPeriodic.start();
- networkFlushPeriodic.executeWithFixedRate(0, 1000 / flushRate, this::flush);
- int bindPort = -1;
- try {
- bindPort = getBindPort();
- tcpServer.bind();
- bindPort = ProjectSWG.getGalaxy().getAdminServerPort();
- if (adminServer != null) {
- adminServer.bind();
- }
- } catch (BindException e) {
- Log.e("Failed to bind to %d", bindPort);
- return false;
- } catch (IOException e) {
- Log.e(e);
- return false;
- }
- return super.start();
+ acceptThreadPool.start();
+ return true;
+ }
+
+ @Override
+ public boolean isOperational() {
+ return operational;
}
@Override
public boolean stop() {
- for (NetworkClient client : tcpServer.getSessions())
+ for (NetworkClient client : clients.values())
client.close(ConnectionStoppedReason.APPLICATION);
- tcpServer.close();
- if (adminServer != null) {
- for (NetworkClient client : adminServer.getSessions())
- client.close(ConnectionStoppedReason.APPLICATION);
- adminServer.close();
+ try {
+ tcpServer.close();
+ } catch (IOException e) {
+ Log.w("Failed to close TCP server");
}
- networkFlushPeriodic.stop();
- securityExecutor.stop(false);
- return securityExecutor.awaitTermination(3000) && networkFlushPeriodic.awaitTermination(1000);
+ acceptThreadPool.stop(true);
+ return acceptThreadPool.awaitTermination(1000);
}
@Override
@@ -152,15 +116,64 @@ public class NetworkClientService extends Service {
return super.terminate();
}
- /**
- * Requests a flush for each network client
- */
- private void flush() {
- clients.forEach(NetworkClient::flush);
+ private void acceptLoop() {
+ try (Selector selector = Selector.open()) {
+ tcpServer.register(selector, SelectionKey.OP_ACCEPT);
+ while (tcpServer.isOpen()) {
+ selector.select();
+ Iterator it = selector.selectedKeys().iterator();
+ while (it.hasNext()) {
+ SelectionKey key = it.next();
+ if (key.isAcceptable()) {
+ acceptConnection(selector);
+ }
+ if (key.isReadable()) {
+ read(key);
+ }
+ it.remove();
+ }
+ }
+ } catch (IOException e) {
+ Log.a(e);
+ } finally {
+ operational = false;
+ }
+ }
+
+ private void acceptConnection(Selector selector) {
+ try {
+ SocketChannel client = tcpServer.accept();
+ if (client != null) {
+ client.configureBlocking(false);
+ NetworkClient networkClient = new NetworkClient(client);
+ client.register(selector, SelectionKey.OP_READ, networkClient);
+ clients.put(networkClient.getId(), networkClient);
+ }
+ } catch (Throwable t) {
+ Log.w("%s: Failed to accept connection", getClass().getSimpleName());
+ }
+ }
+
+ private void read(SelectionKey key) {
+ Player player = null;
+ try {
+ SocketChannel channel = (SocketChannel) key.channel();
+ NetworkClient client = (NetworkClient) key.attachment();
+ player = client.getPlayer();
+ inboundBuffer.clear();
+ channel.read(inboundBuffer);
+ inboundBuffer.flip();
+ client.addToInbound(inboundBuffer);
+ } catch (Throwable t) {
+ if (player != null)
+ StandardLog.onPlayerError(this, player, "failed to read data");
+ else
+ Log.w("%s: Failed to read data", getClass().getSimpleName());
+ }
}
private void disconnect(long networkId) {
- disconnect(getClient(networkId));
+ disconnect(clients.get(networkId));
}
private void disconnect(NetworkClient client) {
@@ -173,9 +186,8 @@ public class NetworkClientService extends Service {
private void onUdpPacket(UDPPacket packet) {
if (packet.getLength() <= 0)
return;
- switch (packet.getData()[0]) {
- case 1: sendState(packet.getAddress(), packet.getPort()); break;
- default: break;
+ if (packet.getData()[0] == 1) {
+ sendState(packet.getAddress(), packet.getPort());
}
}
@@ -187,14 +199,6 @@ public class NetworkClientService extends Service {
udpServer.send(port, addr, data.array());
}
- private NetworkClient createStandardClient(SocketChannel channel) {
- return new NetworkClient(channel, sslContext, securityExecutor, clients);
- }
-
- private AdminNetworkClient createAdminClient(SocketChannel channel) {
- return new AdminNetworkClient(channel, sslContext, securityExecutor, clients);
- }
-
@IntentHandler
private void handleCloseConnectionIntent(CloseConnectionIntent ccii) {
disconnect(ccii.getPlayer().getNetworkId());
@@ -205,56 +209,8 @@ public class NetworkClientService extends Service {
disconnect(cci.getPlayer().getNetworkId());
}
- private NetworkClient getClient(long id) {
- NetworkClient client = tcpServer.getSession(id);
- if (client != null || adminServer == null)
- return client;
- return adminServer.getSession(id);
- }
-
- private SSLContext initializeSecurity() {
- Log.t("Initializing encryption...");
- try {
- File keystoreFile = new File(PswgDatabase.config().getString(this, "keystoreFile", ""));
- InputStream keystoreStream;
- char[] passphrase;
- KeyStore keystore;
- if (!keystoreFile.isFile()) {
- Log.w("Failed to enable security! Keystore file does not exist: %s", keystoreFile);
- keystoreStream = getClass().getResourceAsStream("/security/Holocore.p12");
- passphrase = new char[]{'p', 'a', 's', 's'};
- keystore = KeyStore.getInstance("PKCS12");
- } else {
- keystoreStream = new FileInputStream(keystoreFile);
- passphrase = PswgDatabase.config().getString(this, "keystorePass", "").toCharArray();
- keystore = KeyStore.getInstance(PswgDatabase.config().getString(this, "keystoreType", "PKCS12"));
- }
-
- keystore.load(keystoreStream, passphrase);
- KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
- keyManagerFactory.init(keystore, passphrase);
- TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- trustManagerFactory.init(keystore);
- SSLContext ctx = SSLContext.getInstance("TLSv1.3");
- ctx.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
- Log.i("Enabled TLS encryption");
- return ctx;
- } catch (Exception e) {
- Log.a("Failed to enable security! %s: %s", e.getClass(), e.getMessage());
- throw new RuntimeException("Failed to enable TLS", e);
- }
- }
-
private int getBindPort() {
- return PswgDatabase.config().getInt(this, "bindPort", 44463);
- }
-
- private int getBufferSize() {
- return PswgDatabase.config().getInt(this, "bufferSize", 4096);
- }
-
- private int getFlushRate() {
- return PswgDatabase.config().getInt(this, "flushRate", 10);
+ return PswgDatabase.INSTANCE.getConfig().getInt(this, "bindPort", 44463);
}
}
diff --git a/src/main/java/com/projectswg/holocore/services/support/global/zone/LoginService.java b/src/main/java/com/projectswg/holocore/services/support/global/zone/LoginService.java
index a984fe7f..c0679b97 100644
--- a/src/main/java/com/projectswg/holocore/services/support/global/zone/LoginService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/global/zone/LoginService.java
@@ -49,7 +49,7 @@ import com.projectswg.holocore.intents.support.objects.swg.DestroyObjectIntent;
import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent;
import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
import com.projectswg.holocore.resources.support.data.server_info.mongodb.PswgDatabase;
-import com.projectswg.holocore.resources.support.data.server_info.mongodb.PswgUserDatabase.UserMetadata;
+import com.projectswg.holocore.resources.support.data.server_info.mongodb.UserMetadata;
import com.projectswg.holocore.resources.support.global.network.DisconnectReason;
import com.projectswg.holocore.resources.support.global.player.AccessLevel;
import com.projectswg.holocore.resources.support.global.player.Player;
@@ -94,7 +94,7 @@ public class LoginService extends Service {
@IntentHandler
private void handleDeleteCharacterIntent(DeleteCharacterIntent dci) {
SWGObject obj = dci.getCreature();
- if (PswgDatabase.objects().removeObject(obj.getObjectId())) {
+ if (PswgDatabase.INSTANCE.getObjects().removeObject(obj.getObjectId())) {
DestroyObjectIntent.broadcast(obj);
Player owner = obj.getOwner();
if (owner != null)
@@ -119,8 +119,8 @@ public class LoginService extends Service {
}
private String getServerString() {
- String name = PswgDatabase.config().getString(this, "loginServerName", "LoginServer");
- int id = PswgDatabase.config().getInt(this, "loginServerId", 1);
+ String name = PswgDatabase.INSTANCE.getConfig().getString(this, "loginServerName", "LoginServer");
+ int id = PswgDatabase.INSTANCE.getConfig().getInt(this, "loginServerId", 1);
return name + ':' + id;
}
@@ -134,7 +134,7 @@ public class LoginService extends Service {
player.setPlayerState(PlayerState.LOGGING_IN);
player.setPlayerServer(PlayerServer.LOGIN);
- UserMetadata user = PswgDatabase.users().getUser(loginRequest.getUsername());
+ UserMetadata user = PswgDatabase.INSTANCE.getUsers().getUser(loginRequest.getUsername());
player.setUsername(loginRequest.getUsername());
if (user == null) {
StandardLog.onPlayerEvent(this, player, "failed to login [incorrect username] from %s", loginRequest.getSocketAddress());
@@ -163,7 +163,7 @@ public class LoginService extends Service {
SWGObject obj = ObjectLookup.getObjectById(request.getPlayerId());
boolean success;
if (obj instanceof CreatureObject) {
- success = PswgDatabase.objects().removeObject(obj.getObjectId());
+ success = PswgDatabase.INSTANCE.getObjects().removeObject(obj.getObjectId());
players.getOrDefault(player.getAccountId(), new ArrayList<>()).remove(obj);
} else {
success = false;
@@ -192,14 +192,14 @@ public class LoginService extends Service {
assert player.getPlayerServer() == PlayerServer.NONE;
player.setPlayerState(PlayerState.LOGGING_IN);
player.setPlayerServer(PlayerServer.LOGIN);
- final boolean doClientCheck = PswgDatabase.config().getBoolean(this, "loginVersionChecks", true);
+ final boolean doClientCheck = PswgDatabase.INSTANCE.getConfig().getBoolean(this, "loginVersionChecks", true);
if (!id.getVersion().equals(REQUIRED_VERSION) && doClientCheck) {
StandardLog.onPlayerEvent(this, player, "failed to login [incorrect version: %s] from %s", id.getVersion(), id.getSocketAddress());
onLoginClientVersionError(player, id);
return;
}
- UserMetadata user = PswgDatabase.users().getUser(id.getUsername());
+ UserMetadata user = PswgDatabase.INSTANCE.getUsers().getUser(id.getUsername());
player.setUsername(id.getUsername());
if (user == null) {
StandardLog.onPlayerEvent(this, player, "failed to login [incorrect username] from %s", id.getSocketAddress());
@@ -261,7 +261,7 @@ public class LoginService extends Service {
cluster.addGalaxy(g);
clusterStatus.addGalaxy(g);
}
- cluster.setMaxCharacters(PswgDatabase.config().getInt(this, "galaxyMaxCharacters", 2));
+ cluster.setMaxCharacters(PswgDatabase.INSTANCE.getConfig().getInt(this, "galaxyMaxCharacters", 2));
player.sendPacket(new ServerNowEpochTime((int)(System.currentTimeMillis()/1E3)));
player.sendPacket(token);
player.sendPacket(cluster);
diff --git a/src/main/java/com/projectswg/holocore/services/support/global/zone/ZoneService.java b/src/main/java/com/projectswg/holocore/services/support/global/zone/ZoneService.java
index 2cf6d285..51c202d4 100644
--- a/src/main/java/com/projectswg/holocore/services/support/global/zone/ZoneService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/global/zone/ZoneService.java
@@ -79,7 +79,7 @@ public class ZoneService extends Service {
}
private void sendMessageOfTheDay(Player player) {
- String message = PswgDatabase.config().getString(this, "firstZoneMessage", "");
+ String message = PswgDatabase.INSTANCE.getConfig().getString(this, "firstZoneMessage", "");
if(!message.isEmpty()) // If the message isn't nothing
new SystemMessageIntent(player, message).broadcast(); // Send it
diff --git a/src/main/java/com/projectswg/holocore/services/support/global/zone/creation/CharacterCreationService.java b/src/main/java/com/projectswg/holocore/services/support/global/zone/creation/CharacterCreationService.java
index 55fa44e1..39101500 100644
--- a/src/main/java/com/projectswg/holocore/services/support/global/zone/creation/CharacterCreationService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/global/zone/creation/CharacterCreationService.java
@@ -73,7 +73,7 @@ public class CharacterCreationService extends Service {
@Override
public boolean start() {
- creationRestriction.setCreationsPerPeriod(PswgDatabase.config().getInt(this, "galaxyMaxCharactersPerPeriod", 2));
+ creationRestriction.setCreationsPerPeriod(PswgDatabase.INSTANCE.getConfig().getInt(this, "galaxyMaxCharactersPerPeriod", 2));
return super.start();
}
@@ -94,7 +94,7 @@ public class CharacterCreationService extends Service {
int spaceIndex = name.indexOf(' ');
if (spaceIndex != -1)
name = name.substring(0, spaceIndex);
- return PswgDatabase.objects().isCharacter(name);
+ return PswgDatabase.INSTANCE.getObjects().isCharacter(name);
}
private void handleRandomNameRequest(Player player, RandomNameRequest request) {
@@ -110,7 +110,7 @@ public class CharacterCreationService extends Service {
private void handleApproveNameRequest(Player player, ClientVerifyAndLockNameRequest request) {
String name = request.getName();
ErrorMessage err = getNameValidity(name, player.getAccessLevel() != AccessLevel.PLAYER);
- int max = PswgDatabase.config().getInt(this, "galaxyMaxCharacters", 0);
+ int max = PswgDatabase.INSTANCE.getConfig().getInt(this, "galaxyMaxCharacters", 0);
if (max != 0 && getCharacterCount(player.getAccountId()) >= max)
err = ErrorMessage.SERVER_CHARACTER_CREATION_MAX_CHARS;
if (err == ErrorMessage.NAME_APPROVED_MODIFIED)
@@ -143,7 +143,7 @@ public class CharacterCreationService extends Service {
return null;
}
// Too many characters
- int max = PswgDatabase.config().getInt(this, "galaxyMaxCharacters", 0);
+ int max = PswgDatabase.INSTANCE.getConfig().getInt(this, "galaxyMaxCharacters", 0);
if (max != 0 && getCharacterCount(player.getAccountId()) >= max) {
sendCharCreationFailure(player, create, ErrorMessage.SERVER_CHARACTER_CREATION_MAX_CHARS, "too many characters");
return null;
@@ -191,7 +191,7 @@ public class CharacterCreationService extends Service {
}
private int getCharacterCount(String username) {
- return PswgDatabase.objects().getCharacterCount(username);
+ return PswgDatabase.INSTANCE.getObjects().getCharacterCount(username);
}
private ErrorMessage getNameValidity(String name, boolean admin) {
@@ -218,7 +218,7 @@ public class CharacterCreationService extends Service {
}
private CreatureObject createCharacter(Player player, ClientCreateCharacter create) {
- String spawnLocation = PswgDatabase.config().getString(this, "primarySpawnLocation", "tat_moseisley");
+ String spawnLocation = PswgDatabase.INSTANCE.getConfig().getString(this, "primarySpawnLocation", "tat_moseisley");
StandardLog.onPlayerTrace(this, player, "created player at spawn location %s", spawnLocation);
ZoneInsertion info = DataLoader.zoneInsertions().getInsertion(spawnLocation);
if (info == null) {
diff --git a/src/main/java/com/projectswg/holocore/services/support/npc/spawn/SpawnerService.java b/src/main/java/com/projectswg/holocore/services/support/npc/spawn/SpawnerService.java
index 8d36bcb4..7076f833 100644
--- a/src/main/java/com/projectswg/holocore/services/support/npc/spawn/SpawnerService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/npc/spawn/SpawnerService.java
@@ -76,7 +76,7 @@ public final class SpawnerService extends Service {
@Override
public boolean initialize() {
executor.start();
- if (PswgDatabase.config().getBoolean(this, "spawnEggsEnabled", true))
+ if (PswgDatabase.INSTANCE.getConfig().getBoolean(this, "spawnEggsEnabled", true))
loadSpawners();
return true;
diff --git a/src/main/java/com/projectswg/holocore/services/support/objects/ObjectStorageService.java b/src/main/java/com/projectswg/holocore/services/support/objects/ObjectStorageService.java
index 3ad92015..7a4135ba 100644
--- a/src/main/java/com/projectswg/holocore/services/support/objects/ObjectStorageService.java
+++ b/src/main/java/com/projectswg/holocore/services/support/objects/ObjectStorageService.java
@@ -79,7 +79,7 @@ public class ObjectStorageService extends Service {
private boolean initializeSavedObjects() {
long startTime = StandardLog.onStartLoad("server objects");
- List objectDocuments = PswgDatabase.objects().getObjects();
+ List objectDocuments = PswgDatabase.INSTANCE.getObjects().getObjects();
Map objects = new HashMap<>();
AtomicLong highestId = new AtomicLong(0);
for (MongoData doc : objectDocuments) {
@@ -135,7 +135,7 @@ public class ObjectStorageService extends Service {
private void saveObjects() {
List saveList = new ArrayList<>();
persistedObjects.forEach(obj -> saveChildren(saveList, obj));
- PswgDatabase.objects().addObjects(saveList);
+ PswgDatabase.INSTANCE.getObjects().addObjects(saveList);
}
private void saveChildren(Collection saveList, @Nullable SWGObject obj) {
@@ -156,7 +156,7 @@ public class ObjectStorageService extends Service {
if (persistedObjects.add(obj)) {
List saveList = new ArrayList<>();
saveChildren(saveList, obj);
- PswgDatabase.objects().addObjects(saveList);
+ PswgDatabase.INSTANCE.getObjects().addObjects(saveList);
}
}
}
@@ -196,13 +196,13 @@ public class ObjectStorageService extends Service {
}
if (object.isPersisted())
persistedObjects.remove(object);
- PswgDatabase.objects().removeObject(object.getObjectId());
+ PswgDatabase.INSTANCE.getObjects().removeObject(object.getObjectId());
objectMap.remove(object.getObjectId());
}
private List createEventList() {
List events = new ArrayList<>();
- for (String event : PswgDatabase.config().getString(this, "events", "").split(",")) {
+ for (String event : PswgDatabase.INSTANCE.getConfig().getString(this, "events", "").split(",")) {
if (event.isEmpty())
continue;
events.add(event.toLowerCase(Locale.US));
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index 13e96c13..dbf396e2 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -13,4 +13,5 @@ open module holocore {
requires com.projectswg.common;
requires fast.json;
requires commons.cli;
+ requires kotlin.stdlib;
}