Update Upstream

9516002 Register platforms and commands in a more proper way (1766)
This commit is contained in:
NotMyFault
2021-06-05 10:27:38 +02:00
parent f139088b6e
commit d1af6c38e7
21 changed files with 858 additions and 81 deletions

View File

@ -0,0 +1,40 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.event.platform;
import com.sk89q.worldedit.event.Event;
import com.sk89q.worldedit.extension.platform.Platform;
public abstract class PlatformEvent extends Event {
private final Platform platform;
protected PlatformEvent(Platform platform) {
this.platform = platform;
}
/**
* Get the platform for this event.
*
* @return the platform
*/
public Platform getPlatform() {
return platform;
}
}

View File

@ -19,11 +19,13 @@
package com.sk89q.worldedit.event.platform;
import com.sk89q.worldedit.event.Event;
import com.sk89q.worldedit.extension.platform.Platform;
/**
* Raised when a platform thinks that all the platforms have had a chance to
* register themselves.
* Raised when a platform has finished loading its data.
*/
public class PlatformReadyEvent extends Event {
public class PlatformReadyEvent extends PlatformEvent {
public PlatformReadyEvent(Platform platform) {
super(platform);
}
}

View File

@ -0,0 +1,31 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.event.platform;
import com.sk89q.worldedit.extension.platform.Platform;
/**
* Raised when a platform needs to retract all registered data, e.g. due to a reload.
*/
public class PlatformUnreadyEvent extends PlatformEvent {
public PlatformUnreadyEvent(Platform platform) {
super(platform);
}
}

View File

@ -0,0 +1,28 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.event.platform;
import com.sk89q.worldedit.event.Event;
/**
* Fired by a platform when it believes all available platforms should be registered.
*/
public class PlatformsRegisteredEvent extends Event {
}

View File

@ -19,6 +19,8 @@
package com.sk89q.worldedit.extension.platform;
import com.sk89q.worldedit.WorldEdit;
/**
* A collection of capabilities that a {@link Platform} may support.
*/
@ -31,11 +33,12 @@ public enum Capability {
GAME_HOOKS {
@Override
void initialize(PlatformManager platformManager, Platform platform) {
platform.registerGameHooks();
platform.setGameHooksEnabled(true);
}
@Override
void unload(PlatformManager platformManager, Platform platform) {
void uninitialize(PlatformManager platformManager, Platform platform) {
platform.setGameHooksEnabled(false);
}
},
@ -54,7 +57,7 @@ public enum Capability {
}
@Override
void unload(PlatformManager platformManager, Platform platform) {
void uninitialize(PlatformManager platformManager, Platform platform) {
platformManager.getPlatformCommandManager().removeCommands();
}
},
@ -76,7 +79,7 @@ public enum Capability {
WORLD_EDITING {
/*
@Override
void initialize(PlatformManager platformManager, Platform platform) {
void ready(PlatformManager platformManager, Platform platform) {
BlockRegistry blockRegistry = platform.getRegistries().getBlockRegistry();
for (BlockType type : BlockType.REGISTRY) {
for (BlockState state : type.getAllStates()) {
@ -86,18 +89,35 @@ public enum Capability {
}
@Override
void unload(PlatformManager platformManager, Platform platform) {
void unready(PlatformManager platformManager, Platform platform) {
BlockStateIdAccess.clear();
}
*/
};
/**
* Initialize platform-wide state.
*/
void initialize(PlatformManager platformManager, Platform platform) {
}
void unload(PlatformManager platformManager, Platform platform) {
/**
* Un-initialize platform-wide state.
*/
void uninitialize(PlatformManager platformManager, Platform platform) {
}
/**
* Initialize per-level state.
*/
void ready(PlatformManager platformManager, Platform platform) {
}
/**
* Un-initialize per-level state.
*/
void unready(PlatformManager platformManager, Platform platform) {
}
}

View File

@ -138,14 +138,28 @@ public interface Platform extends Keyed {
/**
* Register the commands contained within the given command manager.
*
* <p>
* This method should be ignored if the platform offers a command registration event.
* </p>
*
* @param commandManager the command manager
*/
void registerCommands(CommandManager commandManager);
/**
* Register game hooks.
*
* @deprecated Call {@link #setGameHooksEnabled(boolean)} with {@code true} instead
*/
void registerGameHooks();
@Deprecated
default void registerGameHooks() {
setGameHooksEnabled(true);
}
/**
* Set if the game hooks are enabled for this platform.
*/
void setGameHooksEnabled(boolean enabled);
/**
* Get the configuration from this platform.

View File

@ -40,6 +40,8 @@ import com.sk89q.worldedit.event.platform.ConfigurationLoadEvent;
import com.sk89q.worldedit.event.platform.Interaction;
import com.sk89q.worldedit.event.platform.PlatformInitializeEvent;
import com.sk89q.worldedit.event.platform.PlatformReadyEvent;
import com.sk89q.worldedit.event.platform.PlatformUnreadyEvent;
import com.sk89q.worldedit.event.platform.PlatformsRegisteredEvent;
import com.sk89q.worldedit.event.platform.PlayerInputEvent;
import com.sk89q.worldedit.internal.util.LogManagerCompat;
import com.sk89q.worldedit.math.Vector3;
@ -145,7 +147,7 @@ public class PlatformManager {
while (it.hasNext()) {
Entry<Capability, Platform> entry = it.next();
if (entry.getValue().equals(platform)) {
entry.getKey().unload(this, entry.getValue());
entry.getKey().uninitialize(this, entry.getValue());
it.remove();
choosePreferred = true; // Have to choose new favorites
}
@ -160,8 +162,7 @@ public class PlatformManager {
}
/**
* Get the preferred platform for handling a certain capability. Returns
* null if none is available.
* Get the preferred platform for handling a certain capability. Throws if none are available.
*
* @param capability the capability
* @return the platform
@ -173,12 +174,11 @@ public class PlatformManager {
return platform;
} else {
if (preferences.isEmpty()) {
// Use the first available if preferences have not been decided yet.
if (platforms.isEmpty()) {
// No platforms registered, this is being called too early!
throw new NoCapablePlatformException("No platforms have been registered yet! Please wait until WorldEdit is initialized.");
}
return platforms.get(0);
// Not all platforms registered, this is being called too early!
throw new NoCapablePlatformException(
"Not all platforms have been registered yet!"
+ " Please wait until FastAsyncWorldEdit is initialized."
);
}
throw new NoCapablePlatformException("No platform was found supporting " + capability.name());
}
@ -191,8 +191,15 @@ public class PlatformManager {
for (Capability capability : Capability.values()) {
Platform preferred = findMostPreferred(capability);
if (preferred != null) {
preferences.put(capability, preferred);
capability.initialize(this, preferred);
Platform oldPreferred = preferences.put(capability, preferred);
// only (re)initialize if it changed
if (preferred != oldPreferred) {
// uninitialize if needed
if (oldPreferred != null) {
capability.uninitialize(this, oldPreferred);
}
capability.initialize(this, preferred);
}
}
}
@ -311,14 +318,42 @@ public class PlatformManager {
return queryCapability(Capability.WORLD_EDITING).getSupportedSideEffects();
}
/**
* You shouldn't have been calling this anyways, but this is now deprecated. Either don't
* fire this event at all, or fire the new event via the event bus if you're a platform.
*/
@Deprecated
public void handlePlatformReady(@SuppressWarnings("unused") PlatformReadyEvent event) {
handlePlatformsRegistered(new PlatformsRegisteredEvent());
}
/**
* Internal, do not call.
*/
@Subscribe
public void handlePlatformReady(PlatformReadyEvent event) {
public void handlePlatformsRegistered(PlatformsRegisteredEvent event) {
choosePreferred();
if (initialized.compareAndSet(false, true)) {
worldEdit.getEventBus().post(new PlatformInitializeEvent());
}
}
/**
* Internal, do not call.
*/
@Subscribe
public void handleNewPlatformReady(PlatformReadyEvent event) {
preferences.forEach((cap, platform) -> cap.ready(this, platform));
}
/**
* Internal, do not call.
*/
@Subscribe
public void handleNewPlatformUnready(PlatformUnreadyEvent event) {
preferences.forEach((cap, platform) -> cap.unready(this, platform));
}
private <T extends Tool> T reset(T tool) {
new PatternTraverser(tool).reset(null);
return tool;

View File

@ -0,0 +1,55 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.lifecycle;
import java.util.Objects;
import java.util.Optional;
import java.util.function.BiConsumer;
/**
* A {@link Lifecycled} that never invalidates.
*/
public final class ConstantLifecycled<T> implements Lifecycled<T> {
private final T value;
public ConstantLifecycled(T value) {
this.value = Objects.requireNonNull(value);
}
@Override
public Optional<T> value() {
return Optional.of(value);
}
@Override
public Events<T> events() {
// Simple implementation, we just need to call onNewValue
return new Events<T>() {
@Override
public <O> void onNewValue(O owner, BiConsumer<O, ? super Lifecycled<T>> callback) {
callback.accept(owner, ConstantLifecycled.this);
}
@Override
public <O> void onInvalidated(O owner, BiConsumer<O, ? super Lifecycled<T>> callback) {
}
};
}
}

View File

@ -0,0 +1,80 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.lifecycle;
import java.util.Optional;
import java.util.function.Function;
import javax.annotation.Nullable;
class FlatMapLifecycled<T, U> implements Lifecycled<U> {
private final LifecycledCallbackHandler<U> events = new LifecycledCallbackHandler<>(this);
private Lifecycled<U> mapped;
private Token<FlatMapLifecycled<T, U>> mappedToken;
@Nullable
private U value;
FlatMapLifecycled(Lifecycled<T> upstream, Function<T, Lifecycled<U>> mapper) {
upstream.events().onInvalidated(this, (this$, up) -> {
boolean fire = this$.value != null;
this$.value = null;
// drop `mapped` hooks if needed
this$.mappedToken = null;
this$.mapped = null;
if (fire) {
this$.events.fireInvalidated();
}
});
upstream.events().onNewValue(this, (this$, up) -> {
this$.mapped = mapper.apply(up.valueOrThrow());
this$.mappedToken = new Token<>(this$);
mapped.events().onInvalidated(this$.mappedToken, (token, mapped$) -> {
boolean fire = token.inner.value != null;
token.inner.value = null;
// note we do not drop the token here, onNewValue may be called again
if (fire) {
this$.events.fireInvalidated();
}
});
mapped.events().onNewValue(this$.mappedToken, (token, mapped$) -> {
U newValue = mapped$.valueOrThrow();
boolean fire = token.inner.value != newValue;
token.inner.value = newValue;
if (fire) {
this$.events.fireOnNewValue();
}
});
});
}
@Override
public Optional<U> value() {
return Optional.ofNullable(value);
}
@Override
public boolean isValid() {
return value != null;
}
@Override
public Events<U> events() {
return events;
}
}

View File

@ -0,0 +1,151 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.lifecycle;
import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Predicate;
/**
* Represents an object with a simple valid/invalid lifecycle.
*
* <p>
* A lifecycled object will start with no value, then trigger
* {@link Events#onNewValue(Object, BiConsumer)} callbacks when it gets one, and
* {@link Events#onInvalidated(Object, BiConsumer)} callbacks when it loses it. A full
* invalidated->new value cycle is called a "reload".
* </p>
*
* <p>
* Downstream lifecycled objects can be derived using functional methods, and share some
* common rules. They will apply the operation sometime before the result is needed, either
* eagerly or lazily. They will re-do the operation after the upstream {@link Lifecycled} is
* reloaded.
* </p>
*
* <p>
* Unless specified, {@link Lifecycled} objects are <em>not</em> thread-safe. However, the
* {@link Events} objects are, and callbacks may be added from any thread.
* </p>
*
* @param <T> the value type
*/
public interface Lifecycled<T> {
interface Events<T> {
/**
* Add a callback for when this lifecycled is given a new value. Will be called immediately
* if this lifecycled is currently valid.
*
* <p>
* The callback should not reference the owner, it must only access it via the parameter.
* This ensures that the owner will be GC-able, otherwise it may be stuck in a reference
* loop.
* </p>
*
* @param owner when the owner is GC'd, the callback is removed
* @param callback the callback, will be passed the lifecycled object
*/
<O> void onNewValue(O owner, BiConsumer<O, ? super Lifecycled<T>> callback);
/**
* Add a callback for when this lifecycled is invalidated. Will be called immediately if
* this lifecycled is currently invalid.
*
* <p>
* The callback should not reference the owner, it must only access it via the parameter.
* This ensures that the owner will be GC-able, otherwise it may be stuck in a reference
* loop.
* </p>
*
* @param owner when the owner is GC'd, the callback is removed
* @param callback the callback, will be passed the lifecycled object
*/
<O> void onInvalidated(O owner, BiConsumer<O, ? super Lifecycled<T>> callback);
}
/**
* Get the value or {@link Optional#empty()}.
*
* @return the value
*/
Optional<T> value();
/**
* Get the value or throw.
*
* @return the value
* @throws IllegalStateException if there is no value
*/
default T valueOrThrow() throws IllegalStateException {
return value().orElseThrow(() -> new IllegalStateException("Currently invalid"));
}
/**
* Check for validity, usually without triggering computation.
*
* @return if this lifecycled's {@link #value()} is valid
*/
default boolean isValid() {
return value().isPresent();
}
/**
* Get the event manager for this lifecycled object.
*
* @return the event manager
*/
Events<T> events();
/**
* Map the value.
*
* @param mapper the mapper function
* @param <U> the new type
* @return the downstream lifecycled
*/
default <U> Lifecycled<U> map(Function<T, U> mapper) {
return new MapLifecycled<>(this, mapper);
}
/**
* Filter the value. In other words, create a new lifecycled object where validity is ANDed
* with the result of calling the filter function.
*
* @param filterer the filter function
* @return the downstream lifecycled
*/
default Lifecycled<T> filter(Predicate<T> filterer) {
SimpleLifecycled<T> downstream = SimpleLifecycled.invalid();
events().onInvalidated(downstream, (d, lifecycled) -> d.invalidate());
events().onNewValue(downstream, (d, lifecycled) -> {
T value = lifecycled.valueOrThrow();
if (filterer.test(value)) {
d.newValue(value);
}
});
return downstream;
}
default <U> Lifecycled<U> flatMap(Function<T, Lifecycled<U>> mapper) {
return new FlatMapLifecycled<>(this, mapper);
}
}

View File

@ -0,0 +1,114 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.lifecycle;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.BiConsumer;
/**
* Convenience class for implementing the callbacks of {@link Lifecycled}.
*/
public class LifecycledCallbackHandler<T> implements Lifecycled.Events<T> {
private final Lifecycled<T> lifecycled;
private final Lock lock = new ReentrantLock();
private final Map<Object, BiConsumer<?, ? super Lifecycled<T>>> onInvalidatedCallbacks =
new WeakHashMap<>();
private final Map<Object, BiConsumer<?, ? super Lifecycled<T>>> onNewValueCallbacks =
new WeakHashMap<>();
public LifecycledCallbackHandler(Lifecycled<T> lifecycled) {
this.lifecycled = lifecycled;
}
@Override
public <O> void onInvalidated(O owner, BiConsumer<O, ? super Lifecycled<T>> callback) {
lock.lock();
try {
onInvalidatedCallbacks.put(owner, callback);
if (!lifecycled.isValid()) {
callback.accept(owner, lifecycled);
}
} finally {
lock.unlock();
}
}
@Override
public <O> void onNewValue(O owner, BiConsumer<O, ? super Lifecycled<T>> callback) {
lock.lock();
try {
onNewValueCallbacks.put(owner, callback);
if (lifecycled.isValid()) {
callback.accept(owner, lifecycled);
}
} finally {
lock.unlock();
}
}
/**
* Fire {@link #onInvalidated(Object, BiConsumer)} callbacks.
*/
public void fireInvalidated() {
lock.lock();
try {
for (Map.Entry<Object, BiConsumer<?, ? super Lifecycled<T>>> callback : onInvalidatedCallbacks.entrySet()) {
Object owner = callback.getKey();
if (owner == null) {
// GC'd, continue
continue;
}
@SuppressWarnings("unchecked")
BiConsumer<Object, ? super Lifecycled<T>> cast =
(BiConsumer<Object, ? super Lifecycled<T>>) callback.getValue();
cast.accept(owner, lifecycled);
}
} finally {
lock.unlock();
}
}
/**
* Fire {@link #onNewValue(Object, BiConsumer)} callbacks, the {@link Lifecycled#value()} must
* be available.
*/
public void fireOnNewValue() {
lock.lock();
try {
for (Map.Entry<Object, BiConsumer<?, ? super Lifecycled<T>>> callback : onNewValueCallbacks.entrySet()) {
Object owner = callback.getKey();
if (owner == null) {
// GC'd, continue
continue;
}
@SuppressWarnings("unchecked")
BiConsumer<Object, ? super Lifecycled<T>> cast =
(BiConsumer<Object, ? super Lifecycled<T>>) callback.getValue();
cast.accept(owner, lifecycled);
}
} finally {
lock.unlock();
}
}
}

View File

@ -0,0 +1,82 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.lifecycle;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
import javax.annotation.Nullable;
class MapLifecycled<T, U> implements Lifecycled<U> {
private final LifecycledCallbackHandler<U> events = new LifecycledCallbackHandler<>(this);
private final Lifecycled<T> upstream;
private final Function<T, U> mapper;
@Nullable
private U cache;
private boolean computable;
MapLifecycled(Lifecycled<T> upstream, Function<T, U> mapper) {
this.upstream = upstream;
this.mapper = mapper;
upstream.events().onInvalidated(this, (this$, __) -> {
boolean fire = this$.computable;
this$.cache = null;
this$.computable = false;
if (fire) {
this$.events.fireInvalidated();
}
});
upstream.events().onNewValue(this, (this$, __) -> {
boolean fire = !this$.computable;
this$.computable = true;
if (fire) {
this$.events.fireOnNewValue();
}
});
}
private void compute() {
T value = upstream.value().orElseThrow(() ->
new AssertionError("Upstream lost value without calling onInvalidated event")
);
this.cache = Objects.requireNonNull(mapper.apply(value), "Mapper cannot produce null");
}
@Override
public Optional<U> value() {
if (!computable) {
return Optional.empty();
}
if (cache == null) {
compute();
}
return Optional.of(cache);
}
@Override
public boolean isValid() {
return computable;
}
@Override
public Events<U> events() {
return events;
}
}

View File

@ -0,0 +1,79 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.lifecycle;
import java.util.Objects;
import java.util.Optional;
import javax.annotation.Nullable;
/**
* A {@link Lifecycled} that can be directly called to {@linkplain #invalidate() invalidate} it or
* set a {@linkplain #newValue(Object) new value}.
*/
public final class SimpleLifecycled<T> implements Lifecycled<T> {
public static <T> SimpleLifecycled<T> valid(T value) {
return new SimpleLifecycled<>(Objects.requireNonNull(value));
}
public static <T> SimpleLifecycled<T> invalid() {
return new SimpleLifecycled<>(null);
}
private final LifecycledCallbackHandler<T> events = new LifecycledCallbackHandler<>(this);
@Nullable
private T value;
private SimpleLifecycled(@Nullable T value) {
this.value = value;
}
/**
* Set the value of this lifecycled and fire the new value event.
*
* @param value the value
*/
public void newValue(T value) {
// Ensure lifecycle constraints are upheld.
invalidate();
this.value = Objects.requireNonNull(value);
events.fireOnNewValue();
}
/**
* Remove the value of this lifecycled and fire the invalidated event.
*/
public void invalidate() {
boolean fire = this.value != null;
this.value = null;
if (fire) {
events.fireInvalidated();
}
}
@Override
public Optional<T> value() {
return Optional.ofNullable(value);
}
@Override
public Events<T> events() {
return events;
}
}

View File

@ -0,0 +1,33 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.util.lifecycle;
/**
* Used to create a new strong reference to an object that can be separately dropped.
*
* @param <T> the inner object
*/
class Token<T> {
final T inner;
Token(T inner) {
this.inner = inner;
}
}