initial port to fabric 1.21.5
This port was inspired by the #4198 port for spigot 1.21.5 for the NBT updates. Like that it also does not implement the new blocks. Only light testing has been done, but it seems to work just as under 1.21.4 so far. Note that gradle had to be updated to 8.12.
This commit is contained in:
parent
10e6739811
commit
802f62bc6a
47 changed files with 3977 additions and 1 deletions
32
fabric-1.21.5/.gitignore
vendored
Normal file
32
fabric-1.21.5/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# gradle
|
||||
|
||||
.gradle/
|
||||
build/
|
||||
out/
|
||||
classes/
|
||||
|
||||
# eclipse
|
||||
|
||||
*.launch
|
||||
|
||||
# idea
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# vscode
|
||||
|
||||
.settings/
|
||||
.vscode/
|
||||
bin/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
# fabric
|
||||
|
||||
run/
|
||||
|
||||
# other
|
||||
*.log
|
||||
73
fabric-1.21.5/build.gradle
Normal file
73
fabric-1.21.5/build.gradle
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
plugins {
|
||||
id 'fabric-loom' version '1.10-SNAPSHOT'
|
||||
}
|
||||
|
||||
archivesBaseName = "Dynmap"
|
||||
version = parent.version
|
||||
group = parent.group
|
||||
|
||||
eclipse {
|
||||
project {
|
||||
name = "Dynmap(Fabric-1.21.5)"
|
||||
}
|
||||
}
|
||||
|
||||
sourceCompatibility = targetCompatibility = compileJava.sourceCompatibility = compileJava.targetCompatibility = JavaLanguageVersion.of(21) // Need this here so eclipse task generates correctly.
|
||||
|
||||
configurations {
|
||||
shadow
|
||||
implementation.extendsFrom(shadow)
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||
|
||||
compileOnly group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'
|
||||
|
||||
shadow project(path: ':DynmapCore', configuration: 'shadow')
|
||||
|
||||
modCompileOnly "me.lucko:fabric-permissions-api:0.1-SNAPSHOT"
|
||||
compileOnly 'net.luckperms:api:5.4'
|
||||
}
|
||||
|
||||
loom {
|
||||
accessWidenerPath = file("src/main/resources/dynmap.accesswidener")
|
||||
}
|
||||
|
||||
processResources {
|
||||
filesMatching('fabric.mod.json') {
|
||||
expand "version": project.version
|
||||
}
|
||||
}
|
||||
|
||||
java {
|
||||
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
|
||||
// if it is present.
|
||||
// If you remove this line, sources will not be generated.
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
jar {
|
||||
from "LICENSE"
|
||||
from {
|
||||
configurations.shadow.collect { it.toString().contains("guava") ? null : it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
}
|
||||
|
||||
remapJar {
|
||||
archiveFileName = "${archivesBaseName}-${project.version}-fabric-${project.minecraft_version}.jar"
|
||||
destinationDirectory = file '../target'
|
||||
}
|
||||
|
||||
remapJar.doLast {
|
||||
task ->
|
||||
ant.checksum file: task.archivePath
|
||||
}
|
||||
4
fabric-1.21.5/gradle.properties
Normal file
4
fabric-1.21.5/gradle.properties
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
minecraft_version=1.21.5
|
||||
yarn_mappings=1.21.5+build.1
|
||||
loader_version=0.16.12
|
||||
fabric_version=0.119.6+1.21.5
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import org.dynmap.DynmapCore;
|
||||
import org.dynmap.Log;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class DynmapMod implements ModInitializer {
|
||||
private static final String MODID = "dynmap";
|
||||
private static final ModContainer MOD_CONTAINER = FabricLoader.getInstance().getModContainer(MODID)
|
||||
.orElseThrow(() -> new RuntimeException("Failed to get mod container: " + MODID));
|
||||
// The instance of your mod that Fabric uses.
|
||||
public static DynmapMod instance;
|
||||
|
||||
public static DynmapPlugin plugin;
|
||||
public static File jarfile;
|
||||
public static String ver;
|
||||
public static boolean useforcedchunks;
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
instance = this;
|
||||
|
||||
Path path = MOD_CONTAINER.getRootPath();
|
||||
try {
|
||||
jarfile = new File(DynmapCore.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
} catch (URISyntaxException e) {
|
||||
Log.severe("Unable to get DynmapCore jar path", e);
|
||||
}
|
||||
|
||||
if (path.getFileSystem().provider().getScheme().equals("jar")) {
|
||||
path = Paths.get(path.getFileSystem().toString());
|
||||
jarfile = path.toFile();
|
||||
}
|
||||
|
||||
ver = MOD_CONTAINER.getMetadata().getVersion().getFriendlyString();
|
||||
|
||||
Log.setLogger(new FabricLogger());
|
||||
org.dynmap.modsupport.ModSupportImpl.init();
|
||||
|
||||
// Initialize the plugin, we will enable it fully when the server starts.
|
||||
plugin = new DynmapPlugin();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,796 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
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.event.lifecycle.v1.ServerWorldEvents;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.FluidBlock;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.registry.tag.BlockTags;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.collection.IdList;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.world.EmptyBlockView;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.ChunkSection;
|
||||
import org.dynmap.*;
|
||||
import org.dynmap.common.BiomeMap;
|
||||
import org.dynmap.common.DynmapCommandSender;
|
||||
import org.dynmap.common.DynmapListenerManager;
|
||||
import org.dynmap.common.DynmapPlayer;
|
||||
import org.dynmap.common.chunk.GenericChunkCache;
|
||||
import org.dynmap.fabric_1_21_5.command.DmapCommand;
|
||||
import org.dynmap.fabric_1_21_5.command.DmarkerCommand;
|
||||
import org.dynmap.fabric_1_21_5.command.DynmapCommand;
|
||||
import org.dynmap.fabric_1_21_5.command.DynmapExpCommand;
|
||||
import org.dynmap.fabric_1_21_5.event.BlockEvents;
|
||||
import org.dynmap.fabric_1_21_5.event.CustomServerChunkEvents;
|
||||
import org.dynmap.fabric_1_21_5.event.CustomServerLifecycleEvents;
|
||||
import org.dynmap.fabric_1_21_5.event.PlayerEvents;
|
||||
import org.dynmap.fabric_1_21_5.mixin.BiomeEffectsAccessor;
|
||||
import org.dynmap.fabric_1_21_5.permissions.*;
|
||||
import org.dynmap.permissions.PermissionsHandler;
|
||||
import org.dynmap.renderer.DynmapBlockState;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
||||
public class DynmapPlugin {
|
||||
// FIXME: Fix package-private fields after splitting is done
|
||||
DynmapCore core;
|
||||
private PermissionProvider permissions;
|
||||
private boolean core_enabled;
|
||||
public GenericChunkCache sscache;
|
||||
public PlayerList playerList;
|
||||
MapManager mapManager;
|
||||
/**
|
||||
* Server is set when running and unset at shutdown.
|
||||
*/
|
||||
private net.minecraft.server.MinecraftServer server;
|
||||
public static DynmapPlugin plugin;
|
||||
ChatHandler chathandler;
|
||||
private HashMap<String, Integer> sortWeights = new HashMap<String, Integer>();
|
||||
private HashMap<String, FabricWorld> worlds = new HashMap<String, FabricWorld>();
|
||||
private WorldAccess last_world;
|
||||
private FabricWorld last_fworld;
|
||||
private Map<String, FabricPlayer> players = new HashMap<String, FabricPlayer>();
|
||||
private FabricServer fserver;
|
||||
private boolean tickregistered = false;
|
||||
// TPS calculator
|
||||
double tps;
|
||||
long lasttick;
|
||||
long avgticklen;
|
||||
// Per tick limit, in nsec
|
||||
long perTickLimit = (50000000); // 50 ms
|
||||
private boolean useSaveFolder = true;
|
||||
|
||||
private static final String[] TRIGGER_DEFAULTS = {"blockupdate", "chunkpopulate", "chunkgenerate"};
|
||||
|
||||
static final Pattern patternControlCode = Pattern.compile("(?i)\\u00A7[0-9A-FK-OR]");
|
||||
|
||||
DynmapPlugin() {
|
||||
plugin = this;
|
||||
// Fabric events persist between server instances
|
||||
ServerLifecycleEvents.SERVER_STARTING.register(this::serverStart);
|
||||
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> registerCommands(dispatcher));
|
||||
CustomServerLifecycleEvents.SERVER_STARTED_PRE_WORLD_LOAD.register(this::serverStarted);
|
||||
ServerLifecycleEvents.SERVER_STOPPING.register(this::serverStop);
|
||||
}
|
||||
|
||||
int getSortWeight(String name) {
|
||||
return sortWeights.getOrDefault(name, 0);
|
||||
}
|
||||
|
||||
void setSortWeight(String name, int wt) {
|
||||
sortWeights.put(name, wt);
|
||||
}
|
||||
|
||||
void dropSortWeight(String name) {
|
||||
sortWeights.remove(name);
|
||||
}
|
||||
|
||||
public static class BlockUpdateRec {
|
||||
WorldAccess w;
|
||||
String wid;
|
||||
int x, y, z;
|
||||
}
|
||||
|
||||
ConcurrentLinkedQueue<BlockUpdateRec> blockupdatequeue = new ConcurrentLinkedQueue<BlockUpdateRec>();
|
||||
|
||||
public static DynmapBlockState[] stateByID;
|
||||
|
||||
/**
|
||||
* Initialize block states (org.dynmap.blockstate.DynmapBlockState)
|
||||
*/
|
||||
public void initializeBlockStates() {
|
||||
stateByID = new DynmapBlockState[512 * 32]; // Simple map - scale as needed
|
||||
Arrays.fill(stateByID, DynmapBlockState.AIR); // Default to air
|
||||
|
||||
IdList<BlockState> bsids = Block.STATE_IDS;
|
||||
|
||||
DynmapBlockState basebs = null;
|
||||
Block baseb = null;
|
||||
int baseidx = 0;
|
||||
|
||||
Iterator<BlockState> iter = bsids.iterator();
|
||||
DynmapBlockState.Builder bld = new DynmapBlockState.Builder();
|
||||
while (iter.hasNext()) {
|
||||
BlockState bs = iter.next();
|
||||
int idx = bsids.getRawId(bs);
|
||||
if (idx >= stateByID.length) {
|
||||
int plen = stateByID.length;
|
||||
stateByID = Arrays.copyOf(stateByID, idx*11/10); // grow array by 10%
|
||||
Arrays.fill(stateByID, plen, stateByID.length, DynmapBlockState.AIR);
|
||||
}
|
||||
Block b = bs.getBlock();
|
||||
// If this is new block vs last, it's the base block state
|
||||
if (b != baseb) {
|
||||
basebs = null;
|
||||
baseidx = idx;
|
||||
baseb = b;
|
||||
}
|
||||
|
||||
Identifier ui = Registries.BLOCK.getId(b);
|
||||
if (ui == null) {
|
||||
continue;
|
||||
}
|
||||
String bn = ui.getNamespace() + ":" + ui.getPath();
|
||||
// Only do defined names, and not "air"
|
||||
if (!bn.equals(DynmapBlockState.AIR_BLOCK)) {
|
||||
String statename = "";
|
||||
for (net.minecraft.state.property.Property<?> p : bs.getProperties()) {
|
||||
if (statename.length() > 0) {
|
||||
statename += ",";
|
||||
}
|
||||
statename += p.getName() + "=" + bs.get(p).toString();
|
||||
}
|
||||
int lightAtten = bs.isOpaqueFullCube() ? 15 : (bs.isTransparent() ? 0 : 1);
|
||||
//Log.info("statename=" + bn + "[" + statename + "], lightAtten=" + lightAtten);
|
||||
// Fill in base attributes
|
||||
bld.setBaseState(basebs).setStateIndex(idx - baseidx).setBlockName(bn).setStateName(statename).setLegacyBlockID(idx).setAttenuatesLight(lightAtten);
|
||||
if (bs.getSoundGroup() != null) { bld.setMaterial(bs.getSoundGroup().toString()); }
|
||||
if (bs.isSolid()) { bld.setSolid(); }
|
||||
if (bs.isAir()) { bld.setAir(); }
|
||||
if (bs.isIn(BlockTags.LOGS)) { bld.setLog(); }
|
||||
if (bs.isIn(BlockTags.LEAVES)) { bld.setLeaves(); }
|
||||
if ((!bs.getFluidState().isEmpty()) && !(bs.getBlock() instanceof FluidBlock)) {
|
||||
bld.setWaterlogged();
|
||||
}
|
||||
DynmapBlockState dbs = bld.build(); // Build state
|
||||
stateByID[idx] = dbs;
|
||||
if (basebs == null) { basebs = dbs; }
|
||||
}
|
||||
}
|
||||
// for (int gidx = 0; gidx < DynmapBlockState.getGlobalIndexMax(); gidx++) {
|
||||
// DynmapBlockState bs = DynmapBlockState.getStateByGlobalIndex(gidx);
|
||||
// Log.info(gidx + ":" + bs.toString() + ", gidx=" + bs.globalStateIndex + ", sidx=" + bs.stateIndex);
|
||||
// }
|
||||
}
|
||||
|
||||
public static final Item getItemByID(int id) {
|
||||
return Item.byRawId(id);
|
||||
}
|
||||
|
||||
FabricPlayer getOrAddPlayer(ServerPlayerEntity player) {
|
||||
String name = player.getName().getString();
|
||||
FabricPlayer fp = players.get(name);
|
||||
if (fp != null) {
|
||||
fp.player = player;
|
||||
} else {
|
||||
fp = new FabricPlayer(this, player);
|
||||
players.put(name, fp);
|
||||
}
|
||||
return fp;
|
||||
}
|
||||
|
||||
static class ChatMessage {
|
||||
String message;
|
||||
ServerPlayerEntity sender;
|
||||
}
|
||||
|
||||
ConcurrentLinkedQueue<ChatMessage> msgqueue = new ConcurrentLinkedQueue<ChatMessage>();
|
||||
|
||||
public static class ChatHandler {
|
||||
private final DynmapPlugin plugin;
|
||||
|
||||
ChatHandler(DynmapPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void handleChat(ServerPlayerEntity player, String message) {
|
||||
if (!message.startsWith("/")) {
|
||||
ChatMessage cm = new ChatMessage();
|
||||
cm.message = message;
|
||||
cm.sender = player;
|
||||
plugin.msgqueue.add(cm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FabricServer getFabricServer() {
|
||||
return fserver;
|
||||
}
|
||||
|
||||
private void serverStart(MinecraftServer server) {
|
||||
// Set the server so we don't NPE during setup
|
||||
this.server = server;
|
||||
this.fserver = new FabricServer(this, server);
|
||||
this.onEnable();
|
||||
}
|
||||
|
||||
private void serverStarted(MinecraftServer server) {
|
||||
this.onStart();
|
||||
if (core != null) {
|
||||
core.serverStarted();
|
||||
}
|
||||
}
|
||||
|
||||
private void serverStop(MinecraftServer server) {
|
||||
this.onDisable();
|
||||
this.server = null;
|
||||
}
|
||||
|
||||
public boolean isOp(String player) {
|
||||
String[] ops = server.getPlayerManager().getOpList().getNames();
|
||||
|
||||
for (String op : ops) {
|
||||
if (op.equalsIgnoreCase(player)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Consider whether cheats are enabled for integrated server
|
||||
return server.isSingleplayer() && server.isHost(server.getPlayerManager().getPlayer(player).getGameProfile());
|
||||
}
|
||||
|
||||
boolean hasPerm(PlayerEntity psender, String permission) {
|
||||
PermissionsHandler ph = PermissionsHandler.getHandler();
|
||||
if ((ph != null) && (psender != null) && ph.hasPermission(psender.getName().getString(), permission)) {
|
||||
return true;
|
||||
}
|
||||
return permissions.has(psender, permission);
|
||||
}
|
||||
|
||||
boolean hasPermNode(PlayerEntity psender, String permission) {
|
||||
PermissionsHandler ph = PermissionsHandler.getHandler();
|
||||
if ((ph != null) && (psender != null) && ph.hasPermissionNode(psender.getName().getString(), permission)) {
|
||||
return true;
|
||||
}
|
||||
return permissions.hasPermissionNode(psender, permission);
|
||||
}
|
||||
|
||||
Set<String> hasOfflinePermissions(String player, Set<String> perms) {
|
||||
Set<String> rslt = null;
|
||||
PermissionsHandler ph = PermissionsHandler.getHandler();
|
||||
if (ph != null) {
|
||||
rslt = ph.hasOfflinePermissions(player, perms);
|
||||
}
|
||||
Set<String> rslt2 = hasOfflinePermissions(player, perms);
|
||||
if ((rslt != null) && (rslt2 != null)) {
|
||||
Set<String> newrslt = new HashSet<String>(rslt);
|
||||
newrslt.addAll(rslt2);
|
||||
rslt = newrslt;
|
||||
} else if (rslt2 != null) {
|
||||
rslt = rslt2;
|
||||
}
|
||||
return rslt;
|
||||
}
|
||||
|
||||
boolean hasOfflinePermission(String player, String perm) {
|
||||
PermissionsHandler ph = PermissionsHandler.getHandler();
|
||||
if (ph != null) {
|
||||
if (ph.hasOfflinePermission(player, perm)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return permissions.hasOfflinePermission(player, perm);
|
||||
}
|
||||
|
||||
void setChatHandler(ChatHandler chatHandler) {
|
||||
plugin.chathandler = chatHandler;
|
||||
}
|
||||
|
||||
public class TexturesPayload {
|
||||
public long timestamp;
|
||||
public String profileId;
|
||||
public String profileName;
|
||||
public boolean isPublic;
|
||||
public Map<String, ProfileTexture> textures;
|
||||
|
||||
}
|
||||
|
||||
public class ProfileTexture {
|
||||
public String url;
|
||||
}
|
||||
|
||||
public void loadExtraBiomes(String mcver) {
|
||||
int cnt = 0;
|
||||
BiomeMap.loadWellKnownByVersion(mcver);
|
||||
|
||||
Registry<Biome> biomeRegistry = getFabricServer().getBiomeRegistry();
|
||||
Biome[] list = getFabricServer().getBiomeList(biomeRegistry);
|
||||
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
Biome bb = list[i];
|
||||
if (bb != null) {
|
||||
String id = biomeRegistry.getId(bb).getPath();
|
||||
String rl = biomeRegistry.getId(bb).toString();
|
||||
float tmp = bb.getTemperature(), hum = bb.weather.downfall();
|
||||
int watermult = ((BiomeEffectsAccessor) bb.getEffects()).getWaterColor();
|
||||
Log.verboseinfo("biome[" + i + "]: hum=" + hum + ", tmp=" + tmp + ", mult=" + Integer.toHexString(watermult));
|
||||
|
||||
BiomeMap bmap = BiomeMap.NULL;
|
||||
if (rl != null) { // If resource location, lookup by this
|
||||
bmap = BiomeMap.byBiomeResourceLocation(rl);
|
||||
}
|
||||
else {
|
||||
bmap = BiomeMap.byBiomeID(i);
|
||||
}
|
||||
if (bmap.isDefault() || (bmap == BiomeMap.NULL)) {
|
||||
bmap = new BiomeMap((rl != null) ? BiomeMap.NO_INDEX : i, id, tmp, hum, rl);
|
||||
Log.verboseinfo("Add custom biome [" + bmap.toString() + "] (" + i + ")");
|
||||
cnt++;
|
||||
}
|
||||
else {
|
||||
bmap.setTemperature(tmp);
|
||||
bmap.setRainfall(hum);
|
||||
}
|
||||
if (watermult != -1) {
|
||||
bmap.setWaterColorMultiplier(watermult);
|
||||
Log.verboseinfo("Set watercolormult for " + bmap.toString() + " (" + i + ") to " + Integer.toHexString(watermult));
|
||||
}
|
||||
bmap.setBiomeObject(bb);
|
||||
}
|
||||
}
|
||||
if (cnt > 0)
|
||||
Log.info("Added " + cnt + " custom biome mappings");
|
||||
}
|
||||
|
||||
private String[] getBiomeNames() {
|
||||
Registry<Biome> biomeRegistry = getFabricServer().getBiomeRegistry();
|
||||
Biome[] list = getFabricServer().getBiomeList(biomeRegistry);
|
||||
String[] lst = new String[list.length];
|
||||
for (int i = 0; i < list.length; i++) {
|
||||
Biome bb = list[i];
|
||||
if (bb != null) {
|
||||
lst[i] = biomeRegistry.getId(bb).getPath();
|
||||
}
|
||||
}
|
||||
return lst;
|
||||
}
|
||||
|
||||
public void onEnable() {
|
||||
/* Get MC version */
|
||||
String mcver = server.getVersion();
|
||||
|
||||
/* Load extra biomes */
|
||||
loadExtraBiomes(mcver);
|
||||
/* Set up player login/quit event handler */
|
||||
registerPlayerLoginListener();
|
||||
|
||||
/* Initialize permissions handler */
|
||||
if (FabricLoader.getInstance().isModLoaded("luckperms")) {
|
||||
Log.info("Using luckperms for access control");
|
||||
permissions = new LuckPermissions();
|
||||
}
|
||||
else if (FabricLoader.getInstance().isModLoaded("fabric-permissions-api-v0")) {
|
||||
Log.info("Using fabric-permissions-api for access control");
|
||||
permissions = new FabricPermissions();
|
||||
} else {
|
||||
/* Initialize permissions handler */
|
||||
permissions = FilePermissions.create();
|
||||
if (permissions == null) {
|
||||
permissions = new OpPermissions(new String[]{"webchat", "marker.icons", "marker.list", "webregister", "stats", "hide.self", "show.self"});
|
||||
}
|
||||
}
|
||||
/* Get and initialize data folder */
|
||||
File dataDirectory = new File("dynmap");
|
||||
|
||||
if (!dataDirectory.exists()) {
|
||||
dataDirectory.mkdirs();
|
||||
}
|
||||
|
||||
/* Instantiate core */
|
||||
if (core == null) {
|
||||
core = new DynmapCore();
|
||||
}
|
||||
|
||||
/* Inject dependencies */
|
||||
core.setPluginJarFile(DynmapMod.jarfile);
|
||||
core.setPluginVersion(DynmapMod.ver);
|
||||
core.setMinecraftVersion(mcver);
|
||||
core.setDataFolder(dataDirectory);
|
||||
core.setServer(fserver);
|
||||
core.setTriggerDefault(TRIGGER_DEFAULTS);
|
||||
core.setBiomeNames(getBiomeNames());
|
||||
|
||||
if (!core.initConfiguration(null)) {
|
||||
return;
|
||||
}
|
||||
// Extract default permission example, if needed
|
||||
File filepermexample = new File(core.getDataFolder(), "permissions.yml.example");
|
||||
core.createDefaultFileFromResource("/permissions.yml.example", filepermexample);
|
||||
|
||||
DynmapCommonAPIListener.apiInitialized(core);
|
||||
}
|
||||
|
||||
private DynmapCommand dynmapCmd;
|
||||
private DmapCommand dmapCmd;
|
||||
private DmarkerCommand dmarkerCmd;
|
||||
private DynmapExpCommand dynmapexpCmd;
|
||||
|
||||
public void registerCommands(CommandDispatcher<ServerCommandSource> cd) {
|
||||
dynmapCmd = new DynmapCommand(this);
|
||||
dmapCmd = new DmapCommand(this);
|
||||
dmarkerCmd = new DmarkerCommand(this);
|
||||
dynmapexpCmd = new DynmapExpCommand(this);
|
||||
dynmapCmd.register(cd);
|
||||
dmapCmd.register(cd);
|
||||
dmarkerCmd.register(cd);
|
||||
dynmapexpCmd.register(cd);
|
||||
|
||||
Log.info("Register commands");
|
||||
}
|
||||
|
||||
public void onStart() {
|
||||
initializeBlockStates();
|
||||
/* Enable core */
|
||||
if (!core.enableCore(null)) {
|
||||
return;
|
||||
}
|
||||
core_enabled = true;
|
||||
VersionCheck.runCheck(core);
|
||||
// Get per tick time limit
|
||||
perTickLimit = core.getMaxTickUseMS() * 1000000;
|
||||
// Prep TPS
|
||||
lasttick = System.nanoTime();
|
||||
tps = 20.0;
|
||||
|
||||
/* Register tick handler */
|
||||
if (!tickregistered) {
|
||||
ServerTickEvents.END_SERVER_TICK.register(server -> fserver.tickEvent(server));
|
||||
tickregistered = true;
|
||||
}
|
||||
|
||||
playerList = core.playerList;
|
||||
sscache = new GenericChunkCache(core.getSnapShotCacheSize(), core.useSoftRefInSnapShotCache());
|
||||
/* Get map manager from core */
|
||||
mapManager = core.getMapManager();
|
||||
|
||||
/* Load saved world definitions */
|
||||
loadWorlds();
|
||||
|
||||
for (FabricWorld w : worlds.values()) {
|
||||
if (core.processWorldLoad(w)) { /* Have core process load first - fire event listeners if good load after */
|
||||
if (w.isLoaded()) {
|
||||
core.listenerManager.processWorldEvent(DynmapListenerManager.EventType.WORLD_LOAD, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
core.updateConfigHashcode();
|
||||
|
||||
/* Register our update trigger events */
|
||||
registerEvents();
|
||||
Log.info("Register events");
|
||||
|
||||
//DynmapCommonAPIListener.apiInitialized(core);
|
||||
|
||||
Log.info("Enabled");
|
||||
}
|
||||
|
||||
public void onDisable() {
|
||||
DynmapCommonAPIListener.apiTerminated();
|
||||
|
||||
//if (metrics != null) {
|
||||
// metrics.stop();
|
||||
// metrics = null;
|
||||
//}
|
||||
/* Save worlds */
|
||||
saveWorlds();
|
||||
|
||||
/* Purge tick queue */
|
||||
fserver.clearTaskQueue();
|
||||
|
||||
/* Disable core */
|
||||
core.disableCore();
|
||||
core_enabled = false;
|
||||
|
||||
if (sscache != null) {
|
||||
sscache.cleanup();
|
||||
sscache = null;
|
||||
}
|
||||
|
||||
Log.info("Disabled");
|
||||
}
|
||||
|
||||
// TODO: Clean a bit
|
||||
public void handleCommand(ServerCommandSource commandSource, String cmd, String[] args) throws CommandSyntaxException {
|
||||
DynmapCommandSender dsender;
|
||||
ServerPlayerEntity psender = null;
|
||||
|
||||
// getPlayer throws a CommandSyntaxException, so getEntity and instanceof for safety
|
||||
if (commandSource.getEntity() instanceof ServerPlayerEntity) {
|
||||
psender = commandSource.getPlayerOrThrow();
|
||||
}
|
||||
|
||||
if (psender != null) {
|
||||
// FIXME: New Player? Why not query the current player list.
|
||||
dsender = new FabricPlayer(this, psender);
|
||||
} else {
|
||||
dsender = new FabricCommandSender(commandSource);
|
||||
}
|
||||
|
||||
core.processCommand(dsender, cmd, cmd, args);
|
||||
}
|
||||
|
||||
public class PlayerTracker {
|
||||
public void onPlayerLogin(ServerPlayerEntity player) {
|
||||
if (!core_enabled) return;
|
||||
final DynmapPlayer dp = getOrAddPlayer(player);
|
||||
/* This event can be called from off server thread, so push processing there */
|
||||
core.getServer().scheduleServerTask(new Runnable() {
|
||||
public void run() {
|
||||
core.listenerManager.processPlayerEvent(DynmapListenerManager.EventType.PLAYER_JOIN, dp);
|
||||
}
|
||||
}, 2);
|
||||
}
|
||||
|
||||
public void onPlayerLogout(ServerPlayerEntity player) {
|
||||
if (!core_enabled) return;
|
||||
final DynmapPlayer dp = getOrAddPlayer(player);
|
||||
final String name = player.getName().getString();
|
||||
/* This event can be called from off server thread, so push processing there */
|
||||
core.getServer().scheduleServerTask(new Runnable() {
|
||||
public void run() {
|
||||
core.listenerManager.processPlayerEvent(DynmapListenerManager.EventType.PLAYER_QUIT, dp);
|
||||
players.remove(name);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public void onPlayerChangedDimension(ServerPlayerEntity player) {
|
||||
if (!core_enabled) return;
|
||||
getOrAddPlayer(player); // Freshen player object reference
|
||||
}
|
||||
|
||||
public void onPlayerRespawn(ServerPlayerEntity player) {
|
||||
if (!core_enabled) return;
|
||||
getOrAddPlayer(player); // Freshen player object reference
|
||||
}
|
||||
}
|
||||
|
||||
private PlayerTracker playerTracker = null;
|
||||
|
||||
private void registerPlayerLoginListener() {
|
||||
if (playerTracker == null) {
|
||||
playerTracker = new PlayerTracker();
|
||||
PlayerEvents.PLAYER_LOGGED_IN.register(player -> playerTracker.onPlayerLogin(player));
|
||||
PlayerEvents.PLAYER_LOGGED_OUT.register(player -> playerTracker.onPlayerLogout(player));
|
||||
PlayerEvents.PLAYER_CHANGED_DIMENSION.register(player -> playerTracker.onPlayerChangedDimension(player));
|
||||
PlayerEvents.PLAYER_RESPAWN.register(player -> playerTracker.onPlayerRespawn(player));
|
||||
}
|
||||
}
|
||||
|
||||
public class WorldTracker {
|
||||
public void handleWorldLoad(MinecraftServer server, ServerWorld world) {
|
||||
if (!core_enabled) return;
|
||||
|
||||
final FabricWorld fw = getWorld(world);
|
||||
// This event can be called from off server thread, so push processing there
|
||||
core.getServer().scheduleServerTask(new Runnable() {
|
||||
public void run() {
|
||||
if (core.processWorldLoad(fw)) // Have core process load first - fire event listeners if good load after
|
||||
core.listenerManager.processWorldEvent(DynmapListenerManager.EventType.WORLD_LOAD, fw);
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public void handleWorldUnload(MinecraftServer server, ServerWorld world) {
|
||||
if (!core_enabled) return;
|
||||
|
||||
final FabricWorld fw = getWorld(world);
|
||||
if (fw != null) {
|
||||
// This event can be called from off server thread, so push processing there
|
||||
core.getServer().scheduleServerTask(new Runnable() {
|
||||
public void run() {
|
||||
core.listenerManager.processWorldEvent(DynmapListenerManager.EventType.WORLD_UNLOAD, fw);
|
||||
core.processWorldUnload(fw);
|
||||
}
|
||||
}, 0);
|
||||
// Set world unloaded (needs to be immediate, since it may be invalid after event)
|
||||
fw.setWorldUnloaded();
|
||||
// Clean up tracker
|
||||
//WorldUpdateTracker wut = updateTrackers.remove(fw.getName());
|
||||
//if(wut != null) wut.world = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void handleChunkGenerate(ServerWorld world, Chunk chunk) {
|
||||
if (!onchunkgenerate) return;
|
||||
|
||||
FabricWorld fw = getWorld(world, false);
|
||||
ChunkPos chunkPos = chunk.getPos();
|
||||
|
||||
int ymax = Integer.MIN_VALUE;
|
||||
int ymin = Integer.MAX_VALUE;
|
||||
ChunkSection[] sections = chunk.getSectionArray();
|
||||
for (int i = 0; i < sections.length; i++) {
|
||||
if ((sections[i] != null) && (!sections[i].isEmpty())) {
|
||||
int sy = chunk.getBottomY() + i * ChunkSection.field_31407 /* Mojmap: SECTION_HEIGHT */;
|
||||
if (sy < ymin) ymin = sy;
|
||||
if ((sy+16) > ymax) ymax = sy + 16;
|
||||
}
|
||||
}
|
||||
if (ymax != Integer.MIN_VALUE) {
|
||||
mapManager.touchVolume(fw.getName(),
|
||||
chunkPos.getStartX(), ymin, chunkPos.getStartZ(),
|
||||
chunkPos.getEndX(), ymax, chunkPos.getEndZ(),
|
||||
"chunkgenerate");
|
||||
//Log.info("New generated chunk detected at %s[%s]".formatted(fw.getName(), chunkPos.getStartPos()));
|
||||
}
|
||||
}
|
||||
|
||||
public void handleBlockEvent(World world, BlockPos pos) {
|
||||
if (!core_enabled) return;
|
||||
if (!onblockchange) return;
|
||||
if (!(world instanceof ServerWorld)) return;
|
||||
|
||||
BlockUpdateRec r = new BlockUpdateRec();
|
||||
r.w = world;
|
||||
FabricWorld fw = getWorld(world, false);
|
||||
if (fw == null) return;
|
||||
r.wid = fw.getName();
|
||||
r.x = pos.getX();
|
||||
r.y = pos.getY();
|
||||
r.z = pos.getZ();
|
||||
blockupdatequeue.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
private WorldTracker worldTracker = null;
|
||||
private boolean onblockchange = false;
|
||||
private boolean onchunkpopulate = false;
|
||||
private boolean onchunkgenerate = false;
|
||||
boolean onblockchange_with_id = false;
|
||||
|
||||
private void registerEvents() {
|
||||
// To trigger rendering.
|
||||
onblockchange = core.isTrigger("blockupdate");
|
||||
onchunkpopulate = core.isTrigger("chunkpopulate");
|
||||
onchunkgenerate = core.isTrigger("chunkgenerate");
|
||||
onblockchange_with_id = core.isTrigger("blockupdate-with-id");
|
||||
if (onblockchange_with_id)
|
||||
onblockchange = true;
|
||||
if (worldTracker == null)
|
||||
worldTracker = new WorldTracker();
|
||||
if (onchunkpopulate || onchunkgenerate) {
|
||||
CustomServerChunkEvents.CHUNK_GENERATE.register((world, chunk) -> worldTracker.handleChunkGenerate(world, chunk));
|
||||
}
|
||||
if (onblockchange) {
|
||||
BlockEvents.BLOCK_EVENT.register((world, pos) -> worldTracker.handleBlockEvent(world, pos));
|
||||
}
|
||||
|
||||
ServerWorldEvents.LOAD.register((server, world) -> worldTracker.handleWorldLoad(server, world));
|
||||
ServerWorldEvents.UNLOAD.register((server, world) -> worldTracker.handleWorldUnload(server, world));
|
||||
}
|
||||
|
||||
FabricWorld getWorldByName(String name) {
|
||||
return worlds.get(name);
|
||||
}
|
||||
|
||||
FabricWorld getWorld(World w) {
|
||||
return getWorld(w, true);
|
||||
}
|
||||
|
||||
private FabricWorld getWorld(World w, boolean add_if_not_found) {
|
||||
if (last_world == w) {
|
||||
return last_fworld;
|
||||
}
|
||||
String wname = FabricWorld.getWorldName(this, w);
|
||||
|
||||
for (FabricWorld fw : worlds.values()) {
|
||||
if (fw.getRawName().equals(wname)) {
|
||||
last_world = w;
|
||||
last_fworld = fw;
|
||||
if (!fw.isLoaded()) {
|
||||
fw.setWorldLoaded(w);
|
||||
}
|
||||
fw.updateWorld(w);
|
||||
return fw;
|
||||
}
|
||||
}
|
||||
FabricWorld fw = null;
|
||||
if (add_if_not_found) {
|
||||
/* Add to list if not found */
|
||||
fw = new FabricWorld(this, w);
|
||||
worlds.put(fw.getName(), fw);
|
||||
}
|
||||
last_world = w;
|
||||
last_fworld = fw;
|
||||
return fw;
|
||||
}
|
||||
|
||||
private void saveWorlds() {
|
||||
File f = new File(core.getDataFolder(), FabricWorld.SAVED_WORLDS_FILE);
|
||||
ConfigurationNode cn = new ConfigurationNode(f);
|
||||
ArrayList<HashMap<String, Object>> lst = new ArrayList<HashMap<String, Object>>();
|
||||
for (DynmapWorld fw : core.mapManager.getWorlds()) {
|
||||
HashMap<String, Object> vals = new HashMap<String, Object>();
|
||||
vals.put("name", fw.getRawName());
|
||||
vals.put("height", fw.worldheight);
|
||||
vals.put("miny", fw.minY);
|
||||
vals.put("sealevel", fw.sealevel);
|
||||
vals.put("nether", fw.isNether());
|
||||
vals.put("the_end", ((FabricWorld) fw).isTheEnd());
|
||||
vals.put("title", fw.getTitle());
|
||||
lst.add(vals);
|
||||
}
|
||||
cn.put("worlds", lst);
|
||||
cn.put("useSaveFolderAsName", useSaveFolder);
|
||||
cn.put("maxWorldHeight", FabricWorld.getMaxWorldHeight());
|
||||
|
||||
cn.save();
|
||||
}
|
||||
|
||||
private void loadWorlds() {
|
||||
File f = new File(core.getDataFolder(), FabricWorld.SAVED_WORLDS_FILE);
|
||||
if (f.canRead() == false) {
|
||||
useSaveFolder = true;
|
||||
return;
|
||||
}
|
||||
ConfigurationNode cn = new ConfigurationNode(f);
|
||||
cn.load();
|
||||
// If defined, use maxWorldHeight
|
||||
FabricWorld.setMaxWorldHeight(cn.getInteger("maxWorldHeight", 256));
|
||||
|
||||
// If setting defined, use it
|
||||
if (cn.containsKey("useSaveFolderAsName")) {
|
||||
useSaveFolder = cn.getBoolean("useSaveFolderAsName", useSaveFolder);
|
||||
}
|
||||
List<Map<String, Object>> lst = cn.getMapList("worlds");
|
||||
if (lst == null) {
|
||||
Log.warning(String.format("Discarding bad %s", FabricWorld.SAVED_WORLDS_FILE));
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map<String, Object> world : lst) {
|
||||
try {
|
||||
String name = (String) world.get("name");
|
||||
int height = (Integer) world.get("height");
|
||||
Integer miny = (Integer) world.get("miny");
|
||||
int sealevel = (Integer) world.get("sealevel");
|
||||
boolean nether = (Boolean) world.get("nether");
|
||||
boolean theend = (Boolean) world.get("the_end");
|
||||
String title = (String) world.get("title");
|
||||
if (name != null) {
|
||||
FabricWorld fw = new FabricWorld(this, name, height, sealevel, nether, theend, title, (miny != null) ? miny : 0);
|
||||
fw.setWorldUnloaded();
|
||||
core.processWorldLoad(fw);
|
||||
worlds.put(fw.getName(), fw);
|
||||
}
|
||||
} catch (Exception x) {
|
||||
Log.warning(String.format("Unable to load saved worlds from %s", FabricWorld.SAVED_WORLDS_FILE));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import org.dynmap.DynmapLocation;
|
||||
|
||||
public final class FabricAdapter {
|
||||
public static DynmapLocation toDynmapLocation(DynmapPlugin plugin, ServerWorld world, double x, double y, double z) {
|
||||
return new DynmapLocation(plugin.getWorld(world).getName(), x, y, z);
|
||||
}
|
||||
|
||||
private FabricAdapter() {
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.Text;
|
||||
import org.dynmap.common.DynmapCommandSender;
|
||||
|
||||
/* Handler for generic console command sender */
|
||||
public class FabricCommandSender implements DynmapCommandSender {
|
||||
private ServerCommandSource sender;
|
||||
|
||||
protected FabricCommandSender() {
|
||||
sender = null;
|
||||
}
|
||||
|
||||
public FabricCommandSender(ServerCommandSource send) {
|
||||
sender = send;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrivilege(String privid) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String msg) {
|
||||
if (sender != null) {
|
||||
sender.sendFeedback(() -> Text.literal(msg), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOp() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermissionNode(String node) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.dynmap.utils.DynmapLogger;
|
||||
|
||||
public class FabricLogger implements DynmapLogger {
|
||||
Logger log;
|
||||
public static final String DM = "[Dynmap] ";
|
||||
|
||||
FabricLogger() {
|
||||
log = LogManager.getLogger("Dynmap");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String s) {
|
||||
log.info(DM + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(Throwable t) {
|
||||
log.fatal(t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String s) {
|
||||
log.fatal(DM + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void severe(String s, Throwable t) {
|
||||
log.fatal(DM + s, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void verboseinfo(String s) {
|
||||
log.info(DM + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String s) {
|
||||
log.warn(DM + s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warning(String s, Throwable t) {
|
||||
log.warn(DM + s, t);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import net.minecraft.nbt.*;
|
||||
import net.minecraft.server.world.ServerChunkLoadingManager;
|
||||
import net.minecraft.server.world.ServerChunkManager;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.collection.PackedIntegerArray;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.WordPackedArray;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.biome.BiomeEffects;
|
||||
import net.minecraft.world.chunk.ChunkManager;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.SerializedChunk;
|
||||
|
||||
import org.dynmap.DynmapChunk;
|
||||
import org.dynmap.DynmapCore;
|
||||
import org.dynmap.DynmapWorld;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.common.BiomeMap;
|
||||
import org.dynmap.common.chunk.GenericChunk;
|
||||
import org.dynmap.common.chunk.GenericChunkSection;
|
||||
import org.dynmap.common.chunk.GenericMapChunkCache;
|
||||
import org.dynmap.hdmap.HDBlockModels;
|
||||
import org.dynmap.renderer.DynmapBlockState;
|
||||
import org.dynmap.renderer.RenderPatchFactory;
|
||||
import org.dynmap.utils.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread
|
||||
*/
|
||||
public class FabricMapChunkCache extends GenericMapChunkCache {
|
||||
private World w;
|
||||
private ServerChunkManager cps;
|
||||
|
||||
/**
|
||||
* Construct empty cache
|
||||
*/
|
||||
public FabricMapChunkCache(DynmapPlugin plugin) {
|
||||
super(plugin.sscache);
|
||||
}
|
||||
|
||||
public void setChunks(FabricWorld dw, List<DynmapChunk> chunks) {
|
||||
this.w = dw.getWorld();
|
||||
if (dw.isLoaded()) {
|
||||
/* Check if world's provider is ServerChunkManager */
|
||||
ChunkManager cp = this.w.getChunkManager();
|
||||
|
||||
if (cp instanceof ServerChunkManager) {
|
||||
cps = (ServerChunkManager) cp;
|
||||
} else {
|
||||
Log.severe("Error: world " + dw.getName() + " has unsupported chunk provider");
|
||||
}
|
||||
}
|
||||
super.setChunks(dw, chunks);
|
||||
}
|
||||
|
||||
// Load generic chunk from existing and already loaded chunk
|
||||
protected GenericChunk getLoadedChunk(DynmapChunk chunk) {
|
||||
GenericChunk gc = null;
|
||||
if (cps.isChunkLoaded(chunk.x, chunk.z)) {
|
||||
NbtCompound nbt = null;
|
||||
try {
|
||||
SerializedChunk sc = SerializedChunk.fromChunk((ServerWorld) w, cps.getWorldChunk(chunk.x, chunk.z, false));
|
||||
nbt = sc.serialize();
|
||||
} catch (NullPointerException e) {
|
||||
// TODO: find out why this is happening and why it only seems to happen since 1.16.2
|
||||
Log.severe("ChunkSerializer.serialize threw a NullPointerException", e);
|
||||
}
|
||||
if (nbt != null) {
|
||||
gc = parseChunkFromNBT(new NBT.NBTCompound(nbt));
|
||||
}
|
||||
}
|
||||
return gc;
|
||||
}
|
||||
|
||||
private NbtCompound readChunk(int x, int z) {
|
||||
try {
|
||||
ServerChunkLoadingManager acl = cps.chunkLoadingManager;
|
||||
|
||||
ChunkPos coord = new ChunkPos(x, z);
|
||||
// Async chunk reading is synchronized here. Perhaps we can do async and improve performance?
|
||||
return acl.getNbt(coord).join().orElse(null);
|
||||
} catch (Exception exc) {
|
||||
Log.severe(String.format("Error reading chunk: %s,%d,%d", dw.getName(), x, z), exc);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Load generic chunk from unloaded chunk
|
||||
protected GenericChunk loadChunk(DynmapChunk chunk) {
|
||||
GenericChunk gc = null;
|
||||
NbtCompound nbt = readChunk(chunk.x, chunk.z);
|
||||
// If read was good
|
||||
if (nbt != null) {
|
||||
gc = parseChunkFromNBT(new NBT.NBTCompound(nbt));
|
||||
}
|
||||
return gc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFoliageColor(BiomeMap bm, int[] colormap, int x, int z) {
|
||||
return bm.<Biome>getBiomeObject().map(Biome::getEffects).flatMap(BiomeEffects::getFoliageColor).orElse(colormap[bm.biomeLookup()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGrassColor(BiomeMap bm, int[] colormap, int x, int z) {
|
||||
BiomeEffects effects = bm.<Biome>getBiomeObject().map(Biome::getEffects).orElse(null);
|
||||
if (effects == null) return colormap[bm.biomeLookup()];
|
||||
return effects.getGrassColorModifier().getModifiedGrassColor(x, z, effects.getGrassColor().orElse(colormap[bm.biomeLookup()]));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,260 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
|
||||
import net.minecraft.network.packet.s2c.play.SubtitleS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.TitleFadeS2CPacket;
|
||||
import net.minecraft.network.packet.s2c.play.TitleS2CPacket;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import org.dynmap.DynmapLocation;
|
||||
import org.dynmap.common.DynmapPlayer;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Player access abstraction class
|
||||
*/
|
||||
public class FabricPlayer extends FabricCommandSender implements DynmapPlayer {
|
||||
private static final Gson GSON = new GsonBuilder().create();
|
||||
private final DynmapPlugin plugin;
|
||||
// FIXME: Proper setter
|
||||
ServerPlayerEntity player;
|
||||
private final String skinurl;
|
||||
private final UUID uuid;
|
||||
|
||||
public FabricPlayer(DynmapPlugin plugin, ServerPlayerEntity player) {
|
||||
this.plugin = plugin;
|
||||
this.player = player;
|
||||
String url = null;
|
||||
if (this.player != null) {
|
||||
uuid = this.player.getUuid();
|
||||
GameProfile prof = this.player.getGameProfile();
|
||||
if (prof != null) {
|
||||
Property textureProperty = Iterables.getFirst(prof.getProperties().get("textures"), null);
|
||||
|
||||
if (textureProperty != null) {
|
||||
DynmapPlugin.TexturesPayload result = null;
|
||||
try {
|
||||
String json = new String(Base64.getDecoder().decode(textureProperty.value()), StandardCharsets.UTF_8);
|
||||
result = GSON.fromJson(json, DynmapPlugin.TexturesPayload.class);
|
||||
} catch (JsonParseException e) {
|
||||
}
|
||||
if ((result != null) && (result.textures != null) && (result.textures.containsKey("SKIN"))) {
|
||||
url = result.textures.get("SKIN").url;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
uuid = null;
|
||||
}
|
||||
skinurl = url;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isConnected() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
if (player != null) {
|
||||
String n = player.getName().getString();
|
||||
;
|
||||
return n;
|
||||
} else
|
||||
return "[Server]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
if (player != null) {
|
||||
String n = player.getDisplayName().getString();
|
||||
return n;
|
||||
} else
|
||||
return "[Server]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnline() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynmapLocation getLocation() {
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Vec3d pos = player.getPos();
|
||||
return FabricAdapter.toDynmapLocation(plugin, player.getServerWorld(), pos.getX(), pos.getY(), pos.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorld() {
|
||||
if (player == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
World world = player.getWorld();
|
||||
if (world != null) {
|
||||
return plugin.getWorld(world).getName();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InetSocketAddress getAddress() {
|
||||
if (player != null) {
|
||||
ServerPlayNetworkHandler networkHandler = player.networkHandler;
|
||||
if (networkHandler != null) {
|
||||
SocketAddress sa = networkHandler.getConnectionAddress();
|
||||
if (sa instanceof InetSocketAddress) {
|
||||
return (InetSocketAddress) sa;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSneaking() {
|
||||
if (player != null) {
|
||||
return player.isSneaking();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getHealth() {
|
||||
if (player != null) {
|
||||
double h = player.getHealth();
|
||||
if (h > 20) h = 20;
|
||||
return h; // Scale to 20 range
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getArmorPoints() {
|
||||
if (player != null) {
|
||||
return player.getArmor();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynmapLocation getBedSpawnLocation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastLoginTime() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getFirstLoginTime() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPrivilege(String privid) {
|
||||
if (player != null)
|
||||
return plugin.hasPerm(player, privid);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOp() {
|
||||
return plugin.isOp(player.getName().getString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMessage(String msg) {
|
||||
Text ichatcomponent = Text.literal(msg);
|
||||
player.sendMessage(ichatcomponent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInvisible() {
|
||||
if (player != null) {
|
||||
return player.isInvisible();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@Override
|
||||
public boolean isSpectator() {
|
||||
if(player != null) {
|
||||
return player.isSpectator();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSortWeight() {
|
||||
return plugin.getSortWeight(getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSortWeight(int wt) {
|
||||
if (wt == 0) {
|
||||
plugin.dropSortWeight(getName());
|
||||
} else {
|
||||
plugin.setSortWeight(getName(), wt);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermissionNode(String node) {
|
||||
return player != null && plugin.hasPermNode(player, node);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSkinURL() {
|
||||
return skinurl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID getUUID() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send title and subtitle text (called from server thread)
|
||||
*/
|
||||
@Override
|
||||
public void sendTitleText(String title, String subtitle, int fadeInTicks, int stayTicks, int fadeOutTicks) {
|
||||
if (player != null) {
|
||||
ServerPlayerEntity player = this.player;
|
||||
TitleFadeS2CPacket times = new TitleFadeS2CPacket(fadeInTicks, stayTicks, fadeOutTicks);
|
||||
player.networkHandler.sendPacket(times);
|
||||
if (title != null) {
|
||||
TitleS2CPacket titlepkt = new TitleS2CPacket(Text.literal(title));
|
||||
player.networkHandler.sendPacket(titlepkt);
|
||||
}
|
||||
|
||||
if (subtitle != null) {
|
||||
SubtitleS2CPacket subtitlepkt = new SubtitleS2CPacket(Text.literal(subtitle));
|
||||
player.networkHandler.sendPacket(subtitlepkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,609 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.fabricmc.loader.api.ModContainer;
|
||||
import net.minecraft.block.AbstractSignBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.network.message.MessageType;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.registry.RegistryKeys;
|
||||
import net.minecraft.server.BannedIpList;
|
||||
import net.minecraft.server.BannedPlayerList;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.UserCache;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import org.dynmap.DynmapChunk;
|
||||
import org.dynmap.DynmapWorld;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.DynmapCommonAPIListener;
|
||||
import org.dynmap.common.BiomeMap;
|
||||
import org.dynmap.common.DynmapListenerManager;
|
||||
import org.dynmap.common.DynmapPlayer;
|
||||
import org.dynmap.common.DynmapServerInterface;
|
||||
import org.dynmap.fabric_1_21_5.event.BlockEvents;
|
||||
import org.dynmap.fabric_1_21_5.event.ServerChatEvents;
|
||||
import org.dynmap.utils.MapChunkCache;
|
||||
import org.dynmap.utils.VisibilityLimit;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Server access abstraction class
|
||||
*/
|
||||
public class FabricServer extends DynmapServerInterface {
|
||||
/* Server thread scheduler */
|
||||
private final Object schedlock = new Object();
|
||||
private final DynmapPlugin plugin;
|
||||
private final MinecraftServer server;
|
||||
private final Registry<Biome> biomeRegistry;
|
||||
private long cur_tick;
|
||||
private long next_id;
|
||||
private long cur_tick_starttime;
|
||||
private PriorityQueue<TaskRecord> runqueue = new PriorityQueue<TaskRecord>();
|
||||
|
||||
public FabricServer(DynmapPlugin plugin, MinecraftServer server) {
|
||||
this.plugin = plugin;
|
||||
this.server = server;
|
||||
this.biomeRegistry = server.getRegistryManager().getOrThrow(RegistryKeys.BIOME);
|
||||
}
|
||||
|
||||
private Optional<GameProfile> getProfileByName(String player) {
|
||||
UserCache cache = server.getUserCache();
|
||||
return cache.findByName(player);
|
||||
}
|
||||
|
||||
public final Registry<Biome> getBiomeRegistry() {
|
||||
return biomeRegistry;
|
||||
}
|
||||
|
||||
private Biome[] biomelist = null;
|
||||
|
||||
public final Biome[] getBiomeList(Registry<Biome> biomeRegistry) {
|
||||
if (biomelist == null) {
|
||||
biomelist = new Biome[256];
|
||||
Iterator<Biome> iter = biomeRegistry.iterator();
|
||||
while (iter.hasNext()) {
|
||||
Biome b = iter.next();
|
||||
int bidx = biomeRegistry.getRawId(b);
|
||||
if (bidx >= biomelist.length) {
|
||||
biomelist = Arrays.copyOf(biomelist, bidx + biomelist.length);
|
||||
}
|
||||
biomelist[bidx] = b;
|
||||
}
|
||||
}
|
||||
return biomelist;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockIDAt(String wname, int x, int y, int z) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") /* Not much I can do... fix this if it breaks. */
|
||||
@Override
|
||||
public int isSignAt(String wname, int x, int y, int z) {
|
||||
World world = plugin.getWorldByName(wname).getWorld();
|
||||
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
if (!world.isChunkLoaded(pos))
|
||||
return -1;
|
||||
|
||||
Block block = world.getBlockState(pos).getBlock();
|
||||
return (block instanceof AbstractSignBlock ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scheduleServerTask(Runnable run, long delay) {
|
||||
/* Add task record to queue */
|
||||
synchronized (schedlock) {
|
||||
TaskRecord tr = new TaskRecord(cur_tick + delay, next_id++, new FutureTask<Object>(run, null));
|
||||
runqueue.add(tr);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynmapPlayer[] getOnlinePlayers() {
|
||||
if (server.getPlayerManager() == null) return new DynmapPlayer[0];
|
||||
|
||||
List<ServerPlayerEntity> players = server.getPlayerManager().getPlayerList();
|
||||
int playerCount = players.size();
|
||||
DynmapPlayer[] dplay = new DynmapPlayer[players.size()];
|
||||
|
||||
for (int i = 0; i < playerCount; i++) {
|
||||
ServerPlayerEntity player = players.get(i);
|
||||
dplay[i] = plugin.getOrAddPlayer(player);
|
||||
}
|
||||
|
||||
return dplay;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reload() {
|
||||
plugin.onDisable();
|
||||
plugin.onEnable();
|
||||
plugin.onStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynmapPlayer getPlayer(String name) {
|
||||
List<ServerPlayerEntity> players = server.getPlayerManager().getPlayerList();
|
||||
|
||||
for (ServerPlayerEntity player : players) {
|
||||
|
||||
if (player.getName().getString().equalsIgnoreCase(name)) {
|
||||
return plugin.getOrAddPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getIPBans() {
|
||||
BannedIpList bl = server.getPlayerManager().getIpBanList();
|
||||
Set<String> ips = new HashSet<String>();
|
||||
|
||||
for (String s : bl.getNames()) {
|
||||
ips.add(s);
|
||||
}
|
||||
|
||||
return ips;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Future<T> callSyncMethod(Callable<T> task) {
|
||||
return callSyncMethod(task, 0);
|
||||
}
|
||||
|
||||
public <T> Future<T> callSyncMethod(Callable<T> task, long delay) {
|
||||
FutureTask<T> ft = new FutureTask<T>(task);
|
||||
|
||||
/* Add task record to queue */
|
||||
synchronized (schedlock) {
|
||||
TaskRecord tr = new TaskRecord(cur_tick + delay, next_id++, ft);
|
||||
runqueue.add(tr);
|
||||
}
|
||||
|
||||
return ft;
|
||||
}
|
||||
|
||||
void clearTaskQueue() {
|
||||
this.runqueue.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
String sn;
|
||||
if (server.isSingleplayer())
|
||||
sn = "Integrated";
|
||||
else
|
||||
sn = server.getServerIp();
|
||||
if (sn == null) sn = "Unknown Server";
|
||||
return sn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPlayerBanned(String pid) {
|
||||
PlayerManager scm = server.getPlayerManager();
|
||||
BannedPlayerList bl = scm.getUserBanList();
|
||||
try {
|
||||
return bl.contains(getProfileByName(pid).get());
|
||||
} catch (NoSuchElementException e) {
|
||||
/* If this profile doesn't exist, default to "banned" for good measure. */
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stripChatColor(String s) {
|
||||
return DynmapPlugin.patternControlCode.matcher(s).replaceAll("");
|
||||
}
|
||||
|
||||
private Set<DynmapListenerManager.EventType> registered = new HashSet<DynmapListenerManager.EventType>();
|
||||
|
||||
@Override
|
||||
public boolean requestEventNotification(DynmapListenerManager.EventType type) {
|
||||
if (registered.contains(type)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case WORLD_LOAD:
|
||||
case WORLD_UNLOAD:
|
||||
/* Already called for normal world activation/deactivation */
|
||||
break;
|
||||
|
||||
case WORLD_SPAWN_CHANGE:
|
||||
/*TODO
|
||||
pm.registerEvents(new Listener() {
|
||||
@EventHandler(priority=EventPriority.MONITOR)
|
||||
public void onSpawnChange(SpawnChangeEvent evt) {
|
||||
DynmapWorld w = new BukkitWorld(evt.getWorld());
|
||||
core.listenerManager.processWorldEvent(EventType.WORLD_SPAWN_CHANGE, w);
|
||||
}
|
||||
}, DynmapPlugin.this);
|
||||
*/
|
||||
break;
|
||||
|
||||
case PLAYER_JOIN:
|
||||
case PLAYER_QUIT:
|
||||
/* Already handled */
|
||||
break;
|
||||
|
||||
case PLAYER_BED_LEAVE:
|
||||
/*TODO
|
||||
pm.registerEvents(new Listener() {
|
||||
@EventHandler(priority=EventPriority.MONITOR)
|
||||
public void onPlayerBedLeave(PlayerBedLeaveEvent evt) {
|
||||
DynmapPlayer p = new BukkitPlayer(evt.getPlayer());
|
||||
core.listenerManager.processPlayerEvent(EventType.PLAYER_BED_LEAVE, p);
|
||||
}
|
||||
}, DynmapPlugin.this);
|
||||
*/
|
||||
break;
|
||||
|
||||
case PLAYER_CHAT:
|
||||
if (plugin.chathandler == null) {
|
||||
plugin.setChatHandler(new DynmapPlugin.ChatHandler(plugin));
|
||||
ServerChatEvents.EVENT.register((player, message) -> plugin.chathandler.handleChat(player, message));
|
||||
}
|
||||
break;
|
||||
|
||||
case BLOCK_BREAK:
|
||||
/* Already handled by BlockEvents logic */
|
||||
break;
|
||||
|
||||
case SIGN_CHANGE:
|
||||
BlockEvents.SIGN_CHANGE_EVENT.register((world, pos, lines, player, front) -> {
|
||||
plugin.core.processSignChange("fabric", FabricWorld.getWorldName(plugin, world),
|
||||
pos.getX(), pos.getY(), pos.getZ(), lines, player.getName().getString());
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
Log.severe("Unhandled event type: " + type);
|
||||
return false;
|
||||
}
|
||||
|
||||
registered.add(type);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean sendWebChatEvent(String source, String name, String msg) {
|
||||
return DynmapCommonAPIListener.fireWebChatEvent(source, name, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void broadcastMessage(String msg) {
|
||||
Text component = Text.literal(msg);
|
||||
server.getPlayerManager().broadcast(component, false);
|
||||
Log.info(stripChatColor(msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getBiomeIDs() {
|
||||
BiomeMap[] b = BiomeMap.values();
|
||||
String[] bname = new String[b.length];
|
||||
|
||||
for (int i = 0; i < bname.length; i++) {
|
||||
bname[i] = b[i].toString();
|
||||
}
|
||||
|
||||
return bname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getCacheHitRate() {
|
||||
if (plugin.sscache != null)
|
||||
return plugin.sscache.getHitRate();
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCacheStats() {
|
||||
if (plugin.sscache != null)
|
||||
plugin.sscache.resetStats();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynmapWorld getWorldByName(String wname) {
|
||||
return plugin.getWorldByName(wname);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynmapPlayer getOfflinePlayer(String name) {
|
||||
/*
|
||||
OfflinePlayer op = getServer().getOfflinePlayer(name);
|
||||
if(op != null) {
|
||||
return new BukkitPlayer(op);
|
||||
}
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> checkPlayerPermissions(String player, Set<String> perms) {
|
||||
if (isPlayerBanned(player)) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<String> rslt = plugin.hasOfflinePermissions(player, perms);
|
||||
if (rslt == null) {
|
||||
rslt = new HashSet<String>();
|
||||
if (plugin.isOp(player)) {
|
||||
rslt.addAll(perms);
|
||||
}
|
||||
}
|
||||
return rslt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkPlayerPermission(String player, String perm) {
|
||||
if (isPlayerBanned(player)) {
|
||||
return false;
|
||||
}
|
||||
return plugin.hasOfflinePermission(player, perm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render processor helper - used by code running on render threads to request chunk snapshot cache from server/sync thread
|
||||
*/
|
||||
@Override
|
||||
public MapChunkCache createMapChunkCache(DynmapWorld w, List<DynmapChunk> chunks,
|
||||
boolean blockdata, boolean highesty, boolean biome, boolean rawbiome) {
|
||||
FabricMapChunkCache c = (FabricMapChunkCache) w.getChunkCache(chunks);
|
||||
if (c == null) {
|
||||
return null;
|
||||
}
|
||||
if (w.visibility_limits != null) {
|
||||
for (VisibilityLimit limit : w.visibility_limits) {
|
||||
c.setVisibleRange(limit);
|
||||
}
|
||||
|
||||
c.setHiddenFillStyle(w.hiddenchunkstyle);
|
||||
}
|
||||
|
||||
if (w.hidden_limits != null) {
|
||||
for (VisibilityLimit limit : w.hidden_limits) {
|
||||
c.setHiddenRange(limit);
|
||||
}
|
||||
|
||||
c.setHiddenFillStyle(w.hiddenchunkstyle);
|
||||
}
|
||||
|
||||
if (!c.setChunkDataTypes(blockdata, biome, highesty, rawbiome)) {
|
||||
Log.severe("CraftBukkit build does not support biome APIs");
|
||||
}
|
||||
|
||||
if (chunks.size() == 0) /* No chunks to get? */ {
|
||||
c.loadChunks(0);
|
||||
return c;
|
||||
}
|
||||
|
||||
//Now handle any chunks in server thread that are already loaded (on server thread)
|
||||
final FabricMapChunkCache cc = c;
|
||||
Future<Boolean> f = this.callSyncMethod(new Callable<Boolean>() {
|
||||
public Boolean call() throws Exception {
|
||||
// Update busy state on world
|
||||
//FabricWorld fw = (FabricWorld) cc.getWorld();
|
||||
//TODO
|
||||
//setBusy(fw.getWorld());
|
||||
cc.getLoadedChunks();
|
||||
return true;
|
||||
}
|
||||
}, 0);
|
||||
try {
|
||||
f.get();
|
||||
} catch (CancellationException cx) {
|
||||
return null;
|
||||
} catch (InterruptedException cx) {
|
||||
return null;
|
||||
} catch (ExecutionException xx) {
|
||||
Log.severe("Exception while loading chunks", xx.getCause());
|
||||
return null;
|
||||
} catch (Exception ix) {
|
||||
Log.severe(ix);
|
||||
return null;
|
||||
}
|
||||
if (!w.isLoaded()) {
|
||||
return null;
|
||||
}
|
||||
// Now, do rest of chunk reading from calling thread
|
||||
c.readChunks(chunks.size());
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers() {
|
||||
return server.getMaxPlayerCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCurrentPlayers() {
|
||||
return server.getPlayerManager().getCurrentPlayerCount();
|
||||
}
|
||||
|
||||
public void tickEvent(MinecraftServer server) {
|
||||
cur_tick_starttime = System.nanoTime();
|
||||
long elapsed = cur_tick_starttime - plugin.lasttick;
|
||||
plugin.lasttick = cur_tick_starttime;
|
||||
plugin.avgticklen = ((plugin.avgticklen * 99) / 100) + (elapsed / 100);
|
||||
plugin.tps = (double) 1E9 / (double) plugin.avgticklen;
|
||||
// Tick core
|
||||
if (plugin.core != null) {
|
||||
plugin.core.serverTick(plugin.tps);
|
||||
}
|
||||
|
||||
boolean done = false;
|
||||
TaskRecord tr = null;
|
||||
|
||||
while (!plugin.blockupdatequeue.isEmpty()) {
|
||||
DynmapPlugin.BlockUpdateRec r = plugin.blockupdatequeue.remove();
|
||||
BlockState bs = r.w.getBlockState(new BlockPos(r.x, r.y, r.z));
|
||||
int idx = Block.STATE_IDS.getRawId(bs);
|
||||
if (!org.dynmap.hdmap.HDBlockModels.isChangeIgnoredBlock(DynmapPlugin.stateByID[idx])) {
|
||||
if (plugin.onblockchange_with_id)
|
||||
plugin.mapManager.touch(r.wid, r.x, r.y, r.z, "blockchange[" + idx + "]");
|
||||
else
|
||||
plugin.mapManager.touch(r.wid, r.x, r.y, r.z, "blockchange");
|
||||
}
|
||||
}
|
||||
|
||||
long now;
|
||||
|
||||
synchronized (schedlock) {
|
||||
cur_tick++;
|
||||
now = System.nanoTime();
|
||||
tr = runqueue.peek();
|
||||
/* Nothing due to run */
|
||||
if ((tr == null) || (tr.getTickToRun() > cur_tick) || ((now - cur_tick_starttime) > plugin.perTickLimit)) {
|
||||
done = true;
|
||||
} else {
|
||||
tr = runqueue.poll();
|
||||
}
|
||||
}
|
||||
while (!done) {
|
||||
tr.run();
|
||||
|
||||
synchronized (schedlock) {
|
||||
tr = runqueue.peek();
|
||||
now = System.nanoTime();
|
||||
/* Nothing due to run */
|
||||
if ((tr == null) || (tr.getTickToRun() > cur_tick) || ((now - cur_tick_starttime) > plugin.perTickLimit)) {
|
||||
done = true;
|
||||
} else {
|
||||
tr = runqueue.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
while (!plugin.msgqueue.isEmpty()) {
|
||||
DynmapPlugin.ChatMessage cm = plugin.msgqueue.poll();
|
||||
DynmapPlayer dp = null;
|
||||
if (cm.sender != null)
|
||||
dp = plugin.getOrAddPlayer(cm.sender);
|
||||
else
|
||||
dp = new FabricPlayer(plugin, null);
|
||||
|
||||
plugin.core.listenerManager.processChatEvent(DynmapListenerManager.EventType.PLAYER_CHAT, dp, cm.message);
|
||||
}
|
||||
// Check for generated chunks
|
||||
if ((cur_tick % 20) == 0) {
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<ModContainer> getModContainerById(String id) {
|
||||
return FabricLoader.getInstance().getModContainer(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isModLoaded(String name) {
|
||||
return FabricLoader.getInstance().getModContainer(name).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getModVersion(String name) {
|
||||
Optional<ModContainer> mod = getModContainerById(name); // Try case sensitive lookup
|
||||
return mod.map(modContainer -> modContainer.getMetadata().getVersion().getFriendlyString()).orElse(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getServerTPS() {
|
||||
return plugin.tps;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerIP() {
|
||||
if (server.isSingleplayer())
|
||||
return "0.0.0.0";
|
||||
else
|
||||
return server.getServerIp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getModContainerFile(String name) {
|
||||
Optional<ModContainer> container = getModContainerById(name); // Try case sensitive lookup
|
||||
if (container.isPresent()) {
|
||||
Path path = container.get().getRootPath();
|
||||
if (path.getFileSystem().provider().getScheme().equals("jar")) {
|
||||
path = Paths.get(path.getFileSystem().toString());
|
||||
}
|
||||
return path.toFile();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getModList() {
|
||||
return FabricLoader.getInstance()
|
||||
.getAllMods()
|
||||
.stream()
|
||||
.map(container -> container.getMetadata().getId())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<Integer, String> getBlockIDMap() {
|
||||
Map<Integer, String> map = new HashMap<Integer, String>();
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openResource(String modid, String rname) {
|
||||
if (modid == null) modid = "minecraft";
|
||||
|
||||
if ("minecraft".equals(modid)) {
|
||||
return MinecraftServer.class.getClassLoader().getResourceAsStream(rname);
|
||||
} else {
|
||||
if (rname.startsWith("/") || rname.startsWith("\\")) {
|
||||
rname = rname.substring(1);
|
||||
}
|
||||
|
||||
final String finalModid = modid;
|
||||
final String finalRname = rname;
|
||||
return getModContainerById(modid).map(container -> {
|
||||
try {
|
||||
return Files.newInputStream(container.getPath(finalRname));
|
||||
} catch (IOException e) {
|
||||
Log.severe("Failed to load resource of mod :" + finalModid, e);
|
||||
return null;
|
||||
}
|
||||
}).orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get block unique ID map (module:blockid)
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Integer> getBlockUniqueIDMap() {
|
||||
HashMap<String, Integer> map = new HashMap<String, Integer>();
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get item unique ID map (module:itemid)
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Integer> getItemUniqueIDMap() {
|
||||
HashMap<String, Integer> map = new HashMap<String, Integer>();
|
||||
return map;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import net.minecraft.registry.RegistryKey;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.Heightmap;
|
||||
import net.minecraft.world.LightType;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.border.WorldBorder;
|
||||
import org.dynmap.DynmapChunk;
|
||||
import org.dynmap.DynmapLocation;
|
||||
import org.dynmap.DynmapWorld;
|
||||
import org.dynmap.utils.MapChunkCache;
|
||||
import org.dynmap.utils.Polygon;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FabricWorld extends DynmapWorld {
|
||||
// TODO: Store this relative to World saves for integrated server
|
||||
public static final String SAVED_WORLDS_FILE = "fabricworlds.yml";
|
||||
|
||||
private final DynmapPlugin plugin;
|
||||
private World world;
|
||||
private final boolean skylight;
|
||||
private final boolean isnether;
|
||||
private final boolean istheend;
|
||||
private final String env;
|
||||
private DynmapLocation spawnloc = new DynmapLocation();
|
||||
private static int maxWorldHeight = 320; // Maximum allows world height
|
||||
|
||||
public static int getMaxWorldHeight() {
|
||||
return maxWorldHeight;
|
||||
}
|
||||
|
||||
public static void setMaxWorldHeight(int h) {
|
||||
maxWorldHeight = h;
|
||||
}
|
||||
|
||||
public static String getWorldName(DynmapPlugin plugin, World w) {
|
||||
RegistryKey<World> rk = w.getRegistryKey();
|
||||
if (rk == World.OVERWORLD) { // Overworld?
|
||||
return w.getServer().getSaveProperties().getLevelName();
|
||||
} else if (rk == World.END) {
|
||||
return "DIM1";
|
||||
} else if (rk == World.NETHER) {
|
||||
return "DIM-1";
|
||||
} else {
|
||||
return rk.getValue().getNamespace() + "_" + rk.getValue().getPath();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateWorld(World w) {
|
||||
this.updateWorldHeights(w.getHeight(), w.getBottomY(), w.getSeaLevel());
|
||||
}
|
||||
|
||||
public FabricWorld(DynmapPlugin plugin, World w) {
|
||||
this(plugin, getWorldName(plugin, w), w.getHeight(),
|
||||
w.getSeaLevel(),
|
||||
w.getRegistryKey() == World.NETHER,
|
||||
w.getRegistryKey() == World.END,
|
||||
w.getRegistryKey().getValue().getPath(),
|
||||
w.getBottomY());
|
||||
setWorldLoaded(w);
|
||||
}
|
||||
|
||||
public FabricWorld(DynmapPlugin plugin, String name, int height, int sealevel, boolean nether, boolean the_end, String deftitle, int miny) {
|
||||
super(name, (height > maxWorldHeight) ? maxWorldHeight : height, sealevel, miny);
|
||||
this.plugin = plugin;
|
||||
world = null;
|
||||
setTitle(deftitle);
|
||||
isnether = nether;
|
||||
istheend = the_end;
|
||||
skylight = !(isnether || istheend);
|
||||
|
||||
if (isnether) {
|
||||
env = "nether";
|
||||
} else if (istheend) {
|
||||
env = "the_end";
|
||||
} else {
|
||||
env = "normal";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Test if world is nether */
|
||||
@Override
|
||||
public boolean isNether() {
|
||||
return isnether;
|
||||
}
|
||||
|
||||
public boolean isTheEnd() {
|
||||
return istheend;
|
||||
}
|
||||
|
||||
/* Get world spawn location */
|
||||
@Override
|
||||
public DynmapLocation getSpawnLocation() {
|
||||
if (world != null) {
|
||||
BlockPos spawnPos = world.getLevelProperties().getSpawnPos();
|
||||
spawnloc.x = spawnPos.getX();
|
||||
spawnloc.y = spawnPos.getY();
|
||||
spawnloc.z = spawnPos.getZ();
|
||||
spawnloc.world = this.getName();
|
||||
}
|
||||
return spawnloc;
|
||||
}
|
||||
|
||||
/* Get world time */
|
||||
@Override
|
||||
public long getTime() {
|
||||
if (world != null)
|
||||
return world.getTimeOfDay();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* World is storming */
|
||||
@Override
|
||||
public boolean hasStorm() {
|
||||
if (world != null)
|
||||
return world.isRaining();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* World is thundering */
|
||||
@Override
|
||||
public boolean isThundering() {
|
||||
if (world != null)
|
||||
return world.isThundering();
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* World is loaded */
|
||||
@Override
|
||||
public boolean isLoaded() {
|
||||
return (world != null);
|
||||
}
|
||||
|
||||
/* Set world to unloaded */
|
||||
@Override
|
||||
public void setWorldUnloaded() {
|
||||
getSpawnLocation();
|
||||
world = null;
|
||||
}
|
||||
|
||||
/* Set world to loaded */
|
||||
public void setWorldLoaded(World w) {
|
||||
world = w;
|
||||
this.sealevel = w.getSeaLevel(); // Read actual current sealevel from world
|
||||
// Update lighting table
|
||||
for (int lightLevel = 0; lightLevel < 16; lightLevel++) {
|
||||
// Algorithm based on LightmapTextureManager.getBrightness()
|
||||
// We can't call that method because it's client-only.
|
||||
// This means the code below can stop being correct if Mojang ever
|
||||
// updates the curve; in that case we should reflect the changes.
|
||||
float value = (float) lightLevel / 15.0f;
|
||||
float brightness = value / (4.0f - 3.0f * value);
|
||||
this.setBrightnessTableEntry(lightLevel, MathHelper.lerp(w.getDimension().ambientLight(), brightness, 1.0F));
|
||||
}
|
||||
}
|
||||
|
||||
/* Get light level of block */
|
||||
@Override
|
||||
public int getLightLevel(int x, int y, int z) {
|
||||
if (world != null)
|
||||
return world.getLightLevel(new BlockPos(x, y, z));
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Get highest Y coord of given location */
|
||||
@Override
|
||||
public int getHighestBlockYAt(int x, int z) {
|
||||
if (world != null) {
|
||||
return world.getChunk(x >> 4, z >> 4).getHeightmap(Heightmap.Type.MOTION_BLOCKING).get(x & 15, z & 15);
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Test if sky light level is requestable */
|
||||
@Override
|
||||
public boolean canGetSkyLightLevel() {
|
||||
return skylight;
|
||||
}
|
||||
|
||||
/* Return sky light level */
|
||||
@Override
|
||||
public int getSkyLightLevel(int x, int y, int z) {
|
||||
if (world != null) {
|
||||
return world.getLightLevel(LightType.SKY, new BlockPos(x, y, z));
|
||||
} else
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get world environment ID (lower case - normal, the_end, nether)
|
||||
*/
|
||||
@Override
|
||||
public String getEnvironment() {
|
||||
return env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get map chunk cache for world
|
||||
*/
|
||||
@Override
|
||||
public MapChunkCache getChunkCache(List<DynmapChunk> chunks) {
|
||||
if (world != null) {
|
||||
FabricMapChunkCache c = new FabricMapChunkCache(plugin);
|
||||
c.setChunks(this, chunks);
|
||||
return c;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public World getWorld() {
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Polygon getWorldBorder() {
|
||||
if (world != null) {
|
||||
WorldBorder wb = world.getWorldBorder();
|
||||
if ((wb != null) && (wb.getSize() < 5.9E7)) {
|
||||
Polygon p = new Polygon();
|
||||
p.addVertex(wb.getBoundWest(), wb.getBoundNorth());
|
||||
p.addVertex(wb.getBoundWest(), wb.getBoundSouth());
|
||||
p.addVertex(wb.getBoundEast(), wb.getBoundSouth());
|
||||
p.addVertex(wb.getBoundEast(), wb.getBoundNorth());
|
||||
return p;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
138
fabric-1.21.5/src/main/java/org/dynmap/fabric_1_21_5/NBT.java
Normal file
138
fabric-1.21.5/src/main/java/org/dynmap/fabric_1_21_5/NBT.java
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import org.dynmap.common.chunk.GenericBitStorage;
|
||||
import org.dynmap.common.chunk.GenericNBTCompound;
|
||||
import org.dynmap.common.chunk.GenericNBTList;
|
||||
|
||||
import java.util.Set;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.nbt.NbtList;
|
||||
import net.minecraft.util.collection.PackedIntegerArray;
|
||||
|
||||
public class NBT {
|
||||
|
||||
public static class NBTCompound implements GenericNBTCompound {
|
||||
private final NbtCompound obj;
|
||||
public NBTCompound(NbtCompound t) {
|
||||
this.obj = t;
|
||||
}
|
||||
@Override
|
||||
public Set<String> getAllKeys() {
|
||||
return obj.getKeys();
|
||||
}
|
||||
@Override
|
||||
public boolean contains(String s) {
|
||||
return obj.contains(s);
|
||||
}
|
||||
@Override
|
||||
public boolean contains(String s, int i) {
|
||||
// Like contains, but with an extra constraint on type
|
||||
NbtElement base = obj.get(s);
|
||||
if (base == null)
|
||||
return false;
|
||||
int type = base.getType();
|
||||
if (type == i)
|
||||
return true;
|
||||
else if (i != TAG_ANY_NUMERIC)
|
||||
return false;
|
||||
return type == TAG_BYTE || type == TAG_SHORT || type == TAG_INT || type == TAG_LONG || type == TAG_FLOAT
|
||||
|| type == TAG_DOUBLE;
|
||||
}
|
||||
@Override
|
||||
public byte getByte(String s) {
|
||||
return obj.getByte(s, (byte)0);
|
||||
}
|
||||
@Override
|
||||
public short getShort(String s) {
|
||||
return obj.getShort(s, (short)0);
|
||||
}
|
||||
@Override
|
||||
public int getInt(String s) {
|
||||
return obj.getInt(s, (int)0);
|
||||
}
|
||||
@Override
|
||||
public long getLong(String s) {
|
||||
return obj.getLong(s, (long)0);
|
||||
}
|
||||
@Override
|
||||
public float getFloat(String s) {
|
||||
return obj.getFloat(s, (float)0);
|
||||
}
|
||||
@Override
|
||||
public double getDouble(String s) {
|
||||
return obj.getDouble(s, (double)0);
|
||||
}
|
||||
@Override
|
||||
public String getString(String s) {
|
||||
return obj.getString(s, "");
|
||||
}
|
||||
@Override
|
||||
public byte[] getByteArray(String s) {
|
||||
return obj.getByteArray(s).orElseGet(() -> new byte[0]);
|
||||
}
|
||||
@Override
|
||||
public int[] getIntArray(String s) {
|
||||
return obj.getIntArray(s).orElseGet(() -> new int[0]);
|
||||
}
|
||||
@Override
|
||||
public long[] getLongArray(String s) {
|
||||
return obj.getLongArray(s).orElseGet(() -> new long[0]);
|
||||
}
|
||||
@Override
|
||||
public GenericNBTCompound getCompound(String s) {
|
||||
return new NBTCompound(obj.getCompoundOrEmpty(s));
|
||||
}
|
||||
@Override
|
||||
public GenericNBTList getList(String s, int i) {
|
||||
// i argument used to be used to constrain list type, but nbt lists no longer have types as of 1.21.5
|
||||
return new NBTList(obj.getListOrEmpty(s));
|
||||
}
|
||||
@Override
|
||||
public boolean getBoolean(String s) {
|
||||
return obj.getBoolean(s, false);
|
||||
}
|
||||
@Override
|
||||
public String getAsString(String s) {
|
||||
return obj.get(s).asString().orElse("");
|
||||
}
|
||||
@Override
|
||||
public GenericBitStorage makeBitStorage(int bits, int count, long[] data) {
|
||||
return new OurBitStorage(bits, count, data);
|
||||
}
|
||||
public String toString() {
|
||||
return obj.toString();
|
||||
}
|
||||
}
|
||||
public static class NBTList implements GenericNBTList {
|
||||
private final NbtList obj;
|
||||
public NBTList(NbtList t) {
|
||||
obj = t;
|
||||
}
|
||||
@Override
|
||||
public int size() {
|
||||
return obj.size();
|
||||
}
|
||||
@Override
|
||||
public String getString(int idx) {
|
||||
return obj.getString(idx, "");
|
||||
}
|
||||
@Override
|
||||
public GenericNBTCompound getCompound(int idx) {
|
||||
return new NBTCompound(obj.getCompoundOrEmpty(idx));
|
||||
}
|
||||
public String toString() {
|
||||
return obj.toString();
|
||||
}
|
||||
}
|
||||
public static class OurBitStorage implements GenericBitStorage {
|
||||
private final PackedIntegerArray bs;
|
||||
public OurBitStorage(int bits, int count, long[] data) {
|
||||
bs = new PackedIntegerArray(bits, count, data);
|
||||
}
|
||||
@Override
|
||||
public int get(int idx) {
|
||||
return bs.get(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
class TaskRecord implements Comparable<TaskRecord> {
|
||||
TaskRecord(long ticktorun, long id, FutureTask<?> future) {
|
||||
this.ticktorun = ticktorun;
|
||||
this.id = id;
|
||||
this.future = future;
|
||||
}
|
||||
|
||||
private final long ticktorun;
|
||||
private final long id;
|
||||
private final FutureTask<?> future;
|
||||
|
||||
void run() {
|
||||
this.future.run();
|
||||
}
|
||||
|
||||
long getTickToRun() {
|
||||
return this.ticktorun;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TaskRecord o) {
|
||||
if (this.ticktorun < o.ticktorun) {
|
||||
return -1;
|
||||
} else if (this.ticktorun > o.ticktorun) {
|
||||
return 1;
|
||||
} else if (this.id < o.id) {
|
||||
return -1;
|
||||
} else if (this.id > o.id) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
package org.dynmap.fabric_1_21_5;
|
||||
|
||||
import org.dynmap.DynmapCore;
|
||||
import org.dynmap.Log;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class VersionCheck {
|
||||
private static final String VERSION_URL = "http://mikeprimm.com/dynmap/releases.php";
|
||||
|
||||
public static void runCheck(final DynmapCore core) {
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
doCheck(core);
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private static int getReleaseVersion(String s) {
|
||||
int index = s.lastIndexOf('-');
|
||||
if (index < 0)
|
||||
index = s.lastIndexOf('.');
|
||||
if (index >= 0)
|
||||
s = s.substring(0, index);
|
||||
String[] split = s.split("\\.");
|
||||
int v = 0;
|
||||
try {
|
||||
for (int i = 0; (i < split.length) && (i < 3); i++) {
|
||||
v += Integer.parseInt(split[i]) << (8 * (2 - i));
|
||||
}
|
||||
} catch (NumberFormatException nfx) {
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
private static int getBuildNumber(String s) {
|
||||
int index = s.lastIndexOf('-');
|
||||
if (index < 0)
|
||||
index = s.lastIndexOf('.');
|
||||
if (index >= 0)
|
||||
s = s.substring(index + 1);
|
||||
try {
|
||||
return Integer.parseInt(s);
|
||||
} catch (NumberFormatException nfx) {
|
||||
return 99999999;
|
||||
}
|
||||
}
|
||||
|
||||
private static void doCheck(DynmapCore core) {
|
||||
String pluginver = core.getDynmapPluginVersion();
|
||||
String platform = core.getDynmapPluginPlatform();
|
||||
String platver = core.getDynmapPluginPlatformVersion();
|
||||
if ((pluginver == null) || (platform == null) || (platver == null))
|
||||
return;
|
||||
HttpURLConnection conn = null;
|
||||
String loc = VERSION_URL;
|
||||
int cur_ver = getReleaseVersion(pluginver);
|
||||
int cur_bn = getBuildNumber(pluginver);
|
||||
try {
|
||||
while ((loc != null) && (!loc.isEmpty())) {
|
||||
URL url = new URL(loc);
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setRequestProperty("User-Agent", "Dynmap (" + platform + "/" + platver + "/" + pluginver);
|
||||
conn.connect();
|
||||
loc = conn.getHeaderField("Location");
|
||||
}
|
||||
BufferedReader rdr = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
String line = null;
|
||||
while ((line = rdr.readLine()) != null) {
|
||||
String[] split = line.split(":");
|
||||
if (split.length < 4) continue;
|
||||
/* If our platform and version, or wildcard platform version */
|
||||
if (split[0].equals(platform) && (split[1].equals("*") || split[1].equals(platver))) {
|
||||
int recommended_ver = getReleaseVersion(split[2]);
|
||||
int recommended_bn = getBuildNumber(split[2]);
|
||||
if ((recommended_ver > cur_ver) || ((recommended_ver == cur_ver) && (recommended_bn > cur_bn))) { /* Newer recommended build */
|
||||
Log.info("Version obsolete: new recommended version " + split[2] + " is available.");
|
||||
} else if (cur_ver > recommended_ver) { /* Running dev or prerelease? */
|
||||
int prerel_ver = getReleaseVersion(split[3]);
|
||||
int prerel_bn = getBuildNumber(split[3]);
|
||||
if ((prerel_ver > cur_ver) || ((prerel_ver == cur_ver) && (prerel_bn > cur_bn))) {
|
||||
Log.info("Version obsolete: new prerelease version " + split[3] + " is available.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception x) {
|
||||
Log.info("Error checking for latest version");
|
||||
} finally {
|
||||
if (conn != null) {
|
||||
conn.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package org.dynmap.fabric_1_21_5.access;
|
||||
|
||||
public interface ProtoChunkAccessor {
|
||||
boolean getTouchedByWorldGen();
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.dynmap.fabric_1_21_5.command;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
|
||||
public class DmapCommand extends DynmapCommandExecutor {
|
||||
public DmapCommand(DynmapPlugin p) {
|
||||
super("dmap", p);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.dynmap.fabric_1_21_5.command;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
|
||||
public class DmarkerCommand extends DynmapCommandExecutor {
|
||||
public DmarkerCommand(DynmapPlugin p) {
|
||||
super("dmarker", p);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.dynmap.fabric_1_21_5.command;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
|
||||
public class DynmapCommand extends DynmapCommandExecutor {
|
||||
public DynmapCommand(DynmapPlugin p) {
|
||||
super("dynmap", p);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
package org.dynmap.fabric_1_21_5.command;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.tree.ArgumentCommandNode;
|
||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||
import com.mojang.brigadier.tree.RootCommandNode;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
|
||||
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
|
||||
import static net.minecraft.server.command.CommandManager.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class DynmapCommandExecutor implements Command<ServerCommandSource> {
|
||||
private final String cmd;
|
||||
private final DynmapPlugin plugin;
|
||||
|
||||
DynmapCommandExecutor(String cmd, DynmapPlugin plugin) {
|
||||
this.cmd = cmd;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
public void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
final RootCommandNode<ServerCommandSource> root = dispatcher.getRoot();
|
||||
|
||||
final LiteralCommandNode<ServerCommandSource> command = literal(this.cmd)
|
||||
.executes(this)
|
||||
.build();
|
||||
|
||||
final ArgumentCommandNode<ServerCommandSource, String> args = argument("args", greedyString())
|
||||
.executes(this)
|
||||
.build();
|
||||
|
||||
// So this becomes "cmd" [args]
|
||||
command.addChild(args);
|
||||
|
||||
// Add command to the command dispatcher via root node.
|
||||
root.addChild(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int run(CommandContext<ServerCommandSource> context) throws CommandSyntaxException {
|
||||
// Commands in brigadier may be proxied in Minecraft via a syntax like `/execute ... ... run dmap [args]`
|
||||
// Dynmap will fail to parse this properly, so we find the starting position of the actual command being parsed after any forks or redirects.
|
||||
// The start position of the range specifies where the actual command dynmap has registered starts
|
||||
int start = context.getRange().getStart();
|
||||
String dynmapInput = context.getInput().substring(start);
|
||||
|
||||
String[] args = dynmapInput.split("\\s+");
|
||||
plugin.handleCommand(context.getSource(), cmd, Arrays.copyOfRange(args, 1, args.length));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// @Override // TODO: Usage?
|
||||
public String getUsage(ServerCommandSource commandSource) {
|
||||
return "Run /" + cmd + " help for details on using command";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package org.dynmap.fabric_1_21_5.command;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
|
||||
public class DynmapExpCommand extends DynmapCommandExecutor {
|
||||
public DynmapExpCommand(DynmapPlugin p) {
|
||||
super("dynmapexp", p);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package org.dynmap.fabric_1_21_5.event;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class BlockEvents {
|
||||
private BlockEvents() {
|
||||
}
|
||||
|
||||
public static Event<BlockCallback> BLOCK_EVENT = EventFactory.createArrayBacked(BlockCallback.class,
|
||||
(listeners) -> (world, pos) -> {
|
||||
for (BlockCallback callback : listeners) {
|
||||
callback.onBlockEvent(world, pos);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
public static Event<SignChangeCallback> SIGN_CHANGE_EVENT = EventFactory.createArrayBacked(SignChangeCallback.class,
|
||||
(listeners) -> (world, pos, lines, player, front) -> {
|
||||
for (SignChangeCallback callback : listeners) {
|
||||
callback.onSignChange(world, pos, lines, player, front);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@FunctionalInterface
|
||||
public interface BlockCallback {
|
||||
void onBlockEvent(World world, BlockPos pos);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SignChangeCallback {
|
||||
void onSignChange(ServerWorld world, BlockPos pos, String[] lines, ServerPlayerEntity player, boolean front);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package org.dynmap.fabric_1_21_5.event;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
|
||||
public class CustomServerChunkEvents {
|
||||
public static Event<ChunkGenerate> CHUNK_GENERATE = EventFactory.createArrayBacked(ChunkGenerate.class,
|
||||
(listeners) -> (world, chunk) -> {
|
||||
for (ChunkGenerate callback : listeners) {
|
||||
callback.onChunkGenerate(world, chunk);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ChunkGenerate {
|
||||
void onChunkGenerate(ServerWorld world, Chunk chunk);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package org.dynmap.fabric_1_21_5.event;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
|
||||
public class CustomServerLifecycleEvents {
|
||||
public static final Event<ServerLifecycleEvents.ServerStarted> SERVER_STARTED_PRE_WORLD_LOAD =
|
||||
EventFactory.createArrayBacked(ServerLifecycleEvents.ServerStarted.class, (callbacks) -> (server) -> {
|
||||
for (ServerLifecycleEvents.ServerStarted callback : callbacks) {
|
||||
callback.onServerStarted(server);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package org.dynmap.fabric_1_21_5.event;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
public class PlayerEvents {
|
||||
private PlayerEvents() {
|
||||
}
|
||||
|
||||
public static Event<PlayerLoggedIn> PLAYER_LOGGED_IN = EventFactory.createArrayBacked(PlayerLoggedIn.class,
|
||||
(listeners) -> (player) -> {
|
||||
for (PlayerLoggedIn callback : listeners) {
|
||||
callback.onPlayerLoggedIn(player);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
public static Event<PlayerLoggedOut> PLAYER_LOGGED_OUT = EventFactory.createArrayBacked(PlayerLoggedOut.class,
|
||||
(listeners) -> (player) -> {
|
||||
for (PlayerLoggedOut callback : listeners) {
|
||||
callback.onPlayerLoggedOut(player);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
public static Event<PlayerChangedDimension> PLAYER_CHANGED_DIMENSION = EventFactory.createArrayBacked(PlayerChangedDimension.class,
|
||||
(listeners) -> (player) -> {
|
||||
for (PlayerChangedDimension callback : listeners) {
|
||||
callback.onPlayerChangedDimension(player);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
public static Event<PlayerRespawn> PLAYER_RESPAWN = EventFactory.createArrayBacked(PlayerRespawn.class,
|
||||
(listeners) -> (player) -> {
|
||||
for (PlayerRespawn callback : listeners) {
|
||||
callback.onPlayerRespawn(player);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlayerLoggedIn {
|
||||
void onPlayerLoggedIn(ServerPlayerEntity player);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlayerLoggedOut {
|
||||
void onPlayerLoggedOut(ServerPlayerEntity player);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlayerChangedDimension {
|
||||
void onPlayerChangedDimension(ServerPlayerEntity player);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface PlayerRespawn {
|
||||
void onPlayerRespawn(ServerPlayerEntity player);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
package org.dynmap.fabric_1_21_5.event;
|
||||
|
||||
import net.fabricmc.fabric.api.event.Event;
|
||||
import net.fabricmc.fabric.api.event.EventFactory;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
public class ServerChatEvents {
|
||||
private ServerChatEvents() {
|
||||
}
|
||||
|
||||
public static Event<ServerChatCallback> EVENT = EventFactory.createArrayBacked(ServerChatCallback.class,
|
||||
(listeners) -> (player, message) -> {
|
||||
for (ServerChatCallback callback : listeners) {
|
||||
callback.onChatMessage(player, message);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ServerChatCallback {
|
||||
void onChatMessage(ServerPlayerEntity player, String message);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package org.dynmap.fabric_1_21_5.mixin;
|
||||
|
||||
import net.minecraft.world.biome.BiomeEffects;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||
|
||||
@Mixin(BiomeEffects.class)
|
||||
public interface BiomeEffectsAccessor {
|
||||
@Accessor
|
||||
int getWaterColor();
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package org.dynmap.fabric_1_21_5.mixin;
|
||||
|
||||
import net.minecraft.world.chunk.ChunkGenerating;
|
||||
import net.minecraft.world.chunk.ChunkGenerationContext;
|
||||
import net.minecraft.world.chunk.AbstractChunkHolder;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.access.ProtoChunkAccessor;
|
||||
import org.dynmap.fabric_1_21_5.event.CustomServerChunkEvents;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(value = ChunkGenerating.class, priority = 666 /* fire before Fabric API CHUNK_LOAD event */)
|
||||
public abstract class ChunkGeneratingMixin {
|
||||
@Inject(
|
||||
/* Same place as fabric-lifecycle-events-v1 event CHUNK_LOAD (we will fire before it) */
|
||||
method = "method_60553",
|
||||
at = @At("TAIL")
|
||||
)
|
||||
private static void onChunkGenerate(Chunk chunk, ChunkGenerationContext chunkGenerationContext, AbstractChunkHolder chunkHolder, CallbackInfoReturnable<Chunk> callbackInfoReturnable) {
|
||||
if (((ProtoChunkAccessor)chunk).getTouchedByWorldGen()) {
|
||||
CustomServerChunkEvents.CHUNK_GENERATE.invoker().onChunkGenerate(chunkGenerationContext.world(), callbackInfoReturnable.getReturnValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
package org.dynmap.fabric_1_21_5.mixin;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.event.CustomServerLifecycleEvents;
|
||||
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;
|
||||
|
||||
@Mixin(MinecraftServer.class)
|
||||
public class MinecraftServerMixin {
|
||||
@Inject(method = "loadWorld", at = @At("HEAD"))
|
||||
protected void loadWorld(CallbackInfo info) {
|
||||
CustomServerLifecycleEvents.SERVER_STARTED_PRE_WORLD_LOAD.invoker().onServerStarted((MinecraftServer) (Object) this);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.dynmap.fabric_1_21_5.mixin;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.network.ClientConnection;
|
||||
import net.minecraft.server.PlayerManager;
|
||||
import net.minecraft.server.network.ConnectedClientData;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.event.PlayerEvents;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(PlayerManager.class)
|
||||
public class PlayerManagerMixin {
|
||||
@Inject(method = "onPlayerConnect", at = @At("TAIL"))
|
||||
public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player, ConnectedClientData ccd, CallbackInfo info) {
|
||||
PlayerEvents.PLAYER_LOGGED_IN.invoker().onPlayerLoggedIn(player);
|
||||
}
|
||||
|
||||
@Inject(method = "remove", at = @At("HEAD"))
|
||||
public void remove(ServerPlayerEntity player, CallbackInfo info) {
|
||||
PlayerEvents.PLAYER_LOGGED_OUT.invoker().onPlayerLoggedOut(player);
|
||||
}
|
||||
|
||||
@Inject(method = "respawnPlayer", at = @At("RETURN"))
|
||||
public void respawnPlayer(ServerPlayerEntity player, boolean alive, Entity.RemovalReason removalReason, CallbackInfoReturnable<ServerPlayerEntity> info) {
|
||||
PlayerEvents.PLAYER_RESPAWN.invoker().onPlayerRespawn(info.getReturnValue());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package org.dynmap.fabric_1_21_5.mixin;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.chunk.ProtoChunk;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.access.ProtoChunkAccessor;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ProtoChunk.class)
|
||||
public class ProtoChunkMixin implements ProtoChunkAccessor {
|
||||
private boolean touchedByWorldGen = false;
|
||||
|
||||
@Inject(
|
||||
method = "setBlockState",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/chunk/ChunkSection;setBlockState(IIILnet/minecraft/block/BlockState;)Lnet/minecraft/block/BlockState;"
|
||||
)
|
||||
)
|
||||
public void setBlockState(BlockPos pos, BlockState state, int flags, CallbackInfoReturnable<BlockState> info) {
|
||||
touchedByWorldGen = true;
|
||||
}
|
||||
|
||||
public boolean getTouchedByWorldGen() {
|
||||
return touchedByWorldGen;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
package org.dynmap.fabric_1_21_5.mixin;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.SignBlockEntity;
|
||||
import net.minecraft.network.message.FilterMask;
|
||||
import net.minecraft.network.message.SignedMessage;
|
||||
import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket;
|
||||
import net.minecraft.server.filter.FilteredMessage;
|
||||
import net.minecraft.server.filter.TextStream;
|
||||
import net.minecraft.server.network.ServerPlayNetworkHandler;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.event.BlockEvents;
|
||||
import org.dynmap.fabric_1_21_5.event.ServerChatEvents;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
@Mixin(ServerPlayNetworkHandler.class)
|
||||
public abstract class ServerPlayNetworkHandlerMixin {
|
||||
@Shadow
|
||||
public ServerPlayerEntity player;
|
||||
|
||||
@Inject(
|
||||
method = "handleDecoratedMessage",
|
||||
at = @At(
|
||||
value = "HEAD"
|
||||
)
|
||||
)
|
||||
public void onGameMessage(SignedMessage signedMessage, CallbackInfo ci) {
|
||||
ServerChatEvents.EVENT.invoker().onChatMessage(player, signedMessage.getContent().getString());
|
||||
}
|
||||
|
||||
@Inject(
|
||||
method = "onSignUpdate",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/block/entity/SignBlockEntity;tryChangeText(Lnet/minecraft/entity/player/PlayerEntity;ZLjava/util/List;)V",
|
||||
shift = At.Shift.BEFORE
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD,
|
||||
cancellable = true
|
||||
)
|
||||
public void onSignUpdate(UpdateSignC2SPacket packet, List<FilteredMessage> signText, CallbackInfo ci,
|
||||
ServerWorld serverWorld, BlockPos blockPos, BlockEntity blockEntity, SignBlockEntity signBlockEntity)
|
||||
{
|
||||
// Pull the raw text from the input.
|
||||
String[] rawTexts = new String[4];
|
||||
for (int i=0; i<signText.size(); i++)
|
||||
rawTexts[i] = signText.get(i).raw();
|
||||
|
||||
// Fire the event.
|
||||
BlockEvents.SIGN_CHANGE_EVENT.invoker().onSignChange(serverWorld, blockPos, rawTexts, player, packet.isFront());
|
||||
|
||||
// Rebuild the signText list with the new values.
|
||||
List<FilteredMessage> newSignText = Arrays.stream(rawTexts).map((raw) -> new FilteredMessage(raw, FilterMask.PASS_THROUGH)).toList();
|
||||
|
||||
// Execute the setting of the texts with the edited values.
|
||||
signBlockEntity.tryChangeText(this.player, packet.isFront(), newSignText);
|
||||
|
||||
// Cancel the original tryChangeText() since we're calling it ourselves above.
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
package org.dynmap.fabric_1_21_5.mixin;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.world.TeleportTarget;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.event.PlayerEvents;
|
||||
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.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(ServerPlayerEntity.class)
|
||||
public class ServerPlayerEntityMixin {
|
||||
// @Inject(method = "teleport(Lnet/minecraft/server/world/ServerWorld;DDDFF)V", at = @At("RETURN"))
|
||||
// public void teleport(ServerWorld targetWorld, double x, double y, double z, float yaw, float pitch, CallbackInfo info) {
|
||||
// ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;
|
||||
// if (targetWorld != player.getServerWorld()) {
|
||||
// PlayerEvents.PLAYER_CHANGED_DIMENSION.invoker().onPlayerChangedDimension(player);
|
||||
// }
|
||||
// }
|
||||
|
||||
@Inject(method = "teleportTo(Lnet/minecraft/world/TeleportTarget;)Lnet/minecraft/entity/Entity;", at = @At("TAIL"))
|
||||
public void teleportTo(TeleportTarget teleportTarget, CallbackInfoReturnable<Entity> info) {
|
||||
ServerPlayerEntity player = (ServerPlayerEntity) (Object) this;
|
||||
if (player.getRemovalReason() == null) {
|
||||
PlayerEvents.PLAYER_CHANGED_DIMENSION.invoker().onPlayerChangedDimension(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package org.dynmap.fabric_1_21_5.mixin;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
|
||||
import org.dynmap.fabric_1_21_5.event.BlockEvents;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
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.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(WorldChunk.class)
|
||||
public abstract class WorldChunkMixin {
|
||||
@Shadow
|
||||
public abstract World getWorld();
|
||||
|
||||
@Inject(method = "setBlockState", at = @At("RETURN"))
|
||||
public void setBlockState(BlockPos pos, BlockState state, int flags, CallbackInfoReturnable<BlockState> info) {
|
||||
if (info.getReturnValue() != null) {
|
||||
BlockEvents.BLOCK_EVENT.invoker().onBlockEvent(this.getWorld(), pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package org.dynmap.fabric_1_21_5.permissions;
|
||||
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
import org.dynmap.json.simple.parser.JSONParser;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FabricPermissions implements PermissionProvider {
|
||||
|
||||
private String permissionKey(String perm) {
|
||||
return "dynmap." + perm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> hasOfflinePermissions(String player, Set<String> perms) {
|
||||
return perms.stream()
|
||||
.filter(perm -> hasOfflinePermission(player, perm))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOfflinePermission(String player, String perm) {
|
||||
return DynmapPlugin.plugin.isOp(player.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(PlayerEntity player, String permission) {
|
||||
if (player == null) return false;
|
||||
String name = player.getName().getString().toLowerCase();
|
||||
if (DynmapPlugin.plugin.isOp(name)) return true;
|
||||
return Permissions.check(player, permissionKey(permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermissionNode(PlayerEntity player, String permission) {
|
||||
if (player != null) {
|
||||
String name = player.getName().getString().toLowerCase();
|
||||
return DynmapPlugin.plugin.isOp(name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,103 @@
|
|||
package org.dynmap.fabric_1_21_5.permissions;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import org.dynmap.ConfigurationNode;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class FilePermissions implements PermissionProvider {
|
||||
private HashMap<String, Set<String>> perms;
|
||||
private Set<String> defperms;
|
||||
|
||||
public static FilePermissions create() {
|
||||
File f = new File("dynmap/permissions.yml");
|
||||
if (!f.exists())
|
||||
return null;
|
||||
ConfigurationNode cfg = new ConfigurationNode(f);
|
||||
cfg.load();
|
||||
|
||||
Log.info("Using permissions.yml for access control");
|
||||
|
||||
return new FilePermissions(cfg);
|
||||
}
|
||||
|
||||
private FilePermissions(ConfigurationNode cfg) {
|
||||
perms = new HashMap<String, Set<String>>();
|
||||
for (String k : cfg.keySet()) {
|
||||
List<String> p = cfg.getStrings(k, null);
|
||||
if (p != null) {
|
||||
k = k.toLowerCase();
|
||||
HashSet<String> pset = new HashSet<String>();
|
||||
for (String perm : p) {
|
||||
pset.add(perm.toLowerCase());
|
||||
}
|
||||
perms.put(k, pset);
|
||||
if (k.equals("defaultuser")) {
|
||||
defperms = pset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean hasPerm(String player, String perm) {
|
||||
Set<String> ps = perms.get(player);
|
||||
if ((ps != null) && (ps.contains(perm))) {
|
||||
return true;
|
||||
}
|
||||
if (defperms.contains(perm)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> hasOfflinePermissions(String player, Set<String> perms) {
|
||||
player = player.toLowerCase();
|
||||
HashSet<String> rslt = new HashSet<String>();
|
||||
if (DynmapPlugin.plugin.isOp(player)) {
|
||||
rslt.addAll(perms);
|
||||
} else {
|
||||
for (String p : perms) {
|
||||
if (hasPerm(player, p)) {
|
||||
rslt.add(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
return rslt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOfflinePermission(String player, String perm) {
|
||||
player = player.toLowerCase();
|
||||
if (DynmapPlugin.plugin.isOp(player)) {
|
||||
return true;
|
||||
} else {
|
||||
return hasPerm(player, perm);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(PlayerEntity psender, String permission) {
|
||||
if (psender != null) {
|
||||
String n = psender.getName().getString().toLowerCase();
|
||||
return hasPerm(n, permission);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermissionNode(PlayerEntity psender, String permission) {
|
||||
if (psender != null) {
|
||||
String player = psender.getName().getString().toLowerCase();
|
||||
return DynmapPlugin.plugin.isOp(player);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
package org.dynmap.fabric_1_21_5.permissions;
|
||||
|
||||
import me.lucko.fabric.api.permissions.v0.Permissions;
|
||||
import net.luckperms.api.LuckPerms;
|
||||
import net.luckperms.api.LuckPermsProvider;
|
||||
import net.luckperms.api.cacheddata.CachedPermissionData;
|
||||
import net.luckperms.api.model.user.User;
|
||||
import net.luckperms.api.util.Tristate;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
import org.dynmap.json.simple.JSONArray;
|
||||
import org.dynmap.json.simple.JSONObject;
|
||||
import org.dynmap.json.simple.parser.JSONParser;
|
||||
import org.dynmap.json.simple.parser.ParseException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class LuckPermissions implements PermissionProvider {
|
||||
|
||||
private final JSONParser parser = new JSONParser();
|
||||
private LuckPerms api = null;
|
||||
|
||||
private Optional<LuckPerms> getApi() {
|
||||
if (api != null) return Optional.of(api);
|
||||
try {
|
||||
api = LuckPermsProvider.get();
|
||||
return Optional.of(api);
|
||||
} catch (Exception ex) {
|
||||
Log.warning("Trying to access LuckPerms before it has loaded");
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
private Optional<UUID> cachedUUID(String username) {
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader("usercache.json"));
|
||||
JSONArray cache = (JSONArray) parser.parse(reader);
|
||||
for (Object it : cache) {
|
||||
JSONObject user = (JSONObject) it;
|
||||
if (user.get("name").toString().equalsIgnoreCase(username)) {
|
||||
String uuid = user.get("uuid").toString();
|
||||
return Optional.of(UUID.fromString(uuid));
|
||||
}
|
||||
}
|
||||
|
||||
reader.close();
|
||||
} catch (IOException | ParseException ex) {
|
||||
Log.warning("Unable to read usercache.json");
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private String permissionKey(String perm) {
|
||||
return "dynmap." + perm;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> hasOfflinePermissions(String player, Set<String> perms) {
|
||||
return perms.stream()
|
||||
.filter(perm -> hasOfflinePermission(player, perm))
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOfflinePermission(String player, String perm) {
|
||||
if (DynmapPlugin.plugin.isOp(player.toLowerCase())) return true;
|
||||
Optional<LuckPerms> api = getApi();
|
||||
Optional<UUID> uuid = cachedUUID(player);
|
||||
if (!uuid.isPresent() || !api.isPresent()) return false;
|
||||
User user = api.get().getUserManager().loadUser(uuid.get()).join();
|
||||
CachedPermissionData permissions = user.getCachedData().getPermissionData();
|
||||
Tristate state = permissions.checkPermission(permissionKey(perm));
|
||||
return state.asBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(PlayerEntity player, String permission) {
|
||||
if (player == null) return false;
|
||||
String name = player.getName().getString().toLowerCase();
|
||||
if (DynmapPlugin.plugin.isOp(name)) return true;
|
||||
return Permissions.check(player, permissionKey(permission));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermissionNode(PlayerEntity player, String permission) {
|
||||
if (player != null) {
|
||||
String name = player.getName().getString().toLowerCase();
|
||||
return DynmapPlugin.plugin.isOp(name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package org.dynmap.fabric_1_21_5.permissions;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import org.dynmap.Log;
|
||||
import org.dynmap.fabric_1_21_5.DynmapPlugin;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class OpPermissions implements PermissionProvider {
|
||||
public HashSet<String> usrCommands = new HashSet<String>();
|
||||
|
||||
public OpPermissions(String[] usrCommands) {
|
||||
for (String usrCommand : usrCommands) {
|
||||
this.usrCommands.add(usrCommand);
|
||||
}
|
||||
Log.info("Using ops.txt for access control");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> hasOfflinePermissions(String player, Set<String> perms) {
|
||||
HashSet<String> rslt = new HashSet<String>();
|
||||
if (DynmapPlugin.plugin.isOp(player)) {
|
||||
rslt.addAll(perms);
|
||||
}
|
||||
return rslt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasOfflinePermission(String player, String perm) {
|
||||
return DynmapPlugin.plugin.isOp(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(PlayerEntity psender, String permission) {
|
||||
if (psender != null) {
|
||||
if (usrCommands.contains(permission)) {
|
||||
return true;
|
||||
}
|
||||
return DynmapPlugin.plugin.isOp(psender.getName().getString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermissionNode(PlayerEntity psender, String permission) {
|
||||
if (psender != null) {
|
||||
return DynmapPlugin.plugin.isOp(psender.getName().getString());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package org.dynmap.fabric_1_21_5.permissions;
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface PermissionProvider {
|
||||
boolean has(PlayerEntity sender, String permission);
|
||||
|
||||
boolean hasPermissionNode(PlayerEntity sender, String permission);
|
||||
|
||||
Set<String> hasOfflinePermissions(String player, Set<String> perms);
|
||||
|
||||
boolean hasOfflinePermission(String player, String perm);
|
||||
|
||||
}
|
||||
BIN
fabric-1.21.5/src/main/resources/assets/dynmap/icon.png
Normal file
BIN
fabric-1.21.5/src/main/resources/assets/dynmap/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
498
fabric-1.21.5/src/main/resources/configuration.txt
Normal file
498
fabric-1.21.5/src/main/resources/configuration.txt
Normal file
|
|
@ -0,0 +1,498 @@
|
|||
# All paths in this configuration file are relative to Dynmap's data-folder: minecraft_server/dynmap/
|
||||
|
||||
# All map templates are defined in the templates directory
|
||||
# To use the HDMap very-low-res (2 ppb) map templates as world defaults, set value to vlowres
|
||||
# The definitions of these templates are in normal-vlowres.txt, nether-vlowres.txt, and the_end-vlowres.txt
|
||||
# To use the HDMap low-res (4 ppb) map templates as world defaults, set value to lowres
|
||||
# The definitions of these templates are in normal-lowres.txt, nether-lowres.txt, and the_end-lowres.txt
|
||||
# To use the HDMap hi-res (16 ppb) map templates (these can take a VERY long time for initial fullrender), set value to hires
|
||||
# The definitions of these templates are in normal-hires.txt, nether-hires.txt, and the_end-hires.txt
|
||||
# To use the HDMap low-res (4 ppb) map templates, with support for boosting resolution selectively to hi-res (16 ppb), set value to low_boost_hi
|
||||
# The definitions of these templates are in normal-low_boost_hi.txt, nether-low_boost_hi.txt, and the_end-low_boost_hi.txt
|
||||
# To use the HDMap hi-res (16 ppb) map templates, with support for boosting resolution selectively to vhi-res (32 ppb), set value to hi_boost_vhi
|
||||
# The definitions of these templates are in normal-hi_boost_vhi.txt, nether-hi_boost_vhi.txt, and the_end-hi_boost_vhi.txt
|
||||
# To use the HDMap hi-res (16 ppb) map templates, with support for boosting resolution selectively to xhi-res (64 ppb), set value to hi_boost_xhi
|
||||
# The definitions of these templates are in normal-hi_boost_xhi.txt, nether-hi_boost_xhi.txt, and the_end-hi_boost_xhi.txt
|
||||
deftemplatesuffix: hires
|
||||
|
||||
# Set default tile scale (0 = 128px x 128x, 1 = 256px x 256px, 2 = 512px x 512px, 3 = 1024px x 1024px, 4 = 2048px x 2048px) - 0 is default
|
||||
# Note: changing this value will result in all maps that use the default value being required to be fully rendered
|
||||
#defaulttilescale: 0
|
||||
|
||||
# Map storage scheme: only uncommoent one 'type' value
|
||||
# filetree: classic and default scheme: tree of files, with all map data under the directory indicated by 'tilespath' setting
|
||||
# sqlite: single SQLite database file (this can get VERY BIG), located at 'dbfile' setting (default is file dynmap.db in data directory)
|
||||
# mysql: MySQL database, at hostname:port in database, accessed via userid with password
|
||||
# mariadb: MariaDB database, at hostname:port in database, accessed via userid with password
|
||||
# postgres: PostgreSQL database, at hostname:port in database, accessed via userid with password
|
||||
storage:
|
||||
# Filetree storage (standard tree of image files for maps)
|
||||
type: filetree
|
||||
# SQLite db for map storage (uses dbfile as storage location)
|
||||
#type: sqlite
|
||||
#dbfile: dynmap.db
|
||||
# MySQL DB for map storage (at 'hostname':'port' in database 'database' using user 'userid' password 'password' and table prefix 'prefix'
|
||||
#type: mysql
|
||||
#hostname: localhost
|
||||
#port: 3306
|
||||
#database: dynmap
|
||||
#userid: dynmap
|
||||
#password: dynmap
|
||||
#prefix: ""
|
||||
#
|
||||
# AWS S3 backet web site
|
||||
#type: aws_s3
|
||||
#bucketname: "dynmap-bucket-name"
|
||||
#region: us-east-1
|
||||
#aws_access_key_id: "<aws-access-key-id>"
|
||||
#aws_secret_access_key: "<aws-secret-access-key>"
|
||||
#prefix: ""
|
||||
#override_endpoint: ""
|
||||
|
||||
components:
|
||||
- class: org.dynmap.ClientConfigurationComponent
|
||||
|
||||
# Remember to change the following class to org.dynmap.JsonFileClientUpdateComponent when using an external web server.
|
||||
- class: org.dynmap.InternalClientUpdateComponent
|
||||
sendhealth: true
|
||||
sendposition: true
|
||||
allowwebchat: true
|
||||
webchat-interval: 5
|
||||
hidewebchatip: false
|
||||
trustclientname: false
|
||||
includehiddenplayers: false
|
||||
# (optional) if true, color codes in player display names are used
|
||||
use-name-colors: false
|
||||
# (optional) if true, player login IDs will be used for web chat when their IPs match
|
||||
use-player-login-ip: true
|
||||
# (optional) if use-player-login-ip is true, setting this to true will cause chat messages not matching a known player IP to be ignored
|
||||
require-player-login-ip: false
|
||||
# (optional) block player login IDs that are banned from chatting
|
||||
block-banned-player-chat: true
|
||||
# Require login for web-to-server chat (requires login-enabled: true)
|
||||
webchat-requires-login: false
|
||||
# If set to true, users must have dynmap.webchat permission in order to chat
|
||||
webchat-permissions: false
|
||||
# Limit length of single chat messages
|
||||
chatlengthlimit: 256
|
||||
# # Optional - make players hidden when they are inside/underground/in shadows (#=light level: 0=full shadow,15=sky)
|
||||
# hideifshadow: 4
|
||||
# # Optional - make player hidden when they are under cover (#=sky light level,0=underground,15=open to sky)
|
||||
# hideifundercover: 14
|
||||
# # (Optional) if true, players that are crouching/sneaking will be hidden
|
||||
hideifsneaking: false
|
||||
# optional, if true, players that are in spectator mode will be hidden
|
||||
hideifspectator: false
|
||||
# If true, player positions/status is protected (login with ID with dynmap.playermarkers.seeall permission required for info other than self)
|
||||
protected-player-info: false
|
||||
# If true, hide players with invisibility potion effects active
|
||||
hide-if-invisiblity-potion: true
|
||||
# If true, player names are not shown on map, chat, list
|
||||
hidenames: false
|
||||
#- class: org.dynmap.JsonFileClientUpdateComponent
|
||||
# writeinterval: 1
|
||||
# sendhealth: true
|
||||
# sendposition: true
|
||||
# allowwebchat: true
|
||||
# webchat-interval: 5
|
||||
# hidewebchatip: false
|
||||
# includehiddenplayers: false
|
||||
# use-name-colors: false
|
||||
# use-player-login-ip: false
|
||||
# require-player-login-ip: false
|
||||
# block-banned-player-chat: true
|
||||
# hideifshadow: 0
|
||||
# hideifundercover: 0
|
||||
# hideifsneaking: false
|
||||
# # Require login for web-to-server chat (requires login-enabled: true)
|
||||
# webchat-requires-login: false
|
||||
# # If set to true, users must have dynmap.webchat permission in order to chat
|
||||
# webchat-permissions: false
|
||||
# # Limit length of single chat messages
|
||||
# chatlengthlimit: 256
|
||||
# hide-if-invisiblity-potion: true
|
||||
# hidenames: false
|
||||
|
||||
- class: org.dynmap.SimpleWebChatComponent
|
||||
allowchat: true
|
||||
# If true, web UI users can supply name for chat using 'playername' URL parameter. 'trustclientname' must also be set true.
|
||||
allowurlname: false
|
||||
|
||||
# Note: this component is needed for the dmarker commands, and for the Marker API to be available to other plugins
|
||||
- class: org.dynmap.MarkersComponent
|
||||
type: markers
|
||||
showlabel: false
|
||||
enablesigns: false
|
||||
# Default marker set for sign markers
|
||||
default-sign-set: markers
|
||||
# (optional) add spawn point markers to standard marker layer
|
||||
showspawn: true
|
||||
spawnicon: world
|
||||
spawnlabel: "Spawn"
|
||||
# (optional) layer for showing offline player's positions (for 'maxofflinetime' minutes after logoff)
|
||||
showofflineplayers: false
|
||||
offlinelabel: "Offline"
|
||||
offlineicon: offlineuser
|
||||
offlinehidebydefault: true
|
||||
offlineminzoom: 0
|
||||
maxofflinetime: 30
|
||||
# (optional) layer for showing player's spawn beds
|
||||
showspawnbeds: false
|
||||
spawnbedlabel: "Spawn Beds"
|
||||
spawnbedicon: bed
|
||||
spawnbedhidebydefault: true
|
||||
spawnbedminzoom: 0
|
||||
spawnbedformat: "%name%'s bed"
|
||||
# (optional) Show world border (vanilla 1.8+)
|
||||
showworldborder: true
|
||||
worldborderlabel: "Border"
|
||||
|
||||
- class: org.dynmap.ClientComponent
|
||||
type: chat
|
||||
allowurlname: false
|
||||
- class: org.dynmap.ClientComponent
|
||||
type: chatballoon
|
||||
focuschatballoons: false
|
||||
- class: org.dynmap.ClientComponent
|
||||
type: chatbox
|
||||
showplayerfaces: true
|
||||
messagettl: 5
|
||||
# Optional: set number of lines in scrollable message history: if set, messagettl is not used to age out messages
|
||||
#scrollback: 100
|
||||
# Optional: set maximum number of lines visible for chatbox
|
||||
#visiblelines: 10
|
||||
# Optional: send push button
|
||||
sendbutton: false
|
||||
- class: org.dynmap.ClientComponent
|
||||
type: playermarkers
|
||||
showplayerfaces: true
|
||||
showplayerhealth: true
|
||||
# If true, show player body too (only valid if showplayerfaces=true)
|
||||
showplayerbody: false
|
||||
# Option to make player faces small - don't use with showplayerhealth or largeplayerfaces
|
||||
smallplayerfaces: false
|
||||
# Option to make player faces larger - don't use with showplayerhealth or smallplayerfaces
|
||||
largeplayerfaces: false
|
||||
# Optional - make player faces layer hidden by default
|
||||
hidebydefault: false
|
||||
# Optional - ordering priority in layer menu (low goes before high - default is 0)
|
||||
layerprio: 0
|
||||
# Optional - label for player marker layer (default is 'Players')
|
||||
label: "Players"
|
||||
|
||||
#- class: org.dynmap.ClientComponent
|
||||
# type: digitalclock
|
||||
- class: org.dynmap.ClientComponent
|
||||
type: link
|
||||
|
||||
- class: org.dynmap.ClientComponent
|
||||
type: timeofdayclock
|
||||
showdigitalclock: true
|
||||
#showweather: true
|
||||
# Mouse pointer world coordinate display
|
||||
- class: org.dynmap.ClientComponent
|
||||
type: coord
|
||||
label: "Location"
|
||||
hidey: false
|
||||
show-mcr: false
|
||||
show-chunk: false
|
||||
|
||||
# Note: more than one logo component can be defined
|
||||
#- class: org.dynmap.ClientComponent
|
||||
# type: logo
|
||||
# text: "Dynmap"
|
||||
# #logourl: "images/block_surface.png"
|
||||
# linkurl: "http://forums.bukkit.org/threads/dynmap.489/"
|
||||
# # Valid positions: top-left, top-right, bottom-left, bottom-right
|
||||
# position: bottom-right
|
||||
|
||||
#- class: org.dynmap.ClientComponent
|
||||
# type: inactive
|
||||
# timeout: 1800 # in seconds (1800 seconds = 30 minutes)
|
||||
# redirecturl: inactive.html
|
||||
# #showmessage: 'You were inactive for too long.'
|
||||
|
||||
#- class: org.dynmap.TestComponent
|
||||
# stuff: "This is some configuration-value"
|
||||
|
||||
# Treat hiddenplayers.txt as a whitelist for players to be shown on the map? (Default false)
|
||||
display-whitelist: false
|
||||
|
||||
# How often a tile gets rendered (in seconds).
|
||||
renderinterval: 1
|
||||
|
||||
# How many tiles on update queue before accelerate render interval
|
||||
renderacceleratethreshold: 60
|
||||
|
||||
# How often to render tiles when backlog is above renderacceleratethreshold
|
||||
renderaccelerateinterval: 0.2
|
||||
|
||||
# How many update tiles to work on at once (if not defined, default is 1/2 the number of cores)
|
||||
tiles-rendered-at-once: 2
|
||||
|
||||
# If true, use normal priority threads for rendering (versus low priority) - this can keep rendering
|
||||
# from starving on busy Windows boxes (Linux JVMs pretty much ignore thread priority), but may result
|
||||
# in more competition for CPU resources with other processes
|
||||
usenormalthreadpriority: true
|
||||
|
||||
# Save and restore pending tile renders - prevents their loss on server shutdown or /reload
|
||||
saverestorepending: true
|
||||
|
||||
# Save period for pending jobs (in seconds): periodic saving for crash recovery of jobs
|
||||
save-pending-period: 900
|
||||
|
||||
# Zoom-out tile update period - how often to scan for and process tile updates into zoom-out tiles (in seconds)
|
||||
zoomoutperiod: 30
|
||||
|
||||
# Control whether zoom out tiles are validated on startup (can be needed if zoomout processing is interrupted, but can be expensive on large maps)
|
||||
initial-zoomout-validate: true
|
||||
|
||||
# Default delay on processing of updated tiles, in seconds. This can reduce potentially expensive re-rendering
|
||||
# of frequently updated tiles (such as due to machines, pistons, quarries or other automation). Values can
|
||||
# also be set on individual worlds and individual maps.
|
||||
tileupdatedelay: 30
|
||||
|
||||
# Tile hashing is used to minimize tile file updates when no changes have occurred - set to false to disable
|
||||
enabletilehash: true
|
||||
|
||||
# Optional - hide ores: render as normal stone (so that they aren't revealed by maps)
|
||||
#hideores: true
|
||||
|
||||
# Optional - enabled BetterGrass style rendering of grass and snow block sides
|
||||
#better-grass: true
|
||||
|
||||
# Optional - enable smooth lighting by default on all maps supporting it (can be set per map as lighting option)
|
||||
smooth-lighting: true
|
||||
|
||||
# Optional - use world provider lighting table (good for custom worlds with custom lighting curves, like nether)
|
||||
# false=classic Dynmap lighting curve
|
||||
use-brightness-table: true
|
||||
|
||||
# Optional - render specific block names using the textures and models of another block name: can be used to hide/disguise specific
|
||||
# blocks (e.g. make ores look like stone, hide chests) or to provide simple support for rendering unsupported custom blocks
|
||||
block-alias:
|
||||
# "minecraft:quartz_ore": "stone"
|
||||
# "diamond_ore": "coal_ore"
|
||||
|
||||
# Default image format for HDMaps (png, jpg, jpg-q75, jpg-q80, jpg-q85, jpg-q90, jpg-q95, jpg-q100, webp, webp-q75, webp-q80, webp-q85, webp-q90, webp-q95, webp-q100, webp-l),
|
||||
# Note: any webp format requires the presence of the 'webp command line tools' (cwebp, dwebp) (https://developers.google.com/speed/webp/download)
|
||||
#
|
||||
# Has no effect on maps with explicit format settings
|
||||
image-format: jpg-q90
|
||||
|
||||
# If cwebp or dwebp are not on the PATH, use these settings to provide their full path. Do not use these settings if the tools are on the PATH
|
||||
# For Windows, include .exe
|
||||
#
|
||||
#cwebpPath: /usr/bin/cwebp
|
||||
#dwebpPath: /usr/bin/dwebp
|
||||
|
||||
# use-generated-textures: if true, use generated textures (same as client); false is static water/lava textures
|
||||
# correct-water-lighting: if true, use corrected water lighting (same as client); false is legacy water (darker)
|
||||
# transparent-leaves: if true, leaves are transparent (lighting-wise): false is needed for some Spout versions that break lighting on leaf blocks
|
||||
use-generated-textures: true
|
||||
correct-water-lighting: true
|
||||
transparent-leaves: true
|
||||
|
||||
# ctm-support: if true, Connected Texture Mod (CTM) in texture packs is enabled (default)
|
||||
ctm-support: true
|
||||
# custom-colors-support: if true, Custom Colors in texture packs is enabled (default)
|
||||
custom-colors-support: true
|
||||
|
||||
# Control loading of player faces (if set to false, skins are never fetched)
|
||||
#fetchskins: false
|
||||
|
||||
# Control updating of player faces, once loaded (if faces are being managed by other apps or manually)
|
||||
#refreshskins: false
|
||||
|
||||
# Customize URL used for fetching player skins (%player% is macro for name, %uuid% for UUID)
|
||||
skin-url: "http://skins.minecraft.net/MinecraftSkins/%player%.png"
|
||||
|
||||
# Control behavior for new (1.0+) compass orientation (sunrise moved 90 degrees: east is now what used to be south)
|
||||
# default is 'newrose' (preserve pre-1.0 maps, rotate rose)
|
||||
# 'newnorth' is used to rotate maps and rose (requires fullrender of any HDMap map - same as 'newrose' for FlatMap or KzedMap)
|
||||
compass-mode: newnorth
|
||||
|
||||
# Triggers for automatic updates : blockupdate-with-id is debug for breaking down updates by ID:meta
|
||||
# To disable, set just 'none' and comment/delete the rest
|
||||
render-triggers:
|
||||
- blockupdate
|
||||
#- blockupdate-with-id
|
||||
- chunkgenerate
|
||||
#- none
|
||||
|
||||
# Title for the web page - if not specified, defaults to the server's name (unless it is the default of 'Unknown Server')
|
||||
#webpage-title: "My Awesome Server Map"
|
||||
|
||||
# The path where the tile-files are placed.
|
||||
tilespath: web/tiles
|
||||
|
||||
# The path where the web-files are located.
|
||||
webpath: web
|
||||
|
||||
# If set to false, disable extraction of webpath content (good if using custom web UI or 3rd party web UI)
|
||||
# Note: web interface is unsupported in this configuration - you're on your own
|
||||
update-webpath-files: true
|
||||
|
||||
# The path were the /dynmapexp command exports OBJ ZIP files
|
||||
exportpath: export
|
||||
|
||||
# The path where files can be imported for /dmarker commands
|
||||
importpath: import
|
||||
|
||||
# The network-interface the webserver will bind to (0.0.0.0 for all interfaces, 127.0.0.1 for only local access).
|
||||
# If not set, uses same setting as server in server.properties (or 0.0.0.0 if not specified)
|
||||
#webserver-bindaddress: 0.0.0.0
|
||||
|
||||
# The TCP-port the webserver will listen on.
|
||||
webserver-port: 8123
|
||||
|
||||
# Maximum concurrent session on internal web server - limits resources used in Bukkit server
|
||||
max-sessions: 30
|
||||
|
||||
# Disables Webserver portion of Dynmap (Advanced users only)
|
||||
disable-webserver: false
|
||||
|
||||
# Enable/disable having the web server allow symbolic links (true=compatible with existing code, false=more secure (default))
|
||||
allow-symlinks: true
|
||||
|
||||
# Enable login support
|
||||
login-enabled: false
|
||||
# Require login to access website (requires login-enabled: true)
|
||||
login-required: false
|
||||
|
||||
# Period between tile renders for fullrender, in seconds (non-zero to pace fullrenders, lessen CPU load)
|
||||
timesliceinterval: 0.0
|
||||
|
||||
# Maximum chunk loads per server tick (1/20th of a second) - reducing this below 90 will impact render performance, but also will reduce server thread load
|
||||
maxchunkspertick: 200
|
||||
|
||||
# Progress report interval for fullrender/radiusrender, in tiles. Must be 100 or greater
|
||||
progressloginterval: 100
|
||||
|
||||
# Parallel fullrender: if defined, number of concurrent threads used for fullrender or radiusrender
|
||||
# Note: setting this will result in much more intensive CPU use, some additional memory use. Caution should be used when
|
||||
# setting this to equal or exceed the number of physical cores on the system.
|
||||
#parallelrendercnt: 4
|
||||
|
||||
# Interval the browser should poll for updates.
|
||||
updaterate: 2000
|
||||
|
||||
# If nonzero, server will pause fullrender/radiusrender processing when 'fullrenderplayerlimit' or more users are logged in
|
||||
fullrenderplayerlimit: 0
|
||||
# If nonzero, server will pause update render processing when 'updateplayerlimit' or more users are logged in
|
||||
updateplayerlimit: 0
|
||||
# Target limit on server thread use - msec per tick
|
||||
per-tick-time-limit: 50
|
||||
# If TPS of server is below this setting, update renders processing is paused
|
||||
update-min-tps: 18.0
|
||||
# If TPS of server is below this setting, full/radius renders processing is paused
|
||||
fullrender-min-tps: 18.0
|
||||
# If TPS of server is below this setting, zoom out processing is paused
|
||||
zoomout-min-tps: 18.0
|
||||
|
||||
showplayerfacesinmenu: true
|
||||
|
||||
# Control whether players that are hidden or not on current map are grayed out (true=yes)
|
||||
grayplayerswhenhidden: true
|
||||
|
||||
# Set sidebaropened: 'true' to pin menu sidebar opened permanently, 'pinned' to default the sidebar to pinned, but allow it to unpin
|
||||
#sidebaropened: true
|
||||
|
||||
# Customized HTTP response headers - add 'id: value' pairs to all HTTP response headers (internal web server only)
|
||||
#http-response-headers:
|
||||
# Access-Control-Allow-Origin: "my-domain.com"
|
||||
# X-Custom-Header-Of-Mine: "MyHeaderValue"
|
||||
|
||||
# Trusted proxies for web server - which proxy addresses are trusted to supply valid X-Forwarded-For fields
|
||||
# This now supports both IP address, and subnet ranges (e.g. 192.168.1.0/24 or 202.24.0.0/14 )
|
||||
trusted-proxies:
|
||||
- "127.0.0.1"
|
||||
- "0:0:0:0:0:0:0:1"
|
||||
|
||||
joinmessage: "%playername% joined"
|
||||
quitmessage: "%playername% quit"
|
||||
spammessage: "You may only chat once every %interval% seconds."
|
||||
# format for messages from web: %playername% substitutes sender ID (typically IP), %message% includes text
|
||||
webmsgformat: "&color;2[WEB] %playername%: &color;f%message%"
|
||||
|
||||
# Control whether layer control is presented on the UI (default is true)
|
||||
showlayercontrol: true
|
||||
|
||||
# Enable checking for banned IPs via banned-ips.txt (internal web server only)
|
||||
check-banned-ips: true
|
||||
|
||||
# Default selection when map page is loaded
|
||||
defaultzoom: 0
|
||||
defaultworld: world
|
||||
defaultmap: flat
|
||||
# (optional) Zoom level and map to switch to when following a player, if possible
|
||||
#followzoom: 3
|
||||
#followmap: surface
|
||||
|
||||
# If true, make persistent record of IP addresses used by player logins, to support web IP to player matching
|
||||
persist-ids-by-ip: true
|
||||
|
||||
# If true, map text to cyrillic
|
||||
cyrillic-support: false
|
||||
|
||||
# Messages to customize
|
||||
msg:
|
||||
maptypes: "Map Types"
|
||||
players: "Players"
|
||||
chatrequireslogin: "Chat Requires Login"
|
||||
chatnotallowed: "You are not permitted to send chat messages"
|
||||
hiddennamejoin: "Player joined"
|
||||
hiddennamequit: "Player quit"
|
||||
|
||||
# URL for client configuration (only need to be tailored for proxies or other non-standard configurations)
|
||||
url:
|
||||
# configuration URL
|
||||
#configuration: "up/configuration"
|
||||
# update URL
|
||||
#update: "up/world/{world}/{timestamp}"
|
||||
# sendmessage URL
|
||||
#sendmessage: "up/sendmessage"
|
||||
# login URL
|
||||
#login: "up/login"
|
||||
# register URL
|
||||
#register: "up/register"
|
||||
# tiles base URL
|
||||
#tiles: "tiles/"
|
||||
# markers base URL
|
||||
#markers: "tiles/"
|
||||
# Snapshot cache size, in chunks
|
||||
snapshotcachesize: 500
|
||||
# Snapshot cache uses soft references (true), else weak references (false)
|
||||
soft-ref-cache: true
|
||||
|
||||
# Player enter/exit title messages for map markers
|
||||
#
|
||||
# Processing period - how often to check player positions vs markers - default is 1000ms (1 second)
|
||||
#enterexitperiod: 1000
|
||||
# Title message fade in time, in ticks (0.05 second intervals) - default is 10 (1/2 second)
|
||||
#titleFadeIn: 10
|
||||
# Title message stay time, in ticks (0.05 second intervals) - default is 70 (3.5 seconds)
|
||||
#titleStay: 70
|
||||
# Title message fade out time, in ticks (0.05 seocnd intervals) - default is 20 (1 second)
|
||||
#titleFadeOut: 20
|
||||
# Enter/exit messages use on screen titles (true - default), if false chat messages are sent instead
|
||||
#enterexitUseTitle: true
|
||||
# Set true if new enter messages should supercede pending exit messages (vs being queued in order), default false
|
||||
#enterReplacesExits: true
|
||||
|
||||
# Published public URL for Dynmap server (allows users to use 'dynmap url' command to get public URL usable to access server
|
||||
# If not set, 'dynmap url' will not return anything. URL should be fully qualified (e.g. https://mc.westeroscraft.com/)
|
||||
#publicURL: http://my.greatserver.com/dynmap
|
||||
|
||||
# Set to true to enable verbose startup messages - can help with debugging map configuration problems
|
||||
# Set to false for a much quieter startup log
|
||||
verbose: false
|
||||
|
||||
# Enables debugging.
|
||||
#debuggers:
|
||||
# - class: org.dynmap.debug.LogDebugger
|
||||
# Debug: dump blocks missing render data
|
||||
dump-missing-blocks: false
|
||||
|
||||
# Log4J defense: string substituted for attempts to use macros in web chat
|
||||
hackAttemptBlurb: "(IaM5uchA1337Haxr-Ban Me!)"
|
||||
3
fabric-1.21.5/src/main/resources/dynmap.accesswidener
Normal file
3
fabric-1.21.5/src/main/resources/dynmap.accesswidener
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
accessWidener v1 named
|
||||
accessible class net/minecraft/world/biome/Biome$Weather
|
||||
accessible field net/minecraft/world/biome/Biome weather Lnet/minecraft/world/biome/Biome$Weather;
|
||||
19
fabric-1.21.5/src/main/resources/dynmap.mixins.json
Normal file
19
fabric-1.21.5/src/main/resources/dynmap.mixins.json
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "org.dynmap.fabric_1_21_5.mixin",
|
||||
"compatibilityLevel": "JAVA_17",
|
||||
"mixins": [
|
||||
"BiomeEffectsAccessor",
|
||||
"ChunkGeneratingMixin",
|
||||
"MinecraftServerMixin",
|
||||
"PlayerManagerMixin",
|
||||
"ProtoChunkMixin",
|
||||
"ServerPlayerEntityMixin",
|
||||
"ServerPlayNetworkHandlerMixin",
|
||||
"WorldChunkMixin"
|
||||
],
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
}
|
||||
}
|
||||
34
fabric-1.21.5/src/main/resources/fabric.mod.json
Normal file
34
fabric-1.21.5/src/main/resources/fabric.mod.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"schemaVersion": 1,
|
||||
"id": "dynmap",
|
||||
"version": "${version}",
|
||||
"name": "Dynmap",
|
||||
"description": "Dynamic, Google-maps style rendered maps for your Minecraft server",
|
||||
"authors": [
|
||||
"mikeprimm",
|
||||
"LolHens",
|
||||
"i509VCB"
|
||||
],
|
||||
"contact": {
|
||||
"homepage": "https://github.com/webbukkit/dynmap",
|
||||
"sources": "https://github.com/webbukkit/dynmap"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"icon": "assets/dynmap/icon.png",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
"org.dynmap.fabric_1_21_5.DynmapMod"
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"dynmap.mixins.json"
|
||||
],
|
||||
"accessWidener" : "dynmap.accesswidener",
|
||||
|
||||
"depends": {
|
||||
"fabricloader": ">=0.16.9",
|
||||
"fabric": ">=0.108.0",
|
||||
"minecraft": ["1.21.5"]
|
||||
}
|
||||
}
|
||||
27
fabric-1.21.5/src/main/resources/permissions.yml.example
Normal file
27
fabric-1.21.5/src/main/resources/permissions.yml.example
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
#
|
||||
# Sample permissions.yml for dynmap - trivial, flat-file based permissions for dynmap features
|
||||
# To use, copy this file to dynmap/permissions.yml, and edit appropriate. File is YAML format.
|
||||
#
|
||||
# All operators have full permissions to all functions.
|
||||
# All users receive the permissions under the 'defaultuser' section
|
||||
# Specific users can be given more permissions by defining a section with their name containing their permisssions
|
||||
# All permissions correspond to those documented here (https://github.com/webbukkit/dynmap/wiki/Permissions), but
|
||||
# do NOT have the 'dynmap.' prefix when used here (e.g. 'dynmap.fullrender' permission is just 'fullrender' here).
|
||||
#
|
||||
defaultuser:
|
||||
- render
|
||||
- show.self
|
||||
- hide.self
|
||||
- sendtoweb
|
||||
- stats
|
||||
- marker.list
|
||||
- marker.listsets
|
||||
- marker.icons
|
||||
- webregister
|
||||
- webchat
|
||||
#- marker.sign
|
||||
|
||||
#playername1:
|
||||
# - fullrender
|
||||
# - cancelrender
|
||||
# - radiusrender
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
|
@ -1,6 +1,6 @@
|
|||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ include ':DynmapCore'
|
|||
include ':DynmapCoreAPI'
|
||||
include ':fabric-1.21.1'
|
||||
include ':fabric-1.21.3'
|
||||
include ':fabric-1.21.5'
|
||||
include ':fabric-1.21'
|
||||
include ':fabric-1.20.6'
|
||||
include ':fabric-1.19.4'
|
||||
|
|
@ -80,6 +81,7 @@ project(':DynmapCore').projectDir = "$rootDir/DynmapCore" as File
|
|||
project(':DynmapCoreAPI').projectDir = "$rootDir/DynmapCoreAPI" as File
|
||||
project(':fabric-1.21.1').projectDir = "$rootDir/fabric-1.21.1" as File
|
||||
project(':fabric-1.21.3').projectDir = "$rootDir/fabric-1.21.3" as File
|
||||
project(':fabric-1.21.5').projectDir = "$rootDir/fabric-1.21.5" as File
|
||||
project(':fabric-1.21').projectDir = "$rootDir/fabric-1.21" as File
|
||||
project(':fabric-1.20.6').projectDir = "$rootDir/fabric-1.20.6" as File
|
||||
project(':fabric-1.19.4').projectDir = "$rootDir/fabric-1.19.4" as File
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue