Rework of the thread provider + ConnectionManager#getPlayer for UUID

This commit is contained in:
Felix Cravic 2020-08-06 16:28:04 +02:00
parent 118ed220e3
commit a38a143bba
11 changed files with 135 additions and 162 deletions

View File

@ -149,9 +149,10 @@ public class PlayerInit {
p.teleport(player.getPosition());
}*/
ChickenCreature chickenCreature = new ChickenCreature(player.getPosition());
chickenCreature.setInstance(player.getInstance());
chickenCreature.setTarget(player);
for (int i = 0; i < 100; i++) {
ChickenCreature chickenCreature = new ChickenCreature(player.getPosition());
chickenCreature.setInstance(player.getInstance());
}
/*EntityZombie zombie = new EntityZombie(player.getPosition());
zombie.setAttribute(Attribute.MOVEMENT_SPEED, 0.25f);

View File

@ -1,7 +1,7 @@
package fr.themode.demo.entity;
import net.minestom.server.attribute.Attribute;
import net.minestom.server.entity.ai.goal.FollowTargetGoal;
import net.minestom.server.entity.ai.goal.RandomStrollGoal;
import net.minestom.server.entity.ai.target.PlayerTarget;
import net.minestom.server.entity.type.EntityChicken;
import net.minestom.server.utils.Position;
@ -12,8 +12,8 @@ public class ChickenCreature extends EntityChicken {
super(defaultPosition);
//goalSelectors.add(new DoNothingGoal(this, 500, 0.1f));
//goalSelectors.add(new RandomStrollGoal(this, 2));
goalSelectors.add(new FollowTargetGoal(this));
goalSelectors.add(new RandomStrollGoal(this, 2));
//goalSelectors.add(new FollowTargetGoal(this));
targetSelectors.add(new PlayerTarget(this, 15));

View File

@ -288,6 +288,10 @@ public class MinecraftServer {
return tagManager;
}
public static UpdateManager getUpdateManager() {
return updateManager;
}
public void start(String address, int port, ResponseDataConsumer responseDataConsumer) {
LOGGER.info("Starting Minestom server.");
MinecraftServer.responseDataConsumer = responseDataConsumer;

View File

@ -4,12 +4,11 @@ import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.ColoredText;
import net.minestom.server.entity.EntityManager;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.network.ConnectionManager;
import net.minestom.server.network.packet.server.play.KeepAlivePacket;
import net.minestom.server.thread.SingleThreadProvider;
import net.minestom.server.thread.PerInstanceThreadProvider;
import net.minestom.server.thread.ThreadProvider;
import net.minestom.server.utils.thread.MinestomThread;
import net.minestom.server.utils.validate.Check;
@ -27,8 +26,7 @@ public final class UpdateManager {
private ThreadProvider threadProvider;
{
threadProvider = new SingleThreadProvider();
//threadProvider = new PerInstanceThreadProvider();
threadProvider = new PerInstanceThreadProvider();
//threadProvider = new PerGroupChunkProvider();
}
@ -49,24 +47,15 @@ public final class UpdateManager {
long currentTime;
while (!stopRequested) {
currentTime = System.nanoTime();
final long time = System.currentTimeMillis();
// Server tick
//long testTime = System.nanoTime();
threadProvider.start();
for (Instance instance : instanceManager.getInstances()) {
for (Chunk chunk : instance.getChunks()) {
threadProvider.linkThread(instance, chunk);
}
}
threadProvider.end();
threadProvider.update();
//System.out.println("time: " + (System.nanoTime() - testTime));
threadProvider.update(time);
// Waiting players update
entityManager.updateWaitingPlayers();
// Keep Alive Handling
final long time = System.currentTimeMillis();
final KeepAlivePacket keepAlivePacket = new KeepAlivePacket(time);
for (Player player : connectionManager.getOnlinePlayers()) {
final long lastKeepAlive = time - player.getLastKeepAlive();
@ -112,6 +101,33 @@ public final class UpdateManager {
this.threadProvider = threadProvider;
}
/**
* Signal the thread provider that a chunk has been loaded
*
* @param instance the instance of the chunk
* @param chunkX the chunk X
* @param chunkZ the chunk Z
*/
public void signalChunkLoad(Instance instance, int chunkX, int chunkZ) {
if (this.threadProvider == null)
return;
this.threadProvider.onChunkLoad(instance, chunkX, chunkZ);
}
/**
* Signal the thread provider that a chunk has been unloaded
*
* @param instance the instance of the chunk
* @param chunkX the chunk X
* @param chunkZ the chunk Z
*/
public void signalChunkUnload(Instance instance, int chunkX, int chunkZ) {
if (this.threadProvider == null)
return;
this.threadProvider.onChunkUnload(instance, chunkX, chunkZ);
}
public void stop() {
stopRequested = true;
}

