mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-19 06:32:03 +01:00
WIP dynamic chunk thread change
This commit is contained in:
parent
2e4a443bec
commit
e9f13d0a82
@ -8,6 +8,7 @@ import net.minestom.server.lock.Acquisition;
|
||||
import net.minestom.server.monitoring.TickMonitor;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
import net.minestom.server.network.player.NettyPlayerConnection;
|
||||
import net.minestom.server.thread.PerChunkThreadProvider;
|
||||
import net.minestom.server.thread.PerInstanceThreadProvider;
|
||||
import net.minestom.server.thread.ThreadProvider;
|
||||
import net.minestom.server.utils.async.AsyncUtils;
|
||||
@ -39,8 +40,8 @@ public final class UpdateManager {
|
||||
|
||||
{
|
||||
// DEFAULT THREAD PROVIDER
|
||||
threadProvider = new PerInstanceThreadProvider(4);
|
||||
//threadProvider = new PerChunkThreadProvider(4);
|
||||
//threadProvider = new PerInstanceThreadProvider(4);
|
||||
threadProvider = new PerChunkThreadProvider(4);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,7 +45,6 @@ import net.minestom.server.item.Material;
|
||||
import net.minestom.server.item.metadata.WrittenBookMeta;
|
||||
import net.minestom.server.listener.PlayerDiggingListener;
|
||||
import net.minestom.server.lock.Acquirable;
|
||||
import net.minestom.server.lock.AcquirableCollection;
|
||||
import net.minestom.server.network.ConnectionManager;
|
||||
import net.minestom.server.network.ConnectionState;
|
||||
import net.minestom.server.network.PlayerProvider;
|
||||
@ -330,15 +329,16 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
|
||||
//System.out.println(getAcquiredElement().getHandler().getBatchThread());
|
||||
Collection<Acquirable<Player>> players = new ArrayList<>();
|
||||
//if (username.equals("TheMode911"))
|
||||
/*for (Player p1 : MinecraftServer.getConnectionManager().getOnlinePlayers()) {
|
||||
for (Player p1 : MinecraftServer.getConnectionManager().getOnlinePlayers()) {
|
||||
//players.add(p1.getAcquiredElement());
|
||||
p1.getAcquiredElement().acquire(o -> {
|
||||
for (Player p2 : MinecraftServer.getConnectionManager().getOnlinePlayers())
|
||||
//System.out.println(getUsername()+": "+o+" "+getAliveTicks());
|
||||
/*for (Player p2 : MinecraftServer.getConnectionManager().getOnlinePlayers())
|
||||
p2.getAcquiredElement().acquire(o2 -> {
|
||||
//System.out.println(getAcquiredElement().getHandler().getBatchThread().monitor.isOccupiedByCurrentThread());
|
||||
});
|
||||
});*/
|
||||
});
|
||||
}*/
|
||||
}
|
||||
|
||||
/*AcquirableCollection<Player> players1 = new AcquirableCollection<>(players);
|
||||
players1.forEach(player -> {
|
||||
|
@ -54,7 +54,7 @@ import java.util.function.Consumer;
|
||||
* WARNING: when making your own implementation registering the instance manually is required
|
||||
* with {@link InstanceManager#registerInstance(Instance)}, and
|
||||
* you need to be sure to signal the {@link UpdateManager} of the changes using
|
||||
* {@link UpdateManager#signalChunkLoad(Instance, Chunk)} and {@link UpdateManager#signalChunkUnload(Instance, Chunk)}.
|
||||
* {@link UpdateManager#signalChunkLoad(Chunk)} and {@link UpdateManager#signalChunkUnload(Chunk)}.
|
||||
*/
|
||||
public abstract class Instance implements BlockModifier, Tickable, EventHandler, DataContainer, PacketGroupingAudience {
|
||||
|
||||
@ -253,7 +253,7 @@ public abstract class Instance implements BlockModifier, Tickable, EventHandler,
|
||||
* Used when a {@link Chunk} is not currently loaded in memory and need to be retrieved from somewhere else.
|
||||
* Could be read from disk, or generated from scratch.
|
||||
* <p>
|
||||
* Be sure to signal the chunk using {@link UpdateManager#signalChunkLoad(Instance, Chunk)} and to cache
|
||||
* Be sure to signal the chunk using {@link UpdateManager#signalChunkLoad(Chunk)} and to cache
|
||||
* that this chunk has been loaded.
|
||||
* <p>
|
||||
* WARNING: it has to retrieve a chunk, this is not optional and should execute the callback in all case.
|
||||
@ -267,7 +267,7 @@ public abstract class Instance implements BlockModifier, Tickable, EventHandler,
|
||||
/**
|
||||
* Called to generated a new {@link Chunk} from scratch.
|
||||
* <p>
|
||||
* Be sure to signal the chunk using {@link UpdateManager#signalChunkLoad(Instance, Chunk)} and to cache
|
||||
* Be sure to signal the chunk using {@link UpdateManager#signalChunkLoad(Chunk)} and to cache
|
||||
* that this chunk has been loaded.
|
||||
* <p>
|
||||
* This is where you can put your chunk generation code.
|
||||
|
@ -1,8 +1,8 @@
|
||||
package net.minestom.server.lock;
|
||||
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.thread.BatchThread;
|
||||
import net.minestom.server.thread.ThreadProvider;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@ -61,21 +61,19 @@ public interface Acquirable<T> {
|
||||
|
||||
class Handler {
|
||||
|
||||
private volatile BatchThread batchThread;
|
||||
private volatile Chunk batchChunk;
|
||||
private volatile ThreadProvider.ChunkEntry chunkEntry;
|
||||
|
||||
public BatchThread getBatchThread() {
|
||||
return batchThread;
|
||||
}
|
||||
|
||||
public Chunk getBatchChunk() {
|
||||
return batchChunk;
|
||||
public ThreadProvider.ChunkEntry getChunkEntry() {
|
||||
return chunkEntry;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public void refreshBatchInfo(@NotNull BatchThread batchThread, @NotNull Chunk batchChunk) {
|
||||
this.batchThread = batchThread;
|
||||
this.batchChunk = batchChunk;
|
||||
public void refreshChunkEntry(@NotNull ThreadProvider.ChunkEntry chunkEntry) {
|
||||
this.chunkEntry = chunkEntry;
|
||||
}
|
||||
|
||||
public BatchThread getBatchThread() {
|
||||
return chunkEntry != null ? chunkEntry.getThread() : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,12 @@
|
||||
package net.minestom.server.thread;
|
||||
|
||||
import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.entity.Entity;
|
||||
import net.minestom.server.instance.Chunk;
|
||||
import net.minestom.server.instance.Instance;
|
||||
import net.minestom.server.instance.InstanceContainer;
|
||||
import net.minestom.server.instance.SharedInstance;
|
||||
import net.minestom.server.lock.Acquirable;
|
||||
import net.minestom.server.utils.chunk.ChunkUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
@ -22,6 +24,8 @@ public abstract class ThreadProvider {
|
||||
|
||||
private final Map<BatchThread, Set<ChunkEntry>> threadChunkMap = new HashMap<>();
|
||||
private final Map<Chunk, ChunkEntry> chunkEntryMap = new HashMap<>();
|
||||
// Iterated over to refresh the thread used to update entities & chunks
|
||||
private final ArrayDeque<Chunk> chunks = new ArrayDeque<>();
|
||||
private final Set<Entity> removedEntities = ConcurrentHashMap.newKeySet();
|
||||
|
||||
public ThreadProvider(int threadCount) {
|
||||
@ -67,7 +71,8 @@ public abstract class ThreadProvider {
|
||||
ChunkEntry chunkEntry = new ChunkEntry(thread, chunk);
|
||||
chunks.add(chunkEntry);
|
||||
|
||||
chunkEntryMap.put(chunk, chunkEntry);
|
||||
this.chunkEntryMap.put(chunk, chunkEntry);
|
||||
this.chunks.add(chunk);
|
||||
}
|
||||
|
||||
protected void removeChunk(Chunk chunk) {
|
||||
@ -80,6 +85,7 @@ public abstract class ThreadProvider {
|
||||
}
|
||||
chunkEntryMap.remove(chunk);
|
||||
}
|
||||
this.chunks.remove(chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,7 +111,10 @@ public abstract class ThreadProvider {
|
||||
.collect(Collectors.toList());
|
||||
Acquirable.refreshEntities(Collections.unmodifiableList(entities));
|
||||
chunkEntries.forEach(chunkEntry -> {
|
||||
chunkEntry.chunk.tick(time);
|
||||
Chunk chunk = chunkEntry.chunk;
|
||||
if (!ChunkUtils.isLoaded(chunk))
|
||||
return;
|
||||
chunk.tick(time);
|
||||
chunkEntry.entities.forEach(entity -> {
|
||||
thread.monitor.enter();
|
||||
entity.tick(time);
|
||||
@ -122,48 +131,44 @@ public abstract class ThreadProvider {
|
||||
{
|
||||
for (Entity entity : removedEntities) {
|
||||
Acquirable<Entity> acquirable = entity.getAcquiredElement();
|
||||
Chunk batchChunk = acquirable.getHandler().getBatchChunk();
|
||||
|
||||
ChunkEntry chunkEntry = acquirable.getHandler().getChunkEntry();
|
||||
// Remove from list
|
||||
{
|
||||
ChunkEntry chunkEntry = chunkEntryMap.get(batchChunk);
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.remove(entity);
|
||||
}
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.remove(entity);
|
||||
}
|
||||
}
|
||||
this.removedEntities.clear();
|
||||
}
|
||||
|
||||
// Update as many entities as possible
|
||||
// TODO: incremental update instead of full
|
||||
for (Instance instance : MinecraftServer.getInstanceManager().getInstances()) {
|
||||
var entities = instance.getEntities();
|
||||
for (Entity entity : entities) {
|
||||
Acquirable<Entity> acquirable = entity.getAcquiredElement();
|
||||
Chunk batchChunk = acquirable.getHandler().getBatchChunk();
|
||||
Chunk entityChunk = entity.getChunk();
|
||||
if (!Objects.equals(batchChunk, entityChunk)) {
|
||||
// Entity is possibly not in the correct thread
|
||||
|
||||
// Remove from previous list
|
||||
{
|
||||
ChunkEntry chunkEntry = chunkEntryMap.get(batchChunk);
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.remove(entity);
|
||||
}
|
||||
}
|
||||
int size = chunks.size();
|
||||
int counter = 0;
|
||||
// TODO incremental update, maybe a percentage of the tick time?
|
||||
while (counter++ < size) {
|
||||
Chunk chunk = chunks.pollFirst();
|
||||
if (!ChunkUtils.isLoaded(chunk)) {
|
||||
removeChunk(chunk);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add to new list
|
||||
{
|
||||
ChunkEntry chunkEntry = chunkEntryMap.get(entityChunk);
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.add(entity);
|
||||
acquirable.getHandler().refreshBatchInfo(chunkEntry.thread, chunkEntry.chunk);
|
||||
}
|
||||
// Update chunk threads
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
// Update entities
|
||||
{
|
||||
Instance instance = chunk.getInstance();
|
||||
refreshEntitiesThread(instance, chunk);
|
||||
if (instance instanceof InstanceContainer) {
|
||||
for (SharedInstance sharedInstance : ((InstanceContainer) instance).getSharedInstances()) {
|
||||
refreshEntitiesThread(sharedInstance, chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add back to the deque
|
||||
chunks.addLast(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
@ -179,7 +184,37 @@ public abstract class ThreadProvider {
|
||||
return threads;
|
||||
}
|
||||
|
||||
private static class ChunkEntry {
|
||||
private void refreshEntitiesThread(Instance instance, Chunk chunk) {
|
||||
var entities = instance.getChunkEntities(chunk);
|
||||
for (Entity entity : entities) {
|
||||
Acquirable<Entity> acquirable = entity.getAcquiredElement();
|
||||
ChunkEntry handlerChunkEntry = acquirable.getHandler().getChunkEntry();
|
||||
Chunk batchChunk = handlerChunkEntry != null ? handlerChunkEntry.getChunk() : null;
|
||||
|
||||
Chunk entityChunk = entity.getChunk();
|
||||
if (!Objects.equals(batchChunk, entityChunk)) {
|
||||
// Entity is possibly not in the correct thread
|
||||
|
||||
// Remove from previous list
|
||||
{
|
||||
if (handlerChunkEntry != null) {
|
||||
handlerChunkEntry.entities.remove(entity);
|
||||
}
|
||||
}
|
||||
|
||||
// Add to new list
|
||||
{
|
||||
ChunkEntry chunkEntry = chunkEntryMap.get(entityChunk);
|
||||
if (chunkEntry != null) {
|
||||
chunkEntry.entities.add(entity);
|
||||
acquirable.getHandler().refreshChunkEntry(chunkEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChunkEntry {
|
||||
private final BatchThread thread;
|
||||
private final Chunk chunk;
|
||||
private final List<Entity> entities = new ArrayList<>();
|
||||
@ -189,6 +224,14 @@ public abstract class ThreadProvider {
|
||||
this.chunk = chunk;
|
||||
}
|
||||
|
||||
public @NotNull BatchThread getThread() {
|
||||
return thread;
|
||||
}
|
||||
|
||||
public @NotNull Chunk getChunk() {
|
||||
return chunk;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
@ -292,7 +292,7 @@ public class PlayerInit {
|
||||
|
||||
// Unload the chunk (save memory) if it has no remaining viewer
|
||||
if (chunk.getViewers().isEmpty()) {
|
||||
//player.getInstance().unloadChunk(chunk);
|
||||
player.getInstance().unloadChunk(chunk);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user