From da0bde149f6caddeabaff779f480fdb46aa41c34 Mon Sep 17 00:00:00 2001 From: tenkuma Date: Sun, 31 May 2026 23:10:24 -0300 Subject: [PATCH 1/3] Add LICENSE --- LICENSE | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a572c04 --- /dev/null +++ b/LICENSE @@ -0,0 +1,38 @@ +GNU GENERAL PUBLIC LICENSE +Version 3, 29 June 2007 + +Copyright (C) 2007 Free Software Foundation, Inc. +Everyone is permitted to copy and distribute verbatim copies +of this license document, but changing it is not allowed. + +Preamble + +The GNU General Public License is a free, copyleft license for +software and other kinds of works. + +The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + +To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + +For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. \ No newline at end of file From 1216ea57f89fad8d089951b0044e094fbdd54e02 Mon Sep 17 00:00:00 2001 From: tenkuma Date: Sun, 31 May 2026 23:17:33 -0300 Subject: [PATCH 2/3] Switch to GPL-3 or later --- LICENSE | 46 ++++++++++------------------------------------ 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/LICENSE b/LICENSE index a572c04..9ae1405 100644 --- a/LICENSE +++ b/LICENSE @@ -1,38 +1,12 @@ -GNU GENERAL PUBLIC LICENSE -Version 3, 29 June 2007 +SweetDreams +Copyright (C) 2026 Adrian Victor -Copyright (C) 2007 Free Software Foundation, Inc. -Everyone is permitted to copy and distribute verbatim copies -of this license document, but changing it is not allowed. +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. -Preamble - -The GNU General Public License is a free, copyleft license for -software and other kinds of works. - -The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. - -When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - -To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. - -For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. \ No newline at end of file +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. From 7c4647dd534ed994a03f96004b1a9c4d323ebcc0 Mon Sep 17 00:00:00 2001 From: Adrian Victor Date: Tue, 2 Jun 2026 13:15:30 -0300 Subject: [PATCH 3/3] Add config option to enable debug messages and return to overworld when sleeping on dreamlands. Change PlayerStorage to save player current world to avoid desync and correct when player joins the world. This fixes the desync when joining the server after deleting the dreamlands world. --- .../sweetdreams/EventListener.java | 67 ++++++++++++++++--- .../sweetdreams/PlayerStorage.java | 55 ++++++++++++++- .../sweetdreams/PlayerTeleporter.java | 17 ++++- .../adrianvictor/sweetdreams/SweetDreams.java | 38 +++-------- src/main/resources/config.yml | 5 +- 5 files changed, 138 insertions(+), 44 deletions(-) diff --git a/src/main/java/org/adrianvictor/sweetdreams/EventListener.java b/src/main/java/org/adrianvictor/sweetdreams/EventListener.java index 9a593e4..4ec5e40 100644 --- a/src/main/java/org/adrianvictor/sweetdreams/EventListener.java +++ b/src/main/java/org/adrianvictor/sweetdreams/EventListener.java @@ -2,48 +2,72 @@ package org.adrianvictor.sweetdreams; import com.destroystokyo.paper.event.player.PlayerSetSpawnEvent; +import org.bukkit.Sound; +import org.bukkit.World; +import org.bukkit.block.Block; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityPortalEnterEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.player.PlayerBedEnterEvent; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.util.Vector; +import java.util.Objects; +import java.util.UUID; + public class EventListener implements Listener { private final PlayerStorage storage; private final PlayerTeleporter tp; private final YamlConfiguration configuration; + private final World dreamlands; + private final SweetDreams plugin; public EventListener() { this.storage = SweetDreams.getPlugin().getStorage(); this.tp = new PlayerTeleporter(); this.configuration = SweetDreams.getPlugin().getConfiguration().getPluginConfig(); + this.dreamlands = SweetDreams.getPlugin().getWorld(); + this.plugin = SweetDreams.getPlugin(); } @EventHandler(ignoreCancelled = true) public void onPlayerBedEnterEvent(PlayerBedEnterEvent event) { - if (event.getPlayer().getWorld() == SweetDreams.getPlugin().getWorld()) { - storage.savePlayerSpawnPoint(event.getPlayer()); - event.getPlayer().sendMessage("Respawn point set."); - event.setCancelled(true); - return; - } - - Long time = event.getPlayer().getWorld().getTime(); + long time = event.getPlayer().getWorld().getTime(); if (!(time >= 13000 && time <= 23000)) { event.setCancelled(true); return; } - tp.teleport(event.getPlayer(), false); - + tp.teleport(event.getPlayer(), false, "You feel free...", Sound.BLOCK_BEACON_ACTIVATE); event.setCancelled(true); } + @EventHandler + public void onInteract(PlayerInteractEvent event) { + Player player = event.getPlayer(); + Block type = event.getClickedBlock(); + + if (event.getAction() != Action.LEFT_CLICK_BLOCK) + return; + + if (player.getWorld() != dreamlands || type == null) + return; + + if (type.getType().name().endsWith("_BED")) { + storage.savePlayerSpawnPoint(event.getPlayer()); + event.getPlayer().sendMessage("Respawn point set."); + tp.teleport(event.getPlayer(), false, "Maybe you'll return to this dream....", Sound.BLOCK_BEACON_DEACTIVATE); + event.setCancelled(true); + } + } + @EventHandler public void onPlayerSetSpawn(PlayerSetSpawnEvent event) { if (event.getPlayer().getWorld() != SweetDreams.getPlugin().getWorld()) @@ -80,4 +104,27 @@ public class EventListener implements Listener { tp.teleport(player, true); event.setCancelled(true); } + + @EventHandler + public void onJoin(PlayerJoinEvent event) { + UUID uuid = event.getPlayer().getUniqueId(); + Player player = event.getPlayer(); + PlayerStorage.StorageWorldType actualWorld = storage.getActualWorld(player); + plugin.sendDebugMessage( + "Player %s logged in on %s with inventory from %s. (flag: %s, world: %s)" + .formatted(player.getName(), player.getWorld().getName(), actualWorld.getKey(), storage.getFlag(player).getKey(), actualWorld.getKey()) + ); + + if (storage.exists(uuid) && storage.getFlag(player) != actualWorld) { + plugin.getLogger().warning( + "Player %s was desynced! Changing inventory back to %s." + .formatted(player.getName(), actualWorld.getKey()) + ); + + storage.loadPlayer(player, actualWorld); + player.sendMessage("Your dream ended prematurely..."); + player.playSound(player.getLocation(), Sound.BLOCK_BEACON_DEACTIVATE, 1.0f, 1.0f); + player.teleport(Objects.requireNonNullElse(player.getRespawnLocation(), actualWorld.getWorld().getSpawnLocation())); + } + } } diff --git a/src/main/java/org/adrianvictor/sweetdreams/PlayerStorage.java b/src/main/java/org/adrianvictor/sweetdreams/PlayerStorage.java index fdcd206..94a1d86 100644 --- a/src/main/java/org/adrianvictor/sweetdreams/PlayerStorage.java +++ b/src/main/java/org/adrianvictor/sweetdreams/PlayerStorage.java @@ -1,15 +1,23 @@ package org.adrianvictor.sweetdreams; +import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.NamespacedKey; +import org.bukkit.World; import org.bukkit.configuration.file.YamlConfiguration; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.bukkit.persistence.PersistentDataType; import java.util.UUID; public class PlayerStorage { - private final Configuration config; + private final Configuration configuration; + private final YamlConfiguration config; private final YamlConfiguration storage; + private final NamespacedKey worldFlagKey = new NamespacedKey(SweetDreams.getPlugin(), "world_flag"); + private final World overworld; + private final World skylands; // Map inventories = new HashMap<>(); public enum StorageWorldType { @@ -25,11 +33,22 @@ public class PlayerStorage { public String getKey() { return key; } + + public World getWorld() { + if (this.getKey().equals("overworld")) { + return Bukkit.getWorld(SweetDreams.getPlugin().getConfiguration().getPluginConfig().getString("defaultWorld", "world")); + } + + return SweetDreams.getPlugin().getWorld(); + } } public PlayerStorage(Configuration config) { - this.config = config; + this.configuration = config; this.storage = config.getPlayerStorage(); + this.config = config.getPluginConfig(); + this.overworld = Bukkit.getWorld(this.config.getString("defaultWorld", "world")); + this.skylands = SweetDreams.getPlugin().getWorld(); } public void savePlayer(Player player, StorageWorldType type) { @@ -64,7 +83,7 @@ public class PlayerStorage { storage.set(base + ".stats.food", player.getFoodLevel()); storage.set(base + ".stats.saturation", player.getSaturation()); - config.savePlayerStorage(); + configuration.savePlayerStorage(); } public Location loadPlayer(Player player, StorageWorldType type) { @@ -124,6 +143,15 @@ public class PlayerStorage { player.setFoodLevel(storage.getInt(base + ".stats.food", 20)); player.setSaturation((float) storage.getDouble(base + ".stats.saturation", 5.0)); + storage.set("players." + uuid + ".worldFlag", type.name()); + player.getPersistentDataContainer().set( + worldFlagKey, + PersistentDataType.STRING, + type.name() + ); + + configuration.savePlayerStorage(); + Object rawSpawn = storage.get(base + ".spawn"); if (rawSpawn instanceof Location spawn) { return spawn; @@ -132,6 +160,27 @@ public class PlayerStorage { } } + public boolean exists(UUID uuid) { + String base = "players." + uuid; + return storage.contains(base); + } + + public StorageWorldType getFlag(Player player) { + UUID uuid = player.getUniqueId(); + String worldFlag = storage.getString("players." + uuid + ".worldFlag", null); + + if (worldFlag == null) { + worldFlag = player.getPersistentDataContainer().get(worldFlagKey, PersistentDataType.STRING); + } + + return StorageWorldType.valueOf(worldFlag); + } + + public StorageWorldType getActualWorld(Player player) { + return player.getWorld() == SweetDreams.getPlugin().getWorld() ? + StorageWorldType.SKYLANDS : StorageWorldType.OVERWORLD; + } + public void savePlayerSpawnPoint(Player player) { UUID uuid = player.getUniqueId(); String base = "players." + uuid + ".skylands"; diff --git a/src/main/java/org/adrianvictor/sweetdreams/PlayerTeleporter.java b/src/main/java/org/adrianvictor/sweetdreams/PlayerTeleporter.java index 00cff9a..66ced6c 100644 --- a/src/main/java/org/adrianvictor/sweetdreams/PlayerTeleporter.java +++ b/src/main/java/org/adrianvictor/sweetdreams/PlayerTeleporter.java @@ -18,11 +18,9 @@ public class PlayerTeleporter { private final Set teleporting = new HashSet<>(); private final PlayerStorage storage = SweetDreams.getPlugin().getStorage(); - public void teleport(Player player, boolean bad) { + public void teleport(Player player, boolean bad, String message, Sound sound) { UUID uuid = player.getUniqueId(); - Sound sound = bad ? Sound.ENTITY_ENDERMAN_SCREAM : Sound.ENTITY_PLAYER_LEVELUP; YamlConfiguration configuration = SweetDreams.getPlugin().getConfiguration().getPluginConfig(); - String message = bad ? "You woke up from a bad dream..." : "You woke up!"; boolean clearInventory = (bad && configuration.getBoolean("keepInventory", true)); if (!teleporting.add(uuid)) return; @@ -50,6 +48,13 @@ public class PlayerTeleporter { player.playSound(player.getLocation(), sound, 1.0f, 1.0f); } } else { + if (configuration.getBoolean("sendMessageOnWakeUp", true)) { + player.sendMessage(message); + } + + if (configuration.getBoolean("playSoundOnWakeUp", true)) { + player.playSound(player.getLocation(), sound, 1.0f, 1.0f); + } switchInventories(player, PlayerStorage.StorageWorldType.OVERWORLD, PlayerStorage.StorageWorldType.SKYLANDS); player.teleport(storage.loadPlayer(player, PlayerStorage.StorageWorldType.SKYLANDS)); } @@ -59,6 +64,12 @@ public class PlayerTeleporter { }); } + public void teleport(Player player, boolean bad) { + String message = bad ? "You woke up from a bad dream..." : "You woke up!"; + Sound sound = bad ? Sound.ENTITY_ENDERMAN_SCREAM : Sound.ENTITY_PLAYER_LEVELUP; + teleport(player, bad, message, sound); + } + private void switchInventories(Player player, PlayerStorage.StorageWorldType from, PlayerStorage.StorageWorldType to) { storage.savePlayer(player, from); player.getInventory().clear(); diff --git a/src/main/java/org/adrianvictor/sweetdreams/SweetDreams.java b/src/main/java/org/adrianvictor/sweetdreams/SweetDreams.java index 1863a07..b018604 100644 --- a/src/main/java/org/adrianvictor/sweetdreams/SweetDreams.java +++ b/src/main/java/org/adrianvictor/sweetdreams/SweetDreams.java @@ -1,6 +1,9 @@ package org.adrianvictor.sweetdreams; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextColor; import org.bukkit.Bukkit; +import org.bukkit.ChatColor; import org.bukkit.World; import org.bukkit.WorldCreator; import org.bukkit.plugin.java.JavaPlugin; @@ -17,40 +20,15 @@ public final class SweetDreams extends JavaPlugin { public void onEnable() { plugin = this; configuration = new Configuration(); + String worldName = configuration.getPluginConfig().getString("dreamlandsWorld", "world_sweetdreams"); storage = new PlayerStorage(configuration); - - boolean generated = false; - - for (World world: getServer().getWorlds()) { - if (world.getName().equals( - configuration.getPluginConfig().getString("dreamlandsWorld", "world_sweetdreams")) - ) { - this.world = world; - generated = true; - } - } - - if (!generated) { - world = createSkylandsWorld("world_sweetdreams"); - } + world = createSkylandsWorld(worldName); getServer().getPluginManager().registerEvents(new EventListener(), this); Bukkit.getScheduler().runTaskTimer(this, new KickerRunnable(), 20L, 20L); } - private void deleteFolder(File folder) { - if (folder.isDirectory()) { - File[] files = folder.listFiles(); - if (files != null) { - for (File file : files) { - deleteFolder(file); - } - } - } - folder.delete(); - } - @Override public void onDisable() { // Plugin shutdown logic @@ -82,4 +60,10 @@ public final class SweetDreams extends JavaPlugin { public PlayerStorage getStorage() { return storage; } + + public void sendDebugMessage(String message) { + if (configuration.getPluginConfig().getBoolean("debugMessages", false)) { + getLogger().info("[DEBUG] " + message); + } + } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 1c4166f..525f823 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -8,6 +8,7 @@ kickOnDayStart: true playSoundOnWakeUp: true sendMessageOnWakeUp: true keepInventory: true +sleepingOnDreamReturnsToOverworld: true # Won't affect already generated chunks # /!\ Don't change unless you know what you're doing @@ -25,4 +26,6 @@ worldGeneration: treeMaxHeight: 3 # Max random number that will be ADDED to the minimum size treeMinHeight: 4 oreMaxDepth: 20 - dirtLayers: 3 \ No newline at end of file + dirtLayers: 3 + +debugMessages: false \ No newline at end of file