├── src └── main │ ├── resources │ ├── lang │ │ └── lusiiplugin │ │ │ └── en_US.lang │ ├── icon.png │ ├── lusiiplugin.mixins.json │ └── fabric.mod.json │ └── java │ └── lusiiplugin │ ├── mixin │ ├── IEntityMixin.java │ ├── PlayerListMixin.java │ ├── EntityPlayerMixin.java │ ├── TreecapitatorMixin.java │ ├── NetLoginHandlerMixin.java │ ├── BlockFluidMixin.java │ ├── ChunkMixin.java │ ├── EntityTNTMixin.java │ ├── BlockBedMixin.java │ ├── BlockFarmlandMixin.java │ ├── NicknameCommandMixin.java │ ├── ContainerWorkbenchMixin.java │ ├── BlockTNTMixin.java │ ├── SpawnCommandMixin.java │ ├── BlockFireMixin.java │ ├── CommandsMixin.java │ ├── EntityPlayerMPMixin.java │ ├── HelpCommandMixin.java │ ├── EntityBobberMixin.java │ └── NetServerHandlerMixin.java │ ├── commands │ ├── PingCommand.java │ ├── TreecapToggleCommand.java │ ├── CraftingCommand.java │ ├── InfoCommand.java │ ├── OPChatCommand.java │ ├── InvseeCommand.java │ ├── RulesCommand.java │ ├── CrashCommand.java │ ├── TPRequestsCommand.java │ ├── HomesCommand.java │ ├── MOTDCommand.java │ ├── TransferHomesCommand.java │ ├── BackCommand.java │ ├── DelhomeCommand.java │ ├── TPAAllCommand.java │ ├── PayCommand.java │ ├── ColoursCommand.java │ ├── SethomeCommand.java │ ├── TPDenyCommand.java │ ├── RTPCommand.java │ ├── HomeCommand.java │ ├── SudoCommand.java │ ├── TPACommand.java │ ├── TPAHereCommand.java │ └── TPConfirmCommand.java │ ├── utils │ ├── Paginator.java │ ├── HomePosition.java │ ├── WorldPosition.java │ ├── PlayerHomesManager.java │ ├── PlayerHomes.java │ ├── ConfigBuilder.java │ └── PlayerData.java │ └── LusiiPlugin.java ├── .gitignore ├── README.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── settings.gradle ├── .editorconfig ├── .github └── workflows │ └── build.yml ├── gradlew.bat ├── gradlew └── LICENSE /src/main/resources/lang/lusiiplugin/en_US.lang: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle/ 2 | .idea/ 3 | build/ 4 | out/ 5 | run/ 6 | run-server/ 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Example Mod 2 | 3 | Lusii's plugin for her server, but also available for everyone! 4 | -------------------------------------------------------------------------------- /src/main/resources/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preprocessor/BetterthanVanilla/main/src/main/resources/icon.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/preprocessor/BetterthanVanilla/main/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx2G 2 | 3 | # BTA 4 | bta_version=7.1 5 | 6 | # Loader 7 | loader_version=0.15.6-babric.4-bta 8 | 9 | # HalpLibe 10 | halplibe_version=3.5.2 11 | 12 | 13 | # Mod 14 | mod_version=1.13 15 | mod_group=lusii 16 | mod_name=BetterThanVanilla 17 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/IEntityMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import net.minecraft.core.entity.Entity; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(Entity.class) 8 | public interface IEntityMixin { 9 | @Invoker("resetPos") 10 | void invokeResetPos(); 11 | } 12 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | maven { 9 | name = 'Jitpack' 10 | url = 'https://jitpack.io' 11 | } 12 | maven { 13 | name = 'Babric' 14 | url = 'https://maven.glass-launcher.net/babric' 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | tab_width = 4 8 | trim_trailing_whitespace = true 9 | 10 | [*.md] 11 | trim_trailing_whitespace = false 12 | 13 | [*.gradle] 14 | indent_style = tab 15 | 16 | [*.java] 17 | indent_style = tab 18 | 19 | [*.json] 20 | indent_style = space 21 | indent_size = 2 22 | 23 | [fabric.mod.json] 24 | indent_style = tab 25 | tab_width = 2 26 | 27 | [*.properties] 28 | indent_style = space 29 | indent_size = 2 30 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/PingCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import net.minecraft.core.net.command.Command; 4 | import net.minecraft.core.net.command.CommandHandler; 5 | import net.minecraft.core.net.command.CommandSender; 6 | 7 | public class PingCommand extends Command { 8 | public PingCommand() { 9 | super("ping"); 10 | } 11 | 12 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 13 | sender.sendMessage("Pong!"); 14 | return true; 15 | } 16 | 17 | 18 | public boolean opRequired(String[] args) { 19 | return false; 20 | } 21 | 22 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/utils/Paginator.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.utils; 2 | 3 | import java.util.List; 4 | import java.util.Collections; 5 | 6 | public class Paginator { 7 | public static List getPage(List list, int pageNumber, int itemsPerPage) { 8 | if (list == null || list.isEmpty() || itemsPerPage <= 0 || pageNumber < 1) { 9 | return Collections.emptyList(); // Return an empty list for invalid input 10 | } 11 | 12 | int totalItems = list.size(); 13 | int startIndex = (pageNumber - 1) * itemsPerPage; 14 | if (startIndex >= totalItems || startIndex < 0) { 15 | return Collections.emptyList(); // Page number out of range 16 | } 17 | 18 | int endIndex = startIndex + itemsPerPage; 19 | endIndex = Math.min(endIndex, totalItems); // Ensure the end index does not exceed the list size 20 | 21 | return list.subList(startIndex, endIndex); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/PlayerListMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import lusiiplugin.utils.PlayerData; 4 | import net.minecraft.server.entity.player.EntityPlayerMP; 5 | import net.minecraft.server.net.PlayerList; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(value = PlayerList.class, remap = false) 12 | public class PlayerListMixin { 13 | @Inject(at = @At("TAIL"), method = "playerLoggedIn") 14 | public void onLogin(EntityPlayerMP player, CallbackInfo ci) { 15 | PlayerData.update(player); 16 | } 17 | 18 | @Inject(at = @At("TAIL"), method = "playerLoggedOut") 19 | public void onLogout(EntityPlayerMP entityplayermp, CallbackInfo ci) { 20 | PlayerData.get(entityplayermp).save(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/EntityPlayerMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import net.minecraft.core.entity.EntityLiving; 4 | import net.minecraft.core.entity.player.EntityPlayer; 5 | import net.minecraft.core.world.World; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | 10 | @Mixin(value = EntityPlayer.class, remap = false) 11 | public class EntityPlayerMixin extends EntityLiving { 12 | @Shadow 13 | protected boolean isDwarf; 14 | 15 | public EntityPlayerMixin(World world) { 16 | super(world); 17 | } 18 | 19 | @Overwrite 20 | public double getRidingHeight() { 21 | double rideOffset = 0.5; 22 | 23 | if (this.vehicle instanceof EntityPlayer) { 24 | rideOffset = -0.12; 25 | } 26 | return this.isDwarf ? this.heightOffset + (rideOffset / 10.0) : (this.heightOffset - rideOffset); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/resources/lusiiplugin.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "lusiiplugin.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "mixins": [ 7 | "BlockBedMixin", 8 | "BlockFarmlandMixin", 9 | "BlockFireMixin", 10 | "BlockFluidMixin", 11 | "BlockTNTMixin", 12 | "ChunkMixin", 13 | "CommandsMixin", 14 | "ContainerWorkbenchMixin", 15 | "EntityBobberMixin", 16 | "EntityPlayerMixin", 17 | "EntityPlayerMPMixin$Data", 18 | "EntityPlayerMPMixin$Interface", 19 | "EntityPlayerMPMixin$Root", 20 | "EntityTNTMixin", 21 | "HelpCommandMixin", 22 | "IEntityMixin", 23 | "NetLoginHandlerMixin", 24 | "NetServerHandlerMixin", 25 | "NicknameCommandMixin", 26 | "PlayerListMixin", 27 | "SpawnCommandMixin", 28 | "TreecapitatorMixin" 29 | ], 30 | "client": [ 31 | ], 32 | "injectors": { 33 | "defaultRequire": 1 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "betterthanvanilla", 4 | "version": "${version}", 5 | 6 | "name": "Better than Vanilla", 7 | "description": "The plugin that makes your server complete.", 8 | "authors": [ 9 | "Lusii", 10 | "Wyspr" 11 | ], 12 | "contact": { 13 | "homepage": "", 14 | "sources": "" 15 | }, 16 | 17 | "icon": "icon.png", 18 | "license": "CC0-1.0", 19 | 20 | "environment": "*", 21 | "entrypoints": { 22 | "main": [ 23 | "lusiiplugin.LusiiPlugin" 24 | ], 25 | "beforeGameStart": [ 26 | "lusiiplugin.LusiiPlugin" 27 | ], 28 | "afterGameStart": [ 29 | "lusiiplugin.LusiiPlugin" 30 | ], 31 | "recipesReady": [ 32 | "lusiiplugin.LusiiPlugin" 33 | ] 34 | }, 35 | "mixins": [ 36 | "lusiiplugin.mixins.json" 37 | ], 38 | 39 | "depends": { 40 | "minecraft": "~7.1-beta.2", 41 | "fabricloader": ">=0.13.3" 42 | }, 43 | "suggests": { 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/utils/HomePosition.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.utils; 2 | 3 | import java.io.Serializable; 4 | import java.util.Objects; 5 | 6 | public class HomePosition implements Serializable { 7 | private static final long serialVersionUID = 1L; // Ensures version compatibility during deserialization 8 | 9 | public double x, y, z; 10 | public int dim; 11 | 12 | public HomePosition(double x, double y, double z, int dim) { 13 | this.x = x; 14 | this.y = y; 15 | this.z = z; 16 | this.dim = dim; 17 | } 18 | 19 | @Override 20 | public int hashCode() { 21 | return Objects.hash(x, y, z, dim); 22 | } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if (this == o) return true; 27 | if (o == null || getClass() != o.getClass()) return false; 28 | HomePosition that = (HomePosition) o; 29 | return Double.compare(that.x, x) == 0 && 30 | Double.compare(that.y, y) == 0 && 31 | Double.compare(that.z, z) == 0 && 32 | dim == that.dim; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/utils/WorldPosition.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.utils; 2 | 3 | import java.io.Serializable; 4 | import java.util.Objects; 5 | 6 | public class WorldPosition implements Serializable { 7 | private static final long serialVersionUID = 1L; // Ensures version compatibility during deserialization 8 | 9 | public double x, y, z; 10 | public int dim; 11 | 12 | public WorldPosition(double x, double y, double z, int dim) { 13 | this.x = x; 14 | this.y = y; 15 | this.z = z; 16 | this.dim = dim; 17 | } 18 | 19 | @Override 20 | public int hashCode() { 21 | return Objects.hash(x, y, z, dim); 22 | } 23 | 24 | @Override 25 | public boolean equals(Object o) { 26 | if (this == o) return true; 27 | if (o == null || getClass() != o.getClass()) return false; 28 | WorldPosition that = (WorldPosition) o; 29 | return Double.compare(that.x, x) == 0 && 30 | Double.compare(that.y, y) == 0 && 31 | Double.compare(that.z, z) == 0 && 32 | dim == that.dim; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/TreecapToggleCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.utils.PlayerData; 4 | import net.minecraft.core.net.command.*; 5 | 6 | public class TreecapToggleCommand extends Command { 7 | public TreecapToggleCommand() { 8 | super("tt", "ttreecap", "toggletreecap"); 9 | } 10 | 11 | @Override 12 | public boolean execute(CommandHandler handler, CommandSender sender, String[] strings) { 13 | boolean treecapEnabled = PlayerData.get(sender.getPlayer()).treecap().toggle(); 14 | 15 | if (treecapEnabled) { 16 | sender.sendMessage("§1Treecapitator enabled."); 17 | } else { 18 | sender.sendMessage("§1Treecapitator disabled."); 19 | } 20 | 21 | return true; 22 | } 23 | 24 | public boolean opRequired(String[] args) { 25 | return false; 26 | } 27 | 28 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 29 | sender.sendMessage("§3/tt"); 30 | sender.sendMessage("§5Toggles treecapitator."); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/TreecapitatorMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import lusiiplugin.utils.PlayerData; 4 | import net.minecraft.core.data.gamerule.TreecapitatorHelper; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 12 | 13 | @Mixin(value = TreecapitatorHelper.class, remap = false) 14 | public class TreecapitatorMixin { 15 | @Shadow 16 | @Final 17 | public EntityPlayer player; 18 | 19 | @Inject( 20 | method = "chopTree", 21 | at = @At("HEAD"), 22 | cancellable = true 23 | ) 24 | private void disableTreecapPerList(CallbackInfoReturnable cir) { 25 | if (PlayerData.get(player).treecap().isActive()) { 26 | cir.setReturnValue(false); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/CraftingCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import net.minecraft.core.entity.player.EntityPlayer; 5 | import net.minecraft.core.net.command.Command; 6 | import net.minecraft.core.net.command.CommandHandler; 7 | import net.minecraft.core.net.command.CommandSender; 8 | 9 | public class CraftingCommand extends Command { 10 | public CraftingCommand() { 11 | super("craftingtable", "craft", "crafting", "cb"); 12 | } 13 | // 14 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 15 | EntityPlayer p = sender.getPlayer(); 16 | p.displayGUIWorkbench(p.serverPosX, p.serverPosY, p.serverPosZ); 17 | 18 | return true; 19 | } 20 | // 21 | public boolean opRequired(String[] args) { 22 | return !LusiiPlugin.craftCommand; 23 | } 24 | // 25 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 26 | sender.sendMessage("§3/cb"); 27 | sender.sendMessage("§5Opens a crafting table."); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/InfoCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import net.minecraft.core.net.command.Command; 5 | import net.minecraft.core.net.command.CommandHandler; 6 | import net.minecraft.core.net.command.CommandSender; 7 | 8 | public class InfoCommand extends Command { 9 | public InfoCommand() { 10 | super("info"); 11 | } 12 | 13 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 14 | int page = 0; 15 | if (args.length > 0) { 16 | try { 17 | page = Integer.parseInt(args[0]); 18 | } catch (NumberFormatException ignored) { 19 | // We don't need to do anything here. page = 0 20 | } 21 | } 22 | for (String line : LusiiPlugin.info.get(page)) sender.sendMessage(line); 23 | return true; 24 | } 25 | 26 | public boolean opRequired(String[] args) { 27 | return false; 28 | } 29 | 30 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 31 | sender.sendMessage("§3/info"); 32 | sender.sendMessage("§5Display useful information"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/NetLoginHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import net.minecraft.core.net.packet.Packet1Login; 4 | import net.minecraft.server.MinecraftServer; 5 | import net.minecraft.server.net.handler.NetLoginHandler; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | import java.io.File; 13 | 14 | @Mixin(value = NetLoginHandler.class, remap = false) 15 | public abstract class NetLoginHandlerMixin { 16 | @Shadow 17 | private MinecraftServer mcServer; 18 | 19 | @Inject(method = "doLogin", at = @At("TAIL")) 20 | private void welcomeMessage(Packet1Login packet1login, CallbackInfo ci) { 21 | String username = packet1login.username; 22 | File playerData = new File("world/players/" + username + ".dat"); 23 | 24 | if (!playerData.exists()) { 25 | this.mcServer.serverCommandHandler.sendMessageToAllPlayers("§4Welcome §3" + username + "§4 to the server!" ); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/OPChatCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import net.minecraft.core.net.command.Command; 4 | import net.minecraft.core.net.command.CommandHandler; 5 | import net.minecraft.core.net.command.CommandSender; 6 | 7 | public class OPChatCommand extends Command { 8 | public OPChatCommand() { 9 | super("opchat", "opc","chatop"); 10 | } 11 | 12 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 13 | StringBuilder builder = new StringBuilder(); 14 | for (String arg : args) { 15 | builder.append(arg).append(" "); 16 | } 17 | handler.asServer().sendMessageToAdmins("[§e§lOP CHAT§r] <" + sender.getPlayer().username + "§r> " +builder.toString().replace("$$","§")); 18 | return true; 19 | } 20 | 21 | public boolean opRequired(String[] args) { 22 | return true; 23 | } 24 | 25 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 26 | if (sender.isAdmin() || sender.isConsole()) { 27 | sender.sendMessage("§3/opchat §4"); 28 | sender.sendMessage("§5Broadcast a message to all players"); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/BlockFluidMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import net.minecraft.core.block.Block; 4 | import net.minecraft.core.block.BlockFluid; 5 | import net.minecraft.core.block.BlockPortal; 6 | import net.minecraft.core.block.material.Material; 7 | import net.minecraft.core.world.World; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | @Mixin(value = BlockFluid.class, remap = false) 14 | public class BlockFluidMixin extends Block { 15 | 16 | public BlockFluidMixin(String key, int id, Material material) { 17 | super(key, id, material); 18 | } 19 | 20 | @Inject( 21 | method = "onBlockAdded(Lnet/minecraft/core/world/World; I I I) V", 22 | at = @At("TAIL") 23 | ) 24 | public void addParadise(World world, int x, int y, int z, CallbackInfo ci) { 25 | if (world.getBlockId(x, y-1, z) == ((BlockPortal)Block.portalParadise).portalFrameId && !world.isClientSide){ 26 | ((BlockPortal)Block.portalParadise).tryToCreatePortal(world, x, y, z); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/ChunkMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import net.minecraft.core.entity.Entity; 4 | import net.minecraft.core.world.chunk.Chunk; 5 | import org.spongepowered.asm.mixin.Final; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(value = Chunk.class, remap = false) 13 | public class ChunkMixin { 14 | @Shadow 15 | @Final 16 | public int xPosition; 17 | 18 | @Shadow 19 | @Final 20 | public int zPosition; 21 | 22 | @Inject( 23 | method = "addEntity", 24 | at = @At( 25 | value = "INVOKE", 26 | target = "Ljava/lang/Thread;dumpStack()V" 27 | ) 28 | ) 29 | public void debugAddEntity(Entity entity, CallbackInfo ci) { 30 | System.out.println("Chunk pos: " + this.xPosition + " " + this.zPosition); 31 | System.out.println("Problem entity: " + entity.getClass().getName()); 32 | System.out.println("Entity XYZ | entity's chunk XZ: " + entity.x + " " + entity.y + " " + entity.z + " | " + entity.chunkCoordX + " " + entity.chunkCoordZ); 33 | 34 | ((IEntityMixin) entity).invokeResetPos(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/InvseeCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import net.minecraft.core.entity.player.EntityPlayer; 4 | import net.minecraft.core.net.command.Command; 5 | import net.minecraft.core.net.command.CommandHandler; 6 | import net.minecraft.core.net.command.CommandSender; 7 | import net.minecraft.core.player.inventory.IInventory; 8 | import net.minecraft.core.player.inventory.InventoryPlayer; 9 | 10 | public class InvseeCommand extends Command { 11 | public InvseeCommand() { 12 | super("invsee", "openinv"); 13 | } 14 | 15 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 16 | EntityPlayer player = handler.getPlayer(args[0]); 17 | if (player == null) { 18 | return false; 19 | } 20 | 21 | IInventory inventory = new InventoryPlayer(player).player.inventory; 22 | sender.getPlayer().displayGUIChest(inventory); 23 | return true; 24 | } 25 | 26 | 27 | public boolean opRequired(String[] args) { 28 | return true; 29 | } 30 | 31 | 32 | 33 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 34 | if (sender.isConsole() || sender.isAdmin()) { 35 | sender.sendMessage("§3/invsee §4"); 36 | sender.sendMessage("§5View the inventory of another player"); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/RulesCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import net.minecraft.core.net.command.Command; 5 | import net.minecraft.core.net.command.CommandError; 6 | import net.minecraft.core.net.command.CommandHandler; 7 | import net.minecraft.core.net.command.CommandSender; 8 | 9 | import java.io.File; 10 | import java.io.FileWriter; 11 | import java.io.IOException; 12 | import java.nio.file.Files; 13 | import java.util.List; 14 | 15 | public class RulesCommand extends Command { 16 | public RulesCommand() { 17 | super("rules"); 18 | } 19 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 20 | int page = 0; 21 | if (args.length > 0) { 22 | try { 23 | page = Integer.parseInt(args[0]); 24 | } catch (NumberFormatException ignored) { 25 | // We don't need to do anything here. page = 0 26 | } 27 | } 28 | for (String line : LusiiPlugin.rules.get(page)) sender.sendMessage(line); 29 | return true; 30 | } 31 | 32 | public boolean opRequired(String[] args) { 33 | return false; 34 | } 35 | 36 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 37 | sender.sendMessage("§3/rules §4[page]"); 38 | sender.sendMessage("§5Display the server rules"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/CrashCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import net.minecraft.core.entity.player.EntityPlayer; 4 | import net.minecraft.core.net.command.Command; 5 | import net.minecraft.core.net.command.CommandHandler; 6 | import net.minecraft.core.net.command.CommandSender; 7 | import net.minecraft.core.player.inventory.IInventory; 8 | import net.minecraft.core.player.inventory.InventoryBasic; 9 | import net.minecraft.core.player.inventory.InventoryLargeChest; 10 | import net.minecraft.core.player.inventory.InventoryPlayer; 11 | 12 | public class CrashCommand extends Command { 13 | public CrashCommand() { 14 | super("crash"); 15 | } 16 | // 17 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 18 | int slotValue = 2147483647; 19 | EntityPlayer player; 20 | player = handler.getPlayer(args[0]); 21 | if (player == null) { 22 | return false; 23 | } 24 | if (args[1] != null) { 25 | slotValue = Integer.parseInt(args[1]); 26 | } 27 | IInventory inventory = new InventoryBasic("Googative",slotValue); 28 | player.displayGUIChest(inventory); 29 | return true; 30 | } 31 | // 32 | public boolean opRequired(String[] args) { 33 | return true; 34 | } 35 | // 36 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/TPRequestsCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.Command; 7 | import net.minecraft.core.net.command.CommandHandler; 8 | import net.minecraft.core.net.command.CommandSender; 9 | 10 | public class TPRequestsCommand extends Command { 11 | public TPRequestsCommand() { 12 | super("tprequests", "tpreq", "tpr"); 13 | } 14 | 15 | public boolean opRequired(String[] args) { 16 | return !LusiiPlugin.TPACommand; 17 | } 18 | 19 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 20 | sender.sendMessage("§3/tpreq"); 21 | sender.sendMessage("§5View your teleport requests"); 22 | } 23 | 24 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 25 | if (sender.isConsole()) return true; 26 | 27 | EntityPlayer p = sender.getPlayer(); 28 | PlayerData.TPInfo tpInfo = PlayerData.get(p).tpInfo(); 29 | if (tpInfo.hasNoRequests()) { 30 | sender.sendMessage("§4You don't have any requests."); 31 | return true; 32 | } 33 | String requests = String.join(", ", tpInfo.getAllRequests()); 34 | 35 | sender.sendMessage("§1Current TP requests: " + requests); 36 | 37 | return true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/EntityTNTMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import com.mojang.nbt.CompoundTag; 4 | import lusiiplugin.LusiiPlugin; 5 | import net.minecraft.core.entity.Entity; 6 | import net.minecraft.core.entity.EntityTNT; 7 | import net.minecraft.core.world.World; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | @Mixin(value = EntityTNT.class, remap = false) 15 | public class EntityTNTMixin extends Entity { 16 | 17 | public EntityTNTMixin(World world) { 18 | super(world); 19 | } 20 | @Inject(method = "tick", at = @At("HEAD")) 21 | public void tick(CallbackInfo ci) { 22 | if (this.world.dimension.id == 0 && LusiiPlugin.DisableTNTOverworld <= this.y) {this.remove();} 23 | if (this.world.dimension.id == 1 && LusiiPlugin.DisableTNTNether <= this.y) {this.remove();} 24 | if (this.world.dimension.id == 2 && LusiiPlugin.DisableTNTSky <= this.y) {this.remove();} 25 | } 26 | 27 | @Shadow 28 | protected void init() { 29 | 30 | } 31 | @Shadow 32 | public void readAdditionalSaveData(CompoundTag compoundTag) { 33 | 34 | } 35 | @Shadow 36 | public void addAdditionalSaveData(CompoundTag compoundTag) { 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/HomesCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.Command; 7 | import net.minecraft.core.net.command.CommandHandler; 8 | import net.minecraft.core.net.command.CommandSender; 9 | 10 | import java.util.List; 11 | 12 | public class HomesCommand extends Command { 13 | public HomesCommand() { 14 | super("homes"); 15 | } 16 | 17 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 18 | EntityPlayer p = sender.getPlayer(); 19 | PlayerData.Homes homes = PlayerData.get(p).homes(); 20 | List homesList = homes.getHomesList(); 21 | 22 | if (homesList.isEmpty()) { 23 | sender.sendMessage("§1You do not have any homes!"); 24 | sender.sendMessage("§1Set a home with: §3/sethome [name]"); 25 | return true; 26 | } 27 | 28 | String homesString = String.join(", ", homesList); 29 | sender.sendMessage("§1Homes: §4" + homesString); 30 | return true; 31 | } 32 | 33 | public boolean opRequired(String[] args) { 34 | return !LusiiPlugin.homeCommand; 35 | } 36 | 37 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 38 | sender.sendMessage("§3/homes"); 39 | sender.sendMessage("§5List your homes"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/BlockBedMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import net.minecraft.core.block.Block; 5 | import net.minecraft.core.block.BlockBed; 6 | import net.minecraft.core.block.material.Material; 7 | import net.minecraft.core.entity.player.EntityPlayer; 8 | import net.minecraft.core.world.World; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | @Mixin(value = BlockBed.class, remap = false) 15 | public class BlockBedMixin extends Block { 16 | 17 | public BlockBedMixin(String key, int id, Material material) { 18 | super(key, id, material); 19 | } 20 | 21 | @Inject( 22 | method = "blockActivated(Lnet/minecraft/core/world/World; I I I Lnet/minecraft/core/entity/player/EntityPlayer;) Z", 23 | at = @At(value = "INVOKE", 24 | target = "Lnet/minecraft/core/world/World;setBlockWithNotify(IIII)Z", 25 | shift = At.Shift.BEFORE 26 | ), 27 | cancellable = true 28 | ) 29 | public void bedBoomStop(World world, int x, int y, int z, EntityPlayer player, CallbackInfoReturnable cir) { 30 | if (LusiiPlugin.disableBedExplosion) { 31 | player.addChatMessage("§1You may not sleep here."); 32 | cir.setReturnValue(true); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/MOTDCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import net.minecraft.core.net.command.Command; 4 | import net.minecraft.core.net.command.CommandHandler; 5 | import net.minecraft.core.net.command.CommandSender; 6 | 7 | public class MOTDCommand extends Command { 8 | public MOTDCommand() { 9 | super("motd"); 10 | } 11 | 12 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 13 | if (args.length == 0) { 14 | sender.sendMessage(handler.asServer().minecraftServer.motd); 15 | return true; 16 | } else { 17 | if (!sender.isAdmin()) { 18 | sender.sendMessage(handler.asServer().minecraftServer.motd); 19 | return false; 20 | } 21 | StringBuilder builder = new StringBuilder(); 22 | for (String arg : args) { 23 | builder.append(arg).append(" "); 24 | } 25 | handler.asServer().minecraftServer.motd = builder.toString().replace("$$","§"); 26 | return true; 27 | } 28 | } 29 | 30 | public boolean opRequired(String[] args) { 31 | return false; 32 | } 33 | 34 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 35 | sender.sendMessage("§3/motd"); 36 | sender.sendMessage("§5Show the server MOTD"); 37 | if (sender.isAdmin() || sender.isConsole()) { 38 | sender.sendMessage("§3/motd §4"); 39 | sender.sendMessage("§5Set the server MOTD"); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/TransferHomesCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import net.minecraft.core.net.command.Command; 5 | import net.minecraft.core.net.command.CommandHandler; 6 | import net.minecraft.core.net.command.CommandSender; 7 | 8 | import java.io.IOException; 9 | import java.nio.file.*; 10 | 11 | public class TransferHomesCommand extends Command { 12 | public TransferHomesCommand() { 13 | super("transferhomes", "th"); 14 | } 15 | 16 | @Override 17 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 18 | if (args.length < 2) { 19 | return false; 20 | } 21 | String oldPlayer = args[0]; 22 | String newPlayer = args[1]; 23 | Path dataDir = Paths.get(LusiiPlugin.SAVE_DIR, oldPlayer); 24 | 25 | if (Files.notExists(dataDir)) { 26 | sender.sendMessage("§4Player data not found for :§3" + oldPlayer); 27 | return false; 28 | } 29 | 30 | try { 31 | Files.move(dataDir, dataDir.resolveSibling(newPlayer), StandardCopyOption.REPLACE_EXISTING); 32 | } catch (IOException e) { 33 | System.err.println("Could not move directory: " + dataDir); 34 | } 35 | return true; 36 | } 37 | 38 | @Override 39 | public boolean opRequired(String[] strings) { 40 | return true; 41 | } 42 | 43 | @Override 44 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 45 | sender.sendMessage("/th OldPlayer NewPlayer"); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/BlockFarmlandMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import net.minecraft.core.block.Block; 4 | import net.minecraft.core.block.BlockFarmland; 5 | import net.minecraft.core.block.material.Material; 6 | import net.minecraft.core.entity.Entity; 7 | import net.minecraft.core.world.World; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | import static lusiiplugin.LusiiPlugin.disableTrample; 14 | import static lusiiplugin.LusiiPlugin.enableAntiTrampleFence; 15 | 16 | @Mixin(value = BlockFarmland.class,remap = false) 17 | public class BlockFarmlandMixin extends Block { 18 | public BlockFarmlandMixin(String key, int id, Material material) { 19 | super(key, id, material); 20 | } 21 | 22 | @Inject( 23 | method = "onEntityWalking(Lnet/minecraft/core/world/World; I I I Lnet/minecraft/core/entity/Entity;) V", 24 | at = @At("HEAD"), 25 | cancellable = true 26 | ) 27 | public void trampleControl(World world, int x, int y, int z, Entity entity, CallbackInfo ci) { 28 | Block blockBelow = world.getBlock(x,y-1,z); 29 | if (((blockBelow == Block.fencePlanksOak || blockBelow == Block.fencePlanksOakPainted) && enableAntiTrampleFence) || disableTrample) { 30 | world.notifyBlockChange(x, y, z, Block.farmlandDirt.id); 31 | ci.cancel(); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | # Automatically build the project and run any configured tests for every push 2 | # and submitted pull request. This can help catch issues that only occur on 3 | # certain platforms or Java versions, and provides a first line of defence 4 | # against bad commits. 5 | 6 | name: build 7 | on: [pull_request, push] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | # Use these Java versions 14 | java: [ 15 | 17, # Current Java LTS & minimum supported by Minecraft 16 | ] 17 | # and run on both Linux and Windows 18 | os: [ubuntu-20.04, windows-2022] 19 | runs-on: ${{ matrix.os }} 20 | steps: 21 | - name: checkout repository 22 | uses: actions/checkout@v2 23 | - name: validate gradle wrapper 24 | uses: gradle/wrapper-validation-action@v1 25 | - name: setup jdk ${{ matrix.java }} 26 | uses: actions/setup-java@v1 27 | with: 28 | java-version: ${{ matrix.java }} 29 | - name: make gradle wrapper executable 30 | if: ${{ runner.os != 'Windows' }} 31 | run: chmod +x ./gradlew 32 | - name: build 33 | run: ./gradlew build 34 | - name: capture build artifacts 35 | if: ${{ runner.os == 'Linux' && matrix.java == '17' }} # Only upload artifacts built from latest java on one OS 36 | uses: actions/upload-artifact@v2 37 | with: 38 | name: Artifacts 39 | path: build/libs/ 40 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/BackCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import lusiiplugin.utils.WorldPosition; 6 | import net.minecraft.core.entity.player.EntityPlayer; 7 | import net.minecraft.core.net.command.*; 8 | 9 | public class BackCommand extends Command { 10 | public BackCommand() { 11 | super("back"); 12 | } 13 | 14 | public boolean opRequired(String[] args) { 15 | return !LusiiPlugin.BackCommand; 16 | } 17 | 18 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 19 | sender.sendMessage("§3/back"); 20 | sender.sendMessage("§5Teleport to where you last teleported from."); 21 | } 22 | 23 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 24 | if (sender.isConsole()) return true; 25 | 26 | EntityPlayer p = sender.getPlayer(); 27 | PlayerData.TPInfo tpInfo = PlayerData.get(p).tpInfo(); 28 | 29 | if (tpInfo.canTP() || sender.isAdmin()) { 30 | if (tpInfo.atNewPos()) { 31 | WorldPosition lastPos = tpInfo.getLastPos(); 32 | 33 | tpInfo.update(); 34 | 35 | if (LusiiPlugin.teleport(p, lastPos)) { 36 | sender.sendMessage("§4Went back."); 37 | } 38 | } else { 39 | sender.sendMessage("§4You have not moved!"); 40 | } 41 | 42 | } else if (!sender.isAdmin()) { 43 | int waitTime = tpInfo.cooldown(); 44 | sender.sendMessage("§4Teleport available in §1" + waitTime + "§4 seconds."); 45 | } 46 | 47 | return true; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/NicknameCommandMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import net.minecraft.core.net.command.*; 4 | import net.minecraft.core.net.command.commands.NicknameCommand; 5 | import net.minecraft.server.MinecraftServer; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Shadow; 8 | import org.spongepowered.asm.mixin.injection.Constant; 9 | import org.spongepowered.asm.mixin.injection.ModifyConstant; 10 | 11 | import static lusiiplugin.LusiiPlugin.NickLength; 12 | 13 | @Mixin(value = NicknameCommand.class, remap = false) 14 | public class NicknameCommandMixin extends ServerCommand { 15 | public NicknameCommandMixin(MinecraftServer server) { 16 | super(server, "nickname", "nick"); 17 | } 18 | 19 | @Shadow 20 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 21 | return true; 22 | } 23 | 24 | @ModifyConstant(method = "execute", constant = @Constant(intValue = 16)) 25 | private int overrideMaxLength(int len) { 26 | return NickLength; 27 | } 28 | 29 | public boolean opRequired(String[] args) { 30 | return false; 31 | } 32 | 33 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 34 | sender.sendMessage("/nickname set "); 35 | sender.sendMessage("/nickname get"); 36 | sender.sendMessage("/nickname clear"); 37 | if (sender.isConsole() || sender.isAdmin()) { 38 | sender.sendMessage("/nickname set "); 39 | sender.sendMessage("/nickname get"); 40 | sender.sendMessage("/nickname clear"); 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/DelhomeCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.Command; 7 | import net.minecraft.core.net.command.CommandHandler; 8 | import net.minecraft.core.net.command.CommandSender; 9 | 10 | public class DelhomeCommand extends Command { 11 | public DelhomeCommand() { 12 | super("delhome", ""); 13 | } 14 | 15 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 16 | EntityPlayer p = sender.getPlayer(); 17 | PlayerData.Homes homes = PlayerData.get(p).homes(); 18 | 19 | String homeName; 20 | if (args.length > 0) { 21 | homeName = String.join(" ", args); 22 | } else { 23 | homeName = "home"; 24 | } 25 | 26 | if (homeName.equals("bed")) { 27 | sender.sendMessage("§4This home is reserved for your bed."); 28 | return true; 29 | } 30 | 31 | 32 | if (homes.delHome(homeName)) { 33 | sender.sendMessage("§4Removed home: §1" + homeName); 34 | homes.save(); 35 | } else { 36 | sender.sendMessage("§4You don't have a home named: §1" + homeName); 37 | sender.sendMessage("§4Use: §3/sethome " + homeName + "§4 to create"); 38 | } 39 | return true; 40 | } 41 | 42 | public boolean opRequired(String[] args) { 43 | return !LusiiPlugin.homeCommand; 44 | } 45 | 46 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 47 | sender.sendMessage("§3/delhome §4"); 48 | sender.sendMessage("§5Delete one of your home"); } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/ContainerWorkbenchMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import net.minecraft.core.InventoryAction; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.player.inventory.*; 7 | import net.minecraft.core.player.inventory.slot.Slot; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Shadow; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | @Mixin(value = ContainerWorkbench.class, remap = false) 18 | public class ContainerWorkbenchMixin extends Container { 19 | 20 | @Inject( 21 | method = "isUsableByPlayer(Lnet/minecraft/core/entity/player/EntityPlayer;) Z", 22 | at = @At("HEAD"), 23 | cancellable = true 24 | ) 25 | public void craftCommand(EntityPlayer entityplayer, CallbackInfoReturnable cir) { 26 | if (LusiiPlugin.craftCommand) { 27 | cir.setReturnValue(true); 28 | } 29 | } 30 | 31 | @Shadow 32 | public List getMoveSlots(InventoryAction inventoryAction, Slot slot, int i, EntityPlayer entityPlayer) { 33 | return Collections.emptyList(); 34 | } 35 | 36 | @Shadow 37 | public List getTargetSlots(InventoryAction inventoryAction, Slot slot, int i, EntityPlayer entityPlayer) { 38 | return Collections.emptyList(); 39 | } 40 | 41 | @Shadow 42 | public boolean isUsableByPlayer(EntityPlayer entityPlayer) { 43 | return false; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/BlockTNTMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import net.minecraft.core.block.Block; 5 | import net.minecraft.core.block.BlockTNT; 6 | import net.minecraft.core.block.material.Material; 7 | import net.minecraft.core.entity.player.EntityPlayer; 8 | import net.minecraft.core.world.World; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 13 | 14 | @Mixin(value = BlockTNT.class, remap = false) 15 | public class BlockTNTMixin extends Block { 16 | 17 | public BlockTNTMixin(String key, int id, Material material) { 18 | super(key, id, material); 19 | } 20 | 21 | @Inject( 22 | method = "ignite(Lnet/minecraft/core/world/World; I I I Lnet/minecraft/core/entity/player/EntityPlayer;Z)V", 23 | at = @At("HEAD"), 24 | cancellable = true 25 | ) 26 | public void disableAbove(World world, int x, int y, int z, EntityPlayer player, boolean sound, CallbackInfo ci) { 27 | String msg = "Tnt is disabled above y: "; 28 | if (world.dimension.id == 0 && LusiiPlugin.DisableTNTOverworld <= y) { 29 | player.addChatMessage(msg + LusiiPlugin.DisableTNTOverworld); 30 | ci.cancel(); 31 | } 32 | if (world.dimension.id == 1 && LusiiPlugin.DisableTNTNether <= y) { 33 | player.addChatMessage(msg + LusiiPlugin.DisableTNTNether); 34 | ci.cancel(); 35 | } 36 | if (world.dimension.id == 2 && LusiiPlugin.DisableTNTSky <= y) { 37 | player.addChatMessage(msg + LusiiPlugin.DisableTNTSky); 38 | ci.cancel(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/SpawnCommandMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.*; 7 | import net.minecraft.core.net.command.commands.SpawnCommand; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Overwrite; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Inject; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 14 | 15 | @Mixin(value = SpawnCommand.class, remap = false) 16 | public class SpawnCommandMixin extends Command { 17 | public SpawnCommandMixin() { 18 | super("spawn"); 19 | } 20 | 21 | @Inject( 22 | method = "execute", 23 | at = @At(value = "INVOKE", 24 | target = "Lnet/minecraft/core/net/command/CommandSender;sendMessage(Ljava/lang/String;)V", 25 | shift = At.Shift.AFTER, by = 1 26 | ) 27 | ) 28 | public void trackTP(CommandHandler handler, CommandSender sender, String[] args, CallbackInfoReturnable cir) { 29 | EntityPlayer player = sender.getPlayer(); 30 | PlayerData.get(player).tpInfo().update(); 31 | } 32 | 33 | @Shadow 34 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 35 | return true; 36 | } 37 | 38 | @Overwrite 39 | public boolean opRequired(String[] args) { 40 | return !LusiiPlugin.spawnCommand; 41 | } 42 | 43 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 44 | sender.sendMessage("/spawn"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/TPAAllCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.utils.PlayerData; 4 | import lusiiplugin.utils.PlayerData.TPInfo.RequestType; 5 | import net.minecraft.core.net.command.Command; 6 | import net.minecraft.core.net.command.CommandHandler; 7 | import net.minecraft.core.net.command.CommandSender; 8 | import net.minecraft.server.entity.player.EntityPlayerMP; 9 | 10 | import java.util.List; 11 | import java.util.Objects; 12 | 13 | 14 | public class TPAAllCommand extends Command { 15 | public TPAAllCommand() { 16 | super("tpaall", "tpall"); 17 | } 18 | 19 | public boolean opRequired(String[] args) { 20 | return true; 21 | } 22 | 23 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 24 | sender.sendMessage("§3/tpall"); 25 | sender.sendMessage("§5Request all players to teleport to you"); 26 | } 27 | 28 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 29 | List players = handler.asServer().minecraftServer.playerList.playerEntities; 30 | String destUser = sender.getPlayer().username; 31 | 32 | for (EntityPlayerMP p : players) { 33 | if (Objects.equals(p.username, destUser)) continue; 34 | 35 | PlayerData playerData = PlayerData.get(p); 36 | boolean isOnlyRequest = playerData.tpInfo().sendRequest(destUser, RequestType.TPAHERE); 37 | if (isOnlyRequest) { 38 | p.addChatMessage("§4" + destUser + "§1 has sent you a request to teleport to them."); 39 | p.addChatMessage("§5/tpyes §1to accept, §e/tpno §1to deny."); 40 | } 41 | } 42 | sender.sendMessage("§4Sent a request to all players."); 43 | 44 | return true; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/PayCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import net.minecraft.core.entity.player.EntityPlayer; 4 | import net.minecraft.core.net.command.Command; 5 | import net.minecraft.core.net.command.CommandHandler; 6 | import net.minecraft.core.net.command.CommandSender; 7 | 8 | public class PayCommand extends Command { 9 | public PayCommand() { 10 | super("pay"); 11 | } 12 | 13 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 14 | if (args.length < 2) { 15 | return false; 16 | } 17 | 18 | int payAmount = Integer.parseInt(args[1]); 19 | EntityPlayer receivePlayer = handler.getPlayer(args[0]); 20 | EntityPlayer sendPlayer = sender.getPlayer(); 21 | 22 | if (sendPlayer.score < payAmount) { 23 | sender.sendMessage("§e§lInsufficient funds!"); 24 | return true; 25 | } 26 | if (payAmount <= 0) { 27 | sender.sendMessage("§e§lAmount must be above 0."); 28 | return true; 29 | } 30 | if (receivePlayer == null) { 31 | sender.sendMessage("§4You must specify a player!"); 32 | return false; 33 | } 34 | sendPlayer.score -= payAmount; 35 | receivePlayer.score += payAmount; 36 | sender.sendMessage("§1Paid §4" + receivePlayer.username + " §3" + payAmount + "§1 points."); 37 | receivePlayer.addChatMessage("§4" + sendPlayer.username + "§1 has paid you §3" + payAmount + "§1 points."); 38 | return true; 39 | } 40 | 41 | public boolean opRequired(String[] args) { 42 | return false; 43 | } 44 | 45 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 46 | sender.sendMessage("§3/pay §4 "); 47 | sender.sendMessage("§5Pay another user with points"); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/utils/PlayerHomesManager.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import lusiiplugin.LusiiPlugin; 6 | 7 | import java.io.*; 8 | import java.nio.charset.StandardCharsets; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | import java.util.*; 13 | 14 | public class PlayerHomesManager { 15 | public HashMap allPlayerHomes = new HashMap<>(); 16 | private final Path filePath = Paths.get(LusiiPlugin.SAVE_DIR).resolve("homes.ser"); 17 | 18 | public PlayerHomesManager() { 19 | if (Files.exists(filePath)) { 20 | try (ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(filePath))) { 21 | allPlayerHomes = (HashMap) ois.readObject(); 22 | allPlayerHomes.forEach((username, value) -> { 23 | System.out.println(username + ": " + value.userHomes.toString()); 24 | File dataDir = Paths.get(LusiiPlugin.SAVE_DIR, username).toFile(); 25 | 26 | if (!dataDir.exists()) { 27 | dataDir.mkdir(); 28 | } 29 | 30 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 31 | String json = gson.toJson(value.userHomes); 32 | try { 33 | Path homeFile = new File(dataDir, "homes.json").toPath(); 34 | Files.write(homeFile, json.getBytes(StandardCharsets.UTF_8)); 35 | } catch (IOException e) { 36 | System.err.println("Error writing file: " + e.getMessage()); 37 | } 38 | }); 39 | } catch (IOException | ClassNotFoundException e) { 40 | } 41 | try { 42 | Files.move(filePath, filePath.resolveSibling("old_homes.ser")); 43 | } catch (IOException e) {} 44 | } 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/BlockFireMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import net.minecraft.core.block.Block; 5 | import net.minecraft.core.block.BlockFire; 6 | import net.minecraft.core.block.material.Material; 7 | import net.minecraft.core.world.World; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; 13 | 14 | import java.util.Random; 15 | 16 | @Mixin(value = BlockFire.class, remap = false) 17 | public class BlockFireMixin extends Block { 18 | public BlockFireMixin(String key, int id) { 19 | super(key, id, Material.fire); 20 | this.setTicking(!LusiiPlugin.staticFire); 21 | this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F); 22 | } 23 | 24 | @Inject(method = "updateTick", at = @At("HEAD"), cancellable = true) 25 | public void stopTick(World world, int x, int y, int z, Random rand, CallbackInfo ci) { 26 | if (LusiiPlugin.staticFire) ci.cancel(); 27 | } 28 | 29 | @Inject(method = "canFirePersist", at = @At("HEAD"), cancellable = true) 30 | public void stopFireDespawn(World world, int x, int y, int z, CallbackInfoReturnable cir) { 31 | if (LusiiPlugin.staticFire) { 32 | cir.setReturnValue(world.isBlockNormalCube(x, y - 1, z)); 33 | } 34 | } 35 | 36 | @Inject(method = "tryToCatchBlockOnFire", at = @At("HEAD"), cancellable = true) 37 | public void disableCatchOnFire(World world, int x, int y, int z, int chance, Random random, int meta, CallbackInfo ci) { 38 | if (LusiiPlugin.staticFire) ci.cancel(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/CommandsMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | 4 | import lusiiplugin.commands.*; 5 | import net.minecraft.core.net.command.Command; 6 | import net.minecraft.core.net.command.Commands; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | 13 | import java.util.List; 14 | 15 | @Mixin(value = Commands.class, remap = false) 16 | public final class CommandsMixin { 17 | @Shadow 18 | public static List commands; 19 | @Inject(method = "initCommands", at = @At("TAIL")) 20 | private static void lusiiCommands(CallbackInfo ci) { 21 | commands.add(new MOTDCommand()); 22 | commands.add(new InfoCommand()); 23 | commands.add(new ColoursCommand()); 24 | commands.add(new SethomeCommand()); 25 | commands.add(new DelhomeCommand()); 26 | commands.add(new HomeCommand()); 27 | commands.add(new HomesCommand()); 28 | commands.add(new TransferHomesCommand()); 29 | commands.add(new RulesCommand()); 30 | commands.add(new OPChatCommand()); 31 | commands.add(new InvseeCommand()); 32 | commands.add(new CraftingCommand()); 33 | commands.add(new PingCommand()); 34 | commands.add(new RTPCommand()); 35 | commands.add(new CrashCommand()); 36 | commands.add(new PayCommand()); 37 | commands.add(new BackCommand()); 38 | commands.add(new TPAAllCommand()); 39 | commands.add(new TPAHereCommand()); 40 | commands.add(new TPACommand()); 41 | commands.add(new TPConfirmCommand()); 42 | commands.add(new TPDenyCommand()); 43 | commands.add(new TPRequestsCommand()); 44 | commands.add(new TreecapToggleCommand()); 45 | commands.add(new SudoCommand()); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/ColoursCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import net.minecraft.core.net.command.Command; 4 | import net.minecraft.core.net.command.CommandHandler; 5 | import net.minecraft.core.net.command.CommandSender; 6 | 7 | public class ColoursCommand extends Command { 8 | public ColoursCommand() { 9 | super("colours", "colors"); 10 | } 11 | 12 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 13 | sender.sendMessage("$$0 outputs §0White"); 14 | sender.sendMessage("$$1 outputs §1Orange"); 15 | sender.sendMessage("$$2 outputs §2Magenta"); 16 | sender.sendMessage("$$3 outputs §3Light blue / Aqua"); 17 | sender.sendMessage("$$4 outputs §4Yellow"); 18 | sender.sendMessage("$$5 outputs §5Lime"); 19 | sender.sendMessage("$$6 outputs §6Pink"); 20 | sender.sendMessage("$$7 outputs §7Grey"); 21 | sender.sendMessage("$$8 outputs §8Light Grey / Silver"); 22 | sender.sendMessage("$$9 outputs §9Cyan / Turquoise"); 23 | sender.sendMessage("$$a outputs §aPurple"); 24 | sender.sendMessage("$$b outputs §bBlue"); 25 | sender.sendMessage("$$c outputs §cBrown"); 26 | sender.sendMessage("$$d outputs §dGreen"); 27 | sender.sendMessage("$$e outputs §eRed"); 28 | sender.sendMessage("$$f outputs §fBlack"); 29 | sender.sendMessage("$$k outputs §kObfuscated §r(Obfuscated, §e§lOperator§r only.)"); 30 | sender.sendMessage("$$l outputs §lBold"); 31 | sender.sendMessage("$$m outputs §mStrikethrough"); 32 | sender.sendMessage("$$n outputs §nUnderline"); 33 | sender.sendMessage("$$o outputs §oItalic"); 34 | sender.sendMessage("$$r §3resets colour to §rnormal!"); 35 | return true; 36 | } 37 | 38 | public boolean opRequired(String[] args) { 39 | return false; 40 | } 41 | 42 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 43 | sender.sendMessage("§3/colours or /colors"); 44 | sender.sendMessage("§5Display formatting code"); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/EntityPlayerMPMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import lusiiplugin.utils.PlayerData; 4 | import net.minecraft.core.block.entity.TileEntity; 5 | import net.minecraft.core.entity.Entity; 6 | import net.minecraft.core.entity.player.EntityPlayer; 7 | import net.minecraft.core.world.World; 8 | import net.minecraft.server.entity.player.EntityPlayerMP; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Unique; 11 | import org.spongepowered.asm.mixin.gen.Invoker; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Inject; 14 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 15 | 16 | public class EntityPlayerMPMixin { 17 | @Mixin(value = EntityPlayerMP.class, remap = false) 18 | public abstract static class Root extends EntityPlayer { 19 | protected Root(World world) { 20 | super(world); 21 | } 22 | 23 | @Inject(method = "tick", at = @At("HEAD")) 24 | private void tickPlayerData(CallbackInfo ci) { 25 | PlayerData data = PlayerData.get(this); 26 | if (data != null) { 27 | data.tick(); 28 | } 29 | } 30 | 31 | @Inject(method = "onDeath", at = @At("HEAD")) 32 | public void saveOnDeath(Entity entity, CallbackInfo ci) { 33 | PlayerData.get(this).save(); 34 | } 35 | } 36 | 37 | @Mixin(value = EntityPlayerMP.class, remap = false) 38 | public static class Data implements PlayerData.Interface { 39 | @Unique 40 | PlayerData playerData; 41 | 42 | @Override 43 | public PlayerData betterthanVanilla$getPlayerData() { 44 | return this.playerData; 45 | } 46 | 47 | @Override 48 | public void betterthanVanilla$setPlayerData(EntityPlayer player) { 49 | this.playerData = new PlayerData(player); 50 | } 51 | } 52 | 53 | @Mixin(value = EntityPlayerMP.class, remap = false) 54 | public interface Interface { 55 | @Invoker("getTileEntityInfo") 56 | void getTEInfo(TileEntity tileEntity); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/SethomeCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.Command; 7 | import net.minecraft.core.net.command.CommandHandler; 8 | import net.minecraft.core.net.command.CommandSender; 9 | 10 | public class SethomeCommand extends Command { 11 | public SethomeCommand() { 12 | super("sethome", ""); 13 | } 14 | 15 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 16 | EntityPlayer p = sender.getPlayer(); 17 | PlayerData.Homes homes = PlayerData.get(p).homes(); 18 | 19 | String homeName; 20 | if (args.length > 0) { 21 | homeName = String.join(" ", args); 22 | } else { 23 | homeName = "home"; 24 | } 25 | 26 | if (homeName.equals("bed")) { 27 | sender.sendMessage("§4This home is reserved for your bed."); 28 | return true; 29 | } 30 | 31 | if (homes.getAmount() == LusiiPlugin.maxHomes) { 32 | sender.sendMessage("§4You've reached the max amount of homes!"); 33 | return true; 34 | } 35 | 36 | if (homes.setHome(p, homeName)) { 37 | sender.sendMessage("§4Created home: §1" + homeName); 38 | // homes.save(); 39 | } else { 40 | sender.sendMessage("§4You already have a home named: §1" + homeName); 41 | sender.sendMessage("§4Use: §3/delhome " + homeName + "§4 to remove"); 42 | } 43 | return true; 44 | } 45 | 46 | public boolean opRequired(String[] args) { 47 | return !LusiiPlugin.homeCommand; 48 | } 49 | 50 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 51 | sender.sendMessage("§3/sethome §4[home]"); 52 | sender.sendMessage("§5Set a new home at your position."); 53 | EntityPlayer p = sender.getPlayer(); 54 | PlayerData playerData = PlayerData.get(p); 55 | int homeCount = playerData.homes().getAmount(); 56 | sender.sendMessage("§5Current homes: §4" + homeCount + "§1/" + LusiiPlugin.maxHomes); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/TPDenyCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import lusiiplugin.utils.PlayerData.TPInfo.RequestType; 6 | import net.minecraft.core.entity.player.EntityPlayer; 7 | import net.minecraft.core.net.command.Command; 8 | import net.minecraft.core.net.command.CommandHandler; 9 | import net.minecraft.core.net.command.CommandSender; 10 | import org.apache.commons.lang3.tuple.Pair; 11 | 12 | import java.util.Objects; 13 | 14 | public class TPDenyCommand extends Command { 15 | public TPDenyCommand() { 16 | super("tpdeny", "tpno", "tn"); 17 | } 18 | 19 | public boolean opRequired(String[] args) { 20 | return !LusiiPlugin.TPACommand; 21 | } 22 | 23 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 24 | sender.sendMessage("§3/tpno §4[username]"); 25 | sender.sendMessage("§5Deny a teleport request, optionally specify a player"); 26 | sender.sendMessage("§5for multiple requests"); 27 | } 28 | 29 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 30 | if (sender.isConsole()) return true; 31 | 32 | EntityPlayer player = sender.getPlayer(); 33 | PlayerData.TPInfo tpInfo = PlayerData.get(player).tpInfo(); 34 | 35 | if (tpInfo.hasNoRequests()) { 36 | sender.sendMessage("§4You don't have any requests."); 37 | return true; 38 | } 39 | 40 | Pair request = tpInfo.getNewestRequest(); 41 | String denyUser = request.getKey(); 42 | 43 | if (args.length > 0) { 44 | String target = args[0]; 45 | if (Objects.equals(target, "wyspr")) target = "wyspr_"; // ;3 46 | 47 | if (tpInfo.hasRequestFrom(target)) { 48 | denyUser = target; 49 | } else { 50 | sender.sendMessage("§1You don't have a request from §4" + target); 51 | return true; 52 | } 53 | } 54 | 55 | tpInfo.removeRequest(denyUser); 56 | 57 | sender.sendMessage("§1Denied TP request from §4" + denyUser); 58 | return true; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/utils/PlayerHomes.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.utils; 2 | 3 | import net.minecraft.core.entity.player.EntityPlayer; 4 | 5 | import java.io.Serializable; 6 | import java.util.*; 7 | 8 | public class PlayerHomes implements Serializable { 9 | private static final long serialVersionUID = 1L; // Ensures version compatibility during deserialization 10 | public HashMap userHomes; 11 | 12 | private PlayerHomes() { 13 | this.userHomes = new HashMap<>(); 14 | } 15 | 16 | public static PlayerHomes blank() { 17 | return new PlayerHomes(); 18 | } 19 | 20 | public int getAmount() { 21 | return userHomes.size(); 22 | } 23 | 24 | /** 25 | * @return true if the home was added, false if the home exists. 26 | */ 27 | public boolean setHome(EntityPlayer p, String homeName) { 28 | if (userHomes.containsKey(homeName)) { 29 | return false; 30 | } 31 | 32 | userHomes.put(homeName, new HomePosition(p.x, p.y, p.z, p.dimension)); 33 | return true; 34 | } 35 | 36 | public void addHome(String name, double x, double y, double z, int dim) { 37 | userHomes.put(name, new HomePosition(x, y, z, dim)); 38 | } 39 | 40 | /** 41 | * @return true if the home was removed, false if the home does not exist. 42 | */ 43 | public boolean delHome(String homeName) { 44 | if (!userHomes.containsKey(homeName)) { 45 | return false; 46 | } 47 | 48 | userHomes.remove(homeName); 49 | return true; 50 | } 51 | 52 | /** 53 | * @return An Optional containing the HomePosition for the player, or an empty Optional if it does not exist. 54 | */ 55 | public Optional getHomePos(String homeName) { 56 | return Optional.ofNullable(userHomes.get(homeName)); 57 | } 58 | 59 | /** 60 | * @return An Optional containing an ArrayList of home names for the player, 61 | * or an empty Optional if the player has no homes. 62 | */ 63 | public Optional> getHomesList() { 64 | ArrayList result = new ArrayList<>(userHomes.keySet()); 65 | 66 | if (result.isEmpty()) { 67 | return Optional.empty(); 68 | } else { 69 | return Optional.of(result); 70 | } 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/RTPCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.Command; 7 | import net.minecraft.core.net.command.CommandHandler; 8 | import net.minecraft.core.net.command.CommandSender; 9 | 10 | import java.util.Random; 11 | 12 | public class RTPCommand extends Command { 13 | Random r = new Random(); 14 | public RTPCommand() { 15 | super("rtp"); 16 | } 17 | 18 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 19 | if (sender.isConsole()) return true; 20 | 21 | EntityPlayer p = sender.getPlayer(); 22 | PlayerData.TPInfo tpInfo = PlayerData.get(p).tpInfo(); 23 | 24 | if (p.score < LusiiPlugin.RTPCost) { 25 | sender.sendMessage("§4You do not have enough points to use this command! You need §1" + (LusiiPlugin.RTPCost - p.score) + "§4 more points!"); 26 | return true; 27 | } 28 | if (p.dimension != 0) { 29 | sender.sendMessage("§4You may only use this in the overworld!"); 30 | return true; 31 | } 32 | if (!tpInfo.canTP() && !sender.isAdmin()) { 33 | int waitTime = tpInfo.cooldown(); 34 | sender.sendMessage("§4Teleport available in §1" + waitTime + "§4 seconds."); 35 | return true; 36 | } 37 | int randX = (int) (r.nextDouble() * 1000001.0 - 500000.0); 38 | int randZ = (int) (r.nextDouble() * 1000001.0 - 500000.0); 39 | 40 | tpInfo.update(); 41 | 42 | if (LusiiPlugin.teleport(p, randX, 256, randZ, p.dimension)) { 43 | sender.sendMessage("§4Teleported! §lShould you get stuck, rejoin§4."); 44 | 45 | p.score -= LusiiPlugin.RTPCost; 46 | p.fireImmuneTicks = 200; 47 | p.onGround = false; 48 | p.maxHurtTime = 200; 49 | p.hurtTime = 200; 50 | p.airSupply = 1000; 51 | p.fallDistance = -1000; 52 | } 53 | 54 | return true; 55 | } 56 | 57 | 58 | public boolean opRequired(String[] args) { 59 | return !LusiiPlugin.RTPCommand; 60 | } 61 | 62 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 63 | sender.sendMessage("§3/rtp"); 64 | sender.sendMessage("§5Randomly teleport yourself great distances"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/HomeCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.*; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.*; 7 | import net.minecraft.core.world.chunk.ChunkCoordinates; 8 | 9 | import java.util.Optional; 10 | 11 | public class HomeCommand extends Command { 12 | public HomeCommand() { 13 | super("home"); 14 | } 15 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 16 | EntityPlayer p = sender.getPlayer(); 17 | PlayerData playerData = PlayerData.get(p); 18 | PlayerData.Homes homes = playerData.homes(); 19 | PlayerData.TPInfo tpInfo = playerData.tpInfo(); 20 | String homeName; 21 | if (args.length > 0) { 22 | homeName = String.join(" ", args); 23 | } else { 24 | homeName = "home"; 25 | } 26 | 27 | Optional homePos; 28 | 29 | if (homeName.equals("bed")) { 30 | ChunkCoordinates b = p.getPlayerSpawnCoordinate(); 31 | if (b == null) { 32 | sender.sendMessage("§1You do not have a bed! You should work on that!"); 33 | return true; 34 | } 35 | homePos = Optional.of(new WorldPosition(b.x, b.y+1.0, b.z, 0)); 36 | } else { 37 | homePos = homes.getHomePos(homeName); 38 | } 39 | boolean homeNotFound = !homePos.isPresent(); 40 | 41 | if (homeNotFound) { 42 | sender.sendMessage("§1You do not have a home named: §4" + homeName); 43 | sender.sendMessage("§1View your homes with: §3/homes"); 44 | return true; 45 | } 46 | 47 | if (tpInfo.canTP() || sender.isAdmin()) { 48 | tpInfo.update(); 49 | WorldPosition h = homePos.get(); 50 | if (LusiiPlugin.teleport(p, h)) { 51 | sender.sendMessage("§4Teleported to §1" + homeName); 52 | } 53 | } else { 54 | int waitTime = tpInfo.cooldown(); 55 | sender.sendMessage("§4Teleport available in §1" + waitTime + "§4 seconds."); 56 | } 57 | 58 | return true; 59 | } 60 | 61 | public boolean opRequired(String[] args) { 62 | return !LusiiPlugin.homeCommand; 63 | } 64 | 65 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 66 | sender.sendMessage("§3/home §4[home]"); 67 | sender.sendMessage("§5Teleport to one of your homes"); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/SudoCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import net.minecraft.core.net.command.*; 4 | import net.minecraft.server.entity.player.EntityPlayerMP; 5 | 6 | import java.util.Arrays; 7 | 8 | public class SudoCommand extends Command { 9 | public SudoCommand() { 10 | super("sudo", "doas"); 11 | } 12 | 13 | @Override 14 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 15 | if (args.length < 2) return false; 16 | 17 | String username = args[0]; 18 | String commandTitle = args[1]; 19 | String[] commandArgs = null; 20 | if (args.length > 2) { 21 | commandArgs = Arrays.copyOfRange(args, 2, args.length); 22 | } 23 | 24 | EntityPlayerMP targetPlayer; 25 | 26 | if (handler.playerExists(username)) { 27 | targetPlayer = (EntityPlayerMP) handler.getPlayer(username); 28 | } else { 29 | sender.sendMessage(TextFormatting.RED + "Player not found: " + TextFormatting.ORANGE + username); 30 | return false; 31 | } 32 | ServerPlayerCommandSender targetPlayerSender = new ServerPlayerCommandSender(handler.asServer().minecraftServer, targetPlayer); 33 | 34 | for (Command command : Commands.commands) { 35 | if (!command.isName(commandTitle)) continue; 36 | if (!targetPlayer.isOperator() && command.opRequired(commandArgs)) { 37 | sender.sendMessage(TextFormatting.ORANGE + username + TextFormatting.RED + "doesn't have permission to use this command!"); 38 | return true; 39 | } 40 | try { 41 | boolean success = command.execute(handler, targetPlayerSender, commandArgs); 42 | if (!success) { 43 | sender.sendMessage(TextFormatting.RED + "Error: invalid arguments"); 44 | command.sendCommandSyntax(handler, sender); 45 | } 46 | } catch (CommandError e) { 47 | sender.sendMessage(TextFormatting.RED + e.getMessage()); 48 | } 49 | return true; 50 | } 51 | 52 | sender.sendMessage(TextFormatting.RED + "Command not found: " + TextFormatting.ORANGE + commandTitle); 53 | return false; 54 | } 55 | 56 | @Override 57 | public boolean opRequired(String[] strings) { 58 | return true; 59 | } 60 | 61 | @Override 62 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 63 | sender.sendMessage("§3/sudo §4 "); 64 | sender.sendMessage("§5Run a command as another user."); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/TPACommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.Command; 7 | import net.minecraft.core.net.command.CommandHandler; 8 | import net.minecraft.core.net.command.CommandSender; 9 | 10 | import java.util.Objects; 11 | 12 | public class TPACommand extends Command { 13 | public TPACommand() { 14 | super("tpa", "tpask"); 15 | } 16 | 17 | public boolean opRequired(String[] args) { 18 | return !LusiiPlugin.TPACommand; 19 | } 20 | 21 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 22 | sender.sendMessage("§3/tpa §4"); 23 | sender.sendMessage("§5Request to teleport to a player"); 24 | } 25 | 26 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 27 | if (sender.isConsole()) return true; 28 | 29 | EntityPlayer p = sender.getPlayer(); 30 | PlayerData.TPInfo ptpInfo = PlayerData.get(p).tpInfo(); 31 | if (p.score < LusiiPlugin.TPACost) { 32 | sender.sendMessage("§4You do not have enough points to use this command! You need §1" + (LusiiPlugin.TPACost - p.score) + "§4 more points!"); 33 | return true; 34 | } 35 | if (p.dimension != 0) { 36 | sender.sendMessage("§4You may only use this in the overworld!"); 37 | return true; 38 | } 39 | if (args.length == 0) { 40 | sender.sendMessage("§4You must specify a player!"); 41 | return true; 42 | } 43 | if (!ptpInfo.canTP() && !sender.isAdmin()) { 44 | int waitTime = ptpInfo.cooldown(); 45 | sender.sendMessage("§4Teleport available in §1" + waitTime + "§4 seconds."); 46 | return true; 47 | } 48 | 49 | String target = args[0]; 50 | if (Objects.equals(target, "wyspr")) target = "wyspr_"; // ;) 51 | 52 | EntityPlayer targetPlayer = handler.getPlayer(target); 53 | if (targetPlayer == null) { 54 | sender.sendMessage("§4Player not found!"); 55 | return true; 56 | } 57 | 58 | PlayerData.TPInfo targettpInfo = PlayerData.get(targetPlayer).tpInfo(); 59 | 60 | boolean isOnlyRequest = targettpInfo.sendRequest(p.username, PlayerData.TPInfo.RequestType.TPA); 61 | if (isOnlyRequest) { 62 | sender.sendMessage("§4Sent a request to " + targetPlayer.getDisplayName()); 63 | targetPlayer.addChatMessage("§4" + p.username + "§1 has sent you a TP request."); 64 | targetPlayer.addChatMessage("§5/tpyes §1to accept, §e/tpno §1to deny."); 65 | } else { 66 | sender.sendMessage("§4You already have a pending request for " + target); 67 | } 68 | 69 | return true; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/TPAHereCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import net.minecraft.core.entity.player.EntityPlayer; 6 | import net.minecraft.core.net.command.Command; 7 | import net.minecraft.core.net.command.CommandHandler; 8 | import net.minecraft.core.net.command.CommandSender; 9 | 10 | import java.util.Objects; 11 | 12 | public class TPAHereCommand extends Command { 13 | public TPAHereCommand() { 14 | super("tpahere", "tphere", "tph"); 15 | } 16 | 17 | public boolean opRequired(String[] args) { 18 | return !LusiiPlugin.TPACommand; 19 | } 20 | 21 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 22 | sender.sendMessage("§3/tpahere §4"); 23 | sender.sendMessage("§5Request a player to teleport to you"); 24 | } 25 | 26 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 27 | if (sender.isConsole()) return true; 28 | 29 | EntityPlayer p = sender.getPlayer(); 30 | PlayerData.TPInfo ptpInfo = PlayerData.get(p).tpInfo(); 31 | if (p.score < LusiiPlugin.TPACost) { 32 | sender.sendMessage("§4You do not have enough points to use this command! You need §1" + (LusiiPlugin.TPACost - p.score) + "§4 more points!"); 33 | return true; 34 | } 35 | if (p.dimension != 0) { 36 | sender.sendMessage("§4You may only use this in the overworld!"); 37 | return true; 38 | } 39 | if (args.length == 0) { 40 | sender.sendMessage("§4You must specify a player!"); 41 | return true; 42 | } 43 | if (!ptpInfo.canTP() && !sender.isAdmin()) { 44 | int waitTime = ptpInfo.cooldown(); 45 | sender.sendMessage("§4Teleport available in §1" + waitTime + "§4 seconds."); 46 | return true; 47 | } 48 | 49 | String target = args[0]; 50 | if (Objects.equals(target, "wyspr")) target = "wyspr_"; // ;) 51 | 52 | EntityPlayer targetPlayer = handler.getPlayer(target); 53 | if (targetPlayer == null) { 54 | sender.sendMessage("§4Player not found!"); 55 | return true; 56 | } 57 | 58 | PlayerData.TPInfo targettpInfo = PlayerData.get(targetPlayer).tpInfo(); 59 | 60 | boolean isOnlyRequest = targettpInfo.sendRequest(p.username, PlayerData.TPInfo.RequestType.TPAHERE); 61 | if (isOnlyRequest) { 62 | sender.sendMessage("§4Sent a request to " + targetPlayer.getDisplayName()); 63 | targetPlayer.addChatMessage("§4" + p.username + "§1 has sent you a request to teleport to them."); 64 | targetPlayer.addChatMessage("§5/tpyes §1to accept, §e/tpno §1to deny."); 65 | } else { 66 | sender.sendMessage("§4You already have a pending request for " + target); 67 | } 68 | 69 | return true; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/commands/TPConfirmCommand.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.commands; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | import lusiiplugin.utils.PlayerData; 5 | import lusiiplugin.utils.PlayerData.TPInfo.RequestType; 6 | import net.minecraft.core.entity.player.EntityPlayer; 7 | import net.minecraft.core.net.command.Command; 8 | import net.minecraft.core.net.command.CommandHandler; 9 | import net.minecraft.core.net.command.CommandSender; 10 | import org.apache.commons.lang3.tuple.Pair; 11 | 12 | import java.util.Objects; 13 | 14 | public class TPConfirmCommand extends Command { 15 | public TPConfirmCommand() { 16 | super("tpconfirm", "tpyes", "ty"); 17 | } 18 | 19 | public boolean opRequired(String[] args) { 20 | return !LusiiPlugin.TPACommand; 21 | } 22 | 23 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 24 | sender.sendMessage("§3/tpyes §4[username]"); 25 | sender.sendMessage("§5Confirm a teleport request, optionally specify a player"); 26 | sender.sendMessage("§5for multiple requests"); 27 | } 28 | 29 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 30 | if (sender.isConsole()) return true; 31 | 32 | EntityPlayer acceptingPlayer = sender.getPlayer(); 33 | PlayerData.TPInfo acceptInfo = PlayerData.get(acceptingPlayer).tpInfo(); 34 | 35 | if (acceptingPlayer.dimension != 0) { 36 | sender.sendMessage("§4You may only use this in the overworld!"); 37 | return true; 38 | } 39 | if (acceptInfo.hasNoRequests()) { 40 | sender.sendMessage("§4You don't have any requests."); 41 | return true; 42 | } 43 | 44 | Pair request = acceptInfo.getNewestRequest(); 45 | String confirmUser = request.getKey(); 46 | 47 | if (args.length > 0) { 48 | String target = args[0]; 49 | if (Objects.equals(target, "wyspr")) target = "wyspr_"; // ;p 50 | 51 | if (acceptInfo.hasRequestFrom(target)) { 52 | confirmUser = target; 53 | } else { 54 | sender.sendMessage("§1You don't have a request from §4" + target); 55 | return true; 56 | } 57 | } 58 | 59 | acceptInfo.removeRequest(confirmUser); 60 | 61 | EntityPlayer requestPlayer = handler.getPlayer(confirmUser); 62 | if (requestPlayer == null) { 63 | sender.sendMessage("§4" + confirmUser + "§1 is not online."); 64 | return true; 65 | } 66 | 67 | PlayerData.TPInfo requestInfo = PlayerData.get(requestPlayer).tpInfo(); 68 | 69 | boolean didTeleport = false; 70 | RequestType requestType = request.getValue(); 71 | 72 | if (requestType == RequestType.TPA) { 73 | requestInfo.update(); 74 | didTeleport = LusiiPlugin.teleport(requestPlayer, acceptingPlayer); 75 | if (didTeleport) { 76 | requestPlayer.addChatMessage("§1Teleported to " + acceptingPlayer.getDisplayName()); 77 | } 78 | } else if (requestType == RequestType.TPAHERE) { 79 | acceptInfo.update(); 80 | didTeleport = LusiiPlugin.teleport(acceptingPlayer, requestPlayer); 81 | if (didTeleport) { 82 | requestPlayer.addChatMessage("§1Teleported " + acceptingPlayer.getDisplayName() + " to you"); 83 | acceptingPlayer.addChatMessage("§1Teleported to " + requestPlayer.getDisplayName()); 84 | } 85 | } 86 | 87 | if (didTeleport) { 88 | requestPlayer.score -= LusiiPlugin.TPACost; 89 | } 90 | 91 | return true; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/HelpCommandMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import net.minecraft.core.net.command.*; 4 | import net.minecraft.core.net.command.commands.HelpCommand; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.Unique; 8 | 9 | import java.util.List; 10 | import java.util.stream.Collectors; 11 | 12 | @Mixin(value = HelpCommand.class, remap = false) 13 | public class HelpCommandMixin extends Command { 14 | public HelpCommandMixin() { super("help", "?"); } 15 | 16 | @Override 17 | public boolean execute(CommandHandler handler, CommandSender sender, String[] args) { 18 | int itemsPerPage = 10; 19 | int pageNumber = 1; 20 | 21 | if (args.length > 0) { 22 | try { 23 | pageNumber = Integer.parseInt(args[0]); 24 | } catch (NumberFormatException ignored) { 25 | getCommand(handler, sender, args); 26 | return true; 27 | } 28 | } 29 | 30 | List allCmds = Commands.commands; 31 | if (!sender.isAdmin()) { // remove non op commands for non ops 32 | allCmds = allCmds.stream() 33 | .filter(c -> !c.opRequired(new String[0])) 34 | .collect(Collectors.toList()); 35 | } 36 | boolean outOfBounds = (pageNumber - 1) * itemsPerPage >= allCmds.size(); 37 | if (pageNumber < 1 || outOfBounds) pageNumber = 1; // stay in your lane 38 | 39 | List cmdsPage = getCommandPage(allCmds, pageNumber, itemsPerPage); 40 | 41 | for (Command cmd : cmdsPage) { 42 | String helpLine = TextFormatting.LIGHT_BLUE + cmd.getName(); 43 | 44 | if (cmd.getNames().size() > 1) { 45 | String alts = String.join("§8, §4", cmd.getNames()); 46 | helpLine += TextFormatting.LIGHT_GRAY + ": " + TextFormatting.YELLOW + alts; 47 | } 48 | sender.sendMessage(helpLine); 49 | } 50 | return true; 51 | } 52 | 53 | @Unique 54 | private List getCommandPage(List commands, int pageNumber, int itemsPerPage) { 55 | int totalItems = commands.size(); 56 | int startIndex = (pageNumber - 1) * itemsPerPage; 57 | 58 | int endIndex = startIndex + itemsPerPage; 59 | endIndex = Math.min(endIndex, totalItems); // Ensure the end index does not exceed the list size 60 | 61 | return commands.subList(startIndex, endIndex); 62 | } 63 | 64 | @Unique 65 | private void getCommand(CommandHandler handler, CommandSender sender, String[] args) { 66 | Command command = Commands.getCommand(args[0]); 67 | if (command == null) { 68 | throw new CommandError("Can't find command: \"" + args[0] + "\""); 69 | } 70 | if (!sender.isAdmin() && command.opRequired(args)) { 71 | throw new CommandError("You don't have permission to use this command!"); 72 | } 73 | if (command.getNames().size() > 1) { 74 | StringBuilder aliasStr = new StringBuilder(); 75 | aliasStr.append(TextFormatting.LIGHT_BLUE).append("Aliases: "); 76 | for (int i = 0; i < command.getNames().size(); ++i) { 77 | if (i > 0) { 78 | aliasStr.append(TextFormatting.LIGHT_GRAY) 79 | .append(", "); 80 | } 81 | aliasStr.append(TextFormatting.YELLOW) 82 | .append(command.getNames().get(i)); 83 | } 84 | sender.sendMessage(aliasStr.toString()); 85 | } 86 | sender.sendMessage(TextFormatting.LIGHT_BLUE + "Syntax: "); 87 | command.sendCommandSyntax(handler, sender); 88 | } 89 | 90 | @Shadow 91 | public boolean opRequired(String[] strings) { 92 | return false; 93 | } 94 | 95 | @Override 96 | public void sendCommandSyntax(CommandHandler handler, CommandSender sender) { 97 | sender.sendMessage("/help [page]"); 98 | sender.sendMessage("/help [command]"); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/utils/ConfigBuilder.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.utils; 2 | 3 | import lusiiplugin.LusiiPlugin; 4 | 5 | import java.io.IOException; 6 | import java.nio.charset.StandardCharsets; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.nio.file.Paths; 10 | import java.util.*; 11 | 12 | public class ConfigBuilder { 13 | private static final Map colorMap = new HashMap<>(24); 14 | static { 15 | colorMap.put("white", "0"); 16 | colorMap.put("orange", "1"); 17 | colorMap.put("magenta", "2"); 18 | colorMap.put("aqua", "3"); 19 | colorMap.put("yellow", "4"); 20 | colorMap.put("lime", "5"); 21 | colorMap.put("pink", "6"); 22 | colorMap.put("grey", "7"); 23 | colorMap.put("gray", "7"); 24 | colorMap.put("silver", "8"); 25 | colorMap.put("cyan", "9"); 26 | colorMap.put("purple", "a"); 27 | colorMap.put("blue", "b"); 28 | colorMap.put("brown", "c"); 29 | colorMap.put("green", "d"); 30 | colorMap.put("red", "e"); 31 | colorMap.put("black", "f"); 32 | colorMap.put("obf", "k"); 33 | colorMap.put("b", "l"); 34 | colorMap.put("s", "m"); 35 | colorMap.put("u", "n"); 36 | colorMap.put("i", "o"); 37 | colorMap.put("r", "r"); 38 | colorMap.put("reset", "r"); 39 | } 40 | 41 | private final Path cfgPath; 42 | private final String fileName; 43 | private final List defaultContent; 44 | private final boolean syntaxEnabled; 45 | public ConfigBuilder(String fileName, List defaultContent, boolean syntaxEnabled) { 46 | this.fileName = fileName; 47 | this.defaultContent = defaultContent; 48 | this.syntaxEnabled = syntaxEnabled; 49 | this.cfgPath = Paths.get(LusiiPlugin.CFG_DIR); 50 | createBase(); 51 | } 52 | 53 | private void createBase() { 54 | Path baseFile = cfgPath.resolve(fileName + ".txt"); 55 | if (!Files.exists(baseFile)) { 56 | try { 57 | System.out.println(baseFile + " does not exist. Creating it for you..."); 58 | Files.write(baseFile, defaultContent, StandardCharsets.UTF_8); 59 | System.out.println("Done! Check your config folder for " + baseFile); 60 | } catch (IOException e) { 61 | System.err.println("Error creating file: " + e.getMessage()); 62 | } 63 | } 64 | } 65 | public List get(int pageNumber) { 66 | if (pageNumber <= 1) { 67 | return readFile(cfgPath.resolve(fileName + ".txt")); 68 | } 69 | return readFile(cfgPath.resolve(fileName + pageNumber + ".txt")); 70 | } 71 | 72 | private List readFile(Path path) { 73 | try { 74 | List lines = Files.readAllLines(path, StandardCharsets.UTF_8); 75 | if (syntaxEnabled) { 76 | return parseSyntax(lines); 77 | } else { 78 | return lines; 79 | } 80 | } catch (IOException e) { 81 | List errorMsg = Collections.singletonList(parseTags("Page not found.")); 82 | return parseSyntax(errorMsg); 83 | } 84 | } 85 | 86 | private List parseSyntax(List content) { 87 | List parsedLines = new ArrayList<>(); 88 | 89 | for (String line : content) { 90 | if (line.startsWith("///")) continue; // Skip comments 91 | line = parseTags(line); 92 | parsedLines.add(line); 93 | } 94 | 95 | return parsedLines; 96 | } 97 | 98 | private String parseTags(String line) { 99 | // Handle escaping 100 | line = line.replaceAll("\\\\<", "ESCAPED_LT").replaceAll("\\\\>", "ESCAPED_GT"); 101 | // Process color tags 102 | for (Map.Entry entry : colorMap.entrySet()) { 103 | line = line.replaceAll("<" + entry.getKey() + ">", "§" + entry.getValue()); 104 | } 105 | // Revert escaped characters 106 | return line.replaceAll("ESCAPED_LT", "<").replaceAll("ESCAPED_GT", ">"); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/EntityBobberMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import com.mojang.nbt.CompoundTag; 4 | import lusiiplugin.LusiiPlugin; 5 | import net.minecraft.core.HitResult; 6 | import net.minecraft.core.block.Block; 7 | import net.minecraft.core.block.material.Material; 8 | import net.minecraft.core.entity.Entity; 9 | import net.minecraft.core.entity.EntityBobber; 10 | import net.minecraft.core.entity.player.EntityPlayer; 11 | import net.minecraft.core.item.Item; 12 | import net.minecraft.core.item.ItemStack; 13 | import net.minecraft.core.util.helper.DamageType; 14 | import net.minecraft.core.util.helper.MathHelper; 15 | import net.minecraft.core.util.phys.AABB; 16 | import net.minecraft.core.util.phys.Vec3d; 17 | import net.minecraft.core.world.World; 18 | import org.spongepowered.asm.mixin.Mixin; 19 | import org.spongepowered.asm.mixin.Overwrite; 20 | import org.spongepowered.asm.mixin.Shadow; 21 | 22 | import java.util.List; 23 | @Mixin(value = EntityBobber.class, remap = false) 24 | public class EntityBobberMixin extends Entity { 25 | private int xTile; 26 | private int yTile; 27 | private int zTile; 28 | private int inTile; 29 | private boolean inGround; 30 | public int shake; 31 | public EntityPlayer angler; 32 | private int ticksInGround; 33 | private int ticksInAir; 34 | private int ticksCatchable; 35 | public Entity bobber; 36 | private int field_6388_l; 37 | private double field_6387_m; 38 | private double field_6386_n; 39 | private double field_6385_o; 40 | private double field_6384_p; 41 | private double field_6383_q; 42 | private double velocityX; 43 | private double velocityY; 44 | private double velocityZ; 45 | public EntityBobberMixin(World world) { 46 | super(world); 47 | } 48 | 49 | @Overwrite 50 | public void tick() { 51 | super.tick(); 52 | if (this.field_6388_l > 0) { 53 | double d = this.x + (this.field_6387_m - this.x) / (double)this.field_6388_l; 54 | double d1 = this.y + (this.field_6386_n - this.y) / (double)this.field_6388_l; 55 | double d2 = this.z + (this.field_6385_o - this.z) / (double)this.field_6388_l; 56 | 57 | double d4; 58 | for(d4 = this.field_6384_p - (double)this.yRot; d4 < -180.0; d4 += 360.0) { 59 | } 60 | 61 | while(d4 >= 180.0) { 62 | d4 -= 360.0; 63 | } 64 | 65 | this.yRot = (float)((double)this.yRot + d4 / (double)this.field_6388_l); 66 | this.xRot = (float)((double)this.xRot + (this.field_6383_q - (double)this.xRot) / (double)this.field_6388_l); 67 | --this.field_6388_l; 68 | this.setPos(d, d1, d2); 69 | this.setRot(this.yRot, this.xRot); 70 | } else { 71 | if (!this.world.isClientSide) { 72 | ItemStack itemstack = this.angler.getCurrentEquippedItem(); 73 | if (this.angler.removed || !this.angler.isAlive() || itemstack == null || itemstack.getItem() != Item.toolFishingrod || this.distanceToSqr(this.angler) > 1024.0) { 74 | this.remove(); 75 | this.angler.fishEntity = null; 76 | return; 77 | } 78 | 79 | if (this.bobber != null) { 80 | if (!this.bobber.removed) { 81 | this.x = this.bobber.x; 82 | this.y = this.bobber.bb.minY + (double)this.bobber.bbHeight * 0.8; 83 | this.z = this.bobber.z; 84 | return; 85 | } 86 | 87 | this.bobber = null; 88 | } 89 | } 90 | 91 | if (this.shake > 0) { 92 | --this.shake; 93 | } 94 | 95 | if (this.inGround) { 96 | int i = this.world.getBlockId(this.xTile, this.yTile, this.zTile); 97 | if (i == this.inTile) { 98 | ++this.ticksInGround; 99 | if (this.ticksInGround == 1200) { 100 | this.remove(); 101 | } 102 | 103 | return; 104 | } 105 | 106 | this.inGround = false; 107 | this.xd *= (double)(this.random.nextFloat() * 0.2F); 108 | this.yd *= (double)(this.random.nextFloat() * 0.2F); 109 | this.zd *= (double)(this.random.nextFloat() * 0.2F); 110 | this.ticksInGround = 0; 111 | this.ticksInAir = 0; 112 | } else { 113 | ++this.ticksInAir; 114 | } 115 | 116 | Vec3d vec3d = Vec3d.createVector(this.x, this.y, this.z); 117 | Vec3d vec3d1 = Vec3d.createVector(this.x + this.xd, this.y + this.yd, this.z + this.zd); 118 | HitResult movingobjectposition = this.world.checkBlockCollisionBetweenPoints(vec3d, vec3d1); 119 | vec3d = Vec3d.createVector(this.x, this.y, this.z); 120 | vec3d1 = Vec3d.createVector(this.x + this.xd, this.y + this.yd, this.z + this.zd); 121 | if (movingobjectposition != null) { 122 | vec3d1 = Vec3d.createVector(movingobjectposition.location.xCoord, movingobjectposition.location.yCoord, movingobjectposition.location.zCoord); 123 | } 124 | 125 | Entity entity = null; 126 | List list = this.world.getEntitiesWithinAABBExcludingEntity(this, this.bb.addCoord(this.xd, this.yd, this.zd).expand(1.0, 1.0, 1.0)); 127 | double d3 = 0.0; 128 | 129 | double d7; 130 | for(int j = 0; j < list.size(); ++j) { 131 | Entity entity1 = (Entity)list.get(j); 132 | if (entity1.isPickable() && (entity1 != this.angler || this.ticksInAir >= 5)) { 133 | float f2 = 0.3F; 134 | AABB axisalignedbb = entity1.bb.expand((double)f2, (double)f2, (double)f2); 135 | HitResult movingobjectposition1 = axisalignedbb.func_1169_a(vec3d, vec3d1); 136 | if (movingobjectposition1 != null) { 137 | d7 = vec3d.distanceTo(movingobjectposition1.location); 138 | if (d7 < d3 || d3 == 0.0) { 139 | entity = entity1; 140 | d3 = d7; 141 | } 142 | } 143 | } 144 | } 145 | 146 | if (entity != null) { 147 | movingobjectposition = new HitResult(entity); 148 | } 149 | 150 | if (movingobjectposition != null && movingobjectposition.entity != null && movingobjectposition.entity.hurt(this.angler, 0, DamageType.COMBAT)) { 151 | this.bobber = movingobjectposition.entity; 152 | } 153 | 154 | if (!this.inGround) { 155 | this.move(this.xd, this.yd, this.zd); 156 | float f = MathHelper.sqrt_double(this.xd * this.xd + this.zd * this.zd); 157 | this.yRot = (float)(Math.atan2(this.xd, this.zd) * 180.0 / 3.1415927410125732); 158 | 159 | for(this.xRot = (float)(Math.atan2(this.yd, (double)f) * 180.0 / 3.1415927410125732); this.xRot - this.xRotO < -180.0F; this.xRotO -= 360.0F) { 160 | } 161 | 162 | while(this.xRot - this.xRotO >= 180.0F) { 163 | this.xRotO += 360.0F; 164 | } 165 | 166 | while(this.yRot - this.yRotO < -180.0F) { 167 | this.yRotO -= 360.0F; 168 | } 169 | 170 | while(this.yRot - this.yRotO >= 180.0F) { 171 | this.yRotO += 360.0F; 172 | } 173 | 174 | this.xRot = this.xRotO + (this.xRot - this.xRotO) * 0.2F; 175 | this.yRot = this.yRotO + (this.yRot - this.yRotO) * 0.2F; 176 | float f1 = 0.92F; 177 | if (this.onGround || this.horizontalCollision) { 178 | f1 = 0.5F; 179 | } 180 | 181 | int k = 5; 182 | double d5 = 0.0; 183 | 184 | int catchRate; 185 | for(catchRate = 0; catchRate < k; ++catchRate) { 186 | double d8 = this.bb.minY + (this.bb.maxY - this.bb.minY) * (double)(catchRate + 0) / (double)k - 0.125 + 0.125; 187 | double d9 = this.bb.minY + (this.bb.maxY - this.bb.minY) * (double)(catchRate + 1) / (double)k - 0.125 + 0.125; 188 | AABB axisalignedbb1 = AABB.getBoundingBoxFromPool(this.bb.minX, d8, this.bb.minZ, this.bb.maxX, d9, this.bb.maxZ); 189 | if (this.world.isAABBInMaterial(axisalignedbb1, Material.water)) { 190 | d5 += 1.0 / (double)k; 191 | } 192 | } 193 | 194 | if (d5 > 0.0) { 195 | if (this.ticksCatchable > 0) { 196 | --this.ticksCatchable; 197 | } else { 198 | catchRate = 500; 199 | int rainRate = 0; 200 | int algaeRate = 0; 201 | if (this.world.canBlockBeRainedOn(MathHelper.floor_double(this.x), MathHelper.floor_double(this.y) + 1, MathHelper.floor_double(this.z))) { 202 | rainRate = 200; 203 | } 204 | 205 | if (this.world.getBlockId(MathHelper.floor_double(this.x), MathHelper.floor_double(this.y) + 1, MathHelper.floor_double(this.z)) == Block.algae.id) { 206 | algaeRate = 100; 207 | } 208 | 209 | catchRate = catchRate - rainRate - algaeRate; 210 | if (this.random.nextInt(catchRate) == 0) { 211 | if (LusiiPlugin.addedTicksCatchable <=-40) { 212 | this.remove(); 213 | } else { 214 | this.ticksCatchable = this.random.nextInt(30) + 10 + LusiiPlugin.addedTicksCatchable; 215 | } 216 | this.yd -= 0.20000000298023224; 217 | this.world.playSoundAtEntity((Entity)null, this, "random.splash", 0.25F, 1.0F + (this.random.nextFloat() - this.random.nextFloat()) * 0.4F); 218 | float f3 = (float)MathHelper.floor_double(this.bb.minY); 219 | 220 | int j1; 221 | float f7; 222 | float f5; 223 | for(j1 = 0; (float)j1 < 1.0F + this.bbWidth * 20.0F; ++j1) { 224 | f5 = (this.random.nextFloat() * 2.0F - 1.0F) * this.bbWidth; 225 | f7 = (this.random.nextFloat() * 2.0F - 1.0F) * this.bbWidth; 226 | this.world.spawnParticle("bubble", this.x + (double)f5, (double)(f3 + 1.0F), this.z + (double)f7, this.xd, this.yd - (double)(this.random.nextFloat() * 0.2F), this.zd); 227 | } 228 | 229 | for(j1 = 0; (float)j1 < 1.0F + this.bbWidth * 20.0F; ++j1) { 230 | f5 = (this.random.nextFloat() * 2.0F - 1.0F) * this.bbWidth; 231 | f7 = (this.random.nextFloat() * 2.0F - 1.0F) * this.bbWidth; 232 | this.world.spawnParticle("splash", this.x + (double)f5, (double)(f3 + 1.0F), this.z + (double)f7, this.xd, this.yd, this.zd); 233 | } 234 | } 235 | } 236 | } 237 | 238 | if (this.ticksCatchable > 0) { 239 | this.yd -= (double)(this.random.nextFloat() * this.random.nextFloat() * this.random.nextFloat()) * 0.2; 240 | } 241 | 242 | d7 = d5 * 2.0 - 1.0; 243 | this.yd += 0.03999999910593033 * d7; 244 | if (d5 > 0.0) { 245 | f1 = (float)((double)f1 * 0.9); 246 | this.yd *= 0.8; 247 | } 248 | 249 | this.xd *= (double)f1; 250 | this.yd *= (double)f1; 251 | this.zd *= (double)f1; 252 | this.setPos(this.x, this.y, this.z); 253 | } 254 | } 255 | } 256 | 257 | 258 | 259 | 260 | @Shadow 261 | protected void init() { 262 | 263 | } 264 | 265 | @Shadow 266 | public void readAdditionalSaveData(CompoundTag compoundTag) { 267 | 268 | } 269 | 270 | @Shadow 271 | public void addAdditionalSaveData(CompoundTag compoundTag) { 272 | 273 | } 274 | 275 | 276 | } 277 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/utils/PlayerData.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.GsonBuilder; 5 | import com.google.gson.reflect.TypeToken; 6 | import lusiiplugin.LusiiPlugin; 7 | import net.minecraft.core.entity.player.EntityPlayer; 8 | import org.apache.commons.lang3.tuple.Pair; 9 | 10 | import java.io.File; 11 | import java.io.FileReader; 12 | import java.io.IOException; 13 | import java.io.Serializable; 14 | import java.lang.reflect.Type; 15 | import java.nio.charset.StandardCharsets; 16 | import java.nio.file.Files; 17 | import java.nio.file.Paths; 18 | import java.time.Duration; 19 | import java.time.Instant; 20 | import java.util.*; 21 | 22 | public class PlayerData { 23 | private final TPInfo tpInfo; 24 | private final Homes homes; 25 | private final Treecap treecap; 26 | private int saveTick = 0; 27 | 28 | public PlayerData(EntityPlayer player) { 29 | File dataDir = Paths.get(LusiiPlugin.SAVE_DIR, player.username).toFile(); 30 | tpInfo = new TPInfo(new File(dataDir, "tpInfo.json"), player); 31 | homes = new Homes(new File(dataDir, "homes.json")); 32 | treecap = new Treecap(new File(dataDir, "treecap.txt")); 33 | 34 | if (!dataDir.exists()) { 35 | dataDir.mkdir(); 36 | this.save(); 37 | return; 38 | } 39 | 40 | tpInfo.load(); 41 | homes.load(); 42 | treecap.load(); 43 | } 44 | 45 | public static PlayerData get(EntityPlayer player) { 46 | return ((Interface) player).betterthanVanilla$getPlayerData(); 47 | } 48 | 49 | public static void update(EntityPlayer player) { 50 | ((Interface) player).betterthanVanilla$setPlayerData(player); 51 | } 52 | 53 | public Homes homes() { 54 | return homes; 55 | } 56 | 57 | public TPInfo tpInfo() { 58 | return tpInfo; 59 | } 60 | 61 | public Treecap treecap() { 62 | return treecap; 63 | } 64 | 65 | public void save() { 66 | tpInfo.save(); 67 | homes.save(); 68 | treecap.save(); 69 | } 70 | 71 | public void tick() { 72 | saveTick++; 73 | if (saveTick == 6000) { // 60 * 20 * 5 = 6000 (5 mins) 74 | saveTick = 0; 75 | save(); 76 | } 77 | } 78 | 79 | public static class TPInfo implements Serializable { 80 | private static final long serialVersionUID = 1L; // Ensures version compatibility during deserialization 81 | private final transient File saveFile; 82 | private final transient EntityPlayer player; 83 | private transient Instant lastTPtime; 84 | private WorldPosition lastPos; 85 | private ArrayList requestsOrder; 86 | private HashMap requests; 87 | 88 | TPInfo(File file, EntityPlayer p) { 89 | saveFile = file; 90 | lastTPtime = Instant.now().minus(Duration.ofSeconds(LusiiPlugin.TPTimeout)); 91 | requestsOrder = new ArrayList<>(); 92 | requests = new HashMap<>(); 93 | lastPos = new WorldPosition(p.x, p.y, p.z, p.dimension); 94 | player = p; 95 | if (!saveFile.exists()) { 96 | save(); 97 | } 98 | } 99 | 100 | public void save() { 101 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 102 | String json = gson.toJson(this); 103 | if (!saveFile.exists()) { 104 | try { 105 | saveFile.createNewFile(); 106 | } catch (IOException e) { 107 | System.err.println("Error writing file: " + e.getMessage()); 108 | } 109 | } 110 | try { 111 | Files.write(saveFile.toPath(), json.getBytes(StandardCharsets.UTF_8)); 112 | } catch (IOException e) { 113 | System.err.println("Error writing file: " + e.getMessage()); 114 | } 115 | } 116 | 117 | public void load() { 118 | Gson gson = new Gson(); 119 | try { 120 | String json = new String(Files.readAllBytes(saveFile.toPath()), StandardCharsets.UTF_8); 121 | TPInfo loadedInfo = gson.fromJson(json, TPInfo.class); 122 | lastPos = loadedInfo.lastPos; 123 | requestsOrder = loadedInfo.requestsOrder; 124 | requests = loadedInfo.requests; 125 | } catch (IOException e) { 126 | System.err.println("Error reading file: " + e.getMessage()); 127 | } 128 | } 129 | 130 | // Returns 0 if tp is available 131 | public int cooldown() { 132 | Instant now = Instant.now(); 133 | Instant TPAvailable = lastTPtime.plus(Duration.ofSeconds(LusiiPlugin.TPTimeout)); 134 | 135 | if (now.isAfter(TPAvailable)) { 136 | return 0; 137 | } 138 | 139 | return Math.toIntExact(Duration.between(now, TPAvailable).getSeconds()); 140 | } 141 | 142 | public boolean canTP() { 143 | return cooldown() == 0; 144 | } 145 | 146 | public WorldPosition getLastPos() { 147 | return new WorldPosition(lastPos.x, lastPos.y, lastPos.z, lastPos.dim); 148 | } 149 | 150 | public void update() { 151 | lastTPtime = Instant.now(); 152 | lastPos = new WorldPosition(player.x, player.y, player.z, player.dimension); 153 | } 154 | 155 | public boolean atNewPos() { 156 | WorldPosition newPos = new WorldPosition(player.x, player.y, player.z, player.dimension); 157 | return !(newPos.equals(lastPos)); 158 | } 159 | 160 | /** 161 | * Send a teleport request from a user. 162 | *
163 | * If a user with a pending request sends a new request of a diffent type (TPA vs TPAHERE) 164 | * the old request is removed and the new one is moved to the front of the list. 165 | * @param username 166 | * @return true if request was sent, false if that user has a pending request 167 | */ 168 | public boolean sendRequest(String username, RequestType type) { 169 | if (requests.get(username) == type) { 170 | return false; 171 | } 172 | requestsOrder.remove(username); 173 | requestsOrder.add(username); 174 | requests.put(username, type); 175 | return true; 176 | } 177 | 178 | public boolean hasNoRequests() { 179 | return requests.isEmpty(); 180 | } 181 | 182 | public Pair getNewestRequest() { 183 | // Get the index of the newest request 184 | int lastIndex = requests.size() - 1; 185 | 186 | // Check if there are any requests 187 | if (lastIndex >= 0) { 188 | String lastRequestName = requestsOrder.get(lastIndex); 189 | RequestType req = requests.get(lastRequestName); 190 | 191 | return Pair.of(lastRequestName, req); 192 | } else { 193 | return null; 194 | } 195 | } 196 | 197 | public List getAllRequests() { 198 | if (hasNoRequests()) { 199 | return Collections.singletonList("§4You don't have any requests."); 200 | } 201 | ArrayList out = new ArrayList<>(); 202 | 203 | for (String username : requestsOrder) { 204 | RequestType type = requests.get(username); 205 | 206 | out.add("§1[ §3" + username + " §1| §4" + type.toString() + " §1]§r"); 207 | } 208 | 209 | return out; 210 | } 211 | 212 | public boolean hasRequestFrom(String username) { 213 | return requests.containsKey(username); 214 | } 215 | 216 | public void removeRequest(String username) { 217 | requestsOrder.remove(username); 218 | requests.remove(username); 219 | } 220 | 221 | public enum RequestType { 222 | TPA { @Override public String toString() { return "To you"; } }, 223 | TPAHERE { @Override public String toString() { return "To them"; } } 224 | } 225 | } 226 | 227 | public static class Homes implements Serializable { 228 | private static final long serialVersionUID = 1L; // Ensures version compatibility during deserialization 229 | private final transient File saveFile; 230 | private HashMap homesMap; 231 | 232 | Homes(File file) { 233 | saveFile = file; 234 | homesMap = new HashMap<>(); 235 | if (!saveFile.exists()) { 236 | save(); 237 | } 238 | } 239 | 240 | public void save() { 241 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 242 | String json = gson.toJson(this.homesMap); 243 | if (!saveFile.exists()) { 244 | try { 245 | saveFile.createNewFile(); 246 | } catch (IOException e) { 247 | System.err.println("Error writing file: " + e.getMessage()); 248 | } 249 | } 250 | try { 251 | Files.write(saveFile.toPath(), json.getBytes(StandardCharsets.UTF_8)); 252 | } catch (IOException e) { 253 | System.err.println("Error writing file: " + e.getMessage()); 254 | } 255 | } 256 | 257 | public void load() { 258 | Gson gson = new Gson(); 259 | try { 260 | Type type = new TypeToken>() {}.getType(); 261 | this.homesMap = gson.fromJson(new FileReader(saveFile), type); 262 | } catch (IOException e) { 263 | System.err.println("Error reading file: " + e.getMessage()); 264 | } 265 | } 266 | 267 | public int getAmount() { 268 | return homesMap.size(); 269 | } 270 | 271 | /** 272 | * @return true if the home was added, false if the home exists. 273 | */ 274 | public boolean setHome(EntityPlayer p, String homeName) { 275 | if (homesMap.containsKey(homeName)) { 276 | return false; 277 | } 278 | 279 | homesMap.put(homeName, new WorldPosition(p.x, p.y, p.z, p.dimension)); 280 | return true; 281 | } 282 | 283 | public void addHome(String name, double x, double y, double z, int dim) { 284 | homesMap.put(name, new WorldPosition(x, y, z, dim)); 285 | } 286 | 287 | /** 288 | * @return true if the home was removed, false if the home does not exist. 289 | */ 290 | public boolean delHome(String homeName) { 291 | if (!homesMap.containsKey(homeName)) { 292 | return false; 293 | } 294 | 295 | homesMap.remove(homeName); 296 | return true; 297 | } 298 | 299 | /** 300 | * @return An Optional containing the HomePosition for the player, or an empty Optional if it does not exist. 301 | */ 302 | public Optional getHomePos(String homeName) { 303 | return Optional.ofNullable(homesMap.get(homeName)); 304 | } 305 | 306 | /** 307 | * @return An ArrayList of home names for the player, 308 | * or an empty ArrayList if the player has no homes. 309 | */ 310 | public List getHomesList() { 311 | return new ArrayList<>(homesMap.keySet()); 312 | } 313 | } 314 | 315 | public static class Treecap implements Serializable { 316 | private static final long serialVersionUID = 1L; // Ensures version compatibility during deserialization 317 | private final transient File saveFile; 318 | private boolean inactive; 319 | 320 | Treecap(File file) { 321 | saveFile = file; 322 | inactive = false; 323 | if (!saveFile.exists()) { 324 | save(); 325 | } 326 | } 327 | 328 | public void save() { 329 | Gson gson = new GsonBuilder().setPrettyPrinting().create(); 330 | String json = gson.toJson(this); 331 | if (!saveFile.exists()) { 332 | try { 333 | saveFile.createNewFile(); 334 | } catch (IOException e) { 335 | System.err.println("Error writing file: " + e.getMessage()); 336 | } 337 | } 338 | try { 339 | Files.write(saveFile.toPath(), json.getBytes(StandardCharsets.UTF_8)); 340 | } catch (IOException e) { 341 | System.err.println("Error writing file: " + e.getMessage()); 342 | } 343 | } 344 | 345 | public void load() { 346 | Gson gson = new Gson(); 347 | try { 348 | String json = new String(Files.readAllBytes(saveFile.toPath()), StandardCharsets.UTF_8); 349 | inactive = gson.fromJson(json, Treecap.class).inactive; 350 | } catch (IOException e) { 351 | System.err.println("Error reading file: " + e.getMessage()); 352 | } 353 | } 354 | 355 | public boolean isActive() { 356 | return !inactive; 357 | } 358 | 359 | public boolean toggle() { 360 | inactive = !inactive; 361 | return inactive; 362 | } 363 | } 364 | 365 | public interface Interface { 366 | PlayerData betterthanVanilla$getPlayerData(); 367 | void betterthanVanilla$setPlayerData(EntityPlayer player); 368 | } 369 | } 370 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/mixin/NetServerHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin.mixin; 2 | 3 | import com.llamalad7.mixinextras.sugar.Local; 4 | import lusiiplugin.LusiiPlugin; 5 | import lusiiplugin.utils.PlayerData; 6 | import net.minecraft.core.block.Block; 7 | import net.minecraft.core.block.entity.TileEntity; 8 | import net.minecraft.core.block.entity.TileEntitySign; 9 | import net.minecraft.core.entity.Entity; 10 | import net.minecraft.core.entity.player.EntityPlayer; 11 | import net.minecraft.core.enums.EnumSignPicture; 12 | import net.minecraft.core.item.Item; 13 | import net.minecraft.core.item.ItemStack; 14 | import net.minecraft.core.net.ICommandListener; 15 | import net.minecraft.core.net.command.TextFormatting; 16 | import net.minecraft.core.net.handler.NetHandler; 17 | import net.minecraft.core.net.packet.*; 18 | import net.minecraft.core.player.inventory.slot.Slot; 19 | import net.minecraft.core.util.helper.ChatAllowedCharacters; 20 | import net.minecraft.core.util.helper.Direction; 21 | import net.minecraft.core.util.helper.MathHelper; 22 | import net.minecraft.core.world.chunk.ChunkCoordinates; 23 | import net.minecraft.server.MinecraftServer; 24 | import net.minecraft.server.entity.player.EntityPlayerMP; 25 | import net.minecraft.server.net.PlayerList; 26 | import net.minecraft.server.net.handler.NetServerHandler; 27 | import net.minecraft.server.world.WorldServer; 28 | import org.apache.log4j.Logger; 29 | import org.spongepowered.asm.mixin.Mixin; 30 | import org.spongepowered.asm.mixin.Overwrite; 31 | import org.spongepowered.asm.mixin.Shadow; 32 | import org.spongepowered.asm.mixin.injection.At; 33 | import org.spongepowered.asm.mixin.injection.Inject; 34 | import org.spongepowered.asm.mixin.injection.Redirect; 35 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 36 | 37 | import java.util.*; 38 | 39 | @Mixin(value = NetServerHandler.class, remap = false) 40 | public class NetServerHandlerMixin extends NetHandler implements ICommandListener { 41 | @Shadow 42 | public static Logger logger = Logger.getLogger("Minecraft"); 43 | @Shadow 44 | private MinecraftServer mcServer; 45 | @Shadow 46 | private EntityPlayerMP playerEntity; 47 | @Shadow 48 | private double lastPosX; 49 | @Shadow 50 | private double lastPosY; 51 | @Shadow 52 | private double lastPosZ; 53 | @Shadow 54 | private boolean hasMoved = true; 55 | @Shadow 56 | public void log(String string) {} 57 | @Shadow 58 | public void sendPacket(Packet packet) {} 59 | @Shadow 60 | private void handleSlashCommand(String s) {} 61 | @Shadow 62 | public String getUsername() { 63 | return null; 64 | } 65 | @Shadow 66 | public boolean isServerHandler() { 67 | return true; 68 | } 69 | 70 | @Inject( 71 | method = "handleUseEntity", 72 | at = @At(value = "INVOKE", 73 | target = "Lnet/minecraft/server/entity/player/EntityPlayerMP;useCurrentItemOnEntity(Lnet/minecraft/core/entity/Entity;)V", 74 | shift = At.Shift.BEFORE 75 | ) 76 | ) 77 | public void ridePlayer(Packet7UseEntity packet, CallbackInfo ci, @Local(ordinal = 0) Entity targetEntity) { 78 | if (targetEntity instanceof EntityPlayer && this.playerEntity.getHeldItem() == null && targetEntity.vehicle != this.playerEntity && LusiiPlugin.headSit) { 79 | this.playerEntity.startRiding(targetEntity); 80 | this.playerEntity.collision = false; 81 | this.playerEntity.noPhysics = true; 82 | } 83 | } 84 | 85 | @Inject( 86 | method = "handleEntityAction", 87 | at = @At( 88 | value = "INVOKE", 89 | target = "Lnet/minecraft/server/entity/player/EntityPlayerMP;setSneaking(Z)V", 90 | ordinal = 0, 91 | shift = At.Shift.BEFORE 92 | ) 93 | ) 94 | public void handlePlayerRideEject(Packet19EntityAction packet, CallbackInfo ci) { 95 | if (this.playerEntity.vehicle instanceof EntityPlayer) { 96 | this.playerEntity.noPhysics = false; 97 | this.playerEntity.collision = true; 98 | } 99 | } 100 | 101 | @Redirect( 102 | method = "handleFlying", 103 | at = @At( 104 | value = "INVOKE", 105 | target = "Lnet/minecraft/server/net/handler/NetServerHandler; kickPlayer(Ljava/lang/String;) V" 106 | ) 107 | ) 108 | private void redirectFlyKick(NetServerHandler self, String msg) { 109 | if ("You moved too quickly :( (Hacking?)".equals(msg)) { 110 | self.teleportAndRotate(this.lastPosX, this.lastPosY, this.lastPosZ, this.playerEntity.yRot, this.playerEntity.xRot); 111 | } else { 112 | self.kickPlayer(msg); 113 | } 114 | } 115 | 116 | @Redirect( 117 | method = "handleChat", 118 | at = @At( 119 | value = "INVOKE", 120 | target = "Lnet/minecraft/server/net/handler/NetServerHandler; handleSlashCommand(Ljava/lang/String;) V" 121 | ) 122 | ) 123 | public void allowColoredCommands(NetServerHandler self, String s) { 124 | if (LusiiPlugin.colourChat) { 125 | s = s.replace("$$", "§"); 126 | } 127 | if (!this.playerEntity.isOperator() && s.contains("§k")) { 128 | s = s.replace("§k", "$$k"); 129 | this.mcServer.playerList.sendChatMessageToPlayer(this.playerEntity.username, "§e§lHey!§r You may not use obfuscated text!"); 130 | } 131 | this.handleSlashCommand(s); 132 | } 133 | 134 | 135 | @Redirect(method = "handleChat", 136 | at = @At( 137 | value = "INVOKE", 138 | target = "Lnet/minecraft/server/net/PlayerList; sendEncryptedChatToAllPlayers(Ljava/lang/String;)V" 139 | ) 140 | ) 141 | public void allowColoredChat(PlayerList instance, String i) { 142 | String prefix = "<" + this.playerEntity.getDisplayName() + TextFormatting.RESET + "> " + TextFormatting.WHITE; 143 | String msg = i.substring(prefix.length()); 144 | 145 | if (LusiiPlugin.greenText && (msg.startsWith(">") || msg.startsWith(" >"))) { 146 | msg = TextFormatting.LIME + msg; 147 | } 148 | 149 | if (LusiiPlugin.colourChat) { 150 | msg = msg.replace("$$", "§"); 151 | } 152 | if (this.playerEntity.isOperator()) { 153 | prefix = TextFormatting.RED + TextFormatting.BOLD.toString() + "[OP] " + TextFormatting.RESET + prefix; 154 | } else if (prefix.contains("§k") || msg.contains("§k")) { 155 | msg = msg.replace("§k", "$$k"); 156 | prefix = prefix.replace("§k", "$$k"); 157 | instance.sendChatMessageToPlayer(this.playerEntity.username, "§e§lHey!§r You may not use obfuscated text!"); 158 | } 159 | 160 | System.out.println(prefix + msg); //do not delete! 161 | instance.sendEncryptedChatToAllPlayers(prefix + msg); 162 | } 163 | 164 | @Overwrite 165 | public void handleUpdateSign(Packet130UpdateSign packet) { 166 | WorldServer worldserver = this.mcServer.getDimensionWorld(this.playerEntity.dimension); 167 | if (worldserver.isBlockLoaded(packet.xPosition, packet.yPosition, packet.zPosition)) { 168 | TileEntity tileentity = worldserver.getBlockTileEntity(packet.xPosition, packet.yPosition, packet.zPosition); 169 | 170 | int l; 171 | int i; 172 | for(i = 0; i < 4; ++i) { 173 | boolean isLineValid = true; 174 | if (packet.signLines[i].length() > 15) { 175 | isLineValid = false; 176 | } else { 177 | packet.signLines[i] = packet.signLines[i].replaceAll("§", "$"); 178 | 179 | for(l = 0; l < packet.signLines[i].length(); ++l) { 180 | if (ChatAllowedCharacters.ALLOWED_CHARACTERS.indexOf(packet.signLines[i].charAt(l)) < 0) { 181 | isLineValid = false; 182 | break; 183 | } 184 | } 185 | } 186 | 187 | if (!isLineValid) { 188 | packet.signLines[i] = "!?"; 189 | } 190 | } 191 | 192 | if (tileentity instanceof TileEntitySign) { 193 | i = packet.xPosition; 194 | int y = packet.yPosition; 195 | l = packet.zPosition; 196 | TileEntitySign tileEntity = (TileEntitySign)tileentity; 197 | System.out.println("Sign placed by "+ this.playerEntity.username+" at "+ i + ", " + y + ", " + l); 198 | System.out.println("Contains text: " + Arrays.toString(packet.signLines)); 199 | for(int j1 = 0; j1 < 4; ++j1) { 200 | tileEntity.signText[j1] = packet.signLines[j1]; 201 | } 202 | 203 | tileEntity.setColor(TextFormatting.FORMATTINGS[packet.color]); 204 | tileEntity.setPicture(EnumSignPicture.values()[packet.picture]); 205 | tileEntity.onInventoryChanged(); 206 | worldserver.markBlockNeedsUpdate(i, y, l); 207 | } 208 | } 209 | 210 | } 211 | @Overwrite 212 | public void handlePlace(Packet15Place packet) { 213 | WorldServer worldserver = this.mcServer.getDimensionWorld(this.playerEntity.dimension); 214 | ItemStack itemstack = this.playerEntity.inventory.getCurrentItem(); 215 | 216 | 217 | if (this.playerEntity.getHealth() <= 0) { 218 | System.out.println(this.playerEntity.username + " tried to place a block while dead"); 219 | this.mcServer.playerList.sendChatMessageToPlayer(this.playerEntity.username,"§e§lHey!§r You cannot do that. §5Respawn§r."); 220 | return; 221 | } 222 | 223 | 224 | boolean flag = worldserver.field_819_z = worldserver.dimension.id != 0 || this.mcServer.playerList.isOp(this.playerEntity.username); 225 | if (packet.direction == Direction.NONE) { 226 | if (itemstack == null) { 227 | return; 228 | } 229 | 230 | this.playerEntity.playerController.func_6154_a(this.playerEntity, worldserver, itemstack); 231 | } else { 232 | int x = packet.xPosition; 233 | int y = packet.yPosition; 234 | int z = packet.zPosition; 235 | Direction direction = packet.direction; 236 | double xPlaced = packet.xPlaced; 237 | double yPlaced = packet.yPlaced; 238 | ChunkCoordinates chunkcoordinates = worldserver.getSpawnPoint(); 239 | int i1 = (int)MathHelper.abs((float)(x - chunkcoordinates.x)); 240 | int j1 = (int)MathHelper.abs((float)(z - chunkcoordinates.z)); 241 | if (i1 > j1) { 242 | j1 = i1; 243 | } 244 | 245 | if (this.hasMoved && this.playerEntity.distanceToSqr((double)x + 0.5, (double)y + 0.5, (double)z + 0.5) < 64.0 && (j1 > this.mcServer.spawnProtectionRange || flag)) { 246 | 247 | 248 | this.playerEntity.playerController.activateBlockOrUseItem(this.playerEntity, worldserver, itemstack, x, y, z, direction.getSide(), xPlaced, yPlaced); 249 | } 250 | if (worldserver.isBlockLoaded(packet.xPosition, packet.yPosition, packet.zPosition) && itemstack == Item.sign.getDefaultStack()) { 251 | TileEntity tileentity = worldserver.getBlockTileEntity(packet.xPosition, packet.yPosition, packet.zPosition); 252 | if (tileentity instanceof TileEntitySign) { 253 | TileEntitySign tileentitysign = (TileEntitySign) tileentity; 254 | worldserver.markBlockNeedsUpdate(packet.xPosition, packet.yPosition, packet.zPosition); 255 | return; 256 | } 257 | } 258 | this.playerEntity.playerNetServerHandler.sendPacket(new Packet53BlockChange(x, y, z, worldserver)); 259 | x += direction.getOffsetX(); 260 | y += direction.getOffsetY(); 261 | z += direction.getOffsetZ(); 262 | this.playerEntity.playerNetServerHandler.sendPacket(new Packet53BlockChange(x, y, z, worldserver)); 263 | } 264 | 265 | itemstack = this.playerEntity.inventory.getCurrentItem(); 266 | if (itemstack != null && itemstack.stackSize <= 0) { 267 | this.playerEntity.inventory.mainInventory[this.playerEntity.inventory.currentItem] = null; 268 | } 269 | 270 | this.playerEntity.isChangingQuantityOnly = true; 271 | this.playerEntity.inventory.mainInventory[this.playerEntity.inventory.currentItem] = ItemStack.copyItemStack(this.playerEntity.inventory.mainInventory[this.playerEntity.inventory.currentItem]); 272 | Slot slot = this.playerEntity.craftingInventory.func_20127_a(this.playerEntity.inventory, this.playerEntity.inventory.currentItem); 273 | this.playerEntity.craftingInventory.updateInventory(); 274 | this.playerEntity.isChangingQuantityOnly = false; 275 | if (!ItemStack.areItemStacksEqual(this.playerEntity.inventory.getCurrentItem(), packet.itemStack)) { 276 | this.sendPacket(new Packet103SetSlot(this.playerEntity.craftingInventory.windowId, slot.id, this.playerEntity.inventory.getCurrentItem())); 277 | } 278 | 279 | worldserver.field_819_z = false; 280 | } 281 | @Overwrite 282 | public void handleRespawn(Packet9Respawn packet) { 283 | if (this.playerEntity.getHealth() <= 0) { 284 | int score = this.playerEntity.score; 285 | this.playerEntity = this.mcServer.playerList.recreatePlayerEntity(this.playerEntity, 0); 286 | this.playerEntity.score = (int) ((double) score * LusiiPlugin.deathCost); 287 | PlayerData.update(this.playerEntity); 288 | } 289 | } 290 | 291 | @Overwrite 292 | public void handleBlockDig(Packet14BlockDig packet) { 293 | int x = packet.xPosition; 294 | int y = packet.yPosition; 295 | int z = packet.zPosition; 296 | if (this.playerEntity.getHealth() <= 0) { 297 | System.out.println(this.playerEntity.username + " tried to break a block while dead"); 298 | this.mcServer.playerList.sendChatMessageToPlayer(this.playerEntity.username, "§e§lHey!§r You cannot do that. §5Respawn§r."); 299 | return; 300 | } 301 | 302 | // Copied 303 | WorldServer world = this.mcServer.getDimensionWorld(this.playerEntity.dimension); 304 | if (packet.status == 4) { 305 | this.playerEntity.dropCurrentItem(false); 306 | } else if (packet.status == 5) { 307 | this.playerEntity.dropCurrentItem(true); 308 | } else if (packet.status == 6) { 309 | this.playerEntity.pickBlock(x, y, z); 310 | } else { 311 | double playerDistX = this.playerEntity.x - ((double) x + 0.5); 312 | double playerDistY = this.playerEntity.y - ((double) y + 0.5); 313 | double playerDistZ = this.playerEntity.z - ((double) z + 0.5); 314 | double playerDist = playerDistX * playerDistX + playerDistY * playerDistY + playerDistZ * playerDistZ; 315 | if (!(playerDist > 44.0)) { 316 | boolean ignoreSpawnProtection = world.field_819_z = this.mcServer.spawnProtectionRange <= 0 || world.dimension.id != 0 || this.mcServer.playerList.isOp(this.playerEntity.username); 317 | ChunkCoordinates spawnPos = world.getSpawnPoint(); 318 | int spawnDistX = (int) MathHelper.abs((float) (x - spawnPos.x)); 319 | int spawnDistZ = (int) MathHelper.abs((float) (z - spawnPos.z)); 320 | int distanceFromSpawn = Math.max(spawnDistX, spawnDistZ); 321 | if (distanceFromSpawn <= this.mcServer.spawnProtectionRange && !ignoreSpawnProtection) { 322 | this.playerEntity.playerNetServerHandler.sendPacket(new Packet53BlockChange(x, y, z, world)); 323 | world.field_819_z = false; 324 | } else { 325 | if (packet.status == 0) { 326 | if (!((world.getBlock(x, y, z) == Block.signPostPlanksOak || world.getBlock(x, y, z) == Block.signWallPlanksOak) && this.playerEntity.isSneaking())) { 327 | this.playerEntity.playerController.startMining(x, y, z, packet.side); 328 | } 329 | } else if (packet.status == 1) { 330 | if (!((world.getBlock(x, y, z) == Block.signPostPlanksOak || world.getBlock(x, y, z) == Block.signWallPlanksOak) && this.playerEntity.isSneaking())) { 331 | this.playerEntity.playerController.hitBlock(x, y, z, packet.side); 332 | } 333 | } else if (packet.status == 2 && !this.playerEntity.playerController.destroyBlock(x, y, z)) 334 | if (world.getBlock(x, y, z) == Block.chestLegacy || world.getBlock(x, y, z) == Block.chestPlanksOakPainted || world.getBlock(x, y, z) == Block.chestPlanksOak || world.getBlock(x, y, z) == Block.chestLegacyPainted) { 335 | System.out.println(this.playerEntity.username + " broke chest at " + x + ", " + y + ", " + z); 336 | logger.info(this.playerEntity.username + " broke chest at " + x + ", " + y + ", " + z); 337 | } 338 | if (!((world.getBlock(x, y, z) == Block.signPostPlanksOak || world.getBlock(x, y, z) == Block.signWallPlanksOak) && this.playerEntity.isSneaking())) { 339 | this.playerEntity.playerNetServerHandler.sendPacket(new Packet53BlockChange(x, y, z, world)); 340 | } else { 341 | this.mcServer.playerList.sendChatMessageToPlayer(this.playerEntity.username, "Replace the sign to rewrite some lines, or don't."); 342 | } 343 | } 344 | 345 | world.field_819_z = false; 346 | } 347 | } 348 | } 349 | } 350 | -------------------------------------------------------------------------------- /src/main/java/lusiiplugin/LusiiPlugin.java: -------------------------------------------------------------------------------- 1 | package lusiiplugin; 2 | 3 | import lusiiplugin.mixin.EntityPlayerMPMixin; 4 | import lusiiplugin.utils.*; 5 | import net.fabricmc.api.ModInitializer; 6 | import net.minecraft.core.block.Block; 7 | import net.minecraft.core.block.BlockPortal; 8 | import net.minecraft.core.block.entity.TileEntity; 9 | import net.minecraft.core.entity.player.EntityPlayer; 10 | import net.minecraft.core.net.packet.Packet20NamedEntitySpawn; 11 | import net.minecraft.core.net.packet.Packet51MapChunk; 12 | import net.minecraft.core.net.packet.Packet9Respawn; 13 | import net.minecraft.server.MinecraftServer; 14 | import net.minecraft.server.entity.player.EntityPlayerMP; 15 | import net.minecraft.server.net.handler.NetServerHandler; 16 | import net.minecraft.server.world.WorldServer; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import turniplabs.halplibe.util.GameStartEntrypoint; 20 | import turniplabs.halplibe.util.RecipeEntrypoint; 21 | import turniplabs.halplibe.util.TomlConfigHandler; 22 | import turniplabs.halplibe.util.toml.Toml; 23 | 24 | import java.io.*; 25 | import java.nio.file.Files; 26 | import java.nio.file.Path; 27 | import java.nio.file.Paths; 28 | import java.util.*; 29 | 30 | public class LusiiPlugin implements ModInitializer, GameStartEntrypoint, RecipeEntrypoint { 31 | public static final String MOD_ID = "betterthanvanilla"; 32 | public static final String SAVE_DIR = "lusiibtv"; 33 | public static final String CFG_DIR = "config"; 34 | public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID); 35 | public static final TomlConfigHandler CONFIG; 36 | public static boolean enableSkyDimensionPortal; 37 | public static int NickLength; 38 | public static boolean enableAntiTrampleFence; 39 | public static double deathCost; 40 | public static boolean RTPCommand; 41 | public static int RTPCost; 42 | public static boolean TPACommand; 43 | public static int TPACost; 44 | public static int TPTimeout; 45 | public static boolean BackCommand; 46 | public static boolean disableTrample; 47 | public static int DisableTNTOverworld; 48 | public static int DisableTNTNether; 49 | public static int DisableTNTSky; 50 | public static int addedTicksCatchable; 51 | public static int maxHomes; 52 | public static boolean disableBedExplosion; 53 | public static boolean headSit; 54 | public static boolean colourChat; 55 | public static boolean greenText; 56 | public static boolean gamemodeAll; 57 | public static boolean spawnCommand; 58 | public static boolean giveCommand; 59 | public static boolean homeCommand; 60 | public static boolean staticFire; 61 | public static boolean clearCommand; 62 | public static boolean craftCommand; 63 | 64 | static { 65 | Toml toml = new Toml(); 66 | toml.addCategory("WorldUtils"); 67 | toml.addEntry("WorldUtils.StaticFire", "Static fire (Fire does not spread, and does not delete blocks).", false); 68 | toml.addEntry("WorldUtils.DisableBedExplosions", "Remove explosions from beds in non-respawn dimensions.", false); 69 | toml.addEntry("WorldUtils.EnableSkyDimensionPortals", "Enable making portals to the Sky Dimension", false); 70 | toml.addEntry("WorldUtils.AddTicksCatchableFishing", "Adds time in ticks to the window of time that you can catch fish, very useful for players with higher ping. 10-15 is recommended. 0 to disable, -40 or lower to make fishing impossible.",10); 71 | toml.addEntry("WorldUtils.DisableTNTOverworld", "Disable TNT in the overworld after y level. 0 = no TNT", 127); 72 | toml.addEntry("WorldUtils.DisableTNTNether", "Disable TNT in the nether after y level. 0 = no TNT", 256); 73 | toml.addEntry("WorldUtils.DisableTNTSky", "Disable TNT in the sky dimension after y level. 0 = no TNT", 256); 74 | toml.addEntry("WorldUtils.EnableAntiTrampleFences", "Re-enable farmland trample prevention by putting fences under them", false); 75 | toml.addEntry("WorldUtils.DisableTrample", "Completely disables trampling crops", false); 76 | toml.addEntry("WorldUtils.DeathCost", "Points taken upon death, will be multiplied with this number.", 0.95); 77 | toml.addCategory("Commands"); 78 | toml.addEntry("Commands.Give", "Let non-opped players use /give.", false); 79 | toml.addEntry("Commands.Home", "Let non-opped players use /home commands.", true); 80 | toml.addEntry("Commands.HomeLimit","Max amount of homes a player may have. 0 = infinite", 5); 81 | toml.addEntry("Commands.Spawn", "Let non-opped players use /spawn.", true); 82 | toml.addEntry("Commands.Gamemode", "Let non-opped players use /gamemode.", false); 83 | toml.addEntry("Commands.Clear", "Let non-opped players use /clear.", false); 84 | toml.addEntry("Commands.Craft", "Let non-opped players use /craft", true); 85 | toml.addEntry("Commands.TPA", "Let non-opped players use /tpa", true); 86 | toml.addEntry("Commands.Back", "Let non-opped players use /back", true); 87 | toml.addEntry("Commands.TPACost","Amount of points that TPA will take on use. 0 to disable (Not recommended, easily spammable)", 100); 88 | toml.addEntry("Commands.TPTimeout", "Number of seconds between uses of /tpa, /rtp, and /home", 15); 89 | toml.addEntry("Commands.RTP", "Let players use /RTP", true); 90 | toml.addEntry("Commands.RTPCost","Amount of points that RTP will take on use. 0 to disable (Not recommended, easily spammable)", 1000); 91 | toml.addEntry("Commands.NickLength","Nickname length limit, default = 16", 16); 92 | toml.addCategory("PlayerUtils"); 93 | toml.addEntry("PlayerUtils.headSit", "Allows players to sit on eachothers' heads when holding nothing in their hand.", false); 94 | toml.addEntry("PlayerUtils.colourChat", "Allows players to use $$ as colour code for colourful chatting, obfuscation is disabled.", true); 95 | toml.addEntry("PlayerUtils.greenText", "Allows players to turn their text green by putting '>' at the start of their messages.", true); 96 | toml.addCategory("ServerUtils"); 97 | toml.addEntry("ServerUtils.MOTD", "Message of the day, shows up in server list. Don't be inappropriate!", "§5§lJoin us!"); 98 | 99 | CONFIG = new TomlConfigHandler(MOD_ID, toml); 100 | 101 | staticFire = CONFIG.getBoolean("WorldUtils.StaticFire"); 102 | disableBedExplosion = CONFIG.getBoolean("WorldUtils.DisableBedExplosions"); 103 | enableSkyDimensionPortal = CONFIG.getBoolean("WorldUtils.EnableSkyDimensionPortals"); 104 | addedTicksCatchable = CONFIG.getInt("WorldUtils.AddTicksCatchableFishing"); 105 | DisableTNTOverworld = CONFIG.getInt("WorldUtils.DisableTNTOverworld"); 106 | DisableTNTNether = CONFIG.getInt("WorldUtils.DisableTNTNether"); 107 | DisableTNTSky = CONFIG.getInt("WorldUtils.DisableTNTSky"); 108 | enableAntiTrampleFence = CONFIG.getBoolean("WorldUtils.EnableAntiTrampleFences"); 109 | disableTrample = CONFIG.getBoolean("WorldUtils.DisableTrample"); 110 | deathCost = CONFIG.getDouble("WorldUtils.DeathCost"); 111 | giveCommand = CONFIG.getBoolean("Commands.Give"); 112 | homeCommand = CONFIG.getBoolean("Commands.Home"); 113 | maxHomes = CONFIG.getInt("Commands.HomeLimit"); 114 | spawnCommand = CONFIG.getBoolean("Commands.Spawn"); 115 | gamemodeAll = CONFIG.getBoolean("Commands.Gamemode"); 116 | clearCommand = CONFIG.getBoolean("Commands.Clear"); 117 | craftCommand = CONFIG.getBoolean("Commands.Craft"); 118 | RTPCommand = CONFIG.getBoolean("Commands.RTP"); 119 | RTPCost = CONFIG.getInt("Commands.RTPCost"); 120 | BackCommand = CONFIG.getBoolean("Commands.Back"); 121 | TPACommand = CONFIG.getBoolean("Commands.TPA"); 122 | TPACost = CONFIG.getInt("Commands.TPACost"); 123 | TPTimeout = CONFIG.getInt("Commands.TPTimeout"); 124 | NickLength = CONFIG.getInt("Commands.NickLength"); 125 | headSit = CONFIG.getBoolean("PlayerUtils.headSit"); 126 | colourChat = CONFIG.getBoolean("PlayerUtils.colourChat"); 127 | greenText = CONFIG.getBoolean("PlayerUtils.greenText"); 128 | MOTD = CONFIG.getString("ServerUtils.MOTD"); 129 | } 130 | 131 | public static String MOTD; 132 | public static ConfigBuilder info; 133 | public static ConfigBuilder rules; 134 | public static final Set vanished = new HashSet<>(); 135 | public static File vanishedFile; 136 | 137 | @Override 138 | public void onInitialize() { 139 | if (enableSkyDimensionPortal) { 140 | ((BlockPortal) Block.portalParadise).portalTriggerId = Block.fluidWaterFlowing.id; 141 | } 142 | 143 | System.out.println(); 144 | System.out.println("Better than Vanilla loading."); 145 | System.out.println(); 146 | 147 | Path oldInfoFile = Paths.get(CFG_DIR) 148 | .resolve("BetterThanVanillaInfo.txt"); 149 | Path newInfoFile = Paths.get(CFG_DIR) 150 | .resolve("BTVInfo.txt"); 151 | 152 | if (Files.exists(oldInfoFile) && !Files.exists(newInfoFile)) { 153 | try { 154 | Files.move(oldInfoFile, newInfoFile); 155 | } catch (IOException e) { 156 | System.out.println("Could not migrate info file!"); 157 | System.out.println("Generating new from default"); 158 | } 159 | } 160 | 161 | initInfo(); 162 | initRules(); 163 | 164 | Path saveDirPath = Paths.get(SAVE_DIR); 165 | if (!Files.exists(saveDirPath)) { 166 | try { 167 | Files.createDirectories(saveDirPath); 168 | System.out.println("Created /" + saveDirPath +"/ for BTV data files"); 169 | } catch (IOException e) { 170 | System.out.println("Could not create save directory: " + SAVE_DIR); 171 | return; // Exit if the directory cannot be created 172 | } 173 | } 174 | 175 | new PlayerHomesManager(); 176 | System.out.println(); 177 | System.out.println("Better than Vanilla initialized."); 178 | System.out.println(); 179 | } 180 | 181 | public static void initInfo() { 182 | info = new ConfigBuilder("BTVInfo", 183 | Arrays.asList( 184 | "Thanks for installing Better than Vanilla!", 185 | "this is an automatically generated message", 186 | "and you may customize it in the config folder!", 187 | "/// ----------------==================== INFO ===================-----------------", 188 | "///", 189 | "/// - You are able to add more pages to info and rules by following this format", 190 | "/// Example: /info 2 = BTVInfo2.txt", 191 | "/// - These files update live so be mindful of any changes you save to the disk", 192 | "///", 193 | "/// ----------------=================== SYNTAX ==================-----------------", 194 | "///", 195 | "/// - Lines staring with '///' are a comment and are not displayed to the user. ", 196 | "///", 197 | "/// - Use html like tags for formatting", 198 | "/// Example: BOLD RED normal text", 199 | "/// - You can escape the '<' and '>' symbols with a '\\'", 200 | "/// Example: \\", 201 | "///", 202 | "/// Formatting tags: ", 203 | "/// +-----------------------------------------------+", 204 | "/// | white | gray | grey | silver | black |", 205 | "/// |---------+---------+--------+--------+---------|", 206 | "/// | red | orange | yellow | green | blue |", 207 | "/// |---------+---------+--------+--------+---------|", 208 | "/// | magenta | brown | cyan | lime | aqua |", 209 | "/// |---------+---------+--------+--------+---------+", 210 | "/// | i = italics | purple | pink |", 211 | "/// |-------------------+--------+--------|", 212 | "/// | u = underline | b = bold |", 213 | "/// |-------------------+-----------------|", 214 | "/// | r / reset = reset | s = strike |", 215 | "/// |-------------------+-----------------+", 216 | "/// | o = obfuscate |", 217 | "/// +-------------------+" 218 | ), 219 | true 220 | ); 221 | } 222 | 223 | public static void initRules() { 224 | rules = new ConfigBuilder("BTVRules", 225 | Arrays.asList( 226 | "Basic rules:", 227 | " No cheating", 228 | " No harassing", 229 | " No minecraft youtuber shenanigans", 230 | "/// -----------------=================== INFO ===================-----------------", 231 | "/// ", 232 | "///", 233 | "/// - You are able to add more pages to info and rules by following this format", 234 | "/// Example: /rules 2 = BTVRules2.txt", 235 | "/// - These files update live so be mindful of any changes you save to the disk", 236 | "///", 237 | "/// -----------------================== SYNTAX ==================-----------------", 238 | "///", 239 | "/// - Lines staring with '///' are a comment and are not displayed to the user. ", 240 | "///", 241 | "/// - Use html like tags for formatting", 242 | "/// Example: BOLD RED normal text", 243 | "/// - You can escape the '<' and '>' symbols with a '\\'", 244 | "/// Example: \\", 245 | "///", 246 | "/// Formatting tags: ", 247 | "/// +-----------------------------------------------+", 248 | "/// | white | gray | grey | silver | black |", 249 | "/// |---------+---------+--------+--------+---------|", 250 | "/// | red | orange | yellow | green | blue |", 251 | "/// |---------+---------+--------+--------+---------|", 252 | "/// | magenta | brown | cyan | lime | aqua |", 253 | "/// |---------+---------+--------+--------+---------+", 254 | "/// | i = italics | purple | pink |", 255 | "/// |-------------------+--------+--------|", 256 | "/// | u = underline | b = bold |", 257 | "/// |-------------------+-----------------|", 258 | "/// | r / reset = reset | s = strike |", 259 | "/// |-------------------+-----------------+", 260 | "/// | o = obfuscate |", 261 | "/// +-------------------+" 262 | ), 263 | true 264 | ); 265 | } 266 | 267 | public static boolean teleport(EntityPlayer p, double x, double y, double z, int dimension) { 268 | return teleport(p, new WorldPosition(x, y, z, dimension)); 269 | } 270 | 271 | public static boolean teleport(EntityPlayer startPlayer, EntityPlayer endPlayer) { 272 | if (startPlayer.isPassenger() || endPlayer.isPassenger()) { 273 | startPlayer.addChatMessage("§4You cannot teleport as, or to, a passenger!"); 274 | endPlayer.addChatMessage("§4You cannot teleport as, or to, a passenger!"); 275 | return false; 276 | } 277 | NetServerHandler startHandle = ((EntityPlayerMP) startPlayer).playerNetServerHandler; 278 | NetServerHandler endHandle = ((EntityPlayerMP) endPlayer).playerNetServerHandler; 279 | double x = endPlayer.x; 280 | double y = endPlayer.y; 281 | double z = endPlayer.z; 282 | float xr = endPlayer.xRot; 283 | float yr = endPlayer.yRot; 284 | if (startPlayer.dimension != endPlayer.dimension) { 285 | EntityPlayerMP mp = (EntityPlayerMP) startPlayer; 286 | MinecraftServer.getInstance().playerList.sendPlayerToOtherDimension(mp, endPlayer.dimension); 287 | startHandle.sendPacket(new Packet9Respawn((byte) endPlayer.dimension, (byte) 0)); 288 | } 289 | startHandle.teleportAndRotate(x, y, z, yr, xr); 290 | startPlayer.moveTo(x, y, z, yr, xr); 291 | // Show the teleported player to the accepting player instantly 292 | // instead of waiting on the server to send it 293 | endHandle.sendPacket(new Packet20NamedEntitySpawn(startPlayer)); 294 | return true; 295 | } 296 | 297 | public static boolean teleport(EntityPlayer p, WorldPosition h) { 298 | EntityPlayerMP mp = (EntityPlayerMP) p; 299 | NetServerHandler s = mp.playerNetServerHandler; 300 | if (p.isPassenger()) { 301 | p.addChatMessage("§4You can't teleport while you're a passenger."); 302 | return false; 303 | } 304 | if (p.dimension != h.dim) { 305 | MinecraftServer.getInstance().playerList.sendPlayerToOtherDimension(mp, h.dim); 306 | s.sendPacket(new Packet9Respawn((byte) h.dim, (byte) 0)); 307 | } 308 | 309 | MinecraftServer server = MinecraftServer.getInstance(); 310 | WorldServer world = server.getDimensionWorld(h.dim); 311 | int chunkCoordX = (int)h.x >> 4; 312 | int chunkCoordZ = (int)h.z >> 4; 313 | int chunkX = chunkCoordX * 16; 314 | int chunkZ = chunkCoordZ * 16; 315 | 316 | world.getChunkProvider().prepareChunk(chunkCoordX, chunkCoordZ); 317 | s.sendPacket(new Packet51MapChunk(chunkX, 0, chunkZ, 16, 256, 16, world)); 318 | 319 | List list = world.getTileEntityList(chunkX, 0, chunkZ, chunkX + 16, 256, chunkZ + 16); 320 | for (TileEntity te : list) { 321 | ((EntityPlayerMPMixin.Interface) mp).getTEInfo(te); 322 | } 323 | 324 | s.teleportAndRotate(h.x, h.y, h.z, p.yRot, p.xRot); 325 | p.moveTo(h.x, h.y, h.z, p.yRot, p.xRot); 326 | return true; 327 | } 328 | 329 | public static void vanishPlayer(String s) { 330 | vanished.add(s.toLowerCase()); 331 | writeVanishedPlayers(); 332 | } 333 | 334 | public static void unvanishPlayer(String s) { 335 | vanished.remove(s.toLowerCase()); 336 | writeVanishedPlayers(); 337 | } 338 | public static List readVanishedFileLines() throws IOException { 339 | return Files.readAllLines(vanishedFile.toPath()); 340 | } 341 | 342 | private static void writeVanishedPlayers() { 343 | try { 344 | PrintWriter printwriter = new PrintWriter(new FileWriter(vanishedFile, false)); 345 | Iterator iterator = vanished.iterator(); 346 | 347 | while(iterator.hasNext()) { 348 | String s = (String)iterator.next(); 349 | printwriter.println(s); 350 | } 351 | 352 | printwriter.close(); 353 | } catch (Exception var4) { 354 | LOGGER.warn("Failed to save ban list: " + var4); 355 | } 356 | 357 | } 358 | 359 | @Override 360 | public void beforeGameStart() { 361 | } 362 | 363 | @Override 364 | public void afterGameStart() { 365 | MinecraftServer mcs = MinecraftServer.getInstance(); 366 | mcs.motd = MOTD; 367 | } 368 | @Override 369 | public void onRecipesReady() { 370 | } 371 | } 372 | --------------------------------------------------------------------------------