mirror of
https://bitbucket.org/projectswg/holocore.git
synced 2026-01-16 23:04:20 -05:00
Changed mongodb database to use kotlin, changed TCP to be insecure again
This commit is contained in:
Submodule pswgcommon updated: 44dc921082...fff4daa98e
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>. *
|
||||
***********************************************************************************/
|
||||
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<ConfigFile, Config> configMap;
|
||||
private final BasicThread watcherThread;
|
||||
private final AtomicBoolean running;
|
||||
|
||||
private WatchService watcher;
|
||||
|
||||
public ConfigWatcher(Map<ConfigFile, Config> 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<Path>) 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<String, String> entry : cfg.load().entrySet()) {
|
||||
new ConfigChangedIntent(cfgFile, entry.getKey(), entry.getValue(), cfg.getString(entry.getKey(), null)).broadcast();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>. *
|
||||
***********************************************************************************/
|
||||
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<ConfigFile, Config> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://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 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<Document> collection;
|
||||
|
||||
PswgConfigDatabase() {
|
||||
this.collection = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(MongoCollection<Document> collection) {
|
||||
this.collection = collection;
|
||||
collection.createIndex(Indexes.ascending("package"), new IndexOptions().unique(true));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MongoCollection<Document> 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<Document> 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<Document> 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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http:></http:>//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<Document>){
|
||||
|
||||
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<Document> {
|
||||
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<Document>()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<Document> 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 { }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>. *
|
||||
***********************************************************************************/
|
||||
|
||||
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<Document> collection = database.getCollection(collectionName).withWriteConcern(writeConcern);
|
||||
implementation.open(collection);
|
||||
}
|
||||
|
||||
PswgDatabase getImplementation() {
|
||||
return implementation;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://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 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<Document> collection;
|
||||
|
||||
PswgObjectDatabase() {
|
||||
this.collection = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(MongoCollection<Document> collection) {
|
||||
this.collection = collection;
|
||||
collection.createIndex(Indexes.ascending("id"), new IndexOptions().unique(true));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public MongoCollection<Document> 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<SWGObject> 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<MongoData> getObjects() {
|
||||
return collection.find().map(MongoData::new).into(new ArrayList<>());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http:></http:>//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<Document>) {
|
||||
|
||||
val objects: List<MongoData>
|
||||
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<SWGObject>) {
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://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 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<Document> collection;
|
||||
|
||||
PswgResourceDatabase() {
|
||||
this.collection = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(MongoCollection<Document> 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<GalacticResource> 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<GalacticResource> getResources() {
|
||||
return collection.find().map(MongoData::new).map(data -> MongoData.create(data, GalacticResource::new)).into(new ArrayList<>());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http:></http:>//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<Document>) {
|
||||
|
||||
var resources: List<GalacticResource>
|
||||
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))
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://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 org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PswgUserDatabase implements PswgDatabase {
|
||||
|
||||
private MongoCollection<Document> collection;
|
||||
|
||||
PswgUserDatabase() {
|
||||
this.collection = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void open(MongoCollection<Document> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http://www.gnu.org/licenses/>. *
|
||||
***********************************************************************************/
|
||||
* along with Holocore. If not, see <http:></http:>//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<Document>) {
|
||||
|
||||
public AdminNetworkClient(SocketChannel socket, SSLContext sslContext, ThreadPool securityExecutor, Collection<NetworkClient> 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()
|
||||
|
||||
}
|
||||
@@ -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 <http://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.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<NetworkClient> flushList;
|
||||
private final Queue<ByteBuffer> outboundPackets;
|
||||
private final IntentChain intentChain;
|
||||
private final AtomicBoolean connected;
|
||||
private final AtomicReference<SessionStatus> status;
|
||||
private final ByteBuffer outboundBuffer;
|
||||
private final NetBuffer buffer;
|
||||
private final Player player;
|
||||
|
||||
public NetworkClient(SocketChannel socket, SSLContext sslContext, ThreadPool securityExecutor, Collection<NetworkClient> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <http:></http:>//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<SessionStatus>
|
||||
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<SWGPacket> { 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
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <http://www.gnu.org/licenses/>. *
|
||||
***********************************************************************************/
|
||||
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<CreatureObject, Set<Mount>> 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<Mount> 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<Mount> 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<Mount> 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<Mount> 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<Mount> 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<Mount> 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 <http://www.gnu.org/licenses/>. *
|
||||
***********************************************************************************/
|
||||
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<CreatureObject, Set<Mount>> 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<Mount> 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<Mount> 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<Mount> 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<Mount> 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<Mount> 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<Mount> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<NetworkClient> tcpServer;
|
||||
private final TCPServer<AdminNetworkClient> adminServer;
|
||||
private final ScheduledThreadPool networkFlushPeriodic;
|
||||
private final List<NetworkClient> clients;
|
||||
private static final int INBOUND_BUFFER_SIZE = 4096;
|
||||
|
||||
private final ServerSocketChannel tcpServer;
|
||||
private final BasicThread acceptThreadPool;
|
||||
private final Map<Long, NetworkClient> 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.<NetworkClient>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.<AdminNetworkClient>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<SelectionKey> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -79,7 +79,7 @@ public class ObjectStorageService extends Service {
|
||||
|
||||
private boolean initializeSavedObjects() {
|
||||
long startTime = StandardLog.onStartLoad("server objects");
|
||||
List<MongoData> objectDocuments = PswgDatabase.objects().getObjects();
|
||||
List<MongoData> objectDocuments = PswgDatabase.INSTANCE.getObjects().getObjects();
|
||||
Map<Long, SWGObject> 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<SWGObject> saveList = new ArrayList<>();
|
||||
persistedObjects.forEach(obj -> saveChildren(saveList, obj));
|
||||
PswgDatabase.objects().addObjects(saveList);
|
||||
PswgDatabase.INSTANCE.getObjects().addObjects(saveList);
|
||||
}
|
||||
|
||||
private void saveChildren(Collection<SWGObject> saveList, @Nullable SWGObject obj) {
|
||||
@@ -156,7 +156,7 @@ public class ObjectStorageService extends Service {
|
||||
if (persistedObjects.add(obj)) {
|
||||
List<SWGObject> 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<String> createEventList() {
|
||||
List<String> 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));
|
||||
|
||||
@@ -13,4 +13,5 @@ open module holocore {
|
||||
requires com.projectswg.common;
|
||||
requires fast.json;
|
||||
requires commons.cli;
|
||||
requires kotlin.stdlib;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user