From 018cd2eaf46f42775745b9e90257271d0bdbd62b Mon Sep 17 00:00:00 2001 From: Obique Date: Mon, 4 Feb 2019 19:58:20 -0600 Subject: [PATCH] Removed automatic delta sending for SWGMap and added some new SDB loaders --- pswgcommon | 2 +- .../holocore/display/ResourceSpawnViewer.java | 159 ---------- .../projectswg/holocore/ProjectSWGRunner.java | 32 +- .../integration/resources/HolocoreClient.java | 12 +- .../com/projectswg/holocore/ProjectSWG.java | 2 +- .../gameplay/combat/buff/BuffData.java | 170 ----------- .../gameplay/combat/buff/BuffMap.java | 97 ------ .../gameplay/world/travel/TravelHelper.java | 54 +--- .../data/location/InstanceLocation.java | 4 +- .../data/persistable/SWGObjectFactory.java | 8 +- .../data/server_info/loader/BuffLoader.java | 271 ++++++++++++++++ .../data/server_info/loader/CachedLoader.java | 5 +- .../data/server_info/loader/DataLoader.java | 12 + .../server_info/loader/ObjectDataLoader.java | 9 +- .../server_info/loader/PerformanceLoader.java | 193 ++++++++++++ .../server_info/loader/TravelCostLoader.java} | 99 +++--- .../callbacks/FindFriendCallback.java | 3 +- .../callbacks/RequestWaypointCmdCallback.java | 3 +- .../callbacks/WaypointCmdCallback.java | 3 +- .../global/network/BaselineObject.java | 20 -- .../support/global/player/PlayerFlags.java | 56 +++- .../zone/creation/CharacterCreation.java | 5 +- .../objects/permissions/AdminPermissions.java | 4 +- .../permissions/ContainerPermissions.java | 2 +- .../permissions/DefaultPermissions.java | 4 +- .../permissions/ReadOnlyPermissions.java | 6 +- .../permissions/ReadWritePermissions.java | 6 +- .../radial/terminal/TerminalBankRadial.java | 38 ++- .../gameplay/combat/buffs/BuffService.java | 71 ++--- .../entertainment/EntertainmentService.java | 39 +-- .../gameplay/gcw/faction/CivilWarService.java | 28 +- .../gameplay/player/badge/BadgeService.java | 16 +- .../support/data/dev/CustomObjectService.java | 26 +- .../global/chat/ChatFriendService.java | 2 +- .../global/zone/ConnectionService.java | 4 +- .../support/objects/ObjectStorageService.java | 27 +- .../ClientdataPrinterDatatable.java | 55 ---- .../world/travel/TestTravelHelper.java | 4 +- .../support/data/collections/TestSWGFlag.java | 24 +- .../objects/swg/TestSWGPersistence.java | 289 ++++++++++++++++++ .../test/resources/GenericCreatureObject.java | 19 +- .../ClientdataPrinterCommands.java | 2 +- .../ClientdataPrinterDatatable.java | 184 +++++++++++ .../packets/PacketCaptureAnalysis.java | 10 +- .../utility/packets/ProcessPacketCapture.java | 149 ++++----- 45 files changed, 1343 insertions(+), 885 deletions(-) delete mode 100644 src/display/java/com/projectswg/holocore/display/ResourceSpawnViewer.java delete mode 100644 src/main/java/com/projectswg/holocore/resources/gameplay/combat/buff/BuffData.java delete mode 100644 src/main/java/com/projectswg/holocore/resources/gameplay/combat/buff/BuffMap.java create mode 100644 src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/BuffLoader.java create mode 100644 src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/PerformanceLoader.java rename src/{utility/java/com/projectswg/utility/packets/PacketRecord.java => main/java/com/projectswg/holocore/resources/support/data/server_info/loader/TravelCostLoader.java} (51%) delete mode 100644 src/main/java/com/projectswg/holocore/utilities/clientdata_printer/ClientdataPrinterDatatable.java create mode 100644 src/test/java/com/projectswg/holocore/resources/support/objects/swg/TestSWGPersistence.java rename src/{main/java/com/projectswg/holocore/utilities => utility/java/com/projectswg/utility}/clientdata_printer/ClientdataPrinterCommands.java (98%) create mode 100644 src/utility/java/com/projectswg/utility/clientdata_printer/ClientdataPrinterDatatable.java diff --git a/pswgcommon b/pswgcommon index d08a091fd..9f0e9fd4f 160000 --- a/pswgcommon +++ b/pswgcommon @@ -1 +1 @@ -Subproject commit d08a091fd478c342071b948dd5d05074dfb2c2bc +Subproject commit 9f0e9fd4fa35fee8be34c4b7bc083e2408ceb709 diff --git a/src/display/java/com/projectswg/holocore/display/ResourceSpawnViewer.java b/src/display/java/com/projectswg/holocore/display/ResourceSpawnViewer.java deleted file mode 100644 index de58cb3b6..000000000 --- a/src/display/java/com/projectswg/holocore/display/ResourceSpawnViewer.java +++ /dev/null @@ -1,159 +0,0 @@ -/*********************************************************************************** - * Copyright (c) 2018 /// Project SWG /// www.projectswg.com * - * * - * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * - * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * - * Our goal is to create an emulator which will provide a server for players to * - * continue playing a game similar to the one they used to play. We are basing * - * it on the final publish of the game prior to end-game events. * - * * - * This file is part of Holocore. * - * * - * --------------------------------------------------------------------------------* - * * - * Holocore is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * Holocore is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with Holocore. If not, see . * - ***********************************************************************************/ -package com.projectswg.holocore.display; - -import com.projectswg.common.data.location.Terrain; -import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.GalacticResourceLoader; -import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.GalacticResourceSpawn; -import javafx.application.Application; -import javafx.collections.FXCollections; -import javafx.embed.swing.SwingFXUtils; -import javafx.scene.Scene; -import javafx.scene.canvas.Canvas; -import javafx.scene.canvas.GraphicsContext; -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.image.WritableImage; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Color; -import javafx.stage.Stage; - -import javax.imageio.ImageIO; -import java.io.File; -import java.io.IOException; -import java.util.List; - -public class ResourceSpawnViewer extends Application { - - private static final int MAP_SIZE = 16384; - private static final double POSITION_GAUSSIAN_FACTOR = MAP_SIZE / 2 * Math.sqrt(2); - private static final int STAGE_SIZE = 800; - - private static final Terrain [] ALL_PLANETS = new Terrain[] { - Terrain.CORELLIA, Terrain.DANTOOINE, Terrain.DATHOMIR, - Terrain.ENDOR, Terrain.KASHYYYK_MAIN, Terrain.LOK, - Terrain.MUSTAFAR, Terrain.NABOO, Terrain.RORI, - Terrain.TALUS, Terrain.TATOOINE, Terrain.YAVIN4 - }; - private static final Color [] COLORS = new Color[] { - Color.BLACK, Color.LIGHTGRAY, Color.BLUE, Color.MAGENTA, - Color.CYAN, Color.ORANGE, Color.DARKGRAY, Color.PINK, Color.GRAY, - Color.RED, Color.GREEN, Color.YELLOW - }; - - private ComboBox terrainCombo; - private Button saveButton; - private Canvas resourceCanvas; - - public static void main(String [] args) { - Application.launch(args); - } - - @Override - public void start(Stage primaryStage) { - VBox root = new VBox(); - HBox topPanel = new HBox(); - root.getChildren().add(topPanel); - root.getChildren().add(resourceCanvas = new Canvas(800, 800)); - topPanel.getChildren().add(terrainCombo = new ComboBox<>()); - topPanel.getChildren().add(saveButton = new Button("Save")); - setupTopPanel(); - primaryStage.setScene(new Scene(root, STAGE_SIZE, STAGE_SIZE + 20)); - primaryStage.show(); - } - - private void setupTopPanel() { - terrainCombo.setItems(FXCollections.observableArrayList(ALL_PLANETS)); - terrainCombo.valueProperty().addListener((val, o, n) -> onTerrainChanged(n)); - terrainCombo.setValue(Terrain.TATOOINE); - saveButton.setOnAction(e -> save()); - } - - private void save() { - WritableImage writableImage = new WritableImage(STAGE_SIZE, STAGE_SIZE); - resourceCanvas.snapshot(null, writableImage); - try { - ImageIO.write(SwingFXUtils.fromFXImage(writableImage, null), "png", new File("resource_spawns.png")); - } catch (IOException e) { - e.printStackTrace(); - } - } - - private void onTerrainChanged(Terrain terrain) { - GraphicsContext gc = resourceCanvas.getGraphicsContext2D(); - gc.clearRect(0, 0, STAGE_SIZE, STAGE_SIZE); - for (int x = 0; x < STAGE_SIZE/2; x++) { - for (int y = 0; y < STAGE_SIZE/2; y++) { - double dist = Math.sqrt(Math.pow(STAGE_SIZE/2-x, 2)+Math.pow(STAGE_SIZE/2-y, 2)); - double opacity = getProbabilityOpacity(dist/STAGE_SIZE*MAP_SIZE); - gc.setStroke(new Color(0, 0, 0, opacity * 0.75)); - gc.strokeLine(x, y, x, y); - gc.strokeLine(STAGE_SIZE-x, STAGE_SIZE-y, STAGE_SIZE-x, STAGE_SIZE-y); - gc.strokeLine(STAGE_SIZE-x, y, STAGE_SIZE-x, y); - gc.strokeLine(x, STAGE_SIZE-y, x, STAGE_SIZE-y); - } - } - GalacticResourceLoader loader = new GalacticResourceLoader(); - List spawns = loader.loadSpawns(); - int index = 0; - for (GalacticResourceSpawn spawn : spawns) { - if (spawn.getTerrain() != terrain) - continue; - Color c = COLORS[(index++) % COLORS.length]; - gc.setFill(c.deriveColor(0, 1, 1, 0.7)); - double x = convertToStage(spawn.getX()); - double z = convertToStage(spawn.getZ()); - double width = (double) spawn.getRadius() / (MAP_SIZE/2) * STAGE_SIZE; - gc.fillOval(x-width/2, STAGE_SIZE-z-width/2, width, width); - gc.setFill(Color.BLACK); - gc.fillText(Long.toString(spawn.getResourceId()), convertToStage(spawn.getX()) - getHorizontalShift(spawn.getResourceId()), STAGE_SIZE-convertToStage(spawn.getZ())+5); - } - } - - private int convertToStage(double x) { - return (int) (x / (MAP_SIZE/2) * STAGE_SIZE/2) + STAGE_SIZE/2; - } - - - private int getHorizontalShift(long resourceId) { - if (resourceId < 10) - return 4; - if (resourceId < 100) - return 8; - return 12; - } - - private double getProbabilityOpacity(double dist) { - return getGaussianY(((dist / POSITION_GAUSSIAN_FACTOR) - 0.5) * 6); - } - - private double getGaussianY(double x) { - return Math.pow(Math.exp(-((x * x) / 2)), 1 / (Math.sqrt(2 * Math.PI))); - } - -} diff --git a/src/integration/java/com/projectswg/holocore/ProjectSWGRunner.java b/src/integration/java/com/projectswg/holocore/ProjectSWGRunner.java index 9e5b9c5b0..023e94c4d 100644 --- a/src/integration/java/com/projectswg/holocore/ProjectSWGRunner.java +++ b/src/integration/java/com/projectswg/holocore/ProjectSWGRunner.java @@ -1,7 +1,7 @@ package com.projectswg.holocore; -import com.projectswg.connection.HolocoreSocket; -import com.projectswg.connection.ServerConnectionChangedReason; +import com.projectswg.common.network.packets.swg.holo.HoloConnectionStopped.ConnectionStoppedReason; +import com.projectswg.holocore.client.HolocoreSocket; import me.joshlarson.jlcommon.concurrency.BasicThread; import me.joshlarson.jlcommon.concurrency.Delay; @@ -18,25 +18,25 @@ public class ProjectSWGRunner { public void start() { runner.start(); { - HolocoreSocket socket = new HolocoreSocket(InetAddress.getLoopbackAddress(), 44463); - long start = System.nanoTime(); - boolean connected = false; - while (System.nanoTime() - start <= 60E9) { // 60s max wait - connected = socket.getServerStatus().equals("UP"); - if (connected) - break; - Delay.sleepSeconds(1); - } - if (connected) { - start = System.nanoTime(); + try (HolocoreSocket socket = new HolocoreSocket(InetAddress.getLoopbackAddress(), 44463)) { + long start = System.nanoTime(); + boolean connected = false; while (System.nanoTime() - start <= 60E9) { // 60s max wait - if (socket.connect(1000)) { - socket.disconnect(ServerConnectionChangedReason.CLIENT_DISCONNECT); + connected = socket.getServerStatus().equals("UP"); + if (connected) break; + Delay.sleepSeconds(1); + } + if (connected) { + start = System.nanoTime(); + while (System.nanoTime() - start <= 60E9) { // 60s max wait + if (socket.connect(1000)) { + socket.disconnect(ConnectionStoppedReason.NETWORK); + break; + } } } } - socket.terminate(); } } diff --git a/src/integration/java/com/projectswg/holocore/integration/resources/HolocoreClient.java b/src/integration/java/com/projectswg/holocore/integration/resources/HolocoreClient.java index 4efb7827e..b100fadcb 100644 --- a/src/integration/java/com/projectswg/holocore/integration/resources/HolocoreClient.java +++ b/src/integration/java/com/projectswg/holocore/integration/resources/HolocoreClient.java @@ -6,6 +6,7 @@ import com.projectswg.common.data.location.Terrain; import com.projectswg.common.network.NetBuffer; import com.projectswg.common.network.packets.PacketType; import com.projectswg.common.network.packets.SWGPacket; +import com.projectswg.common.network.packets.swg.holo.HoloConnectionStopped.ConnectionStoppedReason; import com.projectswg.common.network.packets.swg.holo.login.HoloLoginRequestPacket; import com.projectswg.common.network.packets.swg.holo.login.HoloLoginResponsePacket; import com.projectswg.common.network.packets.swg.login.EnumerateCharacterId.SWGCharacter; @@ -15,9 +16,8 @@ import com.projectswg.common.network.packets.swg.zone.SceneEndBaselines; import com.projectswg.common.network.packets.swg.zone.baselines.Baseline; import com.projectswg.common.network.packets.swg.zone.insertion.CmdStartScene; import com.projectswg.common.network.packets.swg.zone.insertion.SelectCharacter; -import com.projectswg.connection.HolocoreSocket; -import com.projectswg.connection.RawPacket; -import com.projectswg.connection.ServerConnectionChangedReason; +import com.projectswg.holocore.client.HolocoreSocket; +import com.projectswg.holocore.client.RawPacket; import com.projectswg.holocore.resources.support.objects.ObjectCreator; import com.projectswg.holocore.resources.support.objects.swg.SWGObject; import me.joshlarson.jlcommon.concurrency.BasicThread; @@ -26,9 +26,7 @@ import org.junit.Assert; import java.net.InetSocketAddress; import java.util.Map; -import java.util.Map.Entry; import java.util.Objects; -import java.util.Queue; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.LinkedBlockingQueue; @@ -107,8 +105,8 @@ public class HolocoreClient { public void disconnect() { listenThread.stop(false); - socket.disconnect(ServerConnectionChangedReason.CLIENT_DISCONNECT); - socket.terminate(); + socket.disconnect(ConnectionStoppedReason.APPLICATION); + socket.close(); listenThread.awaitTermination(1000); } diff --git a/src/main/java/com/projectswg/holocore/ProjectSWG.java b/src/main/java/com/projectswg/holocore/ProjectSWG.java index 71938bdb1..821c635fc 100644 --- a/src/main/java/com/projectswg/holocore/ProjectSWG.java +++ b/src/main/java/com/projectswg/holocore/ProjectSWG.java @@ -56,7 +56,7 @@ import java.util.*; public class ProjectSWG { - public static final String VERSION = "DEC18"; + public static final String VERSION = "FEB19"; private static final Galaxy GALAXY = new Galaxy(); diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/combat/buff/BuffData.java b/src/main/java/com/projectswg/holocore/resources/gameplay/combat/buff/BuffData.java deleted file mode 100644 index c9b78ba4e..000000000 --- a/src/main/java/com/projectswg/holocore/resources/gameplay/combat/buff/BuffData.java +++ /dev/null @@ -1,170 +0,0 @@ -/*********************************************************************************** - * Copyright (c) 2018 /// Project SWG /// www.projectswg.com * - * * - * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * - * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * - * Our goal is to create an emulator which will provide a server for players to * - * continue playing a game similar to the one they used to play. We are basing * - * it on the final publish of the game prior to end-game events. * - * * - * This file is part of Holocore. * - * * - * --------------------------------------------------------------------------------* - * * - * Holocore is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * Holocore is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with Holocore. If not, see . * - ***********************************************************************************/ -package com.projectswg.holocore.resources.gameplay.combat.buff; - -import com.projectswg.common.data.CRC; -import me.joshlarson.jlcommon.utilities.Arguments; - -import java.util.Locale; - -public class BuffData { - - private final int crc; - private final String name; - private final String groupName; - private final int groupPriority; - private final String [] effectNames; - private final float [] effectValues; - - private int maxStackCount; - private float defaultDuration; - private String effectFileName; - private String particleHardPoint; - private String stanceParticle; - private String callback; - private boolean persistent; - private boolean removedOnDeath; - private boolean decayOnPvpDeath; - - public BuffData(String name, String groupName, int groupPriority) { - this.crc = CRC.getCrc(name.toLowerCase(Locale.ENGLISH)); - this.name = name; - this.groupName = groupName; - this.groupPriority = groupPriority; - this.effectNames = new String[5]; - this.effectValues = new float[5]; - } - - public int getCrc() { - return crc; - } - - public String getName() { - return name; - } - - public String getGroupName() { - return groupName; - } - - public int getGroupPriority() { - return groupPriority; - } - - public int getMaxStackCount() { - return maxStackCount; - } - - public String getEffectName(int effect) { - Arguments.validate(effect >= 0 && effect < 5, "Effect # must be in range: [0, 5)"); - return effectNames[effect]; - } - - public float getEffectValue(int effect) { - Arguments.validate(effect >= 0 && effect < 5, "Effect # must be in range: [0, 5)"); - return effectValues[effect]; - } - - public float getDefaultDuration() { - return defaultDuration; - } - - public String getEffectFileName() { - return effectFileName; - } - - public String getParticleHardPoint() { - return particleHardPoint; - } - - public String getStanceParticle() { - return stanceParticle; - } - - public String getCallback() { - return callback; - } - - public boolean isPersistent() { - return persistent; - } - - public boolean isRemovedOnDeath() { - return removedOnDeath; - } - - public boolean isDecayOnPvpDeath() { - return decayOnPvpDeath; - } - - public void setMaxStackCount(int maxStackCount) { - this.maxStackCount = maxStackCount; - } - - public void setEffectName(int effect, String name) { - Arguments.validate(effect >= 0 && effect < 5, "Effect # must be in range: [0, 5)"); - effectNames[effect] = name; - } - - public void setEffectValue(int effect, float value) { - Arguments.validate(effect >= 0 && effect < 5, "Effect # must be in range: [0, 5)"); - effectValues[effect] = value; - } - - public void setDefaultDuration(float defaultDuration) { - this.defaultDuration = defaultDuration; - } - - public void setEffectFileName(String effectFileName) { - this.effectFileName = effectFileName; - } - - public void setParticleHardPoint(String particleHardPoint) { - this.particleHardPoint = particleHardPoint; - } - - public void setStanceParticle(String stanceParticle) { - this.stanceParticle = stanceParticle; - } - - public void setCallback(String callback) { - this.callback = callback; - } - - public void setPersistent(boolean persistent) { - this.persistent = persistent; - } - - public void setRemovedOnDeath(boolean removedOnDeath) { - this.removedOnDeath = removedOnDeath; - } - - public void setDecayOnPvpDeath(boolean decayOnPvpDeath) { - this.decayOnPvpDeath = decayOnPvpDeath; - } - -} diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/combat/buff/BuffMap.java b/src/main/java/com/projectswg/holocore/resources/gameplay/combat/buff/BuffMap.java deleted file mode 100644 index 2ba5b074d..000000000 --- a/src/main/java/com/projectswg/holocore/resources/gameplay/combat/buff/BuffMap.java +++ /dev/null @@ -1,97 +0,0 @@ -/*********************************************************************************** - * Copyright (c) 2018 /// Project SWG /// www.projectswg.com * - * * - * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * - * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * - * Our goal is to create an emulator which will provide a server for players to * - * continue playing a game similar to the one they used to play. We are basing * - * it on the final publish of the game prior to end-game events. * - * * - * This file is part of Holocore. * - * * - * --------------------------------------------------------------------------------* - * * - * Holocore is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * Holocore is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with Holocore. If not, see . * - ***********************************************************************************/ -package com.projectswg.holocore.resources.gameplay.combat.buff; - -import com.projectswg.common.data.CRC; -import com.projectswg.common.data.swgfile.ClientFactory; -import com.projectswg.common.data.swgfile.visitors.DatatableData; - -import java.util.Locale; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -public class BuffMap { - - private final Map buffMap; - - public BuffMap() { - this.buffMap = new ConcurrentHashMap<>(); - } - - public void load() { - DatatableData buffTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/buff/buff.iff"); - int rows = buffTable.getRowCount(); - - buffMap.clear(); - for (int row = 0; row < rows; ++row) { - BuffData buff = new BuffData(buffTable.getString(row, "NAME"), buffTable.getString(row, "GROUP1"), buffTable.getInt(row, "PRIORITY")); - buff.setMaxStackCount(buffTable.getInt(row, "MAX_STACKS")); - for (int i = 0; i < 5; i++) { - buff.setEffectName(i, buffTable.getString(row, "EFFECT"+(i+1)+"_PARAM")); - buff.setEffectValue(i, buffTable.getFloat(row, "EFFECT"+(i+1)+"_VALUE")); - } - buff.setDefaultDuration(buffTable.getFloat(row, "DURATION")); - buff.setEffectFileName(buffTable.getString(row, "PARTICLE")); - buff.setParticleHardPoint(buffTable.getString(row, "PARTICLE_HARDPOINT")); - buff.setStanceParticle(buffTable.getString(row, "STANCE_PARTICLE")); - buff.setCallback(buffTable.getString(row, "CALLBACK")); - buff.setPersistent(buffTable.getInt(row, "IS_PERSISTENT") == 1); - buff.setRemovedOnDeath(buffTable.getInt(row, "REMOVE_ON_DEATH") == 1); - buff.setDecayOnPvpDeath(buffTable.getInt(row, "DECAY_ON_PVP_DEATH") == 1); - buffMap.put(buff.getCrc(), buff); - } - } - - public int size() { - return buffMap.size(); - } - - public BuffData getBuff(int crc) { - return buffMap.get(crc); - } - - public BuffData getBuff(CRC crc) { - return getBuff(crc.getCrc()); - } - - public BuffData getBuff(String name) { - return getBuff(getCrc(name)); - } - - public boolean containsBuff(int crc) { - return buffMap.containsKey(crc); - } - - public boolean containsBuff(String name) { - return containsBuff(getCrc(name)); - } - - private int getCrc(String name) { - return CRC.getCrc(name.toLowerCase(Locale.ENGLISH)); - } - -} diff --git a/src/main/java/com/projectswg/holocore/resources/gameplay/world/travel/TravelHelper.java b/src/main/java/com/projectswg/holocore/resources/gameplay/world/travel/TravelHelper.java index dc9e6a7a4..ee3810d40 100644 --- a/src/main/java/com/projectswg/holocore/resources/gameplay/world/travel/TravelHelper.java +++ b/src/main/java/com/projectswg/holocore/resources/gameplay/world/travel/TravelHelper.java @@ -31,8 +31,6 @@ import com.projectswg.common.data.encodables.oob.StringId; import com.projectswg.common.data.info.Config; import com.projectswg.common.data.location.Location; import com.projectswg.common.data.location.Terrain; -import com.projectswg.common.data.swgfile.ClientFactory; -import com.projectswg.common.data.swgfile.visitors.DatatableData; import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent; import com.projectswg.holocore.intents.support.objects.swg.DestroyObjectIntent; import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent; @@ -41,6 +39,7 @@ 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.SdbLoader; import com.projectswg.holocore.resources.support.data.server_info.SdbLoader.SdbResultSet; +import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader; import com.projectswg.holocore.resources.support.global.player.Player; import com.projectswg.holocore.resources.support.objects.SpecificObject; import com.projectswg.holocore.resources.support.objects.swg.SWGObject; @@ -60,16 +59,13 @@ public class TravelHelper { private final Map travel; private final ThreadPool travelExecutor; - private final AllowedRouteManager routeManager; private final TravelPointManager pointManager; public TravelHelper() { this.travel = new ConcurrentHashMap<>(); this.travelExecutor = new ThreadPool(3, "travel-shuttles-%d"); - this.routeManager = new AllowedRouteManager(); this.pointManager = new TravelPointManager(); - loadAllowedRoutesAndPrices(); createGalaxyTravels(); loadTravelPoints(); } @@ -89,11 +85,11 @@ public class TravelHelper { } public boolean isValidRoute(Terrain departure, Terrain destination) { - return routeManager.isRouteAvailable(departure, destination); + return getTravelFee(departure, destination) != 0; } public int getTravelFee(Terrain departure, Terrain destination) { - return routeManager.getRouteFee(departure, destination); + return DataLoader.travelCosts().getCost(departure, destination); } public TravelGroup getTravelGroup(String template) { @@ -191,25 +187,6 @@ public class TravelHelper { traveler.moveToContainer(destination.getCollector().getParent(), destination.getLocation()); } - private void loadAllowedRoutesAndPrices() { - DatatableData travelFeeTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/travel/travel.iff"); - // Sets up the travelPlanets array to be in the order of the datatable - Terrain [] travelPlanets = new Terrain[travelFeeTable.getRowCount()]; - travelFeeTable.handleRows(currentRow -> travelPlanets[currentRow] = Terrain.getTerrainFromName((String) travelFeeTable.getCell(currentRow, "Planet"))); - - for (int rowIndex = 0; rowIndex < travelPlanets.length; rowIndex++) { - for (int columnIndex = rowIndex; columnIndex < travelPlanets.length; columnIndex++) { - int price = (int) travelFeeTable.getCell(rowIndex, columnIndex+1); - - if (price <= 0) // If price is below or equal to 0 then this is an invalid route and isn't an option. - continue; - - routeManager.addRoute(travelPlanets[rowIndex], travelPlanets[columnIndex], price); - routeManager.addRoute(travelPlanets[columnIndex], travelPlanets[rowIndex], price); - } - } - } - private void createGalaxyTravels() { Config config = DataManager.getConfig(ConfigFile.FEATURES); long groundTime = config.getInt("SHUTTLE-GROUND-TIME", 120); @@ -266,29 +243,4 @@ public class TravelHelper { } } - private static class AllowedRouteManager { - - private final Map> routeCosts; - - public AllowedRouteManager() { - this.routeCosts = new ConcurrentHashMap<>(); - } - - public void addRoute(Terrain departure, Terrain destination, int fee) { - Map departureCosts = routeCosts.computeIfAbsent(departure, k -> new ConcurrentHashMap<>()); - departureCosts.put(destination, fee); - } - - public boolean isRouteAvailable(Terrain departure, Terrain destination) { - return routeCosts.get(departure) != null && routeCosts.get(departure).get(destination) != null; - } - - public int getRouteFee(Terrain departure, Terrain destination) { - Integer fee = routeCosts.get(departure).get(destination); - if (fee == null) - return 0; - return fee; - } - } - } diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/location/InstanceLocation.java b/src/main/java/com/projectswg/holocore/resources/support/data/location/InstanceLocation.java index 363b3e1ba..9d7e1103c 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/data/location/InstanceLocation.java +++ b/src/main/java/com/projectswg/holocore/resources/support/data/location/InstanceLocation.java @@ -62,14 +62,14 @@ public class InstanceLocation implements Persistable, MongoPersistable { } @Override - public void read(MongoData data) { + public void readMongo(MongoData data) { instanceNumber = data.getInteger("number", 0); instanceType = InstanceType.valueOf(data.getString("type", "NONE")); location = data.getDocument("location", new Location()); } @Override - public void save(MongoData data) { + public void saveMongo(MongoData data) { data.putInteger("number", instanceNumber); data.putString("type", instanceType.name()); data.putDocument("location", location); diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/persistable/SWGObjectFactory.java b/src/main/java/com/projectswg/holocore/resources/support/data/persistable/SWGObjectFactory.java index 204b73efb..205fb06d2 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/data/persistable/SWGObjectFactory.java +++ b/src/main/java/com/projectswg/holocore/resources/support/data/persistable/SWGObjectFactory.java @@ -39,8 +39,12 @@ public class SWGObjectFactory { obj.save(stream); } + public static MongoData save(SWGObject obj) { + return save(obj, new MongoData()); + } + public static MongoData save(SWGObject obj, MongoData data) { - obj.save(data); + obj.saveMongo(data); assert data.containsKey("id") : "serialized MongoData does not contain the objectId"; assert data.containsKey("template") : "serialized MongoData does not contain the template"; return data; @@ -60,7 +64,7 @@ public class SWGObjectFactory { assert objectId != 0 : "objectId is not defined in MongoData"; assert template != null : "template is not defined in MongoData"; SWGObject obj = ObjectCreator.createObjectFromTemplate(objectId, template); - obj.read(data); + obj.readMongo(data); return obj; } diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/BuffLoader.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/BuffLoader.java new file mode 100644 index 000000000..56663f34a --- /dev/null +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/BuffLoader.java @@ -0,0 +1,271 @@ +/*********************************************************************************** + * Copyright (c) 2019 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * --------------------------------------------------------------------------------* + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + ***********************************************************************************/ + +package com.projectswg.holocore.resources.support.data.server_info.loader; + +import com.projectswg.common.data.CRC; +import com.projectswg.holocore.resources.support.data.server_info.SdbColumnArraySet.SdbRealColumnArraySet; +import com.projectswg.holocore.resources.support.data.server_info.SdbColumnArraySet.SdbTextColumnArraySet; +import com.projectswg.holocore.resources.support.data.server_info.SdbLoader; +import com.projectswg.holocore.resources.support.data.server_info.SdbLoader.SdbResultSet; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +public final class BuffLoader extends DataLoader { + + private final Map buffsByCrc; + private final Map buffsByName; + + BuffLoader() { + this.buffsByCrc = new HashMap<>(); + this.buffsByName = new HashMap<>(); + } + + @Nullable + public BuffInfo getBuff(int crc) { + return buffsByCrc.get(crc); + } + + @Nullable + public BuffInfo getBuff(CRC crc) { + return getBuff(crc.getCrc()); + } + + @Nullable + public BuffInfo getBuff(String name) { + return buffsByName.get(name.toLowerCase(Locale.US)); + } + + public boolean containsBuff(int crc) { + return buffsByCrc.containsKey(crc); + } + + public boolean containsBuff(String name) { + return buffsByName.containsKey(name.toLowerCase(Locale.US)); + } + + @Override + public final void load() throws IOException { + try (SdbResultSet set = SdbLoader.load(new File("serverdata/buff/buff.sdb"))) { + SdbTextColumnArraySet effectParams = set.getTextArrayParser("effect([0-9]+)_param"); + SdbRealColumnArraySet effectValues = set.getRealArrayParser("effect([0-9]+)_value"); + while (set.next()) { + BuffInfo buff = new BuffInfo(set, effectParams, effectValues); + buffsByCrc.put(buff.getCrc(), buff); + buffsByName.put(buff.getName(), buff); + } + } + } + + public static class BuffInfo { + + private final String name; + private final int crc; + private final String group1; + private final String group2; + private final String block; + private final int priority; + private final String icon; + private final double duration; + private final String [] effectNames; + private final double [] effectValues; + private final String state; + private final String callback; + private final String particle; + private final String particleHardpoint; + private final int visible; + private final boolean debuff; + private final String stanceParticle; + private final boolean dispellPlayer; + private final boolean removeOnDeath; + private final boolean playerRemovable; + private final boolean celestial; + private final int maxStackCount; + private final boolean persistent; + private final boolean displayOrder; + private final boolean removeOnRespec; + private final boolean aiRemoveOnCombatEnd; + private final boolean decayOnPvpDeath; + + public BuffInfo(SdbResultSet set, SdbTextColumnArraySet effectNames, SdbRealColumnArraySet effectValues) { + this.name = set.getText("name").toLowerCase(Locale.US); + this.crc = CRC.getCrc(this.name); + this.group1 = set.getText("group1"); + this.group2 = set.getText("group2"); + this.block = set.getText("block"); + this.priority = (int) set.getInt("priority"); + this.icon = set.getText("icon"); + this.duration = set.getReal("duration"); + this.effectNames = effectNames.getArray().clone(); + this.effectValues = effectValues.getArray().clone(); + this.state = set.getText("state"); + this.callback = set.getText("callback"); + this.particle = set.getText("particle"); + this.particleHardpoint = set.getText("particle_hardpoint"); + this.visible = (int) set.getInt("visible"); + this.debuff = set.getBoolean("debuff"); + this.stanceParticle = set.getText("stance_particle"); + this.dispellPlayer = set.getInt("dispell_player") != 0; + this.removeOnDeath = set.getInt("remove_on_death") != 0; + this.playerRemovable = set.getInt("player_removable") != 0; + this.celestial = set.getInt("is_celestial") != 0; + this.maxStackCount = (int) set.getInt("max_stacks"); + this.persistent = set.getInt("is_persistent") != 0; + this.displayOrder = set.getInt("display_order") != 0; + this.removeOnRespec = set.getInt("remove_on_respec") != 0; + this.aiRemoveOnCombatEnd = set.getInt("ai_remove_on_combat_end") != 0; + this.decayOnPvpDeath = set.getInt("decay_on_pvp_death") != 0; + + assert this.effectNames.length == this.effectValues.length : "effect params and effect values differ in size"; + } + + public String getName() { + return name; + } + + public int getCrc() { + return crc; + } + + public String getGroup1() { + return group1; + } + + public String getGroup2() { + return group2; + } + + public String getBlock() { + return block; + } + + public int getPriority() { + return priority; + } + + public String getIcon() { + return icon; + } + + public double getDuration() { + return duration; + } + + public String[] getEffectNames() { + return effectNames.clone(); + } + + public String getEffectName(int index) { + return effectNames[index]; + } + + public double[] getEffectValues() { + return effectValues.clone(); + } + + public double getEffectValue(int index) { + return effectValues[index]; + } + + public int getEffects() { + return effectNames.length; + } + + public String getState() { + return state; + } + + public String getCallback() { + return callback; + } + + public String getParticle() { + return particle; + } + + public String getParticleHardpoint() { + return particleHardpoint; + } + + public int getVisible() { + return visible; + } + + public boolean isDebuff() { + return debuff; + } + + public String getStanceParticle() { + return stanceParticle; + } + + public boolean isDispellPlayer() { + return dispellPlayer; + } + + public boolean isRemoveOnDeath() { + return removeOnDeath; + } + + public boolean isPlayerRemovable() { + return playerRemovable; + } + + public boolean isCelestial() { + return celestial; + } + + public int getMaxStackCount() { + return maxStackCount; + } + + public boolean isPersistent() { + return persistent; + } + + public boolean isDisplayOrder() { + return displayOrder; + } + + public boolean isRemoveOnRespec() { + return removeOnRespec; + } + + public boolean isAiRemoveOnCombatEnd() { + return aiRemoveOnCombatEnd; + } + + public boolean isDecayOnPvpDeath() { + return decayOnPvpDeath; + } + } +} diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/CachedLoader.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/CachedLoader.java index 2419f669c..6327887a1 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/CachedLoader.java +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/CachedLoader.java @@ -8,6 +8,7 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.function.Supplier; enum CachedLoader { + BUFFS (BuffLoader::new), BUILDING_CELLS (BuildingCellLoader::new), NPC_LOADER (NpcLoader::new), NPC_COMBAT_PROFILES (NpcCombatProfileLoader::new), @@ -17,10 +18,12 @@ enum CachedLoader { NPC_WEAPON_RANGES (NpcWeaponRangeLoader::new), STATIC_SPAWNS (NpcStaticSpawnLoader::new), OBJECT_DATA (ObjectDataLoader::new), + PERFORMANCES (PerformanceLoader::new), COMMANDS (CommandLoader::new), SLOT_DEFINITIONS (SlotDefinitionLoader::new), ZONE_INSERTIONS (TerrainZoneInsertionLoader::new), - VEHICLES (VehicleLoader::new); + VEHICLES (VehicleLoader::new), + TRAVEL_COSTS (TravelCostLoader::new); private final AtomicReference> cachedLoader; private final Supplier supplier; diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/DataLoader.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/DataLoader.java index 3b86d2cb0..56e0e6ea0 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/DataLoader.java +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/DataLoader.java @@ -18,6 +18,10 @@ public abstract class DataLoader { } } + public static BuffLoader buffs() { + return (BuffLoader) CachedLoader.BUFFS.load(); + } + public static BuildoutLoader buildouts() { return BuildoutLoader.load(List.of()); } @@ -66,6 +70,10 @@ public abstract class DataLoader { return (ObjectDataLoader) CachedLoader.OBJECT_DATA.load(); } + public static PerformanceLoader performances() { + return (PerformanceLoader) CachedLoader.PERFORMANCES.load(); + } + public static CommandLoader commands() { return (CommandLoader) CachedLoader.COMMANDS.load(); } @@ -78,6 +86,10 @@ public abstract class DataLoader { return (TerrainZoneInsertionLoader) CachedLoader.ZONE_INSERTIONS.load(); } + public static TravelCostLoader travelCosts() { + return (TravelCostLoader) CachedLoader.TRAVEL_COSTS.load(); + } + public static VehicleLoader vehicles() { return (VehicleLoader) CachedLoader.VEHICLES.load(); } diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ObjectDataLoader.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ObjectDataLoader.java index 5ae305453..e75cdcc84 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ObjectDataLoader.java +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/ObjectDataLoader.java @@ -35,10 +35,7 @@ import me.joshlarson.jlcommon.log.Log; import java.io.File; import java.io.IOException; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public final class ObjectDataLoader extends DataLoader { @@ -52,6 +49,10 @@ public final class ObjectDataLoader extends DataLoader { return attributes.get(iff); } + public Collection getObjects() { + return Collections.unmodifiableCollection(attributes.keySet()); + } + @Override public void load() throws IOException { try (SdbResultSet set = SdbLoader.load(new File("serverdata/objects/object_data.sdb"))) { diff --git a/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/PerformanceLoader.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/PerformanceLoader.java new file mode 100644 index 000000000..acff163a7 --- /dev/null +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/PerformanceLoader.java @@ -0,0 +1,193 @@ +/*********************************************************************************** + * Copyright (c) 2019 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * --------------------------------------------------------------------------------* + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + ***********************************************************************************/ + +package com.projectswg.holocore.resources.support.data.server_info.loader; + +import com.projectswg.common.data.CRC; +import com.projectswg.holocore.resources.support.data.server_info.SdbColumnArraySet.SdbTextColumnArraySet; +import com.projectswg.holocore.resources.support.data.server_info.SdbLoader; +import com.projectswg.holocore.resources.support.data.server_info.SdbLoader.SdbResultSet; +import org.jetbrains.annotations.Nullable; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public final class PerformanceLoader extends DataLoader { + + private final Map nameMap; + private final Map danceMap; + + PerformanceLoader() { + this.nameMap = new HashMap<>(); + this.danceMap = new HashMap<>(); + } + + @Nullable + public PerformanceInfo getPerformanceByName(String performanceName) { + return nameMap.get(performanceName); + } + + @Nullable + public PerformanceInfo getPerformanceByDanceId(int danceVisualId) { + return danceMap.get(danceVisualId); + } + + @Override + public final void load() throws IOException { + try (SdbResultSet set = SdbLoader.load(new File("serverdata/performance/performance.sdb"))) { + SdbTextColumnArraySet flourishes = set.getTextArrayParser("flourish(%d+)"); + while (set.next()) { + PerformanceInfo performance = new PerformanceInfo(set, flourishes); + nameMap.put(performance.getPerformanceName(), performance); + danceMap.put(performance.getDanceVisualId(), performance); + } + } + } + + public static class PerformanceInfo { + + private final String performanceName; + private final int instrumentAudioId; + private final String requiredSong; + private final String requiredInstrument; + private final String requiredDance; + private final int danceVisualId; + private final int actionPointsPerLoop; + private final double loopDuration; + private final CRC type; + private final int baseXp; + private final int flourishXpMod; + private final int healMindWound; + private final int healShockWound; + private final String requiredSkillMod; + private final int requiredSkillModValue; + private final String mainloop; + private final String [] flourishes; + private final String intro; + private final String outro; + + public PerformanceInfo(SdbResultSet set, SdbTextColumnArraySet flourishes) { + this.performanceName = set.getText("performance_name"); + this.instrumentAudioId = (int) set.getInt("instrument_audio_id"); + this.requiredSong = set.getText("required_song"); + this.requiredInstrument = set.getText("required_instrument"); + this.requiredDance = set.getText("required_dance"); + this.danceVisualId = (int) set.getInt("dance_visual_id"); + this.actionPointsPerLoop = (int) set.getInt("action_points_per_loop"); + this.loopDuration = set.getReal("loop_duration"); + this.type = new CRC((int) set.getInt("type")); + this.baseXp = (int) set.getInt("base_xp"); + this.flourishXpMod = (int) set.getInt("flourish_xp_mod"); + this.healMindWound = (int) set.getInt("heal_mind_wound"); + this.healShockWound = (int) set.getInt("heal_shock_wound"); + this.requiredSkillMod = set.getText("required_skill_mod"); + this.requiredSkillModValue = (int) set.getInt("required_skill_mod_value"); + this.mainloop = set.getText("mainloop"); + this.flourishes = flourishes.getArray().clone(); + this.intro = set.getText("intro"); + this.outro = set.getText("outro"); + } + + public String getPerformanceName() { + return performanceName; + } + + public int getInstrumentAudioId() { + return instrumentAudioId; + } + + public String getRequiredSong() { + return requiredSong; + } + + public String getRequiredInstrument() { + return requiredInstrument; + } + + public String getRequiredDance() { + return requiredDance; + } + + public int getDanceVisualId() { + return danceVisualId; + } + + public int getActionPointsPerLoop() { + return actionPointsPerLoop; + } + + public double getLoopDuration() { + return loopDuration; + } + + public CRC getType() { + return type; + } + + public int getBaseXp() { + return baseXp; + } + + public int getFlourishXpMod() { + return flourishXpMod; + } + + public int getHealMindWound() { + return healMindWound; + } + + public int getHealShockWound() { + return healShockWound; + } + + public String getRequiredSkillMod() { + return requiredSkillMod; + } + + public int getRequiredSkillModValue() { + return requiredSkillModValue; + } + + public String getMainloop() { + return mainloop; + } + + public String[] getFlourishes() { + return flourishes.clone(); + } + + public String getIntro() { + return intro; + } + + public String getOutro() { + return outro; + } + } +} diff --git a/src/utility/java/com/projectswg/utility/packets/PacketRecord.java b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/TravelCostLoader.java similarity index 51% rename from src/utility/java/com/projectswg/utility/packets/PacketRecord.java rename to src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/TravelCostLoader.java index c6bffcc7b..4b51f1af5 100644 --- a/src/utility/java/com/projectswg/utility/packets/PacketRecord.java +++ b/src/main/java/com/projectswg/holocore/resources/support/data/server_info/loader/TravelCostLoader.java @@ -1,5 +1,5 @@ /*********************************************************************************** - * 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. * @@ -25,59 +25,74 @@ * along with Holocore. If not, see . * ***********************************************************************************/ -package com.projectswg.utility.packets; +package com.projectswg.holocore.resources.support.data.server_info.loader; -import com.projectswg.common.network.NetBuffer; -import com.projectswg.common.network.packets.PacketType; -import com.projectswg.common.network.packets.SWGPacket; -import com.projectswg.common.network.packets.swg.zone.object_controller.ObjectController; +import com.projectswg.common.data.location.Terrain; +import com.projectswg.holocore.resources.support.data.server_info.SdbLoader; +import com.projectswg.holocore.resources.support.data.server_info.SdbLoader.SdbResultSet; -import java.time.Instant; +import java.io.File; +import java.io.IOException; +import java.util.Collections; +import java.util.EnumMap; +import java.util.Map; -public class PacketRecord { +public final class TravelCostLoader extends DataLoader { - private final boolean server; - private final Instant time; - private final byte[] data; + private final Map> costs; - public PacketRecord(boolean server, Instant time, byte[] data) { - this.server = server; - this.time = time; - this.data = data; + TravelCostLoader() { + this.costs = new EnumMap<>(Terrain.class); } - public boolean isServer() { - return server; + public boolean isCostDefined(Terrain source) { + return costs.containsKey(source); } - public Instant getTime() { - return time; + public int getCost(Terrain source, Terrain destination) { + Map costMap = costs.get(source); + if (costMap == null) + return 0; + return costMap.getOrDefault(destination, 0); } - public byte[] getData() { - return data; - } - - public PacketType parseType() { - NetBuffer data = NetBuffer.wrap(this.data); - data.position(2); - return PacketType.fromCrc(data.getInt()); - } - - public SWGPacket parse() { - NetBuffer data = NetBuffer.wrap(this.data); - data.position(2); - PacketType type = PacketType.fromCrc(data.getInt()); - data.position(0); - SWGPacket packet; - if (type == PacketType.OBJECT_CONTROLLER) { - return ObjectController.decodeController(data); - } else { - packet = PacketType.getForCrc(type.getCrc()); - if (packet != null) - packet.decode(data); - return packet; + @Override + public final void load() throws IOException { + try (SdbResultSet set = SdbLoader.load(new File("serverdata/travel/travel_costs.sdb"))) { + while (set.next()) { + TravelCostInfo travel = new TravelCostInfo(set); + costs.put(travel.getPlanet(), travel.getCosts()); + } + } + for (Terrain key : costs.keySet()) { + for (Map costMap : costs.values()) { + assert costMap.keySet().equals(costs.keySet()) : "planet "+key+" is improperly defined in travel_costs.sdb"; + } } } + public static class TravelCostInfo { + + private final Terrain planet; + private final EnumMap costs; + + public TravelCostInfo(SdbResultSet set) { + this.planet = Terrain.getTerrainFromName(set.getText("planet")); + this.costs = new EnumMap<>(Terrain.class); + for (String col : set.getColumns()) { + if (col.equalsIgnoreCase("planet")) + continue; + costs.put(Terrain.getTerrainFromName(col), (int) set.getInt(col)); + } + } + + public Terrain getPlanet() { + return planet; + } + + public Map getCosts() { + return Collections.unmodifiableMap(costs); + } + + } } diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/FindFriendCallback.java b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/FindFriendCallback.java index e88f043fa..e1e59be94 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/FindFriendCallback.java +++ b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/FindFriendCallback.java @@ -93,7 +93,8 @@ public class FindFriendCallback implements ICmdCallback { waypoint.setPosition(location.getTerrain(), location.getX(), location.getY(), location.getZ()); waypoint.setColor(WaypointColor.PURPLE); waypoint.setName(friendName); - ghost.addWaypoint(waypoint); + if (!ghost.addWaypoint(waypoint)) + SystemMessageIntent.broadcastPersonal(player, "@base_player:too_many_waypoints"); new ObjectCreatedIntent(waypoint).broadcast(); new SystemMessageIntent(player, new ProsePackage(new StringId("ui_cmnty", "friend_location_create_new_wp"), "TU", friendName)).broadcast(); } else { diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/RequestWaypointCmdCallback.java b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/RequestWaypointCmdCallback.java index 24b634c72..87aab41b4 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/RequestWaypointCmdCallback.java +++ b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/RequestWaypointCmdCallback.java @@ -65,7 +65,8 @@ public class RequestWaypointCmdCallback implements ICmdCallback { waypoint.setPosition(terrain, position.getX(), position.getY(), position.getZ()); waypoint.setName(name); waypoint.setColor(color); - player.getPlayerObject().addWaypoint(waypoint); + if (!player.getPlayerObject().addWaypoint(waypoint)) + SystemMessageIntent.broadcastPersonal(player, "@base_player:too_many_waypoints"); ObjectCreatedIntent.broadcast(waypoint); } } diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/WaypointCmdCallback.java b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/WaypointCmdCallback.java index 6e6553849..4e80e87e8 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/WaypointCmdCallback.java +++ b/src/main/java/com/projectswg/holocore/resources/support/global/commands/callbacks/WaypointCmdCallback.java @@ -166,7 +166,8 @@ public class WaypointCmdCallback implements ICmdCallback { if (name == null || name.isEmpty()) name = "Waypoint"; - ghost.addWaypoint(createWaypoint(color, name, location.build())); + if (!ghost.addWaypoint(createWaypoint(color, name, location.build()))) + SystemMessageIntent.broadcastPersonal(player, "@base_player:too_many_waypoints"); if (differentPlanetMessage) { new SystemMessageIntent(player, "Waypoint: New waypoint \""+ name + "\" created for location " diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/network/BaselineObject.java b/src/main/java/com/projectswg/holocore/resources/support/global/network/BaselineObject.java index 66df90622..743067966 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/global/network/BaselineObject.java +++ b/src/main/java/com/projectswg/holocore/resources/support/global/network/BaselineObject.java @@ -75,9 +75,6 @@ public class BaselineObject { case 6: parseBaseline6(buffer); break; - case 7: - parseBaseline7(buffer); - break; case 8: parseBaseline8(buffer); break; @@ -107,10 +104,6 @@ public class BaselineObject { return createBaseline(target, 6, this::createBaseline6); } - public Baseline createBaseline7(Player target) { - return createBaseline(target, 7, this::createBaseline7); - } - public Baseline createBaseline8(Player target) { return createBaseline(target, 8, this::createBaseline8); } @@ -155,15 +148,6 @@ public class BaselineObject { } - /** - * Creates the seventh baseline for the specified target. This baseline is sent when the object is used. - * @param target the target to prepare the baseline for - * @param data the baseline to build - */ - protected void createBaseline7(Player target, BaselineBuilder data) { - - } - /** * Creates the eighth baseline for the specified target. Only sent if the target has some permissions. * @param target the target to prepare the baseline for @@ -198,10 +182,6 @@ public class BaselineObject { } - protected void parseBaseline7(NetBuffer buffer) { - - } - protected void parseBaseline8(NetBuffer buffer) { } diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/player/PlayerFlags.java b/src/main/java/com/projectswg/holocore/resources/support/global/player/PlayerFlags.java index 3cfb26c99..c7701cceb 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/global/player/PlayerFlags.java +++ b/src/main/java/com/projectswg/holocore/resources/support/global/player/PlayerFlags.java @@ -26,23 +26,43 @@ ***********************************************************************************/ package com.projectswg.holocore.resources.support.global.player; +import java.util.BitSet; +import java.util.EnumSet; +import java.util.Set; + public enum PlayerFlags { /** Marks as Looking for a Group */ - LFG (0), + LFG (0), /** Marks as Helper */ - HELPER (1), + HELPER (1), /** Marks as Roleplayer */ - ROLEPLAYER (2), + ROLEPLAYER (2), + /** - */ + FACTION (3), + /** - */ + SPECIES (4), + /** - */ + TITLE (5), + /** - */ + FRIEND (6), /** Away from Keyboard */ - AFK (7), - /** Logged Out */ - LD (8), + AFK (7), + /** Logged Out / Link Dead */ + LD (8), /** Display the Faction Rank */ - FACTIONRANK (9), + FACTIONRANK (9), + /** Display the player's location in the matchmaking search */ + DISPLAY_LOCATION_IN_SEARCH (10), /** Marks as Out of Character */ - OOC (11), + OOC (11), + /** - */ + SEARCH_BY_SOURCE_GALAXY (12), /** Marks as Looking for Work */ - LFW (13); + LFW (13), + /** - */ + ANONYMOUS (127); + + private static final PlayerFlags [] FLAGS = values(); private int flag; @@ -53,4 +73,22 @@ public enum PlayerFlags { public int getFlag() { return flag; } + + public static BitSet bitsetFromFlags(Set flags) { + BitSet ret = new BitSet(128); + for (PlayerFlags flag : flags) { + ret.set(flag.flag); + } + return ret; + } + + public static Set flagsFromBitset(BitSet bitset) { + Set ret = EnumSet.noneOf(PlayerFlags.class); + for (PlayerFlags flag : FLAGS) { + if (bitset.get(flag.flag)) + ret.add(flag); + } + return ret; + } + } diff --git a/src/main/java/com/projectswg/holocore/resources/support/global/zone/creation/CharacterCreation.java b/src/main/java/com/projectswg/holocore/resources/support/global/zone/creation/CharacterCreation.java index 064d0b19e..6d62830c9 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/global/zone/creation/CharacterCreation.java +++ b/src/main/java/com/projectswg/holocore/resources/support/global/zone/creation/CharacterCreation.java @@ -50,7 +50,7 @@ import com.projectswg.holocore.services.support.objects.ObjectStorageService.Bui import me.joshlarson.jlcommon.utilities.Arguments; import org.jetbrains.annotations.NotNull; -import java.util.Calendar; +import java.time.Instant; public class CharacterCreation { @@ -165,8 +165,7 @@ public class CharacterCreation { private void setPlayerObjectValues(PlayerObject playerObj) { playerObj.setProfession(create.getProfession()); - Calendar date = Calendar.getInstance(); - playerObj.setBornDate(date.get(Calendar.YEAR), date.get(Calendar.MONTH) + 1, date.get(Calendar.DAY_OF_MONTH)); + playerObj.setBornDate(Instant.now()); } private void createStarterClothing(CreatureObject creature, String race) { diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/AdminPermissions.java b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/AdminPermissions.java index bbb992b82..6d42ee1f1 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/AdminPermissions.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/AdminPermissions.java @@ -74,12 +74,12 @@ public final class AdminPermissions implements ContainerPermissions { } @Override - public void read(MongoData data) { + public void readMongo(MongoData data) { } @Override - public void save(MongoData data) { + public void saveMongo(MongoData data) { } diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ContainerPermissions.java b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ContainerPermissions.java index 43fc4e186..209705f03 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ContainerPermissions.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ContainerPermissions.java @@ -64,7 +64,7 @@ public interface ContainerPermissions extends Persistable, MongoPersistable { static MongoData save(MongoData data, ContainerPermissions permissions) { data.putString("type", permissions.getType().name()); - permissions.save(data); + permissions.saveMongo(data); return data; } diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/DefaultPermissions.java b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/DefaultPermissions.java index 07546076e..0e35e6886 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/DefaultPermissions.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/DefaultPermissions.java @@ -86,12 +86,12 @@ public final class DefaultPermissions implements ContainerPermissions { } @Override - public void read(MongoData data) { + public void readMongo(MongoData data) { } @Override - public void save(MongoData data) { + public void saveMongo(MongoData data) { } diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ReadOnlyPermissions.java b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ReadOnlyPermissions.java index e2b8224e9..a8344245e 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ReadOnlyPermissions.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ReadOnlyPermissions.java @@ -64,7 +64,7 @@ public final class ReadOnlyPermissions implements ContainerPermissions { private ReadOnlyPermissions(MongoData data) { this.exempt = new HashSet<>(); this.locked = false; - read(data); + readMongo(data); this.locked = true; } @@ -108,7 +108,7 @@ public final class ReadOnlyPermissions implements ContainerPermissions { } @Override - public void read(MongoData data) { + public void readMongo(MongoData data) { if (locked) throw new IllegalStateException("Permissions is already locked"); for (long id : data.getArray("exempt", Long.class)) @@ -116,7 +116,7 @@ public final class ReadOnlyPermissions implements ContainerPermissions { } @Override - public void save(MongoData data) { + public void saveMongo(MongoData data) { data.putArray("exempt", exempt.stream().map(SWGObject::getObjectId).collect(Collectors.toList())); } diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ReadWritePermissions.java b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ReadWritePermissions.java index 2e91294d6..0226c3e07 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ReadWritePermissions.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/permissions/ReadWritePermissions.java @@ -68,7 +68,7 @@ public final class ReadWritePermissions implements ContainerPermissions { private ReadWritePermissions(MongoData data) { this.allowed = new HashSet<>(); this.locked = false; - read(data); + readMongo(data); this.locked = true; } @@ -112,14 +112,14 @@ public final class ReadWritePermissions implements ContainerPermissions { } @Override - public void read(MongoData data) { + public void readMongo(MongoData data) { if (locked) throw new IllegalStateException("Permissions is already locked"); allowed.addAll(data.getArray("allowed", Long.class)); } @Override - public void save(MongoData data) { + public void saveMongo(MongoData data) { data.putArray("allowed", new ArrayList<>(allowed)); } diff --git a/src/main/java/com/projectswg/holocore/resources/support/objects/radial/terminal/TerminalBankRadial.java b/src/main/java/com/projectswg/holocore/resources/support/objects/radial/terminal/TerminalBankRadial.java index a510a0ebf..709fd8884 100644 --- a/src/main/java/com/projectswg/holocore/resources/support/objects/radial/terminal/TerminalBankRadial.java +++ b/src/main/java/com/projectswg/holocore/resources/support/objects/radial/terminal/TerminalBankRadial.java @@ -11,6 +11,7 @@ import com.projectswg.holocore.resources.support.global.zone.sui.SuiWindow; import com.projectswg.holocore.resources.support.objects.radial.RadialHandlerInterface; import com.projectswg.holocore.resources.support.objects.swg.SWGObject; import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject; +import com.projectswg.holocore.resources.support.objects.swg.player.PlayerObject; import com.projectswg.holocore.utilities.IntentFactory; import java.util.ArrayList; @@ -27,6 +28,7 @@ public class TerminalBankRadial implements RadialHandlerInterface { @Override public void getOptions(Collection options, Player player, SWGObject target) { CreatureObject creature = player.getCreatureObject(); + PlayerObject playerObject = player.getPlayerObject(); { List useOptions = new ArrayList<>(); @@ -47,7 +49,7 @@ public class TerminalBankRadial implements RadialHandlerInterface { List reserveOptions = new ArrayList<>(); if (creature.getBankBalance() >= 1E9 || creature.getCashBalance() >= 1E9) reserveOptions.add(RadialOption.create(RadialItem.SERVER_MENU49, "@sui:bank_galactic_reserve_deposit")); - if (creature.getReserveBalance() > 0) + if (playerObject != null && playerObject.getGalacticReserveDeposit() > 0) reserveOptions.add(RadialOption.create(RadialItem.SERVER_MENU48, "@sui:bank_galactic_reserve_withdraw")); options.add(RadialOption.createSilent(RadialItem.SERVER_MENU50, "@sui:bank_galactic_reserve", reserveOptions)); @@ -133,15 +135,19 @@ public class TerminalBankRadial implements RadialHandlerInterface { SystemMessageIntent.broadcastPersonal(player, "You have to wait to perform another Galactic Reserve transaction"); return; } - long amount = creature.getBankBalance(); - if (amount > 1E9) - amount = (long) 1E9; - if (creature.getReserveBalance() + amount > 3E9 || amount == 0) { + PlayerObject playerObject = player.getPlayerObject(); + if (playerObject == null) { + SystemMessageIntent.broadcastPersonal(player, "Internal server error with loading your player"); + return; + } + long updatedBank = creature.getBankBalance() - 1_000_000_000L; + byte reserveBalance = playerObject.getGalacticReserveDeposit(); + if (reserveBalance >= 3 || updatedBank < 0) { SystemMessageIntent.broadcastPersonal(player, "@error_message:bank_deposit"); return; } - creature.setBankBalance((creature.getBankBalance() - amount)); - creature.setReserveBalance((creature.getReserveBalance() + amount)); + creature.setBankBalance(updatedBank); + playerObject.setGalacticReserveDeposit((byte) (reserveBalance + 1)); creature.updateLastGalacticReserveTime(); } @@ -150,15 +156,19 @@ public class TerminalBankRadial implements RadialHandlerInterface { SystemMessageIntent.broadcastPersonal(player, "You have to wait to perform another Galactic Reserve transaction"); return; } - long amount = creature.getReserveBalance(); - if (amount > 1E9) - amount = (long) 1E9; - if (creature.getBankBalance() + amount > 2E9 || amount == 0) { - SystemMessageIntent.broadcastPersonal(player, "@error_message:bank_withdraw"); + PlayerObject playerObject = player.getPlayerObject(); + if (playerObject == null) { + SystemMessageIntent.broadcastPersonal(player, "Internal server error with loading your player"); return; } - creature.setBankBalance(creature.getBankBalance() + amount); - creature.setReserveBalance(creature.getReserveBalance() - amount); + long updatedBank = creature.getBankBalance() + 1_000_000_000L; + byte reserveBalance = playerObject.getGalacticReserveDeposit(); + if (reserveBalance <= 0 || updatedBank >= 2_000_000_000L) { + SystemMessageIntent.broadcastPersonal(player, "@error_message:bank_deposit"); + return; + } + creature.setBankBalance(updatedBank); + playerObject.setGalacticReserveDeposit((byte) (reserveBalance - 1)); creature.updateLastGalacticReserveTime(); } diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/BuffService.java b/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/BuffService.java index a94080574..ab2642909 100644 --- a/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/BuffService.java +++ b/src/main/java/com/projectswg/holocore/services/gameplay/combat/buffs/BuffService.java @@ -33,16 +33,15 @@ import com.projectswg.holocore.intents.gameplay.combat.CreatureKilledIntent; import com.projectswg.holocore.intents.gameplay.combat.buffs.BuffIntent; import com.projectswg.holocore.intents.gameplay.player.experience.skills.SkillModIntent; import com.projectswg.holocore.intents.support.global.zone.PlayerEventIntent; -import com.projectswg.holocore.resources.gameplay.combat.buff.BuffData; -import com.projectswg.holocore.resources.gameplay.combat.buff.BuffMap; import com.projectswg.holocore.resources.support.data.server_info.StandardLog; +import com.projectswg.holocore.resources.support.data.server_info.loader.BuffLoader.BuffInfo; +import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader; import com.projectswg.holocore.resources.support.global.player.Player; import com.projectswg.holocore.resources.support.objects.swg.creature.Buff; import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject; import me.joshlarson.jlcommon.concurrency.BasicScheduledThread; import me.joshlarson.jlcommon.control.IntentHandler; import me.joshlarson.jlcommon.control.Service; -import me.joshlarson.jlcommon.log.Log; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -52,8 +51,8 @@ import java.util.stream.Stream; public class BuffService extends Service { /* - * TODO allow removal of buffs with BuffData where PLAYER_REMOVABLE == 1 - * TODO remove buffs on respec. Listen for respec event and remove buffs with BuffData where + * TODO allow removal of buffs with BuffInfo where PLAYER_REMOVABLE == 1 + * TODO remove buffs on respec. Listen for respec event and remove buffs with BuffInfo where * REMOVE_ON_RESPEC == 1 * TODO remove group buff(s) from receiver when distance between caster and receiver is 100m. * Perform same check upon zoning in. Skillmod1 effect name is "group" @@ -61,20 +60,10 @@ public class BuffService extends Service { private final BasicScheduledThread timerCheckThread; private final Set monitored; - private final BuffMap dataMap; // All CRCs are lower-cased buff names! public BuffService() { timerCheckThread = new BasicScheduledThread("buff-timer-check", this::checkBuffTimers); monitored = new HashSet<>(); - dataMap = new BuffMap(); - } - - @Override - public boolean initialize() { - long startTime = StandardLog.onStartLoad("buffs"); - dataMap.load(); - StandardLog.onEndLoad(dataMap.size(), "buffs", startTime); - return super.initialize(); } @Override @@ -101,9 +90,9 @@ public class BuffService extends Service { @IntentHandler private void handleBuffIntent(BuffIntent bi) { - BuffData buffData = getBuff(bi.getBuffName()); + BuffInfo buffData = getBuff(bi.getBuffName()); Objects.requireNonNull(buffData, "No known buff: " + bi.getBuffName()); - assert buffData.getName().equals(bi.getBuffName()) : "BuffIntent name ["+bi.getBuffName()+"] does not match BuffData name ["+buffData.getName()+ ']'; + assert buffData.getName().equals(bi.getBuffName()) : "BuffIntent name ["+bi.getBuffName()+"] does not match BuffInfo name ["+buffData.getName()+ ']'; if (bi.isRemove()) { removeBuff(bi.getReceiver(), buffData, false); } else { @@ -186,11 +175,11 @@ public class BuffService extends Service { } private boolean isBuffRemovedOnDeath(Buff buff) { - return getBuff(buff).isRemovedOnDeath(); + return getBuff(buff).isRemoveOnDeath(); } - private boolean isBuffInfinite(BuffData buffData) { - return buffData.getDefaultDuration() < 0; + private boolean isBuffInfinite(BuffInfo buffData) { + return buffData.getDuration() < 0; } private boolean isCreatureBuffed(CreatureObject creature) { @@ -207,9 +196,9 @@ public class BuffService extends Service { buffStream.forEach(buff -> removeBuff(creature, getBuff(buff), true)); } - private void addBuff(CreatureObject receiver, @NotNull BuffData buffData, CreatureObject buffer) { - String groupName = buffData.getGroupName(); - Optional groupBuff = receiver.getBuffEntries(buff -> groupName.equals(getBuff(buff).getGroupName())).findAny(); + private void addBuff(CreatureObject receiver, @NotNull BuffInfo buffData, CreatureObject buffer) { + String groupName = buffData.getGroup1(); + Optional groupBuff = receiver.getBuffEntries(buff -> groupName.equals(getBuff(buff).getGroup1())).findAny(); int applyTime = calculatePlayTime(receiver); @@ -224,8 +213,8 @@ public class BuffService extends Service { checkStackCount(receiver, buff, applyTime, 1); } } else { - BuffData oldBuff = getBuff(buff); - if (buffData.getGroupPriority() >= oldBuff.getGroupPriority()) { + BuffInfo oldBuff = getBuff(buff); + if (buffData.getPriority() >= oldBuff.getPriority()) { removeBuff(receiver, oldBuff, true); applyBuff(receiver, buffer, buffData, applyTime); } @@ -235,7 +224,7 @@ public class BuffService extends Service { } } - private void removeBuff(CreatureObject creature, @NotNull BuffData buffData, boolean expired) { + private void removeBuff(CreatureObject creature, @NotNull BuffInfo buffData, boolean expired) { Optional optionalEntry = creature.getBuffEntries(buff -> buff.getCrc() == buffData.getCrc()).findAny(); if (!optionalEntry.isPresent()) return; // Obique: Used to be an assertion, however if a service sends the removal after it expires it would assert - so I just removed it. @@ -257,7 +246,7 @@ public class BuffService extends Service { } private void checkStackCount(CreatureObject receiver, Buff buff, int applyTime, int stackMod) { - BuffData buffData = getBuff(buff); + BuffInfo buffData = getBuff(buff); Objects.requireNonNull(buffData, "No known buff: " + buff.getCrc()); // If it's the same buff, we need to check for stacks @@ -279,26 +268,26 @@ public class BuffService extends Service { // If the stack count was incremented, also renew the duration if (stackMod > 0) { - receiver.setBuffDuration(crc, applyTime, (int) buffData.getDefaultDuration()); + receiver.setBuffDuration(crc, applyTime, (int) buffData.getDuration()); } } - private void applyBuff(CreatureObject receiver, CreatureObject buffer, BuffData buffData, int applyTime) { + private void applyBuff(CreatureObject receiver, CreatureObject buffer, BuffInfo buffData, int applyTime) { // TODO stack counts upon add/remove need to be defined on a per-buff basis due to skillmod influence. Scripts might not be a bad idea. int stackCount = 1; - int buffDuration = (int) buffData.getDefaultDuration(); + int buffDuration = (int) buffData.getDuration(); { Player bufferPlayer = buffer.getOwner(); String bufferUsername = bufferPlayer == null ? "NULL" : bufferPlayer.getUsername(); StandardLog.onPlayerTrace(this, receiver, "received buff '%s' from %s/%s; applyTime: %d, buffDuration: %d", buffData.getName(), bufferUsername, buffer.getObjectName(), applyTime, buffDuration); } - Buff buff = new Buff(buffData.getCrc(), applyTime + buffDuration, buffData.getEffectValue(0), buffDuration, buffer.getObjectId(), stackCount); + Buff buff = new Buff(buffData.getCrc(), applyTime + buffDuration, (float) buffData.getEffectValue(0), buffDuration, buffer.getObjectId(), stackCount); checkSkillMods(buffData, receiver, 1); receiver.addBuff(buff); - sendParticleEffect(buffData.getEffectFileName(), receiver, buffData.getParticleHardPoint()); - sendParticleEffect(buffData.getStanceParticle(), receiver, buffData.getParticleHardPoint()); + sendParticleEffect(buffData.getParticle(), receiver, buffData.getParticleHardpoint()); + sendParticleEffect(buffData.getStanceParticle(), receiver, buffData.getParticleHardpoint()); addToMonitored(receiver); } @@ -309,19 +298,19 @@ public class BuffService extends Service { } } - private void checkCallback(BuffData buffData, CreatureObject creature) { + private void checkCallback(BuffInfo buffData, CreatureObject creature) { String callback = buffData.getCallback(); if (callback.equals("none")) { return; } - if (dataMap.containsBuff(callback)) { + if (DataLoader.buffs().containsBuff(callback)) { addBuff(creature, getBuff(callback), creature); } } - private void checkSkillMods(BuffData buffData, CreatureObject creature, int valueFactor) { + private void checkSkillMods(BuffInfo buffData, CreatureObject creature, int valueFactor) { /* * TODO Check effectName == "group". If yes, every group member within 100m range (maybe * just the ones aware of the buffer) receive the buff. Once outside range, buff needs @@ -331,19 +320,19 @@ public class BuffService extends Service { sendSkillModIntent(creature, buffData.getEffectName(i), buffData.getEffectValue(i), valueFactor); } - private void sendSkillModIntent(CreatureObject creature, String effectName, float effectValue, int valueFactor) { + private void sendSkillModIntent(CreatureObject creature, String effectName, double effectValue, int valueFactor) { if (!effectName.isEmpty()) new SkillModIntent(effectName, 0, (int) effectValue * valueFactor, creature).broadcast(); } @Nullable - private BuffData getBuff(String name) { - return dataMap.getBuff(name); + private BuffInfo getBuff(String name) { + return DataLoader.buffs().getBuff(name); } @Nullable - private BuffData getBuff(@NotNull Buff buff) { - return dataMap.getBuff(buff.getCrc()); + private BuffInfo getBuff(@NotNull Buff buff) { + return DataLoader.buffs().getBuff(buff.getCrc()); } } diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/entertainment/EntertainmentService.java b/src/main/java/com/projectswg/holocore/services/gameplay/entertainment/EntertainmentService.java index 13e6bcc9b..dbe2fe3f2 100644 --- a/src/main/java/com/projectswg/holocore/services/gameplay/entertainment/EntertainmentService.java +++ b/src/main/java/com/projectswg/holocore/services/gameplay/entertainment/EntertainmentService.java @@ -30,8 +30,6 @@ import com.projectswg.common.data.encodables.oob.ProsePackage; import com.projectswg.common.data.encodables.oob.StringId; import com.projectswg.common.data.encodables.tangible.Posture; import com.projectswg.common.data.location.Location; -import com.projectswg.common.data.swgfile.ClientFactory; -import com.projectswg.common.data.swgfile.visitors.DatatableData; import com.projectswg.common.network.packets.swg.zone.object_controller.Animation; import com.projectswg.holocore.intents.gameplay.entertainment.dance.DanceIntent; import com.projectswg.holocore.intents.gameplay.entertainment.dance.FlourishIntent; @@ -40,6 +38,8 @@ import com.projectswg.holocore.intents.gameplay.player.experience.ExperienceInte import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent; import com.projectswg.holocore.intents.support.global.zone.PlayerEventIntent; import com.projectswg.holocore.intents.support.global.zone.PlayerTransformedIntent; +import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader; +import com.projectswg.holocore.resources.support.data.server_info.loader.PerformanceLoader.PerformanceInfo; import com.projectswg.holocore.resources.support.global.player.Player; import com.projectswg.holocore.resources.support.objects.swg.SWGObject; import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject; @@ -65,39 +65,14 @@ public class EntertainmentService extends Service { private static final byte XP_CYCLE_RATE = 10; private static final byte WATCH_RADIUS = 20; - private final Map performanceMap; // performance names mapped to performance data private final Map performerMap; - private final Map danceMap; // Maps performance ID to performance name private final ScheduledExecutorService executorService; public EntertainmentService() { - performanceMap = new HashMap<>(); performerMap = new HashMap<>(); // TODO synchronize access? - danceMap = new HashMap<>(); executorService = Executors.newSingleThreadScheduledExecutor(); } - @Override - public boolean initialize() { - DatatableData performanceTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/performance/performance.iff"); - - for (int i = 0; i < performanceTable.getRowCount(); i++) { - String requiredDance = (String) performanceTable.getCell(i, 4); - - // Load the dances only. Music is currently unsupported. - if (!requiredDance.isEmpty()) { - String performanceName = (String) performanceTable.getCell(i, 0); - String performanceNumber = String.valueOf(performanceTable.getCell(i, 5)); // danceVisualId - PerformanceData performanceData = new PerformanceData(performanceNumber, (int) performanceTable.getCell(i, 10)); // flourishXpMod - - performanceMap.put(performanceName, performanceData); // Map the name to the performance data - danceMap.put(performanceNumber, performanceName); // Map the dance ID to a performance name! - } - } - - return super.initialize(); - } - @Override public boolean terminate() { executorService.shutdownNow(); @@ -116,7 +91,7 @@ public class EntertainmentService extends Service { if (!changeDance && dancer.isPerforming()) { new SystemMessageIntent(dancer.getOwner(), "@performance:already_performing_self").broadcast(); - } else if (performanceMap.containsKey(danceName)) { + } else if (DataLoader.performances().getPerformanceByName(danceName) != null) { // The dance name is valid. if (dancer.hasAbility("startDance+" + danceName)) { @@ -157,7 +132,7 @@ public class EntertainmentService extends Service { case PE_ZONE_IN_SERVER: // We need to check if they're dancing in order to start giving them XP if (isEntertainer(creature) && creature.getPosture().equals(Posture.SKILL_ANIMATING)) { - scheduleExperienceTask(creature, danceMap.get(creature.getAnimation().replace("dance_", ""))); + scheduleExperienceTask(creature, DataLoader.performances().getPerformanceByDanceId(Integer.parseInt(creature.getAnimation().replace("dance_", ""))).getPerformanceName()); } break; @@ -311,7 +286,7 @@ public class EntertainmentService extends Service { } private void startDancing(CreatureObject dancer, String danceName) { - dancer.setAnimation("dance_" + performanceMap.get(danceName).getPerformanceId()); + dancer.setAnimation("dance_" + DataLoader.performances().getPerformanceByName(danceName).getPerformanceName()); dancer.setPerformanceId(0); // 0 - anything else will make it look like we're playing music dancer.setPerformanceCounter(0); dancer.setPerforming(true); @@ -342,7 +317,7 @@ public class EntertainmentService extends Service { private void changeDance(CreatureObject dancer, String newPerformanceName) { performerMap.get(dancer.getObjectId()).setPerformanceName(newPerformanceName); - dancer.setAnimation("dance_" + performanceMap.get(newPerformanceName).getPerformanceId()); + dancer.setAnimation("dance_" + DataLoader.performances().getPerformanceByName(newPerformanceName).getPerformanceName()); } private void startWatching(CreatureObject actor, CreatureObject creature) { @@ -448,7 +423,7 @@ public class EntertainmentService extends Service { } String performanceName = performance.getPerformanceName(); - PerformanceData performanceData = performanceMap.get(performanceName); + PerformanceInfo performanceData = DataLoader.performances().getPerformanceByName(performanceName); int flourishXpMod = performanceData.getFlourishXpMod(); int performanceCounter = performer.getPerformanceCounter(); int xpGained = performanceCounter * flourishXpMod; diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/gcw/faction/CivilWarService.java b/src/main/java/com/projectswg/holocore/services/gameplay/gcw/faction/CivilWarService.java index 2c4a2ccc4..9fe0f639c 100644 --- a/src/main/java/com/projectswg/holocore/services/gameplay/gcw/faction/CivilWarService.java +++ b/src/main/java/com/projectswg/holocore/services/gameplay/gcw/faction/CivilWarService.java @@ -208,7 +208,7 @@ public class CivilWarService extends Service { private void changeRank(PlayerObject playerObject, int newRank) { assert (newRank < 1 || newRank > 12); - int oldRank = playerObject.getCurrentRank(); + int oldRank = playerObject.getCurrentGcwRank(); assert (oldRank > 1); @@ -218,7 +218,7 @@ public class CivilWarService extends Service { PvpFaction faction = creature.getPvpFaction(); int abilityIndex = faction == PvpFaction.IMPERIAL ? IMPERIAL_INDEX : REBEL_INDEX; - playerObject.setCurrentRank(newRank); + playerObject.setCurrentGcwRank(newRank); if (oldRank > newRank) { // They've been demoted @@ -258,8 +258,8 @@ public class CivilWarService extends Service { } private void updateRank(PlayerObject playerObject) { - int currentRank = playerObject.getCurrentRank(); - float oldProgress = playerObject.getRankProgress(); + int currentRank = playerObject.getCurrentGcwRank(); + float oldProgress = playerObject.getCurrentGcwRankProgress(); int points = playerObject.getGcwPoints(); float decay = 0; @@ -270,10 +270,10 @@ public class CivilWarService extends Service { float newProgress = rankProgress(oldProgress, decay, currentRank, points); if (newProgress >= 100) { - int promotion = playerObject.getCurrentRank() + 1; + int promotion = playerObject.getCurrentGcwRank() + 1; if (promotion > 12) { // 12 is the max rank - playerObject.setRankProgress(99.99F); + playerObject.setCurrentGcwRankProgress(99.99F); moveToLifetime(playerObject, points); return; } @@ -292,17 +292,17 @@ public class CivilWarService extends Service { return; } else { // They've ranked up, but cannot rank up again - playerObject.setRankProgress(nextRankProgress); + playerObject.setCurrentGcwRankProgress(nextRankProgress); } } else if (newProgress > 0) { // Set their new progress - playerObject.setRankProgress(newProgress); + playerObject.setCurrentGcwRankProgress(newProgress); } else if (newProgress < 0 && isRankDown(oldProgress, newProgress)) { - int demotion = playerObject.getCurrentRank() - 1; + int demotion = playerObject.getCurrentGcwRank() - 1; if (demotion < 1) { // 1 is the minimum rank - playerObject.setRankProgress(0); + playerObject.setCurrentGcwRankProgress(0); return; } @@ -321,7 +321,7 @@ public class CivilWarService extends Service { return; } else { // They've ranked down, but cannot rank down again - playerObject.setRankProgress(nextRankProgress); + playerObject.setCurrentGcwRankProgress(nextRankProgress); } } @@ -330,7 +330,7 @@ public class CivilWarService extends Service { private void updateRanks() { playerObjects.forEach(playerObject -> { - if (playerObject.getCurrentRank() > 0) + if (playerObject.getCurrentGcwRank() > 0) updateRank(playerObject); }); @@ -431,14 +431,14 @@ public class CivilWarService extends Service { if (fi.getNewFaction() == PvpFaction.NEUTRAL) { // They've left the imperials or rebels and must have rank removed - playerObject.setCurrentRank(0); + playerObject.setCurrentGcwRank(0); int points = playerObject.getGcwPoints(); moveToLifetime(playerObject, points); } else { // They've joined the imperials or rebels and become Privates - playerObject.setCurrentRank(1); + playerObject.setCurrentGcwRank(1); } } diff --git a/src/main/java/com/projectswg/holocore/services/gameplay/player/badge/BadgeService.java b/src/main/java/com/projectswg/holocore/services/gameplay/player/badge/BadgeService.java index 1297c38a3..9ca268275 100644 --- a/src/main/java/com/projectswg/holocore/services/gameplay/player/badge/BadgeService.java +++ b/src/main/java/com/projectswg/holocore/services/gameplay/player/badge/BadgeService.java @@ -63,15 +63,12 @@ public class BadgeService extends Service { } private void grantBadge(PlayerObject player, int beginSlotId, String collectionName, boolean isHidden, String slotName) { - BitSet collections = BitSet.valueOf(player.getCollectionBadges()); - - collections.set(beginSlotId); - player.setCollectionBadges(collections.toByteArray()); + player.setCollectionFlag(beginSlotId); handleMessage(player, hasCompletedCollection(player, collectionName), collectionName, isHidden, slotName); } private void grantBadgeIncrement(PlayerObject player, int beginSlotId, int endSlotId, int maxSlotValue) { - BitSet collections = BitSet.valueOf(player.getCollectionBadges()); + BitSet collections = player.getCollectionBadges(); int binaryValue = 1; int curValue = 0; @@ -94,7 +91,7 @@ public class BadgeService extends Service { collections.clear(beginSlotId + i); } } - player.setCollectionBadges(collections.toByteArray()); + player.setCollectionFlags(collections); } } @@ -264,15 +261,12 @@ public class BadgeService extends Service { } private boolean hasBadge(PlayerObject player, int badgeBeginSlotId) { - BitSet collections = BitSet.valueOf(player.getCollectionBadges()); - - return collections.get(badgeBeginSlotId); + return player.getCollectionFlag(badgeBeginSlotId); } private boolean hasCompletedCollection(PlayerObject player, String collectionTitle) { - String collectionName = ""; - BitSet collections = BitSet.valueOf(player.getCollectionBadges()); + BitSet collections = player.getCollectionBadges(); for (int row = 0; row < collectionTable.getRowCount(); row++) { int beginSlotId = (int) collectionTable.getCell(row, 4); diff --git a/src/main/java/com/projectswg/holocore/services/support/data/dev/CustomObjectService.java b/src/main/java/com/projectswg/holocore/services/support/data/dev/CustomObjectService.java index a76fce9bc..be55adf85 100644 --- a/src/main/java/com/projectswg/holocore/services/support/data/dev/CustomObjectService.java +++ b/src/main/java/com/projectswg/holocore/services/support/data/dev/CustomObjectService.java @@ -6,6 +6,7 @@ import com.projectswg.holocore.intents.support.global.chat.SystemMessageIntent; import com.projectswg.holocore.intents.support.global.command.ExecuteCommandIntent; 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.loader.DataLoader; import com.projectswg.holocore.resources.support.global.commands.Command; import com.projectswg.holocore.resources.support.global.player.AccessLevel; import com.projectswg.holocore.resources.support.global.zone.sui.SuiButtons; @@ -92,21 +93,24 @@ public class CustomObjectService extends Service { } private ListBoxRecursive createListBoxRecursive() { - return createListBoxRecursive(new File("clientdata/object")); + return createListBoxRecursive(DataLoader.objectData().getObjects()); } - private ListBoxRecursive createListBoxRecursive(File start) { + private ListBoxRecursive createListBoxRecursive(Collection objects) { Map mapping = new TreeMap<>(); - File [] children = start.listFiles(); - assert children != null; - for (File file : children) { - if (file.isDirectory()) { - mapping.put(file.getName(), createListBoxRecursive(file)); - } else if (file.isFile() && file.getName().startsWith("shared_") && file.getName().endsWith(".iff")) { - String iff = file.getAbsolutePath().replace(new File("clientdata").getAbsolutePath()+File.separator, ""); - mapping.put(prettyIff(iff), iff.replace(File.separatorChar, '/')); - } + for (String iff : objects) { + mapping.put(prettyIff(iff), iff.replace(File.separatorChar, '/')); } +// File [] children = start.listFiles(); +// assert children != null; +// for (File file : children) { +// if (file.isDirectory()) { +// mapping.put(file.getName(), createListBoxRecursive(file)); +// } else if (file.isFile() && file.getName().startsWith("shared_") && file.getName().endsWith(".iff")) { +// String iff = file.getAbsolutePath().replace(new File("clientdata").getAbsolutePath()+File.separator, ""); +// mapping.put(prettyIff(iff), iff.replace(File.separatorChar, '/')); +// } +// } return new ListBoxRecursive(mapping); } diff --git a/src/main/java/com/projectswg/holocore/services/support/global/chat/ChatFriendService.java b/src/main/java/com/projectswg/holocore/services/support/global/chat/ChatFriendService.java index e86b6853d..f6c1065dc 100644 --- a/src/main/java/com/projectswg/holocore/services/support/global/chat/ChatFriendService.java +++ b/src/main/java/com/projectswg/holocore/services/support/global/chat/ChatFriendService.java @@ -117,7 +117,7 @@ public class ChatFriendService extends Service { } private void handleRequestFriendList(Player player) { - player.getPlayerObject().sendFriendsList(); + player.getPlayerObject().sendFriendList(); } /* Ignore List */ diff --git a/src/main/java/com/projectswg/holocore/services/support/global/zone/ConnectionService.java b/src/main/java/com/projectswg/holocore/services/support/global/zone/ConnectionService.java index 3a189e72e..7c1125887 100644 --- a/src/main/java/com/projectswg/holocore/services/support/global/zone/ConnectionService.java +++ b/src/main/java/com/projectswg/holocore/services/support/global/zone/ConnectionService.java @@ -127,14 +127,14 @@ public class ConnectionService extends Service { PlayerObject player = p.getPlayerObject(); if (player == null) return; - player.setFlagBitmask(PlayerFlags.LD); + player.setFlag(PlayerFlags.LD); } private void clearPlayerFlag(Player p) { PlayerObject player = p.getPlayerObject(); if (player == null) return; - player.clearFlagBitmask(PlayerFlags.LD); + player.clearFlag(PlayerFlags.LD); } private void zoneIn(Player p) { diff --git a/src/main/java/com/projectswg/holocore/services/support/objects/ObjectStorageService.java b/src/main/java/com/projectswg/holocore/services/support/objects/ObjectStorageService.java index 1d780ed5c..a04e24d30 100644 --- a/src/main/java/com/projectswg/holocore/services/support/objects/ObjectStorageService.java +++ b/src/main/java/com/projectswg/holocore/services/support/objects/ObjectStorageService.java @@ -13,10 +13,12 @@ import com.projectswg.holocore.resources.support.data.server_info.ObjectDatabase import com.projectswg.holocore.resources.support.data.server_info.StandardLog; import com.projectswg.holocore.resources.support.data.server_info.loader.BuildoutLoader; import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader; +import com.projectswg.holocore.resources.support.data.server_info.mongodb.users.PswgUserDatabase; import com.projectswg.holocore.resources.support.objects.swg.SWGObject; import com.projectswg.holocore.resources.support.objects.swg.building.BuildingObject; import com.projectswg.holocore.resources.support.objects.swg.cell.CellObject; import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject; +import me.joshlarson.jlcommon.concurrency.ScheduledThreadPool; import me.joshlarson.jlcommon.control.IntentHandler; import me.joshlarson.jlcommon.control.Service; import me.joshlarson.jlcommon.log.Log; @@ -24,6 +26,8 @@ import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import java.util.function.Function; @@ -31,6 +35,9 @@ import java.util.function.Function; public class ObjectStorageService extends Service { private final ObjectDatabase database; + private final PswgUserDatabase userDatabase; + private final ScheduledThreadPool persistenceThread; + private final Set persistedObjects; private final Map objectMap; private final Map buildouts; private final Map buildingLookup; @@ -38,6 +45,9 @@ public class ObjectStorageService extends Service { public ObjectStorageService() { this.database = new CachedObjectDatabase<>("odb/objects.db", SWGObjectFactory::create, SWGObjectFactory::save); + this.userDatabase = new PswgUserDatabase(); + this.persistenceThread = new ScheduledThreadPool(1, 3, "object-storage-service"); + this.persistedObjects = new CopyOnWriteArraySet<>(); this.objectMap = new ConcurrentHashMap<>(256*1024, 0.8f, Runtime.getRuntime().availableProcessors()); this.buildouts = new HashMap<>(128*1024, 1f); this.buildingLookup = new HashMap<>(); @@ -59,14 +69,17 @@ public class ObjectStorageService extends Service { database.traverse(this::loadObject); } + persistenceThread.start(); + persistenceThread.executeWithFixedDelay(TimeUnit.MINUTES.toMillis(5), TimeUnit.MINUTES.toMillis(5), this::saveObjects); started.set(true); return true; } @Override public boolean stop() { + persistenceThread.stop(); started.set(false); - return true; + return persistenceThread.awaitTermination(1000); } @Override @@ -75,6 +88,7 @@ public class ObjectStorageService extends Service { database.close(); } ObjectLookup.setObjectAuthority(null); + saveObjects(); return true; } @@ -84,6 +98,7 @@ public class ObjectStorageService extends Service { if (!database.load() && database.fileExists()) return false; } + database.traverse(persistedObjects::add); StandardLog.onEndLoad(database.size(), "players", startTime); return true; } @@ -129,6 +144,12 @@ public class ObjectStorageService extends Service { } } + private void saveObjects() { + for (SWGObject obj : persistedObjects) { + + } + } + @IntentHandler private void processObjectCreatedIntent(ObjectCreatedIntent intent) { SWGObject obj = intent.getObject(); @@ -205,6 +226,10 @@ public class ObjectStorageService extends Service { AUTHORITY.set(authority); } + public static boolean isDefined() { + return AUTHORITY.get() != null; + } + @Nullable public static SWGObject getObjectById(long id) { return AUTHORITY.get().apply(id); diff --git a/src/main/java/com/projectswg/holocore/utilities/clientdata_printer/ClientdataPrinterDatatable.java b/src/main/java/com/projectswg/holocore/utilities/clientdata_printer/ClientdataPrinterDatatable.java deleted file mode 100644 index 51d432238..000000000 --- a/src/main/java/com/projectswg/holocore/utilities/clientdata_printer/ClientdataPrinterDatatable.java +++ /dev/null @@ -1,55 +0,0 @@ -/*********************************************************************************** - * Copyright (c) 2018 /// Project SWG /// www.projectswg.com * - * * - * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * - * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * - * Our goal is to create an emulator which will provide a server for players to * - * continue playing a game similar to the one they used to play. We are basing * - * it on the final publish of the game prior to end-game events. * - * * - * This file is part of Holocore. * - * * - * --------------------------------------------------------------------------------* - * * - * Holocore is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * Holocore is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with Holocore. If not, see . * - ***********************************************************************************/ -package com.projectswg.holocore.utilities.clientdata_printer; - -import com.projectswg.common.data.swgfile.ClientFactory; -import com.projectswg.common.data.swgfile.visitors.DatatableData; -import me.joshlarson.jlcommon.log.Log; -import me.joshlarson.jlcommon.log.log_wrapper.ConsoleLogWrapper; - -public class ClientdataPrinterDatatable { - - public static void main(String [] args) { - Log.addWrapper(new ConsoleLogWrapper()); - printTable("datatables/buildout/areas_tatooine.iff"); - } - - private static void printTable(String table) { - DatatableData data = (DatatableData) ClientFactory.getInfoFromFile(table); - for (int col = 0; col < data.getColumnCount(); col++) { - System.out.print(data.getColumnName(col) + ','); - } - System.out.println(); - for (int row = 0; row < data.getRowCount(); row++) { - for (int col = 0; col < data.getColumnCount(); col++) { - System.out.print(data.getCell(row, col) + ","); - } - System.out.println(); - } - } - -} diff --git a/src/test/java/com/projectswg/holocore/resources/gameplay/world/travel/TestTravelHelper.java b/src/test/java/com/projectswg/holocore/resources/gameplay/world/travel/TestTravelHelper.java index d3d7a0d69..8674a3389 100644 --- a/src/test/java/com/projectswg/holocore/resources/gameplay/world/travel/TestTravelHelper.java +++ b/src/test/java/com/projectswg/holocore/resources/gameplay/world/travel/TestTravelHelper.java @@ -59,9 +59,9 @@ public class TestTravelHelper extends TestRunnerNoIntents { for (Terrain terrain : terrains) { Assert.assertEquals(100, helper.getTravelFee(terrain, terrain)); } - Assert.assertEquals(2000, helper.getTravelFee(Terrain.DATHOMIR, Terrain.CORELLIA)); + Assert.assertEquals(1250, helper.getTravelFee(Terrain.DATHOMIR, Terrain.CORELLIA)); Assert.assertEquals(2000, helper.getTravelFee(Terrain.CORELLIA, Terrain.DATHOMIR)); - Assert.assertEquals(1750, helper.getTravelFee(Terrain.NABOO, Terrain.ENDOR)); + Assert.assertEquals(4000, helper.getTravelFee(Terrain.NABOO, Terrain.ENDOR)); Assert.assertEquals(500, helper.getTravelFee(Terrain.TATOOINE, Terrain.NABOO)); Assert.assertFalse(helper.isValidRoute(Terrain.TATOOINE, Terrain.YAVIN4)); Assert.assertTrue(helper.isValidRoute(Terrain.TATOOINE, Terrain.LOK)); diff --git a/src/test/java/com/projectswg/holocore/resources/support/data/collections/TestSWGFlag.java b/src/test/java/com/projectswg/holocore/resources/support/data/collections/TestSWGFlag.java index 350bdc981..82f523498 100644 --- a/src/test/java/com/projectswg/holocore/resources/support/data/collections/TestSWGFlag.java +++ b/src/test/java/com/projectswg/holocore/resources/support/data/collections/TestSWGFlag.java @@ -27,14 +27,18 @@ package com.projectswg.holocore.resources.support.data.collections; +import com.projectswg.common.network.NetBuffer; import com.projectswg.holocore.test.runners.TestRunnerNoIntents; import org.junit.Assert; import org.junit.Test; +import java.nio.IntBuffer; +import java.util.BitSet; + public class TestSWGFlag extends TestRunnerNoIntents { @Test - public void testFlag() { + public void testFlagAccuracy() { SWGFlag flag = new SWGFlag(3, 16); flag.set(1); flag.set(4); @@ -47,6 +51,24 @@ public class TestSWGFlag extends TestRunnerNoIntents { Assert.assertEquals(1, ints[1]); Assert.assertEquals(1, ints[2]); Assert.assertEquals(1, ints[3]); + SWGFlag decoded = new SWGFlag(3, 16); + decoded.decode(NetBuffer.wrap(flag.encode())); + Assert.assertArrayEquals(flag.encode(), decoded.encode()); + } + + @Test + public void testFlagSize() { + byte [] encoded; + SWGFlag flag = new SWGFlag(3, 16); + + encoded = flag.encode(); + Assert.assertEquals(4, encoded.length); + + flag.set(1); + encoded = flag.encode(); + Assert.assertEquals(8, encoded.length); + Assert.assertEquals(1, encoded[0]); + Assert.assertEquals(2, encoded[4]); } } diff --git a/src/test/java/com/projectswg/holocore/resources/support/objects/swg/TestSWGPersistence.java b/src/test/java/com/projectswg/holocore/resources/support/objects/swg/TestSWGPersistence.java new file mode 100644 index 000000000..af827b2f4 --- /dev/null +++ b/src/test/java/com/projectswg/holocore/resources/support/objects/swg/TestSWGPersistence.java @@ -0,0 +1,289 @@ +/*********************************************************************************** + * Copyright (c) 2019 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * --------------------------------------------------------------------------------* + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + ***********************************************************************************/ + +package com.projectswg.holocore.resources.support.objects.swg; + +import com.projectswg.common.data.CRC; +import com.projectswg.common.data.encodables.mongo.MongoData; +import com.projectswg.common.data.encodables.mongo.MongoPersistable; +import com.projectswg.common.data.encodables.oob.StringId; +import com.projectswg.common.data.encodables.tangible.PvpFlag; +import com.projectswg.holocore.resources.support.data.persistable.SWGObjectFactory; +import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject; +import com.projectswg.holocore.resources.support.objects.swg.intangible.IntangibleObject; +import com.projectswg.holocore.resources.support.objects.swg.player.PlayerObject; +import com.projectswg.holocore.resources.support.objects.swg.tangible.OptionFlag; +import com.projectswg.holocore.resources.support.objects.swg.tangible.TangibleObject; +import com.projectswg.holocore.test.resources.GenericCreatureObject; +import org.bson.Document; +import org.bson.types.Binary; +import org.junit.Assert; +import org.junit.Test; + +import java.util.*; +import java.util.Map.Entry; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class TestSWGPersistence { + + @SuppressWarnings("unchecked") + private void assertContains(Map expected, Map actual) { + for (Entry e : expected.entrySet()) { + if (actual.get(e.getKey()) instanceof Map) { + assertContains((Map) e.getValue(), (Map) actual.get(e.getKey())); + } else if (actual.get(e.getKey()) instanceof Collection) { + Collection expectedCollection = (Collection) e.getValue(); + Collection actualCollection = (Collection) actual.get(e.getKey()); + Assert.assertEquals("Key: '"+e.getKey()+"' Size mismatch.", expectedCollection.size(), actualCollection.size()); + Assert.assertTrue("Key: '"+e.getKey()+"' Expected <"+expectedCollection+"> but was <"+actualCollection+">", actualCollection.containsAll(expectedCollection)); + } else { + Assert.assertEquals("Key: '" + e.getKey() + "'", e.getValue(), actual.get(e.getKey())); + } + } + } + + private Document encode(MongoData data) { + Document ret = new Document(); + for (Entry e : data.entrySet()) { + if (e.getValue() instanceof MongoData) + ret.put(e.getKey(), encode(((MongoData) e.getValue()))); + else + ret.put(e.getKey(), e.getValue()); + } + return ret; + } + + private void test(SWGObject obj, Document expected) { + Document saved = encode(SWGObjectFactory.save(obj, new MongoData())); + assertContains(expected, saved); + SWGObject gen = SWGObjectFactory.create(new MongoData(saved)); + Document saved2 = SWGObjectFactory.save(gen, new MongoData()).toDocument(); + assertContains(expected, saved2); + Assert.assertEquals(saved, saved2); + } + + private void testSWGObject(SWGObject obj) { + System.out.println(obj); + obj.getChildObjects().stream() + .map(SWGObjectFactory::save) + .map(MongoData::toDocument) + .forEach(System.out::println); + Document expected = map( + "id", obj.getObjectId(), + "template", obj.getTemplate(), + "base1", map( + "cashBalance", obj.getCashBalance(), + "bankBalance", obj.getBankBalance() + ), + "base3", map( + "complexity", obj.getComplexity(), + "stringId", map("file", obj.getStringId().getFile(), "key", obj.getStringId().getKey()), + "objectName", obj.getObjectName(), + "volume", obj.getVolume() + ), + "base6", map( + "detailStringId", map("file", obj.getDetailStringId().getFile(), "key", obj.getDetailStringId().getKey()) + ), + "location", map( + "type", obj.getInstanceLocation().getInstanceType().name(), + "number", obj.getInstanceLocation().getInstanceNumber(), + "location", map( + "terrain", obj.getTerrain().name(), + "point", map( + "x", obj.getX(), + "y", obj.getY(), + "z", obj.getZ() + ), + "orientation", map( + "x", obj.getLocation().getOrientationX(), + "y", obj.getLocation().getOrientationY(), + "z", obj.getLocation().getOrientationZ(), + "w", obj.getLocation().getOrientationW() + ) + ) + ), + "permissions", map("type", obj.getContainerPermissions().getType().name()), + "attributes", map(), + "serverAttributes", map(), + "children", obj.getChildObjects().stream() + .map(SWGObjectFactory::save) + .map(MongoData::toDocument) + .collect(Collectors.toList()) + ); + test(obj, expected); + } + + private void testTangibleObject(TangibleObject obj) { + testSWGObject(obj); + + Document expected = map( + "appearance", Map.ofEntries(obj.getCustomization().entrySet().stream().map(e -> Map.entry(e.getKey(), e.getValue().getValue())).toArray(Entry[]::new)), + "maxHitPoints", obj.getMaxHitPoints(), + "components", obj.getComponents(), + "condition", obj.getCondition(), + "pvpFlags", obj.getPvpFlags().stream().mapToInt(PvpFlag::getBitmask).reduce(0, (a, b) -> a | b), + "pvpStatus", obj.getPvpStatus().name(), + "pvpFaction", obj.getPvpFaction().name(), + "visibleGmOnly", obj.isVisibleGmOnly(), + "objectEffects", new Binary(obj.getObjectEffects()), + "optionFlags", obj.getOptionFlags().stream().map(OptionFlag::getFlag).reduce(0, (a, b) -> a | b) + ); + test(obj, expected); + } + + private void testCreatureObject(CreatureObject obj) { + testTangibleObject(obj); + + Document expected = map( + "base4", map( + "accelPercent", obj.getAccelPercent(), + "accelScale", obj.getAccelScale(), + "movementPercent", obj.getMovementPercent(), + "movementScale", obj.getMovementScale(), + "slopeModPercent", obj.getSlopeModPercent(), + "slopeModAngle", obj.getSlopeModPercent(), + "waterModPercent", obj.getWaterModPercent(), + "runSpeed", obj.getRunSpeed(), + "walkSpeed", obj.getWalkSpeed(), + "turnScale", obj.getTurnScale(), + "totalLevelXp", obj.getTotalLevelXp() + ), + "base6", map( + "level", (int) obj.getLevel(), + "levelHealthGranted", obj.getLevelHealthGranted(), + "animation", obj.getAnimation(), + "moodAnimation", obj.getMoodAnimation(), + "guildId", obj.getGuildId(), + "lookAtTargetId", obj.getLookAtTargetId(), + "intendedTargetId", obj.getIntendedTargetId(), + "moodId", (int) obj.getMoodId(), + "costume", obj.getCostume(), + "visible", obj.isVisible(), + "shownOnRadar", obj.isShownOnRadar(), + "beast", obj.isBeast(), + "difficulty", obj.getDifficulty().name(), + "hologramColor", obj.getHologramColor().name(), + "equippedWeapon", obj.getEquippedWeapon() == null ? null : obj.getEquippedWeapon().getObjectId(), + "maxAttributes", List.of(obj.getMaxHealth(), 0, obj.getMaxAction(), 0, obj.getMaxMind(), 0), + "buffs", Map.ofEntries(obj.getBuffEntries(b -> true).map(b -> Map.entry(CRC.getString(b.getCrc()), MongoData.store(b).toDocument())).toArray(Entry[]::new)) + ), + "posture", obj.getPosture().name(), + "race", obj.getRace().name(), + "height", obj.getHeight(), + "battleFatigue", obj.getBattleFatigue(), + "ownerId", obj.getOwnerId(), + "statesBitmask", obj.getStatesBitmask(), + "factionRank", (int) obj.getFactionRank(), + "skills", obj.getSkills(), + "baseAttributes", List.of(obj.getBaseHealth(), 0, obj.getBaseAction(), 0, obj.getBaseMind(), 0) + ); + test(obj, expected); + } + + private void testIntangibleObject(IntangibleObject obj) { + testSWGObject(obj); + + Document expected = map( + "count", obj.getCount() + ); + test(obj, expected); + } + + private void testPlayerObject(PlayerObject obj) { + testIntangibleObject(obj); + + Document expected = map( + "base3", map( + + ), + "base6", map( + + ), + "base8", map( + + ), + "base9", map( + "languageId", obj.getLanguageId(), + "killMeter", obj, + "petId", obj, + "friendsList", obj, + "ignoreList", obj, + "petAbilities", obj, + "activePetAbilities", obj + ), + "biography", obj.getBiography() + ); + test(obj, expected); + } + + private void test(SWGObject obj) { + switch (obj.getBaselineType()) { + case PLAY: + testPlayerObject((PlayerObject) obj); + break; + case ITNO: + testIntangibleObject((IntangibleObject) obj); + break; + case CREO: + testCreatureObject((CreatureObject) obj); + break; + case TANO: + testTangibleObject((TangibleObject) obj); + break; + default: + testSWGObject(obj); + break; + } + } + + @Test + public void testCreatureObject() { + GenericCreatureObject creature = new GenericCreatureObject(1); + creature.setObjectName("TEST"); + creature.setStringId(new StringId("file", "key")); + creature.setDetailStf(new StringId("file-d", "key-d")); + creature.setComplexity(2); + creature.setVolume(3); + + test(creature); + } + + private static Document map(Object ... values) { + assert values.length % 2 == 0; + Document map = new Document(); + for (int i = 0; i < values.length-1; i+=2) { + assert values[i] instanceof String; + if (values[i+1] instanceof Float) + map.put((String) values[i], ((Float) values[i+1]).doubleValue()); + else + map.put((String) values[i], values[i+1]); + } + return map; + } + +} diff --git a/src/test/java/com/projectswg/holocore/test/resources/GenericCreatureObject.java b/src/test/java/com/projectswg/holocore/test/resources/GenericCreatureObject.java index 560661df3..2bad1ffb8 100644 --- a/src/test/java/com/projectswg/holocore/test/resources/GenericCreatureObject.java +++ b/src/test/java/com/projectswg/holocore/test/resources/GenericCreatureObject.java @@ -28,10 +28,10 @@ package com.projectswg.holocore.test.resources; import com.projectswg.common.data.objects.GameObjectType; import com.projectswg.holocore.resources.support.global.player.PlayerState; +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.creature.CreatureObject; import com.projectswg.holocore.resources.support.objects.swg.player.PlayerObject; -import com.projectswg.holocore.resources.support.objects.swg.tangible.TangibleObject; import java.util.List; import java.util.concurrent.atomic.AtomicLong; @@ -71,14 +71,15 @@ public class GenericCreatureObject extends CreatureObject { setGameObjectType(GameObjectType.GOT_CREATURE_CHARACTER); PlayerObject playerObject = new PlayerObject(-getObjectId()); + playerObject.setTemplate("object/player/shared_player.iff"); playerObject.setArrangement(List.of(List.of("ghost"))); playerObject.systemMove(this); playerObject.setObjectName(name); - createInventoryObject("inventory"); - createInventoryObject("datapad"); - createInventoryObject("appearance_inventory"); - createInventoryObject("bank"); - createInventoryObject("mission_bag"); + createInventoryObject("object/tangible/inventory/shared_character_inventory.iff"); + createInventoryObject("object/tangible/datapad/shared_character_datapad.iff"); + createInventoryObject("object/tangible/inventory/shared_appearance_inventory.iff"); + createInventoryObject("object/tangible/bank/shared_character_bank.iff"); + createInventoryObject("object/tangible/mission_bag/shared_mission_bag.iff"); } @Override @@ -86,10 +87,10 @@ public class GenericCreatureObject extends CreatureObject { return (GenericPlayer) super.getOwner(); } - private void createInventoryObject(String slot) { - SWGObject obj = new TangibleObject(GENERATED_IDS.incrementAndGet()); - obj.setArrangement(List.of(List.of(slot))); + private void createInventoryObject(String template) { + SWGObject obj = ObjectCreator.createObjectFromTemplate(GENERATED_IDS.incrementAndGet(), template); obj.systemMove(this); + assert obj.getSlotArrangement() != -1; } } diff --git a/src/main/java/com/projectswg/holocore/utilities/clientdata_printer/ClientdataPrinterCommands.java b/src/utility/java/com/projectswg/utility/clientdata_printer/ClientdataPrinterCommands.java similarity index 98% rename from src/main/java/com/projectswg/holocore/utilities/clientdata_printer/ClientdataPrinterCommands.java rename to src/utility/java/com/projectswg/utility/clientdata_printer/ClientdataPrinterCommands.java index 570405ce5..0e8975048 100644 --- a/src/main/java/com/projectswg/holocore/utilities/clientdata_printer/ClientdataPrinterCommands.java +++ b/src/utility/java/com/projectswg/utility/clientdata_printer/ClientdataPrinterCommands.java @@ -24,7 +24,7 @@ * You should have received a copy of the GNU Affero General Public License * * along with Holocore. If not, see . * ***********************************************************************************/ -package com.projectswg.holocore.utilities.clientdata_printer; +package com.projectswg.utility.clientdata_printer; import com.projectswg.common.data.swgfile.ClientFactory; import com.projectswg.common.data.swgfile.visitors.DatatableData; diff --git a/src/utility/java/com/projectswg/utility/clientdata_printer/ClientdataPrinterDatatable.java b/src/utility/java/com/projectswg/utility/clientdata_printer/ClientdataPrinterDatatable.java new file mode 100644 index 000000000..43b723a9e --- /dev/null +++ b/src/utility/java/com/projectswg/utility/clientdata_printer/ClientdataPrinterDatatable.java @@ -0,0 +1,184 @@ +/*********************************************************************************** + * Copyright (c) 2018 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * --------------------------------------------------------------------------------* + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + ***********************************************************************************/ +package com.projectswg.utility.clientdata_printer; + +import com.projectswg.common.data.swgfile.ClientFactory; +import com.projectswg.common.data.swgfile.visitors.DatatableData; +import com.projectswg.common.data.swgfile.visitors.DatatableData.ColumnType; +import com.projectswg.holocore.resources.support.data.server_info.SdbLoader; +import com.projectswg.holocore.resources.support.data.server_info.SdbLoader.SdbResultSet; +import com.projectswg.holocore.resources.support.data.server_info.loader.VehicleLoader.VehicleInfo; +import me.joshlarson.jlcommon.log.Log; +import me.joshlarson.jlcommon.log.log_wrapper.ConsoleLogWrapper; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.regex.Pattern; + +public class ClientdataPrinterDatatable { + + private static final Pattern COLUMN_SPLITTER = Pattern.compile("_|(?<=[a-z])(?=[A-Z])"); + + public static void main(String [] args) throws IOException { + Log.addWrapper(new ConsoleLogWrapper()); + printTable("datatables/travel/travel.iff"); + } + + @SuppressWarnings("UseOfSystemOutOrSystemErr") + private static void printTable(String table) throws IOException { + DatatableData data = (DatatableData) ClientFactory.getInfoFromFile(table); + File outputFile = new File("serverdata/", table.replace("datatables/", "").replace(".iff", ".sdb")); + try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8))) { + for (int col = 0; col < data.getColumnCount(); col++) { + if (col > 0) + out.write('\t'); + out.write(toSdbColumn(data.getColumnName(col))); + } + out.newLine(); + for (int col = 0; col < data.getColumnCount(); col++) { + if (col > 0) + out.write('\t'); + out.write(data.getColumnType(col).toString()); + } + out.newLine(); + for (int row = 0; row < data.getRowCount(); row++) { + for (int col = 0; col < data.getColumnCount(); col++) { + if (col > 0) + out.write('\t'); + out.write(String.valueOf(data.getCell(row, col))); + } + out.newLine(); + } + } + + String type = toJavaClass(table.substring(table.lastIndexOf('/')+1).replace(".iff", "")); + + System.out.printf("public final class %sLoader extends DataLoader {%n\t%n"+ + "\t%sLoader() {%n"+ + "\t\t%n"+ + "\t}%n", type, type); + System.out.println("\t"); + + // load() method + System.out.printf("\t@Override%n"+ + "\tpublic final void load() throws IOException {%n"+ + "\t\ttry (SdbResultSet set = SdbLoader.load(new File(\"%s\"))) {%n"+ + "\t\t\twhile (set.next()) {%n"+ + "\t\t\t\t%sInfo %s = new %sInfo(set);%n"+ + "\t\t\t\t// TODO: Store information%n"+ + "\t\t\t}%n"+ + "\t\t}%n"+ + "\t}%n", outputFile, type, Character.toLowerCase(type.charAt(0))+type.substring(1), type); + System.out.println("\t"); + + System.out.printf("\tpublic static class %sInfo {%n", type); + System.out.println("\t\t"); + for (int i = 0; i < data.getColumnCount(); i++) { + System.out.printf("\t\tprivate final %s %s;%n", toJavaType(data.getColumnType(i)), toJavaVariable(data.getColumnName(i))); + } + System.out.println("\t\t"); + System.out.printf("\t\tpublic %sInfo(SdbResultSet set) {%n", type); + for (int i = 0; i < data.getColumnCount(); i++) { + String sdbCol = toSdbColumn(data.getColumnName(i)); + String rhs; + switch (data.getColumnType(i)) { + case BOOLEAN: + rhs = "set.getBoolean(\""+sdbCol+"\")"; + break; + case FLOAT: + rhs = "set.getReal(\""+sdbCol+"\")"; + break; + case CRC: + rhs = "new CRC((int) set.getInt(\""+sdbCol+"\"))"; + break; + case INTEGER: + rhs = "(int) set.getInt(\""+sdbCol+"\")"; + break; + case STRING: + case ENUM: + case DATATABLE_ENUM: + case NONE: + default: + rhs ="set.getText(\""+sdbCol+"\")"; + break; + } + System.out.printf("\t\t\tthis.%s = %s;%n", toJavaVariable(data.getColumnName(i)), rhs); + } + System.out.println("\t\t}"); + System.out.println("\t}"); + System.out.println("}"); + } + + private static String toJavaType(ColumnType type) { + switch (type) { + case BOOLEAN: + return "boolean"; + case FLOAT: + return "double"; + case CRC: + return "CRC"; + case INTEGER: + return "int"; + case STRING: + case ENUM: + case DATATABLE_ENUM: + case NONE: + default: + return "String"; + } + } + + private static String toJavaVariable(String col) { + String [] parts = COLUMN_SPLITTER.split(col); + StringBuilder str = new StringBuilder(); + boolean first = true; + for (String part : parts) { + if (first) + str.append(part.toLowerCase(Locale.US)); + else if (str.length() > 0) + str.append(Character.toUpperCase(part.charAt(0))).append(part.substring(1).toLowerCase(Locale.US)); + first = false; + } + return str.toString(); + } + + private static String toJavaClass(String col) { + String [] parts = COLUMN_SPLITTER.split(col); + StringBuilder str = new StringBuilder(); + for (String part : parts) { + str.append(Character.toUpperCase(part.charAt(0))).append(part.substring(1).toLowerCase(Locale.US)); + } + return str.toString(); + } + + private static String toSdbColumn(String col) { + String [] parts = COLUMN_SPLITTER.split(col); + return String.join("_", parts).toLowerCase(Locale.US); + } + +} diff --git a/src/utility/java/com/projectswg/utility/packets/PacketCaptureAnalysis.java b/src/utility/java/com/projectswg/utility/packets/PacketCaptureAnalysis.java index ead9bd606..3b6fbcef0 100644 --- a/src/utility/java/com/projectswg/utility/packets/PacketCaptureAnalysis.java +++ b/src/utility/java/com/projectswg/utility/packets/PacketCaptureAnalysis.java @@ -31,6 +31,7 @@ import com.projectswg.common.data.CRC; import com.projectswg.common.data.encodables.tangible.Posture; import com.projectswg.common.data.location.Location; import com.projectswg.common.data.location.Terrain; +import com.projectswg.common.network.hcap.PacketRecord; import com.projectswg.common.network.packets.PacketType; import com.projectswg.common.network.packets.SWGPacket; import com.projectswg.common.network.packets.swg.zone.*; @@ -182,9 +183,12 @@ public class PacketCaptureAnalysis { private void handleSceneCreateObjectByCrc(SceneCreateObjectByCrc p) { objectCreations.incrementAndGet(); - assertNotNull(p, currentTerrain.get(), "unknown terrain"); SWGObject obj = ObjectCreator.createObjectFromTemplate(p.getObjectId(), CRC.getString(p.getObjectCrc())); - p.setLocation(Location.builder(p.getLocation()).setTerrain(currentTerrain.get()).build()); + + if (currentTerrain.get() == null) + p.setLocation(Location.builder(p.getLocation()).setTerrain(Terrain.GONE).build()); + else + p.setLocation(Location.builder(p.getLocation()).setTerrain(currentTerrain.get()).build()); obj.setLocation(p.getLocation()); loadingObjects.add(obj); assertFalse(p, objects.containsKey(p.getObjectId()), "object already exists [initialized]"); @@ -201,8 +205,10 @@ public class PacketCaptureAnalysis { } private void handleBaseline(Baseline p) { + assertTrue(p, !loadingObjects.isEmpty(), "no loading objects defined"); assertEquals(p, loadingObjects.getLast().getObjectId(), p.getObjectId(), "baseline sent for non-loading object"); assertFalse(p, objects.containsKey(p.getObjectId()), "already-initialized object"); + loadingObjects.getLast().parseBaseline(p); } private void handleDeltasMessage(DeltasMessage p) { diff --git a/src/utility/java/com/projectswg/utility/packets/ProcessPacketCapture.java b/src/utility/java/com/projectswg/utility/packets/ProcessPacketCapture.java index d6ada036e..fbbcf4012 100644 --- a/src/utility/java/com/projectswg/utility/packets/ProcessPacketCapture.java +++ b/src/utility/java/com/projectswg/utility/packets/ProcessPacketCapture.java @@ -27,111 +27,82 @@ package com.projectswg.utility.packets; +import com.projectswg.common.network.hcap.HcapInputStream; +import com.projectswg.common.network.hcap.PacketRecord; +import com.projectswg.common.network.packets.SWGPacket; import com.projectswg.utility.packets.PacketCaptureAnalysis.PacketCaptureAssertion; -import java.io.DataInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.time.Instant; +import java.io.*; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; import java.time.ZoneId; import java.time.format.DateTimeFormatter; -import java.util.*; -import java.util.Map.Entry; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; -@SuppressWarnings("UseOfSystemOutOrSystemErr") +@SuppressWarnings({ "UseOfSystemOutOrSystemErr", "CallToPrintStackTrace" }) public class ProcessPacketCapture { - public static void main(String [] args) throws IOException { - try (DataInputStream packetCapture = new DataInputStream(new FileInputStream(new File(args[0])))) { - byte version = packetCapture.readByte(); - assert version == 2; - Map information = readSystemInformation(version, packetCapture); - List packets = readPackets(packetCapture); - System.out.println("Read " + packets.size() + " packets"); + public static void main(String [] args) { + for (String arg : args) { + if (!arg.endsWith(".hcap")) { + System.out.println("Skipping " + arg + " - does not have .hcap extension"); + continue; + } - if (args.length > 1 && args[1].equalsIgnoreCase("--printPackets")) { - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yy HH:mm:ss.SSS").withZone((ZoneId) information.get("time.time_zone")); - int packetNumber = 0; - int packetPadding = (int) Math.floor(Math.log10(packets.size())) + 1; - for (PacketRecord packet : packets) { - System.out.printf("%s [%0"+packetPadding+"d] %s %s%n", formatter.format(packet.getTime()), packetNumber, (packet.isServer() ? "OUT: " : "IN: "), packet.parse()); - packetNumber++; - } - } else { - PacketCaptureAnalysis analysis = PacketCaptureAnalysis.from(packets); - System.out.println("Analysis:"); - System.out.println(" Objects Created: " + analysis.getObjectCreations()); - System.out.println(" Objects Deleted: " + analysis.getObjectDeletions() + " [Implicit: " + analysis.getObjectDeletionsImplicit() + "]"); - System.out.println(" Zone-ins: " + analysis.getCharacterZoneIns() + " " + analysis.getPlayers()); - System.out.println(" Errors: " + analysis.getErrors().size()); - for (PacketCaptureAssertion e : analysis.getErrors()) { - System.err.println(" " + e.getMessage()); - System.err.println(" " + e.getPacket()); + try (HcapInputStream packetCapture = new HcapInputStream(new FileInputStream(new File(arg)))) { + try (BufferedWriter output = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(new File(arg.replace(".hcap", ".txt"))), StandardCharsets.UTF_8))) { + Map information = packetCapture.getSystemInformation(); + List packets = readPackets(packetCapture); + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yy HH:mm:ss.SSS").withZone((ZoneId) information.get("time.time_zone")); + int packetNumber = 0; + int packetPadding = (int) Math.floor(Math.log10(packets.size())) + 1; + for (PacketRecord packet : packets) { + SWGPacket parsed = packet.parse(); + String parsedInfo = (parsed == null) ? String.format("%08X", ByteBuffer.wrap(packet.getData()).order(ByteOrder.LITTLE_ENDIAN).getInt(2)) : parsed.toString(); + output.write(String.format("%s [%0"+packetPadding+"d] %s %s%n", formatter.format(packet.getTime()), packetNumber, (packet.isServer() ? "OUT: " : "IN: "), parsedInfo)); + packetNumber++; + } + + PacketCaptureAnalysis analysis = PacketCaptureAnalysis.from(packets); + output.write(String.format("Read %d packets%n", packets.size())); + output.write(String.format("Analysis:%n")); + output.write(String.format(" Objects Created: %d%n", analysis.getObjectCreations())); + output.write(String.format(" Objects Deleted: %d [Implicit: %d]%n", analysis.getObjectDeletions(), analysis.getObjectDeletionsImplicit())); + output.write(String.format(" Zone-ins: %d %s%n", analysis.getCharacterZoneIns(), analysis.getPlayers())); + output.write(String.format(" Errors: %d%n", analysis.getErrors().size())); + for (PacketCaptureAssertion e : analysis.getErrors()) { + output.write(" " + e.getMessage() + System.lineSeparator()); + output.write(" " + e.getPacket() + System.lineSeparator()); + } + System.out.println("Wrote " + packets.size() + " packets with analysis to " + arg.replace(".hcap", ".txt")); } + } catch (Throwable t) { + t.printStackTrace(); } } } - private static Map readSystemInformation(byte version, DataInputStream packetCapture) throws IOException { - int count = packetCapture.readByte(); - System.out.println("System Information:"); - Map information = new LinkedHashMap<>(); - for (int i = 0; i < count; i++) { - Map.Entry entry = parseEntry(version, packetCapture.readUTF()); - information.put(entry.getKey(), entry.getValue()); - } - - int maxLength = information.keySet().stream().mapToInt(String::length).max().orElse(10); - for (Entry e : information.entrySet()) { - System.out.printf(" %-"+maxLength+"s = %s%n", e.getKey(), e.getValue().toString()); - } - return information; - } - - private static List readPackets(DataInputStream packetCapture) throws IOException { + private static List readPackets(HcapInputStream packetCapture) throws IOException { List packets = new ArrayList<>(1024); - while (packetCapture.available() >= 11) { - boolean server = packetCapture.readBoolean(); - Instant time = Instant.ofEpochMilli(packetCapture.readLong()); - int dataLength = packetCapture.readUnsignedShort(); - byte [] data = new byte[dataLength]; - int n = packetCapture.read(data); - while (n < dataLength) - n += packetCapture.read(data, n, dataLength - n); - assert n == dataLength; - packets.add(new PacketRecord(server, time, data)); + PacketRecord record; + while ((record = packetCapture.readPacket()) != null) { + try { + if (record.getData().length < 6) + continue; + record.parse(); + packets.add(record); + } catch (BufferUnderflowException e) { + System.err.printf("Packet parser failed for packet of length %d at %s%n", record.getData().length, record.getTime()); + } catch (Throwable t) { + t.printStackTrace(); + } } return packets; } - private static Map.Entry parseEntry(byte version, String str) { - String [] keyValue = str.split("=", 2); - assert keyValue.length == 2; - String key = keyValue[0].toLowerCase(Locale.US); - String value = keyValue[1]; - - if (version == 2) { - switch (key) { - case "time.current_time": - return Map.entry(key, Instant.ofEpochMilli(Long.parseLong(value))); - case "time.time_zone": - return Map.entry(key, ZoneId.of(value.split(":")[0])); - default: - return Map.entry(key, value); - } - } else if (version == 3) { - switch (key) { - case "time.current_time": - return Map.entry(key, Instant.parse(value)); - case "time.time_zone": - return Map.entry(key, ZoneId.of(value)); - default: - return Map.entry(key, value); - } - } else { - return Map.entry(key, value); - } - } - }