Update fabric forge and neoforge implementations to 1.21

This commit is contained in:
Lukas Rieger (Blue) 2024-06-14 00:28:38 +02:00
parent 1c66536575
commit 0ec7de0df2
No known key found for this signature in database
GPG Key ID: AA33883B1BBA03E6
28 changed files with 1960 additions and 22 deletions

View File

@ -0,0 +1,177 @@
import com.matthewprenger.cursegradle.CurseArtifact
import com.matthewprenger.cursegradle.CurseProject
import com.matthewprenger.cursegradle.CurseRelation
import com.matthewprenger.cursegradle.Options
import net.fabricmc.loom.task.RemapJarTask
plugins {
java
`java-library`
id("com.diffplug.spotless") version "6.1.2"
id ("com.github.node-gradle.node") version "3.0.1"
id ("com.github.johnrengelman.shadow") version "8.1.1"
id ("fabric-loom") version "1.6-SNAPSHOT"
id ("com.modrinth.minotaur") version "2.+"
id ("com.matthewprenger.cursegradle") version "1.4.0"
}
group = "de.bluecolored.bluemap"
version = System.getProperty("bluemap.version") ?: "?" // set by BlueMapCore
val javaTarget = 21
java {
sourceCompatibility = JavaVersion.toVersion(javaTarget)
targetCompatibility = JavaVersion.toVersion(javaTarget)
withSourcesJar()
}
repositories {
mavenCentral()
maven ("https://libraries.minecraft.net")
maven ("https://maven.fabricmc.net/")
maven ("https://oss.sonatype.org/content/repositories/snapshots")
maven ("https://repo.bluecolored.de/releases")
}
val shadowInclude: Configuration by configurations.creating
configurations {
implementation.get().extendsFrom(shadowInclude)
}
dependencies {
shadowInclude ("de.bluecolored.bluemap:BlueMapCommon") {
//exclude dependencies provided by fabric
exclude (group = "com.google.guava", module = "guava")
exclude (group = "com.google.code.gson", module = "gson")
exclude (group = "com.mojang", module = "brigadier")
}
minecraft ("com.mojang:minecraft:1.20.5")
mappings ("net.fabricmc:yarn:1.20.5+build.1")
modImplementation ("net.fabricmc:fabric-loader:0.15.10")
modImplementation ("net.fabricmc.fabric-api:fabric-api:0.97.8+1.20.5")
modImplementation("me.lucko:fabric-permissions-api:0.1-SNAPSHOT")
testImplementation ("org.junit.jupiter:junit-jupiter:5.9.0")
testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.9.0")
}
spotless {
java {
target ("src/*/java/**/*.java")
licenseHeaderFile("../../HEADER")
indentWithSpaces()
trimTrailingWhitespace()
}
}
tasks.withType(JavaCompile::class).configureEach {
options.apply {
encoding = "utf-8"
}
}
tasks.withType(AbstractArchiveTask::class).configureEach {
isReproducibleFileOrder = true
isPreserveFileTimestamps = false
}
tasks.test {
useJUnitPlatform()
}
tasks.processResources {
inputs.property ("version", project.version)
filesMatching("fabric.mod.json") {
expand ("version" to project.version)
}
}
tasks.shadowJar {
configurations = listOf(shadowInclude)
//relocate ("com.flowpowered.math", "de.bluecolored.shadow.flowpowered.math") //DON"T relocate this, because the API depends on it
relocate ("com.typesafe.config", "de.bluecolored.shadow.typesafe.config")
relocate ("de.bluecolored.bluenbt", "de.bluecolored.shadow.bluenbt")
relocate ("org.spongepowered.configurate", "de.bluecolored.shadow.configurate")
relocate ("com.github.benmanes.caffeine", "de.bluecolored.shadow.benmanes.caffeine")
relocate ("org.aopalliance", "de.bluecolored.shadow.aopalliance")
relocate ("javax.inject", "de.bluecolored.shadow.javax.inject")
relocate ("org.checkerframework", "de.bluecolored.shadow.checkerframework")
relocate ("org.codehaus", "de.bluecolored.shadow.codehaus")
relocate ("io.leangen.geantyref", "de.bluecolored.shadow.geantyref")
relocate ("io.airlift", "de.bluecolored.shadow.airlift")
relocate ("net.jpountz", "de.bluecolored.shadow.jpountz")
relocate ("com.google.errorprone", "de.bluecolored.shadow.google.errorprone")
relocate ("com.google.inject", "de.bluecolored.shadow.google.inject")
relocate ("org.apache.commons.dbcp2", "de.bluecolored.shadow.apache.commons.dbcp2")
relocate ("org.apache.commons.logging", "de.bluecolored.shadow.apache.commons.logging")
relocate ("org.apache.commons.pool2", "de.bluecolored.shadow.apache.commons.pool2")
}
tasks.register("remappedShadowJar", type = RemapJarTask::class) {
destinationDirectory.set(file("../../build/release"))
archiveFileName.set("BlueMap-${project.version}-${project.name}.jar")
dependsOn (tasks.shadowJar)
inputFile.set(tasks.shadowJar.get().archiveFile)
addNestedDependencies.set(true)
}
tasks.register("release") {
dependsOn("remappedShadowJar")
}
modrinth {
token.set(System.getenv("MODRINTH_TOKEN"))
projectId.set("swbUV1cr")
versionNumber.set("${project.version}-${project.name}")
changelog.set(file("../../release.md")
.readText()
.replace("{version}", project.version.toString()))
uploadFile.set(tasks.findByName("remappedShadowJar"))
gameVersions.addAll("1.20.5", "1.20.6")
dependencies {
required.project("P7dR8mSH") // Fabric API
}
}
curseforge {
apiKey = System.getenv("CURSEFORGE_TOKEN") ?: ""
project(closureOf<CurseProject> {
id = "406463"
changelogType = "markdown"
changelog = file("../../release.md")
.readText()
.replace("{version}", project.version.toString())
releaseType = "release"
addGameVersion("Fabric")
addGameVersion("Java 21")
addGameVersion("1.20.5")
addGameVersion("1.20.6")
mainArtifact(tasks.findByName("remappedShadowJar"), closureOf<CurseArtifact> {
relations(closureOf<CurseRelation> {
requiredDependency("fabric-api")
})
})
})
options(closureOf<Options> {
javaVersionAutoDetect = false
javaIntegration = false
forgeGradleIntegration = false
})
}
tasks.register("publish") {
dependsOn("modrinth")
dependsOn("curseforge")
}

