BlueMap addon, shop points, map actions wired into Paper and Fabric; client opens shop windows

This commit is contained in:
Claude committed 2026-09-30 20:24:02 +00:00
1 parent ad7285efac
commit 2817ad2ac5
33 files changed
+754 -13

No files matched your search

+2
View File
@@ -16,6 +16,7 @@ dependencies {
implementation project(':common')
if (features) {
modImplementation 'net.fabricmc.fabric-api:fabric-api:0.92.2+1.20.1'
implementation project(':bluemap')
// Optional at runtime: present only when the LuckPerms mod is installed.
compileOnly 'net.luckperms:api:5.4'
}
@@ -31,5 +32,6 @@ processResources {
jar {
archiveBaseName = "scopenet-fabric-${mc}"
from project(':common').sourceSets.main.output
if (features) from project(':bluemap').sourceSets.main.output
}
base.archivesName = "scopenet-fabric-${mc}"
@@ -7,5 +7,5 @@
"entrypoints": { "server": ["net.scopenet.fabric.ScopenetFabric"] },
"mixins": ["scopenet.mixins.json"],
"depends": { "fabricloader": ">=0.16.10", "minecraft": "${mc}", "java": ">=${java}" },
"suggests": { "fabric-api": "*", "luckperms": "*", "placeholder-api": "*" }
"suggests": { "fabric-api": "*", "luckperms": "*", "placeholder-api": "*", "bluemap": "*" }
}
@@ -0,0 +1,21 @@
package net.scopenet.fabric.features;
import net.scopenet.bluemap.MapAddon;
import net.scopenet.core.map.MapData;
import net.scopenet.core.map.MapService;
import java.util.logging.Logger;
/** Kept apart so BlueMap's classes are only loaded when BlueMap is installed. */
final class BlueMapHook {
private BlueMapHook() {}
static Object start(MapService service, Logger log) {
return MapAddon.start(new MapAddon.Feed() {
@Override public MapData snapshot() { return service.snapshot(); }
@Override public com.google.gson.JsonObject playersJson() { return service.playersJson(); }
}, log);
}
static void stop(Object handle) { ((MapAddon) handle).stop(); }
}
@@ -15,12 +15,17 @@ import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.scopenet.core.*;
import net.scopenet.core.map.MapService;
import net.scopenet.fabric.api.ScopenetEconomy;
import net.scopenet.integration.Integration;
import net.scopenet.integration.Settings;
@@ -49,6 +54,9 @@ public final class FabricFeatures implements Runnable {
private ClientLink link;
private Perms perms;
private FabricConfig config;
private ActionPoller poller;
private MapService map;
private Object blueMap;
private int ticks;
private String placeholderStatus = "off";
@@ -64,6 +72,17 @@ public final class FabricFeatures implements Runnable {
if (entity instanceof ServerPlayer p && commands != null) commands.essentials.remember(p.getUUID(), platform.wrap(p).pos());
return true;
});
// Right-clicking a shop point opens the market or the shop.
UseBlockCallback.EVENT.register((player, level, hand, hit) -> {
if (level.isClientSide || hand != InteractionHand.MAIN_HAND || !(player instanceof ServerPlayer sp) || commands == null) return InteractionResult.PASS;
BlockPos at = hit.getBlockPos();
Optional<ShopPoints.Shop> shop = commands.shops.at(level.dimension().location().toString(), at.getX(), at.getY(), at.getZ());
if (shop.isEmpty()) return InteractionResult.PASS;
CorePlayer p = platform.wrap(sp);
String kind = shop.get().kind().equals("market") ? "market" : "shop";
if (!link.open(p, kind)) commands.run(kind, p);
return InteractionResult.SUCCESS;
});
ServerPlayNetworking.registerGlobalReceiver(FROM_CLIENT, (s, player, handler, buf, sender) -> {
byte[] data = new byte[buf.readableBytes()];
buf.readBytes(data);
@@ -93,6 +112,14 @@ public final class FabricFeatures implements Runnable {
integration.onNotifications(events -> link.onPanelEvents(events));
Bridge.claimBypass = p -> perms.has(p, "scopenet.claims.bypass");
// Actions from the map, and the BlueMap addon (claims, pins, player panel) when BlueMap is installed.
poller = new ActionPoller(env, (from, to) -> commands.essentials.tpa(from, new String[]{to.name()}));
map = new MapService(env, integration.client().claims(), commands.mapSource(), cache, integration.settings().panel().toString(),
config.bool("bluemap.show_homes", false), s.getMotd());
if (config.bool("bluemap.enabled", true) && net.fabricmc.loader.api.FabricLoader.getInstance().isModLoaded("bluemap")) {
try { blueMap = BlueMapHook.start(map, LOG); } catch (Throwable t) { LOG.warning("BlueMap addon could not start: " + t); }
}
registerCommands(s.getCommands().getDispatcher());
registerPlaceholders();
ScopenetEconomy.install(new ScopenetEconomy(env.panel, r -> platform.runAsync(r)));
@@ -103,6 +130,7 @@ public final class FabricFeatures implements Runnable {
private void stopping() {
ScopenetEconomy.uninstall();
Bridge.claimBypass = p -> false;
if (blueMap != null) { try { BlueMapHook.stop(blueMap); } catch (Throwable ignored) { } blueMap = null; }
if (platform != null) platform.shutdown();
commands = null;
}
@@ -125,6 +153,8 @@ public final class FabricFeatures implements Runnable {
if (ticks % 20 == 0) {
cache.tick(p -> link.pushState(p));
link.tick();
poller.tick();
map.tick();
}
if (ticks % 40 == 0) commands.tick();
}