package de.geolykt.starloader.api; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Vector; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus.AvailableSince; import org.jetbrains.annotations.ApiStatus.Obsolete; import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NonBlocking; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.UnmodifiableView; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.badlogic.gdx.Application; import net.minestom.server.extras.selfmodification.MinestomExtensionClassLoader; import de.geolykt.starloader.DeprecatedSince; import de.geolykt.starloader.StarloaderAPIExtension; import de.geolykt.starloader.api.actor.Actor; import de.geolykt.starloader.api.actor.SpawnPredicatesContainer; import de.geolykt.starloader.api.actor.StateActorSpawnPredicate; import de.geolykt.starloader.api.actor.WeaponsManager; import de.geolykt.starloader.api.dimension.Dimension; import de.geolykt.starloader.api.dimension.Empire; import de.geolykt.starloader.api.empire.Alliance; import de.geolykt.starloader.api.empire.Star; import de.geolykt.starloader.api.empire.StarlaneGenerator; import de.geolykt.starloader.api.empire.War; import de.geolykt.starloader.api.empire.people.DynastyMember; import de.geolykt.starloader.api.event.lifecycle.ApplicationStartEvent; import de.geolykt.starloader.api.event.lifecycle.ApplicationStartedEvent; import de.geolykt.starloader.api.event.lifecycle.LogicalTickEvent; import de.geolykt.starloader.api.event.lifecycle.LogicalTickEvent.Phase; import de.geolykt.starloader.api.gui.BackgroundTask; import de.geolykt.starloader.api.gui.MapMode; import de.geolykt.starloader.api.gui.MouseInputListener; import de.geolykt.starloader.api.serial.SavegameFormat; import de.geolykt.starloader.api.serial.SupportedSavegameFormat; import de.geolykt.starloader.api.sound.SoundHandler; import de.geolykt.starloader.api.utils.NoiseProvider; import de.geolykt.starloader.api.utils.RandomNameType; import de.geolykt.starloader.api.utils.TickLoopLock; /** * Class to redirect to instance-wide constants or other static * methods/variables. This should be used to reduce the amount of calls to * obfuscated methods, which will improve the sanity of anyone that is working * on updating an extension. * *
It also contains miscellaneous methods that do not really fall under a category. * * @since 1.0.0 */ public final class Galimulator { /** * Interface that contains the methods used by Starloader's {@link Galimulator} class. * This is needed in order to abstract out certain concepts and give the possibility * of implementing the Starloader API on non-galimulator games or with nonstandard mappings. * *
This class should not be implemented by anything but the API Implementation. * * @since 1.2.0 */ public static interface GameImplementation { /** * Connect two stars with each other. The preferred way of connecting the two stars. * * @param starA the first Star to connect to the second star * @param starB the second Star to connect * @deprecated This method was replaced by {@link Dimension#connectStars(Star, Star)} */ @Deprecated @ScheduledForRemoval @DeprecatedSince("2.0.0-a20240519") public void connectStars(@NotNull Star starA, @NotNull Star starB); /** * Disconnect two stars with each other. The preferred way of removing the starlane * between two stars. * * @param starA the first Star to disconnect from the second star * @param starB the second Star to disconnect * @deprecated This method was replaced by {@link Dimension#disconnectStars(Star, Star)} */ @Deprecated @ScheduledForRemoval @DeprecatedSince("2.0.0-a20240519") public void disconnectStars(@NotNull Star starA, @NotNull Star starB); /** * Generates a random name through galimulator's name generator. * From which dictionary the name is pulled from or which algorithm * is used to generate the word is determined by the parameter. * * @param type The meaning the word should have * @return The generated word (or sentence) * @since 2.0.0 */ @NotNull @Contract(pure = true, value = "null -> fail, !null -> new") public String generateRandomName(@NotNull RandomNameType type); /** * Obtains the currently active map mode. * * @return The currently active map mode. * @see #setActiveMapmode(MapMode) */ @NotNull public MapMode getActiveMapmode(); /** * Gets the currently active background task. * A background task is a task that is being performed if the main thread is unable to * acquire any ticking lock (as per {@link TickLoopLock#acquireSoftControl()}), usually * caused by another thread having acquired "hard" control of the tick loop lock * (as per {@link TickLoopLock#acquireHardControl()}. This scenario causes the * loading screen to show up. * *
This method is safe to invoke on any thread. * *
The {@link BackgroundTask} API supersedes the short-lived
* {@link #setBackgroundTaskProgress(String)} API. While {@link #setBackgroundTaskProgress(String)}
* reused galimulator APIs, the {@link BackgroundTask} API inserts itself into the
* loading menu rendering pipeline. This change was performed as it was discovered
* that {@link #setBackgroundTaskProgress(String)} would be called very frequently,
* meaning that it would be better for performance to offload string concatenation
* used to call the method to the main rendering thread. This is achieved by making
* {@link BackgroundTask#getProgressDescription()} a method that lazily obtains the
* necessary string.
*
* @return The current {@link BackgroundTask} instance used.
* Please note that this method will return a potentially nonsensical instance if
* no background task is being executed while calling this method.
* @since 2.0.0-a20240831
* @see #setBackgroundTask(BackgroundTask)
*/
@NonBlocking
@ApiStatus.AvailableSince("2.0.0-a20240831")
@NotNull
@Contract(pure = true, value = "-> !null")
public BackgroundTask getBackgroundTask();
/**
* Returns the ActiveEmpire mapped to the given unique ID. If however
* there is no matching empire, the neutral empire is to be returned. Default
* implementation notice: The implementation of this method is very inefficient
* as it iterates over all known empires at worst. It is advisable that the extensions
* make use of caching.
*
* @param uid The UID of the empire, as defined by {@link Empire#getUID()}
* @return The {@link Empire} bound to the unique ID
* @since 1.5.0
* @deprecated The {@link de.geolykt.starloader.api.empire.ActiveEmpire} interface is scheduled for removal.
* Use {@link #lookupEmpire(int)} instead.
*/
@Deprecated
@ScheduledForRemoval(inVersion = "3.0.0")
@DeprecatedSince("2.0.0")
@Nullable
public de.geolykt.starloader.api.empire.@Nullable ActiveEmpire getEmpireByUID(int uid);
/**
* Gets the currently registered active empires. Note that like many other
* methods in the API, this is NOT a clone of the backing collection, which
* means that any modifications done to the collections will happen in game.
* This behaviour is intended as it can be useful in many situations as well as
* being more performance friendly
* As such the SLAPI documentation uses the terms "year" and "milliYear" interchangeably
* when talking about values (indirectly) produced by this method. However
* as of SLAPI 2.0 latter naming scheme is the recommended one, older documentations
* might still use the former.
*
* For the sake of brevity, in SLAPI, all dimensions share the same timeline.
* This is to reduce the chances of mods getting confused by sudden jumps in
* the current time. Hence, in multi-dimensional terms, {@link #getGameYear()} should
* be seen more as a global tick counter. It should strictly be ever-increasing.
*
* @return The in-game year.
*/
public int getGameYear();
/**
* Obtains the currently active map.
*
* @return The currently active map
* @deprecated Deprecated for removal without a replacement (the replacement is scheduled
* to be worked on in the future).
*/
@NotNull
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
public Map getMap();
/**
* Obtains the nearest Star that is near the given board coordinates and within the given
* search radius.
*
* @param boardX The X coordinate
* @param boardY The Y coordinate
* @param searchRadius The search radius
* @return The nearest star, or null if there is no star at the location.
* @since 2.0.0
* @deprecated Replaced by {@link Dimension#getNearestStar(float, float, float)}
*/
@Nullable
@Contract(pure = true)
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
public Star getNearestStar(float boardX, float boardY, float searchRadius);
/**
* Convenience method to obtain the neutral empire. The neutral empire should
* NOT be ticked as it may create serious side effects within the ticking
* mechanism. Additionally merging or destroying the empire might have serious
* side effects, which is why that should be avoided.
*
* @return The {@link Empire} that is the neutral non-playable empire.
* @deprecated Replaced by {@link Dimension#getNeutralEmpire()}
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
@NotNull
public de.geolykt.starloader.api.empire.@NotNull ActiveEmpire getNeutralEmpire();
/**
* Obtains the empire the player is controlling. If there is no player or no
* empire in control of the player, then it returns null.
*
* @return The {@link Empire} owned by the player, or null
* @deprecated Replaced by {@link Dimension#getPlayerEmpire()}
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
@Nullable
public de.geolykt.starloader.api.empire.@Nullable ActiveEmpire getPlayerEmpire();
/**
* Obtains the closest-matching {@link SavegameFormat} instance that can decode a given
* input stream. This method does not guarantee that the returned format fully supports
* the given input stream - that can only be evaluated once the format actually loads the
* savegame in it's entirety. Instead, this method checks which format might be able to
* load the savegame based on headers and whatnot. This means that this method is likely
* to return null for savegames saved by vanilla galimulator.
*
* @param input The input stream from which to read the savegame from.
* @return The closest matching {@link SavegameFormat}, or null if unknown.
* @since 2.0.0
* @implNote Implementations of this method shouldn't throw an exception, exception being
* if the parameter is null.
*/
@Nullable
public SavegameFormat getSavegameFormat(@NotNull InputStream input);
/**
* Obtains the {@link SavegameFormat} instance that is implementing a certain {@link SupportedSavegameFormat}.
*
* @param format The format the should be implemented
* @return The {@link SavegameFormat} implementing the specified format
* @throws UnsupportedOperationException If for one reason or another the savegame format is not supported
* @since 2.0.0
*/
@NotNull
@Contract(pure = true)
public SavegameFormat getSavegameFormat(@NotNull SupportedSavegameFormat format);
/**
* Obtains all supported savegame formats.
*
* @return The {@link SavegameFormat formats} that are supported.
* @since 2.0.0
*/
@NotNull
@Contract(pure = true, value = "-> !null")
public Iterable extends SavegameFormat> getSavegameFormats();
/**
* Obtains the lock on the simulation loop.
*
* @return The simulation loop {@link TickLoopLock}.
* @since 2.0.0
*/
@NotNull
public TickLoopLock getSimulationLoopLock();
/**
* Obtains the currently active {@link SoundHandler}.
*
* @return The active {@link SoundHandler}.
*/
public @NotNull SoundHandler getSoundHandler();
/**
* Obtains the star that is at or next to the given coordinates.
* This is basically a {@link #getNearestStar(float, float, float)} call
* with the radius being set to a magic value.
*
* @param boardX The X coordinate
* @param boardY The Y coordinate
* @return The star at the given location, or null if there is none
* @since 2.0.0
* @deprecated Replaced by {@link Dimension#getStarAt(float, float)}
*/
@Nullable
@Contract(pure = true)
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
public Star getStarAt(float boardX, float boardY);
/**
* Obtains an immutable view of the underlying list of the internal vector
* of stars.
*
* @return An immutable {@link List} of {@link Star stars} that are known
* @since 2.0.0
* @see Unsafe#getStarsUnsafe()
* @deprecated Replaced by {@link Dimension#getStarsView()}
*/
@Contract(pure = true, value = "-> new")
@NotNull
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
public List<@NotNull Star> getStarList();
/**
* Gets the currently registered Stars. Note that like many other methods in the
* API, this is NOT a clone of the backing collection, which means that any
* modifications done to the collections will happen in game. This behaviour is
* intended as it can be useful in many situations as well as being more
* performance friendly.
* This is basically the method that is used by Galimulator to load savegames.
*
* @param savegameFile The savegame file to load. Relative to the current working directory, but can be absolute.
* @since 2.0.0
*/
@NonBlocking
public void loadSavegameFile(@NotNull Path savegameFile);
/**
* Loads a savegame on another thread while also blocking the main ticking loop.
* If loading fails, a new galaxy is generated using the stretched_spiral generator with a size of
* 300 stars.
*
* This is basically the method that is used by Galimulator to load savegames.
*
* @param savegameFile The savegame file to load. Relative to the current working directory, but can be absolute.
* @since 2.0.0
*/
@SuppressWarnings("null")
@NonBlocking
public default void loadSavegameFile(@NotNull String savegameFile) {
loadSavegameFile(Paths.get(savegameFile));
}
/**
* Returns the {@link Empire} mapped to the given unique ID. If however
* there is no matching empire, the neutral empire is to be returned. Default
* implementation notice: The implementation of this method is very inefficient
* as it iterates over all known empires at worst. It is advisable that the extensions
* make use of caching.
*
* @param uid The UID of the empire, as defined by {@link Empire#getUID()}.
* @return The {@link Empire} bound to the unique ID, or {@link Dimension#getNeutralEmpire() neutral} as a fallback.
* @since 2.0.0
*/
@NotNull
public Empire lookupEmpire(int uid);
/**
* Obtains the {@link Star} instance that is associated by the given ID.
* If there is no star with the given ID an {@link IllegalArgumentException}
* is thrown.
*
* @param id The ID of the star
* @return The instance of the Star associated to the ID.
* @throws IllegalArgumentException If there is no star with the given ID
* @since 2.0.0
* @see Star#getUID()
* @implNote The implementation of this method is O(1), so unlike most other
* lookup methods, it is perfectly safe performance-wise to run this method
* repeatedly in an uncached manner.
*/
@NotNull
public Star lookupStar(int id);
/**
* "Panic" (also frequently known as "crashing") the game, causing it to show
* a panic message alongside a stacktrace of the caller.
*
* For all intents and purposes, a panic is irrevocable - that is it
* cannot be undone. As such, this method should always be used as a last resort
* when other methods of handling an error state cannot work or would lead to
* a less graceful application state (e.g. "freeze", "hang" or "deadlock").
*
* Vanilla galimulator (as of release 5.0.2 as of May 2024) only automatically
* handles error conditions in a way similar to this one for crashes in the main
* simulation loop and some crashes in the rendering logic. Especially when spawning
* additional threads or when otherwise working with asynchronous tasks then the
* likelihood of error handling being almost nonexistent is high so calling
* {@link GameImplementation#panic(String, boolean)} in these cases is appropriate
* as it improves the ability to efficiently debug the game.
*
* {@link GameImplementation#panic(String, boolean)} displays and logs the error
* message using an appropriate {@link Logger}, resulting in the crash message
* to be searchable in log files. Further, the list of used mods will be displayed,
* further improving the ability of finding the root cause of mod incompatibility
* issues. Other methods of handling fatal error states are likely to not include these
* features and thus can be harder to trace down (especially the logging aspect is
* important as users who may report errors might not always have the console on,
* while SLL always does logging in the background for as long as {@link Logger} instances
* are being used).
*
* DO NOT USE this method to handle non-fatal errors. More often
* than not one can simply use {@link Logger#error(String)} or {@link Logger#warn(String)}
* to log an error and proceed.
*
* This method is not blocking, that is it will exit and won't throw an exception while
* doing so. Having some way of aborting execution if this method is invokes may as such
* be preferable. That being said, this method will try to block and pause the simulation
* loop if possible in order to mitigate collateral damage. However, it is still possible
* that some logic continues to run in the background after executing this method. This is
* a limitation with how errors can be handled in java. As a nuclear option
* {@link System#exit(int)} can be used, but if {@link System#exit(int)} is to be
* used, then it's usage must be logged (even if nothing enforces it, but it's
* otherwise hard to debug system exits).
*
* For some things such as during {@link ApplicationStartEvent} or
* {@link ApplicationStartedEvent}, this method will automatically be called in case of an
* error. Explicitly calling this method is advised against, instead an appropriate exception
* should be thrown within the event handler.
*
* @param message The crash message to display.
* @param save Whether to save the game's state to disk before crashing.
* @see Logger#error(String)
* @since 2.0.0-a20240513
*/
@AvailableSince("2.0.0-a20240513")
public void panic(@NotNull String message, boolean save);
/**
* "Panic" (also frequently known as "crashing") the game, causing it to show
* a panic message alongside a the stacktrace of the provided {@link Throwable}.
*
* For all intents and purposes, a panic is irrevocable - that is it
* cannot be undone. As such, this method should always be used as a last resort
* when other methods of handling an error state cannot work or would lead to
* a less graceful application state (e.g. "freeze", "hang" or "deadlock").
*
* Vanilla galimulator (as of release 5.0.2 as of May 2024) only automatically
* handles error conditions in a way similar to this one for crashes in the main
* simulation loop and some crashes in the rendering logic. Especially when spawning
* additional threads or when otherwise working with asynchronous tasks then the
* likelihood of error handling being almost nonexistent is high so calling
* {@link GameImplementation#panic(String, boolean, Throwable)} in these cases
* is appropriate as it improves the ability to efficiently debug the game.
*
* {@link GameImplementation#panic(String, boolean, Throwable)} displays and logs
* the error message using an appropriate {@link Logger}, resulting in the crash message
* to be searchable in log files. Further, the list of used mods will be displayed,
* further improving the ability of finding the root cause of mod incompatibility
* issues. Other methods of handling fatal error states are likely to not include these
* features and thus can be harder to trace down (especially the logging aspect is
* important as users who may report errors might not always have the console on,
* while SLL always does logging in the background for as long as {@link Logger} instances
* are being used).
*
* DO NOT USE this method to handle non-fatal errors. More often
* than not one can simply use {@link Logger#error(String)} or {@link Logger#warn(String)}
* to log an error and proceed.
*
* This method is not blocking, that is it will exit and won't throw an exception while
* doing so. Having some way of aborting execution if this method is invokes may as such
* be preferable. That being said, this method will try to block and pause the simulation
* loop if possible in order to mitigate collateral damage. However, it is still possible
* that some logic continues to run in the background after executing this method. This is
* a limitation with how errors can be handled in java. As a nuclear option
* {@link System#exit(int)} can be used, but if {@link System#exit(int)} is to be
* used, then it's usage must be logged (even if nothing enforces it, but it's
* otherwise hard to debug system exits).
*
* For some things such as during {@link ApplicationStartEvent} or
* {@link ApplicationStartedEvent}, this method will automatically be called in case of an
* error. Explicitly calling this method is advised against, instead an appropriate exception
* should be thrown within the event handler.
*
* @param message The crash message to display.
* @param save Whether to save the game's state to disk before crashing.
* @param cause A {@link Throwable} that caused the panic state; used to define the displayed stacktrace.
* @see Logger#error(String, Throwable)
* @see Throwable#printStackTrace()
* @since 2.0.0-a20240513
*/
@AvailableSince("2.0.0-a20240513")
public void panic(@NotNull String message, boolean save, @NotNull Throwable cause);
/**
* Pauses the game. This only pauses the logical components of the application and will not impact the graphical components.
* It will also not cause the loading screen to show up.
*
* @see #resumeGame()
* @see #setPaused(boolean)
* @implNote Under galimulator 4.X this operation was not thread safe and would cause a crash if called on another thread.
* If such compatibility is needed, this method should be called on the next tick via {@link Application#postRunnable(Runnable)}.
* Under galimulator 5.X, this problem does not occur. The sibling method {@link #resumeGame()} is unaffected.
*
* @deprecated While the javadocs of this method state that no impact on graphical components exist, this is not the case
* as this method causes the "step" widget to show. Calling this method while already paused has undocumented behaviour
* due to that. {@link #setPaused(boolean)} has this behaviour documented more cleanly.
*/
@Obsolete(since = "2.0.0-a20240105")
public void pauseGame();
/**
* Convenience method to calculate the voronoi graphs (the fancy section-outline
* around the stars) for all stars. This is helpful if a star got moved, however
* a call to this method does not recalculate starlanes, which is also needed
* alongside this method in some cases.
*/
public void recalculateVoronoiGraphs();
/**
* Registers a {@link MouseInputListener}.
* The priority of a {@link MouseInputListener} registered through this method is defined by the point of time
* of this method call compared to the time of registration of the other listeners.
*
* Registration can happen at any time, including during the extension loading phase (provided SLAPI
* is already on the classpath and initialized).
*
* @param listener The listener to register
* @since 2.0.0
*/
public void registerMouseInputListener(@NotNull MouseInputListener listener);
/**
* Resumes the game. This method basically reverts {@link #pauseGame()}
*
* This method is obsolete, using {@link #setPaused(boolean)} with the argument of false is recommended
* as an alternative. However this method is not acutely a candidate for deprecation.
*/
@Obsolete(since = "2.0.0-20240105")
public void resumeGame();
/**
* Schedule a task that should run on the next frame. More concretely, it should run before the next frame is drawn, however
* other tasks may run before that. If two tasks are scheduled at the same frame, the task that is scheduled first should run
* before. All scheduled tasks need to be run on the main graphical/LWJGL thread; The simulation loop lock (as per
* {@link #getSimulationLoopLock()}) is not acquired.
*
* This methods schedules task to be run on the next graphical frame,
* not on the next simulation tick. For that, use {@link #runTaskOnNextTick(Runnable)} instead.
*
* Calling this method is equivalent to using {@link Application#postRunnable(Runnable)}.
*
* @param task The task to run on the next tick
* @since 2.0.0
*/
public void runTaskOnNextFrame(Runnable task);
/**
* Run a given action on the next simulation tick.
* The simulation loop lock (see {@link #getSimulationLoopLock()}) will not be acquired at that point of time.
*
* To schedule a task on the next frame, use {@link #runTaskOnNextFrame(Runnable)} instead.
*
* Warning: This method is pause-insensitive - that is it can also be called while the game is in slow-motion
* or paused. As such, the task WILL be called before any {@link LogicalTickEvent} handler but is roughly equivalent
* to {@link Phase#PRE_GRAPHICAL}.
*
* @param runnable The task to execute
* @since 2.0.0
*/
public void runTaskOnNextTick(@NotNull Runnable runnable);
/**
* Saves a file inside the data folder.
* The file will NOT be overridden if it already exists.
*
* @param name The name of the file, the `data/` part must be omitted
* @param data The data of the file.
*/
public void saveFile(@NotNull String name, byte[] data);
/**
* Saves a file inside the data folder.
* The file will NOT be overridden if it already exists. The input data stream will be exhausted by this operation,
* however it will NOT be closed
*
* @param name The name of the file, the `data/` part must be omitted
* @param data The data of the file.
*/
public void saveFile(@NotNull String name, @NotNull InputStream data);
/**
* Changes the currently active map mode to a new value.
*
* @param mode The new active map mode.
* @see #getActiveMapmode()
*/
public void setActiveMapmode(@NotNull MapMode mode);
/**
* Sets the currently active background task.
* A background task is a task that is being performed if the main thread is unable to
* acquire any ticking lock (as per {@link TickLoopLock#acquireSoftControl()}), usually
* caused by another thread having acquired "hard" control of the tick loop lock
* (as per {@link TickLoopLock#acquireHardControl()}. This scenario causes the
* loading screen to show up.
*
* This method is safe to invoke on any thread, but should generally
* be invoked by the thread holding the "hard" tick loop lock control,
* or a child thread of said thread.
*
* The {@link BackgroundTask} API supersedes the short-lived
* {@link #setBackgroundTaskProgress(String)} API. While {@link #setBackgroundTaskProgress(String)}
* reused galimulator APIs, the {@link BackgroundTask} API inserts itself into the
* loading menu rendering pipeline. This change was performed as it was discovered
* that {@link #setBackgroundTaskProgress(String)} would be called very frequently,
* meaning that it would be better for performance to offload string concatenation
* used to call the method to the main rendering thread. This is achieved by making
* {@link BackgroundTask#getProgressDescription()} a method that lazily obtains the
* necessary string.
*
* @param task The new {@link BackgroundTask} instance which should be used from now on.
* @since 2.0.0-a20240831
* @see #getBackgroundTask()
*/
@NonBlocking
@ApiStatus.AvailableSince("2.0.0-a20240831")
@Contract(pure = false)
public void setBackgroundTask(@NotNull BackgroundTask task);
/**
* Sets the progress of the currently performed background task.
* A background task is a task that is being performed if the main thread is unable to
* acquire any ticking lock (as per {@link TickLoopLock#acquireSoftControl()}), usually
* caused by another thread having acquired "hard" control of the tick loop lock
* (as per {@link TickLoopLock#acquireHardControl()}. This scenario causes the
* loading screen to show up.
*
* This progress string is displayed on the aforementioned loading screen
* after the "main" background task description. This method is supposed to be
* used for features such as galaxy generation (e.g. within {@link StarlaneGenerator})
* or galaxy loading, or any other feature that may induce a loading screen due
* to the semantics mentioned in the previous paragraph.
*
* This method is safe to invoke on any thread, but should generally
* be invoked by the thread holding the "hard" tick loop lock control,
* or a child thread of said thread.
*
* In vanilla galimulator this method is solely used by the landmark generation,
* and even then only in a flawed capacity that means that it isn't being incremented
* correctly. The string used here follows the following format:
* This method can be safely called on a different thread.
*
* @param paused True to pause, false to resume
* @since 2.0.0-a20240105
*/
@NonBlocking
@ApiStatus.AvailableSince("2.0.0-a20240105")
public void setPaused(boolean paused);
/**
* Sets the amount of empires that have transcended in during the game.
*
* @param count The amount of transcended empires
*/
public void setTranscendedEmpires(int count);
/**
* Sets whether the sandbox mode has been used within this savegame.
*
* @param state The sandbox used modifier
*/
public void setUsedSandbox(boolean state);
/**
* Displays the widgets used for the galaxy creation process to the user.
*
* @since 2.0.0
*/
public void showGalaxyCreationScreen();
/**
* Shows the online scenario browser.
*
* @since 2.0.0
*/
public void showOnlineScenarioBrowser();
/**
* Displays the widget responsible for allowing the user to upload their maps or ship mods
* to the steam workshop.
*
* @since 2.0.0
*/
public void showModUploadScreen();
/**
* Shows the editor that allows one to upload a scenario to the Internet
* and to edit the scenario metadata of the scenario saved within the given map.
*
* @param map The map where the scenario to edit lies within
* @since 2.0.0
* @deprecated Deprecated for removal without a replacement (the replacement is scheduled
* to be worked on in the future).
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
public void showScenarioMetadataEditor(de.geolykt.starloader.api.@NotNull Map map);
/**
* Displays the widget responsible for saving the currently active savegame as a scenario
* to the user.
*
* @since 2.0.0
*/
public void showScenarioSaveScreen();
}
/**
* "Unsafe" methods for implementation.
* Usage of these methods is discouraged, however could prove to be beneficial in some
* circumstances, especially when performance is required.
*
* @deprecated Full binary compatibility is not guaranteed for this interface.
*/
@DeprecatedSince("1.5.0")
@Deprecated
public interface Unsafe {
/**
* Obtains the internal agent vector without cloning it.
*
* @return The actors currently active
*/
public Vector This vector can be modified but beware of the dangers such an operation
* may have. As galimulator keeps track the amount of stars in a separate variable,
* it is generally recommended to invoke {@link #setStarsUnsafe(Vector)} after any modification,
* as that method is programmed to reset the variable to the proper value given by
* {@link Vector#size()}.
*
* @return The stars currently registered
*/
public Vector This method is safe to invoke on any thread.
*
* The {@link BackgroundTask} API supersedes the short-lived
* {@link #setBackgroundTaskProgress(String)} API. While {@link #setBackgroundTaskProgress(String)}
* reused galimulator APIs, the {@link BackgroundTask} API inserts itself into the
* loading menu rendering pipeline. This change was performed as it was discovered
* that {@link #setBackgroundTaskProgress(String)} would be called very frequently,
* meaning that it would be better for performance to offload string concatenation
* used to call the method to the main rendering thread. This is achieved by making
* {@link BackgroundTask#getProgressDescription()} a method that lazily obtains the
* necessary string.
*
* @return The current {@link BackgroundTask} instance used.
* Please note that this method will return a potentially nonsensical instance if
* no background task is being executed while calling this method.
*/
@NonBlocking
@ApiStatus.AvailableSince("2.0.0-a20240831")
@NotNull
@Contract(pure = true, value = "-> !null")
public static BackgroundTask getBackgroundTask() {
return Galimulator.impl.getBackgroundTask();
}
/**
* Obtains the currently active {@link GameConfiguration} directly.
*
* @return The implementation of the configuration that is used right now
*/
@NotNull
public static GameConfiguration getConfiguration() {
GameConfiguration conf = Galimulator.config;
if (conf == null) {
throw new IllegalStateException("The implementation was not (yet) specified. This is a programmer error.");
}
return conf;
}
/**
* Returns the {@link Empire} mapped to the given unique ID. If however
* there is no matching empire, the neutral empire is to be returned. Default
* implementation notice: The implementation of this method is very inefficient
* as it iterates over all known empires at worst. It is advisable that the extensions
* make use of caching.
*
* @param uid The UID of the empire, as defined by {@link Empire#getUID()}
* @return The {@link Empire} bound to the unique ID
* @deprecated The {@link de.geolykt.starloader.api.empire.ActiveEmpire} interface is scheduled for removal.
* Use {@link #lookupEmpire(int)} instead.
*/
@Deprecated
@ScheduledForRemoval(inVersion = "3.0.0")
@DeprecatedSince("2.0.0")
@Nullable
public static de.geolykt.starloader.api.empire.@Nullable ActiveEmpire getEmpireByUID(int uid) {
return Galimulator.impl.getEmpireByUID(uid);
}
/**
* Gets the currently registered active empires. Note that like many other
* methods in the API, this is NOT a clone of the backing collection, which
* means that any modifications done to the collections will happen in game.
* This behaviour is intended as it can be useful in many situations as well as
* being more performance friendly
*
* @return A {@link Vector} of {@link Empire empires} that are known
* @deprecated This API breaches several design principles present in modern releases of SLAPI.
* Replaced by {@link Dimension#getEmpiresView()}.
*/
@SuppressWarnings("null")
@Deprecated
@DeprecatedSince("2.0.0")
@ScheduledForRemoval(inVersion = "3.0.0")
public static @NotNull Vector As such the SLAPI documentation uses the terms "year" and "milliYear" interchangeably
* when talking about values (indirectly) produced by this method. However
* as of SLAPI 2.0 latter naming scheme is the recommended one, older documentations
* might still use the former.
*
* @return The in-game year.
*/
public static int getGameYear() {
return impl.getGameYear();
}
/**
* Obtains the {@link GameImplementation} directly.
*
* @return The implementation of this class that is used right now
*/
public static @NotNull GameImplementation getImplementation() {
GameImplementation gameImpl = impl;
if (gameImpl == null) {
try {
Class> self = StarloaderAPIExtension.getInstance().getClass().getClassLoader().loadClass("de.geolykt.starloader.api.Galimulator");
Field f = self.getDeclaredField("impl");
f.setAccessible(true);
gameImpl = impl = (GameImplementation) f.get(null);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (gameImpl == null) {
throw new IllegalStateException("The implementation was not specified. This is a programmer error. Current classloader: " + Galimulator.class.getClassLoader());
}
}
}
return gameImpl;
}
/**
* Obtains the currently active map.
*
* @return The currently active map
* @deprecated Deprecated for removal without a replacement (the replacement is scheduled
* to be worked on in the future).
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
@NotNull
public static Map getMap() {
return impl.getMap();
}
/**
* Obtains the nearest Star that is near the given board coordinates and within the given
* search radius.
*
* @param boardX The X coordinate
* @param boardY The Y coordinate
* @param searchRadius The search radius
* @return The nearest star, or null if there is no star at the location.
* @since 2.0.0
* @deprecated Deprecated for removal; Replaced by {@link Dimension#getNearestStar(float, float, float)}.
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
@Nullable
@Contract(pure = true)
public static Star getNearestStar(float boardX, float boardY, float searchRadius) {
return impl.getNearestStar(boardX, boardY, searchRadius);
}
/**
* Convenience method to obtain the neutral empire. The neutral empire should
* NOT be ticked as it may create serious side effects within the ticking
* mechanism. Additionally merging or destroying the empire might have serious
* side effects, which is why that should be avoided.
*
* @return The {@link Empire} that is the neutral non-playable empire.
* @deprecated Deprecated for removal; Replaced by {@link Dimension#getNeutralEmpire()}.
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
@NotNull
@Contract(pure = true)
public static de.geolykt.starloader.api.empire.@NotNull ActiveEmpire getNeutralEmpire() {
return Galimulator.impl.getNeutralEmpire();
}
/**
* Obtains the currently active noise provider set by {@link #setNoiseProvider(NoiseProvider)}.
* Should there be no set noise provider, this method will return null but this can only occur if SLAPI
* was not initialized properly so this method is marked as NotNull anyways.
*
* @return The {@link NoiseProvider} that is currently exposed to API consumers.
* @since 2.0.0
*/
@SuppressWarnings("null")
@NotNull
public static NoiseProvider getNoiseProvider() {
return noiseImpl;
}
/**
* The current universe. In the SLAPI context, the universe is the sole
* active {@link Dimension}.
*
* This method returns null if no universe was registered.
* This is usually only the case if performing logic far too early (for
* example within the static initializer block of the mod entrypoint class)
*
* Universes are reset when reloading savegames.
*
* @return The {@link Dimension} which acts as the universe of the current board.
* @since 2.0.0-a20240519
*/
@Nullable
@AvailableSince("2.0.0-a20240519")
public static Dimension getNullableUniverse() {
return Galimulator.universe;
}
/**
* Obtains the empire the player is controlling. If there is no player or no
* empire in control of the player, then it returns null.
*
* @return The {@link Empire} owned by the player, or null
* @deprecated Deprecated for removal; Replaced by {@link Dimension#getPlayerEmpire()}.
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
@Nullable
@Contract(pure = true)
public static de.geolykt.starloader.api.empire.@Nullable ActiveEmpire getPlayerEmpire() {
return Galimulator.impl.getPlayerEmpire();
}
/**
* Obtains the closest-matching {@link SavegameFormat} instance that can decode a given
* input stream. This method does not guarantee that the returned format fully supports
* the given input stream - that can only be evaluated once the format actually loads the
* savegame in it's entirety. Instead, this method checks which format might be able to
* load the savegame based on headers and whatnot. This means that this method is likely
* to return null for savegames saved by vanilla galimulator.
*
* @param input The input stream from which to read the savegame from.
* @return The closest matching {@link SavegameFormat}, or null if unknown.
* @since 2.0.0
* @implNote Implementations of this method shouldn't throw an exception, exception being
* if the parameter is null.
*/
@Nullable
public static SavegameFormat getSavegameFormat(@NotNull InputStream input) {
return impl.getSavegameFormat(input);
}
/**
* Obtains the {@link SavegameFormat} instance that is implementing a certain {@link SupportedSavegameFormat}.
*
* @param format The format the should be implemented
* @return The {@link SavegameFormat} implementing the specified format
* @throws UnsupportedOperationException If for one reason or another the savegame format is not supported
* @since 2.0.0
*/
@NotNull
@Contract(pure = true)
public static SavegameFormat getSavegameFormat(@NotNull SupportedSavegameFormat format) {
return impl.getSavegameFormat(format);
}
/**
* Obtains all supported savegame formats.
*
* @return The {@link SavegameFormat formats} that are supported.
* @since 2.0.0
*/
@NotNull
@Contract(pure = true, value = "-> !null")
public static Iterable extends SavegameFormat> getSavegameFormats() {
return impl.getSavegameFormats();
}
/**
* Obtains the lock on the simulation loop.
*
* @return The simulation loop {@link TickLoopLock}.
* @since 2.0.0
*/
@NotNull
public static TickLoopLock getSimulationLoopLock() {
return impl.getSimulationLoopLock();
}
/**
* Obtains the currently active {@link SoundHandler}.
*
* @return The active {@link SoundHandler}.
*/
@NotNull
public static SoundHandler getSoundHandler() {
return impl.getSoundHandler();
}
/**
* Obtains the star that is at or next to the given coordinates.
* This is basically a {@link #getNearestStar(float, float, float)} call
* with the radius being set to a magic value.
*
* @param boardX The X coordinate
* @param boardY The Y coordinate
* @return The star at the given location, or null if there is none
* @since 2.0.0
* @deprecated Deprecated for removal; Replaced by {@link Dimension#getStarAt(float, float)}.
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
@Nullable
@Contract(pure = true)
public static Star getStarAt(float boardX, float boardY) {
return impl.getStarAt(boardX, boardY);
}
/**
* Obtains an immutable view of the underlying list of the internal vector
* of stars.
*
* @return An immutable {@link List} of {@link Star stars} that are known
* @since 2.0.0
* @see Unsafe#getStarsUnsafe()
* @deprecated Deprecated for removal; Replaced by {@link Dimension#getStarsView()}.
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
@NotNull
@Contract(pure = true, value = "-> new")
public static List<@NotNull Star> getStarList() {
return impl.getStarList();
}
/**
* Gets the currently registered Stars. Note that like many other methods in the
* API, this is NOT a clone of the backing collection, which means that any
* modifications done to the collections will happen in game. This behaviour is
* intended as it can be useful in many situations as well as being more
* performance friendly.
*
* @return A {@link Vector} of {@link Star stars} that are known
* @deprecated This method goes against the current design philosophy of this API, use {@link Unsafe#getStarsUnsafe()}
* or {@link Galimulator#getStarList()} instead.
*/
@SuppressWarnings("null")
@NotNull
@ScheduledForRemoval(inVersion = "3.0.0")
@DeprecatedSince("2.0.0")
@Deprecated
public static Vector<@NotNull Star> getStars() {
List This method throws an exception if no universe was registered.
* This is usually only the case if performing logic far too early (for
* example within the static initializer block of the mod entrypoint class)
*
* Universes are reset when reloading savegames.
*
* @return The {@link Dimension} which acts as the universe of the current board.
* @since 2.0.0-a20240519
*/
@NotNull
@AvailableSince("2.0.0-a20240519")
public static Dimension getUniverse() {
return Objects.requireNonNull(Galimulator.universe);
}
/**
* Obtains the weapons manager that is valid for this instance.
* It is more or less a series of helper methods.
*
* @return The weapons manager.
*/
@NotNull
public static WeaponsManager getWeaponsManager() {
return Galimulator.impl.getWeaponsManager();
}
/**
* Whether the sandbox mode has been used within this savegame.
*
* @return The sandbox used modifier
*/
public static boolean hasUsedSandbox() {
return impl.hasUsedSandbox();
}
/**
* Checks whether the game is currently paused.
* If the game is paused most ticking activities are halted, though mostly cosmetic ticking
* activities might still proceed as normal. An example for this is the background, which will move
* even when paused.
*
* @return The global pause state
*/
public static boolean isPaused() {
return impl.isPaused();
}
/**
* Loads the scenario that is currently in the clipboard.
*
* @since 2.0.0
*/
public static void loadClipboardScenario() {
impl.loadClipboardScenario();
}
/**
* Loads the state of the game from given input data.
* Additional warning: it is recommended to pause the game during the operation as otherwise
* it might corrupt the data
*
* @param data The input data
* @throws IOException If any IO issues occur at the underlying layers
*/
public static void loadGameState(byte[] data) throws IOException {
impl.loadGameState(data);
}
/**
* Loads the state of the game from a given input stream. All bytes may be read from the input stream,
* however the implementation may also want to try to guess what the end of the input stream is.
* Additional warning: it is recommended to pause the game during the operation as otherwise
* it might corrupt the data
*
* @param in The input stream to read the data from
* @throws IOException If any IO issues occur at the underlying layers
*/
public static void loadGameState(@NotNull InputStream in) throws IOException {
impl.loadGameState(in);
}
/**
* Loads a savegame on another thread while also blocking the main ticking loop.
* If loading fails, a new galaxy is generated using the stretched_spiral generator with a size of 300 stars.
*
* This is basically the method that is used by Galimulator to load savegames.
*
* @param savegameFile The savegame file to load. Relative to the current working directory, but can be absolute.
* @since 2.0.0
*/
@NonBlocking
public static void loadSavegameFile(@NotNull Path savegameFile) {
impl.loadSavegameFile(savegameFile);
}
/**
* Loads a savegame on another thread while also blocking the main ticking loop.
* If loading fails, a new galaxy is generated using the stretched_spiral generator with a size of 300 stars.
*
* This is basically the method that is used by Galimulator to load savegames.
*
* @param savegameFile The savegame file to load. Relative to the current working directory, but can be absolute.
* @since 2.0.0
*/
@NonBlocking
public static void loadSavegameFile(@NotNull String savegameFile) {
impl.loadSavegameFile(savegameFile);
}
/**
* Returns the {@link Empire} mapped to the given unique ID. If however
* there is no matching empire, the neutral empire is to be returned. Default
* implementation notice: The implementation of this method is very inefficient
* as it iterates over all known empires at worst. It is advisable that the extensions
* make use of caching.
*
* @param uid The UID of the empire, as defined by {@link Empire#getUID()}.
* @return The {@link Empire} bound to the unique ID, or {@link Dimension#getNeutralEmpire() neutral} as a fallback.
* @since 2.0.0
*/
@NotNull
public static Empire lookupEmpire(int uid) {
return Galimulator.impl.lookupEmpire(uid);
}
/**
* Obtains the {@link Star} instance that is associated by the given ID.
* If there is no star with the given ID an {@link IllegalArgumentException}
* is thrown.
*
* @param id The ID of the star
* @return The instance of the Star associated to the ID.
* @throws IllegalArgumentException If there is no star with the given ID
* @since 2.0.0
* @see Star#getUID()
* @implNote The implementation of this method is O(1), so unlike most other
* lookup methods, it is perfectly safe performance-wise to run this method
* repeatedly in an uncached manner.
*/
@NotNull
public static Star lookupStar(int id) {
return Galimulator.impl.lookupStar(id);
}
/**
* "Panic" (also frequently known as "crashing") the game, causing it to show
* a panic message alongside a stacktrace of the caller.
*
* For all intents and purposes, a panic is irrevocable - that is it
* cannot be undone. As such, this method should always be used as a last resort
* when other methods of handling an error state cannot work or would lead to
* a less graceful application state (e.g. "freeze", "hang" or "deadlock").
*
* Vanilla galimulator (as of release 5.0.2 as of May 2024) only automatically
* handles error conditions in a way similar to this one for crashes in the main
* simulation loop and some crashes in the rendering logic. Especially when spawning
* additional threads or when otherwise working with asynchronous tasks then the
* likelihood of error handling being almost nonexistent is high so calling
* {@link Galimulator#panic(String, boolean)} in these cases is appropriate as it
* improves the ability to efficiently debug the game.
*
* {@link Galimulator#panic(String, boolean)} displays and logs the error
* message using an appropriate {@link Logger}, resulting in the crash message
* to be searchable in log files. Further, the list of used mods will be displayed,
* further improving the ability of finding the root cause of mod incompatibility
* issues. Other methods of handling fatal error states are likely to not include these
* features and thus can be harder to trace down (especially the logging aspect is
* important as users who may report errors might not always have the console on,
* while SLL always does logging in the background for as long as {@link Logger} instances
* are being used).
*
* DO NOT USE this method to handle non-fatal errors. More often
* than not one can simply use {@link Logger#error(String)} or {@link Logger#warn(String)}
* to log an error and proceed.
*
* This method is not blocking, that is it will exit and won't throw an exception while
* doing so. Having some way of aborting execution if this method is invokes may as such
* be preferable. That being said, this method will try to block and pause the simulation
* loop if possible in order to mitigate collateral damage. However, it is still possible
* that some logic continues to run in the background after executing this method. This is
* a limitation with how errors can be handled in java. As a nuclear option
* {@link System#exit(int)} can be used, but if {@link System#exit(int)} is to be
* used, then it's usage must be logged (even if nothing enforces it, but it's
* otherwise hard to debug system exits).
*
* For some things such as during {@link ApplicationStartEvent} or
* {@link ApplicationStartedEvent}, this method will automatically be called in case of an
* error. Explicitly calling this method is advised against, instead an appropriate exception
* should be thrown within the event handler.
*
* @param message The crash message to display.
* @param save Whether to save the game's state to disk before crashing.
* @see Logger#error(String)
* @since 2.0.0-a20240513
*/
@AvailableSince("2.0.0-a20240513")
public static void panic(@NotNull String message, boolean save) {
Galimulator.impl.panic(message, save);
}
/**
* "Panic" (also frequently known as "crashing") the game, causing it to show
* a panic message alongside a the stacktrace of the provided {@link Throwable}.
*
* For all intents and purposes, a panic is irrevocable - that is it
* cannot be undone. As such, this method should always be used as a last resort
* when other methods of handling an error state cannot work or would lead to
* a less graceful application state (e.g. "freeze", "hang" or "deadlock").
*
* Vanilla galimulator (as of release 5.0.2 as of May 2024) only automatically
* handles error conditions in a way similar to this one for crashes in the main
* simulation loop and some crashes in the rendering logic. Especially when spawning
* additional threads or when otherwise working with asynchronous tasks then the
* likelihood of error handling being almost nonexistent is high so calling
* {@link Galimulator#panic(String, boolean, Throwable)} in these cases is appropriate
* as it improves the ability to efficiently debug the game.
*
* {@link Galimulator#panic(String, boolean, Throwable)} displays and logs the error
* message using an appropriate {@link Logger}, resulting in the crash message
* to be searchable in log files. Further, the list of used mods will be displayed,
* further improving the ability of finding the root cause of mod incompatibility
* issues. Other methods of handling fatal error states are likely to not include these
* features and thus can be harder to trace down (especially the logging aspect is
* important as users who may report errors might not always have the console on,
* while SLL always does logging in the background for as long as {@link Logger} instances
* are being used).
*
* DO NOT USE this method to handle non-fatal errors. More often
* than not one can simply use {@link Logger#error(String)} or {@link Logger#warn(String)}
* to log an error and proceed.
*
* This method is not blocking, that is it will exit and won't throw an exception while
* doing so. Having some way of aborting execution if this method is invokes may as such
* be preferable. That being said, this method will try to block and pause the simulation
* loop if possible in order to mitigate collateral damage. However, it is still possible
* that some logic continues to run in the background after executing this method. This is
* a limitation with how errors can be handled in java. As a nuclear option
* {@link System#exit(int)} can be used, but if {@link System#exit(int)} is to be
* used, then it's usage must be logged (even if nothing enforces it, but it's
* otherwise hard to debug system exits).
*
* For some things such as during {@link ApplicationStartEvent} or
* {@link ApplicationStartedEvent}, this method will automatically be called in case of an
* error. Explicitly calling this method is advised against, instead an appropriate exception
* should be thrown within the event handler.
*
* @param message The crash message to display.
* @param save Whether to save the game's state to disk before crashing.
* @param cause A {@link Throwable} that caused the panic state; used to define the displayed stacktrace.
* @see Logger#error(String, Throwable)
* @see Throwable#printStackTrace()
* @since 2.0.0-a20240513
*/
@AvailableSince("2.0.0-a20240513")
public static void panic(@NotNull String message, boolean save, @NotNull Throwable cause) {
Galimulator.impl.panic(message, save, cause);
}
/**
* Pauses the game. This only pauses the logical components of the application and will not impact the graphical components.
* It will also not cause the loading screen to show up.
*
* @see #resumeGame()
* @see #setPaused(boolean)
* @implNote Under galimulator 4.X this operation was not thread safe and would cause a crash if called on another thread.
* If such compatibility is needed, this method should be called on the next tick via {@link Application#postRunnable(Runnable)}.
* Under galimulator 5.X, this problem does not occur. The sibling method {@link #resumeGame()} is unaffected.
*
* @deprecated While the javadocs of this method state that no impact on graphical components exist, this is not the case
* as this method causes the "step" widget to show. Calling this method while already paused has undocumented behaviour
* due to that. {@link #setPaused(boolean)} has this behaviour documented more cleanly.
*/
@Obsolete(since = "2.0.0-a20240105")
public static void pauseGame() {
Galimulator.impl.pauseGame();
}
/**
* Convenience method to calculate the voronoi graphs (the fancy section-outline
* around the stars) for all stars. This is helpful if a star got moved, however
* a call to this method does not recalculate starlanes, which is also needed
* alongside this method in some cases.
*/
public static void recalculateVoronoiGraphs() {
impl.recalculateVoronoiGraphs();
}
/**
* Registers a {@link MouseInputListener}.
* The priority of a {@link MouseInputListener} registered through this method is defined by the point of time
* of this method call compared to the time of registration of the other listeners.
*
* Registration can happen at any time, including during the extension loading phase (provided SLAPI
* is already on the classpath and initialized).
*
* @param listener The listener to register
* @since 2.0.0
*/
public static void registerMouseInputListener(@NotNull MouseInputListener listener) {
impl.registerMouseInputListener(listener);
}
/**
* Resumes the game. This method basically reverts {@link #pauseGame()}
*
* This method is obsolete, using {@link #setPaused(boolean)} with the argument of false is recommended
* as an alternative. However this method is not acutely a candidate for deprecation.
*/
@Obsolete(since = "2.0.0-20240105")
public static void resumeGame() {
impl.resumeGame();
}
/**
* Schedule a task that should run on the next frame. More concretely, it should run before the next frame is drawn, however
* other tasks may run before that. If two tasks are scheduled at the same frame, the task that is scheduled first should run
* before. All scheduled tasks need to be run on the main graphical/LWJGL thread; The simulation loop lock (as per
* {@link #getSimulationLoopLock()}) is not acquired.
*
* This methods schedules task to be run on the next graphical frame,
* not on the next simulation tick. For that, use {@link #runTaskOnNextTick(Runnable)} instead.
*
* Calling this method is equivalent to using {@link Application#postRunnable(Runnable)}.
*
* @param task The task to run on the next tick
* @since 2.0.0
*/
public static void runTaskOnNextFrame(Runnable task) {
impl.runTaskOnNextFrame(task);
}
/**
* Run a given action on the next simulation tick.
* The simulation loop lock (see {@link #getSimulationLoopLock()}) will not be acquired at that point of time.
*
* To schedule a task on the next frame, use {@link Application#postRunnable(Runnable)} instead.
*
* This method is explicitly safe to execute outside the main thread.
*
* Warning: This method is pause-insensitive - that is it can also be called while the game is in slow-motion
* or paused. As such, the task WILL be called before any {@link LogicalTickEvent} handler but is roughly equivalent
* to {@link Phase#PRE_GRAPHICAL}.
*
* @param runnable The task to execute
* @since 2.0.0
*/
public static void runTaskOnNextTick(@NotNull Runnable runnable) {
Galimulator.impl.runTaskOnNextTick(runnable);
}
/**
* Saves a file inside the data folder.
* The file will NOT be overriden if it already exists.
*
* @param name The name of the file, the `data/` part must be ommited
* @param data The data of the file.
*/
public static void saveFile(@NotNull String name, byte[] data) {
Galimulator.impl.saveFile(name, data);
}
/**
* Saves a file inside the data folder.
* The file will NOT be overriden if it already exists. The input data stream will be exhausted by this operation,
* however it will NOT be closed
*
* @param name The name of the file, the `data/` part must be ommited
* @param data The data of the file.
*/
public static void saveFile(@NotNull String name, @NotNull InputStream data) {
Galimulator.impl.saveFile(name, data);
}
/**
* Changes the currently active map mode to a new value.
*
* @param mode The new active map mode.
* @see #getActiveMapmode()
*/
public static void setActiveMapmode(@NotNull MapMode mode) {
Galimulator.impl.setActiveMapmode(Objects.requireNonNull(mode, "The map mode cannot be set to a null value"));
}
/**
* Sets the currently active background task.
* A background task is a task that is being performed if the main thread is unable to
* acquire any ticking lock (as per {@link TickLoopLock#acquireSoftControl()}), usually
* caused by another thread having acquired "hard" control of the tick loop lock
* (as per {@link TickLoopLock#acquireHardControl()}. This scenario causes the
* loading screen to show up.
*
* This method is safe to invoke on any thread, but should generally
* be invoked by the thread holding the "hard" tick loop lock control,
* or a child thread of said thread.
*
* The {@link BackgroundTask} API supersedes the short-lived
* {@link #setBackgroundTaskProgress(String)} API. While {@link #setBackgroundTaskProgress(String)}
* reused galimulator APIs, the {@link BackgroundTask} API inserts itself into the
* loading menu rendering pipeline. This change was performed as it was discovered
* that {@link #setBackgroundTaskProgress(String)} would be called very frequently,
* meaning that it would be better for performance to offload string concatenation
* used to call the method to the main rendering thread. This is achieved by making
* {@link BackgroundTask#getProgressDescription()} a method that lazily obtains the
* necessary string.
*
* @param task The new {@link BackgroundTask} instance which should be used from now on.
* @since 2.0.0-a20240831
*/
@NonBlocking
@ApiStatus.AvailableSince("2.0.0-a20240831")
@Contract(pure = false)
public static void setBackgroundTask(@NotNull BackgroundTask task) {
Galimulator.impl.setBackgroundTask(task);
}
/**
* Sets the progress of the currently performed background task.
* A background task is a task that is being performed if the main thread is unable to
* acquire any ticking lock (as per {@link TickLoopLock#acquireSoftControl()}), usually
* caused by another thread having acquired "hard" control of the tick loop lock
* (as per {@link TickLoopLock#acquireHardControl()}. This scenario causes the
* loading screen to show up.
*
* This progress string is displayed on the aforementioned loading screen
* after the "main" background task description. This method is supposed to be
* used for features such as galaxy generation (e.g. within {@link StarlaneGenerator})
* or galaxy loading, or any other feature that may induce a loading screen due
* to the semantics mentioned in the previous paragraph.
*
* This method is safe to invoke on any thread, but should generally
* be invoked by the thread holding the "hard" tick loop lock control,
* or a child thread of said thread.
*
* In vanilla galimulator this method is solely used by the landmark generation,
* and even then only in a flawed capacity that means that it isn't being incremented
* correctly. The string used here follows the following format:
* This method can be safely called on a different thread.
*
* When pausing, that is migrating from an unpaused state to a paused one,
* the "step" widget will be shown.
*
* @param paused True to pause, false to resume
* @since 2.0.0-a20240105
*/
@NonBlocking
@ApiStatus.AvailableSince("2.0.0-a20240105")
public static void setPaused(boolean paused) {
Galimulator.impl.setPaused(paused);
}
/**
* Sets the amount of empires that have transcended in during the game.
*
* @param count The amount of transcended empires
*/
public static void setTranscendedEmpires(int count) {
Galimulator.impl.setTranscendedEmpires(count);
}
/**
* The current universe. In the SLAPI context, the universe is the sole
* active {@link Dimension}.
*
* This method returns null if no universe was registered.
* This is usually only the case if performing logic far too early (for
* example within the static initializer block of the mod entrypoint class)
*
* Universes are reset when reloading savegames.
*
* @param universe The {@link Dimension} which acts as the new universe of
* the current board.
* @since 2.0.0-a20240519
*/
@AvailableSince("2.0.0-a20240519")
public static void setUniverse(@Nullable Dimension universe) {
Galimulator.universe = universe;
}
/**
* Sets whether the sandbox mode has been used within this savegame.
*
* @param state The sandbox used modifier
*/
public static void setUsedSandbox(boolean state) {
Galimulator.impl.setUsedSandbox(state);
}
/**
* Displays the widgets used for the galaxy creation process to the user.
*
* @since 2.0.0
*/
public static void showGalaxyCreationScreen() {
Galimulator.impl.showGalaxyCreationScreen();
}
/**
* Shows the online scenario browser.
*
* @since 2.0.0
*/
public static void showOnlineScenarioBrowser() {
Galimulator.impl.showOnlineScenarioBrowser();
}
/**
* Displays the widget responsible for allowing the user to upload their maps or ship mods
* to the steam workshop.
*
* @since 2.0.0
*/
public static void showModUploadScreen() {
Galimulator.impl.showModUploadScreen();
}
/**
* Shows the editor that allows one to upload a scenario to the Internet
* and to edit the scenario metadata of the scenario saved within the given map.
*
* @param map The map where the scenario to edit lies within
* @since 2.0.0
* @deprecated Deprecated for removal without a replacement (the replacement is scheduled
* to be worked on in the future).
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
public static void showScenarioMetadataEditor(de.geolykt.starloader.api.@NotNull Map map) {
Galimulator.impl.showScenarioMetadataEditor(map);
}
/**
* Displays the widget responsible for saving the currently active savegame as a scenario
* to the user.
*
* @since 2.0.0
*/
public static void showScenarioSaveScreen() {
Galimulator.impl.showScenarioSaveScreen();
}
/**
* Constructor that should not be called because there is no need to have an instance of this class.
*/
private Galimulator() { }
static {
if (!(Galimulator.class.getClassLoader() instanceof MinestomExtensionClassLoader)) {
LoggerFactory.getLogger(Galimulator.class).error("Class loaded by improper classloader: {}", Galimulator.class.getClassLoader());
}
}
}
* Right now a Vector is required by the Galimulator class due to poor foresight.
* This will be resolved in the 2.0.0 release
*
* @return A {@link List} of {@link Empire empires} that are known
* @since 1.2.0
* @deprecated This method returns a the internal empire list as-is without making it immutable
* or cloning it. However, this approach is deemed improper API design by modern standards.
* Use {@link #getEmpiresView()} instead, or if using the internal empire list is absolutely necessary
* use {@link Unsafe#getEmpiresUnsafe()}.
*/
@NotNull
@Deprecated
@DeprecatedSince("2.0.0")
@ScheduledForRemoval(inVersion = "3.0.0")
public List
* Right now a Vector is required by the Galimulator class due to poor foresight.
* This will be resolved in the 2.0.0 release
*
* @return A {@link List} of {@link Star stars} that are known
* @deprecated This method goes against the current design philosophy of this API, use {@link Unsafe#getStarsUnsafe()}
* or {@link Galimulator#getStarList()} instead.
*/
@NotNull
@ScheduledForRemoval(inVersion = "3.0.0")
@DeprecatedSince("2.0.0")
@Deprecated
public List<@NotNull Star> getStars();
/**
* Obtains the {@link StateActorSpawnPredicate state actor spawning predicates} used globally
* for all empires.
*
* @return A view of the spawning requirements
* @since 2.0.0
*/
@Contract(pure = true)
@NotNull
public SpawnPredicatesContainer getStateActorSpawningPredicates();
/**
* Obtains the amount of empires that have transcended in during the game.
*
* @return The amount of transcended empires
*/
public int getTranscendedEmpires();
/**
* Obtains the unsafe that is valid for this implementation.
* Note that this unsafe class is not fully supported.
*
* @return The unsafe instance
* @deprecated The resulting unsafe object is unsafe and it's usage not recommend
*/
@DeprecatedSince("1.5.0")
@Deprecated
public Galimulator.@NotNull Unsafe getUnsafe();
/**
* Obtains the weapons manager that is valid for this instance.
* It is more or less a series of helper methods.
*
* @return The weapons manager.
*/
public @NotNull WeaponsManager getWeaponsManager();
/**
* Whether the sandbox mode has been used within this savegame.
*
* @return The sandbox used modifier
*/
public boolean hasUsedSandbox();
/**
* Checks whether the game is currently paused.
* If the game is paused most ticking activities are halted, though mostly cosmetic ticking
* activities might still proceed as normal. An example for this is the background, which will move
* even when paused.
*
* @return The global pause state
*/
public boolean isPaused();
/**
* Loads the scenario that is currently in the clipboard.
*
* @since 2.0.0
*/
public void loadClipboardScenario();
/**
* Loads the state of the game from given input data.
* Additional warning: it is recommended to pause the game during the operation as otherwise
* it might corrupt the data
*
* @param data The input data
* @throws IOException If any IO issues occur at the underlying layers
*/
public void loadGameState(byte[] data) throws IOException;
/**
* Loads the state of the game from a given input stream. All bytes may be read from the input stream,
* however the implementation may also want to try to guess what the end of the input stream is.
* Additional warning: it is recommended to pause the game during the operation as otherwise
* it might corrupt the data
*
* @param in The input stream to read the data from
* @throws IOException If any IO issues occur at the underlying layers
*/
public void loadGameState(@NotNull InputStream in) throws IOException;
/**
* Loads a savegame on another thread while also blocking the main ticking loop.
* If loading fails, a new galaxy is generated using the stretched_spiral generator with a size of
* 300 stars.
*
* completedLandmarkCount / allLandmarkCount.
*
* @param progressDescription The description of the progress currently ongoing
* in the currently performed task running in the background (which is blocking
* the main rendering thread). Or null, to clear/unset the progress.
* @since 2.0.0-a20240828
* @see #setBackgroundTask(BackgroundTask)
* @deprecated Replaced by {@link BackgroundTask} and it's associated APIs.
*/
@NonBlocking
@ApiStatus.AvailableSince("2.0.0-a20240828")
@Deprecated
@DeprecatedSince("2.0.0-a20240831")
@ScheduledForRemoval(inVersion = "3.0.0")
public void setBackgroundTaskProgress(@Nullable String progressDescription);
/**
* Set the year in-game. The year is rarely a negative number and should not get
* lower later in game unless a new galaxy is spun up. 1000 in-game years span
* an in-game millenia, which is the time format most players are familiar with
* in the game. However please note that this is not always corresponding in years,
* sometimes it is also in milliyears or other time formats.
*
* @param year The in-game year.
*/
public void setGameYear(int year);
/**
* Sets the currently active map.
*
* @param map The currently active map
* @deprecated Deprecated for removal without a replacement (the replacement is scheduled
* to be worked on in the future).
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
public void setMap(@NotNull Map map);
/**
* Pause or resume the game, depending on the state of the boolean.
* If already paused or resumed, do nothing.
*
* completedLandmarkCount / allLandmarkCount.
*
* @param progressDescription The description of the progress currently ongoing
* in the currently performed task running in the background (which is blocking
* the main rendering thread). Or null, to clear/unset the progress.
* @since 2.0.0-a20240828
* @deprecated Replaced by {@link BackgroundTask} and it's associated APIs.
*/
@NonBlocking
@ApiStatus.AvailableSince("2.0.0-a20240828")
@Deprecated
@DeprecatedSince("2.0.0-a20240831")
@ScheduledForRemoval(inVersion = "3.0.0")
public static void setBackgroundTaskProgress(@Nullable String progressDescription) {
Galimulator.impl.setBackgroundTaskProgress(progressDescription);
}
/**
* Sets the {@link GameConfiguration} directly.
* It is unlikely that anyone would need to use this method except the API implementation itself.
*
* @param config The implementation that should be used in the future
*/
public static void setConfiguration(@NotNull GameConfiguration config) {
Galimulator.config = Objects.requireNonNull(config, "config may not be null");
}
/**
* Get the year in-game. The year is rarely a negative number and should not get
* lower later in game unless a new galaxy is spun up. 1000 in-game years span
* an in-game millenia, which is the time format most players are familiar with
* in the game. However please note that this is not always corresponding in years,
* sometimes it is also in milliyears or other time formats.
*
* @param year The in-game year.
*/
public static void setGameYear(int year) {
impl.setGameYear(year);
}
/**
* Sets the {@link GameImplementation} directly.
* It is unlikely that anyone would need to use this method except the API implementation itself.
*
* @param implementation The implementation that should be used in the future
*/
public static void setImplementation(@NotNull GameImplementation implementation) {
Galimulator.impl = Objects.requireNonNull(implementation, "implementation may not be null.");
}
/**
* Sets the currently active map.
*
* @param map The currently active map
* @deprecated Deprecated for removal without a replacement (the replacement is scheduled
* to be worked on in the future).
*/
@Deprecated
@DeprecatedSince("2.0.0-a20240519")
@ScheduledForRemoval(inVersion = "3.0.0")
public static void setMap(@NotNull Map map) {
Galimulator.impl.setMap(map);
}
/**
* Sets the {@link NoiseProvider} that should be used by the SLAPI and be supplied to consumers of the API.
*
* @param provider The provider to make use of
* @since 2.0.0
*/
public static void setNoiseProvider(@NotNull NoiseProvider provider) {
Galimulator.noiseImpl = Objects.requireNonNull(provider, "provider may not be null");
}
/**
* Pause or resume the game, depending on the state of the boolean.
* If already paused or resumed, do nothing.
*
*