Merge holocore/quality_assurance into quality_assurance

Conflicts:
	src/resources/objects/creature/CreatureObject.java
	src/resources/objects/custom/DefaultAIObject.java
	src/resources/objects/tangible/TangibleObject.java
This commit is contained in:
Mads Boddum
2016-05-20 19:33:00 +02:00
37 changed files with 5060 additions and 10114 deletions

View File

@@ -0,0 +1,38 @@
function executeCommand(galacticManager, player, target, args) {
var actor = player.getCreatureObject();
if(actor === null || target === null || actor === target || (!target instanceof Java.type("resources.objects.weapon.WeaponObject"))) {
return;
}
var newContainer = galacticManager.getObjectManager().getObjectById(args.split(" ")[1]);
var ContainerResult = Java.type("resources.containers.ContainerResult");
var containerResult = target.moveToContainer(actor, newContainer);
switch (containerResult) {
case ContainerResult.SUCCESS:
var defaultWeapon = actor.getDefaultWeapon();
if (actor.getEquipmentList().contains(target)) {
actor.removeEquipment(target);
actor.addEquipment(defaultWeapon);
actor.setEquippedWeapon(defaultWeapon);
} else if (newContainer === actor) {
actor.removeEquipment(defaultWeapon);
actor.addEquipment(target);
actor.setEquippedWeapon(target);
}
break;
case ContainerResult.CONTAINER_FULL:
// TODO container03_prose if container is named
intentFactory.sendSystemMessage(player, "@container_error_message:container03");
break;
case ContainerResult.NO_PERMISSION:
// TODO container08_prose if container is named
intentFactory.sendSystemMessage(player, "@container_error_message:container08");
break;
default:
print("Unhandled ContainerResult " + containerResult + " in transferItemMisc!");
break;
}
}

View File