View File

@ -0,0 +1,13 @@
pluginManagement {
repositories {
maven {
name = "Fabric"
url = uri("https://maven.fabricmc.net/")
}
gradlePluginPortal()
}
}
rootProject.name = "fabric-1.20.5"
includeBuild("../../BlueMapCommon")

View File

@ -0,0 +1,90 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.fabric;
import com.flowpowered.math.vector.Vector3d;
import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.common.plugin.text.Text;
import de.bluecolored.bluemap.common.serverinterface.CommandSource;
import de.bluecolored.bluemap.common.serverinterface.ServerWorld;
import de.bluecolored.bluemap.core.world.World;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.registry.BuiltinRegistries;
import net.minecraft.registry.RegistryWrapper;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.util.math.Vec3d;
import java.util.Optional;
public class FabricCommandSource implements CommandSource {
private static final RegistryWrapper.WrapperLookup lookup = BuiltinRegistries.createWrapperLookup();
private final FabricMod mod;
private final Plugin plugin;
private final ServerCommandSource delegate;
public FabricCommandSource(FabricMod mod, Plugin plugin, ServerCommandSource delegate) {
this.mod = mod;
this.plugin = plugin;
this.delegate = delegate;
}
@Override
public void sendMessage(Text text) {
delegate.sendFeedback(
() -> net.minecraft.text.Text.Serialization
.fromJson(text.toJSONString(), lookup),
false
);
}
@Override
public boolean hasPermission(String permission) {
try {
Class.forName("me.lucko.fabric.api.permissions.v0.Permissions");
return Permissions.check(delegate, permission, 1);
} catch (ClassNotFoundException ex) {
return delegate.hasPermissionLevel(1);
}
}
@Override
public Optional<Vector3d> getPosition() {
Vec3d pos = delegate.getPosition();
if (pos != null) {
return Optional.of(new Vector3d(pos.x, pos.y, pos.z));
}
return Optional.empty();
}
@Override
public Optional<World> getWorld() {
ServerWorld serverWorld = mod.getServerWorld(delegate.getWorld());
return Optional.ofNullable(plugin.getWorld(serverWorld));
}
}

View File

@ -0,0 +1,76 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.fabric;
import de.bluecolored.bluemap.common.serverinterface.ServerEventListener;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
public class FabricEventForwarder {
private final FabricMod mod;
private final Collection<ServerEventListener> eventListeners;
public FabricEventForwarder(FabricMod mod) {
this.mod = mod;
this.eventListeners = new ArrayList<>(1);
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
this.onPlayerJoin(server, handler.getPlayer());
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
this.onPlayerLeave(server, handler.getPlayer());
});
}
public synchronized void addEventListener(ServerEventListener listener) {
this.eventListeners.add(listener);
}
public synchronized void removeAllListeners() {
this.eventListeners.clear();
}
public synchronized void onPlayerJoin(MinecraftServer server, ServerPlayerEntity player) {
if (this.mod.getServer() != server) return;
UUID uuid = player.getUuid();
for (ServerEventListener listener : eventListeners) listener.onPlayerJoin(uuid);
}
public synchronized void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player) {
if (this.mod.getServer() != server) return;
UUID uuid = player.getUuid();
for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid);
}
}

View File

@ -0,0 +1,241 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.fabric;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.common.plugin.commands.Commands;
import de.bluecolored.bluemap.common.serverinterface.Player;
import de.bluecolored.bluemap.common.serverinterface.Server;
import de.bluecolored.bluemap.common.serverinterface.ServerEventListener;
import de.bluecolored.bluemap.common.serverinterface.ServerWorld;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.logger.Logger;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.minecraft.SharedConstants;
import net.minecraft.registry.RegistryKey;
import net.minecraft.registry.RegistryKeys;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.world.World;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class FabricMod implements ModInitializer, Server {
private final Plugin pluginInstance;
private MinecraftServer serverInstance = null;
private final FabricEventForwarder eventForwarder;
private final LoadingCache<net.minecraft.server.world.ServerWorld, ServerWorld> worlds;
private int playerUpdateIndex = 0;
private final Map<UUID, Player> onlinePlayerMap;
private final List<FabricPlayer> onlinePlayerList;
public FabricMod() {
Logger.global.clear();
Logger.global.put(new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME)));
this.onlinePlayerMap = new ConcurrentHashMap<>();
this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
pluginInstance = new Plugin("fabric", this);
this.eventForwarder = new FabricEventForwarder(this);
this.worlds = Caffeine.newBuilder()
.executor(BlueMap.THREAD_POOL)
.weakKeys()
.maximumSize(1000)
.build(FabricWorld::new);
}
@Override
public void onInitialize() {
//register commands
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) ->
new Commands<>(pluginInstance, dispatcher, fabricSource ->
new FabricCommandSource(this, pluginInstance, fabricSource)
)
);
ServerLifecycleEvents.SERVER_STARTED.register((MinecraftServer server) -> {
this.serverInstance = server;
new Thread(()->{
Logger.global.logInfo("Loading BlueMap...");
try {
pluginInstance.load();
if (pluginInstance.isLoaded()) Logger.global.logInfo("BlueMap loaded!");
} catch (IOException e) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}, "BlueMap-Plugin-Loading").start();
});
ServerLifecycleEvents.SERVER_STOPPING.register((MinecraftServer server) -> {
pluginInstance.unload();
Logger.global.logInfo("BlueMap unloaded!");
});
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) -> {
this.onPlayerJoin(server, handler.getPlayer());
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) -> {
this.onPlayerLeave(server, handler.getPlayer());
});
ServerTickEvents.END_SERVER_TICK.register((MinecraftServer server) -> {
if (server == this.serverInstance) this.updateSomePlayers();
});
}
@Override
public String getMinecraftVersion() {
return SharedConstants.getGameVersion().getId();
}
@Override
public void registerListener(ServerEventListener listener) {
eventForwarder.addEventListener(listener);
}
@Override
public void unregisterAllListeners() {
eventForwarder.removeAllListeners();
}
@Override
public Collection<ServerWorld> getLoadedServerWorlds() {
Collection<ServerWorld> loadedWorlds = new ArrayList<>(3);
for (net.minecraft.server.world.ServerWorld serverWorld : serverInstance.getWorlds()) {
loadedWorlds.add(worlds.get(serverWorld));
}
return loadedWorlds;
}
@SuppressWarnings("unchecked")
@Override
public Optional<ServerWorld> getServerWorld(Object world) {
if (world instanceof String) {
Identifier identifier = Identifier.tryParse((String) world);
if (identifier != null) world = serverInstance.getWorld(RegistryKey.of(RegistryKeys.WORLD, identifier));
}
if (world instanceof RegistryKey) {
try {
world = serverInstance.getWorld((RegistryKey<World>) world);
} catch (ClassCastException ignored) {}
}
if (world instanceof net.minecraft.server.world.ServerWorld)
return Optional.of(getServerWorld((net.minecraft.server.world.ServerWorld) world));
return Optional.empty();
}
public ServerWorld getServerWorld(net.minecraft.server.world.ServerWorld serverWorld) {
return worlds.get(serverWorld);
}
@Override
public Path getConfigFolder() {
return Path.of("config", "bluemap");
}
@Override
public Optional<Path> getModsFolder() {
return Optional.of(Path.of("mods"));
}
public void onPlayerJoin(MinecraftServer server, ServerPlayerEntity playerInstance) {
if (this.serverInstance != server) return;
FabricPlayer player = new FabricPlayer(playerInstance, this);
onlinePlayerMap.put(player.getUuid(), player);
onlinePlayerList.add(player);
}
public void onPlayerLeave(MinecraftServer server, ServerPlayerEntity player) {
if (this.serverInstance != server) return;
UUID playerUUID = player.getUuid();
onlinePlayerMap.remove(playerUUID);
synchronized (onlinePlayerList) {
onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID));
}
}
public MinecraftServer getServer() {
return this.serverInstance;
}
public Plugin getPluginInstance() {
return pluginInstance;
}
@Override
public Collection<Player> getOnlinePlayers() {
return onlinePlayerMap.values();
}
/**
* Only update some of the online players each tick to minimize performance impact on the server-thread.
* Only call this method on the server-thread.
*/
private void updateSomePlayers() {
int onlinePlayerCount = onlinePlayerList.size();
if (onlinePlayerCount == 0) return;
int playersToBeUpdated = onlinePlayerCount / 20; //with 20 tps, each player is updated once a second
if (playersToBeUpdated == 0) playersToBeUpdated = 1;
for (int i = 0; i < playersToBeUpdated; i++) {
playerUpdateIndex++;
if (playerUpdateIndex >= 20 && playerUpdateIndex >= onlinePlayerCount) playerUpdateIndex = 0;
if (playerUpdateIndex < onlinePlayerCount) {
onlinePlayerList.get(playerUpdateIndex).update();
}
}
}
}

