mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2025-07-01 02:46:41 +00:00
Update Upstream
9516002 Register platforms and commands in a more proper way (1766)
This commit is contained in:
@ -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) {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user