View File

@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.longs.Long2ObjectMap;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
import net.minestom.server.MinecraftServer;
import net.minestom.server.UpdateManager;
import net.minestom.server.data.Data;
import net.minestom.server.data.DataContainer;
import net.minestom.server.entity.*;
@ -48,6 +49,7 @@ import java.util.function.Consumer;
public abstract class Instance implements BlockModifier, EventHandler, DataContainer {
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
private DimensionType dimensionType;

View File

@ -342,6 +342,8 @@ public class InstanceContainer extends Instance {
this.chunkEntities.remove(index);
chunk.unload();
UPDATE_MANAGER.signalChunkUnload(this, chunkX, chunkZ);
}
@Override
@ -428,6 +430,7 @@ public class InstanceContainer extends Instance {
final boolean loaded = chunkLoader.loadChunk(this, chunkX, chunkZ, chunk -> {
cacheChunk(chunk);
callChunkLoadEvent(chunkX, chunkZ);
UPDATE_MANAGER.signalChunkLoad(this, chunkX, chunkZ);
if (callback != null)
callback.accept(chunk);
});
@ -459,6 +462,7 @@ public class InstanceContainer extends Instance {
callback.accept(chunk);
}
UPDATE_MANAGER.signalChunkLoad(this, chunkX, chunkZ);
callChunkLoadEvent(chunkX, chunkZ);
}

View File

@ -60,6 +60,22 @@ public final class ConnectionManager {
return null;
}
/**
* Get the first player which validate {@link UUID#equals(Object)}
* <p>
* This can cause issue if two or more players have the same UUID
*
* @param uuid the player UUID
* @return the first player who validate the UUID condition
*/
public Player getPlayer(UUID uuid) {
for (Player player : getOnlinePlayers()) {
if (player.getUuid().equals(uuid))
return player;
}
return null;
}
/**
* Send a rich message to all online players who validate the condition {@code condition}
*

View File

@ -2,6 +2,7 @@ package net.minestom.server.thread;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
import java.util.HashMap;
import java.util.HashSet;
@ -13,66 +14,66 @@ import java.util.Set;
* <p>
* (1 chunks group = 1 thread execution)
*/
// FIXME: unusable at the moment, too much overhead because groups need to be created every tick
// Should have a callback for when a chunk is loaded and unloaded, so groups are updated only once
// TODO
public class PerGroupChunkProvider extends ThreadProvider {
/**
* Here are stored all cached chunks waiting for a ChunkGroup
*/
private Map<Chunk, Set<Chunk>> cachedChunks = new HashMap<>();
private Map<Chunk, Set<ChunkCoordinate>> cachedChunks = new HashMap<>();
/**
* Used to know to which instance is linked a Set of chunks
*/
private Map<Set<Chunk>, Instance> instanceMap = new HashMap<>();
private Map<Set<ChunkCoordinate>, Instance> instanceMap = new HashMap<>();
@Override
public void start() {
this.cachedChunks.clear();
this.instanceMap.clear();
}
@Override
public void linkThread(Instance instance, Chunk chunk) {
startChunkQuery(instance, chunk.getChunkX(), chunk.getChunkZ());
}
@Override
public void end() {
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
}
@Override
public void update() {
// The time of the tick
final long time = System.currentTimeMillis();
public void onChunkUnload(Instance instance, int chunkX, int chunkZ) {
}
@Override
public void update(long time) {
// Set of already-updated instances
final Set<Instance> updatedInstance = new HashSet<>();
// Update all the chunks
for (Map.Entry<Set<Chunk>, Instance> entry : instanceMap.entrySet()) {
Set<Chunk> chunks = entry.getKey();
for (Map.Entry<Set<ChunkCoordinate>, Instance> entry : instanceMap.entrySet()) {
Set<ChunkCoordinate> chunks = entry.getKey();
Instance instance = entry.getValue();
final boolean updateInstance = updatedInstance.add(instance);
pool.execute(() -> {
/*if (updateInstance) {
if (updateInstance) {
updateInstance(instance, time);
}
for (Chunk chunk : chunks) {
for (ChunkCoordinate chunkCoordinate : chunks) {
final Chunk chunk = instance.getChunk(chunkCoordinate.chunkX, chunkCoordinate.chunkZ);
if (ChunkUtils.isChunkUnloaded(chunk)) {
continue;
}
updateChunk(instance, chunk, time);
updateEntities(instance, chunk, time);
}*/
}
});
}
}
/*@Override
public void linkThread(Instance instance, Chunk chunk) {
startChunkQuery(instance, chunk.getChunkX(), chunk.getChunkZ());
}*/
/**
* Check the four chunk neighbors (up/down/left/right)
* and add them to the cache list
@ -81,7 +82,7 @@ public class PerGroupChunkProvider extends ThreadProvider {
* @param chunkX the chunk X
* @param chunkZ the chunk Z
*/
private void startChunkQuery(Instance instance, int chunkX, int chunkZ) {
/*private void startChunkQuery(Instance instance, int chunkX, int chunkZ) {
// Constants used to loop through the neighbors
final int[] posX = {1, 0, -1};
final int[] posZ = {1, 0, -1};
@ -124,6 +125,5 @@ public class PerGroupChunkProvider extends ThreadProvider {
this.cachedChunks.put(cachedChunk, cache);
}
this.instanceMap.put(cache, instance);
}
}*/
}

View File

@ -2,96 +2,56 @@ package net.minestom.server.thread;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.utils.chunk.ChunkUtils;
import java.util.*;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* Separate work between instance (1 instance = 1 thread execution)
*/
public class PerInstanceThreadProvider extends ThreadProvider {
private Map<Instance, GroupedInstanceChunk> groupMap = new HashMap<>();
private Map<Instance, Set<ChunkCoordinate>> instanceChunkMap = new HashMap<>();
@Override
public void start() {
this.groupMap.clear();
public void onChunkLoad(Instance instance, int chunkX, int chunkZ) {
Set<ChunkCoordinate> chunkCoordinates = instanceChunkMap.computeIfAbsent(instance, inst -> new HashSet<>());
chunkCoordinates.add(new ChunkCoordinate(chunkX, chunkZ));
}
@Override
public void linkThread(Instance instance, Chunk chunk) {
InstanceChunk instanceChunk = new InstanceChunk(instance, chunk);
public void onChunkUnload(Instance instance, int chunkX, int chunkZ) {
Set<ChunkCoordinate> chunkCoordinates = instanceChunkMap.computeIfAbsent(instance, inst -> new HashSet<>());
GroupedInstanceChunk groupedInstanceChunk = groupMap.computeIfAbsent(instance, inst -> new GroupedInstanceChunk());
groupedInstanceChunk.instanceChunks.add(instanceChunk);
}
@Override
public void end() {
chunkCoordinates.removeIf(chunkCoordinate -> chunkCoordinate.chunkX == chunkX &&
chunkCoordinate.chunkZ == chunkZ);
}
@Override
public void update() {
final long time = System.currentTimeMillis();
for (Map.Entry<Instance, GroupedInstanceChunk> entry : groupMap.entrySet()) {
public void update(long time) {
for (Map.Entry<Instance, Set<ChunkCoordinate>> entry : instanceChunkMap.entrySet()) {
final Instance instance = entry.getKey();
final GroupedInstanceChunk groupedInstanceChunk = entry.getValue();
final Set<ChunkCoordinate> chunkCoordinates = entry.getValue();
pool.execute(() -> {
updateInstance(instance, time);
for (InstanceChunk instanceChunk : groupedInstanceChunk.instanceChunks) {
Chunk chunk = instanceChunk.getChunk();
for (ChunkCoordinate chunkCoordinate : chunkCoordinates) {
final Chunk chunk = instance.getChunk(chunkCoordinate.chunkX, chunkCoordinate.chunkZ);
if (ChunkUtils.isChunkUnloaded(chunk))
continue;
updateChunk(instance, chunk, time);
updateEntities(instance, chunk, time);
}
});
}
}
/**
* Contains a list of {@link InstanceChunk}
*/
private static class GroupedInstanceChunk {
private List<InstanceChunk> instanceChunks = new ArrayList<>();
}
/**
* Contains both a chunk and its instance
*/
private static class InstanceChunk {
private Instance instance;
private Chunk chunk;
protected InstanceChunk(Instance instance, Chunk chunk) {
this.instance = instance;
this.chunk = chunk;
}
public Instance getInstance() {
return instance;
}
public Chunk getChunk() {
return chunk;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
InstanceChunk that = (InstanceChunk) o;
return Objects.equals(instance, that.instance) &&
Objects.equals(chunk, that.chunk);
}
@Override
public int hashCode() {
return Objects.hash(instance, chunk);
}
}

View File

@ -1,41 +0,0 @@
package net.minestom.server.thread;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import java.util.HashSet;
import java.util.Set;
public class SingleThreadProvider extends ThreadProvider {
private Set<Instance> instances = new HashSet<>();
private long time;
@Override
public void start() {
this.instances.clear();
this.time = System.currentTimeMillis();
}
@Override
public void linkThread(Instance instance, Chunk chunk) {
if (instances.add(instance)) {
updateInstance(instance, time);
}
updateChunk(instance, chunk, time);
updateEntities(instance, chunk, time);
}
@Override
public void end() {
}
@Override
public void update() {
}
}

View File

@ -31,27 +31,29 @@ public abstract class ThreadProvider {
}
/**
* Called to prepare the thread provider, to provide threads for the next server tick
*/
public abstract void start();
/**
* Assign a thread to a chunk, create one if none is defined
* Called when a chunk is loaded
*
* @param instance the instance where the chunk is
* @param chunk the chunk which should get an assigned thread
* @param instance the instance of the chunk
* @param chunkX the chunk X
* @param chunkZ the chunk Z
*/
public abstract void linkThread(Instance instance, Chunk chunk);
public abstract void onChunkLoad(Instance instance, int chunkX, int chunkZ);
/**
* Inform the server that all chunks have been assigned to a thread
* Called when a chunk is unloaded
*
* @param instance the instance of the chunk
* @param chunkX the chunk X
* @param chunkZ the chunk Z
*/
public abstract void end();
public abstract void onChunkUnload(Instance instance, int chunkX, int chunkZ);
/**
* Perform a server tick for all chunks based on their linked thread
*
* @param time the update time in milliseconds
*/
public abstract void update();
public abstract void update(long time);
/**
* Get the current size of the thread pool
@ -180,4 +182,13 @@ public abstract class ThreadProvider {
}
}
protected static class ChunkCoordinate {
public int chunkX, chunkZ;
public ChunkCoordinate(int chunkX, int chunkZ) {
this.chunkX = chunkX;
this.chunkZ = chunkZ;
}
}
}