Finished rewrite of sampling

This commit is contained in:
Obique
2019-03-03 16:41:11 -06:00
parent 445ea34f49
commit 864b114d59
2 changed files with 133 additions and 140 deletions
@@ -26,146 +26,69 @@
***********************************************************************************/
package com.projectswg.holocore.resources.gameplay.crafting.survey;
import com.projectswg.common.data.encodables.oob.ProsePackage;
import com.projectswg.common.data.encodables.oob.StringId;
import com.projectswg.common.data.encodables.oob.waypoint.WaypointColor;
import com.projectswg.common.data.encodables.tangible.Posture;
import com.projectswg.common.data.location.Location;
import com.projectswg.common.network.packets.swg.zone.PlayClientEffectObjectMessage;
import com.projectswg.common.network.packets.swg.zone.PlayMusicMessage;
import com.projectswg.common.network.packets.swg.zone.chat.ChatSystemMessage;
import com.projectswg.common.network.packets.swg.zone.chat.ChatSystemMessage.SystemChatType;
import com.projectswg.holocore.intents.support.global.network.CloseConnectionIntent;
import com.projectswg.holocore.intents.support.objects.swg.DestroyObjectIntent;
import com.projectswg.holocore.intents.support.objects.swg.ObjectCreatedIntent;
import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.GalacticResource;
import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.GalacticResourceSpawn;
import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.GalacticResourceStats;
import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.RawResourceType;
import com.projectswg.holocore.resources.gameplay.crafting.resource.galactic.storage.GalacticResourceContainer;
import com.projectswg.holocore.resources.gameplay.crafting.resource.raw.RawResource;
import com.projectswg.holocore.resources.support.data.server_info.StandardLog;
import com.projectswg.holocore.resources.support.data.server_info.loader.CollectionLoader.CollectionSlotInfo;
import com.projectswg.holocore.resources.support.data.server_info.loader.DataLoader;
import com.projectswg.holocore.resources.support.global.network.DisconnectReason;
import com.projectswg.holocore.resources.support.global.player.Player;
import com.projectswg.holocore.resources.support.global.zone.sui.SuiButtons;
import com.projectswg.holocore.resources.support.global.zone.sui.SuiListBox;
import com.projectswg.holocore.resources.support.global.zone.sui.SuiMessageBox;
import com.projectswg.holocore.resources.support.global.zone.sui.SuiWindow;
import com.projectswg.holocore.resources.support.objects.ObjectCreator;
import com.projectswg.holocore.resources.support.objects.permissions.ContainerResult;
import com.projectswg.holocore.resources.support.objects.permissions.ReadWritePermissions;
import com.projectswg.holocore.resources.support.objects.swg.ServerAttribute;
import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject;
import com.projectswg.holocore.resources.support.objects.swg.resource.ResourceContainerObject;
import com.projectswg.holocore.resources.support.objects.swg.tangible.TangibleObject;
import com.projectswg.holocore.resources.support.objects.swg.waypoint.WaypointObject;
import me.joshlarson.jlcommon.concurrency.ScheduledThreadPool;
import me.joshlarson.jlcommon.control.IntentChain;
import me.joshlarson.jlcommon.log.Log;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.jetbrains.annotations.Nullable;
public class SampleHandler {
private final AtomicReference<Location> sampleLocation;
private final AtomicReference<ScheduledFuture<?>> sampleLoop;
private final CreatureObject creature;
private final TangibleObject surveyTool;
private final AtomicBoolean running;
private final SampleLoopSession session;
private final ScheduledThreadPool executor;
private volatile @Nullable SampleLoopSession sampleSession;
public SampleHandler(CreatureObject creature, TangibleObject surveyTool, ScheduledThreadPool executor) {
this.sampleLocation = new AtomicReference<>(null);
this.sampleLoop = new AtomicReference<>(null);
this.session = new AtomicReference<>(null);
this.creature = creature;
this.surveyTool = surveyTool;
this.running = new AtomicBoolean(false);
this.executor = executor;
this.sampleSession = null;
}
public void startSession() {
public synchronized void startSession() {
}
public void stopSession() {
stopSampleLoop();
public synchronized void stopSession() {
SampleLoopSession session = this.sampleSession;
this.sampleSession = null;
if (session != null)
session.stopSession();
}
public void startSampleLoop(GalacticResource resource) {
Location location = creature.getWorldLocation();
double concentration = getConcentration(resource, location);
public synchronized void startSampleLoop(GalacticResource resource) {
Location sampleLocation = creature.getWorldLocation();
if (!isAllowedToSample(resource, concentration))
return;
if (!running.getAndSet(true)) {
ScheduledFuture<?> prevLoop = sampleLoop.getAndSet(executor.executeWithFixedRate(5000, 5000, () -> sample(resource, location)));
stopSampleLoop(prevLoop);
sampleLocation.set(location);
creature.setPosture(Posture.CROUCHED);
creature.setMovementPercent(0);
creature.setTurnScale(0);
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, new ProsePackage(new StringId("@survey:start_sampling"), "TO", resource.getName())));
Log.d("%s started a sample session with %s and concentration %.1f", creature.getObjectName(), resource.getName(), concentration);
SampleLoopSession prevSession = this.sampleSession;
if (prevSession != null && prevSession.isMatching(creature, surveyTool, resource, sampleLocation)) {
if (!prevSession.isSampling())
prevSession.startSession(executor);
} else {
SampleLoopSession nextSession = new SampleLoopSession(creature, surveyTool, resource, sampleLocation);
if (prevSession != null)
prevSession.stopSession();
nextSession.startSession(executor);
this.sampleSession = nextSession;
}
}
public void onPlayerMoved() {
Location oldLocation = sampleLocation.get();
Location newLocation = creature.getWorldLocation();
if (oldLocation == null)
return;
if (oldLocation.distanceTo(newLocation) >= 0.5 || oldLocation.getTerrain() != newLocation.getTerrain())
stopSampleLoop();
public synchronized void onPlayerMoved() {
SampleLoopSession session = this.sampleSession;
if (session != null)
session.onPlayerMoved();
}
public void stopSampleLoop() {
if (!running.getAndSet(false))
return;
stopSampleLoop(sampleLoop.getAndSet(null));
public synchronized void stopSampleLoop() {
stopSession();
}
public boolean isSampling() {
ScheduledFuture<?> loop = this.sampleLoop.get();
return loop != null && !loop.isDone();
}
private void stopSampleLoop(ScheduledFuture<?> loop) {
if (loop != null && loop.cancel(false)) {
creature.setPosture(Posture.UPRIGHT);
creature.setMovementPercent(1);
creature.setTurnScale(1);
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:sample_cancel"));
SuiWindow window = this.sampleWindow.get();
if (window != null)
window.close(creature.getOwner());
}
}
private boolean isAllowedToSample(GalacticResource resource, double concentration) {
if (creature.getSkillModValue("surveying") < 20) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "You aren't allowed to sample without a surveying skillmod"));
return false;
}
if (creature.isInCombat()) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:sample_cancel_attack"));
return false;
}
if (concentration <= 0.3) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, new ProsePackage(new StringId("@survey:density_below_threshold"), "TO", resource.getName())));
return false;
}
return true;
SampleLoopSession session = this.sampleSession;
return session != null && session.isSampling();
}
}
@@ -30,6 +30,7 @@ package com.projectswg.holocore.resources.gameplay.crafting.survey;
import com.projectswg.common.data.encodables.oob.ProsePackage;
import com.projectswg.common.data.encodables.oob.StringId;
import com.projectswg.common.data.encodables.oob.waypoint.WaypointColor;
import com.projectswg.common.data.encodables.tangible.Posture;
import com.projectswg.common.data.location.Location;
import com.projectswg.common.network.packets.swg.zone.PlayClientEffectObjectMessage;
import com.projectswg.common.network.packets.swg.zone.PlayMusicMessage;
@@ -58,12 +59,14 @@ import com.projectswg.holocore.resources.support.objects.permissions.ContainerRe
import com.projectswg.holocore.resources.support.objects.permissions.ReadWritePermissions;
import com.projectswg.holocore.resources.support.objects.swg.ServerAttribute;
import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureObject;
import com.projectswg.holocore.resources.support.objects.swg.creature.CreatureState;
import com.projectswg.holocore.resources.support.objects.swg.resource.ResourceContainerObject;
import com.projectswg.holocore.resources.support.objects.swg.tangible.TangibleObject;
import com.projectswg.holocore.resources.support.objects.swg.waypoint.WaypointObject;
import me.joshlarson.jlcommon.concurrency.ScheduledThreadPool;
import me.joshlarson.jlcommon.control.IntentChain;
import me.joshlarson.jlcommon.log.Log;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map.Entry;
@@ -72,65 +75,96 @@ import java.util.concurrent.ThreadLocalRandom;
public class SampleLoopSession {
private final CreatureObject creature;
private final TangibleObject surveyTool;
private final GalacticResource resource;
private final @NotNull CreatureObject creature;
private final @NotNull TangibleObject surveyTool;
private final @NotNull GalacticResource resource;
private final @NotNull Location sampleLocation;
private SuiWindow sampleWindow;
private Location sampleLocation;
private ScheduledFuture<?> loopFuture;
private boolean paused;
private int sampleMultiplier;
public SampleLoopSession(CreatureObject creature, TangibleObject surveyTool, GalacticResource resource) {
public SampleLoopSession(@NotNull CreatureObject creature, @NotNull TangibleObject surveyTool, @NotNull GalacticResource resource, @NotNull Location sampleLocation) {
this.creature = creature;
this.surveyTool = surveyTool;
this.resource = resource;
this.sampleLocation = sampleLocation;
this.sampleWindow = null;
this.sampleLocation = null;
this.loopFuture = null;
this.paused = false;
this.sampleMultiplier = 1;
}
public synchronized void startSession(ScheduledThreadPool executor, GalacticResource resource, Location sampleLocation) {
this.sampleLocation = sampleLocation;
public boolean isMatching(CreatureObject creature, TangibleObject surveyTool, GalacticResource resource, Location sampleLocation) {
if (this.sampleLocation.distanceTo(sampleLocation) >= 0.5 || this.sampleLocation.getTerrain() != sampleLocation.getTerrain())
return false; // Too far away for the same session
return this.creature.equals(creature) && this.surveyTool.equals(surveyTool) && this.resource.equals(resource);
}
public synchronized boolean isSampling() {
return loopFuture != null && !loopFuture.isDone();
}
public synchronized void onPlayerMoved() {
Location oldLocation = sampleLocation;
Location newLocation = creature.getWorldLocation();
if (oldLocation.distanceTo(newLocation) >= 0.5 || oldLocation.getTerrain() != newLocation.getTerrain())
stopSession();
}
/**
* Attempts to start the sample loop session with the specified executor. If the loop is unable to start, this function returns false; otherwise this function returns true.
* @param executor the executor for the loop to run on
* @return TRUE if the sample loop has begin, FALSE otherwise
*/
public synchronized boolean startSession(ScheduledThreadPool executor) {
if (loopFuture != null) {
loopFuture.cancel(false);
}
double concentration = getConcentration();
if (!isAllowedToSample(concentration))
return false;
loopFuture = executor.executeWithFixedRate(5000, 5000, this::sample);
creature.setPosture(Posture.CROUCHED);
creature.setMovementPercent(0);
creature.setTurnScale(0);
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, new ProsePackage(new StringId("@survey:start_sampling"), "TO", resource.getName())));
StandardLog.onPlayerTrace(this, creature, "started a sample session with %s and concentration %.1f", resource.getName(), concentration);
return true;
}
public synchronized void stopSession() {
if (loopFuture != null)
loopFuture.cancel(false);
else
return;
loopFuture = null;
if (sampleWindow != null) {
Player owner = creature.getOwner();
if (owner != null)
sampleWindow.close(owner);
}
sampleWindow = null;
creature.setPosture(Posture.UPRIGHT);
creature.setMovementPercent(1);
creature.setTurnScale(1);
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:sample_cancel"));
}
private synchronized void sample() {
if (GalacticResourceContainer.getContainer().getTerrainResourceSpawns(resource, creature.getTerrain()).isEmpty()) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:sample_empty"));
stopSession();
return;
}
if (!creature.getInventory().getContainedObjects().contains(surveyTool)) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:sample_gone"));
stopSession();
return;
}
double concentration = getConcentration();
if (concentration <= 0.3) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, new ProsePackage(new StringId("@survey:density_below_threshold"), "TO", resource.getName())));
stopSession();
return;
}
if (paused)
private void sample() {
final double concentration = getConcentration();
if (!isAllowedToSample(concentration) || paused)
return;
ThreadLocalRandom random = ThreadLocalRandom.current();
if (random.nextDouble() > concentration) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, new ProsePackage(new StringId("@survey:sample_failed"), "TO", resource.getName())));
sendSampleEffects();
return;
}
@@ -167,8 +201,7 @@ public class SampleLoopSession {
ObjectCreatedIntent.broadcast(resourceObject);
break;
}
creature.sendSelf(new PlayMusicMessage(0, getMusicFile(), 1, false));
creature.sendObservers(new PlayClientEffectObjectMessage(getEffectFile(), "", creature.getObjectId(), ""));
sendSampleEffects();
}
private void openConcentrationWindow() {
@@ -230,9 +263,6 @@ public class SampleLoopSession {
}
private void createHighestConcentrationWaypoint() {
if (sampleLocation == null)
return;
double highestX = sampleLocation.getX();
double highestZ = sampleLocation.getZ();
double highest = 0;
@@ -258,6 +288,41 @@ public class SampleLoopSession {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:node_waypoint"));
}
private boolean isAllowedToSample(double concentration) {
if (GalacticResourceContainer.getContainer().getTerrainResourceSpawns(resource, creature.getTerrain()).isEmpty()) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:sample_empty"));
stopSession();
return false;
}
if (!creature.getInventory().getContainedObjects().contains(surveyTool)) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:sample_gone"));
stopSession();
return false;
}
if (creature.getSkillModValue("surveying") < 20) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "You aren't allowed to sample without a surveying skillmod"));
return false;
}
if (creature.isStatesBitmask(CreatureState.RIDING_MOUNT)) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "You aren't allowed to sample while on a mount"));
return false;
}
if (creature.getParent() != null) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "You aren't allowed to sample while within a building"));
return false;
}
if (creature.isInCombat()) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, "@survey:sample_cancel_attack"));
return false;
}
if (concentration <= 0.3) {
creature.sendSelf(new ChatSystemMessage(SystemChatType.PERSONAL, new ProsePackage(new StringId("@survey:density_below_threshold"), "TO", resource.getName())));
stopSession();
return false;
}
return true;
}
private double getConcentration() {
List<GalacticResourceSpawn> spawns = GalacticResourceContainer.getContainer().getTerrainResourceSpawns(resource, creature.getTerrain());
double concentration = 0;
@@ -295,10 +360,15 @@ public class SampleLoopSession {
transferStat(obj, "res_toughness", stats.getUnitToughness());
}
private static void transferStat(TangibleObject obj, String name, int attr) {
private void transferStat(TangibleObject obj, String name, int attr) {
obj.addAttribute("@obj_attr_n:" + name, String.valueOf(attr));
}
private void sendSampleEffects() {
creature.sendSelf(new PlayMusicMessage(0, getMusicFile(), 1, false));
creature.sendObservers(new PlayClientEffectObjectMessage(getEffectFile(), "", creature.getObjectId(), ""));
}
private String getMusicFile() {
RawResource rawResource = resource.getRawResource();
if (RawResourceType.MINERAL.isResourceType(rawResource))