Changed mongodb database to use kotlin, changed TCP to be insecure again

This commit is contained in:
Obique
2019-04-24 11:22:45 -05:00
parent b5e69fa995
commit 4de51b40e6
39 changed files with 1208 additions and 1855 deletions

View File

@@ -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() {

View File

@@ -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) {

View File

@@ -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() {

View File

@@ -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);

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -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 { }
});
}
}
}

View File

@@ -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() {}
})
}
}

View File

@@ -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;
}
}

View File

@@ -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<>());
}
}

View File

@@ -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
}
}

View File

@@ -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<>());
}
}

View File

@@ -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))
}
}

View File

@@ -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;
}
}
}

View File

@@ -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()
}

View File

@@ -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);
}
}
}

View File

@@ -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
}
}

View File

@@ -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) {

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}
}
}

View File

@@ -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) {

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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();

View File

@@ -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

View File

@@ -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");

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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));

View File

@@ -13,4 +13,5 @@ open module holocore {
requires com.projectswg.common;
requires fast.json;
requires commons.cli;
requires kotlin.stdlib;
}