From 9d624a95644aaa58c47a378f47ef2d6d687736e2 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sun, 6 Dec 2015 09:19:57 +0100 Subject: [PATCH 01/63] Implemented very simple buff capabilities --- src/intents/BuffIntent.java | 38 ++++ src/resources/Buff.java | 86 ++++++++ .../objects/creature/CreatureObject.java | 32 ++- .../objects/player/PlayerObject.java | 5 + src/services/commands/BuffService.java | 194 ++++++++++++++++++ src/services/galaxy/GameManager.java | 6 +- 6 files changed, 358 insertions(+), 3 deletions(-) create mode 100644 src/intents/BuffIntent.java create mode 100644 src/resources/Buff.java create mode 100644 src/services/commands/BuffService.java diff --git a/src/intents/BuffIntent.java b/src/intents/BuffIntent.java new file mode 100644 index 000000000..46b9a4222 --- /dev/null +++ b/src/intents/BuffIntent.java @@ -0,0 +1,38 @@ +package intents; + +import resources.control.Intent; +import resources.objects.creature.CreatureObject; + +public class BuffIntent extends Intent { + + public static final String TYPE = "BuffIntent"; + + private final String buffName; + private final CreatureObject buffer, receiver; + private final boolean remove; + + public BuffIntent(String buffName, CreatureObject buffer, CreatureObject receiver, boolean remove) { + super(TYPE); + this.buffName = buffName; + this.buffer = buffer; + this.receiver = receiver; + this.remove = remove; + } + + public CreatureObject getReceiver() { + return receiver; + } + + public CreatureObject getBuffer() { + return buffer; + } + + public String getBuffName() { + return buffName; + } + + public boolean isRemove() { + return remove; + } + +} diff --git a/src/resources/Buff.java b/src/resources/Buff.java new file mode 100644 index 000000000..22566c873 --- /dev/null +++ b/src/resources/Buff.java @@ -0,0 +1,86 @@ +package resources; + +import java.io.Serializable; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.TimeUnit; + +import resources.encodables.Encodable; +import resources.objects.creature.CreatureObject; +import resources.objects.player.PlayerObject; + +public class Buff implements Encodable, Serializable { + + private static final long serialVersionUID = 1; + + private int duration; + private final CreatureObject buffer, receiver; + private final long endTime; + private long stackCount; + private float skillMod1Value; + + public Buff(CreatureObject buffer, CreatureObject receiver, int duration, float skillMod1Value) { + this.buffer = buffer; + this.receiver = receiver; + this.duration = duration; + this.skillMod1Value = skillMod1Value; + endTime = getEndTimeFromNow(); + stackCount = 1; + } + + @Override + public byte[] encode() { + ByteBuffer data = ByteBuffer.allocate(Integer.BYTES * 2 + Float.BYTES + Long.BYTES * 2 + Short.BYTES ).order(ByteOrder.LITTLE_ENDIAN); + int sentDuration = duration; + PlayerObject playerObject = receiver.getPlayerObject(); + + if(playerObject != null) { + // TODO how will this work with NPCs? + sentDuration += playerObject.getPlayTime(); + } + + data.putInt(sentDuration); // Buff duration + time played on character + data.putFloat(skillMod1Value); // The value for skillMod #1 on the buff. Displayed on the client as skillMod1Value * stackCount. + data.putInt(duration); // Icon shadow "clock" overlay. + data.putLong(buffer.getObjectId()); // Object ID of the caster + data.putLong(stackCount); // Stack count + data.putShort((short) 0); // Unknown. Could also be two bytes. + + return data.array(); + } + + @Override + public void decode(ByteBuffer data) { + // TODO Auto-generated method stub + + } + + public int getDuration() { + return duration; + } + + public long getStackCount() { + return stackCount; + } + + /** + * Adjusts the stack count. This method doesn't send any delta. + * @param adjustment + */ + public void adjustStackCount(int adjustment) { + stackCount += adjustment; + } + + private long getEndTimeFromNow() { + return System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(duration, TimeUnit.SECONDS); + } + + public long getRemainingTime() { + return endTime - System.currentTimeMillis(); + } + + public long getEndTime() { + return endTime; + } + +} diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 45b40e27a..bfcd7fe54 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -36,6 +36,7 @@ import network.packets.swg.zone.UpdatePostureMessage; import network.packets.swg.zone.UpdatePvpStatusMessage; import network.packets.swg.zone.baselines.Baseline.BaselineType; import network.packets.swg.zone.object_controller.PostureUpdate; +import resources.Buff; import resources.HologramColour; import resources.Posture; import resources.PvpFlag; @@ -43,6 +44,7 @@ import resources.Race; import resources.SkillMod; import resources.collections.SWGList; import resources.collections.SWGMap; +import resources.common.CRC; import resources.encodables.player.Equipment; import resources.network.BaselineBuilder; import resources.objects.SWGObject; @@ -56,7 +58,7 @@ import utilities.Encoder.StringType; public class CreatureObject extends TangibleObject { - private static final long serialVersionUID = 2L; + private static final long serialVersionUID = 3L; private transient GroupInviterData inviterData = new GroupInviterData(0, null, "", 0); private transient long lastReserveOperation = 0; @@ -115,7 +117,7 @@ public class CreatureObject extends TangibleObject { private SWGMap skillMods = new SWGMap<>(BaselineType.CREO, 4, 3, StringType.ASCII); private SWGMap missionCriticalObjs = new SWGMap<>(BaselineType.CREO, 4, 13); private SWGMap abilities = new SWGMap<>(BaselineType.CREO, 4, 14, StringType.ASCII); - private SWGMap buffs = new SWGMap<>(BaselineType.CREO, 6, 26); // TODO: Buff structure + private SWGMap buffs = new SWGMap<>(BaselineType.CREO, 6, 26); public CreatureObject(long objectId) { @@ -628,6 +630,32 @@ public class CreatureObject extends TangibleObject { return skillMod != null ? skillMod.getValue() : 0; } + public void addBuff(String buffName, Buff buff) { + buffs.put(CRC.getCrc(buffName), buff); + buffs.sendDeltaMessage(this); + } + + public void removeBuff(String buffName) { + buffs.remove(CRC.getCrc(buffName)); + buffs.sendDeltaMessage(this); + } + + public boolean hasBuff(String buffName) { + return buffs.containsKey(CRC.getCrc(buffName)); + } + + public Buff getBuffByName(String buffName) { + return buffs.get(CRC.getCrc(buffName)); + } + + public void adjustBuffStackCount(String buffName, int adjustment) { + int buffCrc = CRC.getCrc(buffName); + Buff buff = buffs.get(buffCrc); + buff.adjustStackCount(adjustment); // Adjust the stack count + // TODO reset time remaining? + buffs.update(buffCrc, this); // Send deltas for this key. + } + public boolean isVisible() { return visible; } diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index 946633519..c5c9f750d 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -47,6 +47,7 @@ import utilities.Encoder.StringType; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; public class PlayerObject extends IntangibleObject { @@ -153,6 +154,10 @@ public class PlayerObject extends IntangibleObject { } public int getPlayTime() { + int startTime = getStartPlayTime(); + int deltaTime = (int) ((System.currentTimeMillis()) - startTime); + setPlayTime((int) TimeUnit.MILLISECONDS.toSeconds(deltaTime)); // Set new time and send delta + return playTime; } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java new file mode 100644 index 000000000..f0dd5168e --- /dev/null +++ b/src/services/commands/BuffService.java @@ -0,0 +1,194 @@ +package services.commands; + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.DelayQueue; +import java.util.concurrent.Delayed; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import intents.BuffIntent; +import intents.SkillModIntent; +import intents.object.ObjectCreatedIntent; +import resources.Buff; +import resources.control.Intent; +import resources.control.Service; +import resources.objects.creature.CreatureObject; +import utilities.Scripts; + +public class BuffService extends Service { + + // 100 metres is the max distance you can be away from the source of the buff before it's removed. +// private static final byte GROUP_BUFF_RANGE = 100; + + private final DelayQueue buffRemoval; + private final ExecutorService executor; + private boolean stopBuffRemover; + private final Map dataMap; + + public BuffService() { + registerForIntent(BuffIntent.TYPE); + registerForIntent(ObjectCreatedIntent.TYPE); + + buffRemoval = new DelayQueue<>(); + executor = Executors.newSingleThreadScheduledExecutor(); + dataMap = new HashMap<>(); + } + + @Override + public boolean initialize() { + // TODO read buff datatable into dataMap?? + + executor.execute(new BuffRemover()); + return super.initialize(); + } + + @Override + public void onIntentReceived(Intent i) { + switch(i.getType()) { + case BuffIntent.TYPE: + BuffIntent bi = (BuffIntent) i; + + if(bi.isRemove()) { + handleBuffRemove(bi); + } else { + handleBuffAdd(bi); + } + + break; + case ObjectCreatedIntent.TYPE: handleObjectCreation((ObjectCreatedIntent) i); break; + } + } + + @Override + public boolean terminate() { + stopBuffRemover = true; + executor.shutdown(); + + return super.terminate(); + } + + private void handleObjectCreation(ObjectCreatedIntent oci) { + if(oci.getObject() instanceof CreatureObject) { + // If this is a creature, we'll want to check if they have any buffs that we should start managing. + } + } + + private void handleBuffAdd(BuffIntent bi) { + CreatureObject receiver = bi.getReceiver(); + CreatureObject buffer = bi.getBuffer(); + int duration = 600; + Buff buff = new Buff(buffer, receiver, duration, 1337f); + String buffName = bi.getBuffName(); + + receiver.addBuff(buffName, buff); + + if(duration >= 0) { + // If this buff doesn't last forever, we'll schedule it for removal in the future + buffRemoval.put(new BuffDelayed(buff, buffName, receiver)); + } + } + + private void handleBuffRemove(BuffIntent bi) { + removeBuff(bi.getReceiver(), bi.getBuffName(), false); + } + + private void removeBuff(CreatureObject creature, String buffName, boolean expired) { + // Get the BuffData for this buff name. + BuffData buffData = dataMap.get(buffName); + Buff buff = creature.getBuffByName(buffName); + + // Check if this buff can be stacked + if(buffData.maxStackCount > 1 && !expired) { + // Check if this buff has been stacked + if(buff.getStackCount() > 1) { + // Get the adjustment from the + int adjustment = Scripts.invoke(buffName, "onStackChange", creature, buffName); + + // If it has, reduce the stack count and reset the duration. + creature.adjustBuffStackCount(buffName, adjustment); + } + } else { + // If this is a group buff, skillmods and buffs need to be removed from the group as well. + CreatureObject[] affectedCreatures = null; + + // Remove the buff from the creature(s) + creature.removeBuff(buffName); + + // TODO remove skillmods + new SkillModIntent("skillModName", 0, -10, creature); + } + } + + private class BuffRemover implements Runnable { + @Override + public void run() { + while(!stopBuffRemover) { + try { + BuffDelayed buffToRemove = buffRemoval.take(); // Block here until a buff expires + + removeBuff(buffToRemove.owner, buffToRemove.buffName, true); + } catch (InterruptedException e) { + + } + } + } + } + + private class BuffDelayed implements Delayed { + + private final Buff buff; + private final String buffName; + private final CreatureObject owner; + + private BuffDelayed(Buff buff, String buffName, CreatureObject owner) { + this.buff = buff; + this.buffName = buffName; + this.owner = owner; + } + + @Override + public int compareTo(Delayed o) { + return Long.compare(buff.getEndTime(), ((BuffDelayed) o).buff.getEndTime()); + } + + @Override + public long getDelay(TimeUnit timeUnit) { + return timeUnit.convert(buff.getRemainingTime(), TimeUnit.MILLISECONDS); + } + + } + + /** + * @author Mads + * Each instance of this class holds the base information + * for a specific buff name. Thus, we only store shared data that each {@code Buff} instance + * would otherwise have been holding. + * + * Example: Instead of each {@code Buff} instance storing the max amount of times you can + * stack it, a shared class stores that information. + * + * With many {@code Buff} instances in play, this will result in memory + * usage reduction. + */ + private class BuffData { + private final long maxStackCount; +// private final String effect1Name; +// private final int effect1Value; +// private final String effect2Name; +// private final int effect2Value; +// private final String effect3Name; +// private final int effect3Value; +// private final String effect4Name; +// private final int effect4Value; +// private final String effect5Name; +// private final int effect5Value; + + private BuffData(long maxStackCount) { + this.maxStackCount = maxStackCount; + } + + } + +} diff --git a/src/services/galaxy/GameManager.java b/src/services/galaxy/GameManager.java index 05109b13d..e01d472b4 100644 --- a/src/services/galaxy/GameManager.java +++ b/src/services/galaxy/GameManager.java @@ -28,6 +28,7 @@ package services.galaxy; import resources.control.Manager; +import services.commands.BuffService; import services.commands.CommandService; import services.faction.FactionService; import services.galaxy.terminals.TerminalService; @@ -42,6 +43,7 @@ public class GameManager extends Manager { private final TerminalService terminalManager; private final FactionService factionService; // private final GroupService groupService; + private final BuffService buffService; public GameManager() { commandService = new CommandService(); @@ -51,7 +53,8 @@ public class GameManager extends Manager { terminalManager = new TerminalService(); factionService = new FactionService(); // groupService = new GroupService(); - + buffService = new BuffService(); + addChildService(commandService); addChildService(connectionService); addChildService(suiService); @@ -59,5 +62,6 @@ public class GameManager extends Manager { addChildService(terminalManager); addChildService(factionService); // addChildService(groupService); + addChildService(buffService); } } From 2f782e312a49b1bc6b822f4ed2e2371ad991e78d Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 28 Jan 2016 19:46:45 +0100 Subject: [PATCH 02/63] Implemented decode() in resources.Buff --- src/resources/Buff.java | 41 ++++++++++---------------- src/services/commands/BuffService.java | 4 +-- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/resources/Buff.java b/src/resources/Buff.java index 22566c873..cf9c13fdb 100644 --- a/src/resources/Buff.java +++ b/src/resources/Buff.java @@ -6,43 +6,35 @@ import java.nio.ByteOrder; import java.util.concurrent.TimeUnit; import resources.encodables.Encodable; -import resources.objects.creature.CreatureObject; -import resources.objects.player.PlayerObject; public class Buff implements Encodable, Serializable { private static final long serialVersionUID = 1; private int duration; - private final CreatureObject buffer, receiver; + private long bufferId; + private int playTime; private final long endTime; private long stackCount; private float skillMod1Value; - public Buff(CreatureObject buffer, CreatureObject receiver, int duration, float skillMod1Value) { - this.buffer = buffer; - this.receiver = receiver; + public Buff(long bufferId, int playTime, int duration, float skillMod1Value) { + this.bufferId = bufferId; + this.playTime = playTime; this.duration = duration; this.skillMod1Value = skillMod1Value; - endTime = getEndTimeFromNow(); + endTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(duration, TimeUnit.SECONDS); stackCount = 1; } @Override public byte[] encode() { ByteBuffer data = ByteBuffer.allocate(Integer.BYTES * 2 + Float.BYTES + Long.BYTES * 2 + Short.BYTES ).order(ByteOrder.LITTLE_ENDIAN); - int sentDuration = duration; - PlayerObject playerObject = receiver.getPlayerObject(); - if(playerObject != null) { - // TODO how will this work with NPCs? - sentDuration += playerObject.getPlayTime(); - } - - data.putInt(sentDuration); // Buff duration + time played on character + data.putInt(duration + playTime); // Buff duration + time played on character data.putFloat(skillMod1Value); // The value for skillMod #1 on the buff. Displayed on the client as skillMod1Value * stackCount. data.putInt(duration); // Icon shadow "clock" overlay. - data.putLong(buffer.getObjectId()); // Object ID of the caster + data.putLong(bufferId); // Object ID of the buffer data.putLong(stackCount); // Stack count data.putShort((short) 0); // Unknown. Could also be two bytes. @@ -51,8 +43,13 @@ public class Buff implements Encodable, Serializable { @Override public void decode(ByteBuffer data) { - // TODO Auto-generated method stub - + int playerBuffTime = data.getInt(); + skillMod1Value = data.getFloat(); + duration = data.getInt(); + playTime = playerBuffTime - duration; + bufferId = data.getLong(); + stackCount = data.getLong(); + data.getShort(); // unknown } public int getDuration() { @@ -71,14 +68,6 @@ public class Buff implements Encodable, Serializable { stackCount += adjustment; } - private long getEndTimeFromNow() { - return System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(duration, TimeUnit.SECONDS); - } - - public long getRemainingTime() { - return endTime - System.currentTimeMillis(); - } - public long getEndTime() { return endTime; } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index f0dd5168e..467650a18 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -79,7 +79,7 @@ public class BuffService extends Service { CreatureObject receiver = bi.getReceiver(); CreatureObject buffer = bi.getBuffer(); int duration = 600; - Buff buff = new Buff(buffer, receiver, duration, 1337f); + Buff buff = new Buff(buffer.getObjectId(), receiver.getPlayerObject().getPlayTime(), duration, 1337f); String buffName = bi.getBuffName(); receiver.addBuff(buffName, buff); @@ -155,7 +155,7 @@ public class BuffService extends Service { @Override public long getDelay(TimeUnit timeUnit) { - return timeUnit.convert(buff.getRemainingTime(), TimeUnit.MILLISECONDS); + return timeUnit.convert(buff.getEndTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } } From c2d3f17d1ad3fad3849e5a8c688d92371ed5ee5c Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Fri, 29 Jan 2016 00:41:06 +0100 Subject: [PATCH 03/63] Fixed a few issues with buffs and skillmods --- src/resources/Buff.java | 12 +- .../objects/creature/CreatureObject.java | 17 +- src/services/commands/BuffService.java | 258 ++++++++++++++---- 3 files changed, 233 insertions(+), 54 deletions(-) diff --git a/src/resources/Buff.java b/src/resources/Buff.java index cf9c13fdb..c1d7fb7a9 100644 --- a/src/resources/Buff.java +++ b/src/resources/Buff.java @@ -13,14 +13,14 @@ public class Buff implements Encodable, Serializable { private int duration; private long bufferId; - private int playTime; + private int startPlayTime; private final long endTime; private long stackCount; private float skillMod1Value; public Buff(long bufferId, int playTime, int duration, float skillMod1Value) { this.bufferId = bufferId; - this.playTime = playTime; + this.startPlayTime = playTime; this.duration = duration; this.skillMod1Value = skillMod1Value; endTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(duration, TimeUnit.SECONDS); @@ -31,7 +31,7 @@ public class Buff implements Encodable, Serializable { public byte[] encode() { ByteBuffer data = ByteBuffer.allocate(Integer.BYTES * 2 + Float.BYTES + Long.BYTES * 2 + Short.BYTES ).order(ByteOrder.LITTLE_ENDIAN); - data.putInt(duration + playTime); // Buff duration + time played on character + data.putInt(duration + startPlayTime); // Buff duration + time played on character data.putFloat(skillMod1Value); // The value for skillMod #1 on the buff. Displayed on the client as skillMod1Value * stackCount. data.putInt(duration); // Icon shadow "clock" overlay. data.putLong(bufferId); // Object ID of the buffer @@ -46,7 +46,7 @@ public class Buff implements Encodable, Serializable { int playerBuffTime = data.getInt(); skillMod1Value = data.getFloat(); duration = data.getInt(); - playTime = playerBuffTime - duration; + startPlayTime = playerBuffTime - duration; bufferId = data.getLong(); stackCount = data.getLong(); data.getShort(); // unknown @@ -72,4 +72,8 @@ public class Buff implements Encodable, Serializable { return endTime; } + public long getTotalDuration() { + return duration + startPlayTime; + } + } diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 05859f48e..de134306c 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -633,21 +633,28 @@ public class CreatureObject extends TangibleObject { sendDelta(3, 18, statesBitmask); } - public void adjustSkillmod(String skillModName, int base, int modifier) { + public synchronized void adjustSkillmod(String skillModName, int base, int modifier) { SkillMod skillMod = skillMods.get(skillModName); if(skillMod == null) { // They didn't have this SkillMod already. // Therefore, we send a full delta. skillMods.put(skillModName, new SkillMod(base, modifier)); - skillMods.sendDeltaMessage(this); } else { // They already had this skillmod. // All we need to do is adjust the base and the modifier and send an update from the SWGMap skillMod.adjustBase(base); skillMod.adjustModifier(modifier); - skillMods.update(skillModName, this); + + // If this skillmod now has a total value of 0 + if(skillMod.getValue() == 0) { + // Then remove it from the map + skillMods.remove(skillModName); + skillMods.sendDeltaMessage(this); + return; + } } + skillMods.update(skillModName, this); } public int getSkillModValue(String skillModName) { @@ -681,6 +688,10 @@ public class CreatureObject extends TangibleObject { buffs.update(buffCrc, this); // Send deltas for this key. } + public Collection getBuffs() { + return buffs.values(); + } + public boolean isVisible() { return visible; } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 467650a18..b90d7f0cd 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -1,3 +1,30 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// Project SWG /// www.projectswg.com * +* * +* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * +* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * +* Our goal is to create an emulator which will provide a server for players to * +* continue playing a game similar to the one they used to play. We are basing * +* it on the final publish of the game prior to end-game events. * +* * +* This file is part of Holocore. * +* * +* -------------------------------------------------------------------------------- * +* * +* Holocore is free software: you can redistribute it and/or modify * +* it under the terms of the GNU Affero General Public License as * +* published by the Free Software Foundation, either version 3 of the * +* License, or (at your option) any later version. * +* * +* Holocore is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU Affero General Public License for more details. * +* * +* You should have received a copy of the GNU Affero General Public License * +* along with Holocore. If not, see . * +* * +***********************************************************************************/ package services.commands; import java.util.HashMap; @@ -9,9 +36,11 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import intents.BuffIntent; +import intents.PlayerEventIntent; import intents.SkillModIntent; -import intents.object.ObjectCreatedIntent; import resources.Buff; +import resources.client_info.ClientFactory; +import resources.client_info.visitors.DatatableData; import resources.control.Intent; import resources.control.Service; import resources.objects.creature.CreatureObject; @@ -19,9 +48,20 @@ import utilities.Scripts; public class BuffService extends Service { - // 100 metres is the max distance you can be away from the source of the buff before it's removed. + // TODO remove buffs on respec. Listen for respec intent and remove buffs with + // BuffData that has REMOVE_ON_RESPEC = 1 + + // TODO group buffs + + // TODO particle effects + + // TODO decay buffs on deathblow + + // TODO debuffs vs buffs + // private static final byte GROUP_BUFF_RANGE = 100; + private final DatatableData buffTable; private final DelayQueue buffRemoval; private final ExecutorService executor; private boolean stopBuffRemover; @@ -29,8 +69,9 @@ public class BuffService extends Service { public BuffService() { registerForIntent(BuffIntent.TYPE); - registerForIntent(ObjectCreatedIntent.TYPE); + registerForIntent(PlayerEventIntent.TYPE); + buffTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/buff/buff.iff"); buffRemoval = new DelayQueue<>(); executor = Executors.newSingleThreadScheduledExecutor(); dataMap = new HashMap<>(); @@ -38,7 +79,7 @@ public class BuffService extends Service { @Override public boolean initialize() { - // TODO read buff datatable into dataMap?? + loadBuffs(); executor.execute(new BuffRemover()); return super.initialize(); @@ -51,13 +92,13 @@ public class BuffService extends Service { BuffIntent bi = (BuffIntent) i; if(bi.isRemove()) { - handleBuffRemove(bi); + handleBuffIntentRemove(bi); } else { - handleBuffAdd(bi); + handleBuffIntentAdd(bi); } break; - case ObjectCreatedIntent.TYPE: handleObjectCreation((ObjectCreatedIntent) i); break; + case PlayerEventIntent.TYPE: handleObjectCreation((PlayerEventIntent) i); break; } } @@ -69,69 +110,132 @@ public class BuffService extends Service { return super.terminate(); } - private void handleObjectCreation(ObjectCreatedIntent oci) { - if(oci.getObject() instanceof CreatureObject) { - // If this is a creature, we'll want to check if they have any buffs that we should start managing. + private void loadBuffs() { + for(int row = 0; row < buffTable.getRowCount(); row++) { + dataMap.put((String) buffTable.getCell(row, 0), new BuffData( + (int) buffTable.getCell(row, 28), // max stacks + (String) buffTable.getCell(row, 7), // effect1 + (float) buffTable.getCell(row, 8), // value1 + (String) buffTable.getCell(row, 9), // effect2 + (float) buffTable.getCell(row, 10), // value2 + (String) buffTable.getCell(row, 11), // effect3 + (float) buffTable.getCell(row, 12), // value3 + (String) buffTable.getCell(row, 13), // effect4 + (float) buffTable.getCell(row, 14), // value4 + (String) buffTable.getCell(row, 15), // effect5 + (float) buffTable.getCell(row, 16), // value5 + (float) buffTable.getCell(row, 6) // default duration + )); + } + } + + private void handleObjectCreation(PlayerEventIntent pei) { + CreatureObject creature = pei.getPlayer().getCreatureObject(); + + switch(pei.getEvent()) { + case PE_FIRST_ZONE: handleFirstZone(creature); break; + case PE_DISAPPEAR: break; } } - private void handleBuffAdd(BuffIntent bi) { + private void handleFirstZone(CreatureObject creature) { + for(Buff buff : creature.getBuffs()) { + // TODO fix. Needs buffName + manageBuff(buff, null, creature); + } + } + + private void handleBuffIntentAdd(BuffIntent bi) { CreatureObject receiver = bi.getReceiver(); CreatureObject buffer = bi.getBuffer(); - int duration = 600; - Buff buff = new Buff(buffer.getObjectId(), receiver.getPlayerObject().getPlayTime(), duration, 1337f); String buffName = bi.getBuffName(); + BuffData buffData = dataMap.get(buffName); + + if(buffData == null) + return; + + Buff buff = new Buff(buffer.getObjectId(), receiver.getPlayerObject().getPlayTime(), (int) buffData.getDefaultDuration(), buffData.getEffect1Value()); + + sendSkillModIntent(buffData, receiver, stopBuffRemover); receiver.addBuff(buffName, buff); - if(duration >= 0) { - // If this buff doesn't last forever, we'll schedule it for removal in the future - buffRemoval.put(new BuffDelayed(buff, buffName, receiver)); - } + manageBuff(buff, buffName, receiver); } - private void handleBuffRemove(BuffIntent bi) { + private void handleBuffIntentRemove(BuffIntent bi) { removeBuff(bi.getReceiver(), bi.getBuffName(), false); } + private void manageBuff(Buff buff, String buffName, CreatureObject creature) { + // If this buff has less than or 0 seconds left, then remove it. + if(buff.getEndTime() <= 0) { + removeBuff(creature, buffName, true); + } else if(buff.getDuration() >= 0) { + // If this buff doesn't last forever, we'll schedule it for removal in the future + buffRemoval.put(new BuffDelayed(buff, buffName, creature)); + } + } + private void removeBuff(CreatureObject creature, String buffName, boolean expired) { // Get the BuffData for this buff name. BuffData buffData = dataMap.get(buffName); + + if(buffData == null) { + return; + } + Buff buff = creature.getBuffByName(buffName); // Check if this buff can be stacked - if(buffData.maxStackCount > 1 && !expired) { + if(buffData.getMaxStackCount() > 1 && !expired) { // Check if this buff has been stacked if(buff.getStackCount() > 1) { - // Get the adjustment from the - int adjustment = Scripts.invoke(buffName, "onStackChange", creature, buffName); + // Get the adjustment from the script + // TODO casting exception if the script doesn't exist or returns null + int adjustment = Scripts.invoke(buffName, "onStackChange", creature); // If it has, reduce the stack count and reset the duration. creature.adjustBuffStackCount(buffName, adjustment); } } else { - // If this is a group buff, skillmods and buffs need to be removed from the group as well. - CreatureObject[] affectedCreatures = null; + // Remove skillmods + sendSkillModIntent(buffData, creature, true); - // Remove the buff from the creature(s) + // Remove the buff from the creature creature.removeBuff(buffName); - - // TODO remove skillmods - new SkillModIntent("skillModName", 0, -10, creature); } } + private void sendSkillModIntent(BuffData buffData, CreatureObject creature, boolean remove) { + String effect1Name = buffData.getEffect1Name(); + String effect2Name = buffData.getEffect2Name(); + String effect3Name = buffData.getEffect3Name(); + String effect4Name = buffData.getEffect4Name(); + String effect5Name = buffData.getEffect5Name(); + + int valueFactor = remove ? -1 : 1; + + if(!effect1Name.isEmpty()) + new SkillModIntent(effect1Name, 0, (int) buffData.getEffect1Value() * valueFactor, creature).broadcast(); + if(!effect2Name.isEmpty()) + new SkillModIntent(effect2Name, 0, (int) buffData.getEffect2Value() * valueFactor, creature).broadcast(); + if(!effect3Name.isEmpty()) + new SkillModIntent(effect3Name, 0, (int) buffData.getEffect3Value() * valueFactor, creature).broadcast(); + if(!effect4Name.isEmpty()) + new SkillModIntent(effect4Name, 0, (int) buffData.getEffect4Value() * valueFactor, creature).broadcast(); + if(!effect5Name.isEmpty()) + new SkillModIntent(effect5Name, 0, (int) buffData.getEffect5Value() * valueFactor, creature).broadcast(); + } + private class BuffRemover implements Runnable { @Override public void run() { while(!stopBuffRemover) { - try { - BuffDelayed buffToRemove = buffRemoval.take(); // Block here until a buff expires - + BuffDelayed buffToRemove = buffRemoval.poll(); + + if(buffToRemove != null) removeBuff(buffToRemove.owner, buffToRemove.buffName, true); - } catch (InterruptedException e) { - - } } } } @@ -155,7 +259,7 @@ public class BuffService extends Service { @Override public long getDelay(TimeUnit timeUnit) { - return timeUnit.convert(buff.getEndTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); + return timeUnit.convert(buff.getTotalDuration() - owner.getPlayerObject().getPlayTime(), TimeUnit.MILLISECONDS); } } @@ -173,20 +277,80 @@ public class BuffService extends Service { * usage reduction. */ private class BuffData { - private final long maxStackCount; -// private final String effect1Name; -// private final int effect1Value; -// private final String effect2Name; -// private final int effect2Value; -// private final String effect3Name; -// private final int effect3Value; -// private final String effect4Name; -// private final int effect4Value; -// private final String effect5Name; -// private final int effect5Value; - - private BuffData(long maxStackCount) { + private final int maxStackCount; + private final String effect1Name; + private final float effect1Value; + private final String effect2Name; + private final float effect2Value; + private final String effect3Name; + private final float effect3Value; + private final String effect4Name; + private final float effect4Value; + private final String effect5Name; + private final float effect5Value; + private final float defaultDuration; + + private BuffData(int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration) { this.maxStackCount = maxStackCount; + this.effect1Name = effect1Name; + this.effect1Value = effect1Value; + this.effect2Name = effect2Name; + this.effect2Value = effect2Value; + this.effect3Name = effect3Name; + this.effect3Value = effect3Value; + this.effect4Name = effect4Name; + this.effect4Value = effect4Value; + this.effect5Name = effect5Name; + this.effect5Value = effect5Value; + this.defaultDuration = defaultDuration; + } + + public int getMaxStackCount() { + return maxStackCount; + } + + public String getEffect1Name() { + return effect1Name; + } + + public float getEffect1Value() { + return effect1Value; + } + + public String getEffect2Name() { + return effect2Name; + } + + public float getEffect2Value() { + return effect2Value; + } + + public String getEffect3Name() { + return effect3Name; + } + + public float getEffect3Value() { + return effect3Value; + } + + public String getEffect4Name() { + return effect4Name; + } + + public float getEffect4Value() { + return effect4Value; + } + + public String getEffect5Name() { + return effect5Name; + } + + public float getEffect5Value() { + return effect5Value; + } + + public float getDefaultDuration() { + return defaultDuration; } } From 628cc907cd117b720b40360e9288bd4729559f08 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Fri, 29 Jan 2016 01:06:07 +0100 Subject: [PATCH 04/63] Added particle effects for buffs --- src/network/PacketType.java | 1 + .../PlayClientEffectObjectMessage.java | 98 +++++++++++++++++++ src/services/commands/BuffService.java | 54 +++++++--- 3 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 src/network/packets/swg/zone/spatial/PlayClientEffectObjectMessage.java diff --git a/src/network/PacketType.java b/src/network/PacketType.java index def14c5d4..46b165981 100644 --- a/src/network/PacketType.java +++ b/src/network/PacketType.java @@ -162,6 +162,7 @@ public enum PacketType { NEW_TICKET_ACTIVITY_RESPONSE_MESSAGE (NewTicketActivityResponseMessage.CRC, NewTicketActivityResponseMessage.class), ATTRIBUTE_LIST_MESSAGE (AttributeListMessage.CRC, AttributeListMessage.class), STOP_CLIENT_EFFECT_OBJECT_BY_LABEL (StopClientEffectObjectByLabelMessage.CRC, StopClientEffectObjectByLabelMessage.class), + PLAY_CLIENT_EFFECT_OBJECT_MESSAGE (PlayClientEffectObjectMessage.CRC, PlayClientEffectObjectMessage.class), OPENED_CONTAINER_MESSAGE (ClientOpenContainerMessage.CRC, ClientOpenContainerMessage.class), // Combat diff --git a/src/network/packets/swg/zone/spatial/PlayClientEffectObjectMessage.java b/src/network/packets/swg/zone/spatial/PlayClientEffectObjectMessage.java new file mode 100644 index 000000000..0b25e1aaa --- /dev/null +++ b/src/network/packets/swg/zone/spatial/PlayClientEffectObjectMessage.java @@ -0,0 +1,98 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// Project SWG /// www.projectswg.com * +* * +* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * +* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * +* Our goal is to create an emulator which will provide a server for players to * +* continue playing a game similar to the one they used to play. We are basing * +* it on the final publish of the game prior to end-game events. * +* * +* This file is part of Holocore. * +* * +* -------------------------------------------------------------------------------- * +* * +* Holocore is free software: you can redistribute it and/or modify * +* it under the terms of the GNU Affero General Public License as * +* published by the Free Software Foundation, either version 3 of the * +* License, or (at your option) any later version. * +* * +* Holocore is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU Affero General Public License for more details. * +* * +* You should have received a copy of the GNU Affero General Public License * +* along with Holocore. If not, see . * +* * +***********************************************************************************/ +package network.packets.swg.zone.spatial; + +import java.nio.ByteBuffer; +import static network.packets.Packet.addAscii; +import static network.packets.Packet.addInt; +import static network.packets.Packet.addLong; +import static network.packets.Packet.addShort; +import static network.packets.Packet.getAscii; +import static network.packets.Packet.getLong; +import network.packets.swg.SWGPacket; + +/** + * + * @author Mads + */ +public class PlayClientEffectObjectMessage extends SWGPacket { + + public static final int CRC = getCrc("PlayClientEffectObjectMessage"); + + private String effectFileName; + private long objectId; + private String hardpoint; + private String label; + + public PlayClientEffectObjectMessage(String effectFile, long objectId, String hardpoint, String label) { + this.effectFileName = effectFile; + this.objectId = objectId; + this.hardpoint = hardpoint; + this.label = label; + } + + public String getEffectFileName() { + return effectFileName; + } + + public long getObjectId() { + return objectId; + } + + public String getHardpoint() { + return hardpoint; + } + + public String getLabel() { + return label; + } + + @Override + public void decode(ByteBuffer data) { + if (!super.decode(data, CRC)) { + return; + } + + effectFileName = getAscii(data); + hardpoint = getAscii(data); + objectId = getLong(data); + label = getAscii(data); + } + + @Override + public ByteBuffer encode() { + ByteBuffer data = ByteBuffer.allocate(Short.BYTES * 4 + Integer.BYTES + Long.BYTES + effectFileName.length() + hardpoint.length() + label.length()); + addShort(data, 5); + addInt(data, CRC); + addAscii(data, effectFileName); + addAscii(data, hardpoint); // TODO unknown - some sort of extra parameter string + addLong(data, objectId); + addAscii(data, label); + return data; + } +} diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index b90d7f0cd..6f58c0f2e 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -38,6 +38,7 @@ import java.util.concurrent.TimeUnit; import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; +import network.packets.swg.zone.spatial.PlayClientEffectObjectMessage; import resources.Buff; import resources.client_info.ClientFactory; import resources.client_info.visitors.DatatableData; @@ -124,7 +125,9 @@ public class BuffService extends Service { (float) buffTable.getCell(row, 14), // value4 (String) buffTable.getCell(row, 15), // effect5 (float) buffTable.getCell(row, 16), // value5 - (float) buffTable.getCell(row, 6) // default duration + (float) buffTable.getCell(row, 6), // default duration + (String) buffTable.getCell(row, 19), // particle effect + (String) buffTable.getCell(row, 20) // particle hardpoint )); } } @@ -161,6 +164,11 @@ public class BuffService extends Service { receiver.addBuff(buffName, buff); manageBuff(buff, buffName, receiver); + + String effectFileName = buffData.getEffectFileName(); + + if(!effectFileName.isEmpty()) + sendClientEffectMessage(receiver, effectFileName, buffData.getParticleHardPoint()); } private void handleBuffIntentRemove(BuffIntent bi) { @@ -228,6 +236,10 @@ public class BuffService extends Service { new SkillModIntent(effect5Name, 0, (int) buffData.getEffect5Value() * valueFactor, creature).broadcast(); } + private void sendClientEffectMessage(CreatureObject target, String effectFileName, String hardPoint) { + target.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, target.getObjectId(), hardPoint, "")); + } + private class BuffRemover implements Runnable { @Override public void run() { @@ -289,8 +301,10 @@ public class BuffService extends Service { private final String effect5Name; private final float effect5Value; private final float defaultDuration; - - private BuffData(int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration) { + private final String effectFileName; + private final String particleHardPoint; + + private BuffData(int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint) { this.maxStackCount = maxStackCount; this.effect1Name = effect1Name; this.effect1Value = effect1Value; @@ -303,55 +317,65 @@ public class BuffService extends Service { this.effect5Name = effect5Name; this.effect5Value = effect5Value; this.defaultDuration = defaultDuration; + this.effectFileName = effectFileName; + this.particleHardPoint = particleHardPoint; } - public int getMaxStackCount() { + private int getMaxStackCount() { return maxStackCount; } - public String getEffect1Name() { + private String getEffect1Name() { return effect1Name; } - public float getEffect1Value() { + private float getEffect1Value() { return effect1Value; } - public String getEffect2Name() { + private String getEffect2Name() { return effect2Name; } - public float getEffect2Value() { + private float getEffect2Value() { return effect2Value; } - public String getEffect3Name() { + private String getEffect3Name() { return effect3Name; } - public float getEffect3Value() { + private float getEffect3Value() { return effect3Value; } - public String getEffect4Name() { + private String getEffect4Name() { return effect4Name; } - public float getEffect4Value() { + private float getEffect4Value() { return effect4Value; } - public String getEffect5Name() { + private String getEffect5Name() { return effect5Name; } - public float getEffect5Value() { + private float getEffect5Value() { return effect5Value; } - public float getDefaultDuration() { + private float getDefaultDuration() { return defaultDuration; } + + private String getEffectFileName() { + return effectFileName; + } + + private String getParticleHardPoint() { + return particleHardPoint; + } } From a095dff3cd6d0a417b3ea5fac58d7b44cb9ede55 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Fri, 29 Jan 2016 01:20:30 +0100 Subject: [PATCH 05/63] Finished StopClientEffectObjectByLabelMessage --- .../StopClientEffectObjectByLabelMessage.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/network/packets/swg/zone/spatial/StopClientEffectObjectByLabelMessage.java b/src/network/packets/swg/zone/spatial/StopClientEffectObjectByLabelMessage.java index d4f065d32..16eb0de2c 100644 --- a/src/network/packets/swg/zone/spatial/StopClientEffectObjectByLabelMessage.java +++ b/src/network/packets/swg/zone/spatial/StopClientEffectObjectByLabelMessage.java @@ -35,36 +35,40 @@ public class StopClientEffectObjectByLabelMessage extends SWGPacket { public static final int CRC = getCrc("StopClientEffectObjectByLabelMessage"); private long objectId; - private String effect; + private String label; + private boolean softStop; public StopClientEffectObjectByLabelMessage() { } - public StopClientEffectObjectByLabelMessage(long objectId, String effect) { + public StopClientEffectObjectByLabelMessage(long objectId, String label, boolean softStop) { this.objectId = objectId; - this.effect = effect; + this.label = label; + this.softStop = softStop; } public StopClientEffectObjectByLabelMessage(ByteBuffer data) { decode(data); } + @Override public void decode(ByteBuffer data) { if (!super.decode(data, CRC)) return; objectId = getLong(data); - effect = getAscii(data); - getByte(data); + label = getAscii(data); + softStop = getBoolean(data); } + @Override public ByteBuffer encode() { - ByteBuffer data = ByteBuffer.allocate(6); + ByteBuffer data = ByteBuffer.allocate(Short.BYTES * 2 + Integer.BYTES + Long.BYTES + label.length()); addShort(data, 4); addInt (data, CRC); addLong (data, objectId); - addAscii(data, effect); - addByte (data, 1); + addAscii(data, label); + addBoolean(data, softStop); return data; } From d5b39b30bf96487a20a4796e295646c9b8e0f008 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Fri, 29 Jan 2016 01:24:17 +0100 Subject: [PATCH 06/63] Fixed allocation mistake in StopClientEffectObjectByLabelMessage --- .../swg/zone/spatial/StopClientEffectObjectByLabelMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/packets/swg/zone/spatial/StopClientEffectObjectByLabelMessage.java b/src/network/packets/swg/zone/spatial/StopClientEffectObjectByLabelMessage.java index 16eb0de2c..b36c0e10b 100644 --- a/src/network/packets/swg/zone/spatial/StopClientEffectObjectByLabelMessage.java +++ b/src/network/packets/swg/zone/spatial/StopClientEffectObjectByLabelMessage.java @@ -63,7 +63,7 @@ public class StopClientEffectObjectByLabelMessage extends SWGPacket { @Override public ByteBuffer encode() { - ByteBuffer data = ByteBuffer.allocate(Short.BYTES * 2 + Integer.BYTES + Long.BYTES + label.length()); + ByteBuffer data = ByteBuffer.allocate(Short.BYTES * 2 + Integer.BYTES + Long.BYTES + Byte.BYTES + label.length()); addShort(data, 4); addInt (data, CRC); addLong (data, objectId); From 6912e0b55a827a32470b37e174c97ab796db40b0 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 30 Jan 2016 19:43:38 +0100 Subject: [PATCH 07/63] Implemented recourse buffs --- src/resources/Buff.java | 18 +-- .../objects/creature/CreatureObject.java | 40 +++--- src/services/commands/BuffService.java | 130 +++++++++++------- 3 files changed, 110 insertions(+), 78 deletions(-) diff --git a/src/resources/Buff.java b/src/resources/Buff.java index c1d7fb7a9..90b1a4439 100644 --- a/src/resources/Buff.java +++ b/src/resources/Buff.java @@ -3,7 +3,6 @@ package resources; import java.io.Serializable; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.concurrent.TimeUnit; import resources.encodables.Encodable; @@ -13,17 +12,15 @@ public class Buff implements Encodable, Serializable { private int duration; private long bufferId; - private int startPlayTime; - private final long endTime; + private int endTime; private long stackCount; private float skillMod1Value; public Buff(long bufferId, int playTime, int duration, float skillMod1Value) { this.bufferId = bufferId; - this.startPlayTime = playTime; this.duration = duration; this.skillMod1Value = skillMod1Value; - endTime = System.currentTimeMillis() + TimeUnit.MILLISECONDS.convert(duration, TimeUnit.SECONDS); + endTime = duration + playTime; stackCount = 1; } @@ -31,7 +28,7 @@ public class Buff implements Encodable, Serializable { public byte[] encode() { ByteBuffer data = ByteBuffer.allocate(Integer.BYTES * 2 + Float.BYTES + Long.BYTES * 2 + Short.BYTES ).order(ByteOrder.LITTLE_ENDIAN); - data.putInt(duration + startPlayTime); // Buff duration + time played on character + data.putInt(endTime); // Buff duration + time played on character data.putFloat(skillMod1Value); // The value for skillMod #1 on the buff. Displayed on the client as skillMod1Value * stackCount. data.putInt(duration); // Icon shadow "clock" overlay. data.putLong(bufferId); // Object ID of the buffer @@ -43,10 +40,9 @@ public class Buff implements Encodable, Serializable { @Override public void decode(ByteBuffer data) { - int playerBuffTime = data.getInt(); + endTime = data.getInt(); skillMod1Value = data.getFloat(); duration = data.getInt(); - startPlayTime = playerBuffTime - duration; bufferId = data.getLong(); stackCount = data.getLong(); data.getShort(); // unknown @@ -68,12 +64,8 @@ public class Buff implements Encodable, Serializable { stackCount += adjustment; } - public long getEndTime() { + public int getEndTime() { return endTime; } - public long getTotalDuration() { - return duration + startPlayTime; - } - } diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index de134306c..3d1d9939e 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -31,6 +31,8 @@ import java.util.ArrayList; import java.util.Collection; import java.io.IOException; import java.io.ObjectInputStream; +import java.util.HashMap; +import java.util.Map; import network.packets.swg.zone.UpdatePostureMessage; import network.packets.swg.zone.UpdatePvpStatusMessage; @@ -662,34 +664,40 @@ public class CreatureObject extends TangibleObject { return skillMod != null ? skillMod.getValue() : 0; } - public void addBuff(String buffName, Buff buff) { - buffs.put(CRC.getCrc(buffName), buff); - buffs.sendDeltaMessage(this); + public void addBuff(int buffCrc, Buff buff) { + if(!buffs.containsKey(buffCrc)) { + buffs.put(buffCrc, buff); + buffs.sendDeltaMessage(this); + } } - public void removeBuff(String buffName) { - buffs.remove(CRC.getCrc(buffName)); - buffs.sendDeltaMessage(this); + public void removeBuff(int buffCrc) { + // If a value was associated with the key, then send a delta. + if(buffs.containsKey(buffCrc)) { + buffs.remove(buffCrc); + buffs.sendDeltaMessage(this); + } } - public boolean hasBuff(String buffName) { - return buffs.containsKey(CRC.getCrc(buffName)); + public Buff getBuffByCrc(int buffCrc) { + return buffs.get(buffCrc); } - public Buff getBuffByName(String buffName) { - return buffs.get(CRC.getCrc(buffName)); - } - - public void adjustBuffStackCount(String buffName, int adjustment) { - int buffCrc = CRC.getCrc(buffName); + public void adjustBuffStackCount(int buffCrc, int adjustment) { Buff buff = buffs.get(buffCrc); buff.adjustStackCount(adjustment); // Adjust the stack count // TODO reset time remaining? buffs.update(buffCrc, this); // Send deltas for this key. } - public Collection getBuffs() { - return buffs.values(); + /** + * @return a copy of the buffs map. Removing and adding entries in this + * map will not affect the internal {@code SWGMap}. Do not edit the + * {@code Buff} values in the belief that deltas will be sent because + * they won't - this is incorrect usage. + */ + public Map getBuffs() { + return new HashMap<>(buffs); } public boolean isVisible() { diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 6f58c0f2e..3585f51a3 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -42,37 +42,44 @@ import network.packets.swg.zone.spatial.PlayClientEffectObjectMessage; import resources.Buff; import resources.client_info.ClientFactory; import resources.client_info.visitors.DatatableData; +import resources.common.CRC; import resources.control.Intent; import resources.control.Service; import resources.objects.creature.CreatureObject; -import utilities.Scripts; +import resources.objects.player.PlayerObject; public class BuffService extends Service { + // TODO buff slots + + // TODO skillmod divisors in SkillmodService + // TODO remove buffs on respec. Listen for respec intent and remove buffs with // BuffData that has REMOVE_ON_RESPEC = 1 // TODO group buffs - - // TODO particle effects - + // TODO remove group buff(s) when distance between receiver and caster is 100m + // is it possible to somehow determine if a buff is a group buff? // TODO decay buffs on deathblow // TODO debuffs vs buffs + // TODO test buff stacks + + // TODO cache conversions from buffName to buffCrc in buffNameToCrc() + // private static final byte GROUP_BUFF_RANGE = 100; - private final DatatableData buffTable; + private DatatableData buffTable; private final DelayQueue buffRemoval; private final ExecutorService executor; private boolean stopBuffRemover; - private final Map dataMap; + private final Map dataMap; public BuffService() { registerForIntent(BuffIntent.TYPE); registerForIntent(PlayerEventIntent.TYPE); - buffTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/buff/buff.iff"); buffRemoval = new DelayQueue<>(); executor = Executors.newSingleThreadScheduledExecutor(); dataMap = new HashMap<>(); @@ -112,8 +119,10 @@ public class BuffService extends Service { } private void loadBuffs() { + buffTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/buff/buff.iff"); + for(int row = 0; row < buffTable.getRowCount(); row++) { - dataMap.put((String) buffTable.getCell(row, 0), new BuffData( + dataMap.put(buffNameToCrc((String) buffTable.getCell(row, 0)), new BuffData( (int) buffTable.getCell(row, 28), // max stacks (String) buffTable.getCell(row, 7), // effect1 (float) buffTable.getCell(row, 8), // value1 @@ -127,7 +136,8 @@ public class BuffService extends Service { (float) buffTable.getCell(row, 16), // value5 (float) buffTable.getCell(row, 6), // default duration (String) buffTable.getCell(row, 19), // particle effect - (String) buffTable.getCell(row, 20) // particle hardpoint + (String) buffTable.getCell(row, 20), // particle hardpoint + (String) buffTable.getCell(row, 18) // Callback )); } } @@ -142,18 +152,21 @@ public class BuffService extends Service { } private void handleFirstZone(CreatureObject creature) { - for(Buff buff : creature.getBuffs()) { - // TODO fix. Needs buffName - manageBuff(buff, null, creature); - } + Map buffs = creature.getBuffs(); + + buffs.forEach((crc, buff) -> manageBuff(buff, crc, creature)); + } + + private int buffNameToCrc(String buffName) { + return CRC.getCrc(buffName); } private void handleBuffIntentAdd(BuffIntent bi) { - CreatureObject receiver = bi.getReceiver(); - CreatureObject buffer = bi.getBuffer(); - String buffName = bi.getBuffName(); - - BuffData buffData = dataMap.get(buffName); + addBuff(buffNameToCrc(bi.getBuffName()), bi.getReceiver(), bi.getBuffer()); + } + + private void addBuff(int buffCrc, CreatureObject receiver, CreatureObject buffer) { + BuffData buffData = dataMap.get(buffCrc); if(buffData == null) return; @@ -161,9 +174,9 @@ public class BuffService extends Service { Buff buff = new Buff(buffer.getObjectId(), receiver.getPlayerObject().getPlayTime(), (int) buffData.getDefaultDuration(), buffData.getEffect1Value()); sendSkillModIntent(buffData, receiver, stopBuffRemover); - receiver.addBuff(buffName, buff); + receiver.addBuff(buffCrc, buff); - manageBuff(buff, buffName, receiver); + manageBuff(buff, buffCrc, receiver); String effectFileName = buffData.getEffectFileName(); @@ -172,46 +185,58 @@ public class BuffService extends Service { } private void handleBuffIntentRemove(BuffIntent bi) { - removeBuff(bi.getReceiver(), bi.getBuffName(), false); + removeBuff(bi.getReceiver(), buffNameToCrc(bi.getBuffName()), false); } - private void manageBuff(Buff buff, String buffName, CreatureObject creature) { + private void manageBuff(Buff buff, int buffCrc, CreatureObject creature) { // If this buff has less than or 0 seconds left, then remove it. if(buff.getEndTime() <= 0) { - removeBuff(creature, buffName, true); + removeBuff(creature, buffCrc, true); } else if(buff.getDuration() >= 0) { // If this buff doesn't last forever, we'll schedule it for removal in the future - buffRemoval.put(new BuffDelayed(buff, buffName, creature)); + buffRemoval.put(new BuffDelayed(buff, buffCrc, creature)); } } - private void removeBuff(CreatureObject creature, String buffName, boolean expired) { + private void removeBuff(CreatureObject creature, int buffCrc, boolean expired) { // Get the BuffData for this buff name. - BuffData buffData = dataMap.get(buffName); + BuffData buffData = dataMap.get(buffCrc); if(buffData == null) { return; } - Buff buff = creature.getBuffByName(buffName); + Buff buff = creature.getBuffByCrc(buffCrc); // Check if this buff can be stacked if(buffData.getMaxStackCount() > 1 && !expired) { // Check if this buff has been stacked - if(buff.getStackCount() > 1) { - // Get the adjustment from the script - // TODO casting exception if the script doesn't exist or returns null - int adjustment = Scripts.invoke(buffName, "onStackChange", creature); - + if(buff.getStackCount() > 1) { // If it has, reduce the stack count and reset the duration. - creature.adjustBuffStackCount(buffName, adjustment); + // TODO NGE: buffs can, based on skillmods, adjust with different values + creature.adjustBuffStackCount(buffCrc, -1); } } else { // Remove skillmods sendSkillModIntent(buffData, creature, true); // Remove the buff from the creature - creature.removeBuff(buffName); + creature.removeBuff(buffCrc); + + // Check if the callback is a buff + String callback = buffData.getCallback(); + + if(callback.isEmpty()) + return; + + int callbackCrc = buffNameToCrc(callback); + if(dataMap.containsKey(callbackCrc)) { + // Apply the callback buff + addBuff(callbackCrc, creature, creature); + } else { + // Call the callback command script + // TODO + } } } @@ -247,7 +272,7 @@ public class BuffService extends Service { BuffDelayed buffToRemove = buffRemoval.poll(); if(buffToRemove != null) - removeBuff(buffToRemove.owner, buffToRemove.buffName, true); + removeBuff(buffToRemove.creature, buffToRemove.buffCrc, true); } } } @@ -255,23 +280,25 @@ public class BuffService extends Service { private class BuffDelayed implements Delayed { private final Buff buff; - private final String buffName; - private final CreatureObject owner; + private final int buffCrc; + private final PlayerObject owner; + private final CreatureObject creature; - private BuffDelayed(Buff buff, String buffName, CreatureObject owner) { + private BuffDelayed(Buff buff, int buffCrc, CreatureObject creature) { this.buff = buff; - this.buffName = buffName; - this.owner = owner; + this.buffCrc = buffCrc; + this.creature = creature; + owner = creature.getPlayerObject(); } @Override public int compareTo(Delayed o) { - return Long.compare(buff.getEndTime(), ((BuffDelayed) o).buff.getEndTime()); + return Integer.compare(buff.getEndTime(), ((BuffDelayed) o).buff.getEndTime()); } @Override public long getDelay(TimeUnit timeUnit) { - return timeUnit.convert(buff.getTotalDuration() - owner.getPlayerObject().getPlayTime(), TimeUnit.MILLISECONDS); + return timeUnit.convert(buff.getEndTime() - owner.getPlayTime(), TimeUnit.MILLISECONDS); } } @@ -279,14 +306,13 @@ public class BuffService extends Service { /** * @author Mads * Each instance of this class holds the base information - * for a specific buff name. Thus, we only store shared data that each {@code Buff} instance - * would otherwise have been holding. + * for a specific buff name. * - * Example: Instead of each {@code Buff} instance storing the max amount of times you can - * stack it, a shared class stores that information. + * Example: Instead of each {@code Buff} instance storing the max amount of + * times you can stack it, a shared class stores that information. * - * With many {@code Buff} instances in play, this will result in memory - * usage reduction. + * With many {@code Buff} instances in play, this will result in a noticeable + * memory usage reduction. */ private class BuffData { private final int maxStackCount; @@ -303,8 +329,9 @@ public class BuffService extends Service { private final float defaultDuration; private final String effectFileName; private final String particleHardPoint; + private final String callback; - private BuffData(int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint) { + private BuffData(int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String callback) { this.maxStackCount = maxStackCount; this.effect1Name = effect1Name; this.effect1Value = effect1Value; @@ -319,6 +346,7 @@ public class BuffService extends Service { this.defaultDuration = defaultDuration; this.effectFileName = effectFileName; this.particleHardPoint = particleHardPoint; + this.callback = callback; } private int getMaxStackCount() { @@ -376,6 +404,10 @@ public class BuffService extends Service { private String getParticleHardPoint() { return particleHardPoint; } + + public String getCallback() { + return callback; + } } From 5d4fb756f73f751b0227314cea8e90220f7af0a4 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 31 Mar 2016 10:03:22 +0200 Subject: [PATCH 08/63] Fixed an issue where applying a buff to a player would bomb the player with PLAY09 deltas --- src/resources/objects/player/PlayerObject.java | 11 ++++++----- src/services/galaxy/ConnectionService.java | 6 +----- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index e22fa52bb..a3798c035 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -155,11 +155,12 @@ public class PlayerObject extends IntangibleObject { sendDelta(3, 7, title, StringType.ASCII); } + /** + * Calling this method will update the playTime field. + * @return the amount of time this PlayerObject has played. + */ public int getPlayTime() { - int startTime = getStartPlayTime(); - int deltaTime = (int) ((System.currentTimeMillis()) - startTime); - setPlayTime((int) TimeUnit.MILLISECONDS.toSeconds(deltaTime)); // Set new time and send delta - + playTime = (int) TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis() - getStartPlayTime()); // Set new time and send delta return playTime; } @@ -167,7 +168,7 @@ public class PlayerObject extends IntangibleObject { this.playTime = playTime; sendDelta(3, 9, playTime); } - + public int getGcwPoints() { return gcwPoints; } diff --git a/src/services/galaxy/ConnectionService.java b/src/services/galaxy/ConnectionService.java index df8e68833..ab9d8c1c4 100644 --- a/src/services/galaxy/ConnectionService.java +++ b/src/services/galaxy/ConnectionService.java @@ -258,11 +258,7 @@ public class ConnectionService extends Service { if (playerObject == null) return; - int currentTime = playerObject.getPlayTime(); - int startTime = playerObject.getStartPlayTime(); - int deltaTime = (int) ((System.currentTimeMillis()) - startTime); - int newTotalTime = currentTime + (int) TimeUnit.MILLISECONDS.toSeconds(deltaTime); - playerObject.setPlayTime(newTotalTime); + playerObject.setPlayTime(playerObject.getPlayTime()); } private static class DisappearPlayer { From d8e34953c04fcbc3f3868c272b595badfbee1bf8 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 31 Mar 2016 17:38:29 +0200 Subject: [PATCH 09/63] Removed the old Buff class and refactored existing code to use the new one --- src/resources/Buff.java | 71 ------------------- .../objects/creature/CreatureObject.java | 11 ++- src/services/commands/BuffService.java | 38 +++++----- 3 files changed, 22 insertions(+), 98 deletions(-) delete mode 100644 src/resources/Buff.java diff --git a/src/resources/Buff.java b/src/resources/Buff.java deleted file mode 100644 index 90b1a4439..000000000 --- a/src/resources/Buff.java +++ /dev/null @@ -1,71 +0,0 @@ -package resources; - -import java.io.Serializable; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import resources.encodables.Encodable; - -public class Buff implements Encodable, Serializable { - - private static final long serialVersionUID = 1; - - private int duration; - private long bufferId; - private int endTime; - private long stackCount; - private float skillMod1Value; - - public Buff(long bufferId, int playTime, int duration, float skillMod1Value) { - this.bufferId = bufferId; - this.duration = duration; - this.skillMod1Value = skillMod1Value; - endTime = duration + playTime; - stackCount = 1; - } - - @Override - public byte[] encode() { - ByteBuffer data = ByteBuffer.allocate(Integer.BYTES * 2 + Float.BYTES + Long.BYTES * 2 + Short.BYTES ).order(ByteOrder.LITTLE_ENDIAN); - - data.putInt(endTime); // Buff duration + time played on character - data.putFloat(skillMod1Value); // The value for skillMod #1 on the buff. Displayed on the client as skillMod1Value * stackCount. - data.putInt(duration); // Icon shadow "clock" overlay. - data.putLong(bufferId); // Object ID of the buffer - data.putLong(stackCount); // Stack count - data.putShort((short) 0); // Unknown. Could also be two bytes. - - return data.array(); - } - - @Override - public void decode(ByteBuffer data) { - endTime = data.getInt(); - skillMod1Value = data.getFloat(); - duration = data.getInt(); - bufferId = data.getLong(); - stackCount = data.getLong(); - data.getShort(); // unknown - } - - public int getDuration() { - return duration; - } - - public long getStackCount() { - return stackCount; - } - - /** - * Adjusts the stack count. This method doesn't send any delta. - * @param adjustment - */ - public void adjustStackCount(int adjustment) { - stackCount += adjustment; - } - - public int getEndTime() { - return endTime; - } - -} diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 46e105e58..f76e3ff41 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -39,7 +39,6 @@ import network.packets.swg.zone.UpdatePostureMessage; import network.packets.swg.zone.UpdatePvpStatusMessage; import network.packets.swg.zone.baselines.Baseline.BaselineType; import network.packets.swg.zone.object_controller.PostureUpdate; -import resources.Buff; import resources.HologramColour; import resources.Posture; import resources.PvpFlag; @@ -667,7 +666,7 @@ public class CreatureObject extends TangibleObject { return skillMod != null ? skillMod.getValue() : 0; } - public void addBuff(int buffCrc, Buff buff) { + public void addBuff(CRC buffCrc, Buff buff) { if(!buffs.containsKey(buffCrc)) { buffs.put(buffCrc, buff); buffs.sendDeltaMessage(this); @@ -675,7 +674,7 @@ public class CreatureObject extends TangibleObject { } } - public void removeBuff(int buffCrc) { + public void removeBuff(CRC buffCrc) { // If a value was associated with the key, then send a delta. if(buffs.containsKey(buffCrc)) { buffs.remove(buffCrc); @@ -684,11 +683,11 @@ public class CreatureObject extends TangibleObject { } } - public Buff getBuffByCrc(int buffCrc) { + public Buff getBuffByCrc(CRC buffCrc) { return buffs.get(buffCrc); } - public void adjustBuffStackCount(int buffCrc, int adjustment) { + public void adjustBuffStackCount(CRC buffCrc, int adjustment) { Buff buff = buffs.get(buffCrc); buff.adjustStackCount(adjustment); // Adjust the stack count // TODO reset time remaining? @@ -701,7 +700,7 @@ public class CreatureObject extends TangibleObject { * {@code Buff} values in the belief that deltas will be sent because * they won't - this is incorrect usage. */ - public Map getBuffs() { + public Map getBuffs() { return new HashMap<>(buffs); } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 3585f51a3..2cfba7f50 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -39,12 +39,12 @@ import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; import network.packets.swg.zone.spatial.PlayClientEffectObjectMessage; -import resources.Buff; import resources.client_info.ClientFactory; import resources.client_info.visitors.DatatableData; import resources.common.CRC; import resources.control.Intent; import resources.control.Service; +import resources.objects.creature.Buff; import resources.objects.creature.CreatureObject; import resources.objects.player.PlayerObject; @@ -66,15 +66,13 @@ public class BuffService extends Service { // TODO test buff stacks - // TODO cache conversions from buffName to buffCrc in buffNameToCrc() - // private static final byte GROUP_BUFF_RANGE = 100; private DatatableData buffTable; private final DelayQueue buffRemoval; private final ExecutorService executor; private boolean stopBuffRemover; - private final Map dataMap; + private final Map dataMap; public BuffService() { registerForIntent(BuffIntent.TYPE); @@ -122,7 +120,7 @@ public class BuffService extends Service { buffTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/buff/buff.iff"); for(int row = 0; row < buffTable.getRowCount(); row++) { - dataMap.put(buffNameToCrc((String) buffTable.getCell(row, 0)), new BuffData( + dataMap.put(new CRC((String) buffTable.getCell(row, 0)), new BuffData( (int) buffTable.getCell(row, 28), // max stacks (String) buffTable.getCell(row, 7), // effect1 (float) buffTable.getCell(row, 8), // value1 @@ -152,28 +150,26 @@ public class BuffService extends Service { } private void handleFirstZone(CreatureObject creature) { - Map buffs = creature.getBuffs(); + Map buffs = creature.getBuffs(); buffs.forEach((crc, buff) -> manageBuff(buff, crc, creature)); } - private int buffNameToCrc(String buffName) { - return CRC.getCrc(buffName); - } - private void handleBuffIntentAdd(BuffIntent bi) { - addBuff(buffNameToCrc(bi.getBuffName()), bi.getReceiver(), bi.getBuffer()); + addBuff(new CRC(bi.getBuffName()), bi.getReceiver(), bi.getBuffer()); } - private void addBuff(int buffCrc, CreatureObject receiver, CreatureObject buffer) { + private void addBuff(CRC buffCrc, CreatureObject receiver, CreatureObject buffer) { BuffData buffData = dataMap.get(buffCrc); if(buffData == null) return; + // TODO stack counts upon add/remove probably need to be defined on a per-buff basis due to skillmod influence. + int stackCount = 1; + int buffDuration = (int) buffData.getDefaultDuration(); + Buff buff = new Buff(receiver.getPlayerObject().getPlayTime() + buffDuration, buffData.getEffect1Value(), buffDuration, buffer.getObjectId(), stackCount); - Buff buff = new Buff(buffer.getObjectId(), receiver.getPlayerObject().getPlayTime(), (int) buffData.getDefaultDuration(), buffData.getEffect1Value()); - - sendSkillModIntent(buffData, receiver, stopBuffRemover); + sendSkillModIntent(buffData, receiver, false); receiver.addBuff(buffCrc, buff); manageBuff(buff, buffCrc, receiver); @@ -185,10 +181,10 @@ public class BuffService extends Service { } private void handleBuffIntentRemove(BuffIntent bi) { - removeBuff(bi.getReceiver(), buffNameToCrc(bi.getBuffName()), false); + removeBuff(bi.getReceiver(), new CRC(bi.getBuffName()), false); } - private void manageBuff(Buff buff, int buffCrc, CreatureObject creature) { + private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { // If this buff has less than or 0 seconds left, then remove it. if(buff.getEndTime() <= 0) { removeBuff(creature, buffCrc, true); @@ -198,7 +194,7 @@ public class BuffService extends Service { } } - private void removeBuff(CreatureObject creature, int buffCrc, boolean expired) { + private void removeBuff(CreatureObject creature, CRC buffCrc, boolean expired) { // Get the BuffData for this buff name. BuffData buffData = dataMap.get(buffCrc); @@ -229,7 +225,7 @@ public class BuffService extends Service { if(callback.isEmpty()) return; - int callbackCrc = buffNameToCrc(callback); + CRC callbackCrc = new CRC(callback); if(dataMap.containsKey(callbackCrc)) { // Apply the callback buff addBuff(callbackCrc, creature, creature); @@ -280,11 +276,11 @@ public class BuffService extends Service { private class BuffDelayed implements Delayed { private final Buff buff; - private final int buffCrc; + private final CRC buffCrc; private final PlayerObject owner; private final CreatureObject creature; - private BuffDelayed(Buff buff, int buffCrc, CreatureObject creature) { + private BuffDelayed(Buff buff, CRC buffCrc, CreatureObject creature) { this.buff = buff; this.buffCrc = buffCrc; this.creature = creature; From 2603ad4d0b279f7bba4c2d2c3a0f91deedf74d6a Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 2 Apr 2016 04:44:19 +0200 Subject: [PATCH 10/63] Tweaked a few code comments in BuffService --- src/services/commands/BuffService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 2cfba7f50..1e4c53cb2 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -185,11 +185,11 @@ public class BuffService extends Service { } private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { - // If this buff has less than or 0 seconds left, then remove it. if(buff.getEndTime() <= 0) { + // If this buff has less than or 0 seconds left, then remove it. removeBuff(creature, buffCrc, true); } else if(buff.getDuration() >= 0) { - // If this buff doesn't last forever, we'll schedule it for removal in the future + // If this buff doesn't last forever or hasn't expired, we'll schedule it for removal in the future buffRemoval.put(new BuffDelayed(buff, buffCrc, creature)); } } From 8f4163636702f7f4d01876f73f1ef5b914e179bf Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 2 Apr 2016 04:44:55 +0200 Subject: [PATCH 11/63] Fixed an issue where buffs could never be applied to anything --- src/resources/common/CRC.java | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/resources/common/CRC.java b/src/resources/common/CRC.java index f6a82cccd..e9d53bd16 100644 --- a/src/resources/common/CRC.java +++ b/src/resources/common/CRC.java @@ -121,6 +121,31 @@ public class CRC implements Encodable, Serializable { public String toString() { return str; } + + @Override + public int hashCode() { + int hash = 7; + hash = 89 * hash + this.crc; + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final CRC other = (CRC) obj; + if (this.crc != other.crc) { + return false; + } + return true; + } public static int getCrc(String input) { return getCrc(input.getBytes(StandardCharsets.UTF_8)); From 38cdf54fdde23b5d2d42b246fd531a3f7c25696e Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 2 Apr 2016 04:48:03 +0200 Subject: [PATCH 12/63] Removed a couple of debug prints from CreatureObject --- src/resources/objects/creature/CreatureObject.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index f76e3ff41..7557e6e24 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -670,7 +670,6 @@ public class CreatureObject extends TangibleObject { if(!buffs.containsKey(buffCrc)) { buffs.put(buffCrc, buff); buffs.sendDeltaMessage(this); - System.out.println("added buff"); } } @@ -679,7 +678,6 @@ public class CreatureObject extends TangibleObject { if(buffs.containsKey(buffCrc)) { buffs.remove(buffCrc); buffs.sendDeltaMessage(this); - System.out.println("removed buff"); } } From 84340b75704514e00ce1a77331ad488799a2d0c0 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 10 Oct 2016 16:02:59 +0200 Subject: [PATCH 13/63] Reverted changes to cfg/nge.cfg --- cfg/nge.cfg | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cfg/nge.cfg b/cfg/nge.cfg index f251c8d29..57f3c778f 100644 --- a/cfg/nge.cfg +++ b/cfg/nge.cfg @@ -1,15 +1,15 @@ -# 29/08/2016 05:25:30 CEST -CLEAN-CHARACTER-DATA=1 -ENABLE-LOGGING=true -GALAXY-MAX-CHARACTERS=5 -GALAXY-MAX-CHARACTERS-PER-PERIOD=5 -GALAXY-MAX-ONLINE=3000 -GALAXY-NAME=Holocore -LOAD-OBJECTS=true -LOCAL-DB=nge -LOCAL-PASS=nge -LOCAL-USER=nge -LOG-LEVEL=DEBUG -PACKET-DEBUG=false -PRIMARY-SPAWN-LOCATION=tat_moseisley -WIPE-ODB-FILES=1 +-# 19/08/2016 05:19:18 MESZ +-CLEAN-CHARACTER-DATA=0 +-ENABLE-LOGGING=true +-GALAXY-MAX-CHARACTERS=5 +-GALAXY-MAX-CHARACTERS-PER-PERIOD=5 +-GALAXY-MAX-ONLINE=3000 +-GALAXY-NAME=Holocore +-LOAD-OBJECTS=true +-LOCAL-DB=nge +-LOCAL-PASS=nge +-LOCAL-USER=nge +-LOG-LEVEL=DEBUG +-PACKET-DEBUG=false +-PRIMARY-SPAWN-LOCATION=tat_moseisley +-WIPE-ODB-FILES=0 From 60f3cb9f7509f6c71f47de1d1e3451c91a6d3db0 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 10 Oct 2016 16:04:48 +0200 Subject: [PATCH 14/63] Fixed incorrect formatting of cfg/nge.cfg --- cfg/nge.cfg | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/cfg/nge.cfg b/cfg/nge.cfg index 57f3c778f..1eed58af5 100644 --- a/cfg/nge.cfg +++ b/cfg/nge.cfg @@ -1,15 +1,15 @@ --# 19/08/2016 05:19:18 MESZ --CLEAN-CHARACTER-DATA=0 --ENABLE-LOGGING=true --GALAXY-MAX-CHARACTERS=5 --GALAXY-MAX-CHARACTERS-PER-PERIOD=5 --GALAXY-MAX-ONLINE=3000 --GALAXY-NAME=Holocore --LOAD-OBJECTS=true --LOCAL-DB=nge --LOCAL-PASS=nge --LOCAL-USER=nge --LOG-LEVEL=DEBUG --PACKET-DEBUG=false --PRIMARY-SPAWN-LOCATION=tat_moseisley --WIPE-ODB-FILES=0 +# 19/08/2016 05:19:18 MESZ +CLEAN-CHARACTER-DATA=0 +ENABLE-LOGGING=true +GALAXY-MAX-CHARACTERS=5 +GALAXY-MAX-CHARACTERS-PER-PERIOD=5 +GALAXY-MAX-ONLINE=3000 +GALAXY-NAME=Holocore +LOAD-OBJECTS=true +LOCAL-DB=nge +LOCAL-PASS=nge +LOCAL-USER=nge +LOG-LEVEL=DEBUG +PACKET-DEBUG=false +PRIMARY-SPAWN-LOCATION=tat_moseisley +WIPE-ODB-FILES=0 From 642ed9e7f3e49171d518880c4c56d678209aca11 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 10 Oct 2016 16:05:24 +0200 Subject: [PATCH 15/63] Added whitespace --- cfg/nge.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/nge.cfg b/cfg/nge.cfg index 1eed58af5..b87887447 100644 --- a/cfg/nge.cfg +++ b/cfg/nge.cfg @@ -12,4 +12,4 @@ LOCAL-USER=nge LOG-LEVEL=DEBUG PACKET-DEBUG=false PRIMARY-SPAWN-LOCATION=tat_moseisley -WIPE-ODB-FILES=0 +WIPE-ODB-FILES=0 From 3f80c00151954a97ffc4db5d5f45b8d46be22288 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 10 Oct 2016 16:07:17 +0200 Subject: [PATCH 16/63] Yet another attempt at reverting nge.cfg --- cfg/nge.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cfg/nge.cfg b/cfg/nge.cfg index b87887447..1eed58af5 100644 --- a/cfg/nge.cfg +++ b/cfg/nge.cfg @@ -12,4 +12,4 @@ LOCAL-USER=nge LOG-LEVEL=DEBUG PACKET-DEBUG=false PRIMARY-SPAWN-LOCATION=tat_moseisley -WIPE-ODB-FILES=0 +WIPE-ODB-FILES=0 From 218c5366c7167782a16ebe12da29192cc37f2247 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 10 Oct 2016 20:07:03 +0200 Subject: [PATCH 17/63] Optimised BuffService slightly --- src/services/commands/BuffService.java | 75 +++++++++++++++++--------- 1 file changed, 49 insertions(+), 26 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 972f4ed8e..22c98a97a 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit; import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; -import main.ProjectSWG; import network.packets.swg.zone.PlayClientEffectObjectMessage; import resources.client_info.ClientFactory; import resources.client_info.visitors.DatatableData; @@ -47,7 +46,7 @@ import resources.control.Intent; import resources.control.Service; import resources.objects.creature.Buff; import resources.objects.creature.CreatureObject; -import resources.objects.player.PlayerObject; +import resources.server_info.Log; import utilities.ThreadUtilities; public class BuffService extends Service { @@ -70,27 +69,34 @@ public class BuffService extends Service { // private static final byte GROUP_BUFF_RANGE = 100; - private final DelayQueue buffRemoval; + private final Map> buffRemoval; private final ExecutorService executor; - private boolean stopBuffRemover; private final Map dataMap; public BuffService() { registerForIntent(BuffIntent.TYPE); registerForIntent(PlayerEventIntent.TYPE); - buffRemoval = new DelayQueue<>(); + buffRemoval = new HashMap<>(); executor = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("buff-service")); dataMap = new HashMap<>(); } @Override public boolean initialize() { + long startTime = System.currentTimeMillis(); + Log.i(this, "Loading buffs..."); loadBuffs(); - - executor.execute(new BuffRemover()); + Log.i(this, "Finished loading buffs in %dms", System.currentTimeMillis() - startTime); return super.initialize(); } + + @Override + public boolean start() { + executor.execute(() -> buffRemoval.values().forEach(buffQueue -> checkBuffQueue(buffQueue))); + + return super.start(); + } @Override public void onIntentReceived(Intent i) { @@ -110,11 +116,18 @@ public class BuffService extends Service { } @Override - public boolean terminate() { - stopBuffRemover = true; + public boolean stop() { executor.shutdown(); - return super.terminate(); + return super.stop(); + } + + private void checkBuffQueue(DelayQueue buffQueue) { + BuffDelayed buffToRemove = buffQueue.poll(); + + if (buffToRemove != null) { + removeBuff(buffToRemove.creature, buffToRemove.buffCrc, true); + } } private void loadBuffs() { @@ -146,7 +159,7 @@ public class BuffService extends Service { switch(pei.getEvent()) { case PE_FIRST_ZONE: handleFirstZone(creature); break; - case PE_DISAPPEAR: break; // TODO stop managing their buffs if they disappear + case PE_DISAPPEAR: handleDisappear(creature); break; } } @@ -154,6 +167,10 @@ public class BuffService extends Service { creature.getBuffs().forEach((crc, buff) -> manageBuff(buff, crc, creature)); } + private void handleDisappear(CreatureObject creature) { + buffRemoval.remove(creature); + } + private void handleBuffIntentAdd(BuffIntent bi) { addBuff(new CRC(bi.getBuffName()), bi.getReceiver(), bi.getBuffer()); } @@ -194,7 +211,15 @@ public class BuffService extends Service { removeBuff(creature, buffCrc, true); } else if(buff.getDuration() >= 0) { // If this buff doesn't last forever or hasn't expired, we'll schedule it for removal in the future - buffRemoval.put(new BuffDelayed(buff, buffCrc, creature)); + DelayQueue buffQueue = buffRemoval.get(creature); + + if(buffQueue == null) { + buffQueue = new DelayQueue<>(); + + buffRemoval.put(creature, buffQueue); + } + + buffQueue.add(new BuffDelayed(buff, buffCrc, creature)); } } @@ -265,30 +290,16 @@ public class BuffService extends Service { target.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, hardPoint, target.getObjectId())); } - private class BuffRemover implements Runnable { - @Override - public void run() { - while(!stopBuffRemover) { - BuffDelayed buffToRemove = buffRemoval.poll(); - - if(buffToRemove != null) - removeBuff(buffToRemove.creature, buffToRemove.buffCrc, true); - } - } - } - private class BuffDelayed implements Delayed { private final Buff buff; private final CRC buffCrc; - private final PlayerObject owner; private final CreatureObject creature; private BuffDelayed(Buff buff, CRC buffCrc, CreatureObject creature) { this.buff = buff; this.buffCrc = buffCrc; this.creature = creature; - owner = creature.getPlayerObject(); } @Override @@ -300,6 +311,18 @@ public class BuffService extends Service { public long getDelay(TimeUnit timeUnit) { return timeUnit.convert(buff.getEndTime() - System.currentTimeMillis() / 1000, TimeUnit.MILLISECONDS); } + + public Buff getBuff() { + return buff; + } + + public CRC getBuffCrc() { + return buffCrc; + } + + public CreatureObject getCreature() { + return creature; + } } From 7bea04546f290bbe27698c208f98960ff2c33033 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 10 Oct 2016 21:14:07 +0200 Subject: [PATCH 18/63] Added a bit of logging to BuffService --- src/services/commands/BuffService.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 22c98a97a..ee8354416 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -178,8 +178,10 @@ public class BuffService extends Service { private void addBuff(CRC buffCrc, CreatureObject receiver, CreatureObject buffer) { BuffData buffData = dataMap.get(buffCrc); - if(buffData == null) + if(buffData == null) { + Log.e(this, "Could not add %s to %s - buff data for it does not exist", receiver, buffCrc); return; + } // The client-side timer hinges on the playTime of the PlayerObject. // We therefore must update it to current time, so the timer starts from full duration @@ -224,10 +226,10 @@ public class BuffService extends Service { } private void removeBuff(CreatureObject creature, CRC buffCrc, boolean expired) { - // Get the BuffData for this buff name. BuffData buffData = dataMap.get(buffCrc); if(buffData == null) { + Log.e(this, "Could not remove %s from %s - buff data for it does not exist", creature, buffCrc); return; } From b4693df56b5c319b0be07d7d410cf03be8802603 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 10 Oct 2016 21:21:09 +0200 Subject: [PATCH 19/63] Fixed private field access to BuffDelayed --- src/services/commands/BuffService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index ee8354416..6aa8d17a7 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -69,8 +69,8 @@ public class BuffService extends Service { // private static final byte GROUP_BUFF_RANGE = 100; - private final Map> buffRemoval; private final ExecutorService executor; + private final Map> buffRemoval; private final Map dataMap; public BuffService() { @@ -78,8 +78,8 @@ public class BuffService extends Service { registerForIntent(PlayerEventIntent.TYPE); buffRemoval = new HashMap<>(); - executor = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("buff-service")); dataMap = new HashMap<>(); + executor = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("buff-service")); } @Override @@ -126,7 +126,7 @@ public class BuffService extends Service { BuffDelayed buffToRemove = buffQueue.poll(); if (buffToRemove != null) { - removeBuff(buffToRemove.creature, buffToRemove.buffCrc, true); + removeBuff(buffToRemove.getCreature(), buffToRemove.getBuffCrc(), true); } } From e719b1fa7224e3aa7c20841923635934e5c521c4 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 11 Oct 2016 00:39:42 +0200 Subject: [PATCH 20/63] Fixed an issue where buffs weren't expiring --- src/services/commands/BuffService.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 6aa8d17a7..7b21f635e 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -31,13 +31,13 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; +import java.util.concurrent.ScheduledExecutorService; import network.packets.swg.zone.PlayClientEffectObjectMessage; import resources.client_info.ClientFactory; import resources.client_info.visitors.DatatableData; @@ -69,7 +69,7 @@ public class BuffService extends Service { // private static final byte GROUP_BUFF_RANGE = 100; - private final ExecutorService executor; + private final ScheduledExecutorService executor; private final Map> buffRemoval; private final Map dataMap; @@ -93,7 +93,8 @@ public class BuffService extends Service { @Override public boolean start() { - executor.execute(() -> buffRemoval.values().forEach(buffQueue -> checkBuffQueue(buffQueue))); + // Polls buff queues every second + executor.scheduleAtFixedRate(() -> buffRemoval.values().forEach(buffQueue -> checkBuffQueue(buffQueue)), 0, 1, TimeUnit.SECONDS); return super.start(); } From 5596d27953a8e92553b47cdec642c3332fd66ff7 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 12 Oct 2016 14:55:03 +0200 Subject: [PATCH 21/63] Added license to intents.BuffIntent --- src/intents/BuffIntent.java | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/intents/BuffIntent.java b/src/intents/BuffIntent.java index 46b9a4222..50e9b0b90 100644 --- a/src/intents/BuffIntent.java +++ b/src/intents/BuffIntent.java @@ -1,3 +1,30 @@ +/*********************************************************************************** +* Copyright (c) 2015 /// Project SWG /// www.projectswg.com * +* * +* ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * +* July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * +* Our goal is to create an emulator which will provide a server for players to * +* continue playing a game similar to the one they used to play. We are basing * +* it on the final publish of the game prior to end-game events. * +* * +* This file is part of Holocore. * +* * +* -------------------------------------------------------------------------------- * +* * +* Holocore is free software: you can redistribute it and/or modify * +* it under the terms of the GNU Affero General Public License as * +* published by the Free Software Foundation, either version 3 of the * +* License, or (at your option) any later version. * +* * +* Holocore is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * +* GNU Affero General Public License for more details. * +* * +* You should have received a copy of the GNU Affero General Public License * +* along with Holocore. If not, see . * +* * +***********************************************************************************/ package intents; import resources.control.Intent; From 6196862b397b9ad38c004a4843f5c2e40afba7c4 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 12 Oct 2016 15:00:56 +0200 Subject: [PATCH 22/63] Moved some logic from BuffService.onIntentReceived() into a method --- src/services/commands/BuffService.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 7b21f635e..3f095e704 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -102,16 +102,7 @@ public class BuffService extends Service { @Override public void onIntentReceived(Intent i) { switch(i.getType()) { - case BuffIntent.TYPE: - BuffIntent bi = (BuffIntent) i; - - if(bi.isRemove()) { - handleBuffIntentRemove(bi); - } else { - handleBuffIntentAdd(bi); - } - - break; + case BuffIntent.TYPE: handleBuffIntent((BuffIntent) i); break; case PlayerEventIntent.TYPE: handlePlayerEventIntent((PlayerEventIntent) i); break; } } @@ -155,6 +146,14 @@ public class BuffService extends Service { } } + private void handleBuffIntent(BuffIntent i) { + if (i.isRemove()) { + handleBuffIntentRemove(i); + } else { + handleBuffIntentAdd(i); + } + } + private void handlePlayerEventIntent(PlayerEventIntent pei) { CreatureObject creature = pei.getPlayer().getCreatureObject(); From a349c08ee7302f8f97c3a6d6b36b743ecc19aa50 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 12 Oct 2016 15:03:17 +0200 Subject: [PATCH 23/63] Removed a couple of one-liner methods in BuffService --- src/services/commands/BuffService.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 3f095e704..7bca808f5 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -148,9 +148,9 @@ public class BuffService extends Service { private void handleBuffIntent(BuffIntent i) { if (i.isRemove()) { - handleBuffIntentRemove(i); + removeBuff(i.getReceiver(), new CRC(i.getBuffName()), false); } else { - handleBuffIntentAdd(i); + addBuff(new CRC(i.getBuffName()), i.getReceiver(), i.getBuffer()); } } @@ -171,10 +171,6 @@ public class BuffService extends Service { buffRemoval.remove(creature); } - private void handleBuffIntentAdd(BuffIntent bi) { - addBuff(new CRC(bi.getBuffName()), bi.getReceiver(), bi.getBuffer()); - } - private void addBuff(CRC buffCrc, CreatureObject receiver, CreatureObject buffer) { BuffData buffData = dataMap.get(buffCrc); @@ -203,10 +199,6 @@ public class BuffService extends Service { sendClientEffectMessage(receiver, effectFileName, buffData.getParticleHardPoint()); } - private void handleBuffIntentRemove(BuffIntent bi) { - removeBuff(bi.getReceiver(), new CRC(bi.getBuffName()), false); - } - private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { if(buff.getEndTime() <= 0) { // If this buff has less than or 0 seconds left, then remove it. From 1315c2455f3d9316e39b260f67a35519efc61350 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 12 Oct 2016 15:08:28 +0200 Subject: [PATCH 24/63] Removed old TODOs in BuffService --- src/services/commands/BuffService.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 7bca808f5..6702b97f0 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -53,8 +53,6 @@ public class BuffService extends Service { // TODO buff slots - // TODO skillmod divisors in SkillmodService - // TODO remove buffs on respec. Listen for respec intent and remove buffs with // BuffData that has REMOVE_ON_RESPEC = 1 @@ -63,10 +61,6 @@ public class BuffService extends Service { // is it possible to somehow determine if a buff is a group buff? // TODO decay buffs on deathblow - // TODO debuffs vs buffs - - // TODO test buff stacks - // private static final byte GROUP_BUFF_RANGE = 100; private final ScheduledExecutorService executor; @@ -94,7 +88,7 @@ public class BuffService extends Service { @Override public boolean start() { // Polls buff queues every second - executor.scheduleAtFixedRate(() -> buffRemoval.values().forEach(buffQueue -> checkBuffQueue(buffQueue)), 0, 1, TimeUnit.SECONDS); + executor.scheduleAtFixedRate(() -> buffRemoval.values().forEach(buffQueue -> checkBuffQueue(buffQueue)), 1, 1, TimeUnit.SECONDS); return super.start(); } From 8b0cb181df109aa09f846b7ae0b431b667761be7 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 13 Oct 2016 03:31:36 +0200 Subject: [PATCH 25/63] Made a couple of inner classes of BuffService static --- src/services/commands/BuffService.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 6702b97f0..d4025cee9 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -184,7 +184,6 @@ public class BuffService extends Service { sendSkillModIntent(buffData, receiver, false); receiver.addBuff(buffCrc, buff); - manageBuff(buff, buffCrc, receiver); String effectFileName = buffData.getEffectFileName(); @@ -203,7 +202,6 @@ public class BuffService extends Service { if(buffQueue == null) { buffQueue = new DelayQueue<>(); - buffRemoval.put(creature, buffQueue); } @@ -278,7 +276,7 @@ public class BuffService extends Service { target.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, hardPoint, target.getObjectId())); } - private class BuffDelayed implements Delayed { + private static class BuffDelayed implements Delayed { private final Buff buff; private final CRC buffCrc; @@ -325,7 +323,7 @@ public class BuffService extends Service { * With many {@code Buff} instances in play, this will result in a noticeable * memory usage reduction. */ - private class BuffData { + private static class BuffData { private final int maxStackCount; private final String effect1Name; private final float effect1Value; From a5986301099572a769bcbd785c13833148a52988 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 17 Oct 2016 20:09:30 +0200 Subject: [PATCH 26/63] Rewrote a couple of TODOs in BuffService --- src/services/commands/BuffService.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index d4025cee9..e5ecb7fca 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -51,18 +51,11 @@ import utilities.ThreadUtilities; public class BuffService extends Service { - // TODO buff slots - - // TODO remove buffs on respec. Listen for respec intent and remove buffs with - // BuffData that has REMOVE_ON_RESPEC = 1 - - // TODO group buffs - // TODO remove group buff(s) from receiver when distance between receiver and caster is 100m - // is it possible to somehow determine if a buff is a group buff? + // TODO buff slots A, B, C and D + // TODO remove buffs on respec. Listen for respec event and remove buffs with BuffData 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 // TODO decay buffs on deathblow -// private static final byte GROUP_BUFF_RANGE = 100; - private final ScheduledExecutorService executor; private final Map> buffRemoval; private final Map dataMap; @@ -179,7 +172,7 @@ public class BuffService extends Service { // TODO stack counts upon add/remove probably need to be defined on a per-buff basis due to skillmod influence. int stackCount = 1; - int buffDuration = (int) buffData.getDefaultDuration(); + int buffDuration = (int) buffData.getDefaultDuration(); Buff buff = new Buff(receiver.getPlayerObject().getPlayTime() + buffDuration, buffData.getEffect1Value(), buffDuration, buffer.getObjectId(), stackCount); sendSkillModIntent(buffData, receiver, false); @@ -213,7 +206,7 @@ public class BuffService extends Service { BuffData buffData = dataMap.get(buffCrc); if(buffData == null) { - Log.e(this, "Could not remove %s from %s - buff data for it does not exist", creature, buffCrc); + Log.e(this, "Could not remove %s from %s - buff data for it does not exist", buffCrc, creature); return; } @@ -234,7 +227,6 @@ public class BuffService extends Service { // Remove the buff from the creature creature.removeBuff(buffCrc); - // Check if the callback is a buff String callback = buffData.getCallback(); if(callback.isEmpty()) From 48cb3ebde05cfb0d2bceb045e276e8a4c9cece0b Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 18 Oct 2016 14:50:58 +0200 Subject: [PATCH 27/63] Added incapacitation and cloning sickness debuffs --- src/services/combat/CombatManager.java | 5 +++-- src/services/combat/CorpseService.java | 3 ++- src/services/commands/BuffService.java | 22 +++++++++++++--------- 3 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java index ba25ef5f0..d75088ade 100644 --- a/src/services/combat/CombatManager.java +++ b/src/services/combat/CombatManager.java @@ -27,6 +27,7 @@ ***********************************************************************************/ package services.combat; +import intents.BuffIntent; import intents.chat.ChatBroadcastIntent; import java.awt.Color; import java.util.HashMap; @@ -356,6 +357,7 @@ public class CombatManager extends Manager { incapacitatedCreatures.put(incapacitatedPlayer, executor.schedule(() -> expireIncapacitation(incapacitatedPlayer), incapacitationCounter, TimeUnit.SECONDS)); } + new BuffIntent("incapWeaken", incapacitator, incapacitatedPlayer, false).broadcast(); new ChatBroadcastIntent(incapacitator.getOwner(), new ProsePackage(new StringId("base_player", "prose_target_incap"), "TT", incapacitatedPlayer.getName())).broadcast(); new ChatBroadcastIntent(incapacitatedPlayer.getOwner(), new ProsePackage(new StringId("base_player", "prose_victim_incap"), "TT", incapacitator.getName())).broadcast(); } @@ -392,7 +394,7 @@ public class CombatManager extends Manager { private void killCreature(CreatureObject killer, CreatureObject corpse) { corpse.setPosture(Posture.DEAD); - Log.i(this, "%s was killed", corpse); + Log.i(this, "%s was killed by %s", corpse, killer); new CreatureKilledIntent(killer, corpse).broadcast(); } @@ -422,7 +424,6 @@ public class CombatManager extends Manager { if (incapacitationTimer != null) { if (incapacitationTimer.cancel(false)) { // If the task is running, let them get back up killCreature(killer, corpse); - Log.i(this, "%s was deathblown by %s", corpse, killer); } } else { // Can't happen with the current code, but in case it's ever refactored... diff --git a/src/services/combat/CorpseService.java b/src/services/combat/CorpseService.java index 546adc1e4..c7660dc27 100644 --- a/src/services/combat/CorpseService.java +++ b/src/services/combat/CorpseService.java @@ -27,6 +27,7 @@ ***********************************************************************************/ package services.combat; +import intents.BuffIntent; import intents.FactionIntent; import intents.PlayerEventIntent; import intents.chat.ChatBroadcastIntent; @@ -367,7 +368,7 @@ public final class CorpseService extends Service { corpse.setTurnScale(1); corpse.setMovementScale(1); corpse.setHealth(corpse.getMaxHealth()); - // TODO NGE: cloning debuff + new BuffIntent("incapWeaken", corpse, corpse, true).broadcastAfterIntent(new BuffIntent("cloning_sickness", corpse, corpse, false)); } /** diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index e5ecb7fca..d2ab38af4 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -54,7 +54,7 @@ public class BuffService extends Service { // TODO buff slots A, B, C and D // TODO remove buffs on respec. Listen for respec event and remove buffs with BuffData 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 - // TODO decay buffs on deathblow + // TODO decay buffs on deathblow buffs with BuffData where DECAY_ON_PVP_DEATH == 1 private final ScheduledExecutorService executor; private final Map> buffRemoval; @@ -155,7 +155,9 @@ public class BuffService extends Service { } private void handleDisappear(CreatureObject creature) { - buffRemoval.remove(creature); + synchronized(buffRemoval) { + buffRemoval.remove(creature); + } } private void addBuff(CRC buffCrc, CreatureObject receiver, CreatureObject buffer) { @@ -191,14 +193,16 @@ public class BuffService extends Service { removeBuff(creature, buffCrc, true); } else if(buff.getDuration() >= 0) { // If this buff doesn't last forever or hasn't expired, we'll schedule it for removal in the future - DelayQueue buffQueue = buffRemoval.get(creature); - - if(buffQueue == null) { - buffQueue = new DelayQueue<>(); - buffRemoval.put(creature, buffQueue); + synchronized (buffRemoval) { + DelayQueue buffQueue = buffRemoval.get(creature); + + if(buffQueue == null) { + buffQueue = new DelayQueue<>(); + buffRemoval.put(creature, buffQueue); + } + + buffQueue.add(new BuffDelayed(buff, buffCrc, creature)); } - - buffQueue.add(new BuffDelayed(buff, buffCrc, creature)); } } From 1a53b1c1f85eaf43cf24559a3d649b1d2733d624 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 18 Oct 2016 15:54:31 +0200 Subject: [PATCH 28/63] Incapacitation timer is now 10 seconds instead of 15 --- src/services/combat/CombatManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java index d75088ade..fe23ca491 100644 --- a/src/services/combat/CombatManager.java +++ b/src/services/combat/CombatManager.java @@ -346,7 +346,7 @@ public class CombatManager extends Manager { } private void incapacitatePlayer(CreatureObject incapacitator, CreatureObject incapacitatedPlayer) { - int incapacitationCounter = 15; + int incapacitationCounter = 10; incapacitatedPlayer.setPosture(Posture.INCAPACITATED); incapacitatedPlayer.setCounter(incapacitationCounter); From 7b64bbb7a213f74e172d13ff7de0e999d5cc8cd0 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 18 Oct 2016 15:55:07 +0200 Subject: [PATCH 29/63] Moved a couple of method bodies from CreatureObject into CREO06 --- .../objects/creature/CreatureObject.java | 27 ++--------- .../creature/CreatureObjectSharedNP.java | 47 ++++++++++++++++++- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 105cdc39d..395a02091 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -595,40 +595,21 @@ public class CreatureObject extends TangibleObject { } public void addBuff(CRC buffCrc, Buff buff) { - SWGMap buffs = creo6.getBuffs(); - if(!buffs.containsKey(buffCrc)) { - buffs.put(buffCrc, buff); - buffs.sendDeltaMessage(this); - } + creo6.addBuff(buffCrc, buff, this); } public void removeBuff(CRC buffCrc) { - SWGMap buffs = creo6.getBuffs(); - // If a value was associated with the key, then send a delta. - if(buffs.containsKey(buffCrc)) { - buffs.remove(buffCrc); - buffs.sendDeltaMessage(this); - } + creo6.removeBuff(buffCrc, this); } public Buff getBuffByCrc(CRC buffCrc) { - return creo6.getBuffs().get(buffCrc); + return creo6.getBuffByCrc(buffCrc); } public void adjustBuffStackCount(CRC buffCrc, int adjustment) { - SWGMap buffs = creo6.getBuffs(); - Buff buff = buffs.get(buffCrc); - buff.adjustStackCount(adjustment); // Adjust the stack count - // TODO reset time remaining? - buffs.update(buffCrc, this); // Send deltas for this key. + creo6.adjustBuffStackCount(buffCrc, adjustment, this); } - /** - * @return a copy of the buffs map. Removing and adding entries in this - * map will not affect the internal {@code SWGMap}. Do not edit the - * {@code Buff} values in the belief that deltas will be sent because - * they won't - this is incorrect usage. - */ public Map getBuffs() { return new HashMap<>(creo6.getBuffs()); } diff --git a/src/resources/objects/creature/CreatureObjectSharedNP.java b/src/resources/objects/creature/CreatureObjectSharedNP.java index bd960b7f5..8ef5405ac 100644 --- a/src/resources/objects/creature/CreatureObjectSharedNP.java +++ b/src/resources/objects/creature/CreatureObjectSharedNP.java @@ -27,6 +27,8 @@ ***********************************************************************************/ package resources.objects.creature; +import java.util.HashMap; +import java.util.Map; import resources.HologramColour; import resources.collections.SWGList; import resources.collections.SWGMap; @@ -456,8 +458,49 @@ class CreatureObjectSharedNP implements Persistable { } } - public SWGMap getBuffs() { - return buffs; + /** + * @return a copy of the buffs map. Removing and adding entries in this + * map will not affect the internal {@code SWGMap}. Do not edit the + * {@code Buff} values in the belief that deltas will be sent because + * they won't - this is incorrect usage. + */ + public Map getBuffs() { + synchronized (buffs) { + return new HashMap<>(buffs); + } + } + + public void addBuff(CRC buffCrc, Buff buff, SWGObject target) { + synchronized (buffs) { + if(!buffs.containsKey(buffCrc)) { + buffs.put(buffCrc, buff); + buffs.sendDeltaMessage(target); + } + } + } + + public void removeBuff(CRC buffCrc, SWGObject target) { + synchronized (buffs) { + if(buffs.containsKey(buffCrc)) { + buffs.remove(buffCrc); + buffs.sendDeltaMessage(target); + } + } + } + + public Buff getBuffByCrc(CRC buffCrc) { + synchronized (buffs) { + return buffs.get(buffCrc); + } + } + + public void adjustBuffStackCount(CRC buffCrc, int adjustment, SWGObject target) { + synchronized (buffs) { + Buff buff = buffs.get(buffCrc); + buff.adjustStackCount(adjustment); // Adjust the stack count + // TODO reset time remaining + buffs.update(buffCrc, target); // Send deltas for this key. + } } private void initMaxAttributes() { From 47bc9d4e9a3e73ea0f23979b9e2037bd3c7b0b32 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 19 Oct 2016 10:18:57 +0200 Subject: [PATCH 30/63] Being incapacitated while having the weakened debuff now results in instant death --- src/services/combat/CombatManager.java | 44 ++++++++++++++------------ src/services/combat/CorpseService.java | 5 ++- src/services/commands/BuffService.java | 13 +++++--- 3 files changed, 36 insertions(+), 26 deletions(-) diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java index fe23ca491..416875d0c 100644 --- a/src/services/combat/CombatManager.java +++ b/src/services/combat/CombatManager.java @@ -72,6 +72,8 @@ import utilities.ThreadUtilities; public class CombatManager extends Manager { + private static final byte INCAP_TIMER = 10; // Amount of seconds to be incapacitated + private final Map inCombat; private final Set regeneratingHealthCreatures; // Only allowed outside of combat private final Set regeneratingActionCreatures; // Always allowed @@ -318,9 +320,9 @@ public class CombatManager extends Manager { } } - private void doCreatureDeath(CreatureObject killedCreature, CreatureObject killer) { - killedCreature.setHealth(0); - killer.removeDefender(killedCreature); + private void doCreatureDeath(CreatureObject corpse, CreatureObject killer) { + corpse.setHealth(0); + killer.removeDefender(corpse); // Let's check if the killer needs to remain in-combat... if(!killer.hasDefenders()) { @@ -329,37 +331,39 @@ public class CombatManager extends Manager { } // The creature should not be able to move or turn. - killedCreature.setTurnScale(0); - killedCreature.setMovementScale(0); + corpse.setTurnScale(0); + corpse.setMovementScale(0); // We need to handle this differently, depending on whether killedCreature is a player or not - if(killedCreature.isPlayer()) { - // TODO account for AI deathblowing players..? - // If it's a player, they need to be incapacitated - incapacitatePlayer(killer, killedCreature); + if(corpse.isPlayer()) { + if (corpse.getBuffByCrc(new CRC("incapweaken")) != null) { + // Kill this sucka immediately + killCreature(killer, corpse); + } else { + incapacitatePlayer(killer, corpse); + } } else { // This is just a plain ol' NPC. Die! - killCreature(killer, killedCreature); + killCreature(killer, corpse); } - exitCombat(killedCreature); + exitCombat(corpse); } - private void incapacitatePlayer(CreatureObject incapacitator, CreatureObject incapacitatedPlayer) { - int incapacitationCounter = 10; - incapacitatedPlayer.setPosture(Posture.INCAPACITATED); - incapacitatedPlayer.setCounter(incapacitationCounter); + private void incapacitatePlayer(CreatureObject incapacitator, CreatureObject incapacitated) { + incapacitated.setPosture(Posture.INCAPACITATED); + incapacitated.setCounter(INCAP_TIMER); - Log.i(this, "%s was incapacitated", incapacitatedPlayer); + Log.i(this, "%s was incapacitated", incapacitated); // Once the incapacitation counter expires, revive them. synchronized(incapacitatedCreatures) { - incapacitatedCreatures.put(incapacitatedPlayer, executor.schedule(() -> expireIncapacitation(incapacitatedPlayer), incapacitationCounter, TimeUnit.SECONDS)); + incapacitatedCreatures.put(incapacitated, executor.schedule(() -> expireIncapacitation(incapacitated), INCAP_TIMER, TimeUnit.SECONDS)); } - new BuffIntent("incapWeaken", incapacitator, incapacitatedPlayer, false).broadcast(); - new ChatBroadcastIntent(incapacitator.getOwner(), new ProsePackage(new StringId("base_player", "prose_target_incap"), "TT", incapacitatedPlayer.getName())).broadcast(); - new ChatBroadcastIntent(incapacitatedPlayer.getOwner(), new ProsePackage(new StringId("base_player", "prose_victim_incap"), "TT", incapacitator.getName())).broadcast(); + new BuffIntent("incapWeaken", incapacitator, incapacitated, false).broadcast(); + new ChatBroadcastIntent(incapacitator.getOwner(), new ProsePackage(new StringId("base_player", "prose_target_incap"), "TT", incapacitated.getName())).broadcast(); + new ChatBroadcastIntent(incapacitated.getOwner(), new ProsePackage(new StringId("base_player", "prose_victim_incap"), "TT", incapacitator.getName())).broadcast(); } private void expireIncapacitation(CreatureObject incapacitatedPlayer) { diff --git a/src/services/combat/CorpseService.java b/src/services/combat/CorpseService.java index c7660dc27..95d81af31 100644 --- a/src/services/combat/CorpseService.java +++ b/src/services/combat/CorpseService.java @@ -368,7 +368,10 @@ public final class CorpseService extends Service { corpse.setTurnScale(1); corpse.setMovementScale(1); corpse.setHealth(corpse.getMaxHealth()); - new BuffIntent("incapWeaken", corpse, corpse, true).broadcastAfterIntent(new BuffIntent("cloning_sickness", corpse, corpse, false)); + + BuffIntent cloningSickness = new BuffIntent("cloning_sickness", corpse, corpse, false); + new BuffIntent("incapweaken", corpse, corpse, true).broadcastAfterIntent(cloningSickness); + cloningSickness.broadcast(); } /** diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index d2ab38af4..37b0e0668 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -37,6 +37,7 @@ import java.util.concurrent.TimeUnit; import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; +import java.util.Locale; import java.util.concurrent.ScheduledExecutorService; import network.packets.swg.zone.PlayClientEffectObjectMessage; import resources.client_info.ClientFactory; @@ -58,7 +59,7 @@ public class BuffService extends Service { private final ScheduledExecutorService executor; private final Map> buffRemoval; - private final Map dataMap; + private final Map dataMap; // All CRCs are lower-cased buff names! public BuffService() { registerForIntent(BuffIntent.TYPE); @@ -113,7 +114,7 @@ public class BuffService extends Service { DatatableData buffTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/buff/buff.iff"); for(int row = 0; row < buffTable.getRowCount(); row++) { - dataMap.put(new CRC((String) buffTable.getCell(row, 0)), new BuffData( + dataMap.put(new CRC(((String) buffTable.getCell(row, 0)).toLowerCase(Locale.ENGLISH)), new BuffData( (int) buffTable.getCell(row, 28), // max stacks (String) buffTable.getCell(row, 7), // effect1 (float) buffTable.getCell(row, 8), // value1 @@ -134,10 +135,12 @@ public class BuffService extends Service { } private void handleBuffIntent(BuffIntent i) { + CRC buffCrc = new CRC(i.getBuffName().toLowerCase(Locale.ENGLISH)); + if (i.isRemove()) { - removeBuff(i.getReceiver(), new CRC(i.getBuffName()), false); + removeBuff(i.getReceiver(), buffCrc, false); } else { - addBuff(new CRC(i.getBuffName()), i.getReceiver(), i.getBuffer()); + addBuff(buffCrc, i.getReceiver(), i.getBuffer()); } } @@ -164,7 +167,7 @@ public class BuffService extends Service { BuffData buffData = dataMap.get(buffCrc); if(buffData == null) { - Log.e(this, "Could not add %s to %s - buff data for it does not exist", receiver, buffCrc); + Log.e(this, "Could not add %s to %s - buff data for it does not exist", buffCrc, receiver); return; } From 0a3ebe53a5cfb2df979e1bb955e7d57b6cf76f84 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 19 Oct 2016 10:50:48 +0200 Subject: [PATCH 31/63] Fixed callback buffs for buffs --- src/network/PacketType.java | 1 - src/resources/objects/creature/CreatureObject.java | 8 +++++--- src/resources/objects/player/PlayerObject.java | 6 +----- src/services/combat/CombatManager.java | 2 +- src/services/commands/BuffService.java | 8 ++++---- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/src/network/PacketType.java b/src/network/PacketType.java index 5399ce029..781655060 100644 --- a/src/network/PacketType.java +++ b/src/network/PacketType.java @@ -27,7 +27,6 @@ ***********************************************************************************/ package network; -import network.packets.swg.zone.StopClientEffectObjectByLabelMessage; import network.packets.swg.*; import network.packets.swg.holo.HoloConnectionStarted; import network.packets.swg.holo.HoloConnectionStopped; diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 395a02091..783eabc41 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -33,6 +33,7 @@ import java.util.Set; import java.io.IOException; import java.io.ObjectInputStream; import java.util.HashMap; +import java.util.Locale; import java.util.Map; import network.packets.swg.zone.UpdatePostureMessage; @@ -62,9 +63,6 @@ import utilities.Encoder.StringType; public class CreatureObject extends TangibleObject { - private static final long serialVersionUID = 3L; - - private transient GroupInviterData inviterData = new GroupInviterData(0, null, "", 0); private transient long lastReserveOperation = 0; private final CreatureObjectClientServerNP creo4 = new CreatureObjectClientServerNP(); @@ -602,6 +600,10 @@ public class CreatureObject extends TangibleObject { creo6.removeBuff(buffCrc, this); } + public boolean hasBuff(String buffName) { + return getBuffByCrc(new CRC(buffName.toLowerCase(Locale.ENGLISH))) != null; + } + public Buff getBuffByCrc(CRC buffCrc) { return creo6.getBuffByCrc(buffCrc); } diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index 2a123a1cd..c67f5f159 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -90,10 +90,6 @@ public class PlayerObject extends IntangibleObject { sendDelta(3, 7, title, StringType.ASCII); } - /** - * Calling this method will update the playTime field. - * @return the amount of time this PlayerObject has played. - */ public int getPlayTime() { return play3.getPlayTime(); } @@ -103,7 +99,7 @@ public class PlayerObject extends IntangibleObject { play3.setPlayTime(playTime); sendDelta(3, 9, playTime); } - + public int getGcwPoints() { return play3.getGcwPoints(); } diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java index 416875d0c..29b73ae40 100644 --- a/src/services/combat/CombatManager.java +++ b/src/services/combat/CombatManager.java @@ -336,7 +336,7 @@ public class CombatManager extends Manager { // We need to handle this differently, depending on whether killedCreature is a player or not if(corpse.isPlayer()) { - if (corpse.getBuffByCrc(new CRC("incapweaken")) != null) { + if (corpse.hasBuff("incapWeaken")) { // Kill this sucka immediately killCreature(killer, corpse); } else { diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 37b0e0668..85fc90597 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -52,10 +52,10 @@ import utilities.ThreadUtilities; public class BuffService extends Service { - // TODO buff slots A, B, C and D + // TODO buff groups and prioritisation // TODO remove buffs on respec. Listen for respec event and remove buffs with BuffData 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 - // TODO decay buffs on deathblow buffs with BuffData where DECAY_ON_PVP_DEATH == 1 + // TODO on deathblow, decay buffs with BuffData where DECAY_ON_PVP_DEATH == 1 private final ScheduledExecutorService executor; private final Map> buffRemoval; @@ -236,10 +236,10 @@ public class BuffService extends Service { String callback = buffData.getCallback(); - if(callback.isEmpty()) + if(callback.equals("none")) return; - CRC callbackCrc = new CRC(callback); + CRC callbackCrc = new CRC(callback.toLowerCase(Locale.ENGLISH)); if(dataMap.containsKey(callbackCrc)) { // Apply the callback buff addBuff(callbackCrc, creature, creature); From e302dcf839e395a8fc5e164446bf980f202573dc Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 19 Oct 2016 11:02:31 +0200 Subject: [PATCH 32/63] Improved logging in BuffService slightly --- src/services/commands/BuffService.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 85fc90597..4ae265e4c 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -72,10 +72,10 @@ public class BuffService extends Service { @Override public boolean initialize() { - long startTime = System.currentTimeMillis(); + long startTime = System.nanoTime(); Log.i(this, "Loading buffs..."); loadBuffs(); - Log.i(this, "Finished loading buffs in %dms", System.currentTimeMillis() - startTime); + Log.i(this, "Finished loading %d buffs in %fms", dataMap.size(), (System.nanoTime() - startTime) / 1E6); return super.initialize(); } @@ -187,7 +187,7 @@ public class BuffService extends Service { String effectFileName = buffData.getEffectFileName(); if(!effectFileName.isEmpty()) - sendClientEffectMessage(receiver, effectFileName, buffData.getParticleHardPoint()); + receiver.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, buffData.getParticleHardPoint(), receiver.getObjectId())); } private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { @@ -271,10 +271,6 @@ public class BuffService extends Service { new SkillModIntent(effect5Name, 0, (int) buffData.getEffect5Value() * valueFactor, creature).broadcast(); } - private void sendClientEffectMessage(CreatureObject target, String effectFileName, String hardPoint) { - target.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, hardPoint, target.getObjectId())); - } - private static class BuffDelayed implements Delayed { private final Buff buff; From 10ab9c327d09b0b3f6bc7bf7b8cd17b583d66604 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 19 Oct 2016 12:16:05 +0200 Subject: [PATCH 33/63] Implemented buff group prioritisation --- src/services/commands/BuffService.java | 108 ++++++++++++++++++------- 1 file changed, 81 insertions(+), 27 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 4ae265e4c..6a686a13b 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -39,6 +39,7 @@ import intents.PlayerEventIntent; import intents.SkillModIntent; import java.util.Locale; import java.util.concurrent.ScheduledExecutorService; +import java.util.stream.Stream; import network.packets.swg.zone.PlayClientEffectObjectMessage; import resources.client_info.ClientFactory; import resources.client_info.visitors.DatatableData; @@ -52,7 +53,7 @@ import utilities.ThreadUtilities; public class BuffService extends Service { - // TODO buff groups and prioritisation + // TODO allow removal of non-debuffs by right-clicking them // TODO remove buffs on respec. Listen for respec event and remove buffs with BuffData 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 // TODO on deathblow, decay buffs with BuffData where DECAY_ON_PVP_DEATH == 1 @@ -113,23 +114,43 @@ public class BuffService extends Service { private void loadBuffs() { DatatableData buffTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/buff/buff.iff"); + int group1 = buffTable.getColumnFromName("group1"); + int priority = buffTable.getColumnFromName("priority"); + int maxStacks = buffTable.getColumnFromName("max_stacks"); + int effect1Param = buffTable.getColumnFromName("effect1_param"); + int effect1Value = buffTable.getColumnFromName("effect1_value"); + int effect2Param = buffTable.getColumnFromName("effect2_param"); + int effect2Value = buffTable.getColumnFromName("effect2_value"); + int effect3Param = buffTable.getColumnFromName("effect3_param"); + int effect3Value = buffTable.getColumnFromName("effect3_value"); + int effect4Param = buffTable.getColumnFromName("effect4_param"); + int effect4Value = buffTable.getColumnFromName("effect4_value"); + int effect5Param = buffTable.getColumnFromName("effect5_param"); + int effect5Value = buffTable.getColumnFromName("effect5_value"); + int duration = buffTable.getColumnFromName("duration"); + int particle = buffTable.getColumnFromName("particle"); + int particleHardpoint = buffTable.getColumnFromName("particle_hardpoint"); + int callback = buffTable.getColumnFromName("callback"); + for(int row = 0; row < buffTable.getRowCount(); row++) { dataMap.put(new CRC(((String) buffTable.getCell(row, 0)).toLowerCase(Locale.ENGLISH)), new BuffData( - (int) buffTable.getCell(row, 28), // max stacks - (String) buffTable.getCell(row, 7), // effect1 - (float) buffTable.getCell(row, 8), // value1 - (String) buffTable.getCell(row, 9), // effect2 - (float) buffTable.getCell(row, 10), // value2 - (String) buffTable.getCell(row, 11), // effect3 - (float) buffTable.getCell(row, 12), // value3 - (String) buffTable.getCell(row, 13), // effect4 - (float) buffTable.getCell(row, 14), // value4 - (String) buffTable.getCell(row, 15), // effect5 - (float) buffTable.getCell(row, 16), // value5 - (float) buffTable.getCell(row, 6), // default duration - (String) buffTable.getCell(row, 19), // particle effect - (String) buffTable.getCell(row, 20), // particle hardpoint - (String) buffTable.getCell(row, 18) // Callback + (String) buffTable.getCell(row, group1), + (int) buffTable.getCell(row, priority), + (int) buffTable.getCell(row, maxStacks), + (String) buffTable.getCell(row, effect1Param), + (float) buffTable.getCell(row, effect1Value), + (String) buffTable.getCell(row, effect2Param), + (float) buffTable.getCell(row, effect2Value), + (String) buffTable.getCell(row, effect3Param), + (float) buffTable.getCell(row, effect3Value), + (String) buffTable.getCell(row, effect4Param), + (float) buffTable.getCell(row, effect4Value), + (String) buffTable.getCell(row, effect5Param), + (float) buffTable.getCell(row, effect5Value), + (float) buffTable.getCell(row, duration), + (String) buffTable.getCell(row, particle), + (String) buffTable.getCell(row, particleHardpoint), + (String) buffTable.getCell(row, callback) )); } } @@ -166,11 +187,25 @@ public class BuffService extends Service { private void addBuff(CRC buffCrc, CreatureObject receiver, CreatureObject buffer) { BuffData buffData = dataMap.get(buffCrc); - if(buffData == null) { + if (buffData == null) { Log.e(this, "Could not add %s to %s - buff data for it does not exist", buffCrc, receiver); return; } + String groupName = buffData.getGroupName(); + + if (!groupName.isEmpty()) { + // Let's see if they have any buffs in this group already + Stream buffCrcStream = receiver.getBuffs().keySet().stream().filter(candidate -> checkGroup(groupName, candidate, buffData)); + + // If not, let's just stop here. No reason to increase network traffic with effects and deltas. + if(buffCrcStream.count() <= 0) { + return; + } + + buffCrcStream.forEach(crc -> removeBuff(receiver, crc, false)); + } + // The client-side timer hinges on the playTime of the PlayerObject. // We therefore must update it to current time, so the timer starts from full duration receiver.getPlayerObject().updatePlayTime(); @@ -186,12 +221,18 @@ public class BuffService extends Service { String effectFileName = buffData.getEffectFileName(); - if(!effectFileName.isEmpty()) + if (!effectFileName.isEmpty()) receiver.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, buffData.getParticleHardPoint(), receiver.getObjectId())); } + private boolean checkGroup(String groupName, CRC candidate, BuffData existingBuffData) { + BuffData candidateData = dataMap.get(candidate); + + return candidateData.getGroupName().equals(groupName) && candidateData.getGroupPriority() >= existingBuffData.getGroupPriority(); + } + private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { - if(buff.getEndTime() <= 0) { + if (buff.getEndTime() <= 0) { // If this buff has less than or 0 seconds left, then remove it. removeBuff(creature, buffCrc, true); } else if(buff.getDuration() >= 0) { @@ -212,7 +253,7 @@ public class BuffService extends Service { private void removeBuff(CreatureObject creature, CRC buffCrc, boolean expired) { BuffData buffData = dataMap.get(buffCrc); - if(buffData == null) { + if (buffData == null) { Log.e(this, "Could not remove %s from %s - buff data for it does not exist", buffCrc, creature); return; } @@ -220,7 +261,7 @@ public class BuffService extends Service { Buff buff = creature.getBuffByCrc(buffCrc); // Check if this buff can be stacked - if(buffData.getMaxStackCount() > 1 && !expired) { + if (buffData.getMaxStackCount() > 1 && !expired) { // Check if this buff has been stacked if(buff.getStackCount() > 1) { // If it has, reduce the stack count and reset the duration. @@ -259,15 +300,15 @@ public class BuffService extends Service { int valueFactor = remove ? -1 : 1; - if(!effect1Name.isEmpty()) + if (!effect1Name.isEmpty()) new SkillModIntent(effect1Name, 0, (int) buffData.getEffect1Value() * valueFactor, creature).broadcast(); - if(!effect2Name.isEmpty()) + if (!effect2Name.isEmpty()) new SkillModIntent(effect2Name, 0, (int) buffData.getEffect2Value() * valueFactor, creature).broadcast(); - if(!effect3Name.isEmpty()) + if (!effect3Name.isEmpty()) new SkillModIntent(effect3Name, 0, (int) buffData.getEffect3Value() * valueFactor, creature).broadcast(); - if(!effect4Name.isEmpty()) + if (!effect4Name.isEmpty()) new SkillModIntent(effect4Name, 0, (int) buffData.getEffect4Value() * valueFactor, creature).broadcast(); - if(!effect5Name.isEmpty()) + if (!effect5Name.isEmpty()) new SkillModIntent(effect5Name, 0, (int) buffData.getEffect5Value() * valueFactor, creature).broadcast(); } @@ -319,6 +360,9 @@ public class BuffService extends Service { * memory usage reduction. */ private static class BuffData { + + private final String groupName; + private final int groupPriority; private final int maxStackCount; private final String effect1Name; private final float effect1Value; @@ -335,7 +379,9 @@ public class BuffService extends Service { private final String particleHardPoint; private final String callback; - private BuffData(int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String callback) { + private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String callback) { + this.groupName = groupName; + this.groupPriority = groupPriority; this.maxStackCount = maxStackCount; this.effect1Name = effect1Name; this.effect1Value = effect1Value; @@ -353,6 +399,14 @@ public class BuffService extends Service { this.callback = callback; } + public String getGroupName() { + return groupName; + } + + public int getGroupPriority() { + return groupPriority; + } + private int getMaxStackCount() { return maxStackCount; } From ff58554f540bd7d542a3e25507a49b9f1e4f7927 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 19 Oct 2016 12:23:00 +0200 Subject: [PATCH 34/63] Fixed incorrect column names being used for buff.iff --- src/services/commands/BuffService.java | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 6a686a13b..cea73d653 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -114,23 +114,23 @@ public class BuffService extends Service { private void loadBuffs() { DatatableData buffTable = (DatatableData) ClientFactory.getInfoFromFile("datatables/buff/buff.iff"); - int group1 = buffTable.getColumnFromName("group1"); - int priority = buffTable.getColumnFromName("priority"); - int maxStacks = buffTable.getColumnFromName("max_stacks"); - int effect1Param = buffTable.getColumnFromName("effect1_param"); - int effect1Value = buffTable.getColumnFromName("effect1_value"); - int effect2Param = buffTable.getColumnFromName("effect2_param"); - int effect2Value = buffTable.getColumnFromName("effect2_value"); - int effect3Param = buffTable.getColumnFromName("effect3_param"); - int effect3Value = buffTable.getColumnFromName("effect3_value"); - int effect4Param = buffTable.getColumnFromName("effect4_param"); - int effect4Value = buffTable.getColumnFromName("effect4_value"); - int effect5Param = buffTable.getColumnFromName("effect5_param"); - int effect5Value = buffTable.getColumnFromName("effect5_value"); - int duration = buffTable.getColumnFromName("duration"); - int particle = buffTable.getColumnFromName("particle"); - int particleHardpoint = buffTable.getColumnFromName("particle_hardpoint"); - int callback = buffTable.getColumnFromName("callback"); + int group1 = buffTable.getColumnFromName("GROUP1"); + int priority = buffTable.getColumnFromName("PRIORITY"); + int maxStacks = buffTable.getColumnFromName("MAX_STACKS"); + int effect1Param = buffTable.getColumnFromName("EFFECT1_PARAM"); + int effect1Value = buffTable.getColumnFromName("EFFECT1_VALUE"); + int effect2Param = buffTable.getColumnFromName("EFFECT2_PARAM"); + int effect2Value = buffTable.getColumnFromName("EFFECT2_VALUE"); + int effect3Param = buffTable.getColumnFromName("EFFECT3_PARAM"); + int effect3Value = buffTable.getColumnFromName("EFFECT3_VALUE"); + int effect4Param = buffTable.getColumnFromName("EFFECT4_PARAM"); + int effect4Value = buffTable.getColumnFromName("EFFECT4_VALUE"); + int effect5Param = buffTable.getColumnFromName("EFFECT5_PARAM"); + int effect5Value = buffTable.getColumnFromName("EFFECT5_VALUE"); + int duration = buffTable.getColumnFromName("DURATION"); + int particle = buffTable.getColumnFromName("PARTICLE"); + int particleHardpoint = buffTable.getColumnFromName("PARTICLE_HARDPOINT"); + int callback = buffTable.getColumnFromName("CALLBACK"); for(int row = 0; row < buffTable.getRowCount(); row++) { dataMap.put(new CRC(((String) buffTable.getCell(row, 0)).toLowerCase(Locale.ENGLISH)), new BuffData( From 8774e24b5ff1ed17716bcb4ac4e568da60e56eee Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 19 Oct 2016 12:32:54 +0200 Subject: [PATCH 35/63] Added more logging to BuffService --- src/services/commands/BuffService.java | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index cea73d653..c6c109850 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -193,19 +193,21 @@ public class BuffService extends Service { } String groupName = buffData.getGroupName(); + + // Let's see if they have any buffs in this group already + Stream buffsToRemove = receiver.getBuffs().keySet().stream().filter(candidate -> checkGroup(groupName, candidate, buffData)); + long groupBuffCount = buffsToRemove.count(); - if (!groupName.isEmpty()) { - // Let's see if they have any buffs in this group already - Stream buffCrcStream = receiver.getBuffs().keySet().stream().filter(candidate -> checkGroup(groupName, candidate, buffData)); - - // If not, let's just stop here. No reason to increase network traffic with effects and deltas. - if(buffCrcStream.count() <= 0) { - return; - } - - buffCrcStream.forEach(crc -> removeBuff(receiver, crc, false)); + // If not, let's just stop here + if (groupBuffCount <= 0) { + return; + } else if (groupBuffCount > 1) { + // Only one buff per group should be possible + Log.e(this, "%s had multiple buffs from the same group %s!", receiver, groupName); } - + + buffsToRemove.forEach(crc -> removeBuff(receiver, crc, false)); + // The client-side timer hinges on the playTime of the PlayerObject. // We therefore must update it to current time, so the timer starts from full duration receiver.getPlayerObject().updatePlayTime(); From 1538b904fbc8de37aaae573bd6b598d0bc3f7781 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sat, 22 Oct 2016 18:30:22 +0200 Subject: [PATCH 36/63] Added buff stacking support --- .../objects/creature/CreatureObject.java | 4 + .../creature/CreatureObjectSharedNP.java | 14 ++- src/services/commands/BuffService.java | 88 +++++++++---------- 3 files changed, 59 insertions(+), 47 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 783eabc41..379df623b 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -612,6 +612,10 @@ public class CreatureObject extends TangibleObject { creo6.adjustBuffStackCount(buffCrc, adjustment, this); } + public void setBuffDuration(CRC buffCrc, int playTime, int duration) { + creo6.setBuffDuration(buffCrc, playTime, duration, this); + } + public Map getBuffs() { return new HashMap<>(creo6.getBuffs()); } diff --git a/src/resources/objects/creature/CreatureObjectSharedNP.java b/src/resources/objects/creature/CreatureObjectSharedNP.java index 8ef5405ac..79dc982c8 100644 --- a/src/resources/objects/creature/CreatureObjectSharedNP.java +++ b/src/resources/objects/creature/CreatureObjectSharedNP.java @@ -497,9 +497,17 @@ class CreatureObjectSharedNP implements Persistable { public void adjustBuffStackCount(CRC buffCrc, int adjustment, SWGObject target) { synchronized (buffs) { Buff buff = buffs.get(buffCrc); - buff.adjustStackCount(adjustment); // Adjust the stack count - // TODO reset time remaining - buffs.update(buffCrc, target); // Send deltas for this key. + buff.adjustStackCount(adjustment); + buffs.update(buffCrc, target); + } + } + + public void setBuffDuration(CRC buffCrc, int playTime, int duration, SWGObject target) { + synchronized (buffs) { + Buff buff = buffs.get(buffCrc); + buff.setEndTime(playTime + duration); + buff.setDuration(duration); + buffs.update(buffCrc, target); } } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index c6c109850..ba8640b64 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -38,6 +38,7 @@ import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; import java.util.Locale; +import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; import java.util.stream.Stream; import network.packets.swg.zone.PlayClientEffectObjectMessage; @@ -184,53 +185,56 @@ public class BuffService extends Service { } } - private void addBuff(CRC buffCrc, CreatureObject receiver, CreatureObject buffer) { - BuffData buffData = dataMap.get(buffCrc); + private void addBuff(CRC newCrc, CreatureObject receiver, CreatureObject buffer) { + BuffData buffData = dataMap.get(newCrc); if (buffData == null) { - Log.e(this, "Could not add %s to %s - buff data for it does not exist", buffCrc, receiver); + Log.e(this, "Could not add %s to %s - buff data does not exist", newCrc, receiver); return; } String groupName = buffData.getGroupName(); - - // Let's see if they have any buffs in this group already - Stream buffsToRemove = receiver.getBuffs().keySet().stream().filter(candidate -> checkGroup(groupName, candidate, buffData)); - long groupBuffCount = buffsToRemove.count(); - - // If not, let's just stop here - if (groupBuffCount <= 0) { - return; - } else if (groupBuffCount > 1) { - // Only one buff per group should be possible - Log.e(this, "%s had multiple buffs from the same group %s!", receiver, groupName); - } - - buffsToRemove.forEach(crc -> removeBuff(receiver, crc, false)); - - // The client-side timer hinges on the playTime of the PlayerObject. - // We therefore must update it to current time, so the timer starts from full duration + Optional groupBuff = receiver.getBuffs().keySet().stream().filter(candidate -> groupName.equals(dataMap.get(candidate).groupName)).findFirst(); receiver.getPlayerObject().updatePlayTime(); + int playTime = receiver.getPlayerObject().getPlayTime(); - // TODO stack counts upon add/remove probably need to be defined on a per-buff basis due to skillmod influence. - int stackCount = 1; - int buffDuration = (int) buffData.getDefaultDuration(); - Buff buff = new Buff(receiver.getPlayerObject().getPlayTime() + buffDuration, buffData.getEffect1Value(), buffDuration, buffer.getObjectId(), stackCount); - - sendSkillModIntent(buffData, receiver, false); - receiver.addBuff(buffCrc, buff); - manageBuff(buff, buffCrc, receiver); - - String effectFileName = buffData.getEffectFileName(); - - if (!effectFileName.isEmpty()) - receiver.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, buffData.getParticleHardPoint(), receiver.getObjectId())); + if (groupBuff.isPresent()) { // They already have a buff of this group + CRC oldCrc = groupBuff.get(); + + if (oldCrc.equals(newCrc)) { + if (buffData.getMaxStackCount() > 1) { + // TODO buffs can, based on skillmods, adjust with different values + receiver.adjustBuffStackCount(newCrc, 1); + // TODO stackable buffs increase the skillmods they give by the amount of stacks + sendSkillModIntent(buffData, receiver, false); + } + + // We reset the duration + receiver.setBuffDuration(newCrc, playTime, (int) buffData.getDefaultDuration()); + } else if (dataMap.get(oldCrc).getGroupPriority() >= buffData.getGroupPriority()) { + removeBuff(receiver, newCrc, true); + applyBuff(receiver, buffer, buffData, playTime, newCrc); + } + } else { // They had no buff from this group already + applyBuff(receiver, buffer, buffData, playTime, newCrc); + } } - private boolean checkGroup(String groupName, CRC candidate, BuffData existingBuffData) { - BuffData candidateData = dataMap.get(candidate); - - return candidateData.getGroupName().equals(groupName) && candidateData.getGroupPriority() >= existingBuffData.getGroupPriority(); + private void applyBuff(CreatureObject receiver, CreatureObject buffer, BuffData buffData, int playTime, CRC crc) { + // 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(); + Buff buff = new Buff(playTime + buffDuration, buffData.getEffect1Value(), buffDuration, buffer.getObjectId(), stackCount); + + sendSkillModIntent(buffData, receiver, false); + receiver.addBuff(crc, buff); + manageBuff(buff, crc, receiver); + + String effectFileName = buffData.getEffectFileName(); + + if (!effectFileName.isEmpty()) { + receiver.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, buffData.getParticleHardPoint(), receiver.getObjectId())); + } } private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { @@ -263,13 +267,9 @@ public class BuffService extends Service { Buff buff = creature.getBuffByCrc(buffCrc); // Check if this buff can be stacked - if (buffData.getMaxStackCount() > 1 && !expired) { - // Check if this buff has been stacked - if(buff.getStackCount() > 1) { - // If it has, reduce the stack count and reset the duration. - // TODO NGE: buffs can, based on skillmods, adjust with different values - creature.adjustBuffStackCount(buffCrc, -1); - } + if (buffData.getMaxStackCount() > 1 && !expired && buff.getStackCount() > 1) { + // TODO NGE: buffs can, based on skillmods, adjust with different values + creature.adjustBuffStackCount(buffCrc, -1); } else { // Remove skillmods sendSkillModIntent(buffData, creature, true); From 4107d64cbc9b55f11d6a66d59b25f1c880a82c6e Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sun, 23 Oct 2016 03:12:59 +0200 Subject: [PATCH 37/63] Fixed an issue where stackable buffs could be stacked above their max --- .../objects/creature/CreatureObject.java | 7 +-- .../creature/CreatureObjectSharedNP.java | 13 +++- src/services/commands/BuffService.java | 61 +++++++++---------- 3 files changed, 42 insertions(+), 39 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 379df623b..237eb4310 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -30,8 +30,6 @@ package resources.objects.creature; import java.util.ArrayList; import java.util.Collection; import java.util.Set; -import java.io.IOException; -import java.io.ObjectInputStream; import java.util.HashMap; import java.util.Locale; import java.util.Map; @@ -46,7 +44,6 @@ import resources.PvpFlag; import resources.PvpStatus; import resources.Race; import resources.collections.SWGList; -import resources.collections.SWGMap; import resources.collections.SWGSet; import resources.common.CRC; import resources.network.BaselineBuilder; @@ -596,8 +593,8 @@ public class CreatureObject extends TangibleObject { creo6.addBuff(buffCrc, buff, this); } - public void removeBuff(CRC buffCrc) { - creo6.removeBuff(buffCrc, this); + public Buff removeBuff(CRC buffCrc) { + return creo6.removeBuff(buffCrc, this); } public boolean hasBuff(String buffName) { diff --git a/src/resources/objects/creature/CreatureObjectSharedNP.java b/src/resources/objects/creature/CreatureObjectSharedNP.java index 79dc982c8..38712d135 100644 --- a/src/resources/objects/creature/CreatureObjectSharedNP.java +++ b/src/resources/objects/creature/CreatureObjectSharedNP.java @@ -479,12 +479,19 @@ class CreatureObjectSharedNP implements Persistable { } } - public void removeBuff(CRC buffCrc, SWGObject target) { + public Buff removeBuff(CRC buffCrc, SWGObject target) { synchronized (buffs) { if(buffs.containsKey(buffCrc)) { - buffs.remove(buffCrc); - buffs.sendDeltaMessage(target); + Buff removedBuff = buffs.remove(buffCrc); + + if(removedBuff != null) { + buffs.sendDeltaMessage(target); + } + + return removedBuff; } + + return null; } } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index ba8640b64..392361b1d 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -40,7 +40,6 @@ import intents.SkillModIntent; import java.util.Locale; import java.util.Optional; import java.util.concurrent.ScheduledExecutorService; -import java.util.stream.Stream; import network.packets.swg.zone.PlayClientEffectObjectMessage; import resources.client_info.ClientFactory; import resources.client_info.visitors.DatatableData; @@ -202,11 +201,17 @@ public class BuffService extends Service { CRC oldCrc = groupBuff.get(); if (oldCrc.equals(newCrc)) { - if (buffData.getMaxStackCount() > 1) { + int maxStackCount = buffData.getMaxStackCount(); + + if (maxStackCount > 1) { // TODO buffs can, based on skillmods, adjust with different values - receiver.adjustBuffStackCount(newCrc, 1); - // TODO stackable buffs increase the skillmods they give by the amount of stacks - sendSkillModIntent(buffData, receiver, false); + int addedStacks = 1; + int currentStacks = receiver.getBuffByCrc(oldCrc).getStackCount(); + + if (addedStacks + currentStacks <= maxStackCount) { + receiver.adjustBuffStackCount(oldCrc, addedStacks); + checkSkillMods(buffData, receiver, addedStacks); + } } // We reset the duration @@ -226,7 +231,7 @@ public class BuffService extends Service { int buffDuration = (int) buffData.getDefaultDuration(); Buff buff = new Buff(playTime + buffDuration, buffData.getEffect1Value(), buffDuration, buffer.getObjectId(), stackCount); - sendSkillModIntent(buffData, receiver, false); + checkSkillMods(buffData, receiver, 1); receiver.addBuff(crc, buff); manageBuff(buff, crc, receiver); @@ -266,16 +271,17 @@ public class BuffService extends Service { Buff buff = creature.getBuffByCrc(buffCrc); - // Check if this buff can be stacked if (buffData.getMaxStackCount() > 1 && !expired && buff.getStackCount() > 1) { - // TODO NGE: buffs can, based on skillmods, adjust with different values creature.adjustBuffStackCount(buffCrc, -1); } else { - // Remove skillmods - sendSkillModIntent(buffData, creature, true); + Buff removedBuff = creature.removeBuff(buffCrc); - // Remove the buff from the creature - creature.removeBuff(buffCrc); + if (removedBuff == null) { + return; + } + + // Remove skillmods + checkSkillMods(buffData, creature, -removedBuff.getStackCount()); String callback = buffData.getCallback(); @@ -283,6 +289,7 @@ public class BuffService extends Service { return; CRC callbackCrc = new CRC(callback.toLowerCase(Locale.ENGLISH)); + if(dataMap.containsKey(callbackCrc)) { // Apply the callback buff addBuff(callbackCrc, creature, creature); @@ -293,25 +300,17 @@ public class BuffService extends Service { } } - private void sendSkillModIntent(BuffData buffData, CreatureObject creature, boolean remove) { - String effect1Name = buffData.getEffect1Name(); - String effect2Name = buffData.getEffect2Name(); - String effect3Name = buffData.getEffect3Name(); - String effect4Name = buffData.getEffect4Name(); - String effect5Name = buffData.getEffect5Name(); - - int valueFactor = remove ? -1 : 1; - - if (!effect1Name.isEmpty()) - new SkillModIntent(effect1Name, 0, (int) buffData.getEffect1Value() * valueFactor, creature).broadcast(); - if (!effect2Name.isEmpty()) - new SkillModIntent(effect2Name, 0, (int) buffData.getEffect2Value() * valueFactor, creature).broadcast(); - if (!effect3Name.isEmpty()) - new SkillModIntent(effect3Name, 0, (int) buffData.getEffect3Value() * valueFactor, creature).broadcast(); - if (!effect4Name.isEmpty()) - new SkillModIntent(effect4Name, 0, (int) buffData.getEffect4Value() * valueFactor, creature).broadcast(); - if (!effect5Name.isEmpty()) - new SkillModIntent(effect5Name, 0, (int) buffData.getEffect5Value() * valueFactor, creature).broadcast(); + private void checkSkillMods(BuffData buffData, CreatureObject creature, int valueFactor) { + sendSkillModIntent(creature, buffData.getEffect1Name(), buffData.getEffect1Value(), valueFactor); + sendSkillModIntent(creature, buffData.getEffect2Name(), buffData.getEffect2Value(), valueFactor); + sendSkillModIntent(creature, buffData.getEffect3Name(), buffData.getEffect3Value(), valueFactor); + sendSkillModIntent(creature, buffData.getEffect4Name(), buffData.getEffect4Value(), valueFactor); + sendSkillModIntent(creature, buffData.getEffect5Name(), buffData.getEffect5Value(), valueFactor); + } + + private void sendSkillModIntent(CreatureObject creature, String effectName, float effectValue, int valueFactor) { + if (!effectName.isEmpty()) + new SkillModIntent(effectName, 0, (int) effectValue * valueFactor, creature).broadcast(); } private static class BuffDelayed implements Delayed { From 54db22252e106580a856b44e9ee8315bde5643bd Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sun, 23 Oct 2016 04:16:40 +0200 Subject: [PATCH 38/63] Added support for single target damage abilities --- scripts/commands/combat/meleeHit.js | 7 -- .../commands/combat/rangedShotlightRifle.js | 7 -- scripts/commands/combat/rangedShotpistol.js | 7 -- scripts/commands/combat/rangedShotrifle.js | 7 -- src/resources/combat/AttackInfoLight.java | 50 -------------- src/resources/commands/CombatCommand.java | 9 +++ src/services/combat/CombatManager.java | 65 ++++++++++--------- src/services/commands/CommandService.java | 2 + 8 files changed, 44 insertions(+), 110 deletions(-) delete mode 100644 scripts/commands/combat/meleeHit.js delete mode 100644 scripts/commands/combat/rangedShotlightRifle.js delete mode 100644 scripts/commands/combat/rangedShotpistol.js delete mode 100644 scripts/commands/combat/rangedShotrifle.js delete mode 100644 src/resources/combat/AttackInfoLight.java diff --git a/scripts/commands/combat/meleeHit.js b/scripts/commands/combat/meleeHit.js deleted file mode 100644 index cc849b9ec..000000000 --- a/scripts/commands/combat/meleeHit.js +++ /dev/null @@ -1,7 +0,0 @@ -function canPerform(source, target, command) { - return (Java.type("resources.combat.CombatStatus")).SUCCESS; -} - -function doCombat(source, target, command) { - return 0; -} diff --git a/scripts/commands/combat/rangedShotlightRifle.js b/scripts/commands/combat/rangedShotlightRifle.js deleted file mode 100644 index cc849b9ec..000000000 --- a/scripts/commands/combat/rangedShotlightRifle.js +++ /dev/null @@ -1,7 +0,0 @@ -function canPerform(source, target, command) { - return (Java.type("resources.combat.CombatStatus")).SUCCESS; -} - -function doCombat(source, target, command) { - return 0; -} diff --git a/scripts/commands/combat/rangedShotpistol.js b/scripts/commands/combat/rangedShotpistol.js deleted file mode 100644 index cc849b9ec..000000000 --- a/scripts/commands/combat/rangedShotpistol.js +++ /dev/null @@ -1,7 +0,0 @@ -function canPerform(source, target, command) { - return (Java.type("resources.combat.CombatStatus")).SUCCESS; -} - -function doCombat(source, target, command) { - return 0; -} diff --git a/scripts/commands/combat/rangedShotrifle.js b/scripts/commands/combat/rangedShotrifle.js deleted file mode 100644 index cc849b9ec..000000000 --- a/scripts/commands/combat/rangedShotrifle.js +++ /dev/null @@ -1,7 +0,0 @@ -function canPerform(source, target, command) { - return (Java.type("resources.combat.CombatStatus")).SUCCESS; -} - -function doCombat(source, target, command) { - return 0; -} diff --git a/src/resources/combat/AttackInfoLight.java b/src/resources/combat/AttackInfoLight.java deleted file mode 100644 index 7e71569ed..000000000 --- a/src/resources/combat/AttackInfoLight.java +++ /dev/null @@ -1,50 +0,0 @@ -/************************************************************************************ - * Copyright (c) 2015 /// Project SWG /// www.projectswg.com * - * * - * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * - * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * - * Our goal is to create an emulator which will provide a server for players to * - * continue playing a game similar to the one they used to play. We are basing * - * it on the final publish of the game prior to end-game events. * - * * - * This file is part of Holocore. * - * * - * -------------------------------------------------------------------------------- * - * * - * Holocore is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * Holocore is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with Holocore. If not, see . * - * * - ***********************************************************************************/ -package resources.combat; - -public class AttackInfoLight { - - private int damage; - - public AttackInfoLight() { - this(0); - } - - public AttackInfoLight(int damage) { - setDamage(damage); - } - - public int getDamage() { - return damage; - } - - public void setDamage(int damage) { - this.damage = damage; - } - -} diff --git a/src/resources/commands/CombatCommand.java b/src/resources/commands/CombatCommand.java index 28e9eebc8..9c69eba24 100644 --- a/src/resources/commands/CombatCommand.java +++ b/src/resources/commands/CombatCommand.java @@ -49,6 +49,7 @@ public class CombatCommand extends Command { private boolean pvpOnly; private int attackRolls; private float percentAddFromWeapon; + private int addedDamage; public CombatCommand(String name) { super(name); @@ -155,5 +156,13 @@ public class CombatCommand extends Command { public void setPercentAddFromWeapon(float percentAddFromWeapon) { this.percentAddFromWeapon = percentAddFromWeapon; } + + public int getAddedDamage() { + return addedDamage; + } + + public void setAddedDamage(int addedDamage) { + this.addedDamage = addedDamage; + } } diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java index 29b73ae40..4cd68189f 100644 --- a/src/services/combat/CombatManager.java +++ b/src/services/combat/CombatManager.java @@ -50,7 +50,6 @@ import java.util.concurrent.Future; import resources.Posture; import resources.PvpFaction; import resources.PvpFlag; -import resources.combat.AttackInfoLight; import resources.combat.AttackType; import resources.combat.CombatStatus; import resources.combat.HitLocation; @@ -217,22 +216,12 @@ public class CombatManager extends Manager { CombatStatus status = canPerform(cci.getSource(), cci.getTarget(), c); if (!handleStatus(cci.getSource(), status)) return; - Object res = Scripts.invoke("commands/combat/"+c.getName(), "doCombat", cci.getSource(), cci.getTarget(), c); - if (res == null) { - handleStatus(cci.getSource(), CombatStatus.UNKNOWN); - return; - } + updateCombatList(cci.getSource()); if (cci.getTarget() instanceof CreatureObject) updateCombatList((CreatureObject) cci.getTarget()); - if (res instanceof Number) - doCombat(cci.getSource(), cci.getTarget(), new AttackInfoLight(((Number) res).intValue()), c); - else if (res instanceof AttackInfoLight) - doCombat(cci.getSource(), cci.getTarget(), (AttackInfoLight) res, c); - else { - Log.w(this, "Unknown return from combat script: " + res); - return; - } + + doCombat(cci.getSource(), cci.getTarget(), c); } private void updateCombatList(CreatureObject creature) { @@ -249,7 +238,7 @@ public class CombatManager extends Manager { combat.updateLastCombat(); } - private void doCombat(CreatureObject source, SWGObject target, AttackInfoLight info, CombatCommand command) { + private void doCombat(CreatureObject source, SWGObject target, CombatCommand command) { CombatAction action = new CombatAction(source.getObjectId()); String anim = command.getRandomAnimation(source.getEquippedWeapon().getType()); action.setActionCrc(CRC.getCrc(anim)); @@ -258,15 +247,25 @@ public class CombatManager extends Manager { action.setCommandCrc(command.getCrc()); action.setTrail(TrailLocation.WEAPON); action.setUseLocation(false); - if (target instanceof CreatureObject) { - if (command.getAttackType() == AttackType.SINGLE_TARGET) - doCombatSingle(source, (CreatureObject) target, info, command); - action.addDefender((CreatureObject) target, true, (byte) 0, HitLocation.HIT_LOCATION_BODY, (short) info.getDamage()); + + for(int i = 0; i < command.getAttackRolls(); i++) { + int damage = 0; + + damage += calculateWeaponDamage(source, command); + damage += command.getAddedDamage(); + addSelfBuff(source, command); + + if (target instanceof CreatureObject) { + if (command.getAttackType() == AttackType.SINGLE_TARGET) + doCombatSingle(source, (CreatureObject) target, damage, command); + action.addDefender((CreatureObject) target, true, (byte) 0, HitLocation.HIT_LOCATION_BODY, (short) damage); + } } + source.sendObserversAndSelf(action); } - private void doCombatSingle(CreatureObject source, CreatureObject target, AttackInfoLight info, CombatCommand command) { + private void doCombatSingle(CreatureObject source, CreatureObject target, int damage, CombatCommand command) { if (!source.isInCombat()) enterCombat(source); if (!target.isInCombat()) @@ -274,12 +273,20 @@ public class CombatManager extends Manager { target.addDefender(source); source.addDefender(target); - addWeaponDamage(source, command, info); + addTargetBuff(source, command); - if (target.getHealth() <= info.getDamage()) + if (target.getHealth() <= damage) doCreatureDeath(target, source); else - target.modifyHealth(-info.getDamage()); + target.modifyHealth(-damage); + } + + private void addTargetBuff(CreatureObject target, CombatCommand command) { + + } + + private void addSelfBuff(CreatureObject creature, CombatCommand command) { + } private void enterCombat(CreatureObject creature) { @@ -491,10 +498,7 @@ public class CombatManager extends Manager { } if (status != CombatStatus.SUCCESS) return status; - status = Scripts.invoke("commands/combat/"+c.getName(), "canPerform", source, target, c); - if (status == null) { - return CombatStatus.UNKNOWN; - } + return status; } @@ -512,15 +516,12 @@ public class CombatManager extends Manager { return CombatStatus.SUCCESS; } - private void addWeaponDamage(CreatureObject source, CombatCommand command, AttackInfoLight info) { - int abilityDamage = info.getDamage(); + private int calculateWeaponDamage(CreatureObject source, CombatCommand command) { WeaponObject weapon = source.getEquippedWeapon(); int minDamage = weapon.getMinDamage(); int weaponDamage = random.nextInt((weapon.getMaxDamage() - minDamage) + 1) + minDamage; - weaponDamage *= command.getPercentAddFromWeapon(); - - info.setDamage(abilityDamage + weaponDamage); + return (int) (weaponDamage * command.getPercentAddFromWeapon()); } private void showFlyText(TangibleObject obj, String text, Scale scale, Color c, ShowFlyText.Flag ... flags) { diff --git a/src/services/commands/CommandService.java b/src/services/commands/CommandService.java index ef7a20bea..33249d41f 100644 --- a/src/services/commands/CommandService.java +++ b/src/services/commands/CommandService.java @@ -416,6 +416,7 @@ public class CommandService extends Service { int attackRolls = combatCommands.getColumnFromName("attack_rolls"); int animDefault = combatCommands.getColumnFromName("animDefault"); int percentAddFromWeapon = combatCommands.getColumnFromName("percentAddFromWeapon"); + int addedDamage = combatCommands.getColumnFromName("addedDamage"); // animDefault anim_unarmed anim_onehandmelee anim_twohandmelee anim_polearm // anim_pistol anim_lightRifle anim_carbine anim_rifle anim_heavyweapon // anim_thrown anim_onehandlightsaber anim_twohandlightsaber anim_polearmlightsaber @@ -438,6 +439,7 @@ public class CommandService extends Service { cc.setAttackRolls((int) cmdRow[attackRolls]); cc.setDefaultAnimation(getAnimationList((String) cmdRow[animDefault])); cc.setPercentAddFromWeapon((float) cmdRow[percentAddFromWeapon]); + cc.setAddedDamage((int) cmdRow[addedDamage]); cc.setAnimations(WeaponType.UNARMED, getAnimationList((String) cmdRow[animDefault+1])); cc.setAnimations(WeaponType.ONE_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault+2])); cc.setAnimations(WeaponType.TWO_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault+3])); From 2ee29469666e5e9427afeb0eefb252ea5e5f49eb Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sun, 23 Oct 2016 05:10:09 +0200 Subject: [PATCH 39/63] Effects are now played when applying Jedi stances --- src/services/commands/BuffService.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 392361b1d..a2bb85571 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -130,6 +130,7 @@ public class BuffService extends Service { int duration = buffTable.getColumnFromName("DURATION"); int particle = buffTable.getColumnFromName("PARTICLE"); int particleHardpoint = buffTable.getColumnFromName("PARTICLE_HARDPOINT"); + int stanceParticle = buffTable.getColumnFromName("STANCE_PARTICLE"); int callback = buffTable.getColumnFromName("CALLBACK"); for(int row = 0; row < buffTable.getRowCount(); row++) { @@ -150,6 +151,7 @@ public class BuffService extends Service { (float) buffTable.getCell(row, duration), (String) buffTable.getCell(row, particle), (String) buffTable.getCell(row, particleHardpoint), + (String) buffTable.getCell(row, stanceParticle), (String) buffTable.getCell(row, callback) )); } @@ -235,10 +237,13 @@ public class BuffService extends Service { receiver.addBuff(crc, buff); manageBuff(buff, crc, receiver); - String effectFileName = buffData.getEffectFileName(); - + sendObjectEffect(buffData.getEffectFileName(), receiver, buffData.getParticleHardPoint()); + sendObjectEffect(buffData.getStanceParticle(), receiver, buffData.getParticleHardPoint()); + } + + private void sendObjectEffect(String effectFileName, CreatureObject receiver, String hardPoint) { if (!effectFileName.isEmpty()) { - receiver.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, buffData.getParticleHardPoint(), receiver.getObjectId())); + receiver.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, hardPoint, receiver.getObjectId())); } } @@ -378,9 +383,10 @@ public class BuffService extends Service { private final float defaultDuration; private final String effectFileName; private final String particleHardPoint; + private final String stanceParticle; private final String callback; - private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String callback) { + private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String stanceParticle, String callback) { this.groupName = groupName; this.groupPriority = groupPriority; this.maxStackCount = maxStackCount; @@ -397,6 +403,7 @@ public class BuffService extends Service { this.defaultDuration = defaultDuration; this.effectFileName = effectFileName; this.particleHardPoint = particleHardPoint; + this.stanceParticle = stanceParticle; this.callback = callback; } @@ -464,6 +471,10 @@ public class BuffService extends Service { return particleHardPoint; } + public String getStanceParticle() { + return stanceParticle; + } + public String getCallback() { return callback; } From d58733995c187b47efabdd4ce9577dab8d4e2daa Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sun, 23 Oct 2016 07:35:01 +0200 Subject: [PATCH 40/63] Added support for combat command ranges --- src/resources/commands/CombatCommand.java | 20 +++++- src/resources/commands/Command.java | 6 +- .../objects/creature/CreatureObject.java | 18 ++--- .../objects/tangible/TangibleObject.java | 12 ++-- src/services/combat/CombatManager.java | 65 ++++++++++--------- src/services/commands/CommandService.java | 7 ++ 6 files changed, 74 insertions(+), 54 deletions(-) diff --git a/src/resources/commands/CombatCommand.java b/src/resources/commands/CombatCommand.java index 9c69eba24..28a9c0c2d 100644 --- a/src/resources/commands/CombatCommand.java +++ b/src/resources/commands/CombatCommand.java @@ -50,6 +50,8 @@ public class CombatCommand extends Command { private int attackRolls; private float percentAddFromWeapon; private int addedDamage; + private String buffNameTarget; + private String buffNameSelf; public CombatCommand(String name) { super(name); @@ -164,5 +166,21 @@ public class CombatCommand extends Command { public void setAddedDamage(int addedDamage) { this.addedDamage = addedDamage; } - + + public String getBuffNameTarget() { + return buffNameTarget; + } + + public void setBuffNameTarget(String buffNameTarget) { + this.buffNameTarget = buffNameTarget; + } + + public String getBuffNameSelf() { + return buffNameSelf; + } + + public void setBuffNameSelf(String buffNameSelf) { + this.buffNameSelf = buffNameSelf; + } + } diff --git a/src/resources/commands/Command.java b/src/resources/commands/Command.java index a7d319727..64f428b4c 100644 --- a/src/resources/commands/Command.java +++ b/src/resources/commands/Command.java @@ -50,7 +50,7 @@ public class Command { private boolean callOnTarget; // command group // disabled - private int maxRange; + private float maxRange; private int godLevel; // display group private boolean combatCommand; @@ -94,8 +94,8 @@ public class Command { public void setTargetType(int targetType) { this.targetType = targetType; } public boolean isCallOnTarget() { return callOnTarget; } public void setCallOnTarget(boolean callOnTarget) { this.callOnTarget = callOnTarget; } - public int getMaxRange() { return maxRange; } - public void setMaxRange(int maxRange) { this.maxRange = maxRange; } + public float getMaxRange() { return maxRange; } + public void setMaxRange(float maxRange) { this.maxRange = maxRange; } public int getGodLevel() { return godLevel; } public void setGodLevel(int godLevel) { this.godLevel = godLevel; } public boolean isCombatCommand() { return combatCommand; } diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 237eb4310..ee4be70bc 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -794,20 +794,16 @@ public class CreatureObject extends TangibleObject { public boolean isEnemy(TangibleObject otherObject) { boolean enemy = super.isEnemy(otherObject); - if(enemy) { - // If these are both creatures, there's a chance both of them are players! - if (this instanceof CreatureObject && otherObject instanceof CreatureObject) { - CreatureObject thisCreature = (CreatureObject) this; - CreatureObject otherCreature = (CreatureObject) otherObject; + if (enemy && otherObject instanceof CreatureObject) { + PvpStatus ourStatus = getPvpStatus(); + PvpStatus theirStatus = otherObject.getPvpStatus(); - // If they're both players, both of them might be special force - if (thisCreature.isPlayer() && otherCreature.isPlayer()) { - // They are enemies if they're both players and members of the special force! - return getPvpStatus() == PvpStatus.SPECIALFORCES && otherObject.getPvpStatus() == PvpStatus.SPECIALFORCES; - } + if (isPlayer() && ((CreatureObject) otherObject).isPlayer()) { + // They are enemies if both players are SF + return ourStatus == PvpStatus.SPECIALFORCES && theirStatus == PvpStatus.SPECIALFORCES; } } - + return enemy; } diff --git a/src/resources/objects/tangible/TangibleObject.java b/src/resources/objects/tangible/TangibleObject.java index 9ec24a304..6bd236ccd 100644 --- a/src/resources/objects/tangible/TangibleObject.java +++ b/src/resources/objects/tangible/TangibleObject.java @@ -257,14 +257,12 @@ public class TangibleObject extends SWGObject { // They CAN be enemies if they're not from the same faction and neither of them are neutral PvpFaction otherFaction = otherObject.getPvpFaction(); - if (otherFaction != PvpFaction.NEUTRAL && getPvpFaction() != otherFaction) { - if (getPvpStatus() != PvpStatus.ONLEAVE && otherObject.getPvpStatus() != PvpStatus.ONLEAVE) { - // Both objects are not players. In this case, both just need to be combatants. - return true; - } + if (hasPvpFlag(PvpFlag.ATTACKABLE) || otherObject.hasPvpFlag(PvpFlag.ATTACKABLE)) { + return true; + } else { + return otherFaction != PvpFaction.NEUTRAL && getPvpFaction() != otherFaction + && getPvpStatus() != PvpStatus.ONLEAVE && otherObject.getPvpStatus() != PvpStatus.ONLEAVE; } - - return false; } public String getCurrentCity() { diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java index 4cd68189f..316d7c154 100644 --- a/src/services/combat/CombatManager.java +++ b/src/services/combat/CombatManager.java @@ -239,6 +239,9 @@ public class CombatManager extends Manager { } private void doCombat(CreatureObject source, SWGObject target, CombatCommand command) { + // TODO HitType checking. This will for instance prevent buff commands from behaving as a single target combat command + addBuff(source, source, command.getBuffNameSelf()); // Add self buff if present + CombatAction action = new CombatAction(source.getObjectId()); String anim = command.getRandomAnimation(source.getEquippedWeapon().getType()); action.setActionCrc(CRC.getCrc(anim)); @@ -253,7 +256,6 @@ public class CombatManager extends Manager { damage += calculateWeaponDamage(source, command); damage += command.getAddedDamage(); - addSelfBuff(source, command); if (target instanceof CreatureObject) { if (command.getAttackType() == AttackType.SINGLE_TARGET) @@ -273,7 +275,7 @@ public class CombatManager extends Manager { target.addDefender(source); source.addDefender(target); - addTargetBuff(source, command); + addBuff(source, target, command.getBuffNameTarget()); // Add target buff if (target.getHealth() <= damage) doCreatureDeath(target, source); @@ -281,14 +283,6 @@ public class CombatManager extends Manager { target.modifyHealth(-damage); } - private void addTargetBuff(CreatureObject target, CombatCommand command) { - - } - - private void addSelfBuff(CreatureObject creature, CombatCommand command) { - - } - private void enterCombat(CreatureObject creature) { creature.setInCombat(true); @@ -463,57 +457,56 @@ public class CombatManager extends Manager { private CombatStatus canPerform(CreatureObject source, SWGObject target, CombatCommand c) { if (source.getEquippedWeapon() == null) return CombatStatus.NO_WEAPON; + + if (target == null || source.equals(target)) + return CombatStatus.SUCCESS; + if (!(target instanceof TangibleObject)) return CombatStatus.INVALID_TARGET; + TangibleObject tangibleTarget = (TangibleObject) target; - if(tangibleTarget.getPvpFaction() != PvpFaction.NEUTRAL) { - if(!tangibleTarget.isEnemy(source)) { - return CombatStatus.INVALID_TARGET; - } - } else if ((tangibleTarget.getPvpFlags() & PvpFlag.ATTACKABLE.getBitmask()) == 0) + + if (!tangibleTarget.isEnemy(source)) { return CombatStatus.INVALID_TARGET; + } if(target instanceof CreatureObject) { - CreatureObject creature = (CreatureObject) target; - - switch(creature.getPosture()) { + switch(((CreatureObject) target).getPosture()) { case DEAD: case INCAPACITATED: return CombatStatus.INVALID_TARGET; } } - CombatStatus status; switch (c.getAttackType()) { case AREA: case TARGET_AREA: - status = canPerformArea(source, c); - break; + return canPerformArea(source, c); case SINGLE_TARGET: - status = canPerformSingle(source, target, c); - break; + return canPerformSingle(source, target, c); default: - status = CombatStatus.UNKNOWN; - break; + return CombatStatus.UNKNOWN; } - if (status != CombatStatus.SUCCESS) - return status; - - return status; } private CombatStatus canPerformSingle(CreatureObject source, SWGObject target, CombatCommand c) { - if (target == null || !(target instanceof CreatureObject)) + if (target == null || !(target instanceof TangibleObject)) return CombatStatus.NO_TARGET; + WeaponObject weapon = source.getEquippedWeapon(); double dist = source.getLocation().distanceTo(target.getLocation()); - if (dist > weapon.getMaxRange() || (dist > c.getMaxRange() && c.getMaxRange() > 0)) + float commandRange = c.getMaxRange(); + float range = commandRange > 0 ? commandRange : weapon.getMaxRange(); + + if (dist > range) return CombatStatus.TOO_FAR; + return CombatStatus.SUCCESS; } private CombatStatus canPerformArea(CreatureObject source, CombatCommand c) { - return CombatStatus.SUCCESS; + // TODO implement AoE + return CombatStatus.UNKNOWN; } private int calculateWeaponDamage(CreatureObject source, CombatCommand command) { @@ -524,6 +517,14 @@ public class CombatManager extends Manager { return (int) (weaponDamage * command.getPercentAddFromWeapon()); } + private void addBuff(CreatureObject caster, CreatureObject receiver, String buffName) { + if (buffName.isEmpty()) { + return; + } + + new BuffIntent(buffName, caster, receiver, false).broadcast(); + } + private void showFlyText(TangibleObject obj, String text, Scale scale, Color c, ShowFlyText.Flag ... flags) { obj.sendSelf(new ShowFlyText(obj.getObjectId(), text, scale, new RGB(c), flags)); } diff --git a/src/services/commands/CommandService.java b/src/services/commands/CommandService.java index 33249d41f..7024cada7 100644 --- a/src/services/commands/CommandService.java +++ b/src/services/commands/CommandService.java @@ -400,6 +400,7 @@ public class CommandService extends Service { cc.setCooldownGroup2(c.getCooldownGroup2()); cc.setCooldownTime(c.getCooldownTime()); cc.setCooldownTime2(c.getCooldownTime2()); + cc.setMaxRange(c.getMaxRange()); return cc; } @@ -417,6 +418,9 @@ public class CommandService extends Service { int animDefault = combatCommands.getColumnFromName("animDefault"); int percentAddFromWeapon = combatCommands.getColumnFromName("percentAddFromWeapon"); int addedDamage = combatCommands.getColumnFromName("addedDamage"); + int buffNameTarget = combatCommands.getColumnFromName("buffNameTarget"); + int buffNameSelf = combatCommands.getColumnFromName("buffNameSelf"); + int maxRange = combatCommands.getColumnFromName("maxRange"); // animDefault anim_unarmed anim_onehandmelee anim_twohandmelee anim_polearm // anim_pistol anim_lightRifle anim_carbine anim_rifle anim_heavyweapon // anim_thrown anim_onehandlightsaber anim_twohandlightsaber anim_polearmlightsaber @@ -440,6 +444,9 @@ public class CommandService extends Service { cc.setDefaultAnimation(getAnimationList((String) cmdRow[animDefault])); cc.setPercentAddFromWeapon((float) cmdRow[percentAddFromWeapon]); cc.setAddedDamage((int) cmdRow[addedDamage]); + cc.setBuffNameTarget((String) cmdRow[buffNameTarget]); + cc.setBuffNameSelf((String) cmdRow[buffNameSelf]); + cc.setMaxRange((float) cmdRow[maxRange]); cc.setAnimations(WeaponType.UNARMED, getAnimationList((String) cmdRow[animDefault+1])); cc.setAnimations(WeaponType.ONE_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault+2])); cc.setAnimations(WeaponType.TWO_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault+3])); From c51b495483b65580ab28d9f95e2c306010bc066a Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Sun, 23 Oct 2016 07:56:09 +0200 Subject: [PATCH 41/63] Added support for buffs on NPCs --- src/services/commands/BuffService.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index a2bb85571..7c2150b8b 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -196,8 +196,12 @@ public class BuffService extends Service { String groupName = buffData.getGroupName(); Optional groupBuff = receiver.getBuffs().keySet().stream().filter(candidate -> groupName.equals(dataMap.get(candidate).groupName)).findFirst(); - receiver.getPlayerObject().updatePlayTime(); - int playTime = receiver.getPlayerObject().getPlayTime(); + int playTime = 0; + + if(receiver.isPlayer()) { + receiver.getPlayerObject().updatePlayTime(); + playTime = receiver.getPlayerObject().getPlayTime(); + } if (groupBuff.isPresent()) { // They already have a buff of this group CRC oldCrc = groupBuff.get(); @@ -249,7 +253,6 @@ public class BuffService extends Service { private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { if (buff.getEndTime() <= 0) { - // If this buff has less than or 0 seconds left, then remove it. removeBuff(creature, buffCrc, true); } else if(buff.getDuration() >= 0) { // If this buff doesn't last forever or hasn't expired, we'll schedule it for removal in the future From 2232144c3016794706e11c6ccdfbdc32d6f44998 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 24 Oct 2016 01:27:21 +0200 Subject: [PATCH 42/63] Fixed an issue where multiple buffs from the same group could be applied --- src/services/commands/BuffService.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 7c2150b8b..200f64577 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -53,7 +53,7 @@ import utilities.ThreadUtilities; public class BuffService extends Service { - // TODO allow removal of non-debuffs by right-clicking them + // 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 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 // TODO on deathblow, decay buffs with BuffData where DECAY_ON_PVP_DEATH == 1 @@ -222,8 +222,8 @@ public class BuffService extends Service { // We reset the duration receiver.setBuffDuration(newCrc, playTime, (int) buffData.getDefaultDuration()); - } else if (dataMap.get(oldCrc).getGroupPriority() >= buffData.getGroupPriority()) { - removeBuff(receiver, newCrc, true); + } else if (buffData.getGroupPriority() >= dataMap.get(oldCrc).getGroupPriority()) { + removeBuff(receiver, oldCrc, true); applyBuff(receiver, buffer, buffData, playTime, newCrc); } } else { // They had no buff from this group already @@ -252,7 +252,8 @@ public class BuffService extends Service { } private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { - if (buff.getEndTime() <= 0) { + System.out.println("default duration on buff: " + dataMap.get(buffCrc).getDefaultDuration()); + if (dataMap.get(buffCrc).getDefaultDuration() > -1 && buff.getEndTime() <= 0) { removeBuff(creature, buffCrc, true); } else if(buff.getDuration() >= 0) { // If this buff doesn't last forever or hasn't expired, we'll schedule it for removal in the future From bb1df1589ccd57b54cfc2cdedfb30aa5c6a11c28 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 24 Oct 2016 01:29:08 +0200 Subject: [PATCH 43/63] Removed debug print in BuffService --- src/services/commands/BuffService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 200f64577..90798e801 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -252,7 +252,6 @@ public class BuffService extends Service { } private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { - System.out.println("default duration on buff: " + dataMap.get(buffCrc).getDefaultDuration()); if (dataMap.get(buffCrc).getDefaultDuration() > -1 && buff.getEndTime() <= 0) { removeBuff(creature, buffCrc, true); } else if(buff.getDuration() >= 0) { From 244af8254013c878255a698a999f43c9603e7e20 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Mon, 24 Oct 2016 02:57:36 +0200 Subject: [PATCH 44/63] Added support for different kinds of combat commands --- src/resources/combat/HitType.java | 62 +++++++++++++++++++++++ src/resources/commands/CombatCommand.java | 10 ++++ src/services/combat/CombatManager.java | 45 ++++++++++------ src/services/commands/CommandService.java | 3 ++ 4 files changed, 104 insertions(+), 16 deletions(-) create mode 100644 src/resources/combat/HitType.java diff --git a/src/resources/combat/HitType.java b/src/resources/combat/HitType.java new file mode 100644 index 000000000..e1ab5a767 --- /dev/null +++ b/src/resources/combat/HitType.java @@ -0,0 +1,62 @@ +/************************************************************************************ + * Copyright (c) 2015 /// Project SWG /// www.projectswg.com * + * * + * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * + * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * + * Our goal is to create an emulator which will provide a server for players to * + * continue playing a game similar to the one they used to play. We are basing * + * it on the final publish of the game prior to end-game events. * + * * + * This file is part of Holocore. * + * * + * -------------------------------------------------------------------------------- * + * * + * Holocore is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * Holocore is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with Holocore. If not, see . * + * * + ***********************************************************************************/ +package resources.combat; + +import java.util.HashMap; +import java.util.Map; + +public enum HitType { + ATTACK (-1), + BUFF (0), + DEBUFF (4), + HEAL (5), + DELAY_ATTACK (6), + REVIVE (7); + + private static final Map HIT_TYPES = new HashMap<>(); + + static { + for(HitType hitType : values()) { + HIT_TYPES.put(hitType.getNum(), hitType); + } + } + + private final int num; + + HitType(int num) { + this.num = num; + } + + public int getNum() { + return num; + } + + public static HitType getHitType(int num) { + return HIT_TYPES.getOrDefault(num, ATTACK); + } +} \ No newline at end of file diff --git a/src/resources/commands/CombatCommand.java b/src/resources/commands/CombatCommand.java index 28a9c0c2d..3e2e82cf9 100644 --- a/src/resources/commands/CombatCommand.java +++ b/src/resources/commands/CombatCommand.java @@ -32,6 +32,7 @@ import java.util.Map; import resources.combat.AttackType; import resources.combat.DamageType; +import resources.combat.HitType; import resources.combat.ValidTarget; import resources.objects.weapon.WeaponType; @@ -52,6 +53,7 @@ public class CombatCommand extends Command { private int addedDamage; private String buffNameTarget; private String buffNameSelf; + private HitType hitType; public CombatCommand(String name) { super(name); @@ -183,4 +185,12 @@ public class CombatCommand extends Command { this.buffNameSelf = buffNameSelf; } + public HitType getHitType() { + return hitType; + } + + public void setHitType(HitType hitType) { + this.hitType = hitType; + } + } diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java index 316d7c154..f6d10ac61 100644 --- a/src/services/combat/CombatManager.java +++ b/src/services/combat/CombatManager.java @@ -209,19 +209,19 @@ public class CombatManager extends Manager { } } - private void processChatCommand(ChatCommandIntent cci) { - if (!cci.getCommand().isCombatCommand() || !(cci.getCommand() instanceof CombatCommand)) - return; - CombatCommand c = (CombatCommand) cci.getCommand(); - CombatStatus status = canPerform(cci.getSource(), cci.getTarget(), c); - if (!handleStatus(cci.getSource(), status)) + private void processChatCommand(ChatCommandIntent intent) { + if (!intent.getCommand().isCombatCommand() || !(intent.getCommand() instanceof CombatCommand)) return; + CombatCommand c = (CombatCommand) intent.getCommand(); + CreatureObject source = intent.getSource(); + SWGObject target = intent.getTarget(); - updateCombatList(cci.getSource()); - if (cci.getTarget() instanceof CreatureObject) - updateCombatList((CreatureObject) cci.getTarget()); - - doCombat(cci.getSource(), cci.getTarget(), c); + // TODO implement support for remaining HitTypes + switch (c.getHitType()) { + case ATTACK: handleAttack(source, target, c); break; + case BUFF: handleBuff(source, target, c); break; + default: handleStatus(source, CombatStatus.UNKNOWN); break; + } } private void updateCombatList(CreatureObject creature) { @@ -238,9 +238,11 @@ public class CombatManager extends Manager { combat.updateLastCombat(); } - private void doCombat(CreatureObject source, SWGObject target, CombatCommand command) { - // TODO HitType checking. This will for instance prevent buff commands from behaving as a single target combat command - addBuff(source, source, command.getBuffNameSelf()); // Add self buff if present + private void handleAttack(CreatureObject source, SWGObject target, CombatCommand command) { + CombatStatus status = canPerform(source, target, command); + + if (!handleStatus(source, status)) + return; CombatAction action = new CombatAction(source.getObjectId()); String anim = command.getRandomAnimation(source.getEquippedWeapon().getType()); @@ -267,7 +269,19 @@ public class CombatManager extends Manager { source.sendObserversAndSelf(action); } + private void handleBuff(CreatureObject source, SWGObject target, CombatCommand combatCommand) { + addBuff(source, source, combatCommand.getBuffNameSelf()); + + // Only CreatureObjects have buffs + if(target instanceof CreatureObject) + addBuff(source, (CreatureObject) target, combatCommand.getBuffNameTarget()); + } + private void doCombatSingle(CreatureObject source, CreatureObject target, int damage, CombatCommand command) { + updateCombatList(source); + if (target instanceof CreatureObject) + updateCombatList((CreatureObject) target); + if (!source.isInCombat()) enterCombat(source); if (!target.isInCombat()) @@ -448,8 +462,7 @@ public class CombatManager extends Manager { showFlyText(source, "@combat_effects:range_too_far", Scale.MEDIUM, Color.CYAN, ShowFlyText.Flag.PRIVATE); return false; default: - showFlyText(source, "@combat_effects:cant_attack_fly", Scale.MEDIUM, Color.WHITE, ShowFlyText.Flag.PRIVATE); - Log.e(this, "Character unable to attack. Player: %s Reason: %s", source.getName(), status); + showFlyText(source, "@combat_effects:action_failed", Scale.MEDIUM, Color.WHITE, ShowFlyText.Flag.PRIVATE); return false; } } diff --git a/src/services/commands/CommandService.java b/src/services/commands/CommandService.java index 7024cada7..255762c5e 100644 --- a/src/services/commands/CommandService.java +++ b/src/services/commands/CommandService.java @@ -72,6 +72,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import network.packets.swg.zone.object_controller.CommandTimer; +import resources.combat.HitType; import resources.commands.DefaultPriority; import resources.objects.creature.CreatureObject; import utilities.ThreadUtilities; @@ -421,6 +422,7 @@ public class CommandService extends Service { int buffNameTarget = combatCommands.getColumnFromName("buffNameTarget"); int buffNameSelf = combatCommands.getColumnFromName("buffNameSelf"); int maxRange = combatCommands.getColumnFromName("maxRange"); + int hitType = combatCommands.getColumnFromName("hitType"); // animDefault anim_unarmed anim_onehandmelee anim_twohandmelee anim_polearm // anim_pistol anim_lightRifle anim_carbine anim_rifle anim_heavyweapon // anim_thrown anim_onehandlightsaber anim_twohandlightsaber anim_polearmlightsaber @@ -447,6 +449,7 @@ public class CommandService extends Service { cc.setBuffNameTarget((String) cmdRow[buffNameTarget]); cc.setBuffNameSelf((String) cmdRow[buffNameSelf]); cc.setMaxRange((float) cmdRow[maxRange]); + cc.setHitType(HitType.getHitType((Integer) cmdRow[hitType])); cc.setAnimations(WeaponType.UNARMED, getAnimationList((String) cmdRow[animDefault+1])); cc.setAnimations(WeaponType.ONE_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault+2])); cc.setAnimations(WeaponType.TWO_HANDED_MELEE, getAnimationList((String) cmdRow[animDefault+3])); From a7599335049cf95eed154d35234c4245ad1e3795 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 25 Oct 2016 07:02:18 +0200 Subject: [PATCH 45/63] Fixed an issue where clicking to deathblow wasn't working --- src/services/faction/FactionService.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/faction/FactionService.java b/src/services/faction/FactionService.java index 6036c3a89..3f080407a 100644 --- a/src/services/faction/FactionService.java +++ b/src/services/faction/FactionService.java @@ -71,11 +71,11 @@ public final class FactionService extends Service { } @Override - public boolean terminate() { + public boolean stop() { // If some were in the middle of switching, finish the switches immediately executor.shutdownNow().forEach(runnable -> runnable.run()); - return super.terminate(); + return super.stop(); } private void handleFactionIntent(FactionIntent i) { @@ -248,7 +248,7 @@ public final class FactionService extends Service { int pvpBitmask = 0; if(target.isEnemy(observer)) { - pvpBitmask |= PvpFlag.AGGRESSIVE.getBitmask() | PvpFlag.ATTACKABLE.getBitmask(); + pvpBitmask |= PvpFlag.AGGRESSIVE.getBitmask() | PvpFlag.ATTACKABLE.getBitmask() | PvpFlag.ENEMY.getBitmask(); } return pvpBitmask; From 4bd8aa8fdcfe6805e5ad0813afe8306466c12bb9 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 25 Oct 2016 10:20:29 +0200 Subject: [PATCH 46/63] Refactored buffs slightly --- .../objects/creature/CreatureObject.java | 17 +- .../creature/CreatureObjectSharedNP.java | 41 ++-- .../objects/player/PlayerObject.java | 5 +- .../objects/tangible/TangibleObject.java | 5 +- src/services/combat/CombatManager.java | 9 +- src/services/commands/BuffService.java | 231 ++++++++++-------- 6 files changed, 166 insertions(+), 142 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index ee4be70bc..ff386e057 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -30,9 +30,11 @@ package resources.objects.creature; import java.util.ArrayList; import java.util.Collection; import java.util.Set; -import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.Predicate; +import java.util.stream.Stream; import network.packets.swg.zone.UpdatePostureMessage; import network.packets.swg.zone.UpdatePvpStatusMessage; @@ -100,6 +102,7 @@ public class CreatureObject extends TangibleObject { removeEquipment(obj); } + @Override protected void handleSlotReplacement(SWGObject oldParent, SWGObject obj, int arrangement) { SWGObject inventory = getSlottedObject("inventory"); for (String slot : obj.getArrangement().get(arrangement-4)) { @@ -590,7 +593,7 @@ public class CreatureObject extends TangibleObject { } public void addBuff(CRC buffCrc, Buff buff) { - creo6.addBuff(buffCrc, buff, this); + creo6.putBuff(buffCrc, buff, this); } public Buff removeBuff(CRC buffCrc) { @@ -598,11 +601,11 @@ public class CreatureObject extends TangibleObject { } public boolean hasBuff(String buffName) { - return getBuffByCrc(new CRC(buffName.toLowerCase(Locale.ENGLISH))) != null; + return getBuffEntries(buffEntry -> new CRC(buffName.toLowerCase(Locale.ENGLISH)).equals(buffEntry.getKey())).count() > 0; } - public Buff getBuffByCrc(CRC buffCrc) { - return creo6.getBuffByCrc(buffCrc); + public Stream> getBuffEntries(Predicate> predicate) { + return creo6.getBuffEntries(predicate); } public void adjustBuffStackCount(CRC buffCrc, int adjustment) { @@ -613,8 +616,8 @@ public class CreatureObject extends TangibleObject { creo6.setBuffDuration(buffCrc, playTime, duration, this); } - public Map getBuffs() { - return new HashMap<>(creo6.getBuffs()); + public void forEachBuff(BiConsumer action) { + creo6.forEachBuff(action); } public boolean isVisible() { diff --git a/src/resources/objects/creature/CreatureObjectSharedNP.java b/src/resources/objects/creature/CreatureObjectSharedNP.java index 38712d135..1056edaff 100644 --- a/src/resources/objects/creature/CreatureObjectSharedNP.java +++ b/src/resources/objects/creature/CreatureObjectSharedNP.java @@ -29,6 +29,11 @@ package resources.objects.creature; import java.util.HashMap; import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.function.BiConsumer; +import java.util.function.Predicate; +import java.util.stream.Stream; import resources.HologramColour; import resources.collections.SWGList; import resources.collections.SWGMap; @@ -457,20 +462,14 @@ class CreatureObjectSharedNP implements Persistable { maxAttributes.sendDeltaMessage(target); } } - - /** - * @return a copy of the buffs map. Removing and adding entries in this - * map will not affect the internal {@code SWGMap}. Do not edit the - * {@code Buff} values in the belief that deltas will be sent because - * they won't - this is incorrect usage. - */ - public Map getBuffs() { + + public void forEachBuff(BiConsumer action) { synchronized (buffs) { - return new HashMap<>(buffs); + buffs.forEach(action); } } - public void addBuff(CRC buffCrc, Buff buff, SWGObject target) { + public void putBuff(CRC buffCrc, Buff buff, SWGObject target) { synchronized (buffs) { if(!buffs.containsKey(buffCrc)) { buffs.put(buffCrc, buff); @@ -481,30 +480,26 @@ class CreatureObjectSharedNP implements Persistable { public Buff removeBuff(CRC buffCrc, SWGObject target) { synchronized (buffs) { - if(buffs.containsKey(buffCrc)) { - Buff removedBuff = buffs.remove(buffCrc); - - if(removedBuff != null) { - buffs.sendDeltaMessage(target); - } - - return removedBuff; + Buff removedBuff = buffs.remove(buffCrc); + + if (removedBuff != null) { + buffs.sendDeltaMessage(target); } - return null; + return removedBuff; } } - public Buff getBuffByCrc(CRC buffCrc) { + public Stream> getBuffEntries(Predicate> predicate) { synchronized (buffs) { - return buffs.get(buffCrc); + return buffs.entrySet().stream() + .filter(predicate); } } public void adjustBuffStackCount(CRC buffCrc, int adjustment, SWGObject target) { synchronized (buffs) { - Buff buff = buffs.get(buffCrc); - buff.adjustStackCount(adjustment); + buffs.get(buffCrc).adjustStackCount(adjustment); buffs.update(buffCrc, target); } } diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index c67f5f159..84a0fed0b 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -95,7 +95,10 @@ public class PlayerObject extends IntangibleObject { } public void updatePlayTime() { - int playTime = (int) (System.currentTimeMillis() / 1000); + int currentTime = (int) (System.currentTimeMillis() / 1000); + int oldPlayTime = getPlayTime(); + int playTime = oldPlayTime + (currentTime - oldPlayTime); + play3.setPlayTime(playTime); sendDelta(3, 9, playTime); } diff --git a/src/resources/objects/tangible/TangibleObject.java b/src/resources/objects/tangible/TangibleObject.java index 6bd236ccd..82387002d 100644 --- a/src/resources/objects/tangible/TangibleObject.java +++ b/src/resources/objects/tangible/TangibleObject.java @@ -259,9 +259,10 @@ public class TangibleObject extends SWGObject { if (hasPvpFlag(PvpFlag.ATTACKABLE) || otherObject.hasPvpFlag(PvpFlag.ATTACKABLE)) { return true; + } else if (otherFaction != PvpFaction.NEUTRAL && getPvpFaction() != otherFaction) { + return getPvpStatus() != PvpStatus.ONLEAVE && otherObject.getPvpStatus() != PvpStatus.ONLEAVE; } else { - return otherFaction != PvpFaction.NEUTRAL && getPvpFaction() != otherFaction - && getPvpStatus() != PvpStatus.ONLEAVE && otherObject.getPvpStatus() != PvpStatus.ONLEAVE; + return false; } } diff --git a/src/services/combat/CombatManager.java b/src/services/combat/CombatManager.java index f6d10ac61..cd432478d 100644 --- a/src/services/combat/CombatManager.java +++ b/src/services/combat/CombatManager.java @@ -239,9 +239,7 @@ public class CombatManager extends Manager { } private void handleAttack(CreatureObject source, SWGObject target, CombatCommand command) { - CombatStatus status = canPerform(source, target, command); - - if (!handleStatus(source, status)) + if (!handleStatus(source, canPerform(source, target, command))) return; CombatAction action = new CombatAction(source.getObjectId()); @@ -339,9 +337,7 @@ public class CombatManager extends Manager { corpse.setHealth(0); killer.removeDefender(corpse); - // Let's check if the killer needs to remain in-combat... if(!killer.hasDefenders()) { - // They have no active targets they're in combat with, make them exit combat exitCombat(killer); } @@ -349,16 +345,13 @@ public class CombatManager extends Manager { corpse.setTurnScale(0); corpse.setMovementScale(0); - // We need to handle this differently, depending on whether killedCreature is a player or not if(corpse.isPlayer()) { if (corpse.hasBuff("incapWeaken")) { - // Kill this sucka immediately killCreature(killer, corpse); } else { incapacitatePlayer(killer, corpse); } } else { - // This is just a plain ol' NPC. Die! killCreature(killer, corpse); } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 90798e801..b9ff2b960 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -29,16 +29,18 @@ package services.commands; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.DelayQueue; -import java.util.concurrent.Delayed; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; +import intents.combat.CreatureKilledIntent; +import java.util.HashSet; import java.util.Locale; +import java.util.Map.Entry; import java.util.Optional; +import java.util.Set; import java.util.concurrent.ScheduledExecutorService; import network.packets.swg.zone.PlayClientEffectObjectMessage; import resources.client_info.ClientFactory; @@ -56,17 +58,16 @@ 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 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 - // TODO on deathblow, decay buffs with BuffData where DECAY_ON_PVP_DEATH == 1 private final ScheduledExecutorService executor; - private final Map> buffRemoval; + private final Set monitored; private final Map dataMap; // All CRCs are lower-cased buff names! public BuffService() { registerForIntent(BuffIntent.TYPE); registerForIntent(PlayerEventIntent.TYPE); - buffRemoval = new HashMap<>(); + monitored = new HashSet<>(); dataMap = new HashMap<>(); executor = Executors.newSingleThreadScheduledExecutor(ThreadUtilities.newThreadFactory("buff-service")); } @@ -82,8 +83,9 @@ public class BuffService extends Service { @Override public boolean start() { - // Polls buff queues every second - executor.scheduleAtFixedRate(() -> buffRemoval.values().forEach(buffQueue -> checkBuffQueue(buffQueue)), 1, 1, TimeUnit.SECONDS); + synchronized (monitored) { + executor.scheduleAtFixedRate(() -> monitored.parallelStream().forEach(creature -> checkBuffTimers(creature)), 1, 1, TimeUnit.SECONDS); + } return super.start(); } @@ -93,6 +95,7 @@ public class BuffService extends Service { switch(i.getType()) { case BuffIntent.TYPE: handleBuffIntent((BuffIntent) i); break; case PlayerEventIntent.TYPE: handlePlayerEventIntent((PlayerEventIntent) i); break; + case CreatureKilledIntent.TYPE: handleCreatureKilledIntent((CreatureKilledIntent) i); break; } } @@ -103,12 +106,9 @@ public class BuffService extends Service { return super.stop(); } - private void checkBuffQueue(DelayQueue buffQueue) { - BuffDelayed buffToRemove = buffQueue.poll(); - - if (buffToRemove != null) { - removeBuff(buffToRemove.getCreature(), buffToRemove.getBuffCrc(), true); - } + private void checkBuffTimers(CreatureObject creature) { + creature.getBuffEntries(buffEntry -> isBuffExpired(buffEntry.getValue())) + .forEach(buffEntry -> removeBuff(creature, buffEntry.getKey(), true)); } private void loadBuffs() { @@ -177,15 +177,69 @@ public class BuffService extends Service { } private void handleFirstZone(CreatureObject creature) { - creature.getBuffs().forEach((crc, buff) -> manageBuff(buff, crc, creature)); + if (hasBuffs(creature)) { + synchronized (monitored) { + monitored.add(creature); + } + } + } + + private void handleCreatureKilledIntent(CreatureKilledIntent i) { + CreatureObject corpse = i.getCorpse(); + + if (!monitored.contains(corpse)) { + return; + } + + synchronized (monitored) { + // TODO remove buffs where REMOVE_ON_DEATH == 1 + + if (corpse.isPlayer()) { + corpse.getBuffEntries(buffEntry -> isBuffDecayable(buffEntry)) + .forEach(buffEntry -> decayDuration(corpse, buffEntry)); + } + } } private void handleDisappear(CreatureObject creature) { - synchronized(buffRemoval) { - buffRemoval.remove(creature); + synchronized (monitored) { + monitored.remove(creature); + + // Buffs that aren't persistable should be removed at this point + creature.getBuffEntries(buffEntry -> isBuffPersistent(buffEntry.getKey())) + .forEach(buffEntry -> removeBuff(creature, buffEntry.getKey(), true)); } } + private boolean isBuffExpired(Buff buff) { + return buff.getDuration() >= 0 && System.currentTimeMillis() / 1000 >= buff.getEndTime(); + } + + private boolean isBuffDecayable(Entry buffEntry) { + // DECAY_ON_PVP_DEATH == 1 + return !isBuffExpired(buffEntry.getValue()) /*&& dataMap.get(buffEntry.getKey()).isDecayable()*/; + } + + private boolean isBuffPersistent(CRC crc) { +// IS_PERSISTENT == 0 + return false; + } + + private boolean hasBuffs(CreatureObject creature) { + return creature.getBuffEntries(buffEntry -> true).count() > 0; + } + + private void decayDuration(CreatureObject creature, Entry buffEntry) { + int currentDuration = buffEntry.getValue().getDuration(); + int newDuration = (int) (currentDuration * 0.10); // Duration decays with 10% + + creature.setBuffDuration(buffEntry.getKey(), creature.getPlayerObject().getPlayTime(), newDuration); + } + + private int calculatePlayTime(CreatureObject creature) { + return creature.isPlayer() ? creature.getPlayerObject().getPlayTime() : 0; + } + private void addBuff(CRC newCrc, CreatureObject receiver, CreatureObject buffer) { BuffData buffData = dataMap.get(newCrc); @@ -195,42 +249,55 @@ public class BuffService extends Service { } String groupName = buffData.getGroupName(); - Optional groupBuff = receiver.getBuffs().keySet().stream().filter(candidate -> groupName.equals(dataMap.get(candidate).groupName)).findFirst(); - int playTime = 0; + Optional> groupBuff = receiver.getBuffEntries(buffEntry -> groupName.equals(dataMap.get(buffEntry.getKey()).getGroupName())).findAny(); - if(receiver.isPlayer()) { + if (receiver.isPlayer()) { receiver.getPlayerObject().updatePlayTime(); - playTime = receiver.getPlayerObject().getPlayTime(); } - if (groupBuff.isPresent()) { // They already have a buff of this group - CRC oldCrc = groupBuff.get(); + int playTime = calculatePlayTime(receiver); + + if (groupBuff.isPresent()) { + Entry buffEntry = groupBuff.get(); + CRC oldCrc = buffEntry.getKey(); if (oldCrc.equals(newCrc)) { - int maxStackCount = buffData.getMaxStackCount(); - - if (maxStackCount > 1) { - // TODO buffs can, based on skillmods, adjust with different values - int addedStacks = 1; - int currentStacks = receiver.getBuffByCrc(oldCrc).getStackCount(); - - if (addedStacks + currentStacks <= maxStackCount) { - receiver.adjustBuffStackCount(oldCrc, addedStacks); - checkSkillMods(buffData, receiver, addedStacks); - } - } - - // We reset the duration - receiver.setBuffDuration(newCrc, playTime, (int) buffData.getDefaultDuration()); + // TODO skillmods influencing stack increment + checkStackCount(receiver, buffData, buffEntry, playTime, 1); } else if (buffData.getGroupPriority() >= dataMap.get(oldCrc).getGroupPriority()) { removeBuff(receiver, oldCrc, true); applyBuff(receiver, buffer, buffData, playTime, newCrc); } - } else { // They had no buff from this group already + } else { applyBuff(receiver, buffer, buffData, playTime, newCrc); } } + private void checkStackCount(CreatureObject receiver, BuffData buffData, Entry buffEntry, int playTime, int stackMod) { + // If it's the same buff, we need to check for stacks + int maxStackCount = buffData.getMaxStackCount(); + + if (maxStackCount < 2) { + return; + } + + int currentStacks = buffEntry.getValue().getStackCount(); + + if (stackMod + currentStacks > maxStackCount) { + stackMod = maxStackCount; + } + + CRC crc = buffEntry.getKey(); + + receiver.adjustBuffStackCount(crc, stackMod); + checkSkillMods(buffData, receiver, stackMod); + + // If the stack count was incremented, also renew the duration + if (stackMod > 0) { + receiver.setBuffDuration(crc, playTime, (int) buffData.getDefaultDuration()); + } + } + private void applyBuff(CreatureObject receiver, CreatureObject buffer, BuffData buffData, int playTime, CRC crc) { // 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; @@ -239,36 +306,17 @@ public class BuffService extends Service { checkSkillMods(buffData, receiver, 1); receiver.addBuff(crc, buff); - manageBuff(buff, crc, receiver); - - sendObjectEffect(buffData.getEffectFileName(), receiver, buffData.getParticleHardPoint()); - sendObjectEffect(buffData.getStanceParticle(), receiver, buffData.getParticleHardPoint()); + + sendParticleEffect(buffData.getEffectFileName(), receiver, buffData.getParticleHardPoint()); + sendParticleEffect(buffData.getStanceParticle(), receiver, buffData.getParticleHardPoint()); } - private void sendObjectEffect(String effectFileName, CreatureObject receiver, String hardPoint) { + private void sendParticleEffect(String effectFileName, CreatureObject receiver, String hardPoint) { if (!effectFileName.isEmpty()) { receiver.sendObserversAndSelf(new PlayClientEffectObjectMessage(effectFileName, hardPoint, receiver.getObjectId())); } } - private void manageBuff(Buff buff, CRC buffCrc, CreatureObject creature) { - if (dataMap.get(buffCrc).getDefaultDuration() > -1 && buff.getEndTime() <= 0) { - removeBuff(creature, buffCrc, true); - } else if(buff.getDuration() >= 0) { - // If this buff doesn't last forever or hasn't expired, we'll schedule it for removal in the future - synchronized (buffRemoval) { - DelayQueue buffQueue = buffRemoval.get(creature); - - if(buffQueue == null) { - buffQueue = new DelayQueue<>(); - buffRemoval.put(creature, buffQueue); - } - - buffQueue.add(new BuffDelayed(buff, buffCrc, creature)); - } - } - } - private void removeBuff(CreatureObject creature, CRC buffCrc, boolean expired) { BuffData buffData = dataMap.get(buffCrc); @@ -277,10 +325,20 @@ public class BuffService extends Service { return; } - Buff buff = creature.getBuffByCrc(buffCrc); + Optional> optionalEntry = creature.getBuffEntries(buffEntry -> buffEntry.getKey().equals(buffCrc)).findAny(); - if (buffData.getMaxStackCount() > 1 && !expired && buff.getStackCount() > 1) { - creature.adjustBuffStackCount(buffCrc, -1); + if (!optionalEntry.isPresent()) { + // It's hard for us to remove a buff that the creature doesn't have... + return; + } + + Entry buffEntry = optionalEntry.get(); + Buff buff = buffEntry.getValue(); + int maxStackCount = buffData.getMaxStackCount(); + int currentStacks = buff.getStackCount(); + + if (maxStackCount > 1 && !expired && currentStacks > 1) { + checkStackCount(creature, buffData, buffEntry, calculatePlayTime(creature), maxStackCount); } else { Buff removedBuff = creature.removeBuff(buffCrc); @@ -298,13 +356,20 @@ public class BuffService extends Service { CRC callbackCrc = new CRC(callback.toLowerCase(Locale.ENGLISH)); - if(dataMap.containsKey(callbackCrc)) { + if (dataMap.containsKey(callbackCrc)) { // Apply the callback buff addBuff(callbackCrc, creature, creature); } else { // Call the callback command script // TODO } + + // If they have no more expirable buffs, we can stop monitoring them + if (hasBuffs(creature)) { + synchronized (monitored) { + monitored.remove(creature); + } + } } } @@ -321,42 +386,6 @@ public class BuffService extends Service { new SkillModIntent(effectName, 0, (int) effectValue * valueFactor, creature).broadcast(); } - private static class BuffDelayed implements Delayed { - - private final Buff buff; - private final CRC buffCrc; - private final CreatureObject creature; - - private BuffDelayed(Buff buff, CRC buffCrc, CreatureObject creature) { - this.buff = buff; - this.buffCrc = buffCrc; - this.creature = creature; - } - - @Override - public int compareTo(Delayed o) { - return Integer.compare(buff.getEndTime(), ((BuffDelayed) o).buff.getEndTime()); - } - - @Override - public long getDelay(TimeUnit timeUnit) { - return timeUnit.convert(buff.getEndTime() - System.currentTimeMillis() / 1000, TimeUnit.MILLISECONDS); - } - - public Buff getBuff() { - return buff; - } - - public CRC getBuffCrc() { - return buffCrc; - } - - public CreatureObject getCreature() { - return creature; - } - - } - /** * @author Mads * Each instance of this class holds the base information From e1ae9b4fd10e3ef06883f31d38b6815873ac5a8e Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 25 Oct 2016 10:27:36 +0200 Subject: [PATCH 47/63] Service.registerForIntent() and unregisterForIntent() can no longer be overridden --- src/resources/control/Manager.java | 28 ++++++++++++---------------- src/resources/control/Service.java | 5 +++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/resources/control/Manager.java b/src/resources/control/Manager.java index 64f2423aa..4b4b4d1c0 100644 --- a/src/resources/control/Manager.java +++ b/src/resources/control/Manager.java @@ -27,8 +27,8 @@ ***********************************************************************************/ package resources.control; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import resources.server_info.Log; @@ -39,10 +39,10 @@ import resources.server_info.Log; */ public abstract class Manager extends Service { - private List children; + private final Set children; public Manager() { - children = new ArrayList(); + children = new HashSet<>(); } /** @@ -146,25 +146,21 @@ public abstract class Manager extends Service { /** * Adds a child to the manager's list of children. This creates a tree of - * managers that allows information to propogate freely through the network + * services that allows information to propogate freely through the network * in an easy way. - * @param m the manager to add as a child. + * @param s the service to add as a child. */ public void addChildService(Service s) { if (s == null) throw new NullPointerException("Child service cannot be null!"); synchronized (children) { - for (Service child : children) { - if (s == child || s.equals(child)) - return; - } children.add(s); } } /** - * Removes the sub-manager from the list of children - * @param m the sub-manager to remove + * Removes the service from the list of children + * @param s the service to remove */ public void removeChildService(Service s) { if (s == null) @@ -175,12 +171,12 @@ public abstract class Manager extends Service { } /** - * Returns a copied ArrayList of the children of this manager - * @return a copied ArrayList of the children of this manager + * Returns a copied {@code Set] of the children of this manager + * @return a copied {@code Set] of the children of this manager */ - public List getManagerChildren() { + public Set getManagerChildren() { synchronized (children) { - return new ArrayList(children); + return new HashSet<>(children); } } diff --git a/src/resources/control/Service.java b/src/resources/control/Service.java index f9e5c132b..a9c4052ed 100644 --- a/src/resources/control/Service.java +++ b/src/resources/control/Service.java @@ -100,7 +100,7 @@ public abstract class Service implements IntentReceiver { * Registers for the specified intent string * @param type the intent string */ - public void registerForIntent(String type) { + protected final void registerForIntent(String type) { IntentManager.getInstance().registerForIntent(type, this); } @@ -108,13 +108,14 @@ public abstract class Service implements IntentReceiver { * Unregisters for the specified intent string * @param type the intent string */ - public void unregisterForIntent(String type) { + protected final void unregisterForIntent(String type) { IntentManager.getInstance().unregisterForIntent(type, this); } /** * Callback when an intent is received from the system */ + @Override public void onIntentReceived(Intent i) { Log.w(this, "Warning: " + getClass().getSimpleName() + " did not override onIntentReceived"); } From 0d95b6b2396b61fcd13e64ec0f675eb40ff11d28 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 25 Oct 2016 10:41:52 +0200 Subject: [PATCH 48/63] Deleted an unused class --- src/utilities/DebugUtilities.java | 55 ------------------------------- 1 file changed, 55 deletions(-) delete mode 100644 src/utilities/DebugUtilities.java diff --git a/src/utilities/DebugUtilities.java b/src/utilities/DebugUtilities.java deleted file mode 100644 index 3c8ad24ee..000000000 --- a/src/utilities/DebugUtilities.java +++ /dev/null @@ -1,55 +0,0 @@ -/************************************************************************************ - * Copyright (c) 2015 /// Project SWG /// www.projectswg.com * - * * - * ProjectSWG is the first NGE emulator for Star Wars Galaxies founded on * - * July 7th, 2011 after SOE announced the official shutdown of Star Wars Galaxies. * - * Our goal is to create an emulator which will provide a server for players to * - * continue playing a game similar to the one they used to play. We are basing * - * it on the final publish of the game prior to end-game events. * - * * - * This file is part of Holocore. * - * * - * -------------------------------------------------------------------------------- * - * * - * Holocore is free software: you can redistribute it and/or modify * - * it under the terms of the GNU Affero General Public License as * - * published by the Free Software Foundation, either version 3 of the * - * License, or (at your option) any later version. * - * * - * Holocore is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU Affero General Public License for more details. * - * * - * You should have received a copy of the GNU Affero General Public License * - * along with Holocore. If not, see . * - * * - ***********************************************************************************/ -package utilities; - -import resources.control.Service; -import resources.objects.creature.CreatureObject; -import resources.player.Player; -import resources.server_info.Log; - -public class DebugUtilities { - - public static void printPlayerCharacterDebug(Service service, Player p, String identifyingText) { - CreatureObject c = p.getCreatureObject(); - boolean hasGhost = c.hasSlot("ghost"); - Log.d(service, "[%s] PLAY=%s/%d CREO=%s/%d hasGhost=%b", identifyingText, p.getUsername(), p.getUserId(), c.getName(), c.getObjectId(), hasGhost); - } - - public static void printPlayerCharacterDebug(Service service, CreatureObject c, String identifyingText) { - Player p = c.getOwner(); - String username = "null"; - int id = 0; - if (p != null) { - username = p.getUsername(); - id = p.getUserId(); - } - boolean hasGhost = c.hasSlot("ghost"); - Log.d(service, "[%s] PLAY=%s/%d CREO=%s/%d hasGhost=%b", identifyingText, username, id, c.getName(), c.getObjectId(), hasGhost); - } - -} From d53027d3dc8909fd91ab1f4d61a611e369c315e8 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Tue, 25 Oct 2016 11:14:39 +0200 Subject: [PATCH 49/63] Removed exception print for TCPServer --- src/resources/network/TCPServer.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/resources/network/TCPServer.java b/src/resources/network/TCPServer.java index b3550a5a2..66bfc3b25 100644 --- a/src/resources/network/TCPServer.java +++ b/src/resources/network/TCPServer.java @@ -249,7 +249,6 @@ public class TCPServer { callbackExecutor.execute(() -> callback.onIncomingConnection(sc.socket())); } } catch (IOException e) { - e.printStackTrace(); } } From 9b9662307de574255a17a146203bed04350459fc Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 26 Oct 2016 01:30:41 +0200 Subject: [PATCH 50/63] Buffs are now persisted --- src/resources/objects/creature/Buff.java | 27 +++++++++++++---- .../creature/CreatureObjectSharedNP.java | 24 ++++++++++++--- .../objects/player/PlayerObject.java | 1 - src/services/commands/BuffService.java | 30 ++++++++++++------- 4 files changed, 61 insertions(+), 21 deletions(-) diff --git a/src/resources/objects/creature/Buff.java b/src/resources/objects/creature/Buff.java index b4a17783b..c84648338 100644 --- a/src/resources/objects/creature/Buff.java +++ b/src/resources/objects/creature/Buff.java @@ -27,14 +27,13 @@ ***********************************************************************************/ package resources.objects.creature; -import java.io.Serializable; import java.nio.ByteBuffer; import resources.encodables.Encodable; import resources.network.NetBuffer; +import resources.network.NetBufferStream; +import resources.persistable.Persistable; -public class Buff implements Encodable, Serializable { - - private static final long serialVersionUID = 1; +public class Buff implements Encodable, Persistable { private int endTime; private float value; @@ -74,6 +73,24 @@ public class Buff implements Encodable, Serializable { return data.array(); } + @Override + public void save(NetBufferStream stream) { + stream.addInt(endTime); + stream.addFloat(value); + stream.addInt(duration); + stream.addLong(bufferId); + stream.addInt(stackCount); + } + + @Override + public void read(NetBufferStream stream) { + endTime = stream.getInt(); + value = stream.getFloat(); + duration = stream.getInt(); + bufferId = stream.getLong(); + stackCount = stream.getInt(); + } + public int getEndTime() { return endTime; } @@ -122,5 +139,5 @@ public class Buff implements Encodable, Serializable { public String toString() { return String.format("Buff[End=%d Value=%f Duration=%d Buffer=%d StackCount=%d]", endTime, value, duration, bufferId, stackCount); } - + } diff --git a/src/resources/objects/creature/CreatureObjectSharedNP.java b/src/resources/objects/creature/CreatureObjectSharedNP.java index 1056edaff..59e636bc5 100644 --- a/src/resources/objects/creature/CreatureObjectSharedNP.java +++ b/src/resources/objects/creature/CreatureObjectSharedNP.java @@ -27,10 +27,7 @@ ***********************************************************************************/ package resources.objects.creature; -import java.util.HashMap; -import java.util.Map; import java.util.Map.Entry; -import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.Predicate; import java.util.stream.Stream; @@ -604,7 +601,7 @@ class CreatureObjectSharedNP implements Persistable { @Override public void save(NetBufferStream stream) { - stream.addByte(1); + stream.addByte(2); stream.addShort(level); stream.addInt(levelHealthGranted); stream.addAscii(animation); @@ -625,6 +622,12 @@ class CreatureObjectSharedNP implements Persistable { synchronized (maxAttributes) { stream.addList(maxAttributes, (i) -> stream.addInt(i)); } + synchronized (buffs) { + stream.addMap(buffs, (e) -> { + e.getKey().save(stream); + e.getValue().save(stream); + }); + } } @Override @@ -632,6 +635,7 @@ class CreatureObjectSharedNP implements Persistable { switch(stream.getByte()) { case 0: readVersion0(stream); break; case 1: readVersion1(stream); break; + case 2: readVersion2(stream); break; } } @@ -680,4 +684,16 @@ class CreatureObjectSharedNP implements Persistable { }); } + private void readVersion2(NetBufferStream stream) { + readVersion1(stream); + stream.getList((i) -> { + CRC crc = new CRC(); + Buff buff = new Buff(); + + crc.read(stream); + buff.read(stream); + buffs.put(crc, buff); + }); + } + } diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index 84a0fed0b..ab0a5d7e7 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -44,7 +44,6 @@ import utilities.Encoder.StringType; import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.concurrent.TimeUnit; public class PlayerObject extends IntangibleObject { diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index b9ff2b960..4dfcc0c5c 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -192,22 +192,25 @@ public class BuffService extends Service { } synchronized (monitored) { - // TODO remove buffs where REMOVE_ON_DEATH == 1 - - if (corpse.isPlayer()) { + if (i.getKiller().isPlayer()) { + // PvP death - decay durations of certain buffs corpse.getBuffEntries(buffEntry -> isBuffDecayable(buffEntry)) .forEach(buffEntry -> decayDuration(corpse, buffEntry)); + } else { + // PvE death - remove certain buffs + corpse.getBuffEntries(buffEntry -> isRemovedOnDeath(buffEntry.getKey())) + .forEach(buffEntry -> decayDuration(corpse, buffEntry)); } } } private void handleDisappear(CreatureObject creature) { synchronized (monitored) { - monitored.remove(creature); - - // Buffs that aren't persistable should be removed at this point - creature.getBuffEntries(buffEntry -> isBuffPersistent(buffEntry.getKey())) - .forEach(buffEntry -> removeBuff(creature, buffEntry.getKey(), true)); + if (monitored.remove(creature)) { + // Buffs that aren't persistable should be removed at this point + creature.getBuffEntries(buffEntry -> isBuffPersistent(buffEntry.getKey())) + .forEach(buffEntry -> removeBuff(creature, buffEntry.getKey(), true)); + } } } @@ -217,11 +220,16 @@ public class BuffService extends Service { private boolean isBuffDecayable(Entry buffEntry) { // DECAY_ON_PVP_DEATH == 1 - return !isBuffExpired(buffEntry.getValue()) /*&& dataMap.get(buffEntry.getKey()).isDecayable()*/; + return !isBuffExpired(buffEntry.getValue()) /*&& dataMap.get(buffEntry.getKey()).isPvpDeathDecay()*/; } private boolean isBuffPersistent(CRC crc) { -// IS_PERSISTENT == 0 +// IS_PERSISTENT == 1 + return false; + } + + private boolean isRemovedOnDeath(CRC crc) { + // REMOVE_ON_DEATH == 1 return false; } @@ -324,7 +332,7 @@ public class BuffService extends Service { Log.e(this, "Could not remove %s from %s - buff data for it does not exist", buffCrc, creature); return; } - + System.out.println("removeBuff called"); Optional> optionalEntry = creature.getBuffEntries(buffEntry -> buffEntry.getKey().equals(buffCrc)).findAny(); if (!optionalEntry.isPresent()) { From 1dcf2bd9ab493b43046ade926620b4ec93f3d430 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 26 Oct 2016 01:47:47 +0200 Subject: [PATCH 51/63] Buffs are now persisted according to the buffs datatable --- src/services/commands/BuffService.java | 30 +++++++++++++++++--------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 4dfcc0c5c..3ce109bdd 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -132,6 +132,7 @@ public class BuffService extends Service { int particleHardpoint = buffTable.getColumnFromName("PARTICLE_HARDPOINT"); int stanceParticle = buffTable.getColumnFromName("STANCE_PARTICLE"); int callback = buffTable.getColumnFromName("CALLBACK"); + int persistent = buffTable.getColumnFromName("IS_PERSISTENT"); for(int row = 0; row < buffTable.getRowCount(); row++) { dataMap.put(new CRC(((String) buffTable.getCell(row, 0)).toLowerCase(Locale.ENGLISH)), new BuffData( @@ -152,7 +153,8 @@ public class BuffService extends Service { (String) buffTable.getCell(row, particle), (String) buffTable.getCell(row, particleHardpoint), (String) buffTable.getCell(row, stanceParticle), - (String) buffTable.getCell(row, callback) + (String) buffTable.getCell(row, callback), + (int) buffTable.getCell(row, persistent) == 1 )); } } @@ -179,7 +181,9 @@ public class BuffService extends Service { private void handleFirstZone(CreatureObject creature) { if (hasBuffs(creature)) { synchronized (monitored) { - monitored.add(creature); + if (!monitored.add(creature)) { + Log.w(this, "Redundant addition %s to monitored creatures", creature); + } } } } @@ -187,11 +191,12 @@ public class BuffService extends Service { private void handleCreatureKilledIntent(CreatureKilledIntent i) { CreatureObject corpse = i.getCorpse(); - if (!monitored.contains(corpse)) { - return; - } - synchronized (monitored) { + if (!monitored.contains(corpse)) { + return; + } + + if (i.getKiller().isPlayer()) { // PvP death - decay durations of certain buffs corpse.getBuffEntries(buffEntry -> isBuffDecayable(buffEntry)) @@ -208,7 +213,7 @@ public class BuffService extends Service { synchronized (monitored) { if (monitored.remove(creature)) { // Buffs that aren't persistable should be removed at this point - creature.getBuffEntries(buffEntry -> isBuffPersistent(buffEntry.getKey())) + creature.getBuffEntries(buffEntry -> !isBuffPersistent(buffEntry.getKey())) .forEach(buffEntry -> removeBuff(creature, buffEntry.getKey(), true)); } } @@ -224,8 +229,7 @@ public class BuffService extends Service { } private boolean isBuffPersistent(CRC crc) { -// IS_PERSISTENT == 1 - return false; + return dataMap.get(crc).isPersistent(); } private boolean isRemovedOnDeath(CRC crc) { @@ -425,8 +429,9 @@ public class BuffService extends Service { private final String particleHardPoint; private final String stanceParticle; private final String callback; + private final boolean persistent; - private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String stanceParticle, String callback) { + private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String stanceParticle, String callback, boolean persistent) { this.groupName = groupName; this.groupPriority = groupPriority; this.maxStackCount = maxStackCount; @@ -445,6 +450,7 @@ public class BuffService extends Service { this.particleHardPoint = particleHardPoint; this.stanceParticle = stanceParticle; this.callback = callback; + this.persistent = persistent; } public String getGroupName() { @@ -518,6 +524,10 @@ public class BuffService extends Service { public String getCallback() { return callback; } + + public boolean isPersistent() { + return persistent; + } } From 0f894c9ec7448f8a1da418c3f9e3b248fed1662d Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 26 Oct 2016 02:17:09 +0200 Subject: [PATCH 52/63] Buffs are now removed upon PvE death according to the buffs datatable --- src/services/commands/BuffService.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 3ce109bdd..ce692cded 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -133,6 +133,7 @@ public class BuffService extends Service { int stanceParticle = buffTable.getColumnFromName("STANCE_PARTICLE"); int callback = buffTable.getColumnFromName("CALLBACK"); int persistent = buffTable.getColumnFromName("IS_PERSISTENT"); + int removeOnDeath = buffTable.getColumnFromName("REMOVE_ON_DEATH"); for(int row = 0; row < buffTable.getRowCount(); row++) { dataMap.put(new CRC(((String) buffTable.getCell(row, 0)).toLowerCase(Locale.ENGLISH)), new BuffData( @@ -154,7 +155,8 @@ public class BuffService extends Service { (String) buffTable.getCell(row, particleHardpoint), (String) buffTable.getCell(row, stanceParticle), (String) buffTable.getCell(row, callback), - (int) buffTable.getCell(row, persistent) == 1 + (int) buffTable.getCell(row, persistent) == 1, + (int) buffTable.getCell(row, removeOnDeath) == 1 )); } } @@ -203,7 +205,7 @@ public class BuffService extends Service { .forEach(buffEntry -> decayDuration(corpse, buffEntry)); } else { // PvE death - remove certain buffs - corpse.getBuffEntries(buffEntry -> isRemovedOnDeath(buffEntry.getKey())) + corpse.getBuffEntries(buffEntry -> isBuffRemovedOnDeath(buffEntry.getKey())) .forEach(buffEntry -> decayDuration(corpse, buffEntry)); } } @@ -232,9 +234,8 @@ public class BuffService extends Service { return dataMap.get(crc).isPersistent(); } - private boolean isRemovedOnDeath(CRC crc) { - // REMOVE_ON_DEATH == 1 - return false; + private boolean isBuffRemovedOnDeath(CRC crc) { + return dataMap.get(crc).isRemovedOnDeath(); } private boolean hasBuffs(CreatureObject creature) { @@ -430,8 +431,9 @@ public class BuffService extends Service { private final String stanceParticle; private final String callback; private final boolean persistent; + private final boolean removedOnDeath; - private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String stanceParticle, String callback, boolean persistent) { + private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String stanceParticle, String callback, boolean persistent, boolean removedOnDeath) { this.groupName = groupName; this.groupPriority = groupPriority; this.maxStackCount = maxStackCount; @@ -451,6 +453,7 @@ public class BuffService extends Service { this.stanceParticle = stanceParticle; this.callback = callback; this.persistent = persistent; + this.removedOnDeath = removedOnDeath; } public String getGroupName() { @@ -528,6 +531,10 @@ public class BuffService extends Service { public boolean isPersistent() { return persistent; } + + public boolean isRemovedOnDeath() { + return removedOnDeath; + } } From 9b696b935904254ee9a6f16deb60ed29d936d80e Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 26 Oct 2016 02:21:02 +0200 Subject: [PATCH 53/63] Buff durations now decay on PvP death according to the buffs datatable --- src/services/commands/BuffService.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index ce692cded..95bb3662f 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -134,6 +134,7 @@ public class BuffService extends Service { int callback = buffTable.getColumnFromName("CALLBACK"); int persistent = buffTable.getColumnFromName("IS_PERSISTENT"); int removeOnDeath = buffTable.getColumnFromName("REMOVE_ON_DEATH"); + int decayOnPvpDeath = buffTable.getColumnFromName("DECAY_ON_PVP_DEATH"); for(int row = 0; row < buffTable.getRowCount(); row++) { dataMap.put(new CRC(((String) buffTable.getCell(row, 0)).toLowerCase(Locale.ENGLISH)), new BuffData( @@ -156,7 +157,8 @@ public class BuffService extends Service { (String) buffTable.getCell(row, stanceParticle), (String) buffTable.getCell(row, callback), (int) buffTable.getCell(row, persistent) == 1, - (int) buffTable.getCell(row, removeOnDeath) == 1 + (int) buffTable.getCell(row, removeOnDeath) == 1, + (int) buffTable.getCell(row, decayOnPvpDeath) == 1 )); } } @@ -222,12 +224,13 @@ public class BuffService extends Service { } private boolean isBuffExpired(Buff buff) { - return buff.getDuration() >= 0 && System.currentTimeMillis() / 1000 >= buff.getEndTime(); + return buff.getDuration() >= 0 && + System.currentTimeMillis() / 1000 >= buff.getEndTime(); } private boolean isBuffDecayable(Entry buffEntry) { - // DECAY_ON_PVP_DEATH == 1 - return !isBuffExpired(buffEntry.getValue()) /*&& dataMap.get(buffEntry.getKey()).isPvpDeathDecay()*/; + return !isBuffExpired(buffEntry.getValue()) && + dataMap.get(buffEntry.getKey()).isDecayOnPvpDeath(); } private boolean isBuffPersistent(CRC crc) { @@ -432,8 +435,9 @@ public class BuffService extends Service { private final String callback; private final boolean persistent; private final boolean removedOnDeath; + private final boolean decayOnPvpDeath; - private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String stanceParticle, String callback, boolean persistent, boolean removedOnDeath) { + private BuffData(String groupName, int groupPriority, int maxStackCount, String effect1Name, float effect1Value, String effect2Name, float effect2Value, String effect3Name, float effect3Value, String effect4Name, float effect4Value, String effect5Name, float effect5Value, float defaultDuration, String effectFileName, String particleHardPoint, String stanceParticle, String callback, boolean persistent, boolean removedOnDeath, boolean decayOnPvpDeath) { this.groupName = groupName; this.groupPriority = groupPriority; this.maxStackCount = maxStackCount; @@ -454,6 +458,7 @@ public class BuffService extends Service { this.callback = callback; this.persistent = persistent; this.removedOnDeath = removedOnDeath; + this.decayOnPvpDeath = decayOnPvpDeath; } public String getGroupName() { @@ -535,6 +540,10 @@ public class BuffService extends Service { public boolean isRemovedOnDeath() { return removedOnDeath; } + + public boolean isDecayOnPvpDeath() { + return decayOnPvpDeath; + } } From 3eec9e740d4ad47e7e9fa077ef3923c3399f0abc Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 26 Oct 2016 04:15:49 +0200 Subject: [PATCH 54/63] Scripts.invoke() now throws FileNotFoundException if script file wasn't found --- scripts/commands/generic/debug.js | 3 -- scripts/objects/load_creature.js | 7 --- .../commands/callbacks/QaToolCmdCallback.java | 9 +++- .../commands/callbacks/ServerCmdCallback.java | 9 ---- src/resources/radial/Radials.java | 16 +++++- src/services/commands/BuffService.java | 53 +++++++++++-------- src/services/commands/CommandService.java | 8 ++- src/services/objects/ObjectManager.java | 3 -- src/services/sui/SuiService.java | 9 +++- src/utilities/Scripts.java | 7 +-- 10 files changed, 70 insertions(+), 54 deletions(-) delete mode 100644 scripts/commands/generic/debug.js delete mode 100644 scripts/objects/load_creature.js diff --git a/scripts/commands/generic/debug.js b/scripts/commands/generic/debug.js deleted file mode 100644 index 06fa34414..000000000 --- a/scripts/commands/generic/debug.js +++ /dev/null @@ -1,3 +0,0 @@ -function executeCommand(galacticManager, player, target, args) { - print("Hello"); -} \ No newline at end of file diff --git a/scripts/objects/load_creature.js b/scripts/objects/load_creature.js deleted file mode 100644 index 77109683c..000000000 --- a/scripts/objects/load_creature.js +++ /dev/null @@ -1,7 +0,0 @@ -function onLoad(creature) { - var basicDance = "startDance+basic"; - if(!creature.hasAbility(basicDance)) { - creature.addAbility(basicDance); - } - creature.addOptionFlags((Java.type("resources.objects.tangible.OptionFlag")).HAM_BAR); -} \ No newline at end of file diff --git a/src/resources/commands/callbacks/QaToolCmdCallback.java b/src/resources/commands/callbacks/QaToolCmdCallback.java index e677d2f2a..ea9cc4579 100644 --- a/src/resources/commands/callbacks/QaToolCmdCallback.java +++ b/src/resources/commands/callbacks/QaToolCmdCallback.java @@ -35,6 +35,7 @@ import intents.object.ObjectCreatedIntent; import intents.object.ObjectTeleportIntent; import intents.object.CreateStaticItemIntent; import intents.player.DeleteCharacterIntent; +import java.io.FileNotFoundException; import resources.Location; import resources.Terrain; import resources.commands.ICmdCallback; @@ -62,6 +63,8 @@ import utilities.Scripts; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; /** * Created by Waverunner on 8/19/2015 @@ -97,7 +100,11 @@ public class QaToolCmdCallback implements ICmdCallback { recoverPlayer(galacticManager.getObjectManager(), galacticManager.getPlayerManager(), player, args.substring(args.indexOf(' ') + 1)); break; case "details": - Scripts.invoke("commands/helper/qatool/details", "sendDetails", player, target, args.split(" ")); + try { + Scripts.invoke("commands/helper/qatool/details", "sendDetails", player, target, args.split(" ")); + } catch (FileNotFoundException ex) { + Log.e("QATool", "sendDetails qatool script not found!"); + } break; case "xp": if(command.length == 3) diff --git a/src/resources/commands/callbacks/ServerCmdCallback.java b/src/resources/commands/callbacks/ServerCmdCallback.java index 8e7b0bd24..bda89f6b8 100644 --- a/src/resources/commands/callbacks/ServerCmdCallback.java +++ b/src/resources/commands/callbacks/ServerCmdCallback.java @@ -39,7 +39,6 @@ import resources.sui.SuiEvent; import resources.sui.SuiInputBox; import resources.sui.SuiListBox; import resources.sui.SuiMessageBox; -import utilities.Scripts; import services.galaxy.GalacticManager; import java.util.Map; @@ -50,10 +49,6 @@ public class ServerCmdCallback implements ICmdCallback { @Override public void execute(GalacticManager galacticManager, Player player, SWGObject target, String args) { - if (args.startsWith("debug")) { - debug(galacticManager, player, target, args); - return; - } SuiListBox listBox = new SuiListBox(SuiButtons.OK_CANCEL, "Server Management", "Select the management function you wish to perform from the list."); listBox.addListItem("Kick Player"); @@ -66,10 +61,6 @@ public class ServerCmdCallback implements ICmdCallback { listBox.display(player); } - private void debug(GalacticManager galacticManager, Player player, SWGObject target, String args) { - Scripts.invoke("commands/generic/debug", "executeCommand", galacticManager, player, target, args); - } - private static class ServerSuiCallback implements ISuiCallback { @Override public void handleEvent(Player player, SWGObject actor, SuiEvent event, Map parameters) { diff --git a/src/resources/radial/Radials.java b/src/resources/radial/Radials.java index fc95a16de..821173a68 100644 --- a/src/resources/radial/Radials.java +++ b/src/resources/radial/Radials.java @@ -27,11 +27,15 @@ ***********************************************************************************/ package resources.radial; +import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; import resources.objects.SWGObject; import resources.player.Player; +import resources.server_info.Log; import utilities.Scripts; public class Radials { @@ -40,12 +44,20 @@ public class Radials { public static List getRadialOptions(String script, Player player, SWGObject target, Object ... args) { List options = new ArrayList<>(); - Scripts.invoke(SCRIPT_PREFIX + script, "getOptions", options, player, target, args); + try { + Scripts.invoke(SCRIPT_PREFIX + script, "getOptions", options, player, target, args); + } catch (FileNotFoundException ex) { + Log.w("Radials", "Couldn't retrieve radial options from %s for object %s because the script couldn't be found", SCRIPT_PREFIX + script, target); + } return options; } public static void handleSelection(String script, Player player, SWGObject target, RadialItem selection, Object ... args) { - Scripts.invoke(SCRIPT_PREFIX + script, "handleSelection", player, target, selection, args); + try { + Scripts.invoke(SCRIPT_PREFIX + script, "handleSelection", player, target, selection, args); + } catch (FileNotFoundException ex) { + Log.w("Radials", "Can't handle selection %s on object %s because the script couldn't be found"); + } } } diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index 95bb3662f..fa4c1e80f 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -36,12 +36,15 @@ import intents.BuffIntent; import intents.PlayerEventIntent; import intents.SkillModIntent; import intents.combat.CreatureKilledIntent; +import java.io.FileNotFoundException; import java.util.HashSet; import java.util.Locale; import java.util.Map.Entry; import java.util.Optional; import java.util.Set; import java.util.concurrent.ScheduledExecutorService; +import java.util.logging.Level; +import java.util.logging.Logger; import network.packets.swg.zone.PlayClientEffectObjectMessage; import resources.client_info.ClientFactory; import resources.client_info.visitors.DatatableData; @@ -51,13 +54,14 @@ import resources.control.Service; import resources.objects.creature.Buff; import resources.objects.creature.CreatureObject; import resources.server_info.Log; +import utilities.Scripts; import utilities.ThreadUtilities; 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 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 + // 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" private final ScheduledExecutorService executor; private final Set monitored; @@ -199,8 +203,7 @@ public class BuffService extends Service { if (!monitored.contains(corpse)) { return; } - - + if (i.getKiller().isPlayer()) { // PvP death - decay durations of certain buffs corpse.getBuffEntries(buffEntry -> isBuffDecayable(buffEntry)) @@ -208,7 +211,7 @@ public class BuffService extends Service { } else { // PvE death - remove certain buffs corpse.getBuffEntries(buffEntry -> isBuffRemovedOnDeath(buffEntry.getKey())) - .forEach(buffEntry -> decayDuration(corpse, buffEntry)); + .forEach(buffEntry -> removeBuff(corpse, buffEntry.getKey(), true)); } } } @@ -340,11 +343,10 @@ public class BuffService extends Service { Log.e(this, "Could not remove %s from %s - buff data for it does not exist", buffCrc, creature); return; } - System.out.println("removeBuff called"); + Optional> optionalEntry = creature.getBuffEntries(buffEntry -> buffEntry.getKey().equals(buffCrc)).findAny(); if (!optionalEntry.isPresent()) { - // It's hard for us to remove a buff that the creature doesn't have... return; } @@ -362,23 +364,8 @@ public class BuffService extends Service { return; } - // Remove skillmods checkSkillMods(buffData, creature, -removedBuff.getStackCount()); - - String callback = buffData.getCallback(); - - if(callback.equals("none")) - return; - - CRC callbackCrc = new CRC(callback.toLowerCase(Locale.ENGLISH)); - - if (dataMap.containsKey(callbackCrc)) { - // Apply the callback buff - addBuff(callbackCrc, creature, creature); - } else { - // Call the callback command script - // TODO - } + checkCallback(buffData, creature); // If they have no more expirable buffs, we can stop monitoring them if (hasBuffs(creature)) { @@ -389,7 +376,29 @@ public class BuffService extends Service { } } + private void checkCallback(BuffData buffData, CreatureObject creature) { + String callback = buffData.getCallback(); + + if (callback.equals("none")) { + return; + } + + CRC callbackCrc = new CRC(callback.toLowerCase(Locale.ENGLISH)); + + if (dataMap.containsKey(callbackCrc)) { + addBuff(callbackCrc, creature, creature); + } else { + try { + Scripts.invoke("buffs/callback" + callback, callback, creature); + } catch (FileNotFoundException ex) { + Log.w(this, "Callback script %s doesn't exist", callback); + } + } + } + private void checkSkillMods(BuffData buffData, CreatureObject creature, int valueFactor) { + // TODO Check effect1name == "group". If yes, every group member within 100m range (maybe just awareness range?) recive the buff. + // TODO once outside range, buff needs removal sendSkillModIntent(creature, buffData.getEffect1Name(), buffData.getEffect1Value(), valueFactor); sendSkillModIntent(creature, buffData.getEffect2Name(), buffData.getEffect2Value(), valueFactor); sendSkillModIntent(creature, buffData.getEffect3Name(), buffData.getEffect3Value(), valueFactor); diff --git a/src/services/commands/CommandService.java b/src/services/commands/CommandService.java index 255762c5e..ca3188582 100644 --- a/src/services/commands/CommandService.java +++ b/src/services/commands/CommandService.java @@ -32,6 +32,7 @@ import intents.chat.ChatBroadcastIntent; import intents.chat.ChatCommandIntent; import intents.network.GalacticPacketIntent; import intents.player.PlayerTransformedIntent; +import java.io.FileNotFoundException; import network.packets.Packet; import network.packets.swg.zone.object_controller.CommandQueueDequeue; import network.packets.swg.zone.object_controller.CommandQueueEnqueue; @@ -71,6 +72,8 @@ import java.util.Set; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.logging.Level; +import java.util.logging.Logger; import network.packets.swg.zone.object_controller.CommandTimer; import resources.combat.HitType; import resources.commands.DefaultPriority; @@ -329,7 +332,10 @@ public class CommandService extends Service { } } else - Scripts.invoke("commands/generic/" + command.getDefaultScriptCallback(), "executeCommand", galacticManager, player, target, args); + try { + Scripts.invoke("commands/generic/" + command.getDefaultScriptCallback(), "executeCommand", galacticManager, player, target, args); + } catch (FileNotFoundException ex) { + } } private void loadBaseCommands() { diff --git a/src/services/objects/ObjectManager.java b/src/services/objects/ObjectManager.java index d2e707818..ee18ec11a 100644 --- a/src/services/objects/ObjectManager.java +++ b/src/services/objects/ObjectManager.java @@ -128,9 +128,6 @@ public class ObjectManager extends Manager { } private void loadObject(SWGObject obj) { - if (obj instanceof CreatureObject && ((CreatureObject) obj).isPlayer()) - Scripts.invoke("objects/load_creature", "onLoad", obj); - updateBuildoutParent(obj); addChildrenObjects(obj); } diff --git a/src/services/sui/SuiService.java b/src/services/sui/SuiService.java index 435c6132a..4a90ca42f 100644 --- a/src/services/sui/SuiService.java +++ b/src/services/sui/SuiService.java @@ -29,6 +29,7 @@ package services.sui; import intents.network.GalacticPacketIntent; import intents.sui.SuiWindowIntent; +import java.io.FileNotFoundException; import network.packets.Packet; import network.packets.swg.SWGPacket; import network.packets.swg.zone.server_ui.SuiCreatePageMessage; @@ -49,6 +50,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; public class SuiService extends Service { @@ -149,7 +152,11 @@ public class SuiService extends Service { if (window.hasCallbackFunction(callback)) { String script = window.getCallbackScript(callback); - Scripts.invoke(script, callback, player, player.getCreatureObject(), event, parameters); + try { + Scripts.invoke(script, callback, player, player.getCreatureObject(), event, parameters); + } catch (FileNotFoundException ex) { + Log.e(this, "Callback script %s not found", script); + } } else if (window.hasJavaCallback(callback)) { ISuiCallback suiCallback = window.getJavaCallback(callback); suiCallback.handleEvent(player, player.getCreatureObject(), event, parameters); diff --git a/src/utilities/Scripts.java b/src/utilities/Scripts.java index 876e5a586..4d0414a06 100644 --- a/src/utilities/Scripts.java +++ b/src/utilities/Scripts.java @@ -70,16 +70,13 @@ public class Scripts { * @param args to pass to the function. * @return whatever the function returns. If the function doesn't have a return statement, this method returns {@code null}. * If an exception occurs, {@code null} is returned. + * @throws java.io.FileNotFoundException if the script file wasn't found */ @SuppressWarnings("unchecked") - public static T invoke(String script, String function, Object... args) { + public static T invoke(String script, String function, Object... args) throws FileNotFoundException { try { ENGINE.eval(new InputStreamReader(new FileInputStream(SCRIPTS + script + EXTENSION), StandardCharsets.UTF_8)); return (T) INVOCABLE.invokeFunction(function, args); - } catch (FileNotFoundException e) { - // No need to print anything, this is a common error. - // Returning null is all that's necessary - return null; } catch (Throwable t) { Log.e("Scripts", "Error invoking script: " + script + " with function: " + function); Log.e("Scripts", " Args: " + Arrays.toString(args)); From 73cadf86ee63306f8019d3a1b6640b2290acfc8d Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 26 Oct 2016 05:16:04 +0200 Subject: [PATCH 55/63] Infinite duration buffs are now removed if adding them while already present --- src/services/commands/BuffService.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index fa4c1e80f..bba111c48 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -263,7 +263,7 @@ public class BuffService extends Service { BuffData buffData = dataMap.get(newCrc); if (buffData == null) { - Log.e(this, "Could not add %s to %s - buff data does not exist", newCrc, receiver); + Log.e(this, "Could not add %s to %s - unrecognised buff name", newCrc, receiver); return; } @@ -281,8 +281,13 @@ public class BuffService extends Service { CRC oldCrc = buffEntry.getKey(); if (oldCrc.equals(newCrc)) { - // TODO skillmods influencing stack increment - checkStackCount(receiver, buffData, buffEntry, playTime, 1); + // TODO if buff duration -1, remove the buff + if (buffData.getDefaultDuration() == -1) { + removeBuff(receiver, oldCrc, true); + } else { + // TODO skillmods influencing stack increment + checkStackCount(receiver, buffData, buffEntry, playTime, 1); + } } else if (buffData.getGroupPriority() >= dataMap.get(oldCrc).getGroupPriority()) { removeBuff(receiver, oldCrc, true); applyBuff(receiver, buffer, buffData, playTime, newCrc); @@ -365,7 +370,7 @@ public class BuffService extends Service { } checkSkillMods(buffData, creature, -removedBuff.getStackCount()); - checkCallback(buffData, creature); + checkCallback(buffCrc, buffData, creature); // If they have no more expirable buffs, we can stop monitoring them if (hasBuffs(creature)) { @@ -376,7 +381,7 @@ public class BuffService extends Service { } } - private void checkCallback(BuffData buffData, CreatureObject creature) { + private void checkCallback(CRC crc, BuffData buffData, CreatureObject creature) { String callback = buffData.getCallback(); if (callback.equals("none")) { @@ -391,13 +396,13 @@ public class BuffService extends Service { try { Scripts.invoke("buffs/callback" + callback, callback, creature); } catch (FileNotFoundException ex) { - Log.w(this, "Callback script %s doesn't exist", callback); + Log.w(this, "Callback script %s doesn't exist - buff %s won't behave as expected", callback, crc); } } } private void checkSkillMods(BuffData buffData, CreatureObject creature, int valueFactor) { - // TODO Check effect1name == "group". If yes, every group member within 100m range (maybe just awareness range?) recive the buff. + // TODO Check effect1name == "group". If yes, every group member within 100m range (maybe just the ones aware of the buffer) receive the buff. // TODO once outside range, buff needs removal sendSkillModIntent(creature, buffData.getEffect1Name(), buffData.getEffect1Value(), valueFactor); sendSkillModIntent(creature, buffData.getEffect2Name(), buffData.getEffect2Value(), valueFactor); From 66bf184fef4f45e6e32f6cf9f2a7284c7f83c14d Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Wed, 26 Oct 2016 05:23:12 +0200 Subject: [PATCH 56/63] Removed a TODO for a feature that's already implemented --- src/services/commands/BuffService.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index bba111c48..fe4200b5c 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -281,7 +281,6 @@ public class BuffService extends Service { CRC oldCrc = buffEntry.getKey(); if (oldCrc.equals(newCrc)) { - // TODO if buff duration -1, remove the buff if (buffData.getDefaultDuration() == -1) { removeBuff(receiver, oldCrc, true); } else { From 67e36ab69d5fd3d96ae2c985b74898d3cd1afa97 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 27 Oct 2016 01:30:08 +0200 Subject: [PATCH 57/63] Fixed an issue where FileNotFoundException for scripts was always printed --- src/utilities/Scripts.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utilities/Scripts.java b/src/utilities/Scripts.java index 4d0414a06..40a3efd16 100644 --- a/src/utilities/Scripts.java +++ b/src/utilities/Scripts.java @@ -77,7 +77,7 @@ public class Scripts { try { ENGINE.eval(new InputStreamReader(new FileInputStream(SCRIPTS + script + EXTENSION), StandardCharsets.UTF_8)); return (T) INVOCABLE.invokeFunction(function, args); - } catch (Throwable t) { + } catch (ScriptException | NoSuchMethodException t) { Log.e("Scripts", "Error invoking script: " + script + " with function: " + function); Log.e("Scripts", " Args: " + Arrays.toString(args)); Log.e("Scripts", t); From 10b240b754361bbdba3ed713f2f074b835950549 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 27 Oct 2016 03:38:35 +0200 Subject: [PATCH 58/63] Fixed an issue where the buffs thread wouldn't shut down upon start fail --- src/services/commands/BuffService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/commands/BuffService.java b/src/services/commands/BuffService.java index fe4200b5c..08bb04dd5 100644 --- a/src/services/commands/BuffService.java +++ b/src/services/commands/BuffService.java @@ -104,10 +104,10 @@ public class BuffService extends Service { } @Override - public boolean stop() { + public boolean terminate() { executor.shutdown(); - return super.stop(); + return super.terminate(); } private void checkBuffTimers(CreatureObject creature) { From 7d961296befe153c0501c7f00c02720059dd8330 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 27 Oct 2016 03:42:14 +0200 Subject: [PATCH 59/63] Added exception logging to resources.network.TCPServer --- src/resources/network/TCPServer.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/resources/network/TCPServer.java b/src/resources/network/TCPServer.java index 66bfc3b25..e9db197b2 100644 --- a/src/resources/network/TCPServer.java +++ b/src/resources/network/TCPServer.java @@ -33,6 +33,7 @@ import java.net.InetSocketAddress; import java.net.Socket; import java.net.SocketAddress; import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousCloseException; import java.nio.channels.ClosedByInterruptException; import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectableChannel; @@ -248,7 +249,10 @@ public class TCPServer { if (callback != null) callbackExecutor.execute(() -> callback.onIncomingConnection(sc.socket())); } + } catch (AsynchronousCloseException e) { + } catch (IOException e) { + Log.a(this, e); } } From 08b35280587f104f48b1e9e1ff071b98c31f3553 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 27 Oct 2016 03:43:09 +0200 Subject: [PATCH 60/63] The FactionService Executor is now shut down in terminate() --- src/services/faction/FactionService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/faction/FactionService.java b/src/services/faction/FactionService.java index 3f080407a..9dfa54b59 100644 --- a/src/services/faction/FactionService.java +++ b/src/services/faction/FactionService.java @@ -71,11 +71,11 @@ public final class FactionService extends Service { } @Override - public boolean stop() { + public boolean terminate() { // If some were in the middle of switching, finish the switches immediately executor.shutdownNow().forEach(runnable -> runnable.run()); - return super.stop(); + return super.terminate(); } private void handleFactionIntent(FactionIntent i) { From f9fcb22b9dca446d068cd0696d4a77d114be2711 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 27 Oct 2016 03:44:05 +0200 Subject: [PATCH 61/63] Removed unused imports in QaToolCmdCallback --- src/resources/commands/callbacks/QaToolCmdCallback.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/resources/commands/callbacks/QaToolCmdCallback.java b/src/resources/commands/callbacks/QaToolCmdCallback.java index ea9cc4579..7724ba57f 100644 --- a/src/resources/commands/callbacks/QaToolCmdCallback.java +++ b/src/resources/commands/callbacks/QaToolCmdCallback.java @@ -31,7 +31,6 @@ import intents.chat.ChatBroadcastIntent; import intents.experience.ExperienceIntent; import intents.network.CloseConnectionIntent; import intents.object.DestroyObjectIntent; -import intents.object.ObjectCreatedIntent; import intents.object.ObjectTeleportIntent; import intents.object.CreateStaticItemIntent; import intents.player.DeleteCharacterIntent; @@ -55,7 +54,6 @@ import resources.sui.SuiInputBox; import resources.sui.SuiListBox; import resources.sui.SuiMessageBox; import services.galaxy.GalacticManager; -import services.objects.ObjectCreator; import services.objects.ObjectManager; import services.player.PlayerManager; import utilities.Scripts; @@ -63,8 +61,6 @@ import utilities.Scripts; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; -import java.util.logging.Level; -import java.util.logging.Logger; /** * Created by Waverunner on 8/19/2015 From 4ff6b474ed1fa638fa62540b886ae73b1230c955 Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 27 Oct 2016 03:45:06 +0200 Subject: [PATCH 62/63] Shortened CRC.equals() a bit --- src/resources/common/CRC.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/resources/common/CRC.java b/src/resources/common/CRC.java index 8b5a7097c..9f22f2032 100644 --- a/src/resources/common/CRC.java +++ b/src/resources/common/CRC.java @@ -149,11 +149,8 @@ public class CRC implements Encodable, Persistable { if (getClass() != obj.getClass()) { return false; } - final CRC other = (CRC) obj; - if (this.crc != other.crc) { - return false; - } - return true; + + return crc == ((CRC) obj).crc; } public static int getCrc(String input) { From 16f6c6fdb684e8718891832978851407eebcfeed Mon Sep 17 00:00:00 2001 From: Mads Boddum Date: Thu, 27 Oct 2016 03:52:37 +0200 Subject: [PATCH 63/63] CreatureObject.isEnemy() is now more readable --- .../objects/creature/CreatureObject.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index ff386e057..fa2a5e4f9 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -795,19 +795,19 @@ public class CreatureObject extends TangibleObject { @Override public boolean isEnemy(TangibleObject otherObject) { - boolean enemy = super.isEnemy(otherObject); - - if (enemy && otherObject instanceof CreatureObject) { - PvpStatus ourStatus = getPvpStatus(); - PvpStatus theirStatus = otherObject.getPvpStatus(); - - if (isPlayer() && ((CreatureObject) otherObject).isPlayer()) { - // They are enemies if both players are SF - return ourStatus == PvpStatus.SPECIALFORCES && theirStatus == PvpStatus.SPECIALFORCES; - } + if (!super.isEnemy(otherObject)) { + return false; } - - return enemy; + + if (!(otherObject instanceof CreatureObject)) { + return false; + } + + if (!isPlayer() || !((CreatureObject) otherObject).isPlayer()) { + return false; + } + + return getPvpStatus() == PvpStatus.SPECIALFORCES && otherObject.getPvpStatus() == PvpStatus.SPECIALFORCES; } @Override