View File

@ -0,0 +1,157 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.fabric;
import com.flowpowered.math.vector.Vector3d;
import de.bluecolored.bluemap.common.plugin.text.Text;
import de.bluecolored.bluemap.common.serverinterface.Gamemode;
import de.bluecolored.bluemap.common.serverinterface.Player;
import de.bluecolored.bluemap.common.serverinterface.ServerWorld;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.effect.StatusEffects;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameMode;
import net.minecraft.world.LightType;
import java.util.EnumMap;
import java.util.Map;
import java.util.UUID;
public class FabricPlayer implements Player {
private static final Map<GameMode, Gamemode> GAMEMODE_MAP = new EnumMap<>(GameMode.class);
static {
GAMEMODE_MAP.put(GameMode.ADVENTURE, Gamemode.ADVENTURE);
GAMEMODE_MAP.put(GameMode.SURVIVAL, Gamemode.SURVIVAL);
GAMEMODE_MAP.put(GameMode.CREATIVE, Gamemode.CREATIVE);
GAMEMODE_MAP.put(GameMode.SPECTATOR, Gamemode.SPECTATOR);
}
private final UUID uuid;
private Text name;
private ServerWorld world;
private Vector3d position;
private Vector3d rotation;
private int skyLight;
private int blockLight;
private boolean sneaking;
private boolean invisible;
private Gamemode gamemode;
private final FabricMod mod;
public FabricPlayer(ServerPlayerEntity player, FabricMod mod) {
this.uuid = player.getUuid();
this.mod = mod;
update(player);
}
@Override
public UUID getUuid() {
return this.uuid;
}
@Override
public Text getName() {
return this.name;
}
@Override
public ServerWorld getWorld() {
return this.world;
}
@Override
public Vector3d getPosition() {
return this.position;
}
@Override
public Vector3d getRotation() {
return rotation;
}
@Override
public int getSkyLight() {
return skyLight;
}
@Override
public int getBlockLight() {
return blockLight;
}
@Override
public boolean isSneaking() {
return this.sneaking;
}
@Override
public boolean isInvisible() {
return this.invisible;
}
@Override
public Gamemode getGamemode() {
return this.gamemode;
}
/**
* Only call on server thread!
*/
public void update() {
MinecraftServer server = mod.getServer();
if (server == null) return;
ServerPlayerEntity player = server.getPlayerManager().getPlayer(uuid);
if (player == null) return;
update(player);
}
public void update(ServerPlayerEntity player) {
this.gamemode = GAMEMODE_MAP.get(player.interactionManager.getGameMode());
if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL;
StatusEffectInstance invis = player.getStatusEffect(StatusEffects.INVISIBILITY);
this.invisible = invis != null && invis.getDuration() > 0;
this.name = Text.of(player.getName().getString());
Vec3d pos = player.getPos();
this.position = new Vector3d(pos.getX(), pos.getY(), pos.getZ());
this.rotation = new Vector3d(player.getPitch(), player.getHeadYaw(), 0);
this.sneaking = player.isSneaking();
this.skyLight = player.getWorld().getLightingProvider().get(LightType.SKY).getLightLevel(player.getBlockPos());
this.blockLight = player.getWorld().getLightingProvider().get(LightType.BLOCK).getLightLevel(player.getBlockPos());
this.world = mod.getServerWorld(player.getServerWorld());
}
}

View File