@@ -1,15 +1,23 @@
function sendDetails(player, object) {
function sendDetails(player, object, args) {
if (object == null) {
intentFactory.sendSystemMessage(player, "Null target");
return;
}
if (args.length >= 2 && args[1].equalsIgnoreCase("observers")) {
intentFactory.sendSystemMessage(player, "Aware: " + object.getObjectsAware());
intentFactory.sendSystemMessage(player, "Observers: " + object.getObservers());
return;
}
intentFactory.sendSystemMessage(player, object.getName() + " - " + object.getClass().getSimpleName() + " [" + object.getObjectId() + "]");
intentFactory.sendSystemMessage(player, " STR: " + object.getStringId() + " / " + object.getDetailStringId());
intentFactory.sendSystemMessage(player, " Template: " + object.getTemplate());
intentFactory.sendSystemMessage(player, " GOT: " + object.getGameObjectType());
intentFactory.sendSystemMessage(player, " Classification: " + object.getClassification());
intentFactory.sendSystemMessage(player, " Load Range: " + object.getLoadRange());
intentFactory.sendSystemMessage(player, " Health/Action: " + object.getHealth() + "/" + object.getAction());
intentFactory.sendSystemMessage(player, " Base Health/Action: " + object.getBaseHealth() + "/" + object.getBaseAction());
intentFactory.sendSystemMessage(player, " Max Health/Action: " + object.getMaxHealth() + "/" + object.getMaxAction());
if (object instanceof Java.type("resources.objects.creature.CreatureObject")) {
intentFactory.sendSystemMessage(player, " Health/Action: " + object.getHealth() + "/" + object.getAction());
intentFactory.sendSystemMessage(player, " Max Health/Action: " + object.getMaxHealth() + "/" + object.getMaxAction());
}
if (object instanceof Java.type("resources.objects.tangible.TangibleObject"))
intentFactory.sendSystemMessage(player, " PVP Flags: " + object.getPvpFlags())
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -26,9 +26,9 @@ object/building/naboo/shared_cloning_facility_naboo.iff cloning_nab
object/building/military/shared_outpost_cloning_facility.iff cloning_outpost
object/building/military/shared_outpost_cloning_facility_s02.iff cloning_outpost_2
object/building/military/shared_outpost_cloning_facility_s03.iff cloning_outpost_3
object/building/player/city/shared_cloning_corellia.iff cloning_player
object/building/player/city/shared_cloning_naboo.iff cloning_player
object/building/player/city/shared_cloning_tatooine.iff cloning_player
object/building/player/city/shared_cloning_corellia.iff cloning_player_cor
object/building/player/city/shared_cloning_naboo.iff cloning_player_nab
object/building/player/city/shared_cloning_tatooine.iff cloning_player_tat
object/building/tatooine/shared_cloning_facility_tatooine.iff cloning_tat
object/building/tatooine/shared_cloning_facility_tatooine_small.iff cloning_tat_sml
object/building/player/city/shared_garden_corellia_lrg_01.iff decoration_player_lrg

View File

@@ -1,34 +0,0 @@
package intents.object;
import resources.control.Intent;
import resources.objects.SWGObject;
public class UpdateObjectAwareness extends Intent {
public static final String TYPE = "UpdateObjectAwareness";
private SWGObject object;
private boolean inAwareness;
public UpdateObjectAwareness(SWGObject object, boolean inAwareness) {
super(TYPE);
setObject(object);
}
public void setObject(SWGObject object) {
this.object = object;
}
public void setInAwareness(boolean inAwareness) {
this.inAwareness = inAwareness;
}
public SWGObject getObject() {
return object;
}
public boolean isInAwareness() {
return inAwareness;
}
}

View File

@@ -50,6 +50,7 @@ public class CommandQueueDequeue extends ObjectController {
public void decode(ByteBuffer data) {
decodeHeader(data);
getInt(data); // CU Object Controller spacer
counter = getInt(data);
timer = getFloat(data);
error = getInt(data);
@@ -57,8 +58,9 @@ public class CommandQueueDequeue extends ObjectController {
}
public ByteBuffer encode() {
ByteBuffer data = ByteBuffer.allocate(HEADER_LENGTH + 16);
ByteBuffer data = ByteBuffer.allocate(HEADER_LENGTH + 20);
encodeHeader(data);
addInt(data, 0); // CU Object Controller spacer
addInt(data, counter);
addFloat(data, timer);
addInt(data, error);

View File

@@ -56,6 +56,7 @@ public class CommandQueueEnqueue extends ObjectController {
decodeHeader(data);
getInt(data); // CU spacer
counter = getInt(data);
System.out.println("counter: " + counter);
crc = getInt(data);
targetId = getLong(data);
arguments = getUnicode(data);

View File

@@ -64,10 +64,11 @@ public class CombatAction extends ObjectController {
@Override
public ByteBuffer encode() {
NetBuffer data = NetBuffer.allocate(HEADER_LENGTH + 34 + defenders.size() * 14);
NetBuffer data = NetBuffer.allocate(HEADER_LENGTH + 33 + defenders.size() * 14);
encodeHeader(data.getBuffer());
data.addInt(0); // CU Spacer
data.addInt(actionCrc);
// data.addInt(0x7757EE9B);
data.addLong(attackerId);
data.addLong(weaponId);
data.addByte(posture.getId());

View File

@@ -105,13 +105,11 @@ public class Location implements Encodable, Serializable {
public boolean isWithinDistance(Terrain t, double x, double y, double z, double radius) {
if (getTerrain() != t)
return false;
return square(square(getX()-x) + square(getY()-y) + square(getZ()-z)) <= square(radius);
return square(getX()-x) + square(getY()-y) + square(getZ()-z) <= square(radius);
}
public boolean isWithinFlatDistance(Point3D target, double radius){
double xD = Math.abs(getX() - target.getX());
double zD = Math.abs(getZ() - target.getZ());
return xD + zD <= radius;
return square(getX() - target.getX()) + square(getZ() - target.getZ()) <= square(radius);
}
public void translatePosition(double x, double y, double z) {

View File

@@ -1,30 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
/***********************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package resources.collections;
import network.packets.swg.zone.baselines.Baseline;
@@ -36,46 +36,44 @@ import java.nio.ByteOrder;
import java.util.Arrays;
import java.util.BitSet;
/**
* @author Waverunner
*/
public class SWGFlag extends BitSet implements Encodable {
private static final long serialVersionUID = 200L;
private static final long serialVersionUID = 2L;
private final int view;
private final int updateType;
private int view;
private int updateType;
/**
* Creates a new {@link SWGFlag} for the defined baseline with the given view and update.
* Note that this is an extension of {@link BitSet}
* @param baseline {@link Baseline.BaselineType} for this BitSet, should be the same as the parent class this list resides in
* Creates a new {@link SWGFlag} for the defined baseline with the given view and update. Note
* that this is an extension of {@link BitSet}
*
* @param baseline {@link Baseline.BaselineType} for this BitSet, should be the same as the
* parent class this list resides in
* @param view The baseline number this BitSet resides in
* @param updateType The update variable used for sending a delta, it's the operand count that this BitSet resides at within the baseline
* @param updateType The update variable used for sending a delta, it's the operand count that
* this BitSet resides at within the baseline
*/
public SWGFlag(int view, int updateType) {
super(128); // Seems to be the default size for the bitmask sets in packets
this.view = view;
this.updateType = updateType;
}
@Override
public byte[] encode() {
int[] list = toList();
ByteBuffer buffer = ByteBuffer.allocate(4 + (list.length * 4)).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(list.length);
for (int bits : list) {
buffer.putInt(bits);
}
return buffer.array();
}
@Override
public void decode(ByteBuffer data) {
// TODO: Decode method for SWGBitSet
throw new UnsupportedOperationException("Unable to decode bitset!");
}
@@ -83,40 +81,32 @@ public class SWGFlag extends BitSet implements Encodable {
public boolean equals(Object o) {
if (!(o instanceof SWGFlag))
return super.equals(o);
return Arrays.equals(toList(), ((SWGFlag)o).toList());
return Arrays.equals(toList(), ((SWGFlag) o).toList());
}
@Override
public int hashCode() {
return Arrays.hashCode(toList());
}
public void sendDeltaMessage(SWGObject target) {
if (target.getOwner() == null) {
if (target.getOwner() == null)
return;
}
target.sendDelta(view, updateType, encode());
}
public int[] toList() {
// TODO: This is working somehow, but position is wrong...
int[] integers = new int[4];
int count = 0;
int position = 0; // Position within the bit set
int[] integers = new int[(int) Math.ceil(size()/32.0)];
for (int i = 0; i < size(); i++) {
if (position >= 32)
count++;
int setBitIndex = nextSetBit(i);
if (setBitIndex == -1) // No more bits after this one
i = nextSetBit(i);
if (i == -1) // No more bits after this one
break;
integers[count] |= (0x00000001 << setBitIndex);
i += setBitIndex;
integers[i / 32] |= (1 << (i % 32));
}
return integers;
}
}

View File

@@ -30,7 +30,6 @@ package resources.collections;
import network.packets.Packet;
import network.packets.swg.zone.baselines.Baseline.BaselineType;
import resources.encodables.Encodable;
import resources.network.DeltaBuilder;
import resources.network.NetBuffer;
import resources.objects.SWGObject;
import resources.server_info.Log;
@@ -46,238 +45,243 @@ import java.util.AbstractList;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Supports a list of elements which automatically sends data as a delta when changed for baselines.
* @author Waverunner
*
* @param <E> Element that implements {@link Encodable} in order for data to be sent, or a basic type.
*
* @param <E> Element that implements {@link Encodable} in order for data to be sent, or a basic
* type.
*/
public class SWGList<E> extends AbstractList<E> implements Encodable, Serializable {
private static final long serialVersionUID = 1L;
private int view;
private int updateType;
private transient int updateCount;
private int dataSize;
private StringType strType = StringType.UNSPECIFIED;
// This list is a listing of all the data for the element list, this should always be in sync with list
private final List<byte[]> data = new ArrayList<>();
private final List<E> list = new ArrayList<>(); // thread-safe list
public class SWGList<E> extends ArrayList<E> implements Encodable, Serializable {
private static final long serialVersionUID = 2L;
private final StringType strType;
private final int view;
private final int updateType;
private transient Object updateMutex = new Object();
private transient AtomicInteger updateCount;
private transient List<byte[]> deltas;
private transient List<byte[]> data;
private transient int deltaSize;
private transient int dataSize;
private final LinkedList<byte[]> deltas = new LinkedList<>();
private int deltaSize;
/**
* Creates a new {@link SWGList} for the defined baseline with the given view and update. Note that this is an extension of {@link AbstractList} and makes use of {@link java.util.ArrayList}
* @param baseline {@link BaselineType} for this list, should be the same as the parent class this list resides in
* Creates a new {@link SWGList} for the defined baseline with the given view and update. Note
* that this is an extension of {@link AbstractList} and makes use of
* {@link java.util.ArrayList}
*
* @param baseline {@link BaselineType} for this list, should be the same as the parent class
* this list resides in
* @param view The baseline number this list resides in
* @param updateType The update variable used for sending a delta, it's the operand count that this list resides at within the baseline
* @param updateType The update variable used for sending a delta, it's the operand count that
* this list resides at within the baseline
*/
public SWGList(int view, int updateType) {
this.view = view;
this.updateType = updateType;
this(view, updateType, StringType.UNSPECIFIED);
}
/**
* Creates a new {@link SWGList} with the given StringType to encode in. Note that this constructor must be used if the elements within the list is a String.
* @param baseline {@link BaselineType} for this list, should be the same as the parent class this list resides in
* Creates a new {@link SWGList} with the given StringType to encode in. Note that this
* constructor must be used if the elements within the list is a String.
*
* @param baseline {@link BaselineType} for this list, should be the same as the parent class
* this list resides in
* @param view The baseline number this list resides in
* @param strType The {@link StringType} of the string, required only if the element in the list is a String as it's used for encoding either Unicode or ASCII characters
* @param strType The {@link StringType} of the string, required only if the element in the list
* is a String as it's used for encoding either Unicode or ASCII characters
*/
public SWGList(int view, int updateType, StringType strType) {
this (view, updateType);
this.view = view;
this.updateType = updateType;
this.strType = strType;
this.data = new ArrayList<>();
this.dataSize = 0;
this.updateMutex = new Object();
this.updateCount = new AtomicInteger(0);
this.deltaSize = 0;
this.deltas = new LinkedList<>();
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
updateCount = 0;
updateMutex = new Object();
updateCount = new AtomicInteger(0);
data = new ArrayList<>();
dataSize = 0;
deltas = new LinkedList<>();
deltaSize = 0;
for (E e : this) {
addObjectData(data.size(), e, (byte) 1);
}
clearDeltaQueue();
}
public void resetUpdateCount() {
updateCount = 0;
updateCount.set(0);
}
/**
* Appends the specified element to the end of this list if it doesn't already exist. Once added, the updateCount is incremented by one
* and data for the object is encoded.
* <br><br>An <i>add delta</i> is then sent using {@link DeltaBuilder} if noUpdates = false (false by default)
* @param e element to be appended to this list
* @return true if the element was added
*/
@Override
public boolean add(E e) {
add(list.size(), e);
return list.contains(e);
add(size(), e);
return get(size() - 1).equals(e);
}
/**
* Inserts the specified element at the specified position in this list. Shifts the element currently
* at that position (if any) and any subsequent elements to the right (adds one to their indices).
* <br><br>An <i>add delta</i> is then sent using {@link DeltaBuilder} if noUpdates = false (false by default)
* @param index index at which the specified element is to be inserted
* @param e element to be inserted
*/
@Override
public void add(int index, E e) {
synchronized (list) {
updateCount++;
list.add(index, e);
addObjectData(index, e, (byte) 1);
synchronized (updateMutex) {
super.add(index, e);
}
updateCount.incrementAndGet();
addObjectData(index, e, (byte) 1);
}
/**
* Replaces the element at the specified position in this list with the specified element.
* <br><br>A <i>change delta</i> is then sent using {@link DeltaBuilder} if noUpdates = false (false by default). Since this
* sends a change delta, it should only be used for replacing an element, not for adding one.
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return The element that was replaced
*/
@Override
public E set(int index, E element) {
// Sends a "change" delta
E previous;
synchronized (list) {
previous = list.set(index, element);
if (previous != null) {
updateCount++;
removeDataSize(index);
removeData(index);
}
addObjectData(index, element, (byte) 2);
synchronized (updateMutex) {
previous = super.set(index, element);
}
if (previous != null) {
removeData(index);
}
updateCount.incrementAndGet();
addObjectData(index, element, (byte) 2);
return previous;
}
@Override
public boolean remove(Object o) {
//noinspection SuspiciousMethodCalls
int index = list.indexOf(o); // No idea why this produces a suspicious method call..
int index = indexOf(o);
if (index != -1) {
remove(index);
return true;
}
return false;
}
/**
* Removes the element at the specified position in this list. Shifts any subsequent elements to the left
* (subtracts one from their indices). Returns the element that was removed from the list.
* @param index the index of the element to be removed
* @return the element previously at the specified position
*/
@Override
public E remove(int index) {
E element;
synchronized (list) {
element = list.remove(index);
if (element != null) {
updateCount++;
removeObjectData(index, (byte) 0);
}
synchronized (updateMutex) {
element = super.remove(index);
}
if (element != null) {
updateCount.incrementAndGet();
removeObjectData(index);
}
return element;
}
@Override
public int indexOf(Object o) {
return list.indexOf(o);
}
@Override
public E get(int index) {
return list.get(index);
synchronized (updateMutex) {
return super.get(index);
}
}
@Override
public int size() {
return list.size();
}
/**
* Creates an array of bytes based off of the elements within this list. Elements that are not of a standard type
* handled by {@link Encoder} should implement the {@link Encodable} interface.
* Creates an array of bytes based off of the elements within this Elements that are not of a
* standard type handled by {@link Encoder} should implement the {@link Encodable} interface.
*
* @return Array of bytes with the size, update count, and encoded elements
*/
@Override
public byte[] encode() {
int size = list.size();
if (size == 0) {
return new byte[8];
ByteBuffer buffer;
synchronized (data) {
if (dataSize == 0)
return new byte[8];
buffer = ByteBuffer.allocate(8 + dataSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(data.size());
buffer.putInt(updateCount.get());
data.forEach(buffer::put);
}
ByteBuffer buffer = ByteBuffer.allocate(8 + dataSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(size);
buffer.putInt(updateCount);
data.forEach(buffer::put);
return buffer.array();
}
@Override
public void decode(ByteBuffer data) {
/* Not sure how to do decoding for an SWGList because of generics, won't know what specific type to decode as
One possible workaround is to refactor decode to create new object instead of directly initializing the variables
During compile time, a List<Type> is just a List due to type erasure.
*/
try {
throw new Exception("This is not a supported operation. Use decode(ByteBuffer data, Class<T> elementType) instead");
} catch (Exception e) {
e.printStackTrace();
}
// We need specific type information
throw new UnsupportedOperationException("Use decode(ByteBuffer data, Class<E> elementType) instead");
}
@SuppressWarnings("unchecked") // Unfortunately the exception is just caught
public void decode(ByteBuffer data, StringType type) {
int size = Packet.getInt(data);
updateCount = Packet.getInt(data);
int size = Packet.getInt(data);
updateCount.set(Packet.getInt(data));
NetBuffer buffer = NetBuffer.wrap(data);
try {
for (int i = 0; i < size; i++)
list.add((E) buffer.getString(type));
} catch (ClassCastException e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked") // There is type checking in the respective if's
public void decode(ByteBuffer data, Class<E> elementType) {
int size = Packet.getInt(data);
updateCount = Packet.getInt(data);
try {
boolean encodable = Encodable.class.isAssignableFrom(elementType);
NetBuffer wrap = NetBuffer.wrap(data);
for (int i = 0; i < size; i++) {
if (encodable) {
E instance = elementType.newInstance();
if (instance instanceof Encodable) {
((Encodable) instance).decode(data);
list.add(instance);
}
} else {
Object o = wrap.getGeneric(elementType);
if (o != null && elementType.isAssignableFrom(o.getClass()))
list.add((E) o);
}
}
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
for (int i = 0; i < size; i++) {
@SuppressWarnings("unchecked")
E obj = (E) buffer.getString(type);
add(obj);
}
clearDeltaQueue();
}
public void decode(ByteBuffer data, Class<E> elementType) {
int size = Packet.getInt(data);
updateCount.set(Packet.getInt(data));
boolean encodable = Encodable.class.isAssignableFrom(elementType);
NetBuffer wrap = NetBuffer.wrap(data);
for (int i = 0; i < size; i++) {
if (!decodeElement(wrap, elementType, encodable))
break;
}
clearDeltaQueue();
}
private boolean decodeElement(NetBuffer wrap, Class<E> elementType, boolean encodable) {
if (encodable) {
try {
E instance = elementType.newInstance();
if (instance instanceof Encodable) {
((Encodable) instance).decode(wrap.getBuffer());
add(instance);
}
} catch (InstantiationException | IllegalAccessException e) {
Log.e("SWGList", e);
return false;
}
} else {
Object o = wrap.getGeneric(elementType);
if (o != null && elementType.isAssignableFrom(o.getClass())) {
// Shouldn't be possible to get an exception with the isAssignableFrom check
@SuppressWarnings("unchecked")
E obj = (E) o;
add(obj);
} else
return false;
}
return true;
}
public void sendRefreshedListData(SWGObject target) {
clearDeltaQueue();
ByteBuffer buffer;
synchronized (data) {
updateCount.set(data.size()+1);
buffer = ByteBuffer.allocate(11 + dataSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(data.size() + 1);
buffer.putInt(updateCount.get());
buffer.put((byte) 3);
buffer.putShort((short) data.size());
for (byte[] bytes : data) {
buffer.put(bytes);
}
}
target.sendDelta(view, updateType, buffer.array());
}
public void sendDeltaMessage(SWGObject target) {
if (deltas.size() == 0)
return;
@@ -286,47 +290,34 @@ public class SWGList<E> extends AbstractList<E> implements Encodable, Serializab
// Clear the queue since the delta has been sent to observers through the builder
clearDeltaQueue();
}
public void sendRefreshedListData(SWGObject target) {
clearDeltaQueue();
updateCount = 0;
ByteBuffer bb = ByteBuffer.allocate(11 + dataSize).order(ByteOrder.LITTLE_ENDIAN);
bb.putInt(data.size() + 1);
bb.putInt(updateCount += data.size() + 1);
bb.put((byte) 3);
bb.putShort((short) data.size());
for (byte[] bytes : data) {
bb.put(bytes);
}
target.sendDelta(view, updateType, bb.array());
}
public void clearDeltaQueue() {
deltas.clear();
deltaSize = 0;
synchronized (deltas) {
deltas.clear();
deltaSize = 0;
}
}
private byte[] getDeltaData() {
ByteBuffer buffer = ByteBuffer.allocate(8 + deltaSize).order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer buffer;
buffer.putInt(deltas.size());
buffer.putInt(updateCount);
for (byte[] data : deltas) {
buffer.put(data);
synchronized (deltas) {
buffer = ByteBuffer.allocate(8 + deltaSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(deltas.size());
buffer.putInt(updateCount.get());
for (byte[] data : deltas) {
buffer.put(data);
}
}
return buffer.array();
}
private void createDeltaData(byte[] delta, byte update) {
synchronized(deltas) {
byte[] combinedUpdate = new byte[delta.length + 1];
combinedUpdate[0] = update;
System.arraycopy(delta, 0, combinedUpdate, 1, delta.length);
deltaSize += delta.length + 1;
deltas.add(combinedUpdate);
private void createDeltaData(byte[] delta) {
synchronized (deltas) {
deltaSize += delta.length;
deltas.add(delta);
}
}
@@ -336,50 +327,35 @@ public class SWGList<E> extends AbstractList<E> implements Encodable, Serializab
Log.e(toString(), "Tried to encode an object that could not be encoded properly. Object: " + obj);
return;
}
dataSize += encodedData.length;
synchronized (data) {
dataSize += encodedData.length;
data.add(index, encodedData);
}
createIndexedDelta(encodedData, index, update);
}
private void createIndexedDelta(byte[] encodedData, int index, byte update) {
ByteBuffer buffer = ByteBuffer.allocate(encodedData.length + 2).order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer buffer = ByteBuffer.allocate(encodedData.length + 3).order(ByteOrder.LITTLE_ENDIAN);
buffer.put(update);
buffer.putShort((short) index);
buffer.put(encodedData);
byte[] indexedBytes = buffer.array();
createDeltaData(indexedBytes, update);
createDeltaData(buffer.array());
}
private void removeObjectData(int index, byte update) {
if (data.get(index) == null) {
return;
}
private void removeObjectData(int index) {
removeData(index);
// Only the index is sent for removing data
ByteBuffer buffer = ByteBuffer.allocate(3).order(ByteOrder.LITTLE_ENDIAN);
buffer.put((byte) 0);
buffer.putShort((short) index);
createDeltaData(buffer.array());
}
private void removeData(int index) {
synchronized (data) {
dataSize -= data.remove(index).length;
}
// Only the index is sent for removing data
ByteBuffer buffer = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN);
buffer.putShort((short) index);
createDeltaData(buffer.array(), update);
}
// Removes obj size of data without removing it from the data map
private void removeDataSize(int index) {
dataSize -= data.get(index).length;
}
private void removeData(int index) {
synchronized (data) {
data.remove(index);
}
}
@Override
public String toString() {
return "SWGList[0" + view + ":" + updateType + "]";

View File

@@ -39,66 +39,73 @@ import java.io.ObjectInputStream;
import java.io.Serializable;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import network.packets.Packet;
public class SWGMap<K, V> extends AbstractMap<K, V> implements Encodable, Serializable {
private static final long serialVersionUID = 1L;
private int view;
private int updateType;
private transient int updateCount;
private int dataSize;
public class SWGMap<K, V> extends HashMap<K, V> implements Encodable, Serializable {
private StringType strType = StringType.UNSPECIFIED;
private static final long serialVersionUID = 2L;
/*
* Map which will contain all the byte data. A map is used here because it allows the encode method to not have to guess the ByteBuffer size. Doing it this way will
* also allow all the data to be pre-compiled for the list, so it can have a positive impact on large SWGList's. This means that only 1 ByteBuffer is being created,
* and that is to just take the data from this map and put it all together!
*/
private Map<Object, byte[]> data = new ConcurrentHashMap<>();
private Map<K, V> map = new ConcurrentHashMap<K, V>();
private final int view;
private final int updateType;
private final StringType strType;
private Map<Object, byte[]> deltas = new HashMap<>();
private int deltaSize;
private transient Object updateMutex;
private transient AtomicInteger updateCount;
private transient Map<Object, byte[]> deltas;
private transient Map<Object, byte[]> data;
private transient int deltaSize;
private transient int dataSize;
public SWGMap(int view, int updateType) {
this.view = view;
this.updateType = updateType;
this(view, updateType, StringType.UNSPECIFIED);
}
public SWGMap(int view, int updateType, StringType strType) {
this.view = view;
this.updateType = updateType;
this.strType = strType;
this.updateMutex = new Object();
this.updateCount = new AtomicInteger(0);
this.deltas = new HashMap<>();
this.data = new HashMap<>();
this.deltaSize = 0;
this.dataSize = 0;
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
updateCount = 0;
updateMutex = new Object();
updateCount = new AtomicInteger(0);
deltas = new HashMap<>();
data = new HashMap<>();
deltaSize = 0;
dataSize = 0;
for (Entry<K, V> e : entrySet()) {
addData(e.getKey(), e.getValue(), (byte) 0);
}
clearDeltaQueue();
}
public void resetUpdateCount() {
updateCount = 0;
updateCount.set(0);
}
@Override
public V get(Object key) {
return map.get(key);
return super.get(key);
}
@Override
public V put(K key, V value) {
updateCount++;
V old = map.put(key, value);
V old;
synchronized (updateMutex) {
old = super.put(key, value);
}
updateCount.incrementAndGet();
if (old != null) {
removeData(key);
}
@@ -109,167 +116,124 @@ public class SWGMap<K, V> extends AbstractMap<K, V> implements Encodable, Serial
@Override
public V remove(Object key) {
updateCount++;
V old = map.remove(key);
V old;
synchronized (updateMutex) {
old = super.remove(key);
}
updateCount.incrementAndGet();
removeData(key);
return old;
}
@Override
public Set<K> keySet() {
return map.keySet();
}
@Override
public Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
@Override
public int size() {
return map.size();
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
/**
* Sends a delta and updates the data, notifying clients of the changed item in this map. Use this if you are not adding or removing the element
* and just simply changing it. Should be used after changing values for an item in this map.
* Sends a delta and updates the data, notifying clients of the changed item in this map. Use
* this if you are not adding or removing the element and just simply changing it. Should be
* used after changing values for an item in this super.
*
* @param key Associated key with the value that was modified.
* @param parent The parent of this map, who the map is bound to in order to send the delta
*/
public void update(Object key, SWGObject parent) {
updateCount++;
removeDataSize(key); // remove the size of the prior data because not all encodables are fixed sizes (ie some have strings inside them)
addData(key, map.get(key), (byte) 2);
updateCount.incrementAndGet();
removeDataSize(key); // remove the size of the prior data
addData(key, super.get(key), (byte) 2);
sendDeltaMessage(parent);
}
@Override
public byte[] encode() {
int size = map.size();
if (size == 0) {
return new byte[8];
ByteBuffer buffer;
synchronized (data) {
if (dataSize == 0)
return new byte[8];
buffer = ByteBuffer.allocate(8 + dataSize + data.size()).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(data.size());
buffer.putInt(updateCount.get());
for (byte[] bytes : data.values()) {
buffer.put((byte) 0);
buffer.put(bytes);
}
}
ByteBuffer buffer = ByteBuffer.allocate(8 + dataSize + data.size()).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(size);
buffer.putInt(updateCount);
if (data.size() != size) {
// Data got out of sync with the map, so lets clean that up!
clearAllData();
for (Entry<K, V> entry : map.entrySet()) {
addData(entry.getKey(), entry.getValue(), (byte) 0);
}
clearDeltaQueue();
}
for (byte[] bytes : data.values()) {
buffer.put((byte) 0);
buffer.put(bytes);
}
return buffer.array();
}
@Override
public void decode(ByteBuffer data) {
//throw new NotImplementedException();
/* Not sure how to do decoding for an SWGMap because of generics, won't know what specific type to decode as
One possible workaround is to refactor decode to create new object instead of directly initializing the variables
int size = Packet.getInt(data);
updateCount = Packet.getInt(data);
for (int i = 0; i < size; i++) {
}
return 0;*/
throw new UnsupportedOperationException("Use decode(ByteBuffer data, Class<K> kType, Class<V> vType) instead");
}
@SuppressWarnings("unchecked") // Unfortunately the exception is just caught
public void decode(ByteBuffer data, StringType keyType, StringType valType, boolean addByte) {
int size = Packet.getInt(data);
updateCount = Packet.getInt(data);
@SuppressWarnings("unchecked")
// Unfortunately the exception is just caught
public void decode(ByteBuffer data, StringType keyType, StringType valType) {
int size = Packet.getInt(data);
updateCount.set(Packet.getInt(data));
NetBuffer buffer = NetBuffer.wrap(data);
try {
for (int i = 0; i < size; i++) {
if (addByte)
buffer.getByte();
map.put((K) buffer.getString(keyType), (V) buffer.getString(valType));
buffer.getByte();
put((K) buffer.getString(keyType), (V) buffer.getString(valType));
}
} catch (ClassCastException e) {
e.printStackTrace();
}
clearDeltaQueue();
}
@SuppressWarnings("unchecked") // Unfortunately the exception is just caught
public void decode(ByteBuffer data, StringType keyType, Class<V> vType, boolean addByte) {
int size = Packet.getInt(data);
updateCount = Packet.getInt(data);
@SuppressWarnings("unchecked")
// Unfortunately the exception is just caught
public void decode(ByteBuffer data, StringType keyType, Class<V> vType) {
int size = Packet.getInt(data);
updateCount.set(Packet.getInt(data));
NetBuffer buffer = NetBuffer.wrap(data);
try {
for (int i = 0; i < size; i++) {
if (addByte)
buffer.getByte();
buffer.getByte();
String key = buffer.getString(keyType);
Object value = buffer.getGeneric(vType);
if (value != null && vType.isAssignableFrom(value.getClass()))
map.put((K) key, (V) value);
put((K) key, (V) value);
else
Log.e("SWGMap", "Unable to parse: key=%s value=%s", key, value);
}
} catch (ClassCastException e) {
e.printStackTrace();
}
clearDeltaQueue();
}
@SuppressWarnings("unchecked") // There is type checking in the respective if's
public void decode(ByteBuffer data, Class<K> kType, Class<V> vType, boolean addByte) {
int size = Packet.getInt(data);
updateCount = Packet.getInt(data);
@SuppressWarnings("unchecked")
// There is type checking in the respective if's
public void decode(ByteBuffer data, Class<K> kType, Class<V> vType) {
int size = Packet.getInt(data);
updateCount.set(Packet.getInt(data));
NetBuffer buffer = NetBuffer.wrap(data);
for (int i = 0; i < size; i++) {
if (addByte)
buffer.getByte();
buffer.getByte();
Object key = buffer.getGeneric(kType);
if (key == null) {
Log.e("SWGMap", "Failed to decode: "+kType.getSimpleName());
Log.e("SWGMap", "Failed to decode: " + kType.getSimpleName());
break;
}
Object value = buffer.getGeneric(vType);
if (value == null) {
Log.e("SWGMap", "Failed to decode: "+vType.getSimpleName());
Log.e("SWGMap", "Failed to decode: " + vType.getSimpleName());
break;
}
if (kType.isAssignableFrom(key.getClass()) && vType.isAssignableFrom(value.getClass()))
map.put((K) key, (V) value);
put((K) key, (V) value);
else
Log.e("SWGMap", "Failed to insert key="+key+" value="+value);
Log.e("SWGMap", "Failed to insert key=" + key + " value=" + value);
}
clearDeltaQueue();
}
public void sendDeltaMessage(SWGObject target) {
if (!(deltas.size() > 0))
if (deltas.size() == 0)
return;
target.sendDelta(view, updateType, getDeltaData());
@@ -278,43 +242,40 @@ public class SWGMap<K, V> extends AbstractMap<K, V> implements Encodable, Serial
}
public void clearDeltaQueue() {
deltas.clear();
deltaSize = 0;
synchronized (deltas) {
deltas.clear();
deltaSize = 0;
}
}
private byte[] getDeltaData() {
ByteBuffer buffer = ByteBuffer.allocate(8 + deltaSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(deltas.size());
buffer.putInt(updateCount);
for (byte[] data : deltas.values()) {
buffer.put(data);
ByteBuffer buffer;
synchronized (deltas) {
buffer = ByteBuffer.allocate(8 + deltaSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(deltas.size());
buffer.putInt(updateCount.get());
for (byte[] data : deltas.values()) {
buffer.put(data);
}
}
return buffer.array();
}
private void createDeltaData(Object key, byte[] delta, byte update) {
synchronized(deltas) {
byte[] combinedUpdate = new byte[delta.length + 1];
combinedUpdate[0] = update;
System.arraycopy(delta, 0, combinedUpdate, 1, delta.length);
byte[] combinedUpdate = new byte[delta.length + 1];
combinedUpdate[0] = update;
System.arraycopy(delta, 0, combinedUpdate, 1, delta.length);
synchronized (deltas) {
if (deltas.containsKey(key)) {
deltaSize -= deltas.remove(key).length;
}
deltaSize += delta.length + 1;
deltaSize += combinedUpdate.length;
deltas.put(key, combinedUpdate);
}
}
private void clearAllData() {
dataSize = 0;
data.clear();
clearDeltaQueue();
}
private void addData(Object key, Object value, byte update) {
byte[] encodedKey = Encoder.encode(key, strType);
byte[] encodedValue = Encoder.encode(value, strType);
@@ -323,29 +284,27 @@ public class SWGMap<K, V> extends AbstractMap<K, V> implements Encodable, Serial
System.arraycopy(encodedKey, 0, encodedData, 0, encodedKey.length);
System.arraycopy(encodedValue, 0, encodedData, encodedKey.length, encodedValue.length);
data.put(key, encodedData);
dataSize += encodedData.length;
synchronized (data) {
data.put(key, encodedData);
dataSize += encodedData.length;
}
createDeltaData(key, encodedData, update);
}
// Removes Key and its Value size of data and removes it from the data map
private void removeData(Object key) {
byte[] bytes = data.remove(key);
if (bytes == null) {
Log.e("SWGMap", "Could not remove key as it wasn't in the data map: " + key);
return;
byte [] value;
synchronized (data) {
value = data.remove(key);
dataSize -= value.length;
}
dataSize -= bytes.length;
createDeltaData(key, bytes, (byte) 1);
createDeltaData(key, value, (byte) 1);
}
// Removes Key and its Value size of data without removing it from the data map
private void removeDataSize(Object key) {
dataSize -= data.get(key).length;
synchronized (data) {
dataSize -= data.get(key).length;
}
}
}

View File

@@ -1,30 +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 <http://www.gnu.org/licenses/>
******************************************************************************/
/***********************************************************************************
* 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 <http://www.gnu.org/licenses/>. *
* *
***********************************************************************************/
package resources.collections;
import network.packets.Packet;
@@ -43,242 +43,266 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.AbstractSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Created by Waverunner on 8/18/2015
*/
public class SWGSet<E> extends AbstractSet<E> implements Encodable, Serializable {
private static final long serialVersionUID = 1L;
private int view;
private int updateType;
private transient int updateCount;
private int dataSize;
private Encoder.StringType strType = StringType.UNSPECIFIED;
// Data set for this set, should be in sync (same number of elements if encode data is not modified) with set.
private final Set<byte[]> data = new HashSet<>();
private final Set<E> set = new HashSet<>();
private final LinkedList<byte[]> deltas = new LinkedList<>();
private int deltaSize;
public class SWGSet<E> extends HashSet<E> implements Encodable, Serializable {
private static final long serialVersionUID = 2L;
private final int view;
private final int updateType;
private final Encoder.StringType strType;
private transient Object updateMutex;
private transient AtomicInteger updateCount;
private transient List<byte[]> deltas;
private transient Set<ByteBuffer> data;
private transient int deltaSize;
private transient int dataSize;
/**
* Creates a new {@link SWGSet} for the defined baseline with the given view and update. Note that this is an extension of {@link AbstractSet} and makes use of {@link HashSet}
* @param baseline {@link Baseline.BaselineType} for this set, should be the same as the parent class this list resides in
* Creates a new {@link SWGSet} for the defined baseline with the given view and update. Note
* that this is an extension of {@link AbstractSet} and makes use of {@link HashSet}
*
* @param baseline {@link Baseline.BaselineType} for this set, should be the same as the parent
* class this list resides in
* @param view The baseline number this list resides in
* @param updateType The update variable used for sending a delta, it's the operand count that this list resides at within the baseline
* @param updateType The update variable used for sending a delta, it's the operand count that
* this list resides at within the baseline
*/
public SWGSet(int view, int updateType) {
this.view = view;
this.updateType = updateType;
this(view, updateType, StringType.UNSPECIFIED);
}
/**
* Creates a new {@link SWGSet} with the given StringType to encode in. Note that this constructor must be used if the elements within the list is a String.
* @param baseline {@link Baseline.BaselineType} for this set, should be the same as the parent class this list resides in
* Creates a new {@link SWGSet} with the given StringType to encode in. Note that this
* constructor must be used if the elements within the list is a String.
*
* @param baseline {@link Baseline.BaselineType} for this set, should be the same as the parent
* class this list resides in
* @param view The baseline number this set resides in
* @param strType The {@link StringType} of the string, required only if the element in the set is a String as it's used for encoding either Unicode or ASCII characters
* @param strType The {@link StringType} of the string, required only if the element in the set
* is a String as it's used for encoding either Unicode or ASCII characters
*/
public SWGSet(int view, int updateType, StringType strType) {
this(view, updateType);
this.view = view;
this.updateType = updateType;
this.strType = strType;
this.data = new HashSet<>();
this.dataSize = 0;
this.updateMutex = new Object();
this.updateCount = new AtomicInteger(0);
this.deltas = new LinkedList<>();
this.deltaSize = 0;
}
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
ois.defaultReadObject();
updateCount = 0;
updateMutex = new Object();
updateCount = new AtomicInteger(0);
data = new HashSet<>();
dataSize = 0;
deltas = new LinkedList<>();
deltaSize = 0;
for (E e : this) {
addObjectData(e, (byte) 1);
}
clearDeltaQueue();
}
public void resetUpdateCount() {
updateCount = 0;
updateCount.set(0);
}
@Override
public boolean add(E e) {
if (!set.add(e))
return false;
updateCount++;
synchronized (updateMutex) {
if (!super.add(e))
return false;
}
updateCount.incrementAndGet();
addObjectData(e, (byte) 1);
return true;
}
@Override
public boolean remove(Object o) {
if (!set.remove(o))
return false;
updateCount++;
synchronized (updateMutex) {
if (!super.remove(o))
return false;
}
updateCount.incrementAndGet();
removeObjectData(o, (byte) 0);
return set.remove(o);
return true;
}
@Override
public void clear() {
set.clear();
updateCount++;
synchronized (updateMutex) {
super.clear();
}
updateCount.incrementAndGet();
clearAllObjectData();
}
@Override
public Iterator<E> iterator() {
return set.iterator();
}
@Override
public int size() {
return set.size();
}
public void sendDeltaMessage(SWGObject target) {
if (!(deltas.size() > 0))
if (deltas.size() == 0)
return;
target.sendDelta(view, updateType, getDeltaData());
// Clear the queue since the delta has been sent to observers through the builder
clearDeltaQueue();
}
private void addObjectData(E obj, byte update) {
byte[] encodedData = Encoder.encode(obj, strType);
if (encodedData == null) {
Log.e("SWGSet", "Tried to encode an object that could not be encoded properly. Object: " + obj);
return;
}
dataSize += encodedData.length;
synchronized (data) {
data.add(encodedData);
dataSize += encodedData.length;
data.add(ByteBuffer.wrap(encodedData));
}
createDeltaData(encodedData, update);
}
private void removeObjectData(Object obj, byte update) {
byte[] encodedData = Encoder.encode(obj, strType);
if (encodedData == null) {
Log.e("SWGSet", "Tried to encode an object that could not be encoded properly. Object: " + obj);
return;
}
synchronized (data) {
dataSize -= encodedData.length;
data.remove(ByteBuffer.wrap(encodedData));
}
createDeltaData(encodedData, update);
}
private void clearAllObjectData() {
clearAllData();
deltaSize = 1;
deltas.add(new byte[]{(byte) 2});
}
public void clearDeltaQueue() {
deltas.clear();
deltaSize = 0;
}
private byte[] getDeltaData() {
ByteBuffer buffer = ByteBuffer.allocate(8 + deltaSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(deltas.size());
buffer.putInt(updateCount);
for (byte[] data : deltas) {
buffer.put(data);
synchronized (deltas) {
deltaSize = 1;
deltas.add(new byte[] {(byte) 2});
}
}
public void clearDeltaQueue() {
synchronized (deltas) {
deltas.clear();
deltaSize = 0;
}
}
private byte[] getDeltaData() {
ByteBuffer buffer;
synchronized (deltas) {
buffer = ByteBuffer.allocate(8 + deltaSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(deltas.size());
buffer.putInt(updateCount.get());
for (byte[] data : deltas) {
buffer.put(data);
}
}
return buffer.array();
}
private void createDeltaData(byte[] delta, byte update) {
synchronized(deltas) {
byte[] combinedUpdate = new byte[delta.length + 1];
combinedUpdate[0] = update;
System.arraycopy(delta, 0, combinedUpdate, 1, delta.length);
byte[] combinedUpdate = new byte[delta.length + 1];
combinedUpdate[0] = update;
System.arraycopy(delta, 0, combinedUpdate, 1, delta.length);
synchronized (deltas) {
deltaSize += delta.length + 1;
deltas.add(combinedUpdate);
}
}
private void clearAllData() {
dataSize = 0;
data.clear();
synchronized (data) {
dataSize = 0;
data.clear();
}
clearDeltaQueue();
}
@Override
public byte[] encode() {
int size = data.size();
if (size == 0) {
return new byte[8];
}
ByteBuffer buffer = ByteBuffer.allocate(8 + dataSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(size);
buffer.putInt(updateCount);
data.forEach(buffer::put);
return buffer.array();
}
@Override
public void decode(ByteBuffer data) {
/*Not sure how to do decoding for an SWGSet because of generics, won't know what specific type to decode as
One possible workaround is to refactor decode to create new object instead of directly initializing the variables
During compile time, a Set<Type> is just a Set due to type erasure.
*/
try {
throw new Exception("This is not a supported operation. Use decode(ByteBuffer data, Class<T> elementType) instead");
} catch (Exception e) {
e.printStackTrace();
}
}
@SuppressWarnings("unchecked") // Unfortunately the exception is just caught
public void decode(ByteBuffer data, StringType type) {
int size = Packet.getInt(data);
updateCount = Packet.getInt(data);
NetBuffer buffer = NetBuffer.wrap(data);
try {
for (int i = 0; i < size; i++)
set.add((E) buffer.getString(type));
} catch (ClassCastException e) {
e.printStackTrace();
@Override
public byte[] encode() {
ByteBuffer buffer;
synchronized (data) {
if (dataSize == 0)
return new byte[8];
buffer = ByteBuffer.allocate(8 + dataSize).order(ByteOrder.LITTLE_ENDIAN);
buffer.putInt(data.size());
buffer.putInt(updateCount.get());
data.forEach(buffer::put);
}
return buffer.array();
}
@SuppressWarnings("unchecked") // There is type checking in the respective if's
public void decode(ByteBuffer data, Class<E> elementType) {
int size = Packet.getInt(data);
updateCount = Packet.getInt(data);
try {
boolean encodable = Encodable.class.isAssignableFrom(elementType);
NetBuffer wrap = NetBuffer.wrap(data);
for (int i = 0; i < size; i++) {
if (encodable) {
E instance = elementType.newInstance();
if (instance instanceof Encodable) {
((Encodable) instance).decode(data);
set.add(instance);
}
} else {
Object o = wrap.getGeneric(elementType);
if (o != null && elementType.isAssignableFrom(o.getClass()))
set.add((E) o);
}
}
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
@Override
public void decode(ByteBuffer data) {
throw new UnsupportedOperationException("Use decode(ByteBuffer data, Class<E> elementType) instead");
}
public void decode(ByteBuffer data, StringType type) {
int size = Packet.getInt(data);
updateCount.set(Packet.getInt(data));
NetBuffer buffer = NetBuffer.wrap(data);
for (int i = 0; i < size; i++) {
@SuppressWarnings("unchecked")
E obj = (E) buffer.getString(type);
add(obj);
}
clearDeltaQueue();
}
public void decode(ByteBuffer data, Class<E> elementType) {
int size = Packet.getInt(data);
updateCount.set(Packet.getInt(data));
boolean encodable = Encodable.class.isAssignableFrom(elementType);
NetBuffer wrap = NetBuffer.wrap(data);
for (int i = 0; i < size; i++) {
if (!decodeElement(wrap, elementType, encodable))
break;
}
clearDeltaQueue();
}
private boolean decodeElement(NetBuffer wrap, Class<E> elementType, boolean encodable) {
if (encodable) {
try {
E instance = elementType.newInstance();
if (instance instanceof Encodable) {
((Encodable) instance).decode(wrap.getBuffer());
add(instance);
}
} catch (InstantiationException | IllegalAccessException e) {
Log.e("SWGList", e);
return false;
}
} else {
Object o = wrap.getGeneric(elementType);
if (o != null && elementType.isAssignableFrom(o.getClass())) {
// Shouldn't be possible to get an exception with the isAssignableFrom check
@SuppressWarnings("unchecked")
E obj = (E) o;
add(obj);
} else
return false;
}
return true;
}
}

View File

@@ -92,7 +92,7 @@ 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);
Scripts.invoke("commands/helper/qatool/details", "sendDetails", player, target, args.split(" "));
break;
default:
displayMainWindow(player);

View File

@@ -243,7 +243,7 @@ public class NetBuffer {
}
public SWGSet<String> getSwgSet(int num, int var, StringType type) {
SWGSet<String> set = new SWGSet<>(num, var);
SWGSet<String> set = new SWGSet<>(num, var, type);
set.decode(data, type);
return set;
}
@@ -255,33 +255,32 @@ public class NetBuffer {
}
public SWGList<String> getSwgList(int num, int var, StringType type) {
SWGList<String> set = new SWGList<>(num, var);
SWGList<String> set = new SWGList<>(num, var, type);
set.decode(data, type);
return set;
}
public <T> SWGList<T> getSwgList(int num, int var, Class<T> type) {
SWGList<T> set = new SWGList<>(num, var);
set.decode(data, type);
return set;
}
public SWGMap<String, String> getSwgMap(int num, int var, StringType key, StringType val) {
SWGMap<String, String> map = new SWGMap<>(num, var);
map.decode(data, key, val, true);
public SWGMap<String, String> getSwgMap(int num, int var, StringType strType) {
SWGMap<String, String> map = new SWGMap<>(num, var, strType);
map.decode(data, strType, strType);
return map;
}
public <V> SWGMap<String, V> getSwgMap(int num, int var, StringType key, Class<V> val) {
SWGMap<String, V> map = new SWGMap<>(num, var);
map.decode(data, key, val, true);
SWGMap<String, V> map = new SWGMap<>(num, var, key);
map.decode(data, key, val);
return map;
}
public <K, V> SWGMap<K, V> getSwgMap(int num, int var, Class<K> key, Class<V> val) {
SWGMap<K, V> map = new SWGMap<>(num, var);
map.decode(data, key, val, true);
map.decode(data, key, val);
return map;
}

View File

@@ -276,9 +276,9 @@ public class NetBufferStream extends OutputStream {
}
}
public SWGMap<String, String> getSwgMap(int num, int var, StringType key, StringType val) {
public SWGMap<String, String> getSwgMap(int num, int var, StringType strType) {
synchronized (bufferMutex) {
return buffer.getSwgMap(num, var, key, val);
return buffer.getSwgMap(num, var, strType);
}
}

View File

@@ -51,7 +51,6 @@ import resources.network.BaselineObject;
import resources.network.NetBuffer;
import resources.objects.building.BuildingObject;
import resources.objects.creature.CreatureObject;
import resources.objects.tangible.TangibleObject;
import resources.player.Player;
import resources.server_info.Log;
import services.CoreManager;
@@ -120,7 +119,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
areaId = -1;
ois.defaultReadObject();
objectsAware = new HashSet<SWGObject>();
objectsAware = new HashSet<>();
buildoutArea = null;
owner = null;
}
@@ -129,7 +128,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
* Adds the specified object to this object and places it in the appropriate slot if needed
* @param object Object to add to this container, which will either be put into the appropriate slot(s) or become a contained object
*/
public boolean addObject(SWGObject object) {
protected boolean addObject(SWGObject object) {
// If the arrangement is -1, then this object will be a contained object
int arrangementId = getArrangementId(object);
if (arrangementId == -1) {
@@ -148,9 +147,12 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
setSlot(requiredSlot, object);
}
}
object.parent = this;
object.slotArrangement = arrangementId;
synchronized (object.objectsAware) {
object.objectsAware.clear();
}
return true;
}
@@ -158,7 +160,12 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
* Removes the specified object from this current object.
* @param object Object to remove
*/
public void removeObject(SWGObject object) {
protected void removeObject(SWGObject object) {
synchronized (object.objectsAware) {
object.objectsAware.clear();
object.objectsAware.addAll(getObjectsAware());
object.objectsAware.remove(object);
}
// This object is a container object, so remove it from the container
if (object.getSlotArrangement() == -1) {
synchronized (containedObjects) {
@@ -170,7 +177,7 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
setSlot(slot, null);
}
}
object.parent = null;
object.slotArrangement = -1;
}
@@ -183,9 +190,52 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
*/
public ContainerResult moveToContainer(SWGObject requester, SWGObject container) {
// Check if the requester has MOVE permissions for the current container of the object
ContainerResult result = moveToContainerChecks(requester, container);
if (result != ContainerResult.SUCCESS)
return result;
// Get a pre-parent-removal list of the observers so we can send create/destroy/update messages
Set<SWGObject> oldObservers = getObservers();
Player prevOwner = (getParent() != null) ? getParent().getOwner() : null;
if (prevOwner != null)
oldObservers.add(prevOwner.getCreatureObject());
// Remove this object from the old parent if one exists
if (parent != null) {
parent.removeObject(this);
}
Player newOwner = null;
if (container != null) {
if (!container.addObject(this))
Log.e("SWGObject", "Failed adding " + this + " to " + container);
newOwner = container.getOwner();
}
// Observer notification
Set<SWGObject> containerObservers = getObservers();
if (newOwner != null)
containerObservers.add(newOwner.getCreatureObject());
sendUpdatedContainment(oldObservers, containerObservers);
return ContainerResult.SUCCESS;
}
/**
* Attempts to move this object to the defined container without checking for permissions
* @param container
* @return {@link ContainerResult}
*/
public ContainerResult moveToContainer(SWGObject container) {
return moveToContainer(null, container);
}
private ContainerResult moveToContainerChecks(SWGObject requester, SWGObject container) {
if(!hasPermission(requester, ContainerPermissions.Permission.MOVE)) {
return ContainerResult.NO_PERMISSION;
}
if (container == null)
return ContainerResult.SUCCESS;
// Check if the requester has MOVE permissions to the destination container
if (!container.hasPermission(requester, ContainerPermissions.Permission.MOVE)) {
@@ -197,8 +247,8 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
int arrangementId = container.getArrangementId(this);
if (arrangementId == -1) {
// Item is going to go into the container, so check to see if it'll fit
if (container.getMaxContainerSize() <= container.getContainedObjects().size()) {
Log.w("SWGObject", "Unable to add object to container! Container Full");
if (container.getMaxContainerSize() <= container.getContainedObjects().size() && container.getMaxContainerSize() > 0) {
Log.w("SWGObject", "Unable to add object to container! Container Full. Max Size: %d", container.getMaxContainerSize());
return ContainerResult.CONTAINER_FULL;
}
} else {
@@ -211,49 +261,9 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
}
}
}
// Get a pre-parent-removal list of the observers so we can send create/destroy/update messages
Set<SWGObject> oldObservers = getObservers();
Player prevOwner = getOwner();
if (prevOwner != null)
oldObservers.add(prevOwner.getCreatureObject());
// Remove this object from the old parent if one exists
// SWGObject oldParent = null;
if (parent != null) {
// oldParent = parent;
parent.removeObject(this);
}
if (!container.addObject(this))
Log.e("SWGObject", "Failed adding " + this + " to " + container);
// Observer notification
Player newOwner = getOwner();
Set<SWGObject> containerObservers = getObservers();
if (newOwner != null)
containerObservers.add(newOwner.getCreatureObject());
if (prevOwner != newOwner) {
if (prevOwner != null)
oldObservers.add(prevOwner.getCreatureObject());
if (newOwner != null)
containerObservers.add(newOwner.getCreatureObject());
}
sendUpdatedContainment(oldObservers, containerObservers);
// Log.i("Container", "Moved %s from %s to %s", this, oldParent, container);
return ContainerResult.SUCCESS;
}
/**
* Attempts to move this object to the defined container without checking for permissions
* @param container
* @return {@link ContainerResult}
*/
public ContainerResult moveToContainer(SWGObject container) {
return moveToContainer(null, container);
}
/**
* Checks if the passed object has all of the passed permissions
* @param object Requester to view this container
@@ -402,10 +412,6 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
this.owner = player;
}
public void setParent(SWGObject parent) {
this.parent = parent;
}
public void setLocation(Location l) {
if (l == null)
return;
@@ -475,6 +481,15 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
return parent;
}
public SWGObject getSuperParent() {
SWGObject sParent = parent;
if (sParent == null)
return null;
while (sParent.getParent() != null)
sParent = sParent.getParent();
return sParent;
}
public StringId getStringId() {
return stringId;
}
@@ -679,10 +694,11 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
}
private final void sendSceneDestroyObject(Player target) {
if (target == null)
return;
SceneDestroyObject destroy = new SceneDestroyObject();
destroy.setObjectId(objectId);
if (target != null)
target.sendPacket(destroy);
target.sendPacket(destroy);
}
public void createObject(Player target) {
@@ -714,20 +730,35 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
}
public void clearAware(boolean updateSelf) {
List<SWGObject> objects;
synchronized (objectsAware) {
objects = new ArrayList<>(objectsAware);
}
Set<SWGObject> objects = getObjectsAware();
for (SWGObject o : objects) {
o.awarenessOutOfRange(this, updateSelf);
awarenessOutOfRange(o, true);
o.awarenessOutOfRange(this, true);
awarenessOutOfRange(o, updateSelf);
}
}
public Set <SWGObject> getObjectsAware() {
Set<SWGObject> aware;
synchronized (objectsAware) {
return Collections.unmodifiableSet(objectsAware);
aware = new HashSet<>(objectsAware);
}
if (parent != null) {
aware.addAll(parent.getObjectsAware());
aware.add(getSuperParent());
}
return aware;
}
private boolean isAware(SWGObject obj) {
if (equals(obj))
return true;
synchronized (objectsAware) {
if (objectsAware.contains(obj))
return true;
}
if (parent != null)
return parent.isAware(obj);
return false;
}
public Set<SWGObject> getObservers() {
@@ -741,19 +772,13 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
}
private Set<SWGObject> getObservers(Player owner, boolean initial) {
Set<SWGObject> nearby = new HashSet<>();
Set<SWGObject> nearby;
synchronized (containedObjects) {
nearby.addAll(containedObjects.values());
nearby = new HashSet<>(containedObjects.values());
}
if (initial) {
synchronized (objectsAware) {
nearby.addAll(objectsAware);
}
nearby.addAll(getObjectsAware());
}
return getObserversFromSet(nearby, owner);
}
private Set<SWGObject> getObserversFromSet(Set<SWGObject> nearby, Player owner) {
Set<SWGObject> observers = new HashSet<>();
for (SWGObject aware : nearby) {
if (aware instanceof CreatureObject) {
@@ -797,27 +822,17 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
}
private void sendUpdatedContainment(Set<SWGObject> oldObservers, Set<SWGObject> newObservers) {
if (parent == null)
return;
Set<SWGObject> same = new HashSet<>(oldObservers);
same.retainAll(newObservers);
Set<SWGObject> added = new HashSet<>(newObservers);
added.removeAll(oldObservers);
Set<SWGObject> removed = new HashSet<>(oldObservers);
removed.removeAll(newObservers);
for (SWGObject swgObject : same) {
swgObject.getOwner().sendPacket(new UpdateContainmentMessage(objectId, parent.getObjectId(), slotArrangement));
long newId = (parent == null) ? 0 : parent.getObjectId();
for (SWGObject swgObject : oldObservers) {
if (newObservers.contains(swgObject))
swgObject.getOwner().sendPacket(new UpdateContainmentMessage(objectId, newId, slotArrangement));
else
destroyObject(swgObject.getOwner());
}
for (SWGObject swgObject : added) {
createObject(swgObject.getOwner());
}
for (SWGObject swgObject : removed) {
destroyObject(swgObject.getOwner());
for (SWGObject swgObject : newObservers) {
if (!oldObservers.contains(swgObject))
createObject(swgObject.getOwner());
}
}
@@ -826,45 +841,47 @@ public abstract class SWGObject extends BaselineObject implements Comparable<SWG
synchronized (objectsAware) {
outOfRange = new HashSet<>(objectsAware);
}
outOfRange.addAll(getObservers());
Set<SWGObject> observers = getObserversFromSet(withinRange, getOwner());
outOfRange.removeAll(withinRange);
outOfRange.removeAll(observers);
if (outOfRange.contains(this)) {
Log.e("SWGObject", "outOfRange contains this");
}
if (withinRange.contains(this)) {
Log.e("SWGObject", "withinRange contains this");
}
for (SWGObject o : outOfRange) {
awarenessOutOfRange(o, true);
o.awarenessOutOfRange(this, true);
if (!withinRange.contains(o)) {
awarenessOutOfRange(o, true);
o.awarenessOutOfRange(this, true);
}
}
for (SWGObject o : withinRange) {
awarenessInRange(o, true);
o.awarenessInRange(this, true);
}
for (SWGObject o : observers) {
awarenessInRange(o, true);
o.awarenessInRange(this, true);
if (!outOfRange.contains(o)) {
awarenessInRange(o, true);
o.awarenessInRange(this, true);
}
}
}
protected void awarenessOutOfRange(SWGObject o, boolean sendDestroy) {
boolean success = false;
boolean success = isAware(o);
synchronized (objectsAware) {
success = objectsAware.remove(o);
success = objectsAware.remove(o) && success;
}
if (success && sendDestroy) {
Player owner = o.getOwner();
Player owner = getOwner();
if (owner != null)
destroyObject(owner);
o.destroyObject(owner);
}
}
protected void awarenessInRange(SWGObject o, boolean sendCreate) {
boolean success = false;
boolean success = !isAware(o);
synchronized (objectsAware) {
success = objectsAware.add(o);
success = objectsAware.add(o) && success;
}
if (success && sendCreate) {
Player owner = o.getOwner();
Player owner = getOwner();
if (owner != null)
createObject(owner);
o.createObject(owner);
}
}

View File

@@ -82,7 +82,7 @@ public class BuildingObject extends TangibleObject {
}
@Override
public boolean addObject(SWGObject object) {
protected boolean addObject(SWGObject object) {
boolean added = super.addObject(object);
if (!added || !(object instanceof CellObject))
return added;

View File

@@ -112,12 +112,9 @@ class TerrainBuildoutLoader {
objectTable.put(object.getObjectId(), object);
if (containerId != 0) {
SWGObject container = objectTable.get(containerId);
if (container != null)
container.addObject(object);
else {
object.moveToContainer(container);
if (container == null)
Log.e("TerrainBuildoutLoader", "Failed to load object: " + object.getTemplate());
// objects.add(object);
}
} else {
List<SWGObject> list = objects.get(areaName);
if (list == null) {

View File

@@ -102,11 +102,9 @@ public class TerrainSnapshotLoader {
objectTable.put(object.getObjectId(), object);
if (containerId != 0) {
SWGObject container = objectTable.get(containerId);
if (container != null)
container.addObject(object);
else {
object.moveToContainer(container);
if (container == null)
Log.e("TerrainSnapshotLoader", "Failed to load object: " + object.getTemplate());
}
} else {
objects.add(object);
}

View File

@@ -87,6 +87,7 @@ public class CreatureObject extends TangibleObject {
private String moodAnimation = "neutral";
private String animation = "";
private WeaponObject equippedWeapon = null;
private WeaponObject defaultWeapon = null;
private byte moodId = 0;
private long lookAtTargetId = 0;
private int performanceCounter = 0;
@@ -101,18 +102,18 @@ public class CreatureObject extends TangibleObject {
private String currentCity = "";
private long lastTransform = 0;
private SWGSet<String> missionCriticalObjs = new SWGSet<>(4, 13);
private SWGSet<String> missionCriticalObjs = new SWGSet<>(4, 13);
private SWGSet<String> skills = new SWGSet<String>(1, 3, StringType.ASCII);
private SWGList<Integer> baseAttributes = new SWGList<Integer>(1, 2);
private SWGSet<String> skills = new SWGSet<String>(1, 3, StringType.ASCII);
private SWGList<Integer> hamEncumbList = new SWGList<Integer>(4, 2);
private SWGList<Integer> wounds = new SWGList<Integer>(4, 17);
private SWGList<Integer> wounds = new SWGList<Integer>(4, 17);
private SWGList<Integer> attributes = new SWGList<Integer>(6, 14);
private SWGList<Integer> maxAttributes = new SWGList<Integer>(6, 15);
private SWGList<Equipment> equipmentList = new SWGList<Equipment>(6, 16);
private SWGMap<String, SkillMod> skillMods = new SWGMap<>(4, 3, StringType.ASCII); // TODO: SkillMod structure
private SWGMap<String, Integer> abilities = new SWGMap<>(4, 14, StringType.ASCII);
private SWGMap<String, SkillMod> skillMods = new SWGMap<>(4, 3, StringType.ASCII); // TODO: SkillMod structure
private SWGMap<String, Integer> abilities = new SWGMap<>(4, 14, StringType.ASCII);
private SWGMap<CRC, Buff> buffs = new SWGMap<>(6, 26);
public CreatureObject(long objectId) {
@@ -447,6 +448,14 @@ public class CreatureObject extends TangibleObject {
sendDelta(6, 6, weapon.getObjectId());
}
public WeaponObject getDefaultWeapon() {
return defaultWeapon;
}
public void setDefaultWeapon(WeaponObject defaultWeapon) {
this.defaultWeapon = defaultWeapon;
}
public byte getMoodId() {
return moodId;
}

View File

@@ -4,7 +4,6 @@ import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import resources.objects.creature.CreatureObject;
import resources.server_info.Log;
import utilities.ScheduledUtilities;
public abstract class AIObject extends CreatureObject {
@@ -47,7 +46,6 @@ public abstract class AIObject extends CreatureObject {
*/
public void aiStart() {
if (future != null) {
Log.e(getClass().getSimpleName(), "AI has already started!");
return;
}
future = ScheduledUtilities.scheduleAtFixedRate(() -> aiLoop(), initialDelay, delay, unit);
@@ -64,7 +62,6 @@ public abstract class AIObject extends CreatureObject {
*/
public void aiStop() {
if (future == null) {
Log.e(getClass().getSimpleName(), "AI has already stopped!");
return;
}
future.cancel(true);

View File

@@ -5,6 +5,7 @@ import java.util.concurrent.TimeUnit;
import intents.object.MoveObjectIntent;
import resources.Location;
import resources.Point3D;
import resources.objects.creature.CreatureState;
public class DefaultAIObject extends AIObject {
@@ -56,6 +57,7 @@ public class DefaultAIObject extends AIObject {
protected void aiLoop() {
switch (behavior) {
case FLOAT: aiLoopFloat(); break;
case GUARD: aiLoopGuard(); break;
case STOP:
default: break;
}
@@ -70,16 +72,30 @@ public class DefaultAIObject extends AIObject {
if (getObservers().isEmpty()) // No need to dance if nobody is watching
return;
double dist = Math.sqrt(radius);
double x, z, theta;
double theta;
Location l = getLocation();
Point3D point = new Point3D();
do {
theta = r.nextDouble() * Math.PI * 2;
x = l.getX() + Math.cos(theta) * dist;
z = l.getZ() + Math.sin(theta) * dist;
} while (mainLocation.isWithinDistance(mainLocation.getTerrain(), x, mainLocation.getY(), z, radius));
l.setPosition(x, mainLocation.getY(), z);
point.setX(l.getX() + Math.cos(theta) * dist);
point.setZ(l.getZ() + Math.sin(theta) * dist);
} while (!mainLocation.isWithinFlatDistance(point, radius));
l.setPosition(point.getX(), l.getY(), point.getZ());
l.setHeading(l.getYaw() - Math.toDegrees(theta));
new MoveObjectIntent(this, getParent(), l, 1.37, updateCounter++).broadcast();
}
private void aiLoopGuard() {
if (hasState(CreatureState.COMBAT))
return;
Random r = new Random();
if (r.nextDouble() > 0.25) // Only a 25% movement chance
return;
if (getObservers().isEmpty()) // No need to dance if nobody is watching
return;
double theta = r.nextDouble() * 360;
mainLocation.setHeading(theta);
new MoveObjectIntent(this, getParent(), mainLocation, 1.37, updateCounter++).broadcast();
}
}

View File

@@ -328,16 +328,6 @@ public class PlayerObject extends IntangibleObject {
experience.put(xpType, experiencePoints);
experience.sendDeltaMessage(this);
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
@Override
public int hashCode() {
return super.hashCode();
}
public void createChildrenObjects(Player target) {
SWGObject parent = getParent();

View File

@@ -226,14 +226,13 @@ public class CommandService extends Service {
Command c = commands.remove(CRC.getCrc(set.getString("name").toLowerCase(Locale.ENGLISH)));
if (c == null)
continue;
System.out.println("loaded meleeHit!");
c.setCombatCommand(true);
CombatCommand cc = createAsCombatCommand(c);
cc.setAttackType(AttackType.SINGLE_TARGET);
cc.setForceCombat(set.getBoolean("force_combat"));
cc.setValidTarget(ValidTarget.STANDARD);
cc.setDefaultAnimation(new String[]{"attack_high_right_medium_0", "unarmed_coup_de_grace", "melee_coup_de_grace"});
cc.setDefaultAnimation(new String[]{"combo_3d_medium", "combo_3c_light", "combo_5a_medium"});
addCommand(cc);
}

View File

@@ -164,32 +164,32 @@ public final class FactionService extends Service {
}
private void handleFlagChange(TangibleObject object) {
Player objOwner = object.getOwner();
for (SWGObject o : object.getObservers()) {
if (!(o instanceof TangibleObject))
continue;
TangibleObject observer = (TangibleObject) o;
Player obsOwner = observer.getOwner();
int pvpBitmask = 0;
// They CAN be enemies if they're not from the same faction and neither of them are neutral
if (object.getPvpFaction() != observer.getPvpFaction() && observer.getPvpFaction() != PvpFaction.NEUTRAL) {
if (object.getPvpStatus() == PvpStatus.SPECIALFORCES && observer.getPvpStatus() == PvpStatus.SPECIALFORCES) {
pvpBitmask |= PvpFlag.AGGRESSIVE.getBitmask() | PvpFlag.ATTACKABLE.getBitmask();
}
}
UpdatePvpStatusMessage objectPacket = createPvpStatusMessage(object, observer, object.getPvpFlags() | pvpBitmask);
UpdatePvpStatusMessage targetPacket = createPvpStatusMessage(object, observer, observer.getPvpFlags() | pvpBitmask);
if (objOwner != null)
objOwner.sendPacket(objectPacket, targetPacket);
if (obsOwner != null)
obsOwner.sendPacket(objectPacket);
}
// Player objOwner = object.getOwner();
// for (SWGObject o : object.getObservers()) {
// if (!(o instanceof TangibleObject))
// continue;
// TangibleObject observer = (TangibleObject) o;
// Player obsOwner = observer.getOwner();
// int pvpBitmask = 0;
//
// // They CAN be enemies if they're not from the same faction and neither of them are neutral
// if (object.getPvpFaction() != observer.getPvpFaction() && observer.getPvpFaction() != PvpFaction.NEUTRAL) {
// if (object.getPvpStatus() == PvpStatus.SPECIALFORCES && observer.getPvpStatus() == PvpStatus.SPECIALFORCES) {
// pvpBitmask |= PvpFlag.AGGRESSIVE.getBitmask() | PvpFlag.ATTACKABLE.getBitmask();
// }
// }
// UpdatePvpStatusMessage objectPacket = createPvpStatusMessage(object, observer, object.getPvpFlags() | pvpBitmask);
// UpdatePvpStatusMessage targetPacket = createPvpStatusMessage(object, observer, observer.getPvpFlags() | pvpBitmask);
// if (objOwner != null)
// objOwner.sendPacket(objectPacket, targetPacket);
// if (obsOwner != null)
// obsOwner.sendPacket(objectPacket);
// }
}
private UpdatePvpStatusMessage createPvpStatusMessage(TangibleObject object, TangibleObject observer, int flags) {
Set<PvpFlag> flagSet = PvpFlag.getFlags(object.getPvpFlags());
return new UpdatePvpStatusMessage(object.getPvpFaction(), object.getObjectId(), flagSet.toArray(new PvpFlag[flagSet.size()]));
}
// private UpdatePvpStatusMessage createPvpStatusMessage(TangibleObject object, TangibleObject observer, int flags) {
// Set<PvpFlag> flagSet = PvpFlag.getFlags(object.getPvpFlags());
// return new UpdatePvpStatusMessage(object.getPvpFaction(), object.getObjectId(), flagSet.toArray(new PvpFlag[flagSet.size()]));
// }
}

View File

@@ -51,6 +51,7 @@ import resources.player.Player;
import resources.player.PlayerEvent;
import resources.player.PlayerFlags;
import resources.player.PlayerState;
import resources.player.Player.PlayerServer;
import resources.server_info.Log;
import services.CoreManager;
import utilities.ThreadUtilities;
@@ -122,7 +123,6 @@ public class ConnectionService extends Service {
switch (pei.getEvent()) {
case PE_FIRST_ZONE: {
Player p = pei.getPlayer();
CoreManager.getGalaxy().incrementPopulationCount();
removeFromLists(p);
synchronized (zonedInPlayers) {
zonedInPlayers.add(p);
@@ -130,10 +130,11 @@ public class ConnectionService extends Service {
break;
}
case PE_ZONE_IN_SERVER:
CoreManager.getGalaxy().incrementPopulationCount();
clearPlayerFlag(pei.getPlayer(), pei.getEvent(), PlayerFlags.LD);
break;
case PE_LOGGED_OUT:
if (pei.getPlayer().getCreatureObject() != null)
if (pei.getPlayer().getPlayerServer() != PlayerServer.ZONE)
CoreManager.getGalaxy().decrementPopulationCount();
setPlayerFlag(pei.getPlayer(), pei.getEvent(), PlayerFlags.LD);
logOut(pei.getPlayer(), true);
@@ -209,7 +210,7 @@ public class ConnectionService extends Service {
}
private PlayerObject getPlayerObject(Player p, PlayerEvent event) {
if (p.getPlayerState() != PlayerState.ZONED_IN)
if (p.getPlayerServer() != PlayerServer.ZONE)
return null;
CreatureObject creature = p.getCreatureObject();
if (creature == null) {

View File

@@ -132,7 +132,7 @@ public class ClientBuildoutService extends Service {
private void checkChild(Map<Long, SWGObject> objects, SWGObject obj, long container) {
if (container != 0)
objects.get(container).addObject(obj);
obj.moveToContainer(objects.get(container));
}
private List<String> getEvents() {

View File

@@ -33,7 +33,6 @@ import intents.network.GalacticPacketIntent;
import intents.object.MoveObjectIntent;
import intents.object.ObjectCreatedIntent;
import intents.object.ObjectTeleportIntent;
import intents.object.UpdateObjectAwareness;
import intents.player.PlayerTransformedIntent;
import java.util.HashMap;
@@ -45,7 +44,6 @@ import java.util.Set;
import main.ProjectSWG;
import network.packets.Packet;
import network.packets.swg.zone.CmdSceneReady;
import network.packets.swg.zone.UpdateContainmentMessage;
import network.packets.swg.zone.object_controller.DataTransform;
import network.packets.swg.zone.object_controller.DataTransformWithParent;
import resources.Location;
@@ -73,7 +71,6 @@ public class ObjectAwareness extends Service {
registerForIntent(ObjectCreatedIntent.TYPE);
registerForIntent(ObjectTeleportIntent.TYPE);
registerForIntent(GalacticPacketIntent.TYPE);
registerForIntent(UpdateObjectAwareness.TYPE);
registerForIntent(MoveObjectIntent.TYPE);
loadQuadTree();
}
@@ -97,10 +94,6 @@ public class ObjectAwareness extends Service {
if (i instanceof GalacticPacketIntent)
processGalacticPacketIntent((GalacticPacketIntent) i);
break;
case UpdateObjectAwareness.TYPE:
if (i instanceof UpdateObjectAwareness)
processUpdateObjectAwarenessIntent((UpdateObjectAwareness) i);
break;
case MoveObjectIntent.TYPE:
if (i instanceof MoveObjectIntent)
processMoveObjectIntent((MoveObjectIntent) i);
@@ -172,25 +165,6 @@ public class ObjectAwareness extends Service {
}
}
private void processUpdateObjectAwarenessIntent(UpdateObjectAwareness i) {
SWGObject obj = i.getObject();
Location l = obj.getLocation();
QuadTree <SWGObject> tree = getTree(l);
List<SWGObject> objects;
synchronized (tree) {
objects = tree.get(l.getX(), l.getZ());
}
if (objects.contains(obj)) {
if (!i.isInAwareness()) {
remove(obj);
obj.clearAware(false);
}
return;
}
add(obj);
update(obj);
}
private void processMoveObjectIntent(MoveObjectIntent i) {
if (i.getParent() != null)
processMoveObjectIntentParent(i);
@@ -329,8 +303,7 @@ public class ObjectAwareness extends Service {
*/
private void move(SWGObject object, Location nLocation, boolean update) {
if (object.getParent() != null) {
object.getParent().removeObject(object); // Moving from cell to world
object.sendObserversAndSelf(new UpdateContainmentMessage(object.getObjectId(), 0, object.getSlotArrangement()));
object.moveToContainer(null);
} else {
remove(object); // World to World
}
@@ -352,16 +325,14 @@ public class ObjectAwareness extends Service {
private void move(SWGObject object, SWGObject nParent, Location nLocation) {
SWGObject parent = object.getParent();
if (parent != null && nParent != parent) {
parent.removeObject(object); // Moving from cell to cell, for instance
object.moveToContainer(null);
} else if (parent == null) {
remove(object); // Moving from world to cell
}
if (object.getParent() == null) { // Should have been updated in removeObject()
nParent.addObject(object); // If necessary, add to new cell
object.sendObserversAndSelf(new UpdateContainmentMessage(object.getObjectId(), nParent.getObjectId(), object.getSlotArrangement()));
object.moveToContainer(nParent);
}
object.setLocation(nLocation);
object.clearAware(false);
}
/**
@@ -390,8 +361,7 @@ public class ObjectAwareness extends Service {
private void moveFromOld(SWGObject object, Location oldLocation, boolean update) {
if (object.getParent() != null) {
object.getParent().removeObject(object); // Moving from cell to world
object.sendObserversAndSelf(new UpdateContainmentMessage(object.getObjectId(), 0, object.getSlotArrangement()));
object.moveToContainer(null);
} else {
removeFromLocation(object, oldLocation); // World to World
}

View File

@@ -104,19 +104,24 @@ public class ObjectManager extends Manager {
@Override
public boolean initialize() {
loadClientObjects();
loadObjects();
if (!loadObjects())
return false;
return super.initialize();
}
private void loadObjects() {
private boolean loadObjects() {
long startLoad = System.nanoTime();
Log.i("ObjectManager", "Loading objects from ObjectDatabase...");
synchronized (database) {
database.load();
database.traverse((obj) -> loadObject(obj));
if (database.fileExists()) {
if (!database.load())
return false;
database.traverse((obj) -> loadObject(obj));
}
}
double loadTime = (System.nanoTime() - startLoad) / 1E6;
Log.i("ObjectManager", "Finished loading %d objects. Time: %fms", database.size(), loadTime);
return true;
}
private void loadClientObjects() {
@@ -145,13 +150,10 @@ public class ObjectManager extends Manager {
if (obj.getParent() != null) {
if (!obj.getParent().isGenerated()) {
long id = obj.getParent().getObjectId();
obj.getParent().removeObject(obj);
SWGObject parent = getObjectById(id);
if (parent != null)
parent.addObject(obj);
else {
obj.moveToContainer(parent);
if (parent == null)
Log.e("ObjectManager", "Parent for %s is null! ParentID: %d", obj, id);
}
} else {
updateBuildoutParent(obj.getParent());
}
@@ -308,7 +310,7 @@ public class ObjectManager extends Manager {
}
object.sendObserversAndSelf(new SceneDestroyObject(objId));
parent.removeObject(object);
object.moveToContainer(null);
} else {
object.sendObservers(new SceneDestroyObject(objId));
}
@@ -350,9 +352,7 @@ public class ObjectManager extends Manager {
return null;
}
obj.setLocation(l);
if (parent != null) {
parent.addObject(obj);
}
obj.moveToContainer(parent);
synchronized (database) {
if (addToDatabase) {
database.put(obj.getObjectId(), obj);

View File

@@ -313,10 +313,10 @@ public class CharacterCreationService extends Service {
createStarterClothing(objManager, creatureObj, create.getRace(), create.getClothes());
creatureObj.setVolume(0x000F4240);
creatureObj.setOwner(player);
creatureObj.addObject(playerObj); // ghost slot
playerObj.moveToContainer(creatureObj); // ghost slot
playerObj.setAdminTag(player.getAccessLevel());
creatureObj.setOwner(player);
player.setCreatureObject(creatureObj);
new SkillBoxGrantedIntent("social_entertainer_novice", creatureObj).broadcast();
new SkillBoxGrantedIntent("outdoors_scout_novice", creatureObj).broadcast();
@@ -355,7 +355,7 @@ public class CharacterCreationService extends Service {
return null;
}
SWGObject obj = objManager.createObject(template, info.location);
cell.addObject(obj);
obj.moveToContainer(cell);
if (obj instanceof CreatureObject)
return (CreatureObject) obj;
return null;
@@ -387,7 +387,7 @@ public class CharacterCreationService extends Service {
TangibleObject hairObj = createTangible(objManager, ClientFactory.formatToSharedFile(hair));
hairObj.setAppearanceData(customization);
creatureObj.addObject(hairObj); // slot = hair
hairObj.moveToContainer(creatureObj); // hair slot
creatureObj.addEquipment(hairObj);
}
@@ -403,6 +403,7 @@ public class CharacterCreationService extends Service {
creatureObj.addAbility("rangedShot");
WeaponObject defWeapon = (WeaponObject) createInventoryObject(objManager, creatureObj, "object/weapon/melee/unarmed/shared_unarmed_default_player.iff");
creatureObj.setDefaultWeapon(defWeapon);
defWeapon.setMaxRange(5);
creatureObj.setEquippedWeapon(defWeapon);
creatureObj.addEquipment(createInventoryObject(objManager, creatureObj, "object/tangible/inventory/shared_character_inventory.iff"));

View File

@@ -164,7 +164,7 @@ public final class SpawnerService extends Service {
DefaultAIObject object = ObjectCreator.createObjectFromTemplate(createTemplate(getRandomIff(set.getString("iff"))), DefaultAIObject.class);
object.setLocation(loc);
if (parent != null)
parent.addObject(object);
object.moveToContainer(parent);
object.setName(getCreatureName(name));
object.setLevel((short) set.getInt("combat_level"));
object.setDifficulty(difficulty);

View File

@@ -147,9 +147,8 @@ public class StaticService extends Service {
Location loc = new Location(x, y, z, parent.getTerrain());
loc.setHeading(heading);
SWGObject obj = ObjectCreator.createObjectFromTemplate(iff);
obj.setParent(parent);
obj.setLocation(loc);
parent.addObject(obj);
obj.moveToContainer(parent);
new ObjectCreatedIntent(obj).broadcast();
return obj;
}

View File

@@ -6,7 +6,6 @@ import intents.LoginEventIntent;
import intents.PlayerEventIntent;
import intents.object.ObjectCreatedIntent;
import intents.object.ObjectTeleportIntent;
import intents.object.UpdateObjectAwareness;
import org.junit.Assert;
import org.junit.Test;
@@ -21,7 +20,7 @@ public class TestIntentQueue {
Intent [] intents = new Intent[5];
intents[0] = new PlayerEventIntent(null, null);
intents[1] = new ObjectTeleportIntent(null, null);
intents[2] = new UpdateObjectAwareness(null, false);
intents[2] = new ObjectCreatedIntent(null);
intents[3] = new ObjectCreatedIntent(null);
intents[4] = new LoginEventIntent(0, null);
IntentQueue queue = new IntentQueue();