diff --git a/README.md b/README.md
index 59a4ef3..ac0aecd 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,23 @@
+[//]: # (A skyblock mod with customizable islands, advanced island protection and support for expansion mods.)
+
+[//]: # ()
+[//]: # (NeoSkies provides it's own API and registries for other mods to add content and features and integrate with the island system.)
+
+[//]: # ()
+[//]: # (Each island have their own settings, those settings allow the owner to decide how other players can interact with the island, such as allowing interaction with doors, redstone, containers, etc.)
+
+[//]: # ()
+[//]: # (The mod uses the Common Economy API for currency, each island has it's account, that is shared between all island members.)
+
+[//]: # ()
+[//]: # (The mod is still in development and will change over time.)
+
+[//]: # ()
+[//]: # (This mod is a fork of Skylands, and the current island system and island templates are (mostly) from the original mod.)
+
+[//]: # ()
+[//]: # (Please provide feedback on the [discord server](https://discord.gg/MTqsjwMpN2), it helps a lot to continue developing the mod)
+
## About
diff --git a/build.gradle.kts b/build.gradle.kts
index c627313..9e0f2f0 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,6 +1,7 @@
import java.io.File
import com.modrinth.minotaur.dependencies.ModDependency
+//region Setup
plugins {
id("signing")
id("maven-publish")
@@ -8,15 +9,16 @@ plugins {
id("com.modrinth.minotaur") version "2.+"
}
-val CHANGELOG: String =
- if (file("CHANGELOG.md").exists()) {
- file("CHANGELOG.md").readText()
- } else {
- "No changelog provided"
- }
+loom {
+ accessWidenerPath.set(file("src/main/resources/neoskies.accesswidener"))
+ splitEnvironmentSourceSets()
-fun file(path: String): File {
- return rootProject.file(path)
+ mods {
+ create("neoskies") {
+ sourceSet(sourceSets.main.get())
+ sourceSet(sourceSets.getByName("client"))
+ }
+ }
}
sourceSets {
@@ -30,37 +32,48 @@ sourceSets {
compileClasspath += sourceSets.main.get().compileClasspath
runtimeClasspath += sourceSets.main.get().runtimeClasspath
}
+
+ create("testmodClient") {
+ compileClasspath += main.get().compileClasspath
+ runtimeClasspath += main.get().runtimeClasspath
+
+ compileClasspath += sourceSets.getByName("client").compileClasspath
+ runtimeClasspath += sourceSets.getByName("client").runtimeClasspath
+
+ compileClasspath += sourceSets.getByName("testmod").compileClasspath
+ runtimeClasspath += sourceSets.getByName("testmod").runtimeClasspath
+ }
}
loom {
- accessWidenerPath.set(file("src/main/resources/neoskies.accesswidener"))
-
runs {
create("datagenServer") {
server()
- name = "Data Generation"
+ name("Data Generation")
vmArg("-Dfabric-api.datagen")
vmArg("-Dfabric-api.datagen.output-dir=${file("src/main/generated")}")
vmArg("-Dfabric-api.datagen.modid=neoskies")
-
- //ideConfigGenerated = true
- runDir = "build/datagen"
+ runDir("build/datagen")
+ ideConfigGenerated(true)
}
-
create("testmodServer") {
server()
- name = "Testmod Server"
+ name("Testmod Server")
+ ideConfigGenerated(true)
source(sourceSets.getByName("testmod"))
}
-
create("testmodClient") {
client()
- name = "Testmod Client"
+ name("Testmod Client")
runDir("run_client")
- source(sourceSets.getByName("testmod"))
+ //TODO: Generate dev world
+ programArgs("--quickPlaySingleplayer \"world\"")
+ ideConfigGenerated(true)
+ source(sourceSets.getByName("testmodClient"))
}
}
}
+//endregion
var archivesBaseName: String = property("archives_base_name").toString()
base {
@@ -76,6 +89,7 @@ repositories {
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://maven.ladysnake.org/releases")
maven("https://maven.isxander.dev/releases")
+ maven("https://maven.deftu.dev/snapshots")
maven("https://jitpack.io")
maven {
name = "Modrinth"
@@ -122,11 +136,30 @@ dependencies {
include(api("blue.endless:jankson:${property("jankson_version")}") as Any)
//endregion
+ //region Client
+ "clientImplementation"("dev.deftu:dearimguimc-1.20.6-fabric:0.1.0") {
+ exclude(group = "net.fabricmc.fabric-api")
+ }
+ listOf(
+ "binding",
+ "lwjgl3",
+ "natives-windows",
+ "natives-linux",
+ "natives-macos"
+ ).forEach { module ->
+ val version = "1.86.11"
+ implementation("io.github.spair:imgui-java-$module:$version") {
+ exclude(group = "org.lwjgl")
+ }
+ }
+ //endregion
+
// region Tests
"testmodImplementation"(sourceSets.main.get().output)
//endregion
}
+//region Misc
tasks.processResources {
val map = mapOf(
"version" to version
@@ -156,7 +189,9 @@ tasks.jar {
rename { "${it}_${property("archivesBaseName")}" }
}
}
+//endregion
+//region Publishing
publishing {
repositories {
maven {
@@ -181,6 +216,17 @@ signing {
sign(publishing.publications["main"])
}
+val CHANGELOG: String =
+ if (file("CHANGELOG.md").exists()) {
+ file("CHANGELOG.md").readText()
+ } else {
+ "No changelog provided"
+ }
+
+fun file(path: String): File {
+ return rootProject.file(path)
+}
+
modrinth {
val projectVersion: String = property("mod_version").toString()
val projectVersionNumber: List = projectVersion.split(Regex("-"), 2)
@@ -212,3 +258,4 @@ modrinth {
ModDependency("placeholder-api", "embedded")
)
}
+//endregion
diff --git a/gradle.properties b/gradle.properties
index e251cd1..6cbdadf 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -9,9 +9,9 @@ mod_version=1.0.0-alpha.1
# Dependencies | Check these on https://fabricmc.net/develop
minecraft_version=1.20.6
yarn_mappings=1.20.6+build.1
-fabric_loader=0.15.10
+fabric_loader=0.15.11
-fabric_api=0.98.0+1.20.6
+fabric_api=0.100.4+1.20.6
cardinal_components_api=6.0.0-beta.3
diff --git a/src/client/java/com/awakenedredstone/neoskies/NeoSkiesClient.java b/src/client/java/com/awakenedredstone/neoskies/NeoSkiesClient.java
new file mode 100644
index 0000000..3d038a2
--- /dev/null
+++ b/src/client/java/com/awakenedredstone/neoskies/NeoSkiesClient.java
@@ -0,0 +1,10 @@
+package com.awakenedredstone.neoskies;
+
+import net.fabricmc.api.ClientModInitializer;
+
+public class NeoSkiesClient implements ClientModInitializer {
+ @Override
+ public void onInitializeClient() {
+
+ }
+}
diff --git a/src/client/resources/neoskies.client.mixins.json b/src/client/resources/neoskies.client.mixins.json
new file mode 100644
index 0000000..4de9c26
--- /dev/null
+++ b/src/client/resources/neoskies.client.mixins.json
@@ -0,0 +1,10 @@
+{
+ "required": true,
+ "package": "com.awakenedredstone.neoskies.mixin.client",
+ "compatibilityLevel": "JAVA_17",
+ "client": [
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ }
+}
diff --git a/src/main/java/com/awakenedredstone/neoskies/command/NeoSkiesCommands.java b/src/main/java/com/awakenedredstone/neoskies/command/NeoSkiesCommands.java
index 83a1354..fa6c7e3 100644
--- a/src/main/java/com/awakenedredstone/neoskies/command/NeoSkiesCommands.java
+++ b/src/main/java/com/awakenedredstone/neoskies/command/NeoSkiesCommands.java
@@ -127,7 +127,6 @@ private static void registerAdminCommands(CommandDispatcher
.putAny("current", integer)
.build()));
}, (timeTaken, scannedBlocks) -> {
- Map.Entry i = scannedBlocks.entrySet().stream().findFirst().get();
source.sendMessage(Texts.of("Scanned %total% blocks in %time%", new MapBuilder.StringMap()
.putAny("total", UnitConvertions.readableNumber(scannedBlocks.values().stream().mapToInt(value -> value).sum()))
.putAny("time", UnitConvertions.formatTimings(timeTaken))
@@ -140,6 +139,7 @@ private static void registerAdminCommands(CommandDispatcher
})
)
).then(CommandManager.literal("list")
+ .requires(Permissions.require("neoskies.admin.island.list", 4))
.executes(context -> {
LinedStringBuilder builder = new LinedStringBuilder();
List islands = IslandLogic.getInstance().islands.stuck;
diff --git a/src/main/java/com/awakenedredstone/neoskies/command/admin/IslandDataCommand.java b/src/main/java/com/awakenedredstone/neoskies/command/admin/IslandDataCommand.java
index 170869d..eeb6fef 100644
--- a/src/main/java/com/awakenedredstone/neoskies/command/admin/IslandDataCommand.java
+++ b/src/main/java/com/awakenedredstone/neoskies/command/admin/IslandDataCommand.java
@@ -24,36 +24,38 @@
public class IslandDataCommand {
public static void init(CommandDispatcher dispatcher) {
registerAdmin(dispatcher, adminNode()
- .then(literal("island-data")
- .then(literal("find")
- .requires(Permissions.require("neoskies.admin.island.data.find", 4))
- .then(argument("player", StringArgumentType.word())
- .suggests((context, builder) -> {
- List islands = IslandLogic.getInstance().islands.stuck;
- for (Island island : islands) {
- builder.suggest(island.owner.name);
- island.members.forEach(member -> {
- builder.suggest(member.name);
- });
- }
- return builder.buildFuture();
- }).executes(context -> {
- String playerName = StringArgumentType.getString(context, "player");
- Optional islandOptional = NeoSkiesAPI.getIslandByPlayer(playerName);
- return getIslandData(context.getSource(), islandOptional.orElse(null));
- })
- )
- ).then(literal("get")
- .then(argument("id", StringArgumentType.word())
- .suggests(CommandUtils.ISLAND_SUGGESTIONS)
- .executes(context -> {
- String islandId = StringArgumentType.getString(context, "id");
- Optional islandOptional = NeoSkiesAPI.getIsland(UUID.fromString(islandId));
- return getIslandData(context.getSource(), islandOptional.orElse(null));
- })
- )
- )
+ .then(literal("island-data")
+ .requires(Permissions.require("neoskies.admin.island.data", 4))
+ .then(literal("find")
+ .requires(Permissions.require("neoskies.admin.island.data.find", 4))
+ .then(argument("player", StringArgumentType.word())
+ .suggests((context, builder) -> {
+ List islands = IslandLogic.getInstance().islands.stuck;
+ for (Island island : islands) {
+ builder.suggest(island.owner.name);
+ island.members.forEach(member -> {
+ builder.suggest(member.name);
+ });
+ }
+ return builder.buildFuture();
+ }).executes(context -> {
+ String playerName = StringArgumentType.getString(context, "player");
+ Optional islandOptional = NeoSkiesAPI.getIslandByPlayer(playerName);
+ return getIslandData(context.getSource(), islandOptional.orElse(null));
+ })
+ )
+ ).then(literal("get")
+ .requires(Permissions.require("neoskies.admin.island.data.get", 4))
+ .then(argument("id", StringArgumentType.word())
+ .suggests(CommandUtils.ISLAND_SUGGESTIONS)
+ .executes(context -> {
+ String islandId = StringArgumentType.getString(context, "id");
+ Optional islandOptional = NeoSkiesAPI.getIsland(UUID.fromString(islandId));
+ return getIslandData(context.getSource(), islandOptional.orElse(null));
+ })
+ )
)
+ )
);
}
@@ -73,17 +75,17 @@ private static int getIslandData(ServerCommandSource source, @Nullable Island is
}
MapBuilder.StringMap map = new MapBuilder.StringMap()
- .put("id", island.getIslandId().toString())
- .put("owner", island.owner.name)
- .put("members", members.toString())
- .putAny("balance", island.getWallet().balance())
- .putAny("has_nether", island.hasNether)
- .putAny("has_end", island.hasEnd)
- .putAny("spawn_pos", island.spawnPos)
- .putAny("visit_pos", island.visitsPos)
- .putAny("radius", island.radius)
- .putAny("locked", island.locked)
- .putAny("created", island.getCreated().toEpochMilli());
+ .put("id", island.getIslandId().toString())
+ .put("owner", island.owner.name)
+ .put("members", members.toString())
+ .putAny("balance", island.getWallet().balance())
+ .putAny("has_nether", island.hasNether)
+ .putAny("has_end", island.hasEnd)
+ .putAny("spawn_pos", island.spawnPos)
+ .putAny("visit_pos", island.visitsPos)
+ .putAny("radius", island.radius)
+ .putAny("locked", island.locked)
+ .putAny("created", island.getCreated().toEpochMilli());
source.sendFeedback(() -> Texts.of("message.neoskies.island_data", map.build()), false);
return 1;
}
diff --git a/src/main/java/com/awakenedredstone/neoskies/command/island/HomeCommand.java b/src/main/java/com/awakenedredstone/neoskies/command/island/HomeCommand.java
index 034dc58..5a80f37 100644
--- a/src/main/java/com/awakenedredstone/neoskies/command/island/HomeCommand.java
+++ b/src/main/java/com/awakenedredstone/neoskies/command/island/HomeCommand.java
@@ -1,6 +1,5 @@
package com.awakenedredstone.neoskies.command.island;
-import com.awakenedredstone.neoskies.NeoSkies;
import com.awakenedredstone.neoskies.api.NeoSkiesAPI;
import com.awakenedredstone.neoskies.logic.Island;
import com.awakenedredstone.neoskies.logic.IslandLogic;
@@ -42,21 +41,4 @@ static void run(ServerPlayerEntity player) {
}
}, () -> player.sendMessage(Texts.prefixed("message.neoskies.home.no_island")));
}
-
- static void run(ServerPlayerEntity visitor, String islandOwner) {
- IslandLogic.getInstance().islands.getByPlayer(islandOwner).ifPresentOrElse(island -> {
- if (visitor.getWorld().getRegistryKey().getValue().equals(NeoSkies.id(island.owner.uuid.toString())) && !IslandLogic.getConfig().allowVisitCurrentIsland) {
- visitor.sendMessage(Texts.prefixed("message.neoskies.visit_home.fail", map -> map.put("owner", islandOwner)));
- } else {
- if (island.isMember(visitor)) {
- visitor.sendMessage(Texts.prefixed("message.neoskies.visit_home.success", map -> map.put("owner", islandOwner)));
- island.visitAsMember(visitor);
- } else {
- visitor.sendMessage(Texts.prefixed("message.neoskies.visit_home.not_member"));
- }
- }
- }, () -> {
- visitor.sendMessage(Texts.prefixed("message.neoskies.visit_home.no_island"));
- });
- }
}
diff --git a/src/main/java/com/awakenedredstone/neoskies/command/island/VisitCommand.java b/src/main/java/com/awakenedredstone/neoskies/command/island/VisitCommand.java
index 4e0b9cf..d7ff07d 100644
--- a/src/main/java/com/awakenedredstone/neoskies/command/island/VisitCommand.java
+++ b/src/main/java/com/awakenedredstone/neoskies/command/island/VisitCommand.java
@@ -4,10 +4,12 @@
import com.awakenedredstone.neoskies.logic.IslandLogic;
import com.awakenedredstone.neoskies.util.Texts;
import com.mojang.brigadier.CommandDispatcher;
+import com.mojang.brigadier.arguments.StringArgumentType;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.command.argument.EntityArgumentType;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.text.Text;
import static com.awakenedredstone.neoskies.command.utils.CommandUtils.node;
import static com.awakenedredstone.neoskies.command.utils.CommandUtils.register;
@@ -21,10 +23,16 @@ public static void init(CommandDispatcher dispatcher) {
register(dispatcher, node()
.then(literal("visit")
.requires(Permissions.require("neoskies.island.visit", true))
- .then(argument("player", player())
+ .then(argument("player", StringArgumentType.word())
+ .suggests((context, builder) -> {
+ for (String playerName : context.getSource().getServer().getPlayerManager().getPlayerNames()) {
+ builder.suggest(playerName);
+ }
+ return builder.buildFuture();
+ })
.executes(context -> {
var visitor = context.getSource().getPlayer();
- var owner = EntityArgumentType.getPlayer(context, "player");
+ var owner = StringArgumentType.getString(context, "player");
if (visitor != null && owner != null) {
VisitCommand.run(visitor, owner);
}
@@ -35,25 +43,23 @@ public static void init(CommandDispatcher dispatcher) {
);
}
- static void run(ServerPlayerEntity visitor, ServerPlayerEntity owner) {
- String ownerName = owner.getName().getString();
-
+ static void run(ServerPlayerEntity visitor, String owner) {
IslandLogic.getInstance().islands.getByPlayer(owner).ifPresentOrElse(island -> {
if (!island.isMember(visitor) && island.isBanned(visitor)) {
- visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.ban", map -> map.put("owner", ownerName)));
+ visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.ban", map -> map.put("owner", owner)));
} else {
if (!island.locked) {
if (visitor.getWorld().getRegistryKey().getValue().equals(NeoSkies.id(island.owner.uuid.toString())) && !IslandLogic.getConfig().allowVisitCurrentIsland) {
- visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.fail", map -> map.put("owner", ownerName)));
+ visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.fail", map -> map.put("owner", owner)));
} else {
- visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.success", map -> map.put("owner", ownerName)));
+ visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.success", map -> map.put("owner", owner)));
island.visitAsVisitor(visitor);
}
} else {
- visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.no_visits", map -> map.put("owner", ownerName)));
+ visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.no_visits", map -> map.put("owner", owner)));
}
}
- }, () -> visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.no_island", map -> map.put("owner", ownerName))));
+ }, () -> visitor.sendMessage(Texts.prefixed("message.neoskies.island_visit.no_island", map -> map.put("owner", owner))));
}
}
diff --git a/src/main/java/com/awakenedredstone/neoskies/config/IslandRankingConfig.java b/src/main/java/com/awakenedredstone/neoskies/config/IslandRankingConfig.java
index 7306f91..8fe0157 100644
--- a/src/main/java/com/awakenedredstone/neoskies/config/IslandRankingConfig.java
+++ b/src/main/java/com/awakenedredstone/neoskies/config/IslandRankingConfig.java
@@ -9,19 +9,12 @@
import com.awakenedredstone.neoskies.config.source.annotation.SkipThis;
import com.awakenedredstone.neoskies.mixin.accessor.TagEntryAccessor;
import com.google.gson.JsonElement;
-import com.mojang.serialization.Codec;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
-import com.mojang.serialization.MapCodec;
-import com.mojang.serialization.codecs.RecordCodecBuilder;
-import net.minecraft.block.ButtonBlock;
-import net.minecraft.client.gui.screen.option.KeybindsScreen;
import net.minecraft.registry.Registries;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.registry.tag.TagEntry;
import net.minecraft.registry.tag.TagKey;
-import net.minecraft.text.Text;
-import net.minecraft.text.TranslatableTextContent;
import net.minecraft.util.Identifier;
import java.util.HashMap;
diff --git a/src/main/java/com/awakenedredstone/neoskies/logic/Hub.java b/src/main/java/com/awakenedredstone/neoskies/logic/Hub.java
index cc5893b..988fce4 100644
--- a/src/main/java/com/awakenedredstone/neoskies/logic/Hub.java
+++ b/src/main/java/com/awakenedredstone/neoskies/logic/Hub.java
@@ -2,6 +2,7 @@
import com.awakenedredstone.neoskies.api.events.IslandEvents;
import com.awakenedredstone.neoskies.util.Texts;
+import com.awakenedredstone.neoskies.util.Worlds;
import net.fabricmc.fabric.api.dimension.v1.FabricDimensions;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
@@ -14,14 +15,14 @@ public class Hub {
public boolean hasProtection = false;
//public PositionSongPlayer songPlayer = null;
- public void visit(PlayerEntity player) {
+ public void visit(ServerPlayerEntity player) {
visit(player, false);
}
- public void visit(PlayerEntity player, boolean silent) {
+ public void visit(ServerPlayerEntity player, boolean silent) {
var world = IslandLogic.getServer().getOverworld();
if (!silent) player.sendMessage(Texts.prefixed("message.neoskies.hub_visit"));
- FabricDimensions.teleport(player, world, new TeleportTarget(this.pos, new Vec3d(0, 0, 0), 0, 0));
+ Worlds.teleport(player, world, this.pos, 0, 0);
IslandEvents.ON_HUB_VISIT.invoker().invoke(player, world);
}
diff --git a/src/main/java/com/awakenedredstone/neoskies/logic/Island.java b/src/main/java/com/awakenedredstone/neoskies/logic/Island.java
index 24a5cde..8bac4c9 100644
--- a/src/main/java/com/awakenedredstone/neoskies/logic/Island.java
+++ b/src/main/java/com/awakenedredstone/neoskies/logic/Island.java
@@ -11,12 +11,15 @@
import com.awakenedredstone.neoskies.util.Constants;
import com.awakenedredstone.neoskies.util.Players;
import com.awakenedredstone.neoskies.util.Texts;
+import com.awakenedredstone.neoskies.util.Worlds;
import eu.pb4.common.economy.api.EconomyAccount;
import net.minecraft.block.Block;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.NbtCompound;
+import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.structure.StructurePlacementData;
import net.minecraft.structure.StructureTemplate;
@@ -27,6 +30,7 @@
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.random.RandomSeed;
import net.minecraft.world.Difficulty;
+import net.minecraft.world.World;
import net.minecraft.world.biome.BiomeKeys;
import net.minecraft.world.dimension.DimensionTypes;
import net.minecraft.world.gen.chunk.FlatChunkGenerator;
@@ -308,14 +312,7 @@ public boolean isInteractionAllowed(Identifier identifier, PermissionLevel sourc
return source.getLevel() >= settings.getPermissionLevel().getLevel();
}
- public RuntimeWorldHandle getOverworldHandler() {
- if (this.islandConfig == null) {
- this.islandConfig = createIslandConfig();
- }
- return this.fantasy.getOrOpenPersistentWorld(NeoSkies.id(this.owner.uuid.toString()), this.islandConfig);
- }
-
- private RuntimeWorldConfig createIslandConfig() {
+ private RuntimeWorldConfig createOverworldConfig() {
var biome = IslandLogic.getServer().getRegistryManager().get(RegistryKeys.BIOME).getEntry(IslandLogic.getServer().getRegistryManager().get(RegistryKeys.BIOME).getOrThrow(BiomeKeys.PLAINS));
FlatChunkGeneratorConfig flat = new FlatChunkGeneratorConfig(Optional.empty(), biome, List.of());
FlatChunkGenerator generator = new FlatChunkGenerator(flat);
@@ -329,13 +326,6 @@ private RuntimeWorldConfig createIslandConfig() {
.setSeed(0L);
}
- public RuntimeWorldHandle getNetherHandler() {
- if (this.netherConfig == null) {
- this.netherConfig = createNetherConfig();
- }
- return this.fantasy.getOrOpenPersistentWorld(new Identifier(Constants.NAMESPACE_NETHER, this.owner.uuid.toString()), this.netherConfig);
- }
-
private RuntimeWorldConfig createNetherConfig() {
var biome = IslandLogic.getServer().getRegistryManager().get(RegistryKeys.BIOME).getEntry(IslandLogic.getServer().getRegistryManager().get(RegistryKeys.BIOME).getOrThrow(BiomeKeys.NETHER_WASTES));
FlatChunkGeneratorConfig flat = new FlatChunkGeneratorConfig(Optional.empty(), biome, List.of());
@@ -349,13 +339,6 @@ private RuntimeWorldConfig createNetherConfig() {
.setSeed(RandomSeed.getSeed());
}
- public RuntimeWorldHandle getEndHandler() {
- if (this.endConfig == null) {
- this.endConfig = createEndConfig();
- }
- return this.fantasy.getOrOpenPersistentWorld(new Identifier(Constants.NAMESPACE_END, this.owner.uuid.toString()), this.endConfig);
- }
-
private RuntimeWorldConfig createEndConfig() {
var biome = IslandLogic.getServer().getRegistryManager().get(RegistryKeys.BIOME).getEntry(IslandLogic.getServer().getRegistryManager().get(RegistryKeys.BIOME).getOrThrow(BiomeKeys.THE_END));
FlatChunkGeneratorConfig flat = new FlatChunkGeneratorConfig(Optional.empty(), biome, List.of());
@@ -369,6 +352,27 @@ private RuntimeWorldConfig createEndConfig() {
.setSeed(RandomSeed.getSeed());
}
+ public RuntimeWorldHandle getOverworldHandler() {
+ if (this.islandConfig == null) {
+ this.islandConfig = createOverworldConfig();
+ }
+ return this.fantasy.getOrOpenPersistentWorld(NeoSkies.id(this.owner.uuid.toString()), this.islandConfig);
+ }
+
+ public RuntimeWorldHandle getNetherHandler() {
+ if (this.netherConfig == null) {
+ this.netherConfig = createNetherConfig();
+ }
+ return this.fantasy.getOrOpenPersistentWorld(new Identifier(Constants.NAMESPACE_NETHER, this.owner.uuid.toString()), this.netherConfig);
+ }
+
+ public RuntimeWorldHandle getEndHandler() {
+ if (this.endConfig == null) {
+ this.endConfig = createEndConfig();
+ }
+ return this.fantasy.getOrOpenPersistentWorld(new Identifier(Constants.NAMESPACE_END, this.owner.uuid.toString()), this.endConfig);
+ }
+
public ServerWorld getOverworld() {
RuntimeWorldHandle handler = this.getOverworldHandler();
handler.setTickWhenEmpty(false);
@@ -391,6 +395,18 @@ public ServerWorld getNether() {
return world;
}
+ public RegistryKey getOverworldKey() {
+ return getOverworld().getRegistryKey();
+ }
+
+ public RegistryKey getNetherKey() {
+ return getNether().getRegistryKey();
+ }
+
+ public RegistryKey getEndKey() {
+ return getEnd().getRegistryKey();
+ }
+
public void updateBlocks(@Nullable Map blocks) {
if (blocks != null) this.blocks = blocks;
this.points = 0;
@@ -400,9 +416,9 @@ public void updateBlocks(@Nullable Map blocks) {
});
}
- public void visit(PlayerEntity player, Vec3d pos) {
+ public void visit(ServerPlayerEntity player, Vec3d pos) {
ServerWorld world = this.getOverworld();
- player.teleport(world, pos.getX(), pos.getY(), pos.getZ(), Set.of(), 0, 0);
+ Worlds.teleport(player, world, pos.getX(), pos.getY(), pos.getZ(), 0, 0);
if (!isMember(player)) {
Players.get(this.owner.name).ifPresent(owner -> {
@@ -420,11 +436,11 @@ public void visit(PlayerEntity player, Vec3d pos) {
}
}
- public void visitAsMember(PlayerEntity player) {
+ public void visitAsMember(ServerPlayerEntity player) {
this.visit(player, this.spawnPos);
}
- public void visitAsVisitor(PlayerEntity player) {
+ public void visitAsVisitor(ServerPlayerEntity player) {
this.visit(player, this.visitsPos);
}
diff --git a/src/main/java/com/awakenedredstone/neoskies/logic/IslandStuck.java b/src/main/java/com/awakenedredstone/neoskies/logic/IslandStuck.java
index 201b74e..123c522 100644
--- a/src/main/java/com/awakenedredstone/neoskies/logic/IslandStuck.java
+++ b/src/main/java/com/awakenedredstone/neoskies/logic/IslandStuck.java
@@ -99,6 +99,17 @@ public Optional getByPlayer(UUID playerUuid) {
return Optional.empty();
}
+ public Optional getById(String islandId) {
+ return getById(UUID.fromString(islandId));
+ }
+
+ public Optional getById(UUID islandId) {
+ for (var island : this.stuck) {
+ if (island.getIslandId().equals(islandId)) return Optional.of(island);
+ }
+ return Optional.empty();
+ }
+
public Optional get(UUID islandId) {
for (var island : this.stuck) {
if (island.getIslandId().equals(islandId)) return Optional.of(island);
diff --git a/src/main/java/com/awakenedredstone/neoskies/logic/registry/NeoSkiesIslandSettings.java b/src/main/java/com/awakenedredstone/neoskies/logic/registry/NeoSkiesIslandSettings.java
index ead5e80..db5302b 100644
--- a/src/main/java/com/awakenedredstone/neoskies/logic/registry/NeoSkiesIslandSettings.java
+++ b/src/main/java/com/awakenedredstone/neoskies/logic/registry/NeoSkiesIslandSettings.java
@@ -39,7 +39,7 @@ public class NeoSkiesIslandSettings {
public static final IslandSettings USE_LODESTONE = register("use/lodestone", Items.LODESTONE);
public static final IslandSettings USE_REDSTONE = register("use/redstone", Items.REDSTONE);
public static final IslandSettings USE_RESPAWN_ANCHOR = register("use/respawn_anchor", Items.RESPAWN_ANCHOR);
- public static final IslandSettings USE_SIGNS = register("use/signs", Items.SPAWNER);
+ public static final IslandSettings USE_SIGNS = register("use/signs", Items.OAK_SIGN);
public static final IslandSettings USE_SPAWNER = register("use/spawner", Items.SPAWNER);
public static final IslandSettings USE_TNT = register("use/tnt", Items.TNT);
@@ -51,7 +51,7 @@ public class NeoSkiesIslandSettings {
public static final IslandSettings HARVEST = register("harvest", Items.SWEET_BERRIES);
public static final IslandSettings RIDE_MINECARTS = register("ride/minecarts", Items.MINECART);
- public static final IslandSettings RIDE_BOATS = register("ride/boats", Items.MINECART);
+ public static final IslandSettings RIDE_BOATS = register("ride/boats", Items.OAK_BOAT);
public static final IslandSettings RIDE_OTHERS = register("ride/others", Items.SADDLE);
public static final IslandSettings LEASH_ENTITY = register("leash/entity", Items.LEAD);
public static final IslandSettings SHEAR_ENTITY = register("shear/entity", Items.SHEARS);
diff --git a/src/main/java/com/awakenedredstone/neoskies/mixin/block/EndPortalBlockMixin.java b/src/main/java/com/awakenedredstone/neoskies/mixin/block/EndPortalBlockMixin.java
index 2a3892a..d1b2bd0 100644
--- a/src/main/java/com/awakenedredstone/neoskies/mixin/block/EndPortalBlockMixin.java
+++ b/src/main/java/com/awakenedredstone/neoskies/mixin/block/EndPortalBlockMixin.java
@@ -3,43 +3,41 @@
import com.awakenedredstone.neoskies.api.NeoSkiesAPI;
import com.awakenedredstone.neoskies.logic.Island;
import com.awakenedredstone.neoskies.logic.IslandLogic;
-import net.minecraft.block.BlockState;
+import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.block.EndPortalBlock;
-import net.minecraft.entity.Entity;
-import net.minecraft.server.world.ServerWorld;
-import net.minecraft.util.math.BlockPos;
+import net.minecraft.registry.RegistryKey;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
-import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
+import org.spongepowered.asm.mixin.injection.ModifyVariable;
import java.util.Optional;
@Mixin(EndPortalBlock.class)
public class EndPortalBlockMixin {
- @Inject(method = "onEntityCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;moveToWorld(Lnet/minecraft/server/world/ServerWorld;)Lnet/minecraft/entity/Entity;"), locals = LocalCapture.CAPTURE_FAILHARD, cancellable = true)
- public void resourceKey(BlockState state, World world, BlockPos pos, Entity entity, CallbackInfo ci) {
+ @ModifyVariable(method = "onEntityCollision", at = @At(value = "STORE"), ordinal = 0)
+ public RegistryKey resourceKey(RegistryKey original, @Local(argsOnly = true) World world) {
if (NeoSkiesAPI.isIsland(world)) {
+ Optional island = NeoSkiesAPI.getIsland(world);
if (!IslandLogic.getConfig().enableEndIsland) {
- ci.cancel();
- return;
+ if (island.isPresent()) {
+ return island.get().getOverworldKey();
+ } else {
+ return world.getRegistryKey();
+ }
}
- Optional island = NeoSkiesAPI.getIsland(world);
if (island.isPresent()) {
- ServerWorld targetWorld;
+ RegistryKey targetWorld;
if (NeoSkiesAPI.isEnd(world.getRegistryKey())) {
- targetWorld = island.get().getOverworld();
+ targetWorld = island.get().getOverworldKey();
} else {
- targetWorld = island.get().getEnd();
- }
- if (targetWorld != null) {
- entity.moveToWorld(targetWorld);
+ targetWorld = island.get().getEndKey();
}
- ci.cancel();
+ return targetWorld;
}
}
+
+ return original;
}
}
diff --git a/src/main/java/com/awakenedredstone/neoskies/mixin/entity/EntityAccessor.java b/src/main/java/com/awakenedredstone/neoskies/mixin/entity/EntityAccessor.java
new file mode 100644
index 0000000..7c38dde
--- /dev/null
+++ b/src/main/java/com/awakenedredstone/neoskies/mixin/entity/EntityAccessor.java
@@ -0,0 +1,10 @@
+package com.awakenedredstone.neoskies.mixin.entity;
+
+import net.minecraft.entity.Entity;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+
+@Mixin(Entity.class)
+public interface EntityAccessor {
+ @Invoker void callUnsetRemoved();
+}
diff --git a/src/main/java/com/awakenedredstone/neoskies/mixin/entity/PlayerManagerMixin.java b/src/main/java/com/awakenedredstone/neoskies/mixin/entity/PlayerManagerMixin.java
index ae966ba..17bdda6 100644
--- a/src/main/java/com/awakenedredstone/neoskies/mixin/entity/PlayerManagerMixin.java
+++ b/src/main/java/com/awakenedredstone/neoskies/mixin/entity/PlayerManagerMixin.java
@@ -104,7 +104,11 @@ private void loadIsland(ClientConnection connection, ServerPlayerEntity player,
@ModifyExpressionValue(method = "respawnPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/network/ServerPlayerEntity;getSpawnPointDimension()Lnet/minecraft/registry/RegistryKey;"))
private RegistryKey neoskies$fixRespawnDimension(RegistryKey original, ServerPlayerEntity player) {
- return player.getWorld().getRegistryKey();
+ World playerWorld = player.getWorld();
+ if (NeoSkiesAPI.isIsland(playerWorld)) {
+ return NeoSkiesAPI.getIsland(playerWorld).get().getOverworldKey();
+ }
+ return IslandLogic.getServer().getOverworld().getRegistryKey();
}
@Redirect(method = "respawnPlayer", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerEntity;findRespawnPosition(Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/util/math/BlockPos;FZZ)Ljava/util/Optional;"))
diff --git a/src/main/java/com/awakenedredstone/neoskies/mixin/entity/ServerPlayerEntityAccessor.java b/src/main/java/com/awakenedredstone/neoskies/mixin/entity/ServerPlayerEntityAccessor.java
new file mode 100644
index 0000000..b89a49c
--- /dev/null
+++ b/src/main/java/com/awakenedredstone/neoskies/mixin/entity/ServerPlayerEntityAccessor.java
@@ -0,0 +1,11 @@
+package com.awakenedredstone.neoskies.mixin.entity;
+
+import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.server.world.ServerWorld;
+import org.spongepowered.asm.mixin.Mixin;
+import org.spongepowered.asm.mixin.gen.Invoker;
+
+@Mixin(ServerPlayerEntity.class)
+public interface ServerPlayerEntityAccessor {
+ @Invoker void callWorldChanged(ServerWorld world);
+}
diff --git a/src/main/java/com/awakenedredstone/neoskies/mixin/entity/ServerPlayerEntityMixin.java b/src/main/java/com/awakenedredstone/neoskies/mixin/entity/ServerPlayerEntityMixin.java
index cb10979..bd04bf9 100644
--- a/src/main/java/com/awakenedredstone/neoskies/mixin/entity/ServerPlayerEntityMixin.java
+++ b/src/main/java/com/awakenedredstone/neoskies/mixin/entity/ServerPlayerEntityMixin.java
@@ -5,6 +5,7 @@
import com.awakenedredstone.neoskies.event.PlayerEvents;
import com.awakenedredstone.neoskies.util.Worlds;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
+import com.llamalad7.mixinextras.sugar.Local;
import com.mojang.authlib.GameProfile;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.PlayerEntity;
@@ -21,8 +22,6 @@
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.Redirect;
-import org.spongepowered.asm.mixin.injection.Slice;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ServerPlayerEntity.class)
@@ -73,6 +72,7 @@ public RegistryKey getTeleportTarget_redirectRegistryKey(RegistryKey worldChanged_redirectRegistryKey(RegistryKey world) {
return Worlds.redirect(world);
}
+
@ModifyExpressionValue(method = "createCommonPlayerSpawnInfo", at = @At(value = "INVOKE", target = "Lnet/minecraft/server/world/ServerWorld;getRegistryKey()Lnet/minecraft/registry/RegistryKey;"))
public RegistryKey createCommonPlayerSpawnInfo_redirectRegistryKey(RegistryKey world) {
return Worlds.redirect(world);
diff --git a/src/main/java/com/awakenedredstone/neoskies/util/Worlds.java b/src/main/java/com/awakenedredstone/neoskies/util/Worlds.java
index f0c57b6..2e4e96c 100644
--- a/src/main/java/com/awakenedredstone/neoskies/util/Worlds.java
+++ b/src/main/java/com/awakenedredstone/neoskies/util/Worlds.java
@@ -4,12 +4,19 @@
import com.awakenedredstone.neoskies.logic.Hub;
import com.awakenedredstone.neoskies.logic.Island;
import com.awakenedredstone.neoskies.logic.IslandLogic;
+import com.awakenedredstone.neoskies.mixin.entity.EntityAccessor;
+import com.awakenedredstone.neoskies.mixin.entity.ServerPlayerEntityAccessor;
import net.fabricmc.fabric.api.dimension.v1.FabricDimensions;
+import net.minecraft.entity.Entity;
+import net.minecraft.network.packet.s2c.play.DifficultyS2CPacket;
+import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket;
import net.minecraft.registry.RegistryKey;
import net.minecraft.server.network.ServerPlayerEntity;
+import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.TeleportTarget;
import net.minecraft.world.World;
+import net.minecraft.world.WorldProperties;
import java.util.Optional;
@@ -53,4 +60,36 @@ public static RegistryKey redirect(RegistryKey registryKey) {
return registryKey;
}
+ public static RegistryKey redirect(World world) {
+ return redirect(world.getRegistryKey());
+ }
+
+ public static void teleport(ServerPlayerEntity player, ServerWorld targetWorld, Vec3d pos, float yaw, float pitch) {
+ teleport(player, targetWorld, pos.x, pos.y, pos.z, yaw, pitch);
+ }
+
+ public static void teleport(ServerPlayerEntity player, ServerWorld targetWorld, double x, double y, double z, float yaw, float pitch) {
+ player.setCameraEntity(player);
+ player.stopRiding();
+ if (targetWorld == player.getWorld()) {
+ player.networkHandler.requestTeleport(x, y, z, yaw, pitch);
+ } else {
+ ServerWorld serverWorld = player.getServerWorld();
+ WorldProperties worldProperties = targetWorld.getLevelProperties();
+ if (!redirect(targetWorld).equals(redirect(player.getWorld()))) {
+ player.networkHandler.sendPacket(new PlayerRespawnS2CPacket(player.createCommonPlayerSpawnInfo(targetWorld), PlayerRespawnS2CPacket.KEEP_ALL));
+ }
+ player.networkHandler.sendPacket(new DifficultyS2CPacket(worldProperties.getDifficulty(), worldProperties.isDifficultyLocked()));
+ player.server.getPlayerManager().sendCommandTree(player);
+ serverWorld.removePlayer(player, Entity.RemovalReason.CHANGED_DIMENSION);
+ ((EntityAccessor) player).callUnsetRemoved();
+ player.refreshPositionAndAngles(x, y, z, yaw, pitch);
+ player.setServerWorld(targetWorld);
+ targetWorld.onPlayerTeleport(player);
+ ((ServerPlayerEntityAccessor) player).callWorldChanged(serverWorld);
+ player.networkHandler.requestTeleport(x, y, z, yaw, pitch);
+ player.server.getPlayerManager().sendWorldInfo(player, targetWorld);
+ player.server.getPlayerManager().sendPlayerStatus(player);
+ }
+ }
}
diff --git a/src/main/resources/data/neoskies/lang/en_us.json b/src/main/resources/data/neoskies/lang/en_us.json
index 321cca4..86d5ff5 100644
--- a/src/main/resources/data/neoskies/lang/en_us.json
+++ b/src/main/resources/data/neoskies/lang/en_us.json
@@ -125,7 +125,7 @@
"island_settings.neoskies.ride/others.description": "Allows players to ride mobs",
"island_settings.neoskies.leash/entity.description": "Allows players to leash entities",
"island_settings.neoskies.shear/entity.description": "Allows players to shear entities",
- "island_settings.neoskies.bucket/passive.description": "Allow players to bucket passive mobs",
+ "island_settings.neoskies.bucket/passive.description": "Allow players to bucket passive mobs",
"island_settings.neoskies.harvest.description": "Allows players to harvest crops",
"island_settings/selected": "> %level%",
diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json
index 90cf3fd..d1a1793 100644
--- a/src/main/resources/fabric.mod.json
+++ b/src/main/resources/fabric.mod.json
@@ -20,10 +20,18 @@
],
"cardinal-components": [
"com.awakenedredstone.neoskies.data.NeoSkiesComponents"
+ ],
+
+ "client": [
+ "com.awakenedredstone.neoskies.NeoSkiesClient"
]
},
"mixins": [
- "neoskies.mixins.json"
+ "neoskies.mixins.json",
+ {
+ "config": "neoskies.client.mixins.json",
+ "environment": "client"
+ }
],
"custom": {
"cardinal-components": [
diff --git a/src/main/resources/neoskies.mixins.json b/src/main/resources/neoskies.mixins.json
index 458e634..1b9021c 100644
--- a/src/main/resources/neoskies.mixins.json
+++ b/src/main/resources/neoskies.mixins.json
@@ -14,8 +14,10 @@
"compat.JanksonAcessor",
"compat.POJODeserializerAccessor",
"entity.DataTrackerAccessor",
+ "entity.EntityAccessor",
"entity.EntityMixin",
"entity.PlayerManagerMixin",
+ "entity.ServerPlayerEntityAccessor",
"entity.ServerPlayerEntityMixin",
"item.BoneMealItemMixin",
"item.ItemStackMixin",
diff --git a/src/testmod/resources/fabric.mod.json b/src/testmod/resources/fabric.mod.json
index 8b780b9..8699078 100644
--- a/src/testmod/resources/fabric.mod.json
+++ b/src/testmod/resources/fabric.mod.json
@@ -3,9 +3,11 @@
"id": "neoskies-test",
"version": "${version}",
"name": "NeoSkies TestMod",
- "description": "Library to work with server-side events with the ability to filter by event source",
- "authors": ["Nucleoid Contributors"],
- "license": "LGPLv3",
+ "description": "The testmod for NeoSkies | Also includes devtools",
+ "authors": [
+ "Awakened Redstone"
+ ],
+ "license": "LGPL-3.0",
"environment": "*",
"entrypoints": {
"main": [
diff --git a/src/testmodClient/java/com/awakenedredstone/neoskies/test/DebugDearImGuiEntrypoint.java b/src/testmodClient/java/com/awakenedredstone/neoskies/test/DebugDearImGuiEntrypoint.java
new file mode 100644
index 0000000..5393104
--- /dev/null
+++ b/src/testmodClient/java/com/awakenedredstone/neoskies/test/DebugDearImGuiEntrypoint.java
@@ -0,0 +1,17 @@
+package com.awakenedredstone.neoskies.test;
+
+import dev.deftu.imgui.DearImGuiEntrypoint;
+import dev.deftu.imgui.ImGuiRenderer;
+
+public class DebugDearImGuiEntrypoint implements DearImGuiEntrypoint {
+
+ @Override
+ public ImGuiRenderer createRenderer() {
+ return new DebugImGuiRenderer();
+ }
+
+ @Override
+ public void render() {
+ // Render ImGui here
+ }
+}
diff --git a/src/testmodClient/java/com/awakenedredstone/neoskies/test/DebugImGuiRenderer.java b/src/testmodClient/java/com/awakenedredstone/neoskies/test/DebugImGuiRenderer.java
new file mode 100644
index 0000000..5a80263
--- /dev/null
+++ b/src/testmodClient/java/com/awakenedredstone/neoskies/test/DebugImGuiRenderer.java
@@ -0,0 +1,176 @@
+package com.awakenedredstone.neoskies.test;
+
+import com.awakenedredstone.neoskies.logic.Island;
+import com.awakenedredstone.neoskies.logic.IslandLogic;
+import com.awakenedredstone.neoskies.util.MapBuilder;
+import com.awakenedredstone.neoskies.util.Texts;
+import com.awakenedredstone.neoskies.util.UnitConvertions;
+import dev.deftu.imgui.ImGuiRenderer;
+import imgui.ImGui;
+import imgui.extension.implot.ImPlot;
+import imgui.extension.implot.ImPlotContext;
+import imgui.extension.implot.flag.ImPlotStyleVar;
+import imgui.flag.ImGuiCond;
+import imgui.flag.ImGuiWindowFlags;
+import imgui.type.ImBoolean;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.text.Text;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class DebugImGuiRenderer implements ImGuiRenderer {
+ private static final ImPlotContext IMPLOT_CONTEXT;
+ public static final ImBoolean OPEN = new ImBoolean(true);
+ private static final Map MESSAGES = new HashMap<>();
+
+ static {
+ IMPLOT_CONTEXT = ImPlot.createContext();
+ }
+
+ public static int bufferLength = 600;
+ public static final List MEMORY = new LinkedList<>() {
+ @Override
+ public boolean add(Long aFloat) {
+ if (this.size() >= 10000) {
+ this.removeFirst();
+ }
+ return super.add(aFloat);
+ }
+ };
+
+ @Override
+ public void render() {
+ if (!OPEN.get()) return;
+
+ /*ImPlot.showDemoWindow(OPEN);
+ ImGui.showDemoWindow();*/
+
+ ImGui.setNextWindowSize(600, 750, ImGuiCond.FirstUseEver);
+ if (ImGui.begin("NeoSkies debug tools", OPEN, ImGuiWindowFlags.MenuBar)) {
+ if (MinecraftClient.getInstance().player != null && IslandLogic.getInstance() != null) {
+ ImGui.text(IslandLogic.getServer().getPlayerManager().getPlayer(MinecraftClient.getInstance().player.getUuid()).getWorld().getRegistryKey().getValue().toString());
+ ImGui.text(String.valueOf(MinecraftClient.getInstance().player.getWorld().getWorldBorder().getSize()));
+ }
+
+ if (ImGui.collapsingHeader("Server RAM")) {
+ final Float[] samples = new Float[bufferLength];
+ Arrays.fill(samples, 0f);
+
+ final Float[] samples2 = new Float[bufferLength];
+ for (int i = 0; i < samples.length; i++) {
+ try {
+ if (MEMORY.size() < samples.length && i < MEMORY.size()) {
+ samples[samples.length - MEMORY.size() + i] = (float) toMiB(MEMORY.get(i));
+ } else if (i < MEMORY.size()) {
+ samples[i] = (float) toMiB(MEMORY.get(Math.max(0, MEMORY.size() - bufferLength) + i));
+ }
+ samples2[i] = (float) i;
+ } catch (Throwable ignored) {}
+ }
+
+ if (ImGui.button("Clear")) {
+ MEMORY.clear();
+ }
+ ImGui.sameLine();
+
+ ImGui.text("Buffer size: %s".formatted(formatSeconds(bufferLength / 10)));
+ ImGui.sameLine();
+ if (ImGui.button("+")) {
+ if (Screen.hasShiftDown()) {
+ bufferLength += 100;
+ } else {
+ bufferLength += 10;
+ }
+ }
+ ImGui.sameLine();
+ if (ImGui.button("-")) {
+ if (Screen.hasShiftDown()) {
+ bufferLength -= 100;
+ } else {
+ bufferLength -= 10;
+ }
+ }
+
+ long maxMemory = Runtime.getRuntime().maxMemory();
+ long totalMemory = Runtime.getRuntime().totalMemory();
+ long freeMemory = Runtime.getRuntime().freeMemory();
+ long usedMemory = totalMemory - freeMemory;
+ ImGui.text("Memory: %s%% (%s/%sMiB)".formatted(usedMemory * 100L / maxMemory, toMiB(usedMemory), toMiB(maxMemory)));
+
+ ImPlot.setNextPlotLimits(0, bufferLength, 0, toMiB(maxMemory), ImGuiCond.Always);
+ if (ImPlot.beginPlot("Example Plot")) {
+ ImPlot.pushStyleVar(ImPlotStyleVar.FillAlpha, 0.25f);
+ try {
+ ImPlot.plotLine("Line", samples2, samples);
+ ImPlot.plotShaded("Line", samples2, samples, 0, bufferLength);
+ } catch (Throwable ignored) {}
+ ImPlot.endPlot();
+ }
+ }
+
+ if (ImGui.collapsingHeader("Islands")) {
+ if (MESSAGES.containsKey("island.info")) {
+ ImGui.text(MESSAGES.get("island.info").getString());
+ }
+
+ for (Island island : IslandLogic.getInstance().islands.stuck) {
+ if (ImGui.button("Scan " + island.hashCode())) { //Each button must have a unique id, and the id is the label ._.
+ if (island.isScanning()) {
+ MESSAGES.put("island.info", Texts.of("Can not queue a scan for an island that is already scanning!"));
+ } else {
+ MESSAGES.put("island.info", Texts.of("Scan queued"));
+
+ AtomicInteger total = new AtomicInteger();
+ IslandLogic.getInstance().islandScanner.queueScan(island, integer -> {
+ MESSAGES.put("island.info", Texts.of("Scanning %total% chunks", new MapBuilder.StringMap().putAny("total", integer).build()));
+ total.set(integer);
+ }, integer -> {
+ MESSAGES.put("island.info", Texts.of("Scanned %current%/%total% chunks", new MapBuilder.StringMap()
+ .putAny("total", total.get())
+ .putAny("current", integer)
+ .build()));
+ }, (timeTaken, scannedBlocks) -> {
+ MESSAGES.put("island.info", Texts.of("Scanned %total% blocks in %time%", new MapBuilder.StringMap()
+ .putAny("total", UnitConvertions.readableNumber(scannedBlocks.values().stream().mapToInt(value -> value).sum()))
+ .putAny("time", UnitConvertions.formatTimings(timeTaken))
+ .build()));
+ }, () -> {
+ MESSAGES.put("island.info", Texts.of("Island scan failed"));
+ });
+ }
+ }
+ ImGui.sameLine();
+ ImGui.text("%s: %s (%s)".formatted(island.owner.name, island.getIslandId(), island.getPoints()));
+ }
+ }
+
+ ImGui.end();
+ }
+ }
+
+ private static long toMiB(long bytes) {
+ return bytes / 1024L / 1024L;
+ }
+
+ private static String formatSeconds(int seconds) {
+ StringBuilder builder = new StringBuilder();
+ if (seconds >= 60) {
+ int minutes = seconds / 60;
+ if (minutes >= 60) {
+ int hours = minutes / 60;
+ builder.append(hours).append("h");
+ }
+ if (minutes % 60 > 0) {
+ builder.append(minutes % 60).append("m");
+ }
+ }
+
+ if (seconds % 60 > 0) {
+ builder.append(seconds % 60).append("s");
+ }
+
+ return builder.toString();
+ }
+}
diff --git a/src/testmodClient/java/com/awakenedredstone/neoskies/test/NeoSkiesTestClientMain.java b/src/testmodClient/java/com/awakenedredstone/neoskies/test/NeoSkiesTestClientMain.java
new file mode 100644
index 0000000..61e0ef7
--- /dev/null
+++ b/src/testmodClient/java/com/awakenedredstone/neoskies/test/NeoSkiesTestClientMain.java
@@ -0,0 +1,30 @@
+package com.awakenedredstone.neoskies.test;
+
+import net.fabricmc.api.ClientModInitializer;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandManager;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
+
+public final class NeoSkiesTestClientMain implements ClientModInitializer {
+ private static int tick = 0;
+
+ @Override
+ public void onInitializeClient() {
+ ClientTickEvents.END_CLIENT_TICK.register(client -> {
+ if (tick++ % 2 == 0) {
+ DebugImGuiRenderer.MEMORY.add((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
+ }
+ });
+
+ ClientCommandRegistrationCallback.EVENT.register((dispatcher, registryAccess) -> {
+ dispatcher.register(ClientCommandManager.literal("sbac")
+ .then(ClientCommandManager.literal("debug")
+ .executes(context -> {
+ DebugImGuiRenderer.OPEN.set(true);
+ return 0;
+ })
+ )
+ );
+ });
+ }
+}
diff --git a/src/testmodClient/resources/fabric.mod.json b/src/testmodClient/resources/fabric.mod.json
new file mode 100644
index 0000000..5a8525a
--- /dev/null
+++ b/src/testmodClient/resources/fabric.mod.json
@@ -0,0 +1,23 @@
+{
+ "schemaVersion": 1,
+ "id": "neoskies-test-client",
+ "version": "${version}",
+ "name": "NeoSkies TestMod (Client Edition)",
+ "description": "The testmod for NeoSkies (client edition) | Also includes devtools",
+ "authors": [
+ "Awakened Redstone"
+ ],
+ "license": "LGPL-3.0",
+ "environment": "client",
+ "mixins": [
+ "neoskies.test.client.mixins.json"
+ ],
+ "entrypoints": {
+ "client": [
+ "com.awakenedredstone.neoskies.test.NeoSkiesTestClientMain"
+ ],
+ "imgui": [
+ "com.awakenedredstone.neoskies.test.DebugDearImGuiEntrypoint"
+ ]
+ }
+}
diff --git a/src/testmodClient/resources/neoskies.test.client.mixins.json b/src/testmodClient/resources/neoskies.test.client.mixins.json
new file mode 100644
index 0000000..2f98377
--- /dev/null
+++ b/src/testmodClient/resources/neoskies.test.client.mixins.json
@@ -0,0 +1,11 @@
+{
+ "required": true,
+ "minVersion": "0.8",
+ "package": "com.awakenedredstone.neoskies.test.mixin",
+ "compatibilityLevel": "JAVA_17",
+ "mixins": [
+ ],
+ "injectors": {
+ "defaultRequire": 1
+ }
+}