@ -0,0 +1,110 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.fabric;
import de.bluecolored.bluemap.common.serverinterface.ServerWorld;
import de.bluecolored.bluemap.core.util.Key;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.Identifier;
import net.minecraft.util.WorldSavePath;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
public class FabricWorld implements ServerWorld {
private final WeakReference<net.minecraft.server.world.ServerWorld> delegate;
private final Path worldFolder;
private final Key dimension;
public FabricWorld(net.minecraft.server.world.ServerWorld delegate) {
this.delegate = new WeakReference<>(delegate);
MinecraftServer server = delegate.getServer();
this.worldFolder = delegate.getServer().getRunDirectory().toPath()
.resolve(server.getSavePath(WorldSavePath.ROOT));
Identifier id = delegate.getRegistryKey().getValue();
this.dimension = new Key(id.getNamespace(), id.getPath());
}
@Override
public boolean persistWorldChanges() throws IOException {
net.minecraft.server.world.ServerWorld world = delegate.get();
if (world == null) return false;
var taskResult = CompletableFuture.supplyAsync(() -> {
try {
world.save(null, true, false);
return true;
} catch (Exception e) {
throw new CompletionException(e);
}
}, world.getServer());
try {
return taskResult.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException(e);
} catch (ExecutionException e) {
Throwable t = e.getCause();
if (t instanceof IOException) throw (IOException) t;
if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t;
throw new IOException(t);
}
}
@Override
public Path getWorldFolder() {
return worldFolder;
}
@Override
public Key getDimension() {
return dimension;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FabricWorld that = (FabricWorld) o;
Object world = delegate.get();
return world != null && world.equals(that.delegate.get());
}
@Override
public int hashCode() {
Object world = delegate.get();
return world != null ? world.hashCode() : 0;
}
}

View File

@ -0,0 +1,69 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.fabric;
import org.apache.logging.log4j.Logger;
import de.bluecolored.bluemap.core.logger.AbstractLogger;
public class Log4jLogger extends AbstractLogger {
private final Logger out;
public Log4jLogger(Logger out) {
this.out = out;
}
@Override
public void logError(String message, Throwable throwable) {
out.error(message, throwable);
}
@Override
public void logWarning(String message) {
out.warn(message);
}
@Override
public void logInfo(String message) {
out.info(message);
}
@Override
public void logDebug(String message) {
if (out.isDebugEnabled()) out.debug(message);
}
@Override
public void noFloodDebug(String message) {
if (out.isDebugEnabled()) super.noFloodDebug(message);
}
@Override
public void noFloodDebug(String key, String message) {
if (out.isDebugEnabled()) super.noFloodDebug(key, message);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -0,0 +1,33 @@
{
"schemaVersion": 1,
"id": "bluemap",
"version": "${version}",
"name": "BlueMap",
"description": "A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)",
"authors": [
"Blue (TBlueF, Lukas Rieger)"
],
"contact": {
"homepage": "https://github.com/BlueMap-Minecraft",
"sources": "https://github.com/BlueMap-Minecraft/BlueMap"
},
"license": "MIT",
"icon": "assets/bluemap/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"de.bluecolored.bluemap.fabric.FabricMod"
]
},
"depends": {
"fabricloader": ">=0.15.10",
"fabric": "*",
"fabric-api-base": "*",
"minecraft": "*"
},
"suggests": {}
}

View File

