Simplify Acquirable, support for all objects

This commit is contained in:
TheMode 2021-04-24 18:14:12 +02:00
parent 26bd0dd43c
commit 845a8608db
11 changed files with 127 additions and 158 deletions

View File

@ -1,7 +1,7 @@
package net.minestom.server;
import com.google.common.collect.Queues;
import net.minestom.server.entity.acquirable.Acquisition;
import net.minestom.server.acquirable.Acquisition;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;

View File

@ -1,23 +1,18 @@
package net.minestom.server.entity.acquirable;
package net.minestom.server.acquirable;
import net.minestom.server.entity.Entity;
import net.minestom.server.thread.ThreadProvider;
import net.minestom.server.thread.TickThread;
import net.minestom.server.utils.consumer.EntityConsumer;
import net.minestom.server.utils.async.AsyncUtils;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Represents an {@link Entity entity} which can be acquired.
* Used for synchronization purpose.
*/
public class AcquirableEntity {
private static final ThreadLocal<Stream<Entity>> CURRENT_ENTITIES = ThreadLocal.withInitial(Stream::empty);
public interface Acquirable<T> {
/**
* Gets all the {@link Entity entities} being ticked in the current thread.
@ -26,12 +21,12 @@ public class AcquirableEntity {
*
* @return the entities ticked in the current thread
*/
public static @NotNull Stream<@NotNull Entity> current() {
return CURRENT_ENTITIES.get();
static @NotNull Stream<@NotNull Entity> currentEntities() {
return AcquirableImpl.CURRENT_ENTITIES.get();
}
/**
* Changes the stream returned by {@link #current()}.
* Changes the stream returned by {@link #currentEntities()}.
* <p>
* Mostly for internal use, external calls are unrecommended as they could lead
* to unexpected behavior.
@ -39,21 +34,32 @@ public class AcquirableEntity {
* @param entities the new entity stream
*/
@ApiStatus.Internal
public static void refresh(@NotNull Stream<@NotNull Entity> entities) {
CURRENT_ENTITIES.set(entities);
static void refreshEntities(@NotNull Stream<@NotNull Entity> entities) {
AcquirableImpl.CURRENT_ENTITIES.set(entities);
}
private final Entity entity;
private final Handler handler;
public AcquirableEntity(@NotNull Entity entity) {
this.entity = entity;
this.handler = new Handler();
static <T> @NotNull Acquirable<T> of(@NotNull T value) {
return new AcquirableImpl<>(value);
}
public @NotNull Acquired<? extends Entity> acquire() {
final TickThread elementThread = getHandler().getTickThread();
return new Acquired<>(unwrap(), elementThread);
default void sync(@NotNull Consumer<T> consumer) {
final Thread currentThread = Thread.currentThread();
final TickThread tickThread = getHandler().getTickThread();
Acquisition.acquire(currentThread, tickThread, () -> consumer.accept(unwrap()));
}
default void async(@NotNull Consumer<T> consumer) {
// TODO per-thread list
AsyncUtils.runAsync(() -> sync(consumer));
}
default @NotNull Optional<T> optional() {
final Thread currentThread = Thread.currentThread();
final TickThread tickThread = getHandler().getTickThread();
if (Objects.equals(currentThread, tickThread)) {
return Optional.of(unwrap());
}
return Optional.empty();
}
/**
@ -63,9 +69,7 @@ public class AcquirableEntity {
*
* @return the unwrapped value
*/
public @NotNull Entity unwrap() {
return entity;
}
@NotNull T unwrap();
/**
* Gets the {@link Handler} of this acquirable element,
@ -76,11 +80,9 @@ public class AcquirableEntity {
* @return this element handler
*/
@ApiStatus.Internal
public @NotNull Handler getHandler() {
return handler;
}
@NotNull Handler getHandler();
public static class Handler {
class Handler {
private volatile ThreadProvider.ChunkEntry chunkEntry;

View File

@ -0,0 +1,19 @@
package net.minestom.server.acquirable;
import net.minestom.server.utils.collection.CollectionView;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public class AcquirableCollection<E> extends CollectionView<E, Acquirable<E>> {
private final Collection<Acquirable<E>> acquirableEntityCollection;
public AcquirableCollection(@NotNull Collection<Acquirable<E>> acquirableEntityCollection) {
super(acquirableEntityCollection,
null,
//Entity::getAcquirable,
acquirableEntity -> (E) acquirableEntity.unwrap());
this.acquirableEntityCollection = acquirableEntityCollection;
}
}

View File

@ -0,0 +1,43 @@
package net.minestom.server.acquirable;
import net.minestom.server.entity.Entity;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import java.util.stream.Stream;
class AcquirableImpl<T> implements Acquirable<T> {
protected static final ThreadLocal<Stream<Entity>> CURRENT_ENTITIES = ThreadLocal.withInitial(Stream::empty);
/**
* Changes the stream returned by {@link #currentEntities()}.
* <p>
* Mostly for internal use, external calls are unrecommended as they could lead
* to unexpected behavior.
*
* @param entities the new entity stream
*/
@ApiStatus.Internal
static void refreshEntities(@NotNull Stream<@NotNull Entity> entities) {
AcquirableImpl.CURRENT_ENTITIES.set(entities);
}
private final T value;
private final Acquirable.Handler handler;
public AcquirableImpl(@NotNull T value) {
this.value = value;
this.handler = new Acquirable.Handler();
}
@Override
public @NotNull T unwrap() {
return value;
}
@Override
public @NotNull Acquirable.Handler getHandler() {
return handler;
}
}

View File

@ -1,6 +1,5 @@
package net.minestom.server.entity.acquirable;
package net.minestom.server.acquirable;
import net.minestom.server.entity.Entity;
import net.minestom.server.thread.TickThread;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -27,8 +26,8 @@ public final class Acquisition {
* @param collection the collection to acquire
* @param consumer the consumer called for each of the collection element
*/
public static void acquireForEach(@NotNull Collection<AcquirableEntity> collection,
@NotNull Consumer<Entity> consumer) {
public static <T> void acquireForEach(@NotNull Collection<Acquirable<T>> collection,
@NotNull Consumer<T> consumer) {
final Thread currentThread = Thread.currentThread();
var threadEntitiesMap = retrieveOptionalThreadMap(collection, currentThread, consumer);
@ -36,11 +35,11 @@ public final class Acquisition {
{
for (var entry : threadEntitiesMap.entrySet()) {
final TickThread tickThread = entry.getKey();
final List<Entity> entities = entry.getValue();
final List<T> values = entry.getValue();
acquire(currentThread, tickThread, () -> {
for (Entity entity : entities) {
consumer.accept(entity);
for (T value : values) {
consumer.accept(value);
}
});
}
@ -107,15 +106,15 @@ public final class Acquisition {
* @param consumer the consumer to execute when an element is already in the current thread
* @return a new Thread to acquirable elements map
*/
protected static Map<TickThread, List<Entity>> retrieveOptionalThreadMap(@NotNull Collection<AcquirableEntity> collection,
@NotNull Thread currentThread,
@NotNull Consumer<? super Entity> consumer) {
protected static <T> Map<TickThread, List<T>> retrieveOptionalThreadMap(@NotNull Collection<Acquirable<T>> collection,
@NotNull Thread currentThread,
@NotNull Consumer<T> consumer) {
// Separate a collection of acquirable elements into a map of thread->elements
// Useful to reduce the number of acquisition
Map<TickThread, List<Entity>> threadCacheMap = new HashMap<>();
for (AcquirableEntity element : collection) {
final Entity value = element.unwrap();
Map<TickThread, List<T>> threadCacheMap = new HashMap<>();
for (var element : collection) {
final T value = element.unwrap();
final TickThread elementThread = element.getHandler().getTickThread();
if (currentThread == elementThread) {
@ -123,7 +122,7 @@ public final class Acquisition {
consumer.accept(value);
} else {
// The element is manager in a different thread, cache it
List<Entity> threadCacheList = threadCacheMap.computeIfAbsent(elementThread, tickThread -> new ArrayList<>());
List<T> threadCacheList = threadCacheMap.computeIfAbsent(elementThread, tickThread -> new ArrayList<>());
threadCacheList.add(value);
}
}

View File

@ -8,12 +8,12 @@ import net.kyori.adventure.text.event.HoverEventSource;
import net.minestom.server.MinecraftServer;
import net.minestom.server.Tickable;
import net.minestom.server.Viewable;
import net.minestom.server.acquirable.Acquirable;
import net.minestom.server.chat.JsonMessage;
import net.minestom.server.collision.BoundingBox;
import net.minestom.server.collision.CollisionUtils;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.acquirable.AcquirableEntity;
import net.minestom.server.entity.metadata.EntityMeta;
import net.minestom.server.event.Event;
import net.minestom.server.event.EventCallback;
@ -125,8 +125,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
private long ticks;
private final EntityTickEvent tickEvent = new EntityTickEvent(this);
// Not final in order to be modifiable in subclasses, use at your own risk
protected AcquirableEntity acquirable = new AcquirableEntity(this);
private final Acquirable<? extends Entity> acquirable = Acquirable.of(this);
/**
* Lock used to support #switchEntityType
@ -1574,7 +1573,7 @@ public class Entity implements Viewable, Tickable, EventHandler, DataContainer,
return Objects.requireNonNullElse(this.customSynchronizationCooldown, SYNCHRONIZATION_COOLDOWN);
}
public @NotNull AcquirableEntity getAcquirable() {
public @NotNull Acquirable<? extends Entity> getAcquirable() {
return acquirable;
}

View File

@ -15,6 +15,7 @@ import net.kyori.adventure.text.event.HoverEventSource;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.kyori.adventure.title.Title;
import net.minestom.server.MinecraftServer;
import net.minestom.server.acquirable.Acquirable;
import net.minestom.server.advancements.AdvancementTab;
import net.minestom.server.adventure.AdventurePacketConvertor;
import net.minestom.server.adventure.Localizable;
@ -27,8 +28,6 @@ import net.minestom.server.collision.BoundingBox;
import net.minestom.server.command.CommandManager;
import net.minestom.server.command.CommandSender;
import net.minestom.server.effects.Effects;
import net.minestom.server.entity.acquirable.AcquirablePlayer;
import net.minestom.server.entity.acquirable.Acquired;
import net.minestom.server.entity.damage.DamageType;
import net.minestom.server.entity.fakeplayer.FakePlayer;
import net.minestom.server.entity.vehicle.PlayerVehicleInformation;
@ -187,8 +186,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
this.username = username;
this.playerConnection = playerConnection;
this.acquirable = new AcquirablePlayer(this);
setBoundingBox(0.6f, 1.8f, 0.6f);
setRespawnPoint(new Position(0, 0, 0));
@ -331,13 +328,12 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
//System.out.println(getAcquiredElement().getHandler().getBatchThread());
if (username.equals("TheMode911"))
for (Player p : MinecraftServer.getConnectionManager().getOnlinePlayers()) {
//players.add(p1.getAcquiredElement());
var acquired = p.getAcquirable().acquire();
acquired.sync(player -> {
//System.out.println("sync");
});
}
for (Player p : MinecraftServer.getConnectionManager().getOnlinePlayers()) {
//players.add(p1.getAcquiredElement());
p.getAcquirable().sync(player -> {
});
}
super.update(time); // Super update (item pickup/fire management)
@ -2462,8 +2458,8 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
}
@Override
public @NotNull AcquirablePlayer getAcquirable() {
return (AcquirablePlayer) super.getAcquirable();
public @NotNull Acquirable<? extends Player> getAcquirable() {
return (Acquirable<? extends Player>) super.getAcquirable();
}
@Override

View File

@ -1,25 +0,0 @@
package net.minestom.server.entity.acquirable;
import net.minestom.server.entity.Entity;
import net.minestom.server.utils.collection.CollectionView;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.function.Consumer;
public class AcquirableCollection<E extends Entity> extends CollectionView<E, AcquirableEntity> {
private final Collection<AcquirableEntity> acquirableEntityCollection;
public AcquirableCollection(@NotNull Collection<AcquirableEntity> acquirableEntityCollection) {
super(acquirableEntityCollection,
Entity::getAcquirable,
acquirableEntity -> (E) acquirableEntity.unwrap());
this.acquirableEntityCollection = acquirableEntityCollection;
}
@Override
public void forEach(Consumer<? super E> action) {
// Acquisition.acquireForEach(acquirableEntityCollection, action);
}
}

View File

@ -1,22 +0,0 @@
package net.minestom.server.entity.acquirable;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.Player;
import org.jetbrains.annotations.NotNull;
public class AcquirablePlayer extends AcquirableEntity {
public AcquirablePlayer(@NotNull Entity entity) {
super(entity);
}
@Override
public @NotNull Acquired<? extends Player> acquire() {
return (Acquired<? extends Player>) super.acquire();
}
@Override
public @NotNull Player unwrap() {
return (Player) super.unwrap();
}
}

View File

@ -1,42 +0,0 @@
package net.minestom.server.entity.acquirable;
import net.minestom.server.thread.TickThread;
import net.minestom.server.utils.async.AsyncUtils;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
public class Acquired<T> {
private final T value;
private final TickThread tickThread;
protected Acquired(@NotNull T value, @NotNull TickThread tickThread) {
this.value = value;
this.tickThread = tickThread;
}
public void sync(@NotNull Consumer<T> consumer) {
final Thread currentThread = Thread.currentThread();
Acquisition.acquire(currentThread, tickThread, () -> consumer.accept(unwrap()));
}
public void async(@NotNull Consumer<T> consumer) {
// TODO per-thread list
AsyncUtils.runAsync(() -> sync(consumer));
}
public @NotNull Optional<T> optional() {
final Thread currentThread = Thread.currentThread();
if (Objects.equals(currentThread, tickThread)) {
return Optional.of(unwrap());
}
return Optional.empty();
}
public @NotNull T unwrap() {
return value;
}
}

View File

@ -1,8 +1,8 @@
package net.minestom.server.thread;
import net.minestom.server.MinecraftServer;
import net.minestom.server.acquirable.Acquirable;
import net.minestom.server.entity.Entity;
import net.minestom.server.entity.acquirable.AcquirableEntity;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.MathUtils;
@ -121,13 +121,13 @@ public abstract class ThreadProvider {
final var chunkEntries = threadChunkMap.get(thread);
if (chunkEntries == null || chunkEntries.isEmpty()) {
// Nothing to tick
AcquirableEntity.refresh(Stream.empty());
Acquirable.refreshEntities(Stream.empty());
return;
}
final var entities = chunkEntries.stream()
.flatMap(chunkEntry -> chunkEntry.entities.stream());
AcquirableEntity.refresh(entities);
Acquirable.refreshEntities(entities);
final ReentrantLock lock = thread.getLock();
lock.lock();
@ -146,7 +146,7 @@ public abstract class ThreadProvider {
entity.tick(time);
});
});
AcquirableEntity.refresh(Stream.empty());
Acquirable.refreshEntities(Stream.empty());
lock.unlock();
});
}
@ -294,7 +294,7 @@ public abstract class ThreadProvider {
if (removedEntities.isEmpty())
return;
for (Entity entity : removedEntities) {
AcquirableEntity acquirableEntity = entity.getAcquirable();
var acquirableEntity = entity.getAcquirable();
ChunkEntry chunkEntry = acquirableEntity.getHandler().getChunkEntry();
// Remove from list
if (chunkEntry != null) {
@ -308,7 +308,7 @@ public abstract class ThreadProvider {
if (updatableEntities.isEmpty())
return;
for (Entity entity : updatableEntities) {
AcquirableEntity acquirableEntity = entity.getAcquirable();
var acquirableEntity = entity.getAcquirable();
ChunkEntry handlerChunkEntry = acquirableEntity.getHandler().getChunkEntry();
Chunk entityChunk = entity.getChunk();