package de.geolykt.starloader.impl;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import java.util.Objects;
import java.util.Vector;
import java.util.concurrent.ConcurrentLinkedDeque;
import org.jetbrains.annotations.ApiStatus.AvailableSince;
import org.jetbrains.annotations.ApiStatus.Internal;
import org.jetbrains.annotations.ApiStatus.ScheduledForRemoval;
import org.jetbrains.annotations.Contract;
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.Gdx;
import com.badlogic.gdx.graphics.GL20;
import de.geolykt.starloader.DeprecatedSince;
import de.geolykt.starloader.ExpectedObfuscatedValueException;
import de.geolykt.starloader.Starloader;
import de.geolykt.starloader.api.Galimulator;
import de.geolykt.starloader.api.actor.Actor;
import de.geolykt.starloader.api.actor.SpawnPredicatesContainer;
import de.geolykt.starloader.api.actor.WeaponsManager;
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.War;
import de.geolykt.starloader.api.empire.people.DynastyMember;
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.resource.DataFolderProvider;
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.RandomNameType;
import de.geolykt.starloader.api.utils.TickLoopLock;
import de.geolykt.starloader.impl.actors.GlobalSpawningPredicatesContainer;
import de.geolykt.starloader.impl.asm.SpaceASMTransformer;
import de.geolykt.starloader.impl.gui.ForwardingListener;
import de.geolykt.starloader.impl.gui.GLScissorState;
import de.geolykt.starloader.impl.gui.VanillaBackgroundTask;
import de.geolykt.starloader.impl.serial.BoilerplateSavegameFormat;
import de.geolykt.starloader.impl.serial.VanillaSavegameFormat;
import de.geolykt.starloader.mod.Extension;
import snoddasmannen.galimulator.Galemulator;
import snoddasmannen.galimulator.MapData;
import snoddasmannen.galimulator.MapMode.MapModes;
import snoddasmannen.galimulator.Player;
import snoddasmannen.galimulator.ProceduralStarGenerator;
import snoddasmannen.galimulator.Scenario;
import snoddasmannen.galimulator.Space;
import snoddasmannen.galimulator.SpaceState;
import snoddasmannen.galimulator.VanityHolder;
import snoddasmannen.galimulator.ui.ModUploadWidget;
import snoddasmannen.galimulator.ui.OptionChooserWidget;
import snoddasmannen.namegenerator.NameGenerator;
// TODO split the unsafe impl and the game impl
public class GalimulatorImplementation implements Galimulator.GameImplementation, Galimulator.Unsafe {
/**
* The logger that is used within this class.
*/
protected static final Logger LOGGER = LoggerFactory.getLogger(GalimulatorImplementation.class);
/**
* A small hack {@link Runnable} that serves as a marker to represent a tick barrier within {@link #SCHEDULED_TASKS_NEXT_TICK}.
* That is all tasks before that barrier belong to the current tick, where as all tasks after the barrier belong to the next tick.
* This behaviour is required in order for calls to {@link #runTaskOnNextTick(Runnable)} work as intended within
* a {@link #runTaskOnNextTick(Runnable)} task.
*
*
Executing this runnable does nothing, although this is an implementation detail.
*
* @since 2.0.0
* @see #fireScheduledTasks()
*/
@NotNull
private static final Runnable NEXT_TICK_TASK = () -> {};
/**
* A {@link ThreadLocal} variable that stores whether the current thread is the main thread.
*
* @since 2.0.0
* @see #isRenderThread()
*/
private static final ThreadLocal RENDER_THREAD = ThreadLocal.withInitial(() -> {
String name = Thread.currentThread().getName();
return name.equals("main") || name.contains("LWJGL Application") || name.contains("GLThread");
});
@NotNull
private static final List SAVEGAME_FORMATS = new ArrayList<>(Arrays.asList(VanillaSavegameFormat.INSTANCE, BoilerplateSavegameFormat.INSTANCE));
@NotNull
private static final Deque<@NotNull Runnable> SCHEDULED_TASKS_NEXT_TICK = new ConcurrentLinkedDeque<>();
@NotNull
@AvailableSince("2.0.0-a20240831")
@Internal
private volatile BackgroundTask currentTask = new VanillaBackgroundTask();
@NotNull
private final SpawnPredicatesContainer globalSpawningPredicates = new GlobalSpawningPredicatesContainer();
/**
* A list of all currently registered {@link MouseInputListener MouseInputListeners}. This list is only here
* to allow the registration of listeners at an arbitrary time and is synced to the internal list
* used by the {@link ForwardingListener} that actually calls the methods on the listeners.
*
* As usual with anything in the impl package, this field is not official API.
* The fact that this is documented does not change that.
*
* @since 2.0.0
*/
public final List listeners = new ArrayList<>();
/**
* Renders a crash report to the screen and log. This action cannot be undone.
*
* Warning: This is not public API. Use {@link Galimulator#panic(String, boolean)}
* instead.
*
* @param cause The description of the cause of the issue.
* @param save True if the current game state should be written to disk
* @since 2.0.0
*/
public static void crash(@NotNull String cause, boolean save) {
@NotNull
Throwable backtrace = new AssertionError("GalimulatorImplementation.crash() called: " + cause).fillInStackTrace();
GalimulatorImplementation.crash(backtrace, cause, save);
}
/**
* Renders a crash report to the screen and log. This action cannot be undone.
*
*
Warning: This is not public API. Use {@link Galimulator#panic(String, boolean, Throwable)}
* instead.
*
* @param e The stacktrace that should be displayed. Stacktraces are powerful tools to debug issues
* @param cause The description of the cause of the issue.
* @param save True if the current game state should be written to disk
* @since 2.0.0
*/
public static void crash(@NotNull Throwable e, @NotNull String cause, boolean save) {
try {
if (!GalimulatorImplementation.isRenderThread()) {
Galimulator.setPaused(true);
} else {
Galimulator.setPaused(true); // Pause the game on crash so the simulation loop doesn't continue to run in the background.
Gdx.gl.glDisable(GL20.GL_SCISSOR_TEST); // Sometimes the game can crash while rendering, at which point a scissor might be applied. To render the entire crash message we might need to disable the scissor though.
GLScissorState.glScissor(0, 0, Gdx.graphics.getBackBufferWidth(), Gdx.graphics.getBackBufferHeight());
GLScissorState.forgetScissor();
}
} catch (Throwable ignored) {
}
Galemulator listener = (Galemulator) Gdx.app.getApplicationListener();
if (save) {
// TODO deobf
listener.h = "Game crashed! Saving what still can be saved... Please wait";
Thread thread = new Thread(() -> {
boolean threadDied = false;
try (FileOutputStream fos = new FileOutputStream(new File("crash-save.dat"))) {
Galimulator.getSavegameFormat(SupportedSavegameFormat.SLAPI_BOILERPLATE).saveGameState(fos, "Game crashed", "crash-save.dat", false);
} catch (Throwable t) {
if (t instanceof ThreadDeath) {
t.addSuppressed(e);
t.printStackTrace();
threadDied = true;
throw (ThreadDeath) t;
}
t.printStackTrace();
} finally {
if (!threadDied) {
GalimulatorImplementation.crash(e, cause, false);
}
}
}, "crash-saving-thread");
thread.start();
} else {
StringBuilder builder = new StringBuilder();
builder.append("This game is modded, report this crash report to the respective mod devs first, not snoddasmannen directly.\n\n");
builder.append("The crash report has also been printed to the log, give the FULL logs to the mod devs, not a screenshot of this.\n");
builder.append("Cause (for beginners): " + cause + "\n");
builder.append("Installed mods:\n");
for (Extension ext : Starloader.getExtensionManager().getExtensions()) {
builder.append(" " + ext.getDescription().getName() + " v" + ext.getDescription().getVersion() + "\n");
}
try {
Class.forName("com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application");
builder.append("\n[RED]Alert: LWJGL 3 detected.[][LIME]\n");
} catch (ClassNotFoundException ignored) {
}
builder.append("\nStacktrace:\n");
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
sw.flush();
builder.append(sw.getBuffer().toString().replace("\t", " "));
listener.h = "[LIME]" + builder.toString();
for (String s : builder.toString().split("\n")) {
LoggerFactory.getLogger("CrashReporter").error(s);
}
try {
Galimulator.getSimulationLoopLock().acquireSoftControl();
Galimulator.getSimulationLoopLock().acquireHardControl();
} catch (InterruptedException interrupt) {
}
}
}
/**
* Execute all tasks that have been scheduled up to this point. All tasks that are scheduled while this method is called are
* delegated to the next time this method is called.
*
*
As usual with anything in the impl package, this method is not official API.
* The fact that this is documented does not change that. This method is solely intended to be called from
* {@link SpaceASMTransformer#logicalTickEarly()}
*
* @since 2.0.0
*/
public static void fireScheduledTasks() {
GalimulatorImplementation.SCHEDULED_TASKS_NEXT_TICK.addLast(GalimulatorImplementation.NEXT_TICK_TASK);
Runnable r;
while ((r = GalimulatorImplementation.SCHEDULED_TASKS_NEXT_TICK.removeFirst()) != GalimulatorImplementation.NEXT_TICK_TASK) {
r.run();
}
}
/**
* Obtains whether the current thread is the main thread.
* This is based on a ThreadLocal populated based on the Thread's name.
*
* @return True if the current thread is capable of rendering.
* @since 2.0.0
*/
public static boolean isRenderThread() {
return GalimulatorImplementation.RENDER_THREAD.get();
}
/**
* Converts a Galimulator map mode into a starloader API map mode.
* This is a clean cast and should never throw exception, except if there is an issue unrelated to this method.
*
* @param mode The map mode to convert
* @return The converted map mode
*/
@NotNull
private static MapMode toSLMode(@NotNull MapModes mode) {
return (MapMode) mode;
}
@Override
public void connectStars(@NotNull Star starA, @NotNull Star starB) {
Galimulator.getUniverse().connectStars(starA, starB);
}
@Override
public void disconnectStars(@NotNull Star starA, @NotNull Star starB) {
Galimulator.getUniverse().disconnectStars(starA, starB);
}
@SuppressWarnings({ "null", "unused" })
@Override
@NotNull
public String generateRandomName(@NotNull RandomNameType type) {
switch (type) {
case ADJECTIVE:
return NameGenerator.getRandomAdjective();
case FACTION_NAME:
return NameGenerator.generateRandomFactionName();
case IDENTIFIER:
return NameGenerator.generateRandomIdentifier();
case QUEST_NAME:
return NameGenerator.generateRandomQuestName();
case QUEST_NOMINATOR:
return NameGenerator.getRandomQuestNominator();
case REVOLT_NAME:
return NameGenerator.getRandomRevoltName();
case SHIP_NAME:
return NameGenerator.generateRandomShipName();
case VANITY_NAME:
return NameGenerator.getRandomVanityName();
default:
if (Objects.isNull(type)) {
throw new NullPointerException("type may not be null");
}
throw new IllegalStateException("Unknown enum value: " + type.name());
}
}
@SuppressWarnings("null")
@Override
public @NotNull MapMode getActiveMapmode() {
return toSLMode(snoddasmannen.galimulator.MapMode.getCurrentMode());
}
@SuppressWarnings("rawtypes")
@Override
public Vector getActorsUnsafe() {
return Objects.requireNonNull((Vector) Space.actors);
}
@Override
@SuppressWarnings("rawtypes")
public Vector getAlliancesUnsafe() {
return Objects.requireNonNull((Vector) Space.alliances);
}
@Override
@SuppressWarnings("rawtypes")
public Vector> getArtifactsUnsafe() {
return Objects.requireNonNull((Vector) Space.artifacts);
}
@Override
@NotNull
public BackgroundTask getBackgroundTask() {
return this.currentTask;
}
@Override
@SuppressWarnings("rawtypes")
public Vector> getCooperationsUnsafe() {
return Objects.requireNonNull((Vector) Space.corporations);
}
@Override
public Vector getDisruptedStarsUnsafe() {
return Objects.requireNonNull(Space.disruptedStars);
}
@Override
@Deprecated
@Nullable
public de.geolykt.starloader.api.empire.@Nullable ActiveEmpire getEmpireByUID(int uid) {
return (de.geolykt.starloader.api.empire.ActiveEmpire) Space.e(uid);
}
@SuppressWarnings("all")
@Override
@Deprecated
@DeprecatedSince("2.0.0")
@ScheduledForRemoval(inVersion = "3.0.0")
public @NotNull List getEmpires() {
return (Vector) (Vector>) this.getEmpiresUnsafe();
}
@SuppressWarnings("rawtypes")
@Override
public Vector getEmpiresUnsafe() {
return Objects.requireNonNull((Vector) Space.empires);
}
@SuppressWarnings({ "unchecked", "rawtypes", "null" })
@Override
@NotNull
public Vector getFollowedPeopleUnsafe() {
return (Vector) Space.v;
}
@Override
public int getGameYear() {
return Space.getMilliYear();
}
@SuppressWarnings("null")
@Override
@Deprecated
public @NotNull de.geolykt.starloader.api.@NotNull Map getMap() {
return (de.geolykt.starloader.api.Map) Space.getMapData();
}
@Override
@Nullable
@Contract(pure = true)
public Star getNearestStar(float boardX, float boardY, float searchRadius) {
return Galimulator.getUniverse().getNearestStar(boardX, boardY, searchRadius);
}
@Override
@NotNull
@Deprecated
public de.geolykt.starloader.api.empire.@NotNull ActiveEmpire getNeutralEmpire() {
return (de.geolykt.starloader.api.empire.ActiveEmpire) Galimulator.getUniverse().getNeutralEmpire();
}
@SuppressWarnings("rawtypes")
@Override
public Vector getPeopleUnsafe() {
return Objects.requireNonNull((Vector) Space.getPersons());
}
@Override
@Nullable
@Deprecated
public de.geolykt.starloader.api.empire.@Nullable ActiveEmpire getPlayerEmpire() {
return (de.geolykt.starloader.api.empire.ActiveEmpire) Galimulator.getUniverse().getPlayerEmpire();
}
@Override
@SuppressWarnings("rawtypes")
public Vector> getQuestsUnsafe() {
return Objects.requireNonNull((Vector) Space.quests);
}
@Override
@Nullable
public SavegameFormat getSavegameFormat(@NotNull InputStream input) {
Objects.requireNonNull(input, "\"input\" may not be null!");
byte[] header = new byte[64];
int offset = 0;
try {
for (int read = input.read(header); read != -1; read = input.read(header, offset, 64 - offset)) {
offset += read;
if (offset >= 64) {
break;
}
}
} catch (Exception ignored) {
// Ignored as defined by contract of the method
}
if (offset == 0) {
return null; // Empty stream - what gives?
}
if (BoilerplateSavegameFormat.FORMAT_HEADER.length <= offset
&& JavaInterop.equals(header, 0, BoilerplateSavegameFormat.FORMAT_HEADER.length, BoilerplateSavegameFormat.FORMAT_HEADER, 0, BoilerplateSavegameFormat.FORMAT_HEADER.length)) {
return BoilerplateSavegameFormat.INSTANCE;
}
// I am quite sure that the ObjectOutputStream leaves behind some form of header too, but I am too lazy to go that route.
return null;
}
@SuppressWarnings({ "deprecation", "unused" })
@Override
@NotNull
public SavegameFormat getSavegameFormat(@NotNull SupportedSavegameFormat format) {
switch (format) {
case SLAPI_BOILERPLATE:
return BoilerplateSavegameFormat.INSTANCE;
case VANILLA:
return VanillaSavegameFormat.INSTANCE;
default:
if (Objects.isNull(format)) {
throw new NullPointerException("format must not be null!");
}
throw new UnsupportedOperationException("Format " + format.name() + " is actually not supported. You might want to file a bug.");
}
}
@Override
@NotNull
public Iterable extends SavegameFormat> getSavegameFormats() {
return SAVEGAME_FORMATS;
}
@SuppressWarnings("null")
@Override
@NotNull
public TickLoopLock getSimulationLoopLock() {
return (TickLoopLock) Space.getMainTickLoopLock();
}
@Override
public @NotNull SoundHandler getSoundHandler() {
return SLSoundHandler.getInstance();
}
@Override
@Nullable
@Contract(pure = true)
public Star getStarAt(float boardX, float boardY) {
return Galimulator.getUniverse().getStarAt(boardX, boardY);
}
@SuppressWarnings("null")
@Override
@NotNull
public List<@NotNull Star> getStarList() {
return Collections.unmodifiableList(getStarsUnsafe());
}
@SuppressWarnings({ "null" })
@Override
@ScheduledForRemoval(inVersion = "3.0.0")
@DeprecatedSince("2.0.0")
@Deprecated
public @NotNull List<@NotNull Star> getStars() {
return getStarsUnsafe();
}
@SuppressWarnings("rawtypes")
@Override
public Vector getStarsUnsafe() {
return Objects.requireNonNull((Vector) Space.stars);
}
@Override
@NotNull
public SpawnPredicatesContainer getStateActorSpawningPredicates() {
return globalSpawningPredicates;
}
// Since galim 5.0 / SLAPI 2.0
private ArrayList> getTranscendedEmpireNames() {
return Space.an; // TODO figure out *why* autodeobf is too stupid to figure out that one
}
@Override
public int getTranscendedEmpires() {
return Space.getTranscended();
}
@Override
@DeprecatedSince("1.5.0")
@Deprecated
public Galimulator.@NotNull Unsafe getUnsafe() {
return this;
}
/**
* Obtains the currently valid vanity holder instance.
*
* @return The valid vanity holder instance
*/
public VanityHolder getVanityHolder() {
return Space.vanity;
}
@Override
@SuppressWarnings("rawtypes")
public Vector getWarsUnsafe() {
return Objects.requireNonNull((Vector) Space.wars);
}
@Override
@NotNull
public WeaponsManager getWeaponsManager() {
return SLWeaponsManager.getInstance();
}
@Override
public boolean hasUsedSandbox() {
return Space.sandboxUsed;
}
@Override
public boolean isPaused() {
return Space.isPaused();
}
@Override
public void loadClipboardScenario() {
Scenario.loadClipboardScenario();
}
@Override
public synchronized void loadGameState(byte[] data) throws IOException {
getSavegameFormat(SupportedSavegameFormat.SLAPI_BOILERPLATE).loadGameState(data);
}
@Override
public synchronized void loadGameState(@NotNull InputStream input) throws IOException {
getSavegameFormat(SupportedSavegameFormat.SLAPI_BOILERPLATE).loadGameState(input);
}
@SuppressWarnings("null")
@Override
public synchronized void loadSavegameFile(@NotNull Path savegameFile) {
Galemulator.d = 0L;
new Thread(() -> {
boolean acquiredLocks = false;
Throwable suppressedException = null;
try {
Space.getMainTickLoopLock().acquire(2);
acquiredLocks = true;
loadGameState(Files.newInputStream(savegameFile));
LOGGER.info("Restored from disk, stack depth was: " + Space.saveStackdepth);
} catch (InterruptedException interrupted) {
if (!acquiredLocks) {
Galimulator.panic("Interrupted loading thread while acquiring main tick loop lock - this is almost definetly caused by mods.", false, interrupted);
} else {
LOGGER.info("Loading was interrupted!", interrupted);
}
} catch (OutOfMemoryError oom) {
suppressedException = oom;
System.gc();
} catch (Throwable t) {
suppressedException = t;
} finally {
if (!acquiredLocks) {
// The Galimulator.panic(...) method has been invoked
return;
}
if (suppressedException != null) {
boolean generateSuccess = false;
try {
Space.player = new Player();
Space.generateGalaxy(300, new MapData(ProceduralStarGenerator.STRETCHED_SPIRAL));
Space.ay = true;
generateSuccess = true;
} catch (Throwable t) {
t.addSuppressed(suppressedException);
Galimulator.panic("Unable to generate galaxy after failed loading attempt.", false, t);
} finally {
Space.getMainTickLoopLock().release(2);
}
if (generateSuccess) {
// Not printing if it didn't succeed because the .crash() method already deals with that
suppressedException.printStackTrace();
}
} else {
Space.getMainTickLoopLock().release(2);
}
}
}).start();
}
@SuppressWarnings("null")
@Override
@NotNull
public Star lookupStar(int id) {
if (id < -1 || (id + 1) > Space.stars.size()) {
throw new IllegalArgumentException("There is no star with the given UID: " + id);
}
return (Star) Space.stars.get(id + 1);
}
@Override
public void panic(@NotNull String message, boolean save) {
GalimulatorImplementation.crash(message, save);
}
@Override
public void panic(@NotNull String message, boolean save, @NotNull Throwable cause) {
GalimulatorImplementation.crash(cause, message, save);
}
@Override
public void pauseGame() {
Space.setPaused(true);
}
@Override
public void recalculateVoronoiGraphs() {
Space.regenerateVoronoiCells();
}
@Override
public void registerMouseInputListener(@NotNull MouseInputListener listener) {
this.listeners.add(Objects.requireNonNull(listener, "listener cannot be null"));
}
@Override
public void resumeGame() {
Space.setPaused(false);
}
@Override
public void runTaskOnNextFrame(Runnable task) {
Gdx.app.postRunnable(task);
}
@Override
public void runTaskOnNextTick(@NotNull Runnable runnable) {
SCHEDULED_TASKS_NEXT_TICK.add(runnable);
}
@Override
public void saveFile(@NotNull String name, byte[] data) {
File out = new File(DataFolderProvider.getProvider().provideAsFile(), Objects.requireNonNull(name));
if (!out.exists()) {
try (FileOutputStream fos = new FileOutputStream(out)) {
fos.write(data);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public void saveFile(@NotNull String name, @NotNull InputStream data) {
File out = new File(DataFolderProvider.getProvider().provideAsFile(), Objects.requireNonNull(name));
if (!out.exists()) {
try (FileOutputStream fos = new FileOutputStream(out)) {
JavaInterop.transferTo(data, fos);
fos.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@NotNull
@Contract(pure = true)
public final SpaceState createState() {
return new SpaceState((Vector) getStarsUnsafe(),
(Vector) getEmpiresUnsafe(),
(Vector) getArtifactsUnsafe(),
(Vector) getActorsUnsafe(),
(Vector) getDisruptedStarsUnsafe(),
(snoddasmannen.galimulator.Empire) getNeutralEmpire(),
getGameYear(),
getTranscendedEmpires(),
getVanityHolder(),
(Vector) getQuestsUnsafe(),
Space.getPlayer(),
Space.getMapData(),
hasUsedSandbox(),
snoddasmannen.galimulator.EmploymentAgency.getInstance(),
(Vector) getPeopleUnsafe(),
Space.history,
(List) null,
(Vector) getAlliancesUnsafe(),
(Vector) getCooperationsUnsafe(),
(Vector) getWarsUnsafe(),
(ArrayList) getTranscendedEmpireNames());
}
@Override
public void setActiveMapmode(@NotNull MapMode mode) {
snoddasmannen.galimulator.MapMode.setCurrentMode(ExpectedObfuscatedValueException.requireMapMode(mode));
}
@SuppressWarnings("rawtypes")
@Override
public void setActorsUnsafe(Vector actors) {
Space.actors = Objects.requireNonNull((Vector) actors);
}
@SuppressWarnings("rawtypes")
@Override
public void setAlliancesUnsafe(Vector alliances) {
Space.alliances = Objects.requireNonNull((Vector) alliances);
}
@SuppressWarnings("rawtypes")
@Override
public void setArtifactsUnsafe(Vector> artifacts) {
Space.artifacts = Objects.requireNonNull((Vector) artifacts);
}
@Override
public void setBackgroundTask(@NotNull BackgroundTask task) {
this.currentTask = Objects.requireNonNull(task, "Input argument 'task' may not be null.");
}
@Override
@Deprecated
@ScheduledForRemoval(inVersion = "3.0.0")
@DeprecatedSince("2.0.0-a20240831")
public void setBackgroundTaskProgress(@Nullable String progressDescription) {
Space.h(progressDescription);
}
@SuppressWarnings("rawtypes")
@Override
public void setCorporationsUnsafe(Vector> corporations) {
Space.corporations = Objects.requireNonNull((Vector) corporations);
}
@Override
public void setDisruptedStarsUnsafe(Vector disruptedStars) {
Space.disruptedStars = Objects.requireNonNull(disruptedStars);
}
@SuppressWarnings("rawtypes")
@Override
public void setEmpiresUnsafe(Vector empires) {
Space.empires = Objects.requireNonNull((Vector) empires);
}
@SuppressWarnings("rawtypes")
@Override
public void setFollowedPeopleUnsafe(@NotNull Vector people) {
Space.v = Objects.requireNonNull((Vector) people);
}
@Override
public void setGameYear(int year) {
Space.milliYear = year;
}
@Override
@Deprecated
public void setMap(@NotNull de.geolykt.starloader.api.@NotNull Map map) {
if (!(map instanceof snoddasmannen.galimulator.MapData)) {
throw new ExpectedObfuscatedValueException();
}
Space.mapData = (snoddasmannen.galimulator.MapData) map;
}
@Override
@Deprecated
public void setNeutralEmpire(@NotNull de.geolykt.starloader.api.empire.@NotNull ActiveEmpire empire) {
Space.neutralEmpire = ExpectedObfuscatedValueException.requireEmpire((Empire) Objects.requireNonNull(empire));
}
@Override
public void setPaused(boolean paused) {
if (this.isPaused() && paused) {
// Prevent potentially unwanted logic to occur in this edge case (as the game will attempt to display the "step" widget again)
return;
}
Space.setPaused(paused);
}
@SuppressWarnings("rawtypes")
@Override
public void setPeopleUnsafe(Vector members) {
Space.persons = Objects.requireNonNull((Vector) members);
}
public void setPlayer(Player player) {
Space.player = player;
}
@SuppressWarnings("rawtypes")
@Override
public void setQuestsUnsafe(Vector> quests) {
Space.quests = Objects.requireNonNull((Vector) quests);
}
@SuppressWarnings("rawtypes")
@Override
public void setStarsUnsafe(Vector stars) {
Space.stars = Objects.requireNonNull((Vector) stars);
Space.starCount = stars.size();
}
@Override
public void setTranscendedEmpires(int count) {
Space.transcended = count;
}
@Override
public void setUsedSandbox(boolean state) {
Space.sandboxUsed = state;
}
/**
* Sets the valid vanity holder instance that dictates the vanity names to use.
*
* @param holder The vanity holder to use
*/
public void setVanityHolder(VanityHolder holder) {
Space.vanity = holder;
}
@SuppressWarnings("rawtypes")
@Override
public void setWarsUnsafe(Vector wars) {
Space.wars = Objects.requireNonNull((Vector) wars);
}
@Override
public void showGalaxyCreationScreen() {
Space.showGalaxyCreationScreen();
}
@Override
public void showOnlineScenarioBrowser() {
Space.showOnlineScenarioBrowser();
}
@Override
public void showModUploadScreen() {
Space.showWidget(ModUploadWidget.class);
}
@Override
@Deprecated
public void showScenarioMetadataEditor(de.geolykt.starloader.api.@NotNull Map map) {
Space.showDialog(((MapData) map).getMetadata(), true, null, false);
}
@Override
public void showScenarioSaveScreen() {
OptionChooserWidget var3 = Space.openOptionChooser("Choose slot", "Choose save slot", Space.a("scenarios/Scenario_", false), 0, null, true);
if (var3 != null) {
var3.registerSelectionListener((selection) -> {
String var2 = selection.toString().substring(0, selection.toString().indexOf("\n")) + ".dat";
Space.j(var2);
});
}
}
@Override
public void setNeutralEmpire(@NotNull Empire empire) {
Space.neutralEmpire = (snoddasmannen.galimulator.Empire) empire;
}
@SuppressWarnings("null")
@Override
@NotNull
public Empire lookupEmpire(int uid) {
return (Empire) Space.e(uid);
}
@SuppressWarnings("all")
@Override
@NotNull
@Deprecated
public @UnmodifiableView Collection getEmpiresView() {
return (Collection) (Collection>)
Collections.unmodifiableCollection(Space.empires);
}
}