Removed automatic delta sending for SWGMap and added some new SDB loaders

This commit is contained in:
Obique
2019-02-04 19:58:20 -06:00
parent 97b6e435c4
commit 018cd2eaf4
45 changed files with 1343 additions and 885 deletions
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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<Terrain> 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<GalacticResourceSpawn> 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)));
}
}
@@ -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();
}
}
@@ -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);
}
@@ -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();
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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;
}
}
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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<Integer, BuffData> 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));
}
}
@@ -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<String, TravelGroup> 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<Terrain, Map<Terrain, Integer>> routeCosts;
public AllowedRouteManager() {
this.routeCosts = new ConcurrentHashMap<>();
}
public void addRoute(Terrain departure, Terrain destination, int fee) {
Map<Terrain, Integer> 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;
}
}
}
@@ -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);
@@ -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;
}
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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<Integer, BuffInfo> buffsByCrc;
private final Map<String, BuffInfo> 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;
}
}
}
@@ -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<SoftReference<DataLoader>> cachedLoader;
private final Supplier<DataLoader> supplier;
@@ -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();
}
@@ -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<String> getObjects() {
return Collections.unmodifiableCollection(attributes.keySet());
}
@Override
public void load() throws IOException {
try (SdbResultSet set = SdbLoader.load(new File("serverdata/objects/object_data.sdb"))) {
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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<String, PerformanceInfo> nameMap;
private final Map<Integer, PerformanceInfo> 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;
}
}
}
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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<Terrain, Map<Terrain, Integer>> 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<Terrain, Integer> 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<Terrain, Integer> 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<Terrain, Integer> 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<Terrain, Integer> getCosts() {
return Collections.unmodifiableMap(costs);
}
}
}
@@ -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 {
@@ -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);
}
}
@@ -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 "
@@ -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) {
}
@@ -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<PlayerFlags> flags) {
BitSet ret = new BitSet(128);
for (PlayerFlags flag : flags) {
ret.set(flag.flag);
}
return ret;
}
public static Set<PlayerFlags> flagsFromBitset(BitSet bitset) {
Set<PlayerFlags> ret = EnumSet.noneOf(PlayerFlags.class);
for (PlayerFlags flag : FLAGS) {
if (bitset.get(flag.flag))
ret.add(flag);
}
return ret;
}
}
@@ -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) {
@@ -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) {
}
@@ -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;
}
@@ -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) {
}
@@ -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()));
}
@@ -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));
}
@@ -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<RadialOption> options, Player player, SWGObject target) {
CreatureObject creature = player.getCreatureObject();
PlayerObject playerObject = player.getPlayerObject();
{
List<RadialOption> useOptions = new ArrayList<>();
@@ -47,7 +49,7 @@ public class TerminalBankRadial implements RadialHandlerInterface {
List<RadialOption> 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();
}
@@ -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<CreatureObject> 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<Buff> 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<Buff> 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<Buff> 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());
}
}
@@ -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<String, PerformanceData> performanceMap; // performance names mapped to performance data
private final Map<Long, Performance> performerMap;
private final Map<String, String> 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;
@@ -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);
}
}
@@ -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);
@@ -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<String> objects) {
Map<String, Object> 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);
}
@@ -117,7 +117,7 @@ public class ChatFriendService extends Service {
}
private void handleRequestFriendList(Player player) {
player.getPlayerObject().sendFriendsList();
player.getPlayerObject().sendFriendList();
}
/* Ignore List */
@@ -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) {
@@ -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<SWGObject> database;
private final PswgUserDatabase userDatabase;
private final ScheduledThreadPool persistenceThread;
private final Set<SWGObject> persistedObjects;
private final Map<Long, SWGObject> objectMap;
private final Map<Long, SWGObject> buildouts;
private final Map<String, BuildingObject> 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);
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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();
}
}
}
@@ -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));
@@ -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]);
}
}
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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<String, Object> expected, Map<String, Object> actual) {
for (Entry<String, Object> e : expected.entrySet()) {
if (actual.get(e.getKey()) instanceof Map) {
assertContains((Map<String, Object>) e.getValue(), (Map<String, Object>) actual.get(e.getKey()));
} else if (actual.get(e.getKey()) instanceof Collection) {
Collection<Object> expectedCollection = (Collection<Object>) e.getValue();
Collection<Object> actualCollection = (Collection<Object>) 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<String, Object> 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;
}
}
@@ -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;
}
}
@@ -24,7 +24,7 @@
* You should have received a copy of the GNU Affero General Public License *
* along with Holocore. If not, see <http://www.gnu.org/licenses/>. *
***********************************************************************************/
package com.projectswg.holocore.utilities.clientdata_printer;
package com.projectswg.utility.clientdata_printer;
import com.projectswg.common.data.swgfile.ClientFactory;
import com.projectswg.common.data.swgfile.visitors.DatatableData;
@@ -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 <http://www.gnu.org/licenses/>. *
***********************************************************************************/
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);
}
}
@@ -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) {
@@ -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<String, Object> information = readSystemInformation(version, packetCapture);
List<PacketRecord> 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<String, Object> information = packetCapture.getSystemInformation();
List<PacketRecord> 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<String, Object> readSystemInformation(byte version, DataInputStream packetCapture) throws IOException {
int count = packetCapture.readByte();
System.out.println("System Information:");
Map<String, Object> information = new LinkedHashMap<>();
for (int i = 0; i < count; i++) {
Map.Entry<String, Object> entry = parseEntry(version, packetCapture.readUTF());
information.put(entry.getKey(), entry.getValue());
}
int maxLength = information.keySet().stream().mapToInt(String::length).max().orElse(10);
for (Entry<String, Object> e : information.entrySet()) {
System.out.printf(" %-"+maxLength+"s = %s%n", e.getKey(), e.getValue().toString());
}
return information;
}
private static List<PacketRecord> readPackets(DataInputStream packetCapture) throws IOException {
private static List<PacketRecord> readPackets(HcapInputStream packetCapture) throws IOException {
List<PacketRecord> 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<String, Object> 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);
}
}
}