Add Api world load/unload events and DhApiWorldProxy.get/setReadOnly()
This commit is contained in:
+26
-1
@@ -24,14 +24,39 @@ package com.seibel.distanthorizons.api.interfaces.world;
|
||||
* A world is equivalent to a single server connection or a singleplayer world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-11-20
|
||||
* @version 2024-9-27
|
||||
* @since API 1.0.0
|
||||
*/
|
||||
public interface IDhApiWorldProxy
|
||||
{
|
||||
//===================//
|
||||
// getters / setters //
|
||||
//===================//
|
||||
|
||||
/** Returns true if a world is loaded. */
|
||||
boolean worldLoaded();
|
||||
|
||||
/**
|
||||
* Defaults to false. <br>
|
||||
* Setting this to true will prevent DH from updating or creating new LODs.
|
||||
*
|
||||
* @since API 4.0.0
|
||||
* @see IDhApiWorldProxy#getReadOnly()
|
||||
* @throws IllegalStateException if no world is loaded
|
||||
*/
|
||||
void setReadOnly(boolean readOnly) throws IllegalStateException;
|
||||
/**
|
||||
* @since API 4.0.0
|
||||
* @see IDhApiWorldProxy#setReadOnly(boolean)
|
||||
* @throws IllegalStateException if no world is loaded
|
||||
*/
|
||||
boolean getReadOnly() throws IllegalStateException;
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// level handlers //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* In singleplayer this will return the level the player is currently in. <br>
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.api.methods.events.abstractEvents;
|
||||
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiWorldProxy;
|
||||
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
|
||||
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
|
||||
|
||||
/**
|
||||
* Called after Distant Horizons finishes loading a new level. <br>
|
||||
* Note: this may be fired before Minecraft has loaded in the player.
|
||||
*
|
||||
* @see IDhApiWorldProxy
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2024-9-27
|
||||
* @since API 4.0.0
|
||||
*/
|
||||
public abstract class DhApiWorldLoadEvent implements IDhApiEvent<DhApiWorldLoadEvent.EventParam>
|
||||
{
|
||||
/** Fired after Distant Horizons loads a new level. */
|
||||
public abstract void onLevelLoad(DhApiEventParam<EventParam> input);
|
||||
|
||||
|
||||
//=========================//
|
||||
// internal DH API methods //
|
||||
//=========================//
|
||||
|
||||
@Override
|
||||
public final void fireEvent(DhApiEventParam<EventParam> input) { this.onLevelLoad(input); }
|
||||
|
||||
|
||||
//==================//
|
||||
// parameter object //
|
||||
//==================//
|
||||
|
||||
public static class EventParam implements IDhApiEventParam
|
||||
{
|
||||
public EventParam() { }
|
||||
|
||||
|
||||
@Override
|
||||
public EventParam copy() { return new EventParam(); }
|
||||
}
|
||||
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.api.methods.events.abstractEvents;
|
||||
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
|
||||
import com.seibel.distanthorizons.api.interfaces.world.IDhApiWorldProxy;
|
||||
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent;
|
||||
import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam;
|
||||
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
|
||||
|
||||
/**
|
||||
* Called after Distant Horizons has finished unloading a level.
|
||||
*
|
||||
* @see IDhApiWorldProxy
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2024-9-27
|
||||
* @since API 4.0.0
|
||||
*/
|
||||
public abstract class DhApiWorldUnloadEvent implements IDhApiEvent<DhApiWorldUnloadEvent.EventParam>
|
||||
{
|
||||
/** Fired before Distant Horizons unloads a level. */
|
||||
public abstract void onLevelUnload(DhApiEventParam<EventParam> input);
|
||||
|
||||
|
||||
//=========================//
|
||||
// internal DH API methods //
|
||||
//=========================//
|
||||
|
||||
@Override
|
||||
public final void fireEvent(DhApiEventParam<EventParam> input) { this.onLevelUnload(input); }
|
||||
|
||||
|
||||
//==================//
|
||||
// parameter object //
|
||||
//==================//
|
||||
|
||||
public static class EventParam implements IDhApiEventParam
|
||||
{
|
||||
public EventParam() { }
|
||||
|
||||
|
||||
@Override
|
||||
public DhApiWorldLoadEvent.EventParam copy() { return new DhApiWorldLoadEvent.EventParam(); }
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,8 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.api.internal;
|
||||
|
||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiWorldLoadEvent;
|
||||
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiWorldUnloadEvent;
|
||||
import com.seibel.distanthorizons.core.Initializer;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
@@ -39,6 +41,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -89,6 +92,8 @@ public class SharedApi
|
||||
if (currentWorld != null)
|
||||
{
|
||||
ThreadPoolUtil.setupThreadPools();
|
||||
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldLoadEvent.class, new DhApiWorldLoadEvent.EventParam());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -102,6 +107,11 @@ public class SharedApi
|
||||
|
||||
// recommend that the garbage collector cleans up any objects from the old world and thread pools
|
||||
System.gc();
|
||||
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiWorldUnloadEvent.class, new DhApiWorldUnloadEvent.EventParam());
|
||||
|
||||
// fired after the unload event so API users can't change the read-only for any new worlds
|
||||
DhApiWorldProxy.INSTANCE.setReadOnly(false, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,6 +180,13 @@ public class SharedApi
|
||||
return;
|
||||
}
|
||||
|
||||
// ignore updates if the world is read-only
|
||||
if (DhApiWorldProxy.INSTANCE.getReadOnly())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// only continue if the level is loaded
|
||||
IDhLevel dhLevel = dhWorld.getLevel(level);
|
||||
if (dhLevel == null)
|
||||
@@ -261,10 +278,7 @@ public class SharedApi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** returning a {@link CompletableFuture} isn't necessary, but allows Intellij to properly show the full stack trace when debugging. */
|
||||
@SuppressWarnings("UnusedReturnValue")
|
||||
private static void processQueuedChunkUpdate()
|
||||
{
|
||||
//LOGGER.trace(chunkWrapper.getChunkPos() + " " + executor.getActiveCount() + " / " + executor.getQueue().size() + " - " + executor.getCompletedTaskCount());
|
||||
|
||||
+2
-1
@@ -40,6 +40,7 @@ import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
|
||||
@@ -211,7 +212,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
try
|
||||
{
|
||||
// loop until the generator is shutdown
|
||||
while (!Thread.interrupted())
|
||||
while (!Thread.interrupted() && !DhApiWorldProxy.INSTANCE.getReadOnly())
|
||||
{
|
||||
this.generator.preGeneratorTaskStart();
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.generation.IFullDataSourceRetrievalQueue;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -60,7 +61,7 @@ public class WorldGenModule implements Closeable
|
||||
GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener,
|
||||
GeneratedFullDataSourceProvider dataSourceProvider,
|
||||
Supplier<? extends AbstractWorldGenState> worldGenStateSupplier
|
||||
)
|
||||
)
|
||||
{
|
||||
this.onWorldGenCompleteListener = onWorldGenCompleteListener;
|
||||
this.dataSourceProvider = dataSourceProvider;
|
||||
@@ -111,6 +112,8 @@ public class WorldGenModule implements Closeable
|
||||
public void worldGenTick()
|
||||
{
|
||||
boolean shouldDoWorldGen = this.onWorldGenCompleteListener.shouldDoWorldGen();
|
||||
// if the world is read only don't generate anything
|
||||
shouldDoWorldGen &= !DhApiWorldProxy.INSTANCE.getReadOnly();
|
||||
|
||||
boolean isWorldGenRunning = this.isWorldGenRunning();
|
||||
if (shouldDoWorldGen && !isWorldGenRunning)
|
||||
@@ -188,8 +191,8 @@ public class WorldGenModule implements Closeable
|
||||
String waitingCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getWaitingTaskCount());
|
||||
String inProgressCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getInProgressTaskCount());
|
||||
String totalCountEstimateStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getEstimatedTotalTaskCount());
|
||||
messageList.add("World Gen Tasks: "+waitingCountStr+"/"+totalCountEstimateStr+" (in progress: "+inProgressCountStr+")");
|
||||
|
||||
messageList.add("World Gen Tasks: ${waitingCountStr}/${totalCountEstimateStr} (in progress: ${inProgressCountStr})");
|
||||
|
||||
worldGenState.worldGenerationQueue.addDebugMenuStringsToList(messageList);
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
@@ -133,6 +134,11 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
|
||||
|
||||
public synchronized boolean tick(DhBlockPos2D targetPos)
|
||||
{
|
||||
if (DhApiWorldProxy.INSTANCE.getReadOnly())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.closingFuture != null || !this.networkState.isReady())
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -59,6 +59,18 @@ public abstract class AbstractDhWorld implements IDhWorld, Closeable
|
||||
* by overriding children.
|
||||
*/
|
||||
public void addDebugMenuStringsToList(List<String> messageList)
|
||||
{ messageList.add(this.environment + " World with " + F3Screen.NUMBER_FORMAT.format(this.getLoadedLevelCount()) + " levels"); }
|
||||
{
|
||||
EWorldEnvironment environment = this.environment;
|
||||
String levelCountStr = F3Screen.NUMBER_FORMAT.format(this.getLoadedLevelCount());
|
||||
|
||||
String readOnlyStr = "";
|
||||
if (DhApiWorldProxy.INSTANCE.getReadOnly())
|
||||
{
|
||||
readOnlyStr += " - ReadOnly";
|
||||
}
|
||||
|
||||
String message = "${environment} World with ${levelCountStr} levels${readOnlyStr}";
|
||||
messageList.add(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,9 +25,11 @@ import com.seibel.distanthorizons.api.interfaces.world.IDhApiWorldProxy;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -38,16 +40,20 @@ import java.util.ArrayList;
|
||||
* to be loaded/unloaded.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2022-11-20
|
||||
* @version 2024-9-27
|
||||
*/
|
||||
public class DhApiWorldProxy implements IDhApiWorldProxy
|
||||
{
|
||||
public static DhApiWorldProxy INSTANCE = new DhApiWorldProxy();
|
||||
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
|
||||
private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class);
|
||||
private static final String NO_WORLD_EXCEPTION_STRING = "No world loaded";
|
||||
|
||||
private boolean isReadOnly = false;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
@@ -58,13 +64,61 @@ public class DhApiWorldProxy implements IDhApiWorldProxy
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
//===================//
|
||||
// getters / setters //
|
||||
//===================//
|
||||
|
||||
@Override
|
||||
public boolean worldLoaded() { return SharedApi.getAbstractDhWorld() != null; }
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) { this.setReadOnly(readOnly, true); }
|
||||
/**
|
||||
* Not part of the public API.
|
||||
* Normal API users shouldn't be able to change the upcoming world state
|
||||
* this is only here so DH can revert the readonly value after the world is unloaded
|
||||
*/
|
||||
public void setReadOnly(boolean readOnly, boolean throwIfWorldUnloaded)
|
||||
{
|
||||
if (throwIfWorldUnloaded && SharedApi.getAbstractDhWorld() == null)
|
||||
{
|
||||
throw new IllegalStateException(NO_WORLD_EXCEPTION_STRING);
|
||||
}
|
||||
|
||||
boolean valueChanged = (this.isReadOnly != readOnly);
|
||||
this.isReadOnly = readOnly;
|
||||
|
||||
if (valueChanged)
|
||||
{
|
||||
if (this.isReadOnly)
|
||||
{
|
||||
LOGGER.info("DH world set to read-only. LODs will not update while this API flag is active.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.info("DH world is no longer in read-only mode. LODs will update like normal.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getReadOnly()
|
||||
{
|
||||
if (SharedApi.getAbstractDhWorld() == null)
|
||||
{
|
||||
throw new IllegalStateException(NO_WORLD_EXCEPTION_STRING);
|
||||
}
|
||||
|
||||
|
||||
return this.isReadOnly;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// level handlers //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public IDhApiLevelWrapper getSinglePlayerLevel()
|
||||
{
|
||||
@@ -146,4 +200,5 @@ public class DhApiWorldProxy implements IDhApiWorldProxy
|
||||
return returnList;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+1
-6
@@ -27,12 +27,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
|
||||
|
||||
/**
|
||||
* Can be either a Server world or a Client world.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2023-6-17
|
||||
*/
|
||||
/** Can be either a Server world or a Client world. */
|
||||
public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
|
||||
{
|
||||
|
||||
|
||||
Reference in New Issue
Block a user