diff --git a/src/com/massivecraft/massivecore/ConfServer.java b/src/com/massivecraft/massivecore/ConfServer.java index 34784a8e..4c867bed 100644 --- a/src/com/massivecraft/massivecore/ConfServer.java +++ b/src/com/massivecraft/massivecore/ConfServer.java @@ -19,6 +19,8 @@ public class ConfServer extends SimpleConfig // FIELDS // -------------------------------------------- // + public static boolean localPollingEnabled = true; + public static String serverid = UUID.randomUUID().toString(); public static String dburi = "default"; diff --git a/src/com/massivecraft/massivecore/MassiveCore.java b/src/com/massivecraft/massivecore/MassiveCore.java index e643a6f4..bf73a16f 100644 --- a/src/com/massivecraft/massivecore/MassiveCore.java +++ b/src/com/massivecraft/massivecore/MassiveCore.java @@ -253,7 +253,7 @@ public class MassiveCore extends MassivePlugin // Start the examine threads // Start AFTER initializing the MConf, because they rely on the MConf. - ModificationPollerLocal.get().start(); + if (ConfServer.localPollingEnabled) ModificationPollerLocal.get().start(); ModificationPollerRemote.get().start(); // Delete Files (at once and additionally after all plugins loaded) diff --git a/src/com/massivecraft/massivecore/store/Coll.java b/src/com/massivecraft/massivecore/store/Coll.java index a647a017..d82c7ba7 100644 --- a/src/com/massivecraft/massivecore/store/Coll.java +++ b/src/com/massivecraft/massivecore/store/Coll.java @@ -1,5 +1,6 @@ package com.massivecraft.massivecore.store; +import com.massivecraft.massivecore.ConfServer; import com.massivecraft.massivecore.MassiveCore; import com.massivecraft.massivecore.MassiveCoreMConf; import com.massivecraft.massivecore.MassivePlugin; @@ -7,6 +8,7 @@ import com.massivecraft.massivecore.collections.MassiveList; import com.massivecraft.massivecore.comparator.ComparatorNaturalOrder; import com.massivecraft.massivecore.mixin.MixinModification; import com.massivecraft.massivecore.store.migrator.MigratorUtil; +import com.massivecraft.massivecore.util.MUtil; import com.massivecraft.massivecore.util.ReflectionUtil; import com.massivecraft.massivecore.util.Txt; import com.massivecraft.massivecore.xlib.gson.Gson; @@ -156,6 +158,7 @@ public class Coll> extends CollAbstract { if (id == null) throw new NullPointerException("id"); if (modification == null) throw new NullPointerException("modification"); + Modification old = this.identifiedModifications.get(id); if (old != null && modification.getPriority() <= old.getPriority()) return; this.identifiedModifications.put(id, modification); @@ -168,6 +171,13 @@ public class Coll> extends CollAbstract this.identifiedModifications.remove(id); } + @Override + public Modification getIdentifiedModificationFixed(String id) + { + if (id == null) throw new NullPointerException("id"); + return this.identifiedModifications.get(id); + } + // -------------------------------------------- // // SYNCLOG // -------------------------------------------- // @@ -243,7 +253,7 @@ public class Coll> extends CollAbstract entity.clearSyncLogFields(); JsonObject raw = this.getGson().toJsonTree(entity, this.getEntityClass()).getAsJsonObject(); - entity.setLastRaw(raw); + if (ConfServer.localPollingEnabled) entity.setLastRaw(raw); if (this.isDefault(entity)) { @@ -341,7 +351,7 @@ public class Coll> extends CollAbstract // this.putIdentifiedModificationFixed(id, Modification.UNKNOWN); } - entity.setLastRaw(raw); + if (ConfServer.localPollingEnabled) entity.setLastRaw(raw); entity.setLastMtime(mtime); entity.setLastDefault(false); @@ -429,11 +439,11 @@ public class Coll> extends CollAbstract // ...and it was modified remotely. if ( ! remoteMtime.equals(lastMtime)) return Modification.REMOTE_ALTER; } - // ...and we are looking for local changes + // ...and we are looking for local changes ... if (local) { - // ...and it was modified locally. - if (this.examineHasLocalAlterFixed(id, localEntity)) return Modification.LOCAL_ALTER; + // ... and it was modified locally. + if (this.examineHasLocalAlterFixed(id, localEntity)) return Modification.LOCAL_ALTER; } } // Otherwise, if we only have it locally... @@ -464,6 +474,16 @@ public class Coll> extends CollAbstract protected boolean examineHasLocalAlterFixed(String id, E entity) { + if (!ConfServer.localPollingEnabled) + { + if (MStore.DEBUG_ENABLED) + { + this.getPlugin().log("Attempted examineHasLocalAlterFixed in " + this.getDebugName() + " for " + id); + MUtil.stackTraceDebug("examineHasLocalAlterFixed"); + } + return false; + } + JsonObject lastRaw = entity.getLastRaw(); JsonObject currentRaw = null; @@ -479,32 +499,18 @@ public class Coll> extends CollAbstract MassiveCore.get().log(Txt.parse("Collection: %s", this.getName())); throw new RuntimeException(e); } - + return !MStore.equal(lastRaw, currentRaw); } @Override - public Modification syncIdFixed(String id, Modification modification, Entry remoteEntry) + public Modification syncIdFixed(String id, final Modification suppliedModification, Entry remoteEntry) { if (id == null) throw new NullPointerException("id"); - if (modification == null || modification.isUnknown()) - { - Long remoteMtime = null; - if (remoteEntry != null) remoteMtime = remoteEntry.getValue(); - - Modification actualModification = this.examineIdFixed(id, remoteMtime, true, true); - if (MassiveCoreMConf.get().warnOnLocalAlter && modification == Modification.UNKNOWN_LOG && actualModification.isModified()) - { - E entity = this.idToEntity.get(id); - if (entity != null) - { - this.logModification(entity, actualModification); - } - } - modification = actualModification; - } - if (MStore.DEBUG_ENABLED) System.out.println(this.getDebugName() + " syncronising " + modification + " on " + id); + Modification modification = getActualModification(id, suppliedModification, remoteEntry); + + if (MStore.DEBUG_ENABLED) this.getPlugin().log((this.getDebugName() + " syncronising " + modification + " (" + suppliedModification + ") on " + id)); // DEBUG // MassiveCore.get().log(Txt.parse("syncId Coll: %s Entity: %s Modification: %s", this.getName(), id, modification)); @@ -553,10 +559,66 @@ public class Coll> extends CollAbstract return modification; } + private Modification getActualModification(String id, Modification modification, Entry remoteEntry) + { + if (id == null) throw new NullPointerException("id"); + + if (modification != null && !modification.isUnknown()) + { + return modification; + } + + Long remoteMtime = null; + if (remoteEntry != null) remoteMtime = remoteEntry.getValue(); + + // We look only remotely for changes, because local ones should be caught by .changed() + // or by the poller. This way we are certain, that all local changes where .changed() is not called + // they are found by the poller and then reported appropriately. + Modification actualModification = this.examineIdFixed(id, remoteMtime, false, true); + + if (actualModification == Modification.NONE && (modification == Modification.UNKNOWN_CHANGED || modification == Modification.UNKNOWN_LOG)) + { + actualModification = Modification.LOCAL_ALTER; + checkActuallyModifiedFixed(id); + } + + if (MassiveCoreMConf.get().warnOnLocalAlter && modification == Modification.UNKNOWN_LOG && actualModification.isModified()) + { + E entity = this.idToEntity.get(id); + if (entity != null) + { + this.logModification(entity, actualModification); + } + } + return actualModification; + } + + private void checkActuallyModifiedFixed(String id) + { + if (!ConfServer.localPollingEnabled || !MassiveCoreMConf.get().warnOnLocalAlter) return; + + E entity = this.getFixed(id); + boolean modified = this.examineHasLocalAlterFixed(id, entity); + if (modified) return; + + List messages = new MassiveList<>(); + messages.add(Txt.parse("%s", this.getDebugName())); + messages.add(Txt.parse("%s", entity.getId())); + String change = Txt.implode(messages, Txt.parse(" | ")); + String message = Txt.parse("[No Modification] %s", change); + this.getPlugin().log(message); + } + protected void logModification(E entity, Modification modification) { JsonObject lastRaw = entity.getLastRaw(); + if (!ConfServer.localPollingEnabled) + { + if (MStore.DEBUG_ENABLED) this.getPlugin().log("Attempted logModification in " + this.getDebugName() + " for " + entity.getId()); + return; + } + if (lastRaw == null) { List messages = new MassiveList<>(); @@ -603,7 +665,7 @@ public class Coll> extends CollAbstract String change = Txt.implode(changes, Txt.parse(" | ")); String message = Txt.parse("[Unreported Modification] %s", change); - MassiveCore.get().log(message); + this.getPlugin().log(message); } @Override @@ -694,7 +756,7 @@ public class Coll> extends CollAbstract { if (modification.isModified()) { - if (MStore.DEBUG_ENABLED) System.out.println(this.getDebugName() + " identified " + modification + " on " + id); + if (MStore.DEBUG_ENABLED) this.getPlugin().log(this.getDebugName() + " identified " + modification + " on " + id); if (veto != null && ! modification.isSafe()) modification = veto; this.putIdentifiedModificationFixed(id, modification); } @@ -714,7 +776,7 @@ public class Coll> extends CollAbstract @Override public void syncAll() { - this.identifyModifications(null); + this.identifyRemoteModifications(null); this.syncIdentified(); } @@ -891,8 +953,10 @@ public class Coll> extends CollAbstract this.getPusher().deinit(); } - // TODO: Save outwards only? We may want to avoid loads at this stage... - this.syncAll(); + // syncIdentified is probably good enough. We need not load, and when + // lastRaw is not present we can't identify local modifications anyway. + //this.syncAll(); + this.syncIdentified(); name2instance.remove(this.getName()); } diff --git a/src/com/massivecraft/massivecore/store/CollAbstract.java b/src/com/massivecraft/massivecore/store/CollAbstract.java index 3a5066af..2b08660f 100644 --- a/src/com/massivecraft/massivecore/store/CollAbstract.java +++ b/src/com/massivecraft/massivecore/store/CollAbstract.java @@ -99,7 +99,7 @@ public abstract class CollAbstract> extends EntityContainerA public Modification syncIdFixed(String id) { if (id == null) throw new NullPointerException("id"); - return this.syncIdFixed(id, null); + return this.syncIdFixed(id, this.getIdentifiedModificationFixed(id)); } @Override @@ -109,4 +109,11 @@ public abstract class CollAbstract> extends EntityContainerA return this.syncIdFixed(id, modification, null); } + @Override + public Modification getIdentifiedModification(Object oid) + { + if (oid == null) throw new NullPointerException("oid"); + return this.getIdentifiedModificationFixed(this.fixIdOrThrow(oid)); + } + } diff --git a/src/com/massivecraft/massivecore/store/CollInterface.java b/src/com/massivecraft/massivecore/store/CollInterface.java index 79d46c59..6e2d6915 100644 --- a/src/com/massivecraft/massivecore/store/CollInterface.java +++ b/src/com/massivecraft/massivecore/store/CollInterface.java @@ -98,6 +98,9 @@ public interface CollInterface> extends Named, Active, Ident void identifyRemoteModifications(Modification veto); void identifyRemoteModificationFixed(String id, Long remoteMtime, Modification veto); + Modification getIdentifiedModification(Object oid); + Modification getIdentifiedModificationFixed(String id); + // Init void initLoadAllFromRemote(); diff --git a/src/com/massivecraft/massivecore/store/EntityInternal.java b/src/com/massivecraft/massivecore/store/EntityInternal.java index 1cc94374..f173f820 100644 --- a/src/com/massivecraft/massivecore/store/EntityInternal.java +++ b/src/com/massivecraft/massivecore/store/EntityInternal.java @@ -3,6 +3,7 @@ package com.massivecraft.massivecore.store; import com.massivecraft.massivecore.Identified; import com.massivecraft.massivecore.MassiveCore; import com.massivecraft.massivecore.store.accessor.Accessor; +import com.massivecraft.massivecore.util.MUtil; import com.massivecraft.massivecore.xlib.gson.Gson; import java.lang.ref.WeakReference; @@ -96,7 +97,7 @@ public class EntityInternal> implements Identified //System.out.println(this.getColl().getName() + ": " +this.getId() + " was modified locally"); - this.getContainer().putIdentifiedModificationFixed(this.getId(), Modification.UNKNOWN); + this.getContainer().putIdentifiedModificationFixed(this.getId(), Modification.UNKNOWN_CHANGED); } // -------------------------------------------- // @@ -132,6 +133,19 @@ public class EntityInternal> implements Identified return Objects.equals(value, standard) ? null : value; } + public T convertSet(T value, T current, T standard) + { + current = this.convertGet(current, standard); + this.changed(value, current); + return Objects.equals(value, standard) ? null : value; + } + + public void changed(Object o1, Object o2) + { + if (MUtil.equalsishObject(o1, o2)) return; + changed(); + } + // BOOLEAN public boolean convertGet(Boolean value) { diff --git a/src/com/massivecraft/massivecore/store/GsonEqualsChecker.java b/src/com/massivecraft/massivecore/store/GsonEqualsChecker.java index 3c3b4a57..14d4d474 100644 --- a/src/com/massivecraft/massivecore/store/GsonEqualsChecker.java +++ b/src/com/massivecraft/massivecore/store/GsonEqualsChecker.java @@ -1,5 +1,6 @@ package com.massivecraft.massivecore.store; +import com.massivecraft.massivecore.util.MUtil; import com.massivecraft.massivecore.xlib.gson.JsonArray; import com.massivecraft.massivecore.xlib.gson.JsonElement; import com.massivecraft.massivecore.xlib.gson.JsonNull; @@ -139,7 +140,7 @@ public class GsonEqualsChecker if (floating) { // Our epsilon is pretty big in order to see float and double as the same. - return Math.abs(oneNumber.doubleValue() - twoNumber.doubleValue()) < 0.0001D; + return MUtil.equalsishNumber(oneNumber, twoNumber); } else { diff --git a/src/com/massivecraft/massivecore/store/Modification.java b/src/com/massivecraft/massivecore/store/Modification.java index d6a4854a..44eb5ea7 100644 --- a/src/com/massivecraft/massivecore/store/Modification.java +++ b/src/com/massivecraft/massivecore/store/Modification.java @@ -7,22 +7,26 @@ public enum Modification // ENUM // -------------------------------------------- // - LOCAL_ALTER (true, 4), - LOCAL_ATTACH (true, 8), - LOCAL_DETACH (true, 9), - REMOTE_ALTER (true, 5), - REMOTE_ATTACH (true, 6), - REMOTE_DETACH (true, 7), - NONE (false, 1), - UNKNOWN (false, 3), - UNKNOWN_LOG(false, 2), + LOCAL_ALTER (true, 40), + LOCAL_ATTACH (true, 80), + LOCAL_DETACH (true, 90), + REMOTE_ALTER (true, 50), + REMOTE_ATTACH (true, 60), + REMOTE_DETACH (true, 70), + NONE (false, 10), + UNKNOWN (false, 30), + UNKNOWN_LOG(false, 20), + UNKNOWN_CHANGED(false, 35), ; // -------------------------------------------- // // CONSTANTS // -------------------------------------------- // - - public static final int TOP_PRIORITY = 7; + + // The modifications take priority over all others. + // It is local attach and local detach, because otherwise + // the system would think it was a remote attach or remote detach. + public static final int TOP_PRIORITY = 80; // -------------------------------------------- // // FIELDS @@ -83,6 +87,6 @@ public enum Modification public boolean isUnknown() { - return this == Modification.UNKNOWN || this == Modification.UNKNOWN_LOG; + return this == Modification.UNKNOWN || this == Modification.UNKNOWN_LOG || this == Modification.UNKNOWN_CHANGED; } } diff --git a/src/com/massivecraft/massivecore/store/ModificationPollerAbstract.java b/src/com/massivecraft/massivecore/store/ModificationPollerAbstract.java index 64375547..dd037fbf 100644 --- a/src/com/massivecraft/massivecore/store/ModificationPollerAbstract.java +++ b/src/com/massivecraft/massivecore/store/ModificationPollerAbstract.java @@ -1,5 +1,7 @@ package com.massivecraft.massivecore.store; +import com.massivecraft.massivecore.ConfServer; + public abstract class ModificationPollerAbstract extends Thread { // -------------------------------------------- // @@ -54,7 +56,7 @@ public abstract class ModificationPollerAbstract extends Thread this.poll(coll); } } - + // -------------------------------------------- // // ABSTRACT // -------------------------------------------- // diff --git a/src/com/massivecraft/massivecore/store/ModificationPollerLocal.java b/src/com/massivecraft/massivecore/store/ModificationPollerLocal.java index e1b5dd21..152e0313 100644 --- a/src/com/massivecraft/massivecore/store/ModificationPollerLocal.java +++ b/src/com/massivecraft/massivecore/store/ModificationPollerLocal.java @@ -1,5 +1,6 @@ package com.massivecraft.massivecore.store; +import com.massivecraft.massivecore.ConfServer; import com.massivecraft.massivecore.MassiveCoreMConf; /* @@ -32,6 +33,6 @@ public class ModificationPollerLocal extends ModificationPollerAbstract public void poll(Coll coll) { coll.identifyLocalModifications(Modification.UNKNOWN_LOG); - } + } } diff --git a/src/com/massivecraft/massivecore/store/PusherCollFlatfile.java b/src/com/massivecraft/massivecore/store/PusherCollFlatfile.java index 7eb35840..e5f12ab3 100644 --- a/src/com/massivecraft/massivecore/store/PusherCollFlatfile.java +++ b/src/com/massivecraft/massivecore/store/PusherCollFlatfile.java @@ -126,6 +126,7 @@ public class PusherCollFlatfile extends Thread implements PusherColl case NONE: case UNKNOWN: case UNKNOWN_LOG: + case UNKNOWN_CHANGED: return; // It was modified remotely. diff --git a/src/com/massivecraft/massivecore/util/MUtil.java b/src/com/massivecraft/massivecore/util/MUtil.java index 2ac508fe..d6a471e4 100644 --- a/src/com/massivecraft/massivecore/util/MUtil.java +++ b/src/com/massivecraft/massivecore/util/MUtil.java @@ -931,7 +931,7 @@ public class MUtil if ( ! isFinite(factor)) throw new IllegalStateException("not finite factor: " + factor); // No Change? - if (equalsish(factor, 1)) return; + if (equalsishNumber(factor, 1)) return; for (DamageModifier modifier : DamageModifier.values()) { @@ -1766,6 +1766,8 @@ public class MUtil return object1.equals(object2); } + + public static boolean equals(Object... objects) { @@ -1791,14 +1793,33 @@ public class MUtil // -------------------------------------------- // public static final double EQUALSISH_EPSILON = 0.0001; - - public static boolean equalsish(Number number1, Number number2) + + public static boolean equalsishObject(Object o1, Object o2) + { + if (o1 instanceof Number && o2 instanceof Number) + { + return equalsishNumber((Number) o1, (Number) o2); + } + + return equals(o1, o2); + } + + public static boolean equalsishNumber(Number number1, Number number2) { if (number1 == null) return number2 == null; if (number2 == null) return false; - + return Math.abs(number1.doubleValue() - number2.doubleValue()) < EQUALSISH_EPSILON; } + + /** + * @deprecated use equalsishNumber + */ + @Deprecated + public static boolean equalsish(Number number1, Number number2) + { + return equalsishNumber(number1, number2); + } // -------------------------------------------- // // SORTING