@ -48,10 +48,10 @@ dependencies {
exclude (group = "com.mojang", module = "brigadier")
}
minecraft ("com.mojang:minecraft:1.20.5")
mappings ("net.fabricmc:yarn:1.20.5+build.1")
modImplementation ("net.fabricmc:fabric-loader:0.15.10")
modImplementation ("net.fabricmc.fabric-api:fabric-api:0.97.8+1.20.5")
minecraft ("com.mojang:minecraft:1.21")
mappings ("net.fabricmc:yarn:1.21+build.1")
modImplementation ("net.fabricmc:fabric-loader:0.15.11")
modImplementation ("net.fabricmc.fabric-api:fabric-api:0.100.1+1.21")
modImplementation("me.lucko:fabric-permissions-api:0.1-SNAPSHOT")
testImplementation ("org.junit.jupiter:junit-jupiter:5.9.0")
@ -135,7 +135,7 @@ modrinth {
.readText()
.replace("{version}", project.version.toString()))
uploadFile.set(tasks.findByName("remappedShadowJar"))
gameVersions.addAll("1.20.5", "1.20.6")
gameVersions.addAll("1.21")
dependencies {
required.project("P7dR8mSH") // Fabric API
}
@ -155,8 +155,7 @@ curseforge {
addGameVersion("Java 21")
addGameVersion("1.20.5")
addGameVersion("1.20.6")
addGameVersion("1.21")
mainArtifact(tasks.findByName("remappedShadowJar"), closureOf<CurseArtifact> {
relations(closureOf<CurseRelation> {

View File

@ -47,7 +47,7 @@ public FabricWorld(net.minecraft.server.world.ServerWorld delegate) {
this.delegate = new WeakReference<>(delegate);
MinecraftServer server = delegate.getServer();
this.worldFolder = delegate.getServer().getRunDirectory().toPath()
this.worldFolder = delegate.getServer().getRunDirectory()
.resolve(server.getSavePath(WorldSavePath.ROOT));
Identifier id = delegate.getRegistryKey().getValue();

View File

@ -0,0 +1,195 @@
buildscript {
repositories {
// These repositories are only for Gradle plugins, put any other repositories in the repository block further below
maven { url = 'https://maven.minecraftforge.net' }
mavenCentral()
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '[6.0.24,6.2)', changing: true
}
}
plugins {
id "java"
id "java-library"
id "com.diffplug.spotless" version "6.1.2"
id "com.github.node-gradle.node" version "3.0.1"
id "com.github.johnrengelman.shadow" version "8.1.1"
id "com.modrinth.minotaur" version "2.+"
id "com.matthewprenger.cursegradle" version "1.4.0"
}
apply plugin: "net.minecraftforge.gradle"
group = "de.bluecolored.bluemap"
version = System.getProperty("bluemap.version") ?: "?" // set by BlueMapCore
archivesBaseName = 'bluemap'
java.toolchain.languageVersion = JavaLanguageVersion.of(21)
minecraft {
mappings channel: 'official', version: '1.20.6'
reobf = false
runs {
server {
workingDirectory project.file('run')
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
mods {
bluemap {
source sourceSets.main
}
}
}
}
}
sourceSets.main.resources { srcDir 'src/generated/resources' }
repositories {
mavenCentral()
maven { url = "https://libraries.minecraft.net" }
maven { url = "https://repo.bluecolored.de/releases" }
}
configurations {
implementation.extendsFrom(shadowInclude)
}
dependencies {
minecraft 'net.minecraftforge:forge:1.20.6-50.0.13'
shadowInclude ("de.bluecolored.bluemap:BlueMapCommon") {
//exclude dependencies provided by forge
exclude (group: "com.google.guava", module: "guava")
exclude (group: "com.google.code.gson", module: "gson")
exclude (group: "com.mojang", module: "brigadier")
}
testImplementation ("org.junit.jupiter:junit-jupiter:5.8.2")
testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.8.2")
}
spotless {
java {
target ("src/*/java/**/*.java")
licenseHeaderFile("../../HEADER")
indentWithSpaces()
trimTrailingWhitespace()
}
}
jar {
manifest {
attributes([
"Specification-Title" : "bluemap",
"Specification-Vendor" : "bluemap",
"Specification-Version" : "1", // We are version 1 of ourselves
"Implementation-Title" : project.name,
"Implementation-Version" : project.jar.archiveVersion,
"Implementation-Vendor" : "bluemap",
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
])
}
}
tasks.withType(JavaCompile).configureEach {
options.encoding = "utf-8"
}
tasks.withType(AbstractArchiveTask).configureEach {
setReproducibleFileOrder(true)
setPreserveFileTimestamps(false)
}
test {
useJUnitPlatform()
}
shadowJar {
def version = System.getProperty("bluemap.version") ?: "" // set by BlueMapCore
destinationDirectory.set(file("../../build/release"))
archiveFileName.set("BlueMap-${project.version}-${project.name}.jar")
configurations = [project.configurations.shadowInclude]
//relocate ("com.flowpowered.math", "de.bluecolored.shadow.flowpowered.math") //DON"T relocate this, because the API depends on it
relocate ("com.typesafe.config", "de.bluecolored.shadow.typesafe.config")
relocate ("de.bluecolored.bluenbt", "de.bluecolored.shadow.bluenbt")
relocate ("org.spongepowered.configurate", "de.bluecolored.shadow.configurate")
relocate ("com.github.benmanes.caffeine", "de.bluecolored.shadow.benmanes.caffeine")
relocate ("org.aopalliance", "de.bluecolored.shadow.aopalliance")
relocate ("javax.inject", "de.bluecolored.shadow.javax.inject")
relocate ("org.checkerframework", "de.bluecolored.shadow.checkerframework")
relocate ("org.codehaus", "de.bluecolored.shadow.codehaus")
relocate ("io.leangen.geantyref", "de.bluecolored.shadow.geantyref")
relocate ("io.airlift", "de.bluecolored.shadow.airlift")
relocate ("net.jpountz", "de.bluecolored.shadow.jpountz")
relocate ("com.google.errorprone", "de.bluecolored.shadow.google.errorprone")
relocate ("com.google.inject", "de.bluecolored.shadow.google.inject")
relocate ("org.apache.commons.dbcp2", "de.bluecolored.shadow.apache.commons.dbcp2")
relocate ("org.apache.commons.logging", "de.bluecolored.shadow.apache.commons.logging")
relocate ("org.apache.commons.pool2", "de.bluecolored.shadow.apache.commons.pool2")
}
processResources {
from(sourceSets.main.resources.srcDirs) {
include 'mcmod.info','META-INF/mods.toml'
duplicatesStrategy = DuplicatesStrategy.WARN
expand (
version: project.version
)
}
}
task release {
dependsOn(shadowJar)
}
modrinth {
token = System.getenv("MODRINTH_TOKEN")
projectId = "swbUV1cr"
versionNumber = "${project.version}-${project.name}"
changelog = file("../../release.md")
.getText()
.replace("{version}", project.version.toString())
uploadFile = shadowJar
gameVersions = ["1.20.6"]
}
curseforge {
apiKey = System.getenv("CURSEFORGE_TOKEN") ?: ""
project {
id = "406463"
changelogType = "markdown"
changelog = file("../../release.md")
.getText()
.replace("{version}", project.version.toString())
releaseType = "release"
addGameVersion "Forge"
addGameVersion "Java 21"
addGameVersion "1.20.6"
mainArtifact shadowJar
}
options {
javaVersionAutoDetect = false
javaIntegration = false
forgeGradleIntegration = false
}
}
task publish {
dependsOn(tasks.findByName("modrinth"))
dependsOn(tasks.findByName("curseforge"))
}

View File

@ -0,0 +1,3 @@
rootProject.name = "forge-1.20.6"
includeBuild("../../BlueMapCommon")

View File

@ -0,0 +1,78 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.forge;
import com.flowpowered.math.vector.Vector3d;
import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.common.plugin.text.Text;
import de.bluecolored.bluemap.common.serverinterface.CommandSource;
import de.bluecolored.bluemap.common.serverinterface.ServerWorld;
import de.bluecolored.bluemap.core.world.World;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.registries.VanillaRegistries;
import net.minecraft.network.chat.Component;
import java.util.Optional;
public class ForgeCommandSource implements CommandSource {
private static final HolderLookup.Provider lookup = VanillaRegistries.createLookup();
private final ForgeMod mod;
private final Plugin plugin;
private final CommandSourceStack delegate;
public ForgeCommandSource(ForgeMod mod, Plugin plugin, CommandSourceStack delegate) {
this.mod = mod;
this.plugin = plugin;
this.delegate = delegate;
}
@Override
public void sendMessage(Text text) {
var component = Component.Serializer.fromJsonLenient(text.toJSONString(), lookup);
if (component != null)
delegate.sendSuccess(() -> component, false);
}
@Override
public boolean hasPermission(String permission) {
return delegate.hasPermission(1);
}
@Override
public Optional<Vector3d> getPosition() {
var pos = delegate.getPosition();
return Optional.of(new Vector3d(pos.x, pos.y, pos.z));
}
@Override
public Optional<World> getWorld() {
ServerWorld serverWorld = mod.getServerWorld(delegate.getLevel());
return Optional.ofNullable(plugin.getWorld(serverWorld));
}
}

View File

@ -0,0 +1,67 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.forge;
import de.bluecolored.bluemap.common.serverinterface.ServerEventListener;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.UUID;
public class ForgeEventForwarder {
private final Collection<ServerEventListener> eventListeners;
public ForgeEventForwarder() {
this.eventListeners = new ArrayList<>(1);
MinecraftForge.EVENT_BUS.register(this);
}
public synchronized void addEventListener(ServerEventListener listener) {
this.eventListeners.add(listener);
}
public synchronized void removeAllListeners() {
this.eventListeners.clear();
}
@SubscribeEvent
public synchronized void onPlayerJoin(PlayerLoggedInEvent evt) {
UUID uuid = evt.getEntity().getUUID();
for (ServerEventListener listener : eventListeners) listener.onPlayerJoin(uuid);
}
@SubscribeEvent
public synchronized void onPlayerLeave(PlayerLoggedOutEvent evt) {
UUID uuid = evt.getEntity().getUUID();
for (ServerEventListener listener : eventListeners) listener.onPlayerLeave(uuid);
}
}

View File

@ -0,0 +1,262 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.forge;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import de.bluecolored.bluemap.common.plugin.Plugin;
import de.bluecolored.bluemap.common.plugin.commands.Commands;
import de.bluecolored.bluemap.common.serverinterface.Player;
import de.bluecolored.bluemap.common.serverinterface.Server;
import de.bluecolored.bluemap.common.serverinterface.ServerEventListener;
import de.bluecolored.bluemap.common.serverinterface.ServerWorld;
import de.bluecolored.bluemap.core.BlueMap;
import de.bluecolored.bluemap.core.logger.Logger;
import net.minecraft.SharedConstants;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.Level;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent.ServerTickEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedInEvent;
import net.minecraftforge.event.entity.player.PlayerEvent.PlayerLoggedOutEvent;
import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.event.server.ServerStartingEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.IExtensionPoint;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.common.Mod;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Mod(Plugin.PLUGIN_ID)
public class ForgeMod implements Server {
private final Plugin pluginInstance;
private MinecraftServer serverInstance = null;
private final ForgeEventForwarder eventForwarder;
private final LoadingCache<ServerLevel, ServerWorld> worlds;
private int playerUpdateIndex = 0;
private final Map<UUID, Player> onlinePlayerMap;
private final List<ForgePlayer> onlinePlayerList;
public ForgeMod() {
Logger.global.clear();
Logger.global.put(new Log4jLogger(LogManager.getLogger(Plugin.PLUGIN_NAME)));
this.onlinePlayerMap = new ConcurrentHashMap<>();
this.onlinePlayerList = Collections.synchronizedList(new ArrayList<>());
this.pluginInstance = new Plugin("forge", this);
this.eventForwarder = new ForgeEventForwarder();
this.worlds = Caffeine.newBuilder()
.executor(BlueMap.THREAD_POOL)
.weakKeys()
.maximumSize(1000)
.build(ForgeWorld::new);
MinecraftForge.EVENT_BUS.register(this);
//Make sure the mod being absent on the other network side does not cause the client to display the server as incompatible
ModLoadingContext.get().registerExtensionPoint(
IExtensionPoint.DisplayTest.class,
() -> new IExtensionPoint.DisplayTest(
() -> IExtensionPoint.DisplayTest.IGNORESERVERONLY,
(a, b) -> true
)
);
}
@SubscribeEvent
public void onServerStarting(ServerStartingEvent event) {
this.serverInstance = event.getServer();
}
@SubscribeEvent
public void onRegisterCommands(RegisterCommandsEvent event) {
//register commands
new Commands<>(pluginInstance, event.getDispatcher(), forgeSource ->
new ForgeCommandSource(this, pluginInstance, forgeSource)
);
}
@SubscribeEvent
public void onServerStarted(ServerStartedEvent event) {
//save worlds to generate level.dat files
serverInstance.saveAllChunks(false, true, true);
new Thread(() -> {
Logger.global.logInfo("Loading...");
try {
pluginInstance.load();
if (pluginInstance.isLoaded()) Logger.global.logInfo("Loaded!");
} catch (IOException e) {
Logger.global.logError("Failed to load bluemap!", e);
pluginInstance.unload();
}
}, "BlueMap-Plugin-Loading").start();
}
@SubscribeEvent
public void onServerStopping(ServerStoppingEvent event) {
pluginInstance.unload();
Logger.global.logInfo("BlueMap unloaded!");
}
@SubscribeEvent
public void onTick(ServerTickEvent evt) {
updateSomePlayers();
}
@Override
public String getMinecraftVersion() {
return SharedConstants.getCurrentVersion().getId();
}
@Override
public void registerListener(ServerEventListener listener) {
eventForwarder.addEventListener(listener);
}
@Override
public void unregisterAllListeners() {
eventForwarder.removeAllListeners();
}
@Override
public Collection<ServerWorld> getLoadedServerWorlds() {
Collection<ServerWorld> loadedWorlds = new ArrayList<>(3);
for (ServerLevel serverWorld : serverInstance.getAllLevels()) {
loadedWorlds.add(worlds.get(serverWorld));
}
return loadedWorlds;
}
@SuppressWarnings("unchecked")
@Override
public Optional<ServerWorld> getServerWorld(Object world) {
if (world instanceof String) {
ResourceLocation resourceLocation = ResourceLocation.tryParse((String) world);
if (resourceLocation != null) world = serverInstance.getLevel(ResourceKey.create(Registries.DIMENSION, resourceLocation));
}
if (world instanceof ResourceKey) {
try {
world = serverInstance.getLevel((ResourceKey<Level>) world);
} catch (ClassCastException ignored) {}
}
if (world instanceof ServerLevel)
return Optional.of(getServerWorld((ServerLevel) world));
return Optional.empty();
}
public ServerWorld getServerWorld(ServerLevel world) {
return worlds.get(world);
}
@Override
public Path getConfigFolder() {
return Path.of("config", "bluemap");
}
@Override
public Optional<Path> getModsFolder() {
return Optional.of(Path.of("mods"));
}
@SubscribeEvent
public void onPlayerJoin(PlayerLoggedInEvent evt) {
var playerInstance = evt.getEntity();
if (!(playerInstance instanceof ServerPlayer)) return;
ForgePlayer player = new ForgePlayer(playerInstance.getUUID(), this);
onlinePlayerMap.put(player.getUuid(), player);
onlinePlayerList.add(player);
}
@SubscribeEvent
public void onPlayerLeave(PlayerLoggedOutEvent evt) {
var player = evt.getEntity();
if (!(player instanceof ServerPlayer)) return;
UUID playerUUID = player.getUUID();
onlinePlayerMap.remove(playerUUID);
synchronized (onlinePlayerList) {
onlinePlayerList.removeIf(p -> p.getUuid().equals(playerUUID));
}
}
public MinecraftServer getServer() {
return this.serverInstance;
}
public Plugin getPlugin() {
return this.pluginInstance;
}
@Override
public Collection<Player> getOnlinePlayers() {
return onlinePlayerMap.values();
}
/**
* Only update some of the online players each tick to minimize performance impact on the server-thread.
* Only call this method on the server-thread.
*/
private void updateSomePlayers() {
int onlinePlayerCount = onlinePlayerList.size();
if (onlinePlayerCount == 0) return;
int playersToBeUpdated = onlinePlayerCount / 20; //with 20 tps, each player is updated once a second
if (playersToBeUpdated == 0) playersToBeUpdated = 1;
for (int i = 0; i < playersToBeUpdated; i++) {
playerUpdateIndex++;
if (playerUpdateIndex >= 20 && playerUpdateIndex >= onlinePlayerCount) playerUpdateIndex = 0;
if (playerUpdateIndex < onlinePlayerCount) {
onlinePlayerList.get(playerUpdateIndex).update();
}
}
}
}

View File

@ -0,0 +1,154 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.forge;
import com.flowpowered.math.vector.Vector3d;
import de.bluecolored.bluemap.common.plugin.text.Text;
import de.bluecolored.bluemap.common.serverinterface.Gamemode;
import de.bluecolored.bluemap.common.serverinterface.Player;
import de.bluecolored.bluemap.common.serverinterface.ServerWorld;
import net.minecraft.core.BlockPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.phys.Vec3;
import java.util.EnumMap;
import java.util.Map;
import java.util.UUID;
public class ForgePlayer implements Player {
private static final Map<GameType, Gamemode> GAMEMODE_MAP = new EnumMap<>(GameType.class);
static {
GAMEMODE_MAP.put(GameType.ADVENTURE, Gamemode.ADVENTURE);
GAMEMODE_MAP.put(GameType.SURVIVAL, Gamemode.SURVIVAL);
GAMEMODE_MAP.put(GameType.CREATIVE, Gamemode.CREATIVE);
GAMEMODE_MAP.put(GameType.SPECTATOR, Gamemode.SPECTATOR);
}
private final UUID uuid;
private Text name;
private ServerWorld world;
private Vector3d position;
private Vector3d rotation;
private int skyLight;
private int blockLight;
private boolean sneaking;
private boolean invisible;
private Gamemode gamemode;
private final ForgeMod mod;
public ForgePlayer(UUID playerUuid, ForgeMod mod) {
this.uuid = playerUuid;
this.mod = mod;
update();
}
@Override
public UUID getUuid() {
return this.uuid;
}
@Override
public Text getName() {
return this.name;
}
@Override
public ServerWorld getWorld() {
return this.world;
}
@Override
public Vector3d getPosition() {
return this.position;
}
@Override
public Vector3d getRotation() {
return rotation;
}
@Override
public int getSkyLight() {
return skyLight;
}
@Override
public int getBlockLight() {
return blockLight;
}
@Override
public boolean isSneaking() {
return this.sneaking;
}
@Override
public boolean isInvisible() {
return this.invisible;
}
@Override
public Gamemode getGamemode() {
return this.gamemode;
}
/**
* Only call on server thread!
*/
public void update() {
MinecraftServer server = mod.getServer();
if (server == null) return;
ServerPlayer player = server.getPlayerList().getPlayer(uuid);
if (player == null) return;
this.gamemode = GAMEMODE_MAP.getOrDefault(player.gameMode.getGameModeForPlayer(), Gamemode.SURVIVAL);
if (this.gamemode == null) this.gamemode = Gamemode.SURVIVAL;
MobEffectInstance invis = player.getEffect(MobEffects.INVISIBILITY);
this.invisible = invis != null && invis.getDuration() > 0;
this.name = Text.of(player.getName().getString());
Vec3 pos = player.getPosition(1f);
this.position = new Vector3d(pos.x(), pos.y(), pos.z());
this.rotation = new Vector3d(player.getXRot(), player.getYHeadRot(), 0);
this.sneaking = player.isCrouching();
this.skyLight = player.level().getChunkSource().getLightEngine().getLayerListener(LightLayer.SKY).getLightValue(new BlockPos(player.getBlockX(), player.getBlockY(), player.getBlockZ()));
this.blockLight = player.level().getChunkSource().getLightEngine().getLayerListener(LightLayer.BLOCK).getLightValue(new BlockPos(player.getBlockX(), player.getBlockY(), player.getBlockZ()));
this.world = mod.getServerWorld(player.serverLevel());
}
}

View File

@ -0,0 +1,111 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.forge;
import de.bluecolored.bluemap.common.serverinterface.ServerWorld;
import de.bluecolored.bluemap.core.util.Key;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.storage.LevelResource;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
public class ForgeWorld implements ServerWorld {
private final WeakReference<ServerLevel> delegate;
private final Path worldFolder;
private final Key dimension;
public ForgeWorld(ServerLevel delegate) {
this.delegate = new WeakReference<>(delegate);
MinecraftServer server = delegate.getServer();
this.worldFolder = delegate.getServer().getServerDirectory().toPath()
.resolve(server.getWorldPath(LevelResource.ROOT));
ResourceLocation id = delegate.dimension().location();
this.dimension = new Key(id.getNamespace(), id.getPath());
}
@Override
public boolean persistWorldChanges() throws IOException {
ServerLevel world = delegate.get();
if (world == null) return false;
var taskResult = CompletableFuture.supplyAsync(() -> {
try {
world.save(null, true, false);
return true;
} catch (Exception e) {
throw new CompletionException(e);
}
}, world.getServer());
try {
return taskResult.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException(e);
} catch (ExecutionException e) {
Throwable t = e.getCause();
if (t instanceof IOException) throw (IOException) t;
if (t instanceof IllegalArgumentException) throw (IllegalArgumentException) t;
throw new IOException(t);
}
}
@Override
public Path getWorldFolder() {
return worldFolder;
}
@Override
public Key getDimension() {
return dimension;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ForgeWorld that = (ForgeWorld) o;
Object world = delegate.get();
return world != null && world.equals(that.delegate.get());
}
@Override
public int hashCode() {
Object world = delegate.get();
return world != null ? world.hashCode() : 0;
}
}

View File

@ -0,0 +1,69 @@
/*
* This file is part of BlueMap, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluemap.forge;
import org.apache.logging.log4j.Logger;
import de.bluecolored.bluemap.core.logger.AbstractLogger;
public class Log4jLogger extends AbstractLogger {
private final Logger out;
public Log4jLogger(Logger out) {
this.out = out;
}
@Override
public void logError(String message, Throwable throwable) {
out.error(message, throwable);
}
@Override
public void logWarning(String message) {
out.warn(message);
}
@Override
public void logInfo(String message) {
out.info(message);
}
@Override
public void logDebug(String message) {
if (out.isDebugEnabled()) out.debug(message);
}
@Override
public void noFloodDebug(String message) {
if (out.isDebugEnabled()) super.noFloodDebug(message);
}
@Override
public void noFloodDebug(String key, String message) {
if (out.isDebugEnabled()) super.noFloodDebug(key, message);
}
}

View File

@ -0,0 +1,26 @@
modLoader="javafml"
loaderVersion="[44,)"
license="MIT"
issueTrackerURL="https://github.com/BlueMap-Minecraft/BlueMap/issues"
[[mods]]
modId="bluemap"
version="${version}"
displayName="BlueMap"
displayURL="https://github.com/BlueMap-Minecraft/BlueMap"
authors="Blue (TBlueF, Lukas Rieger)"
description='''
A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)
'''
[[dependencies.bluemap]]
modId="forge"
mandatory=true
versionRange="[50,)"
ordering="NONE"
side="SERVER"
[[dependencies.bluemap]]
modId="minecraft"
mandatory=true
versionRange="[1.20.6,)"
ordering="NONE"
side="SERVER"

View File

@ -0,0 +1,6 @@
{
"pack": {
"description": "BlueMap - A 3d-map of your Minecraft worlds view-able in your browser using three.js (WebGL)",
"pack_format": 8
}
}

View File

@ -28,7 +28,7 @@ archivesBaseName = 'bluemap'
java.toolchain.languageVersion = JavaLanguageVersion.of(21)
minecraft {
mappings channel: 'official', version: '1.20.6'
mappings channel: 'official', version: '1.21'
reobf = false
@ -60,7 +60,7 @@ configurations {
}
dependencies {
minecraft 'net.minecraftforge:forge:1.20.6-50.0.13'
minecraft 'net.minecraftforge:forge:1.21-51.0.1'
shadowInclude ("de.bluecolored.bluemap:BlueMapCommon") {
//exclude dependencies provided by forge
@ -161,7 +161,7 @@ modrinth {
.getText()
.replace("{version}", project.version.toString())
uploadFile = shadowJar
gameVersions = ["1.20.6"]
gameVersions = ["1.21"]
}
curseforge {
@ -178,7 +178,7 @@ curseforge {
addGameVersion "Java 21"
addGameVersion "1.20.6"
addGameVersion "1.21"
mainArtifact shadowJar
}

View File

@ -48,7 +48,7 @@ public ForgeWorld(ServerLevel delegate) {
this.delegate = new WeakReference<>(delegate);
MinecraftServer server = delegate.getServer();
this.worldFolder = delegate.getServer().getServerDirectory().toPath()
this.worldFolder = delegate.getServer().getServerDirectory()
.resolve(server.getWorldPath(LevelResource.ROOT));
ResourceLocation id = delegate.dimension().location();

View File

@ -1,7 +1,7 @@
plugins {
id "java"
id "java-library"
id "net.neoforged.gradle.userdev" version '7.0.120'
id "net.neoforged.gradle.userdev" version '7.0.142'
id "com.diffplug.spotless" version "6.1.2"
id "com.github.node-gradle.node" version "3.0.1"
id "com.modrinth.minotaur" version "2.+"
@ -127,7 +127,7 @@ modrinth {
.replace("{version}", project.version.toString())
uploadFile = shadowJar
loaders = ["neoforge"]
gameVersions = ["1.20.6"]
gameVersions = ["1.21"]
}
curseforge {
@ -144,7 +144,7 @@ curseforge {
addGameVersion "Java 21"
addGameVersion "1.20.6"
addGameVersion "1.21"
mainArtifact shadowJar
}

View File

@ -4,11 +4,11 @@ org.gradle.debug=false
neogradle.subsystems.parchment.minecraftVersion=1.20.6
neogradle.subsystems.parchment.mappingsVersion=2024.05.01
minecraft_version=1.20.6
minecraft_version_range=[1.20.6,1.21)
neo_version=20.6.43-beta
neo_version_range=[20.5,)
loader_version_range=[2,)
minecraft_version=1.21
minecraft_version_range=[1.21,)
neo_version=21.0.0-beta
neo_version_range=[21.0.0-beta,)
loader_version_range=[4,)
mod_id=bluemap
mod_name=BlueMap

View File

@ -48,7 +48,7 @@ public ForgeWorld(ServerLevel delegate) {
this.delegate = new WeakReference<>(delegate);
MinecraftServer server = delegate.getServer();
this.worldFolder = delegate.getServer().getServerDirectory().toPath()
this.worldFolder = delegate.getServer().getServerDirectory()
.resolve(server.getWorldPath(LevelResource.ROOT));
ResourceLocation id = delegate.dimension().location();

View File

@ -21,7 +21,9 @@ includeBuild("implementations/sponge")
includeBuild("implementations/forge-1.18.1")
includeBuild("implementations/forge-1.19.4")
includeBuild("implementations/forge-1.20")
includeBuild("implementations/forge-1.20.6")
includeBuild("implementations/fabric-1.18")
includeBuild("implementations/fabric-1.19.4")
includeBuild("implementations/fabric-1.20")
includeBuild("implementations/fabric-1.20.5")