154 lines
7.2 KiB
Java
154 lines
7.2 KiB
Java
package eu.mhsl.minecraft.endlessjumper;
|
||
|
||
import eu.mhsl.minecraft.endlessjumper.generator.JumpGenerator;
|
||
import net.minestom.server.Auth;
|
||
import net.minestom.server.MinecraftServer;
|
||
import net.minestom.server.command.builder.Command;
|
||
import net.minestom.server.command.builder.arguments.ArgumentType;
|
||
import net.minestom.server.command.builder.arguments.ArgumentWord;
|
||
import net.minestom.server.coordinate.Vec;
|
||
import net.minestom.server.coordinate.Pos;
|
||
import net.minestom.server.entity.Player;
|
||
import net.minestom.server.entity.PlayerSkin;
|
||
import net.minestom.server.event.GlobalEventHandler;
|
||
import net.minestom.server.event.item.ItemDropEvent;
|
||
import net.minestom.server.event.item.PickupItemEvent;
|
||
import net.minestom.server.event.player.AsyncPlayerConfigurationEvent;
|
||
import net.minestom.server.event.player.PlayerBlockBreakEvent;
|
||
import net.minestom.server.event.player.PlayerBlockPlaceEvent;
|
||
import net.minestom.server.event.player.PlayerChatEvent;
|
||
import net.minestom.server.event.player.PlayerDisconnectEvent;
|
||
import net.minestom.server.event.player.PlayerMoveEvent;
|
||
import net.minestom.server.event.player.PlayerSpawnEvent;
|
||
import net.minestom.server.event.player.PlayerSwapItemEvent;
|
||
import net.minestom.server.event.player.PlayerUseItemEvent;
|
||
import net.minestom.server.instance.Instance;
|
||
import net.minestom.server.instance.InstanceContainer;
|
||
import net.minestom.server.instance.block.Block;
|
||
|
||
import java.io.IOException;
|
||
import java.nio.file.Files;
|
||
import java.nio.file.Path;
|
||
import java.util.Map;
|
||
import java.util.concurrent.ConcurrentHashMap;
|
||
|
||
public class EndlessJumper {
|
||
private static final Pos START = new Pos(0, 100, 0);
|
||
private static final Pos SPAWN = START.add(0.5, 1, 0.5);
|
||
private static final Map<Player, JumpGenerator> games = new ConcurrentHashMap<>();
|
||
|
||
public static void main(String[] args) throws IOException {
|
||
MinecraftServer server = MinecraftServer.init(new Auth.Velocity(readForwardingSecret()));
|
||
Progress.load();
|
||
Admins.load();
|
||
GlobalEventHandler eventHandler = MinecraftServer.getGlobalEventHandler();
|
||
PlayerSkin skin = PlayerSkin.fromUsername("Notch");
|
||
|
||
eventHandler.addListener(AsyncPlayerConfigurationEvent.class, event -> {
|
||
final Player p = event.getPlayer();
|
||
p.setRespawnPoint(SPAWN);
|
||
p.setSkin(skin);
|
||
event.setSpawningInstance(createWorld());
|
||
});
|
||
|
||
eventHandler.addListener(PlayerSpawnEvent.class, event -> {
|
||
if(!event.isFirstSpawn()) return;
|
||
Player p = event.getPlayer();
|
||
p.setCanPickupItem(true);
|
||
ScoreTab.join(p);
|
||
Jokers.clear(p);
|
||
// sobald der Spieler in der Welt steht, geht es los
|
||
games.put(p, new JumpGenerator(START, Progress.checkpoint(p.getUuid()), p, p.getInstance()));
|
||
});
|
||
|
||
eventHandler.addListener(PlayerMoveEvent.class, event -> {
|
||
JumpGenerator generator = games.get(event.getPlayer());
|
||
if(generator == null) event.setCancelled(true);
|
||
else generator.onMove(event.getNewPosition());
|
||
});
|
||
|
||
eventHandler.addListener(PlayerBlockBreakEvent.class, event -> event.setCancelled(true));
|
||
eventHandler.addListener(PlayerBlockPlaceEvent.class, event -> event.setCancelled(true));
|
||
eventHandler.addListener(ItemDropEvent.class, event -> event.setCancelled(true));
|
||
eventHandler.addListener(PlayerSwapItemEvent.class, event -> event.setCancelled(true));
|
||
// den Chat verteilt Velocity (chat-bridge) an das ganze Netzwerk, hier würde er doppelt stehen
|
||
eventHandler.addListener(PlayerChatEvent.class, event -> event.setCancelled(true));
|
||
|
||
// der Client meldet Rechtsklicks auch auf Blöcke als Item-Nutzung, solange der Block sie nicht
|
||
// selbst verarbeitet (auf der Strecke nie) – ein Event genügt, Joker werden nicht doppelt verbraucht
|
||
eventHandler.addListener(PlayerUseItemEvent.class, event -> {
|
||
JumpGenerator generator = games.get(event.getPlayer());
|
||
Jokers joker = Jokers.of(event.getItemStack());
|
||
if(generator != null && joker != null && generator.useJoker(joker)) joker.take(event.getPlayer());
|
||
});
|
||
|
||
eventHandler.addListener(PickupItemEvent.class, event -> {
|
||
Jokers joker = Jokers.of(event.getItemStack());
|
||
if(event.getLivingEntity() instanceof Player player && joker != null) joker.add(player);
|
||
else event.setCancelled(true);
|
||
});
|
||
|
||
eventHandler.addListener(PlayerDisconnectEvent.class, event -> {
|
||
Player p = event.getPlayer();
|
||
games.remove(p);
|
||
ScoreTab.leave(p);
|
||
// jede Partie hat eine eigene Welt: nach dem Verlassen freigeben
|
||
releaseLater(p.getInstance());
|
||
});
|
||
|
||
Command reset = new Command("reset");
|
||
reset.setDefaultExecutor((sender, context) -> {
|
||
if(sender instanceof Player player) resetGame(player);
|
||
});
|
||
MinecraftServer.getCommandManager().register(reset);
|
||
|
||
// nur für Admins (admins.txt): Joker zum Testen direkt geben
|
||
Command joker = new Command("joker");
|
||
joker.setCondition((sender, commandString) -> sender instanceof Player player && Admins.is(player));
|
||
ArgumentWord type = ArgumentType.Word("typ").from(Jokers.keys());
|
||
joker.addSyntax((sender, context) -> Jokers.byKey(context.get(type)).add((Player) sender), type);
|
||
MinecraftServer.getCommandManager().register(joker);
|
||
|
||
server.start("0.0.0.0", 25501);
|
||
}
|
||
|
||
private static InstanceContainer createWorld() {
|
||
InstanceContainer world = MinecraftServer.getInstanceManager().createInstanceContainer();
|
||
world.setBlock(START, Block.DIAMOND_BLOCK);
|
||
// Tageszeit steuert der jeweilige Abschnitt
|
||
world.defaultClock().pause();
|
||
return world;
|
||
}
|
||
|
||
private static void releaseLater(Instance world) {
|
||
if(world != null) MinecraftServer.getSchedulerManager().scheduleNextTick(() -> MinecraftServer.getInstanceManager().unregisterInstance(world));
|
||
}
|
||
|
||
/** /reset: Fortschritt, Strecke, Score und Joker zurücksetzen und in einer frischen Welt bei 0 beginnen. */
|
||
private static void resetGame(Player player) {
|
||
JumpGenerator old = games.remove(player);
|
||
if(old != null) old.close();
|
||
Progress.reset(player.getUuid());
|
||
|
||
Instance oldWorld = player.getInstance();
|
||
InstanceContainer world = createWorld();
|
||
player.setVelocity(Vec.ZERO);
|
||
player.setInstance(world, SPAWN).thenRun(() -> {
|
||
Jokers.clear(player);
|
||
games.put(player, new JumpGenerator(START, 0, player, world));
|
||
releaseLater(oldWorld);
|
||
});
|
||
}
|
||
|
||
// Wie bei Velocity: Secret aus forwarding.secret im Arbeitsverzeichnis, alternativ per Umgebungsvariable
|
||
private static String readForwardingSecret() throws IOException {
|
||
String fromEnv = System.getenv("VELOCITY_FORWARDING_SECRET");
|
||
if(fromEnv != null && !fromEnv.isBlank()) return fromEnv.trim();
|
||
|
||
Path file = Path.of("forwarding.secret");
|
||
if(!Files.exists(file))
|
||
throw new IllegalStateException("forwarding.secret fehlt im Arbeitsverzeichnis (oder VELOCITY_FORWARDING_SECRET setzen)");
|
||
return Files.readString(file).trim();
|
||
}
|
||
}
|