Compare commits

...

53 Commits

Author SHA1 Message Date
OmerBenGera 7657949441 Merge branch 'dev' 2024-05-11 20:44:55 +03:00
OmerBenGera 2bcc77a115 [CI SKIP] Updated README 2024-05-11 20:44:45 +03:00
OmerBenGera 60d68b7b80 Merge branch 'dev'
# Conflicts:
#	build.gradle
2024-05-11 20:37:46 +03:00
OmerBenGera 9a0c852b35 Version 2024.1 2024-05-11 20:31:33 +03:00
OmerBenGera 6ec87b736f Updated ReflectionUtils 2024-05-11 19:54:42 +03:00
OmerBenGera adcbffe853 Adds support to 1.20.6 2024-05-10 15:07:31 +03:00
OmerBenGera ed6932b903 Fix missing dependency to the copyAPI task 2024-04-29 20:19:34 +03:00
OmerBenGera a9c1541013 Fix clean task running before copyAPI 2024-04-29 20:16:39 +03:00
Ome_R 3d021891a8
Support to 1.20.5 (#92) 2024-04-29 19:50:42 +03:00
OmerBenGera a07499aa66 Fixed a NPE when placing a chunk loader (#77) 2024-03-30 16:41:26 +03:00
OmerBenGera 901f630d89 Cache player names for holograms of loaders (#88) 2024-03-15 11:55:05 +02:00
OmerBenGera 2b05c098d5 Potential fix for database queue not shutting down properly (#85) 2024-03-01 14:27:01 +02:00
OmerBenGera dcd20b0245 [CI SKIP] Added sources to API jar 2024-03-01 14:19:44 +02:00
OmerBenGera 6d1adf07cc Adds support to EpicSpawners 8 2024-02-01 17:59:54 +02:00
OmerBenGera acafffd6a8 [CI SKIP] Moved some hook modules 2024-02-01 17:55:15 +02:00
OmerBenGera 3bd4b37958 [CI SKIP] Add a way to distinguish between dev builds and stable builds from the CI 2024-01-20 13:31:14 +02:00
OmerBenGera a131f95a20 [CI SKIP] Add a way to distinguish between dev builds and stable builds from the CI 2024-01-19 18:37:59 +02:00
OmerBenGera aded0e8b06 Adds support to 1.20.4 2023-12-16 17:34:58 +02:00
OmerBenGera b5f80816c3 Fixed errors on Spigot servers due to missing methods 2023-11-24 20:58:52 +02:00
OmerBenGera 9f65c62a0c Fixed the plugin not loading on regular Spigot servers 2023-11-24 20:58:39 +02:00
OmerBenGera eddaf54c99 Fixed circular plugin loading errors on Paper servers 2023-10-05 15:31:33 +03:00
OmerBenGera 6ba614dab1 Version 2023.3 2023-09-30 21:28:14 +03:00
OmerBenGera 264666d974 Added support to 1.20.2 2023-09-25 21:55:41 +03:00
OmerBenGera 7396fd7b41 [CI SKIP] Fixed incorrect module names 2023-09-23 14:24:43 +03:00
OmerBenGera 6a23928c55 [CI SKIP] Renamed NMS packages 2023-09-22 16:03:55 +03:00
OmerBenGera 6a88e4ac18 Removed support for 1.19 that are not recent 2023-09-22 15:57:25 +03:00
OmerBenGera c64e6b2342 Version 2023.2 2023-06-24 21:42:22 +03:00
OmerBenGera 740b24b9e9 [CI SKIP] Added compile_v1_20 to the gradle properties 2023-06-24 21:42:12 +03:00
OmerBenGera d850a9bdf4 Added support to 1.20.1 2023-06-17 13:49:39 +03:00
OmerBenGera ddb36f8d28 Fixed NPE errors when loading chunk loaders after deleting player data 2023-04-11 20:54:16 +03:00
OmerBenGera 7da9f07005 Fixed errors in Spigot 1.19.4 2023-04-11 20:53:00 +03:00
OmerBenGera b8400695ba Version 2023.1 2023-03-25 17:31:59 +03:00
OmerBenGera a137e258b7 Removed support for 1.18.1 2023-03-25 16:26:55 +03:00
OmerBenGera 1d3df3e171 [CI SKIP] Grouped all submodules 2023-03-25 12:53:58 +03:00
OmerBenGera 5efb72564e Added support for 1.19.4 2023-03-17 16:01:20 +02:00
OmerBenGera 2df28a1bab Fixed give command only giving 1 loader when specifing custom amount and time (#65) 2023-01-27 08:36:05 +02:00
OmerBenGera 22d8f0d1fb Fixed chunk loaders can be placed next to each other and overlap chunks (#64) 2023-01-13 16:48:36 +02:00
OmerBenGera 6e0d1fa310 Changed all plugin dependencies to another repository 2022-12-10 16:31:43 +02:00
OmerBenGera 5ae2876a61 Added support for 1.19.3 2022-12-10 14:14:10 +02:00
OmerBenGera a891a18ee9 Fixed a NPE when placing chunk loaders in 1.8/1.12 (#61) 2022-10-08 15:45:44 +03:00
OmerBenGera db2b49fbde Fixed loader npcs can get advancements/achievements (#63) 2022-10-08 15:35:10 +03:00
OmerBenGera 19e383ad0b Version 2022.7 2022-09-17 20:41:51 +03:00
OmerBenGera cad5d1ada5 Fixed incorrect detection of non-supported nms versions 2022-09-17 12:33:35 +03:00
OmerBenGera 3fd922a027 Removed obfuscated nms in favor of mojang-mappings jars in 1.17 2022-09-16 19:49:31 +03:00
OmerBenGera 4d91255bac Removed obfuscated nms in favor of mojang-mappings jars 2022-09-10 21:32:48 +03:00
OmerBenGera 0094475513 Changed it so when failing to retrieve mappings for the server the plugin doesn't fail to load 2022-09-03 09:33:46 +03:00
OmerBenGera 67af75f37a [CI SKIP] Changed remaps checker to be an external dependency rather than classes in code 2022-08-13 13:37:28 +03:00
OmerBenGera a76ed53ee7 [CI SKIP] Added debug messages check when building project 2022-08-12 14:16:39 +03:00
OmerBenGera 4dd84d53be Fixed support for 1.19 (Untested) 2022-08-12 13:51:58 +03:00
OmerBenGera 4ee9bc0960 Merge branch 'master' into dev 2022-08-12 13:36:25 +03:00
OmerBenGera c0829d8e19 Changed mappings checking to be done against a remote server instead of hardcoded values 2022-08-06 12:01:31 +03:00
OmerBenGera 723558145c Fixed the server not making entities ticking in the loaded chunks of the loaders 2022-08-05 23:50:59 +03:00
OmerBenGera a13e760e8b Fixed incorrect parameters error when new loaders are created 2022-08-05 23:40:08 +03:00
160 changed files with 5979 additions and 5705 deletions

View File

@ -1,3 +1,11 @@
plugins {
id 'maven-publish'
}
java {
withSourcesJar()
}
group 'API'
dependencies {
@ -16,8 +24,8 @@ publishing {
}
repositories {
String mavenUsername = System.getenv('mavenUsername') == null ? project.mavenUsername : System.getenv('mavenUsername');
String mavenPassword = System.getenv('mavenPassword') == null ? project.mavenUsername : System.getenv('mavenPassword');
String mavenUsername = System.getenv('mavenUsername');
String mavenPassword = System.getenv('mavenPassword');
if (mavenUsername != null && mavenPassword != null) {
maven {

View File

@ -1,10 +1,10 @@
group 'Hook_EpicSpawners6'
group 'Hooks:EpicSpawners6'
dependencies {
compileOnly "com.songoda:EpicSpawners-6:latest"
compileOnly "com.songoda:EpicSpawners:6.0.6"
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('hook.compile_epicspawners6') &&

View File

@ -1,10 +1,10 @@
group 'Hook_EpicSpawners6'
group 'Hooks:EpicSpawners7'
dependencies {
compileOnly "com.songoda:EpicSpawners-7:latest"
compileOnly "com.songoda:EpicSpawners:7.0.2"
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('hook.compile_epicspawners7') &&

View File

@ -0,0 +1,13 @@
group 'Hooks:EpicSpawners8'
dependencies {
compileOnly "com.songoda:EpicSpawners:8.1.0"
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly rootProject
}
if (project.hasProperty('hook.compile_epicspawners8') &&
!Boolean.valueOf(project.findProperty("hook.compile_epicspawners8").toString())) {
project.tasks.all { task -> task.enabled = false }
}

View File

@ -0,0 +1,59 @@
package com.bgsoftware.wildloaders.hooks;
import com.bgsoftware.wildloaders.api.hooks.TickableProvider;
import com.craftaro.epicspawners.api.EpicSpawnersApi;
import org.bukkit.Chunk;
import org.bukkit.Location;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public final class TickableProvider_EpicSpawners8 implements TickableProvider {
private final Map<Location, TickDelay> spawnerDelays = new HashMap<>();
@Override
public void tick(Chunk[] chunks) {
if (EpicSpawnersApi.getSpawnerManager() == null)
return;
List<Long> chunkList = Stream.of(chunks).map(chunk -> pair(chunk.getX(), chunk.getZ())).collect(Collectors.toList());
EpicSpawnersApi.getSpawnerManager().getSpawners().stream()
.filter(spawner -> chunkList.contains(pair(spawner.getX() >> 4, spawner.getZ() >> 4)))
.forEach(spawner -> {
Location location = spawner.getLocation();
TickDelay tickDelay = spawnerDelays.get(location);
if (tickDelay == null) {
spawnerDelays.put(location, new TickDelay(spawner.updateDelay()));
return;
}
tickDelay.delay -= 1;
if (tickDelay.delay <= 0) {
spawner.spawn();
spawnerDelays.remove(location);
}
});
}
private long pair(int x, int z) {
return (x & 0xFFFFFFFFL) | (z & 0xFFFFFFFFL) << 32;
}
private static final class TickDelay {
private int delay;
TickDelay(int delay) {
this.delay = delay;
}
}
}

View File

@ -1,10 +1,10 @@
group 'Hook_FactionsUUID'
group 'Hooks:FactionsUUID'
dependencies {
compileOnly "com.drtshock:FactionsUUID:latest"
compileOnly "com.drtshock:Factions:1.6.9.5-U0.6.22-b309"
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('hook.compile_factionsuuid') &&

View File

@ -1,10 +1,10 @@
group 'Hook_FactionsX'
group 'Hooks:FactionsX'
dependencies {
compileOnly "net.prosavage:FactionsX:latest"
compileOnly "net.prosavage:FactionsX:0.4.2"
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('hook.compile_factionsx') &&

View File

@ -1,10 +1,10 @@
group 'Hook_Lands'
group 'Hooks:Lands'
dependencies {
compileOnly 'me.angeschossen:Lands:latest'
compileOnly 'me.angeschossen:Lands:4.5.2.7'
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('hook.compile_lands') &&

View File

@ -1,11 +1,11 @@
group 'Hook_MassiveFactions'
group 'Hooks:MassiveFactions'
dependencies {
compileOnly "com.massivecraft:Factions:latest"
compileOnly "com.massivecraft:MassiveCore:latest"
compileOnly "com.massivecraft:Factions:2.13.6"
compileOnly "com.massivecraft:MassiveCore:2.13.6"
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('hook.compile_massivefactions') &&

View File

@ -1,10 +1,10 @@
group 'Hook_SuperiorSkyblock'
group 'Hooks:SuperiorSkyblock'
dependencies {
compileOnly "com.bgsoftware:SuperiorSkyblockAPI:1.8.2"
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('hook.compile_superiorskyblock') &&

1
Hooks/build.gradle Normal file
View File

@ -0,0 +1 @@
group 'Hooks'

1
NMS/build.gradle Normal file
View File

@ -0,0 +1 @@
group 'NMS'

View File

@ -1,9 +1,9 @@
group 'v1_12_R1'
group 'NMS:v1_12_R1'
dependencies {
compileOnly "org.spigotmc:v1_12_R1:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('nms.compile_v1_12') && !Boolean.valueOf(project.findProperty("nms.compile_v1_12").toString())) {

View File

@ -3,7 +3,11 @@ package com.bgsoftware.wildloaders.nms.v1_12_R1;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.server.v1_12_R1.Advancement;
import net.minecraft.server.v1_12_R1.AdvancementDataPlayer;
import net.minecraft.server.v1_12_R1.AdvancementProgress;
import net.minecraft.server.v1_12_R1.AxisAlignedBB;
import net.minecraft.server.v1_12_R1.BlockPosition;
import net.minecraft.server.v1_12_R1.DamageSource;
import net.minecraft.server.v1_12_R1.EntityPlayer;
import net.minecraft.server.v1_12_R1.EnumGamemode;
@ -28,20 +32,25 @@ import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
import org.bukkit.entity.Player;
import java.io.File;
import java.lang.reflect.Field;
import java.util.UUID;
public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC {
private static final AxisAlignedBB EMPTY_BOUND = new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D);
private final AxisAlignedBB boundingBox;
private final AdvancementDataPlayer advancements;
public ChunkLoaderNPC(Location location, UUID uuid){
public ChunkLoaderNPC(Location location, UUID uuid) {
super(((CraftServer) Bukkit.getServer()).getServer(),
((CraftWorld) location.getWorld()).getHandle(),
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())),
new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle()));
playerConnection = new DummyPlayerConnection(server, this);
this.boundingBox = new AxisAlignedBB(new BlockPosition(location.getX(), location.getY(), location.getZ()));
this.playerConnection = new DummyPlayerConnection(server, this);
this.advancements = new DummyPlayerAdvancements(server, this);
this.playerInteractManager.setGameMode(EnumGamemode.CREATIVE);
fauxSleeping = true;
@ -52,7 +61,7 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
world.players.add(this);
((WorldServer) world).getPlayerChunkMap().addPlayer(this);
super.a(EMPTY_BOUND);
super.a(this.boundingBox);
}
@Override
@ -62,7 +71,9 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
@Override
public AxisAlignedBB getBoundingBox() {
return EMPTY_BOUND;
// boundingBox is null on initialization of the class, which fixes:
// https://github.com/BG-Software-LLC/WildLoaders/issues/61
return this.boundingBox == null ? super.getBoundingBox() : this.boundingBox;
}
@Override
@ -87,7 +98,12 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
return false;
}
public static class DummyNetworkManager extends NetworkManager{
@Override
public AdvancementDataPlayer getAdvancementData() {
return this.advancements;
}
public static class DummyNetworkManager extends NetworkManager {
private static Field channelField;
private static Field socketAddressField;
@ -103,7 +119,7 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
}
}
DummyNetworkManager(){
DummyNetworkManager() {
super(EnumProtocolDirection.SERVERBOUND);
updateFields();
}
@ -172,4 +188,62 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
}
private static class DummyPlayerAdvancements extends AdvancementDataPlayer {
DummyPlayerAdvancements(MinecraftServer server, EntityPlayer entityPlayer) {
super(server, getAdvancementsFile(server, entityPlayer), entityPlayer);
}
private static File getAdvancementsFile(MinecraftServer server, EntityPlayer entityPlayer) {
File advancementsDir = new File(server.getWorldServer(0).getDataManager().getDirectory(), "advancements");
return new File(advancementsDir, entityPlayer.getUniqueID() + ".json");
}
@Override
public void a(EntityPlayer owner) {
// setPlayer
}
@Override
public void a() {
// stopListening
}
@Override
public void b() {
// reload
}
@Override
public void c() {
// save
}
@Override
public boolean grantCriteria(Advancement advancement, String criterionName) {
return false;
}
@Override
public boolean revokeCritera(Advancement advancement, String criterionName) {
return false;
}
@Override
public void b(EntityPlayer player) {
// flushDirty
}
@Override
public void a(Advancement advancement) {
// setSelectedTab
}
@Override
public AdvancementProgress getProgress(Advancement advancement) {
return new AdvancementProgress();
}
}
}

View File

@ -1,9 +1,9 @@
package com.bgsoftware.wildloaders.nms.v1_12_R1;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.wildloaders.WildLoadersPlugin;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_12_R1.loader.TileEntityChunkLoader;
import net.minecraft.server.v1_12_R1.Block;
import net.minecraft.server.v1_12_R1.BlockPosition;
@ -24,27 +24,15 @@ import org.bukkit.craftbukkit.v1_12_R1.util.LongHash;
import java.util.UUID;
@SuppressWarnings({"ConstantConditions", "unused"})
public final class NMSAdapter implements com.bgsoftware.wildloaders.nms.NMSAdapter {
private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin();
public final class NMSAdapterImpl implements NMSAdapter {
private static final ReflectMethod<Void> TILE_ENTITY_LOAD = new ReflectMethod<>(TileEntity.class, "load", NBTTagCompound.class);
@Override
public boolean isMappingsSupported() {
return true;
}
@Override
public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) {
ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack);
NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound();
if (!tagCompound.hasKeyOfType(key, 8))
return def;
return tagCompound.getString(key);
NBTTagCompound tagCompound = nmsItem.getTag();
return tagCompound == null || !tagCompound.hasKeyOfType(key, 8) ? def : tagCompound.getString(key);
}
@Override
@ -52,6 +40,8 @@ public final class NMSAdapter implements com.bgsoftware.wildloaders.nms.NMSAdapt
ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack);
NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound();
assert tagCompound != null;
tagCompound.set(key, new NBTTagString(value));
nmsItem.setTag(tagCompound);
@ -62,12 +52,8 @@ public final class NMSAdapter implements com.bgsoftware.wildloaders.nms.NMSAdapt
@Override
public long getTag(org.bukkit.inventory.ItemStack itemStack, String key, long def) {
ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack);
NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound();
if (!tagCompound.hasKeyOfType(key, 4))
return def;
return tagCompound.getLong(key);
NBTTagCompound tagCompound = nmsItem.getTag();
return tagCompound == null || !tagCompound.hasKeyOfType(key, 4) ? def : tagCompound.getLong(key);
}
@Override
@ -75,6 +61,8 @@ public final class NMSAdapter implements com.bgsoftware.wildloaders.nms.NMSAdapt
ItemStack nmsItem = CraftItemStack.asNMSCopy(itemStack);
NBTTagCompound tagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound();
assert tagCompound != null;
tagCompound.set(key, new NBTTagLong(value));
nmsItem.setTag(tagCompound);
@ -88,6 +76,8 @@ public final class NMSAdapter implements com.bgsoftware.wildloaders.nms.NMSAdapt
NBTTagCompound nbtTagCompound = nmsItem.hasTag() ? nmsItem.getTag() : new NBTTagCompound();
assert nbtTagCompound != null;
NBTTagCompound skullOwner = nbtTagCompound.hasKey("SkullOwner") ? nbtTagCompound.getCompound("SkullOwner") : new NBTTagCompound();
NBTTagCompound properties = new NBTTagCompound();

View File

@ -18,6 +18,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class TileEntityChunkLoader extends TileEntity implements ITickable, ITileEntityChunkLoader {
@ -26,6 +27,7 @@ public final class TileEntityChunkLoader extends TileEntity implements ITickable
public final List<EntityHolograms> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
@ -39,6 +41,8 @@ public final class TileEntityChunkLoader extends TileEntity implements ITickable
loaderBlock = world.getType(blockPosition).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
@ -117,7 +121,7 @@ public final class TileEntityChunkLoader extends TileEntity implements ITickable
private void updateName(EntityHolograms hologram, String line) {
hologram.setHologramName(line
.replace("{0}", chunkLoader.getWhoPlaced().getName())
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")

View File

@ -1,9 +1,9 @@
group 'v1_16_R3'
group 'NMS:v1_16_R3'
dependencies {
compileOnly "org.spigotmc:v1_16_R3-Tuinity:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('nms.compile_v1_16') && !Boolean.valueOf(project.findProperty("nms.compile_v1_16").toString())) {

View File

@ -3,7 +3,12 @@ package com.bgsoftware.wildloaders.nms.v1_16_R3;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.server.v1_16_R3.Advancement;
import net.minecraft.server.v1_16_R3.AdvancementDataPlayer;
import net.minecraft.server.v1_16_R3.AdvancementDataWorld;
import net.minecraft.server.v1_16_R3.AdvancementProgress;
import net.minecraft.server.v1_16_R3.AxisAlignedBB;
import net.minecraft.server.v1_16_R3.BlockPosition;
import net.minecraft.server.v1_16_R3.EntityPlayer;
import net.minecraft.server.v1_16_R3.EnumGamemode;
import net.minecraft.server.v1_16_R3.EnumProtocolDirection;
@ -20,19 +25,23 @@ import net.minecraft.server.v1_16_R3.PacketPlayInUpdateSign;
import net.minecraft.server.v1_16_R3.PacketPlayInWindowClick;
import net.minecraft.server.v1_16_R3.PlayerConnection;
import net.minecraft.server.v1_16_R3.PlayerInteractManager;
import net.minecraft.server.v1_16_R3.SavedFile;
import net.minecraft.server.v1_16_R3.WorldServer;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_16_R3.CraftServer;
import org.bukkit.craftbukkit.v1_16_R3.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.lang.reflect.Field;
import java.util.UUID;
public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC {
private static final AxisAlignedBB EMPTY_BOUND = new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D);
private final AxisAlignedBB boundingBox;
private final AdvancementDataPlayer advancements;
private boolean dieCall = false;
@ -42,7 +51,10 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())),
new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle()));
playerConnection = new DummyPlayerConnection(server, this);
this.boundingBox = new AxisAlignedBB(new BlockPosition(location.getX(), location.getY(), location.getZ()));
this.playerConnection = new DummyPlayerConnection(server, this);
this.advancements = new DummyPlayerAdvancements(server, this);
this.playerInteractManager.setGameMode(EnumGamemode.CREATIVE);
clientViewDistance = 1;
@ -54,7 +66,7 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
((WorldServer) world).addPlayerJoin(this);
super.a(EMPTY_BOUND);
super.a(this.boundingBox);
}
@Override
@ -64,7 +76,7 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
@Override
public AxisAlignedBB getBoundingBox() {
return EMPTY_BOUND;
return this.boundingBox;
}
@Override
@ -88,6 +100,11 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
return getBukkitEntity();
}
@Override
public AdvancementDataPlayer getAdvancementData() {
return this.advancements;
}
public static class DummyNetworkManager extends NetworkManager {
private static Field channelField;
@ -173,4 +190,63 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
}
private static class DummyPlayerAdvancements extends AdvancementDataPlayer {
DummyPlayerAdvancements(MinecraftServer server, EntityPlayer entityPlayer) {
super(server.getDataFixer(), server.getPlayerList(), server.getAdvancementData(),
getAdvancementsFile(server, entityPlayer), entityPlayer);
}
private static File getAdvancementsFile(MinecraftServer server, EntityPlayer entityPlayer) {
File advancementsDir = server.a(SavedFile.ADVANCEMENTS).toFile();
return new File(advancementsDir, entityPlayer.getUniqueID() + ".json");
}
@Override
public void a(EntityPlayer owner) {
// setPlayer
}
@Override
public void a() {
// stopListening
}
@Override
public void a(AdvancementDataWorld advancementLoader) {
// reload
}
@Override
public void b() {
// save
}
@Override
public boolean grantCriteria(Advancement advancement, String criterionName) {
return false;
}
@Override
public boolean revokeCritera(Advancement advancement, String criterionName) {
return false;
}
@Override
public void b(EntityPlayer player) {
// flushDirty
}
@Override
public void a(@Nullable Advancement advancement) {
// setSelectedTab
}
@Override
public AdvancementProgress getProgress(Advancement advancement) {
return new AdvancementProgress();
}
}
}

View File

@ -1,8 +1,8 @@
package com.bgsoftware.wildloaders.nms.v1_16_R3;
import com.bgsoftware.wildloaders.WildLoadersPlugin;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_16_R3.loader.TileEntityChunkLoader;
import net.minecraft.server.v1_16_R3.Block;
import net.minecraft.server.v1_16_R3.BlockPosition;
@ -24,15 +24,7 @@ import org.bukkit.craftbukkit.v1_16_R3.inventory.CraftItemStack;
import java.util.UUID;
@SuppressWarnings("unused")
public final class NMSAdapter implements com.bgsoftware.wildloaders.nms.NMSAdapter {
private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin();
@Override
public boolean isMappingsSupported() {
return true;
}
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) {

View File

@ -19,6 +19,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class TileEntityChunkLoader extends TileEntity implements ITickable, ITileEntityChunkLoader {
@ -27,6 +28,7 @@ public final class TileEntityChunkLoader extends TileEntity implements ITickable
public final List<EntityHolograms> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
@ -47,6 +49,8 @@ public final class TileEntityChunkLoader extends TileEntity implements ITickable
} catch (Throwable ignored) {
}
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
@ -130,9 +134,8 @@ public final class TileEntityChunkLoader extends TileEntity implements ITickable
}
private void updateName(EntityHolograms hologram, String line) {
assert chunkLoader.getWhoPlaced().getName() != null;
hologram.setHologramName(line
.replace("{0}", chunkLoader.getWhoPlaced().getName())
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")

36
NMS/v1_17/build.gradle Normal file
View File

@ -0,0 +1,36 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.6.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(16))
}
}
group 'NMS:v1_17'
dependencies {
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.17.1-R0.1-SNAPSHOT")
compileOnly project(":API")
compileOnly rootProject
}
shadowJar {
archiveFileName = "${project.name}-exclude.jar"
}
assemble {
dependsOn(reobfJar)
}
tasks {
reobfJar {
File outputFile = new File(rootProject.archiveFolder, "reobf/${project.name}.jar")
outputJar.set(layout.buildDirectory.file(outputFile.getPath()))
}
}
if (project.hasProperty('nms.compile_v1_17') && !Boolean.valueOf(project.findProperty("nms.compile_v1_17").toString())) {
project.tasks.all { task -> task.enabled = false }
}

View File

@ -1,56 +1,54 @@
package com.bgsoftware.wildloaders.nms.v1_17_R1;
package com.bgsoftware.wildloaders.nms.v1_17;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.chat.IChatBaseComponent;
import net.minecraft.sounds.SoundEffect;
import net.minecraft.world.EnumHand;
import net.minecraft.world.EnumInteractionResult;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EnumItemSlot;
import net.minecraft.world.entity.decoration.EntityArmorStand;
import net.minecraft.world.entity.player.EntityHuman;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.World;
import net.minecraft.world.phys.AxisAlignedBB;
import net.minecraft.world.phys.Vec3D;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftArmorStand;
import org.bukkit.craftbukkit.v1_17_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_17_R1.util.CraftChatMessage;
import javax.annotation.Nullable;
public final class EntityHologram extends ArmorStand implements Hologram {
@SuppressWarnings("unused")
public final class EntityHolograms extends EntityArmorStand implements Hologram {
private static final AxisAlignedBB EMPTY_BOUND = new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D);
private static final AABB EMPTY_BOUND = new AABB(0D, 0D, 0D, 0D, 0D, 0D);
private CraftEntity bukkitEntity;
public EntityHolograms(World world, double x, double y, double z){
super(world, x, y, z);
public EntityHologram(ServerLevel serverLevel, double x, double y, double z) {
super(serverLevel, x, y, z);
setInvisible(true);
setSmall(true);
setArms(false);
setShowArms(false);
setNoGravity(true);
setBasePlate(true);
setNoBasePlate(true);
setMarker(true);
super.collides = false;
super.setCustomNameVisible(true);
super.a(EMPTY_BOUND);
super.setCustomNameVisible(true); // Custom name visible
super.setBoundingBox(EMPTY_BOUND);
}
@Override
public void setHologramName(String name) {
super.setCustomName(CraftChatMessage.fromString(name)[0]);
super.setCustomName(CraftChatMessage.fromStringOrNull(name));
}
@Override
public void removeHologram() {
super.a(Entity.RemovalReason.b);
super.remove(RemovalReason.DISCARDED);
}
@Override
@ -63,8 +61,8 @@ public final class EntityHolograms extends EntityArmorStand implements Hologram
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.z) {
this.z = false;
if (this.onGround) {
this.onGround = false;
}
}
@ -73,40 +71,40 @@ public final class EntityHolograms extends EntityArmorStand implements Hologram
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.z) {
this.z = false;
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void saveData(NBTTagCompound nbttagcompound) {
public void addAdditionalSaveData(CompoundTag compoundTag) {
// Do not save NBT.
}
@Override
public boolean d(NBTTagCompound nbttagcompound) {
public boolean saveAsPassenger(CompoundTag compoundTag) {
// Do not save NBT.
return false;
}
@Override
public NBTTagCompound save(NBTTagCompound nbttagcompound) {
public CompoundTag saveWithoutId(CompoundTag compoundTag) {
// Do not save NBT.
return nbttagcompound;
return compoundTag;
}
@Override
public void load(NBTTagCompound nbttagcompound) {
public void readAdditionalSaveData(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public void loadData(NBTTagCompound nbttagcompound) {
public void load(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public boolean isInvulnerable(DamageSource source) {
public boolean isInvulnerableTo(DamageSource source) {
/*
* The field Entity.invulnerable is private.
* It's only used while saving NBTTags, but since the entity would be killed
@ -116,47 +114,43 @@ public final class EntityHolograms extends EntityArmorStand implements Hologram
}
@Override
public boolean isCollidable() {
public boolean repositionEntityAfterLoad() {
return false;
}
@Override
public void setCustomName(@Nullable IChatBaseComponent ichatbasecomponent) {
public void setCustomName(Component component) {
// Locks the custom name.
}
@Override
public void setCustomNameVisible(boolean flag) {
public void setCustomNameVisible(boolean visible) {
// Locks the custom name.
}
@Override
public EnumInteractionResult a(EntityHuman human, Vec3D vec3d, EnumHand enumhand) {
public InteractionResult interactAt(Player player, Vec3 hitPos, InteractionHand hand) {
// Prevent stand being equipped
return EnumInteractionResult.d;
return InteractionResult.PASS;
}
@Override
public void setSlot(EnumItemSlot enumitemslot, ItemStack itemstack) {
public void setSlot(EquipmentSlot equipmentSlot, ItemStack itemStack, boolean silent) {
// Prevent stand being equipped
}
@Override
public AxisAlignedBB cs() {
public AABB getBoundingBoxForCulling() {
return EMPTY_BOUND;
}
public void forceSetBoundingBox(AxisAlignedBB boundingBox) {
super.a(boundingBox);
}
@Override
public void playSound(SoundEffect soundeffect, float f, float f1) {
public void playSound(SoundEvent soundEvent, float volume, float pitch) {
// Remove sounds.
}
@Override
public void a(Entity.RemovalReason entity_removalreason) {
public void remove(RemovalReason removalReason) {
// Prevent being killed.
}

View File

@ -0,0 +1,172 @@
package com.bgsoftware.wildloaders.nms.v1_17;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_17.loader.ChunkLoaderBlockEntity;
import com.bgsoftware.wildloaders.nms.v1_17.npc.ChunkLoaderNPCWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_17_R1.CraftChunk;
import org.bukkit.craftbukkit.v1_17_R1.CraftServer;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_17_R1.inventory.CraftItemStack;
import java.util.UUID;
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 8) ? def : compoundTag.getString(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putString(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public long getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 4) ? def : compoundTag.getLong(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putLong(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack bukkitItem, String texture) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
CompoundTag skullOwner = compoundTag.contains("SkullOwner") ?
compoundTag.getCompound("SkullOwner") : new CompoundTag();
CompoundTag properties = new CompoundTag();
ListTag textures = new ListTag();
CompoundTag signature = new CompoundTag();
signature.putString("Value", texture);
textures.add(signature);
properties.put("textures", textures);
skullOwner.put("Properties", properties);
skullOwner.putString("Id", UUID.randomUUID().toString());
compoundTag.put("SkullOwner", skullOwner);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC createNPC(Location location, UUID uuid) {
return new ChunkLoaderNPCWrapper(((CraftServer) Bukkit.getServer()).getServer(), location, uuid);
}
@Override
public ITileEntityChunkLoader createLoader(ChunkLoader chunkLoader) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot create loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ());
ChunkLoaderBlockEntity ChunkLoaderBlockEntity = new ChunkLoaderBlockEntity(chunkLoader, serverLevel, blockPos);
serverLevel.addBlockEntityTicker(ChunkLoaderBlockEntity.getTicker());
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = ((CraftChunk) bukkitChunk).getHandle();
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = -1;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, true);
}
return ChunkLoaderBlockEntity;
}
@Override
public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ());
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
ChunkLoaderBlockEntity chunkLoaderBlockEntity = ChunkLoaderBlockEntity.chunkLoaderBlockEntityMap.remove(chunkPosLong);
if (chunkLoaderBlockEntity != null) {
chunkLoaderBlockEntity.holograms.forEach(EntityHologram::removeHologram);
chunkLoaderBlockEntity.removed = true;
}
if (spawnParticle)
serverLevel.levelEvent(null, 2001, blockPos, Block.getId(serverLevel.getBlockState(blockPos)));
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = ((CraftChunk) bukkitChunk).getHandle();
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = 16;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, false);
}
}
@Override
public void updateSpawner(Location location, boolean reset) {
World bukkitWorld = location.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(location.getX(), location.getY(), location.getZ());
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos);
if (blockEntity instanceof SpawnerBlockEntity spawnerBlockEntity)
spawnerBlockEntity.getSpawner().requiredPlayerRange = reset ? 16 : -1;
}
}

View File

@ -1,17 +1,16 @@
package com.bgsoftware.wildloaders.nms.v1_17_R1.loader;
package com.bgsoftware.wildloaders.nms.v1_17.loader;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.loaders.WChunkLoader;
import com.bgsoftware.wildloaders.nms.v1_17_R1.EntityHolograms;
import com.bgsoftware.wildloaders.nms.v1_17_R1.NMSAdapter;
import net.minecraft.core.BlockPosition;
import net.minecraft.world.level.ChunkCoordIntPair;
import net.minecraft.world.level.World;
import com.bgsoftware.wildloaders.nms.v1_17.EntityHologram;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.TileEntity;
import net.minecraft.world.level.block.entity.TileEntityTypes;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.ArrayList;
import java.util.Collection;
@ -19,29 +18,37 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class TileEntityChunkLoader extends TileEntity implements ITileEntityChunkLoader {
public final class ChunkLoaderBlockEntity extends BlockEntity implements ITileEntityChunkLoader {
public static final Map<Long, TileEntityChunkLoader> tileEntityChunkLoaderMap = new HashMap<>();
public static final Map<Long, ChunkLoaderBlockEntity> chunkLoaderBlockEntityMap = new HashMap<>();
public final List<EntityHolograms> holograms = new ArrayList<>();
public final List<EntityHologram> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
public final TileEntityChunkLoaderTicker ticker;
private final ChunkLoaderBlockEntityTicker ticker;
private final ServerLevel serverLevel;
private final BlockPos blockPos;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
public boolean removed = false;
public TileEntityChunkLoader(ChunkLoader chunkLoader, World world, BlockPosition blockPosition) {
super(TileEntityTypes.v, blockPosition, world.getType(blockPosition));
public ChunkLoaderBlockEntity(ChunkLoader chunkLoader, ServerLevel serverLevel, BlockPos blockPos) {
super(BlockEntityType.COMMAND_BLOCK, blockPos, serverLevel.getBlockState(blockPos));
this.chunkLoader = (WChunkLoader) chunkLoader;
this.ticker = new TileEntityChunkLoaderTicker(this);
this.ticker = new ChunkLoaderBlockEntityTicker(this);
this.blockPos = blockPos;
this.serverLevel = serverLevel;
setWorld(world);
setLevel(serverLevel);
loaderBlock = world.getType(blockPosition).getBlock();
loaderBlock = serverLevel.getBlockState(blockPos).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
@ -58,16 +65,16 @@ public final class TileEntityChunkLoader extends TileEntity implements ITileEnti
secondsAmount = (short) timeLeft;
}
tileEntityChunkLoaderMap.put(ChunkCoordIntPair.pair(blockPosition.getX() >> 4, blockPosition.getZ() >> 4), this);
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
chunkLoaderBlockEntityMap.put(chunkPosLong, this);
List<String> hologramLines = this.chunkLoader.getHologramLines();
double currentY = getPosition().getY() + 1;
double currentY = blockPos.getY() + 1;
for (int i = hologramLines.size(); i > 0; i--) {
EntityHolograms hologram = new EntityHolograms(world,
getPosition().getX() + 0.5, currentY, getPosition().getZ() + 0.5);
EntityHologram hologram = new EntityHologram(serverLevel, blockPos.getX() + 0.5, currentY, blockPos.getZ() + 0.5);
updateName(hologram, hologramLines.get(i - 1));
world.addEntity(hologram);
serverLevel.addFreshEntity(hologram);
currentY += 0.23;
holograms.add(hologram);
}
@ -79,8 +86,7 @@ public final class TileEntityChunkLoader extends TileEntity implements ITileEnti
currentTick = 0;
assert this.n != null;
if (chunkLoader.isNotActive() || this.n.getType(getPosition()).getBlock() != loaderBlock) {
if (chunkLoader.isNotActive() || this.serverLevel.getBlockState(this.blockPos).getBlock() != loaderBlock) {
chunkLoader.remove();
return;
}
@ -92,7 +98,7 @@ public final class TileEntityChunkLoader extends TileEntity implements ITileEnti
int hologramsAmount = holograms.size();
for (int i = hologramsAmount; i > 0; i--) {
EntityHolograms hologram = holograms.get(hologramsAmount - i);
EntityHologram hologram = holograms.get(hologramsAmount - i);
updateName(hologram, hologramLines.get(i - 1));
}
@ -125,10 +131,13 @@ public final class TileEntityChunkLoader extends TileEntity implements ITileEnti
return removed || super.isRemoved();
}
private void updateName(EntityHolograms hologram, String line) {
assert chunkLoader.getWhoPlaced().getName() != null;
public ChunkLoaderBlockEntityTicker getTicker() {
return ticker;
}
private void updateName(EntityHologram hologram, String line) {
hologram.setHologramName(line
.replace("{0}", chunkLoader.getWhoPlaced().getName())
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")

View File

@ -0,0 +1,30 @@
package com.bgsoftware.wildloaders.nms.v1_17.loader;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
public record ChunkLoaderBlockEntityTicker(
ChunkLoaderBlockEntity chunkLoaderBlockEntity) implements TickingBlockEntity {
@Override
public void tick() {
chunkLoaderBlockEntity.tick();
}
@Override
public boolean isRemoved() {
return chunkLoaderBlockEntity.isRemoved();
}
@Override
public BlockPos getPos() {
return chunkLoaderBlockEntity.getBlockPos();
}
@Override
public String getType() {
return BlockEntityType.getKey(chunkLoaderBlockEntity.getType()) + "";
}
}

View File

@ -0,0 +1,38 @@
package com.bgsoftware.wildloaders.nms.v1_17.npc;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import net.minecraft.server.MinecraftServer;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.UUID;
public class ChunkLoaderNPCWrapper implements ChunkLoaderNPC {
private final ChunkLoaderPlayer chunkLoaderPlayer;
public ChunkLoaderNPCWrapper(MinecraftServer minecraftServer, Location location, UUID uuid) {
this.chunkLoaderPlayer = new ChunkLoaderPlayer(minecraftServer, location, uuid);
}
@Override
public UUID getUniqueId() {
return this.chunkLoaderPlayer.getUUID();
}
@Override
public void die() {
this.chunkLoaderPlayer.discard();
}
@Override
public Location getLocation() {
return getPlayer().getLocation();
}
@Override
public Player getPlayer() {
return this.chunkLoaderPlayer.getBukkitEntity();
}
}

View File

@ -0,0 +1,213 @@
package com.bgsoftware.wildloaders.nms.v1_17.npc;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.core.BlockPos;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundChatPacket;
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket;
import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.phys.AABB;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_17_R1.CraftWorld;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.UUID;
public final class ChunkLoaderPlayer extends ServerPlayer {
private static final ReflectMethod<Void> SET_GAMEMODE = new ReflectMethod<>(ServerPlayerGameMode.class,
1, GameType.class, GameType.class);
private final ServerLevel serverLevel;
private final AABB boundingBox;
private final PlayerAdvancements advancements;
private boolean dieCall = false;
public ChunkLoaderPlayer(MinecraftServer minecraftServer, Location location, UUID uuid) {
super(minecraftServer, ((CraftWorld) location.getWorld()).getHandle(),
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())));
this.serverLevel = getLevel();
this.boundingBox = new AABB(new BlockPos(location.getX(), location.getY(), location.getZ()));
this.connection = new DummyServerGamePacketListenerImpl(minecraftServer, this);
this.advancements = new DummyPlayerAdvancements(minecraftServer, this);
SET_GAMEMODE.invoke(this.gameMode, GameType.CREATIVE, null);
clientViewDistance = 1;
fauxSleeping = true;
spawnIn(this.serverLevel);
moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
this.serverLevel.addNewPlayer(this);
super.setBoundingBox(this.boundingBox);
}
@Override
public AABB getBoundingBoxForCulling() {
return this.boundingBox;
}
@Override
public void remove(RemovalReason removalReason) {
if (!dieCall) {
dieCall = true;
this.serverLevel.removePlayerImmediately(this, RemovalReason.UNLOADED_WITH_PLAYER);
dieCall = false;
} else {
super.remove(removalReason);
}
}
@Override
public PlayerAdvancements getAdvancements() {
return advancements;
}
public static class DummyConnection extends Connection {
DummyConnection() {
super(PacketFlow.SERVERBOUND);
this.channel = new DummyChannel();
this.address = null;
}
}
public static class DummyServerGamePacketListenerImpl extends ServerGamePacketListenerImpl {
DummyServerGamePacketListenerImpl(MinecraftServer minecraftServer, ServerPlayer serverPlayer) {
super(minecraftServer, new DummyConnection(), serverPlayer);
}
@Override
public void handleContainerClick(ServerboundContainerClickPacket containerClickPacket) {
// Do nothing.
}
@Override
public void handleMovePlayer(ServerboundMovePlayerPacket movePlayerPacket) {
// Do nothing.
}
@Override
public void handleSignUpdate(ServerboundSignUpdatePacket signUpdatePacket) {
// Do nothing.
}
@Override
public void handlePlayerAction(ServerboundPlayerActionPacket playerActionPacket) {
// Do nothing.
}
@Override
public void handleUseItem(ServerboundUseItemPacket useItemPacket) {
// Do nothing.
}
@Override
public void handleSetCarriedItem(ServerboundSetCarriedItemPacket setCarriedItemPacket) {
// Do nothing.
}
@Override
public void handleChat(ServerboundChatPacket chatPacket) {
// Do nothing.
}
@Override
public void disconnect(String s) {
}
public void send(Packet<?> packet) {
// Do nothing.
}
}
private static class DummyPlayerAdvancements extends PlayerAdvancements {
DummyPlayerAdvancements(MinecraftServer server, ServerPlayer serverPlayer) {
super(server.getFixerUpper(), server.getPlayerList(), server.getAdvancements(),
getAdvancementsFile(server, serverPlayer), serverPlayer);
}
private static File getAdvancementsFile(MinecraftServer server, ServerPlayer serverPlayer) {
File advancementsDir = server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).toFile();
return new File(advancementsDir, serverPlayer.getUUID() + ".json");
}
@Override
public void setPlayer(ServerPlayer owner) {
// Do nothing.
}
@Override
public void stopListening() {
// Do nothing.
}
@Override
public void reload(ServerAdvancementManager advancementLoader) {
// Do nothing.
}
@Override
public void save() {
// Do nothing.
}
@Override
public boolean award(Advancement advancement, String criterionName) {
return false;
}
@Override
public boolean revoke(Advancement advancement, String criterionName) {
return false;
}
@Override
public void flushDirty(ServerPlayer player) {
// Do nothing.
}
@Override
public void setSelectedTab(@Nullable Advancement advancement) {
// Do nothing.
}
@Override
public AdvancementProgress getOrStartProgress(Advancement advancement) {
return new AdvancementProgress();
}
}
}

36
NMS/v1_18/build.gradle Normal file
View File

@ -0,0 +1,36 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.6.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
group 'NMS:v1_18'
dependencies {
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.18.2-R0.1-SNAPSHOT")
compileOnly project(":API")
compileOnly rootProject
}
shadowJar {
archiveFileName = "${project.name}-exclude.jar"
}
assemble {
dependsOn(reobfJar)
}
tasks {
reobfJar {
File outputFile = new File(rootProject.archiveFolder, "reobf/${project.name}.jar")
outputJar.set(layout.buildDirectory.file(outputFile.getPath()))
}
}
if (project.hasProperty('nms.compile_v1_18') && !Boolean.valueOf(project.findProperty("nms.compile_v1_18").toString())) {
project.tasks.all { task -> task.enabled = false }
}

View File

@ -0,0 +1,235 @@
package com.bgsoftware.wildloaders.nms.v1_18;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.core.BlockPos;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundChatPacket;
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket;
import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.phys.AABB;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.UUID;
public final class ChunkLoaderNPCImpl extends ServerPlayer implements ChunkLoaderNPC {
private static final ReflectMethod<Void> SET_GAMEMODE = new ReflectMethod<>(ServerPlayerGameMode.class,
1, GameType.class, GameType.class);
private final ServerLevel serverLevel;
private final AABB boundingBox;
private final PlayerAdvancements advancements;
private boolean dieCall = false;
public ChunkLoaderNPCImpl(MinecraftServer minecraftServer, Location location, UUID uuid) {
super(minecraftServer, ((CraftWorld) location.getWorld()).getHandle(),
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())));
this.serverLevel = getLevel();
this.boundingBox = new AABB(new BlockPos(location.getX(), location.getY(), location.getZ()));
this.connection = new DummyServerGamePacketListenerImpl(minecraftServer, this);
this.advancements = new DummyPlayerAdvancements(minecraftServer, this);
SET_GAMEMODE.invoke(this.gameMode, GameType.CREATIVE, null);
clientViewDistance = 1;
fauxSleeping = true;
spawnIn(this.serverLevel);
moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
this.serverLevel.addNewPlayer(this);
super.setBoundingBox(this.boundingBox);
}
@Override
public UUID getUniqueId() {
return super.getUUID();
}
@Override
public void die() {
discard();
}
@Override
public AABB getBoundingBoxForCulling() {
return this.boundingBox;
}
@Override
public void remove(RemovalReason removalReason) {
if (!dieCall) {
dieCall = true;
this.serverLevel.removePlayerImmediately(this, RemovalReason.UNLOADED_WITH_PLAYER);
dieCall = false;
} else {
super.remove(removalReason);
}
}
@Override
public Location getLocation() {
return getBukkitEntity().getLocation();
}
@Override
public Player getPlayer() {
return getBukkitEntity();
}
@Override
public PlayerAdvancements getAdvancements() {
return advancements;
}
public static class DummyConnection extends Connection {
DummyConnection() {
super(PacketFlow.SERVERBOUND);
this.channel = new DummyChannel();
this.address = null;
}
}
public static class DummyServerGamePacketListenerImpl extends ServerGamePacketListenerImpl {
DummyServerGamePacketListenerImpl(MinecraftServer minecraftServer, ServerPlayer serverPlayer) {
super(minecraftServer, new DummyConnection(), serverPlayer);
}
@Override
public void handleContainerClick(ServerboundContainerClickPacket containerClickPacket) {
// Do nothing.
}
@Override
public void handleMovePlayer(ServerboundMovePlayerPacket movePlayerPacket) {
// Do nothing.
}
@Override
public void handleSignUpdate(ServerboundSignUpdatePacket signUpdatePacket) {
// Do nothing.
}
@Override
public void handlePlayerAction(ServerboundPlayerActionPacket playerActionPacket) {
// Do nothing.
}
@Override
public void handleUseItem(ServerboundUseItemPacket useItemPacket) {
// Do nothing.
}
@Override
public void handleSetCarriedItem(ServerboundSetCarriedItemPacket setCarriedItemPacket) {
// Do nothing.
}
@Override
public void handleChat(ServerboundChatPacket chatPacket) {
// Do nothing.
}
@Override
public void disconnect(String s) {
}
public void send(Packet<?> packet) {
// Do nothing.
}
}
private static class DummyPlayerAdvancements extends PlayerAdvancements {
DummyPlayerAdvancements(MinecraftServer server, ServerPlayer serverPlayer) {
super(server.getFixerUpper(), server.getPlayerList(), server.getAdvancements(),
getAdvancementsFile(server, serverPlayer), serverPlayer);
}
private static File getAdvancementsFile(MinecraftServer server, ServerPlayer serverPlayer) {
File advancementsDir = server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).toFile();
return new File(advancementsDir, serverPlayer.getUUID() + ".json");
}
@Override
public void setPlayer(ServerPlayer owner) {
// Do nothing.
}
@Override
public void stopListening() {
// Do nothing.
}
@Override
public void reload(ServerAdvancementManager advancementLoader) {
// Do nothing.
}
@Override
public void save() {
// Do nothing.
}
@Override
public boolean award(Advancement advancement, String criterionName) {
return false;
}
@Override
public boolean revoke(Advancement advancement, String criterionName) {
return false;
}
@Override
public void flushDirty(ServerPlayer player) {
// Do nothing.
}
@Override
public void setSelectedTab(@Nullable Advancement advancement) {
// Do nothing.
}
@Override
public AdvancementProgress getOrStartProgress(Advancement advancement) {
return new AdvancementProgress();
}
}
}

View File

@ -0,0 +1,165 @@
package com.bgsoftware.wildloaders.nms.v1_18;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftArmorStand;
import org.bukkit.craftbukkit.v1_18_R2.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_18_R2.util.CraftChatMessage;
public final class EntityHologram extends ArmorStand implements Hologram {
private static final AABB EMPTY_BOUND = new AABB(0D, 0D, 0D, 0D, 0D, 0D);
private CraftEntity bukkitEntity;
public EntityHologram(ServerLevel serverLevel, double x, double y, double z) {
super(serverLevel, x, y, z);
setInvisible(true);
setSmall(true);
setShowArms(false);
setNoGravity(true);
setNoBasePlate(true);
setMarker(true);
super.collides = false;
super.setCustomNameVisible(true); // Custom name visible
super.setBoundingBox(EMPTY_BOUND);
}
@Override
public void setHologramName(String name) {
super.setCustomName(CraftChatMessage.fromStringOrNull(name));
}
@Override
public void removeHologram() {
super.remove(RemovalReason.DISCARDED);
}
@Override
public org.bukkit.entity.Entity getEntity() {
return getBukkitEntity();
}
@Override
public void tick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void inactiveTick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void addAdditionalSaveData(CompoundTag compoundTag) {
// Do not save NBT.
}
@Override
public boolean saveAsPassenger(CompoundTag compoundTag) {
// Do not save NBT.
return false;
}
@Override
public CompoundTag saveWithoutId(CompoundTag compoundTag) {
// Do not save NBT.
return compoundTag;
}
@Override
public void readAdditionalSaveData(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public void load(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public boolean isInvulnerableTo(DamageSource source) {
/*
* The field Entity.invulnerable is private.
* It's only used while saving NBTTags, but since the entity would be killed
* on chunk unload, we prefer to override isInvulnerable().
*/
return true;
}
@Override
public boolean repositionEntityAfterLoad() {
return false;
}
@Override
public void setCustomName(Component component) {
// Locks the custom name.
}
@Override
public void setCustomNameVisible(boolean visible) {
// Locks the custom name.
}
@Override
public InteractionResult interactAt(Player player, Vec3 hitPos, InteractionHand hand) {
// Prevent stand being equipped
return InteractionResult.PASS;
}
@Override
public void setItemSlot(EquipmentSlot equipmentSlot, ItemStack itemStack, boolean silent) {
// Prevent stand being equipped
}
@Override
public AABB getBoundingBoxForCulling() {
return EMPTY_BOUND;
}
@Override
public void playSound(SoundEvent soundEvent, float volume, float pitch) {
// Remove sounds.
}
@Override
public void remove(RemovalReason removalReason) {
// Prevent being killed.
}
@Override
public CraftEntity getBukkitEntity() {
if (bukkitEntity == null) {
bukkitEntity = new CraftArmorStand((CraftServer) Bukkit.getServer(), this);
}
return bukkitEntity;
}
}

View File

@ -0,0 +1,171 @@
package com.bgsoftware.wildloaders.nms.v1_18;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_18.loader.ChunkLoaderBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_18_R2.CraftChunk;
import org.bukkit.craftbukkit.v1_18_R2.CraftServer;
import org.bukkit.craftbukkit.v1_18_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_18_R2.inventory.CraftItemStack;
import java.util.UUID;
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 8) ? def : compoundTag.getString(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putString(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public long getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 4) ? def : compoundTag.getLong(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putLong(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack bukkitItem, String texture) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
CompoundTag skullOwner = compoundTag.contains("SkullOwner") ?
compoundTag.getCompound("SkullOwner") : new CompoundTag();
CompoundTag properties = new CompoundTag();
ListTag textures = new ListTag();
CompoundTag signature = new CompoundTag();
signature.putString("Value", texture);
textures.add(signature);
properties.put("textures", textures);
skullOwner.put("Properties", properties);
skullOwner.putString("Id", UUID.randomUUID().toString());
compoundTag.put("SkullOwner", skullOwner);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC createNPC(Location location, UUID uuid) {
return new ChunkLoaderNPCImpl(((CraftServer) Bukkit.getServer()).getServer(), location, uuid);
}
@Override
public ITileEntityChunkLoader createLoader(ChunkLoader chunkLoader) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot create loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ());
ChunkLoaderBlockEntity ChunkLoaderBlockEntity = new ChunkLoaderBlockEntity(chunkLoader, serverLevel, blockPos);
serverLevel.addBlockEntityTicker(ChunkLoaderBlockEntity.getTicker());
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = ((CraftChunk) bukkitChunk).getHandle();
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = -1;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, true);
}
return ChunkLoaderBlockEntity;
}
@Override
public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getX(), loaderLoc.getY(), loaderLoc.getZ());
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
ChunkLoaderBlockEntity chunkLoaderBlockEntity = ChunkLoaderBlockEntity.chunkLoaderBlockEntityMap.remove(chunkPosLong);
if (chunkLoaderBlockEntity != null) {
chunkLoaderBlockEntity.holograms.forEach(EntityHologram::removeHologram);
chunkLoaderBlockEntity.removed = true;
}
if (spawnParticle)
serverLevel.levelEvent(null, 2001, blockPos, Block.getId(serverLevel.getBlockState(blockPos)));
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = ((CraftChunk) bukkitChunk).getHandle();
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = 16;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, false);
}
}
@Override
public void updateSpawner(Location location, boolean reset) {
World bukkitWorld = location.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(location.getX(), location.getY(), location.getZ());
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos);
if (blockEntity instanceof SpawnerBlockEntity spawnerBlockEntity)
spawnerBlockEntity.getSpawner().requiredPlayerRange = reset ? 16 : -1;
}
}

View File

@ -0,0 +1,149 @@
package com.bgsoftware.wildloaders.nms.v1_18.loader;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.loaders.WChunkLoader;
import com.bgsoftware.wildloaders.nms.v1_18.EntityHologram;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class ChunkLoaderBlockEntity extends BlockEntity implements ITileEntityChunkLoader {
public static final Map<Long, ChunkLoaderBlockEntity> chunkLoaderBlockEntityMap = new HashMap<>();
public final List<EntityHologram> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final ChunkLoaderBlockEntityTicker ticker;
private final ServerLevel serverLevel;
private final BlockPos blockPos;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
public boolean removed = false;
public ChunkLoaderBlockEntity(ChunkLoader chunkLoader, ServerLevel serverLevel, BlockPos blockPos) {
super(BlockEntityType.COMMAND_BLOCK, blockPos, serverLevel.getBlockState(blockPos));
this.chunkLoader = (WChunkLoader) chunkLoader;
this.ticker = new ChunkLoaderBlockEntityTicker(this);
this.blockPos = blockPos;
this.serverLevel = serverLevel;
setLevel(serverLevel);
loaderBlock = serverLevel.getBlockState(blockPos).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
daysAmount = (short) (timeLeft / 86400);
timeLeft = timeLeft % 86400;
hoursAmount = (short) (timeLeft / 3600);
timeLeft = timeLeft % 3600;
minutesAmount = (short) (timeLeft / 60);
timeLeft = timeLeft % 60;
secondsAmount = (short) timeLeft;
}
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
chunkLoaderBlockEntityMap.put(chunkPosLong, this);
List<String> hologramLines = this.chunkLoader.getHologramLines();
double currentY = blockPos.getY() + 1;
for (int i = hologramLines.size(); i > 0; i--) {
EntityHologram hologram = new EntityHologram(serverLevel, blockPos.getX() + 0.5, currentY, blockPos.getZ() + 0.5);
updateName(hologram, hologramLines.get(i - 1));
serverLevel.addFreshEntity(hologram);
currentY += 0.23;
holograms.add(hologram);
}
}
public void tick() {
if (removed || ++currentTick <= 20)
return;
currentTick = 0;
if (chunkLoader.isNotActive() || this.serverLevel.getBlockState(this.blockPos).getBlock() != loaderBlock) {
chunkLoader.remove();
return;
}
if (chunkLoader.isInfinite())
return;
List<String> hologramLines = chunkLoader.getHologramLines();
int hologramsAmount = holograms.size();
for (int i = hologramsAmount; i > 0; i--) {
EntityHologram hologram = holograms.get(hologramsAmount - i);
updateName(hologram, hologramLines.get(i - 1));
}
chunkLoader.tick();
if (!removed) {
secondsAmount--;
if (secondsAmount < 0) {
secondsAmount = 59;
minutesAmount--;
if (minutesAmount < 0) {
minutesAmount = 59;
hoursAmount--;
if (hoursAmount < 0) {
hoursAmount = 23;
daysAmount--;
}
}
}
}
}
@Override
public Collection<Hologram> getHolograms() {
return Collections.unmodifiableList(holograms);
}
@Override
public boolean isRemoved() {
return removed || super.isRemoved();
}
public ChunkLoaderBlockEntityTicker getTicker() {
return ticker;
}
private void updateName(EntityHologram hologram, String line) {
hologram.setHologramName(line
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")
.replace("{4}", secondsAmount + "")
);
}
}

View File

@ -0,0 +1,30 @@
package com.bgsoftware.wildloaders.nms.v1_18.loader;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
public record ChunkLoaderBlockEntityTicker(
ChunkLoaderBlockEntity chunkLoaderBlockEntity) implements TickingBlockEntity {
@Override
public void tick() {
chunkLoaderBlockEntity.tick();
}
@Override
public boolean isRemoved() {
return chunkLoaderBlockEntity.isRemoved();
}
@Override
public BlockPos getPos() {
return chunkLoaderBlockEntity.getBlockPos();
}
@Override
public String getType() {
return BlockEntityType.getKey(chunkLoaderBlockEntity.getType()) + "";
}
}

36
NMS/v1_19/build.gradle Normal file
View File

@ -0,0 +1,36 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.6.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
group 'NMS:v1_19'
dependencies {
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.19.4-R0.1-SNAPSHOT")
compileOnly project(":API")
compileOnly rootProject
}
shadowJar {
archiveFileName = "${project.name}-exclude.jar"
}
assemble {
dependsOn(reobfJar)
}
tasks {
reobfJar {
File outputFile = new File(rootProject.archiveFolder, "reobf/${project.name}.jar")
outputJar.set(layout.buildDirectory.file(outputFile.getPath()))
}
}
if (project.hasProperty('nms.compile_v1_19') && !Boolean.valueOf(project.findProperty("nms.compile_v1_19").toString())) {
project.tasks.all { task -> task.enabled = false }
}

View File

@ -0,0 +1,237 @@
package com.bgsoftware.wildloaders.nms.v1_19;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.core.BlockPos;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundChatPacket;
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket;
import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.phys.AABB;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.nio.file.Path;
import java.util.UUID;
public final class ChunkLoaderNPCImpl extends ServerPlayer implements ChunkLoaderNPC {
private static final ReflectMethod<Void> SET_GAMEMODE = new ReflectMethod<>(ServerPlayerGameMode.class,
1, GameType.class, GameType.class);
private final ServerLevel serverLevel;
private final AABB boundingBox;
private final PlayerAdvancements advancements;
private boolean dieCall = false;
public ChunkLoaderNPCImpl(MinecraftServer minecraftServer, Location location, UUID uuid) {
super(minecraftServer, ((CraftWorld) location.getWorld()).getHandle(),
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())));
this.serverLevel = getLevel();
this.boundingBox = new AABB(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
this.connection = new DummyServerGamePacketListenerImpl(minecraftServer, this);
this.advancements = new DummyPlayerAdvancements(minecraftServer, this);
SET_GAMEMODE.invoke(this.gameMode, GameType.CREATIVE, null);
clientViewDistance = 1;
fauxSleeping = true;
spawnIn(this.serverLevel);
moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
this.serverLevel.addNewPlayer(this);
super.setBoundingBox(this.boundingBox);
}
@Override
public UUID getUniqueId() {
return super.getUUID();
}
@Override
public void die() {
discard();
}
@Override
public AABB getBoundingBoxForCulling() {
return this.boundingBox;
}
@Override
public void remove(RemovalReason removalReason) {
if (!dieCall) {
dieCall = true;
this.serverLevel.removePlayerImmediately(this, RemovalReason.UNLOADED_WITH_PLAYER);
dieCall = false;
} else {
super.remove(removalReason);
}
}
@Override
public Location getLocation() {
return getBukkitEntity().getLocation();
}
@Override
public Player getPlayer() {
return getBukkitEntity();
}
@Override
public PlayerAdvancements getAdvancements() {
return this.advancements;
}
public static class DummyConnection extends Connection {
DummyConnection() {
super(PacketFlow.SERVERBOUND);
this.channel = new DummyChannel();
this.address = null;
}
}
public static class DummyServerGamePacketListenerImpl extends ServerGamePacketListenerImpl {
DummyServerGamePacketListenerImpl(MinecraftServer minecraftServer, ServerPlayer serverPlayer) {
super(minecraftServer, new DummyConnection(), serverPlayer);
}
@Override
public void handleContainerClick(ServerboundContainerClickPacket containerClickPacket) {
// Do nothing.
}
@Override
public void handleMovePlayer(ServerboundMovePlayerPacket movePlayerPacket) {
// Do nothing.
}
@Override
public void handleSignUpdate(ServerboundSignUpdatePacket signUpdatePacket) {
// Do nothing.
}
@Override
public void handlePlayerAction(ServerboundPlayerActionPacket playerActionPacket) {
// Do nothing.
}
@Override
public void handleUseItem(ServerboundUseItemPacket useItemPacket) {
// Do nothing.
}
@Override
public void handleSetCarriedItem(ServerboundSetCarriedItemPacket setCarriedItemPacket) {
// Do nothing.
}
@Override
public void handleChat(ServerboundChatPacket chatPacket) {
// Do nothing.
}
@Override
public void disconnect(String s) {
// Do nothing.
}
public void send(Packet<?> packet) {
// Do nothing.
}
}
private static class DummyPlayerAdvancements extends PlayerAdvancements {
DummyPlayerAdvancements(MinecraftServer server, ServerPlayer serverPlayer) {
super(server.getFixerUpper(), server.getPlayerList(), server.getAdvancements(),
getAdvancementsFile(server, serverPlayer), serverPlayer);
}
private static Path getAdvancementsFile(MinecraftServer server, ServerPlayer serverPlayer) {
File advancementsDir = server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).toFile();
return new File(advancementsDir, serverPlayer.getUUID() + ".json").toPath();
}
@Override
public void setPlayer(ServerPlayer owner) {
// Do nothing.
}
@Override
public void stopListening() {
// Do nothing.
}
@Override
public void reload(ServerAdvancementManager advancementLoader) {
// Do nothing.
}
@Override
public void save() {
// Do nothing.
}
@Override
public boolean award(Advancement advancement, String criterionName) {
return false;
}
@Override
public boolean revoke(Advancement advancement, String criterionName) {
return false;
}
@Override
public void flushDirty(ServerPlayer player) {
// Do nothing.
}
@Override
public void setSelectedTab(@Nullable Advancement advancement) {
// Do nothing.
}
@Override
public AdvancementProgress getOrStartProgress(Advancement advancement) {
return new AdvancementProgress();
}
}
}

View File

@ -0,0 +1,165 @@
package com.bgsoftware.wildloaders.nms.v1_19;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftArmorStand;
import org.bukkit.craftbukkit.v1_19_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_19_R3.util.CraftChatMessage;
public final class EntityHologram extends ArmorStand implements Hologram {
private static final AABB EMPTY_BOUND = new AABB(0D, 0D, 0D, 0D, 0D, 0D);
private CraftEntity bukkitEntity;
public EntityHologram(ServerLevel serverLevel, double x, double y, double z) {
super(serverLevel, x, y, z);
setInvisible(true);
setSmall(true);
setShowArms(false);
setNoGravity(true);
setNoBasePlate(true);
setMarker(true);
super.collides = false;
super.setCustomNameVisible(true); // Custom name visible
super.setBoundingBox(EMPTY_BOUND);
}
@Override
public void setHologramName(String name) {
super.setCustomName(CraftChatMessage.fromStringOrNull(name));
}
@Override
public void removeHologram() {
super.remove(RemovalReason.DISCARDED);
}
@Override
public org.bukkit.entity.Entity getEntity() {
return getBukkitEntity();
}
@Override
public void tick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void inactiveTick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void addAdditionalSaveData(CompoundTag compoundTag) {
// Do not save NBT.
}
@Override
public boolean saveAsPassenger(CompoundTag compoundTag) {
// Do not save NBT.
return false;
}
@Override
public CompoundTag saveWithoutId(CompoundTag compoundTag) {
// Do not save NBT.
return compoundTag;
}
@Override
public void readAdditionalSaveData(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public void load(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public boolean isInvulnerableTo(DamageSource source) {
/*
* The field Entity.invulnerable is private.
* It's only used while saving NBTTags, but since the entity would be killed
* on chunk unload, we prefer to override isInvulnerable().
*/
return true;
}
@Override
public boolean repositionEntityAfterLoad() {
return false;
}
@Override
public void setCustomName(Component component) {
// Locks the custom name.
}
@Override
public void setCustomNameVisible(boolean visible) {
// Locks the custom name.
}
@Override
public InteractionResult interactAt(Player player, Vec3 hitPos, InteractionHand hand) {
// Prevent stand being equipped
return InteractionResult.PASS;
}
@Override
public void setItemSlot(EquipmentSlot equipmentSlot, ItemStack itemStack, boolean silent) {
// Prevent stand being equipped
}
@Override
public AABB getBoundingBoxForCulling() {
return EMPTY_BOUND;
}
@Override
public void playSound(SoundEvent soundEvent, float volume, float pitch) {
// Remove sounds.
}
@Override
public void remove(RemovalReason removalReason) {
// Prevent being killed.
}
@Override
public CraftEntity getBukkitEntity() {
if (bukkitEntity == null) {
bukkitEntity = new CraftArmorStand((CraftServer) Bukkit.getServer(), this);
}
return bukkitEntity;
}
}

View File

@ -0,0 +1,170 @@
package com.bgsoftware.wildloaders.nms.v1_19;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_19.loader.ChunkLoaderBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_19_R3.CraftServer;
import org.bukkit.craftbukkit.v1_19_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_19_R3.inventory.CraftItemStack;
import java.util.UUID;
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 8) ? def : compoundTag.getString(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putString(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public long getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 4) ? def : compoundTag.getLong(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putLong(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack bukkitItem, String texture) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
CompoundTag skullOwner = compoundTag.contains("SkullOwner") ?
compoundTag.getCompound("SkullOwner") : new CompoundTag();
CompoundTag properties = new CompoundTag();
ListTag textures = new ListTag();
CompoundTag signature = new CompoundTag();
signature.putString("Value", texture);
textures.add(signature);
properties.put("textures", textures);
skullOwner.put("Properties", properties);
skullOwner.putString("Id", UUID.randomUUID().toString());
compoundTag.put("SkullOwner", skullOwner);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC createNPC(Location location, UUID uuid) {
return new ChunkLoaderNPCImpl(((CraftServer) Bukkit.getServer()).getServer(), location, uuid);
}
@Override
public ITileEntityChunkLoader createLoader(ChunkLoader chunkLoader) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot create loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
ChunkLoaderBlockEntity ChunkLoaderBlockEntity = new ChunkLoaderBlockEntity(chunkLoader, serverLevel, blockPos);
serverLevel.addBlockEntityTicker(ChunkLoaderBlockEntity.getTicker());
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = -1;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, true);
}
return ChunkLoaderBlockEntity;
}
@Override
public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
ChunkLoaderBlockEntity chunkLoaderBlockEntity = ChunkLoaderBlockEntity.chunkLoaderBlockEntityMap.remove(chunkPosLong);
if (chunkLoaderBlockEntity != null) {
chunkLoaderBlockEntity.holograms.forEach(EntityHologram::removeHologram);
chunkLoaderBlockEntity.removed = true;
}
if (spawnParticle)
serverLevel.levelEvent(null, 2001, blockPos, Block.getId(serverLevel.getBlockState(blockPos)));
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = 16;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, false);
}
}
@Override
public void updateSpawner(Location location, boolean reset) {
World bukkitWorld = location.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos);
if (blockEntity instanceof SpawnerBlockEntity spawnerBlockEntity)
spawnerBlockEntity.getSpawner().requiredPlayerRange = reset ? 16 : -1;
}
}

View File

@ -0,0 +1,149 @@
package com.bgsoftware.wildloaders.nms.v1_19.loader;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.loaders.WChunkLoader;
import com.bgsoftware.wildloaders.nms.v1_19.EntityHologram;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class ChunkLoaderBlockEntity extends BlockEntity implements ITileEntityChunkLoader {
public static final Map<Long, ChunkLoaderBlockEntity> chunkLoaderBlockEntityMap = new HashMap<>();
public final List<EntityHologram> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final ChunkLoaderBlockEntityTicker ticker;
private final ServerLevel serverLevel;
private final BlockPos blockPos;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
public boolean removed = false;
public ChunkLoaderBlockEntity(ChunkLoader chunkLoader, ServerLevel serverLevel, BlockPos blockPos) {
super(BlockEntityType.COMMAND_BLOCK, blockPos, serverLevel.getBlockState(blockPos));
this.chunkLoader = (WChunkLoader) chunkLoader;
this.ticker = new ChunkLoaderBlockEntityTicker(this);
this.blockPos = blockPos;
this.serverLevel = serverLevel;
setLevel(serverLevel);
loaderBlock = serverLevel.getBlockState(blockPos).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
daysAmount = (short) (timeLeft / 86400);
timeLeft = timeLeft % 86400;
hoursAmount = (short) (timeLeft / 3600);
timeLeft = timeLeft % 3600;
minutesAmount = (short) (timeLeft / 60);
timeLeft = timeLeft % 60;
secondsAmount = (short) timeLeft;
}
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
chunkLoaderBlockEntityMap.put(chunkPosLong, this);
List<String> hologramLines = this.chunkLoader.getHologramLines();
double currentY = blockPos.getY() + 1;
for (int i = hologramLines.size(); i > 0; i--) {
EntityHologram hologram = new EntityHologram(serverLevel, blockPos.getX() + 0.5, currentY, blockPos.getZ() + 0.5);
updateName(hologram, hologramLines.get(i - 1));
serverLevel.addFreshEntity(hologram);
currentY += 0.23;
holograms.add(hologram);
}
}
public void tick() {
if (removed || ++currentTick <= 20)
return;
currentTick = 0;
if (chunkLoader.isNotActive() || this.serverLevel.getBlockState(this.blockPos).getBlock() != loaderBlock) {
chunkLoader.remove();
return;
}
if (chunkLoader.isInfinite())
return;
List<String> hologramLines = chunkLoader.getHologramLines();
int hologramsAmount = holograms.size();
for (int i = hologramsAmount; i > 0; i--) {
EntityHologram hologram = holograms.get(hologramsAmount - i);
updateName(hologram, hologramLines.get(i - 1));
}
chunkLoader.tick();
if (!removed) {
secondsAmount--;
if (secondsAmount < 0) {
secondsAmount = 59;
minutesAmount--;
if (minutesAmount < 0) {
minutesAmount = 59;
hoursAmount--;
if (hoursAmount < 0) {
hoursAmount = 23;
daysAmount--;
}
}
}
}
}
@Override
public Collection<Hologram> getHolograms() {
return Collections.unmodifiableList(holograms);
}
@Override
public boolean isRemoved() {
return removed || super.isRemoved();
}
public ChunkLoaderBlockEntityTicker getTicker() {
return ticker;
}
private void updateName(EntityHologram hologram, String line) {
hologram.setHologramName(line
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")
.replace("{4}", secondsAmount + "")
);
}
}

View File

@ -0,0 +1,30 @@
package com.bgsoftware.wildloaders.nms.v1_19.loader;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
public record ChunkLoaderBlockEntityTicker(
ChunkLoaderBlockEntity chunkLoaderBlockEntity) implements TickingBlockEntity {
@Override
public void tick() {
chunkLoaderBlockEntity.tick();
}
@Override
public boolean isRemoved() {
return chunkLoaderBlockEntity.isRemoved();
}
@Override
public BlockPos getPos() {
return chunkLoaderBlockEntity.getBlockPos();
}
@Override
public String getType() {
return BlockEntityType.getKey(chunkLoaderBlockEntity.getType()) + "";
}
}

36
NMS/v1_20_1/build.gradle Normal file
View File

@ -0,0 +1,36 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.6.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
group 'NMS:v1_20_1'
dependencies {
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.20.1-R0.1-SNAPSHOT")
compileOnly project(":API")
compileOnly rootProject
}
shadowJar {
archiveFileName = "${project.name}-exclude.jar"
}
assemble {
dependsOn(reobfJar)
}
tasks {
reobfJar {
File outputFile = new File(rootProject.archiveFolder, "reobf/${project.name}.jar")
outputJar.set(layout.buildDirectory.file(outputFile.getPath()))
}
}
if (project.hasProperty('nms.compile_v1_20') && !Boolean.valueOf(project.findProperty("nms.compile_v1_20").toString())) {
project.tasks.all { task -> task.enabled = false }
}

View File

@ -0,0 +1,237 @@
package com.bgsoftware.wildloaders.nms.v1_20_1;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.advancements.Advancement;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.core.BlockPos;
import net.minecraft.network.Connection;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundChatPacket;
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket;
import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.phys.AABB;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.nio.file.Path;
import java.util.UUID;
public final class ChunkLoaderNPCImpl extends ServerPlayer implements ChunkLoaderNPC {
private static final ReflectMethod<Void> SET_GAMEMODE = new ReflectMethod<>(ServerPlayerGameMode.class,
1, GameType.class, GameType.class);
private final ServerLevel serverLevel;
private final AABB boundingBox;
private final PlayerAdvancements advancements;
private boolean dieCall = false;
public ChunkLoaderNPCImpl(MinecraftServer minecraftServer, Location location, UUID uuid) {
super(minecraftServer, ((CraftWorld) location.getWorld()).getHandle(),
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())));
this.serverLevel = serverLevel();
this.boundingBox = new AABB(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
this.connection = new DummyServerGamePacketListenerImpl(minecraftServer, this);
this.advancements = new DummyPlayerAdvancements(minecraftServer, this);
SET_GAMEMODE.invoke(this.gameMode, GameType.CREATIVE, null);
clientViewDistance = 1;
fauxSleeping = true;
spawnIn(this.serverLevel);
moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
this.serverLevel.addNewPlayer(this);
super.setBoundingBox(this.boundingBox);
}
@Override
public UUID getUniqueId() {
return super.getUUID();
}
@Override
public void die() {
discard();
}
@Override
public AABB getBoundingBoxForCulling() {
return this.boundingBox;
}
@Override
public void remove(RemovalReason removalReason) {
if (!dieCall) {
dieCall = true;
this.serverLevel.removePlayerImmediately(this, RemovalReason.UNLOADED_WITH_PLAYER);
dieCall = false;
} else {
super.remove(removalReason);
}
}
@Override
public Location getLocation() {
return getBukkitEntity().getLocation();
}
@Override
public Player getPlayer() {
return getBukkitEntity();
}
@Override
public PlayerAdvancements getAdvancements() {
return this.advancements;
}
public static class DummyConnection extends Connection {
DummyConnection() {
super(PacketFlow.SERVERBOUND);
this.channel = new DummyChannel();
this.address = null;
}
}
public static class DummyServerGamePacketListenerImpl extends ServerGamePacketListenerImpl {
DummyServerGamePacketListenerImpl(MinecraftServer minecraftServer, ServerPlayer serverPlayer) {
super(minecraftServer, new DummyConnection(), serverPlayer);
}
@Override
public void handleContainerClick(ServerboundContainerClickPacket containerClickPacket) {
// Do nothing.
}
@Override
public void handleMovePlayer(ServerboundMovePlayerPacket movePlayerPacket) {
// Do nothing.
}
@Override
public void handleSignUpdate(ServerboundSignUpdatePacket signUpdatePacket) {
// Do nothing.
}
@Override
public void handlePlayerAction(ServerboundPlayerActionPacket playerActionPacket) {
// Do nothing.
}
@Override
public void handleUseItem(ServerboundUseItemPacket useItemPacket) {
// Do nothing.
}
@Override
public void handleSetCarriedItem(ServerboundSetCarriedItemPacket setCarriedItemPacket) {
// Do nothing.
}
@Override
public void handleChat(ServerboundChatPacket chatPacket) {
// Do nothing.
}
@Override
public void disconnect(String s) {
// Do nothing.
}
public void send(Packet<?> packet) {
// Do nothing.
}
}
private static class DummyPlayerAdvancements extends PlayerAdvancements {
DummyPlayerAdvancements(MinecraftServer server, ServerPlayer serverPlayer) {
super(server.getFixerUpper(), server.getPlayerList(), server.getAdvancements(),
getAdvancementsFile(server, serverPlayer), serverPlayer);
}
private static Path getAdvancementsFile(MinecraftServer server, ServerPlayer serverPlayer) {
File advancementsDir = server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).toFile();
return new File(advancementsDir, serverPlayer.getUUID() + ".json").toPath();
}
@Override
public void setPlayer(ServerPlayer owner) {
// Do nothing.
}
@Override
public void stopListening() {
// Do nothing.
}
@Override
public void reload(ServerAdvancementManager advancementLoader) {
// Do nothing.
}
@Override
public void save() {
// Do nothing.
}
@Override
public boolean award(Advancement advancement, String criterionName) {
return false;
}
@Override
public boolean revoke(Advancement advancement, String criterionName) {
return false;
}
@Override
public void flushDirty(ServerPlayer player) {
// Do nothing.
}
@Override
public void setSelectedTab(@Nullable Advancement advancement) {
// Do nothing.
}
@Override
public AdvancementProgress getOrStartProgress(Advancement advancement) {
return new AdvancementProgress();
}
}
}

View File

@ -0,0 +1,165 @@
package com.bgsoftware.wildloaders.nms.v1_20_1;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftArmorStand;
import org.bukkit.craftbukkit.v1_20_R1.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R1.util.CraftChatMessage;
public final class EntityHologram extends ArmorStand implements Hologram {
private static final AABB EMPTY_BOUND = new AABB(0D, 0D, 0D, 0D, 0D, 0D);
private CraftEntity bukkitEntity;
public EntityHologram(ServerLevel serverLevel, double x, double y, double z) {
super(serverLevel, x, y, z);
setInvisible(true);
setSmall(true);
setShowArms(false);
setNoGravity(true);
setNoBasePlate(true);
setMarker(true);
super.collides = false;
super.setCustomNameVisible(true); // Custom name visible
super.setBoundingBox(EMPTY_BOUND);
}
@Override
public void setHologramName(String name) {
super.setCustomName(CraftChatMessage.fromStringOrNull(name));
}
@Override
public void removeHologram() {
super.remove(RemovalReason.DISCARDED);
}
@Override
public org.bukkit.entity.Entity getEntity() {
return getBukkitEntity();
}
@Override
public void tick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void inactiveTick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void addAdditionalSaveData(CompoundTag compoundTag) {
// Do not save NBT.
}
@Override
public boolean saveAsPassenger(CompoundTag compoundTag) {
// Do not save NBT.
return false;
}
@Override
public CompoundTag saveWithoutId(CompoundTag compoundTag) {
// Do not save NBT.
return compoundTag;
}
@Override
public void readAdditionalSaveData(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public void load(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public boolean isInvulnerableTo(DamageSource source) {
/*
* The field Entity.invulnerable is private.
* It's only used while saving NBTTags, but since the entity would be killed
* on chunk unload, we prefer to override isInvulnerable().
*/
return true;
}
@Override
public boolean repositionEntityAfterLoad() {
return false;
}
@Override
public void setCustomName(Component component) {
// Locks the custom name.
}
@Override
public void setCustomNameVisible(boolean visible) {
// Locks the custom name.
}
@Override
public InteractionResult interactAt(Player player, Vec3 hitPos, InteractionHand hand) {
// Prevent stand being equipped
return InteractionResult.PASS;
}
@Override
public void setItemSlot(EquipmentSlot equipmentSlot, ItemStack itemStack, boolean silent) {
// Prevent stand being equipped
}
@Override
public AABB getBoundingBoxForCulling() {
return EMPTY_BOUND;
}
@Override
public void playSound(SoundEvent soundEvent, float volume, float pitch) {
// Remove sounds.
}
@Override
public void remove(RemovalReason removalReason) {
// Prevent being killed.
}
@Override
public CraftEntity getBukkitEntity() {
if (bukkitEntity == null) {
bukkitEntity = new CraftArmorStand((CraftServer) Bukkit.getServer(), this);
}
return bukkitEntity;
}
}

View File

@ -0,0 +1,170 @@
package com.bgsoftware.wildloaders.nms.v1_20_1;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_20_1.loader.ChunkLoaderBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R1.CraftServer;
import org.bukkit.craftbukkit.v1_20_R1.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R1.inventory.CraftItemStack;
import java.util.UUID;
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 8) ? def : compoundTag.getString(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putString(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public long getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 4) ? def : compoundTag.getLong(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putLong(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack bukkitItem, String texture) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
CompoundTag skullOwner = compoundTag.contains("SkullOwner") ?
compoundTag.getCompound("SkullOwner") : new CompoundTag();
CompoundTag properties = new CompoundTag();
ListTag textures = new ListTag();
CompoundTag signature = new CompoundTag();
signature.putString("Value", texture);
textures.add(signature);
properties.put("textures", textures);
skullOwner.put("Properties", properties);
skullOwner.putString("Id", UUID.randomUUID().toString());
compoundTag.put("SkullOwner", skullOwner);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC createNPC(Location location, UUID uuid) {
return new ChunkLoaderNPCImpl(((CraftServer) Bukkit.getServer()).getServer(), location, uuid);
}
@Override
public ITileEntityChunkLoader createLoader(ChunkLoader chunkLoader) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot create loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
ChunkLoaderBlockEntity ChunkLoaderBlockEntity = new ChunkLoaderBlockEntity(chunkLoader, serverLevel, blockPos);
serverLevel.addBlockEntityTicker(ChunkLoaderBlockEntity.getTicker());
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = -1;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, true);
}
return ChunkLoaderBlockEntity;
}
@Override
public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
ChunkLoaderBlockEntity chunkLoaderBlockEntity = ChunkLoaderBlockEntity.chunkLoaderBlockEntityMap.remove(chunkPosLong);
if (chunkLoaderBlockEntity != null) {
chunkLoaderBlockEntity.holograms.forEach(EntityHologram::removeHologram);
chunkLoaderBlockEntity.removed = true;
}
if (spawnParticle)
serverLevel.levelEvent(null, 2001, blockPos, Block.getId(serverLevel.getBlockState(blockPos)));
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = 16;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, false);
}
}
@Override
public void updateSpawner(Location location, boolean reset) {
World bukkitWorld = location.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos);
if (blockEntity instanceof SpawnerBlockEntity spawnerBlockEntity)
spawnerBlockEntity.getSpawner().requiredPlayerRange = reset ? 16 : -1;
}
}

View File

@ -0,0 +1,149 @@
package com.bgsoftware.wildloaders.nms.v1_20_1.loader;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.loaders.WChunkLoader;
import com.bgsoftware.wildloaders.nms.v1_20_1.EntityHologram;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class ChunkLoaderBlockEntity extends BlockEntity implements ITileEntityChunkLoader {
public static final Map<Long, ChunkLoaderBlockEntity> chunkLoaderBlockEntityMap = new HashMap<>();
public final List<EntityHologram> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final ChunkLoaderBlockEntityTicker ticker;
private final ServerLevel serverLevel;
private final BlockPos blockPos;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
public boolean removed = false;
public ChunkLoaderBlockEntity(ChunkLoader chunkLoader, ServerLevel serverLevel, BlockPos blockPos) {
super(BlockEntityType.COMMAND_BLOCK, blockPos, serverLevel.getBlockState(blockPos));
this.chunkLoader = (WChunkLoader) chunkLoader;
this.ticker = new ChunkLoaderBlockEntityTicker(this);
this.blockPos = blockPos;
this.serverLevel = serverLevel;
setLevel(serverLevel);
loaderBlock = serverLevel.getBlockState(blockPos).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
daysAmount = (short) (timeLeft / 86400);
timeLeft = timeLeft % 86400;
hoursAmount = (short) (timeLeft / 3600);
timeLeft = timeLeft % 3600;
minutesAmount = (short) (timeLeft / 60);
timeLeft = timeLeft % 60;
secondsAmount = (short) timeLeft;
}
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
chunkLoaderBlockEntityMap.put(chunkPosLong, this);
List<String> hologramLines = this.chunkLoader.getHologramLines();
double currentY = blockPos.getY() + 1;
for (int i = hologramLines.size(); i > 0; i--) {
EntityHologram hologram = new EntityHologram(serverLevel, blockPos.getX() + 0.5, currentY, blockPos.getZ() + 0.5);
updateName(hologram, hologramLines.get(i - 1));
serverLevel.addFreshEntity(hologram);
currentY += 0.23;
holograms.add(hologram);
}
}
public void tick() {
if (removed || ++currentTick <= 20)
return;
currentTick = 0;
if (chunkLoader.isNotActive() || this.serverLevel.getBlockState(this.blockPos).getBlock() != loaderBlock) {
chunkLoader.remove();
return;
}
if (chunkLoader.isInfinite())
return;
List<String> hologramLines = chunkLoader.getHologramLines();
int hologramsAmount = holograms.size();
for (int i = hologramsAmount; i > 0; i--) {
EntityHologram hologram = holograms.get(hologramsAmount - i);
updateName(hologram, hologramLines.get(i - 1));
}
chunkLoader.tick();
if (!removed) {
secondsAmount--;
if (secondsAmount < 0) {
secondsAmount = 59;
minutesAmount--;
if (minutesAmount < 0) {
minutesAmount = 59;
hoursAmount--;
if (hoursAmount < 0) {
hoursAmount = 23;
daysAmount--;
}
}
}
}
}
@Override
public Collection<Hologram> getHolograms() {
return Collections.unmodifiableList(holograms);
}
@Override
public boolean isRemoved() {
return removed || super.isRemoved();
}
public ChunkLoaderBlockEntityTicker getTicker() {
return ticker;
}
private void updateName(EntityHologram hologram, String line) {
hologram.setHologramName(line
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")
.replace("{4}", secondsAmount + "")
);
}
}

View File

@ -0,0 +1,30 @@
package com.bgsoftware.wildloaders.nms.v1_20_1.loader;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
public record ChunkLoaderBlockEntityTicker(
ChunkLoaderBlockEntity chunkLoaderBlockEntity) implements TickingBlockEntity {
@Override
public void tick() {
chunkLoaderBlockEntity.tick();
}
@Override
public boolean isRemoved() {
return chunkLoaderBlockEntity.isRemoved();
}
@Override
public BlockPos getPos() {
return chunkLoaderBlockEntity.getBlockPos();
}
@Override
public String getType() {
return BlockEntityType.getKey(chunkLoaderBlockEntity.getType()) + "";
}
}

36
NMS/v1_20_2/build.gradle Normal file
View File

@ -0,0 +1,36 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.6.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
group 'NMS:v1_20_2'
dependencies {
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.20.2-R0.1-SNAPSHOT")
compileOnly project(":API")
compileOnly rootProject
}
shadowJar {
archiveFileName = "${project.name}-exclude.jar"
}
assemble {
dependsOn(reobfJar)
}
tasks {
reobfJar {
File outputFile = new File(rootProject.archiveFolder, "reobf/${project.name}.jar")
outputJar.set(layout.buildDirectory.file(outputFile.getPath()))
}
}
if (project.hasProperty('nms.compile_v1_20') && !Boolean.valueOf(project.findProperty("nms.compile_v1_20").toString())) {
project.tasks.all { task -> task.enabled = false }
}

View File

@ -0,0 +1,251 @@
package com.bgsoftware.wildloaders.nms.v1_20_2;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.advancements.AdvancementHolder;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.core.BlockPos;
import net.minecraft.network.Connection;
import net.minecraft.network.PacketListener;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundChatPacket;
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket;
import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.server.network.CommonListenerCookie;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.phys.AABB;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.nio.file.Path;
import java.util.UUID;
public final class ChunkLoaderNPCImpl extends ServerPlayer implements ChunkLoaderNPC {
private static final ReflectMethod<Void> SET_GAMEMODE = new ReflectMethod<>(ServerPlayerGameMode.class,
1, GameType.class, GameType.class);
private final ServerLevel serverLevel;
private final AABB boundingBox;
private final PlayerAdvancements advancements;
private boolean dieCall = false;
public ChunkLoaderNPCImpl(MinecraftServer minecraftServer, Location location, UUID uuid) {
super(minecraftServer, ((CraftWorld) location.getWorld()).getHandle(),
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())),
ClientInformation.createDefault());
this.serverLevel = serverLevel();
this.boundingBox = new AABB(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
this.connection = new DummyServerGamePacketListenerImpl(minecraftServer, this);
this.advancements = new DummyPlayerAdvancements(minecraftServer, this);
SET_GAMEMODE.invoke(this.gameMode, GameType.CREATIVE, null);
try {
setLoadViewDistance(2);
setTickViewDistance(2);
setSendViewDistance(2);
} catch (Throwable ignored) {
// Doesn't exist on Spigot
}
fauxSleeping = true;
spawnIn(this.serverLevel);
moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
this.serverLevel.addNewPlayer(this);
super.setBoundingBox(this.boundingBox);
}
@Override
public UUID getUniqueId() {
return super.getUUID();
}
@Override
public void die() {
discard();
}
@Override
public AABB getBoundingBoxForCulling() {
return this.boundingBox;
}
@Override
public void remove(RemovalReason removalReason) {
if (!dieCall) {
dieCall = true;
this.serverLevel.removePlayerImmediately(this, RemovalReason.UNLOADED_WITH_PLAYER);
dieCall = false;
} else {
super.remove(removalReason);
}
}
@Override
public Location getLocation() {
return getBukkitEntity().getLocation();
}
@Override
public Player getPlayer() {
return getBukkitEntity();
}
@Override
public PlayerAdvancements getAdvancements() {
return this.advancements;
}
public static class DummyConnection extends Connection {
DummyConnection() {
super(PacketFlow.SERVERBOUND);
this.channel = new DummyChannel();
this.address = null;
}
@Override
public void setListener(PacketListener packetListener) {
// Do nothing.
}
}
public class DummyServerGamePacketListenerImpl extends ServerGamePacketListenerImpl {
DummyServerGamePacketListenerImpl(MinecraftServer minecraftServer, ServerPlayer serverPlayer) {
super(minecraftServer, new DummyConnection(), serverPlayer, CommonListenerCookie.createInitial(ChunkLoaderNPCImpl.this.getGameProfile()));
}
@Override
public void handleContainerClick(ServerboundContainerClickPacket containerClickPacket) {
// Do nothing.
}
@Override
public void handleMovePlayer(ServerboundMovePlayerPacket movePlayerPacket) {
// Do nothing.
}
@Override
public void handleSignUpdate(ServerboundSignUpdatePacket signUpdatePacket) {
// Do nothing.
}
@Override
public void handlePlayerAction(ServerboundPlayerActionPacket playerActionPacket) {
// Do nothing.
}
@Override
public void handleUseItem(ServerboundUseItemPacket useItemPacket) {
// Do nothing.
}
@Override
public void handleSetCarriedItem(ServerboundSetCarriedItemPacket setCarriedItemPacket) {
// Do nothing.
}
@Override
public void handleChat(ServerboundChatPacket chatPacket) {
// Do nothing.
}
@Override
public void disconnect(String s) {
// Do nothing.
}
public void send(Packet<?> packet) {
// Do nothing.
}
}
private static class DummyPlayerAdvancements extends PlayerAdvancements {
DummyPlayerAdvancements(MinecraftServer server, ServerPlayer serverPlayer) {
super(server.getFixerUpper(), server.getPlayerList(), server.getAdvancements(),
getAdvancementsFile(server, serverPlayer), serverPlayer);
}
private static Path getAdvancementsFile(MinecraftServer server, ServerPlayer serverPlayer) {
File advancementsDir = server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).toFile();
return new File(advancementsDir, serverPlayer.getUUID() + ".json").toPath();
}
@Override
public void setPlayer(ServerPlayer owner) {
// Do nothing.
}
@Override
public void stopListening() {
// Do nothing.
}
@Override
public void reload(ServerAdvancementManager advancementLoader) {
// Do nothing.
}
@Override
public void save() {
// Do nothing.
}
@Override
public boolean award(AdvancementHolder advancement, String criterionName) {
return false;
}
@Override
public boolean revoke(AdvancementHolder advancement, String criterionName) {
return false;
}
@Override
public void flushDirty(ServerPlayer player) {
// Do nothing.
}
@Override
public void setSelectedTab(@Nullable AdvancementHolder advancement) {
// Do nothing.
}
@Override
public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) {
return new AdvancementProgress();
}
}
}

View File

@ -0,0 +1,165 @@
package com.bgsoftware.wildloaders.nms.v1_20_2;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftArmorStand;
import org.bukkit.craftbukkit.v1_20_R2.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R2.util.CraftChatMessage;
public final class EntityHologram extends ArmorStand implements Hologram {
private static final AABB EMPTY_BOUND = new AABB(0D, 0D, 0D, 0D, 0D, 0D);
private CraftEntity bukkitEntity;
public EntityHologram(ServerLevel serverLevel, double x, double y, double z) {
super(serverLevel, x, y, z);
setInvisible(true);
setSmall(true);
setShowArms(false);
setNoGravity(true);
setNoBasePlate(true);
setMarker(true);
super.collides = false;
super.setCustomNameVisible(true); // Custom name visible
super.setBoundingBox(EMPTY_BOUND);
}
@Override
public void setHologramName(String name) {
super.setCustomName(CraftChatMessage.fromStringOrNull(name));
}
@Override
public void removeHologram() {
super.remove(RemovalReason.DISCARDED);
}
@Override
public org.bukkit.entity.Entity getEntity() {
return getBukkitEntity();
}
@Override
public void tick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void inactiveTick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void addAdditionalSaveData(CompoundTag compoundTag) {
// Do not save NBT.
}
@Override
public boolean saveAsPassenger(CompoundTag compoundTag) {
// Do not save NBT.
return false;
}
@Override
public CompoundTag saveWithoutId(CompoundTag compoundTag) {
// Do not save NBT.
return compoundTag;
}
@Override
public void readAdditionalSaveData(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public void load(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public boolean isInvulnerableTo(DamageSource source) {
/*
* The field Entity.invulnerable is private.
* It's only used while saving NBTTags, but since the entity would be killed
* on chunk unload, we prefer to override isInvulnerable().
*/
return true;
}
@Override
public boolean repositionEntityAfterLoad() {
return false;
}
@Override
public void setCustomName(Component component) {
// Locks the custom name.
}
@Override
public void setCustomNameVisible(boolean visible) {
// Locks the custom name.
}
@Override
public InteractionResult interactAt(Player player, Vec3 hitPos, InteractionHand hand) {
// Prevent stand being equipped
return InteractionResult.PASS;
}
@Override
public void setItemSlot(EquipmentSlot equipmentSlot, ItemStack itemStack, boolean silent) {
// Prevent stand being equipped
}
@Override
public AABB getBoundingBoxForCulling() {
return EMPTY_BOUND;
}
@Override
public void playSound(SoundEvent soundEvent, float volume, float pitch) {
// Remove sounds.
}
@Override
public void remove(RemovalReason removalReason) {
// Prevent being killed.
}
@Override
public CraftEntity getBukkitEntity() {
if (bukkitEntity == null) {
bukkitEntity = new CraftArmorStand((CraftServer) Bukkit.getServer(), this);
}
return bukkitEntity;
}
}

View File

@ -0,0 +1,170 @@
package com.bgsoftware.wildloaders.nms.v1_20_2;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_20_2.loader.ChunkLoaderBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R2.CraftServer;
import org.bukkit.craftbukkit.v1_20_R2.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R2.inventory.CraftItemStack;
import java.util.UUID;
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 8) ? def : compoundTag.getString(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putString(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public long getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 4) ? def : compoundTag.getLong(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putLong(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack bukkitItem, String texture) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
CompoundTag skullOwner = compoundTag.contains("SkullOwner") ?
compoundTag.getCompound("SkullOwner") : new CompoundTag();
CompoundTag properties = new CompoundTag();
ListTag textures = new ListTag();
CompoundTag signature = new CompoundTag();
signature.putString("Value", texture);
textures.add(signature);
properties.put("textures", textures);
skullOwner.put("Properties", properties);
skullOwner.putString("Id", UUID.randomUUID().toString());
compoundTag.put("SkullOwner", skullOwner);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC createNPC(Location location, UUID uuid) {
return new ChunkLoaderNPCImpl(((CraftServer) Bukkit.getServer()).getServer(), location, uuid);
}
@Override
public ITileEntityChunkLoader createLoader(ChunkLoader chunkLoader) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot create loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
ChunkLoaderBlockEntity ChunkLoaderBlockEntity = new ChunkLoaderBlockEntity(chunkLoader, serverLevel, blockPos);
serverLevel.addBlockEntityTicker(ChunkLoaderBlockEntity.getTicker());
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = -1;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, true);
}
return ChunkLoaderBlockEntity;
}
@Override
public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
ChunkLoaderBlockEntity chunkLoaderBlockEntity = ChunkLoaderBlockEntity.chunkLoaderBlockEntityMap.remove(chunkPosLong);
if (chunkLoaderBlockEntity != null) {
chunkLoaderBlockEntity.holograms.forEach(EntityHologram::removeHologram);
chunkLoaderBlockEntity.removed = true;
}
if (spawnParticle)
serverLevel.levelEvent(null, 2001, blockPos, Block.getId(serverLevel.getBlockState(blockPos)));
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = 16;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, false);
}
}
@Override
public void updateSpawner(Location location, boolean reset) {
World bukkitWorld = location.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos);
if (blockEntity instanceof SpawnerBlockEntity spawnerBlockEntity)
spawnerBlockEntity.getSpawner().requiredPlayerRange = reset ? 16 : -1;
}
}

View File

@ -0,0 +1,149 @@
package com.bgsoftware.wildloaders.nms.v1_20_2.loader;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.loaders.WChunkLoader;
import com.bgsoftware.wildloaders.nms.v1_20_2.EntityHologram;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class ChunkLoaderBlockEntity extends BlockEntity implements ITileEntityChunkLoader {
public static final Map<Long, ChunkLoaderBlockEntity> chunkLoaderBlockEntityMap = new HashMap<>();
public final List<EntityHologram> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final ChunkLoaderBlockEntityTicker ticker;
private final ServerLevel serverLevel;
private final BlockPos blockPos;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
public boolean removed = false;
public ChunkLoaderBlockEntity(ChunkLoader chunkLoader, ServerLevel serverLevel, BlockPos blockPos) {
super(BlockEntityType.COMMAND_BLOCK, blockPos, serverLevel.getBlockState(blockPos));
this.chunkLoader = (WChunkLoader) chunkLoader;
this.ticker = new ChunkLoaderBlockEntityTicker(this);
this.blockPos = blockPos;
this.serverLevel = serverLevel;
setLevel(serverLevel);
loaderBlock = serverLevel.getBlockState(blockPos).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
daysAmount = (short) (timeLeft / 86400);
timeLeft = timeLeft % 86400;
hoursAmount = (short) (timeLeft / 3600);
timeLeft = timeLeft % 3600;
minutesAmount = (short) (timeLeft / 60);
timeLeft = timeLeft % 60;
secondsAmount = (short) timeLeft;
}
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
chunkLoaderBlockEntityMap.put(chunkPosLong, this);
List<String> hologramLines = this.chunkLoader.getHologramLines();
double currentY = blockPos.getY() + 1;
for (int i = hologramLines.size(); i > 0; i--) {
EntityHologram hologram = new EntityHologram(serverLevel, blockPos.getX() + 0.5, currentY, blockPos.getZ() + 0.5);
updateName(hologram, hologramLines.get(i - 1));
serverLevel.addFreshEntity(hologram);
currentY += 0.23;
holograms.add(hologram);
}
}
public void tick() {
if (removed || ++currentTick <= 20)
return;
currentTick = 0;
if (chunkLoader.isNotActive() || this.serverLevel.getBlockState(this.blockPos).getBlock() != loaderBlock) {
chunkLoader.remove();
return;
}
if (chunkLoader.isInfinite())
return;
List<String> hologramLines = chunkLoader.getHologramLines();
int hologramsAmount = holograms.size();
for (int i = hologramsAmount; i > 0; i--) {
EntityHologram hologram = holograms.get(hologramsAmount - i);
updateName(hologram, hologramLines.get(i - 1));
}
chunkLoader.tick();
if (!removed) {
secondsAmount--;
if (secondsAmount < 0) {
secondsAmount = 59;
minutesAmount--;
if (minutesAmount < 0) {
minutesAmount = 59;
hoursAmount--;
if (hoursAmount < 0) {
hoursAmount = 23;
daysAmount--;
}
}
}
}
}
@Override
public Collection<Hologram> getHolograms() {
return Collections.unmodifiableList(holograms);
}
@Override
public boolean isRemoved() {
return removed || super.isRemoved();
}
public ChunkLoaderBlockEntityTicker getTicker() {
return ticker;
}
private void updateName(EntityHologram hologram, String line) {
hologram.setHologramName(line
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")
.replace("{4}", secondsAmount + "")
);
}
}

View File

@ -0,0 +1,30 @@
package com.bgsoftware.wildloaders.nms.v1_20_2.loader;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
public record ChunkLoaderBlockEntityTicker(
ChunkLoaderBlockEntity chunkLoaderBlockEntity) implements TickingBlockEntity {
@Override
public void tick() {
chunkLoaderBlockEntity.tick();
}
@Override
public boolean isRemoved() {
return chunkLoaderBlockEntity.isRemoved();
}
@Override
public BlockPos getPos() {
return chunkLoaderBlockEntity.getBlockPos();
}
@Override
public String getType() {
return BlockEntityType.getKey(chunkLoaderBlockEntity.getType()) + "";
}
}

36
NMS/v1_20_3/build.gradle Normal file
View File

@ -0,0 +1,36 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.6.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
group 'NMS:v1_20_3'
dependencies {
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.20.4-R0.1-SNAPSHOT")
compileOnly project(":API")
compileOnly rootProject
}
shadowJar {
archiveFileName = "${project.name}-exclude.jar"
}
assemble {
dependsOn(reobfJar)
}
tasks {
reobfJar {
File outputFile = new File(rootProject.archiveFolder, "reobf/${project.name}.jar")
outputJar.set(layout.buildDirectory.file(outputFile.getPath()))
}
}
if (project.hasProperty('nms.compile_v1_20') && !Boolean.valueOf(project.findProperty("nms.compile_v1_20").toString())) {
project.tasks.all { task -> task.enabled = false }
}

View File

@ -0,0 +1,251 @@
package com.bgsoftware.wildloaders.nms.v1_20_3;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.advancements.AdvancementHolder;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.core.BlockPos;
import net.minecraft.network.Connection;
import net.minecraft.network.PacketListener;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundChatPacket;
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket;
import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.server.network.CommonListenerCookie;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.phys.AABB;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.nio.file.Path;
import java.util.UUID;
public final class ChunkLoaderNPCImpl extends ServerPlayer implements ChunkLoaderNPC {
private static final ReflectMethod<Void> SET_GAMEMODE = new ReflectMethod<>(ServerPlayerGameMode.class,
1, GameType.class, GameType.class);
private final ServerLevel serverLevel;
private final AABB boundingBox;
private final PlayerAdvancements advancements;
private boolean dieCall = false;
public ChunkLoaderNPCImpl(MinecraftServer minecraftServer, Location location, UUID uuid) {
super(minecraftServer, ((CraftWorld) location.getWorld()).getHandle(),
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())),
ClientInformation.createDefault());
this.serverLevel = serverLevel();
this.boundingBox = new AABB(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
this.connection = new DummyServerGamePacketListenerImpl(minecraftServer, this);
this.advancements = new DummyPlayerAdvancements(minecraftServer, this);
SET_GAMEMODE.invoke(this.gameMode, GameType.CREATIVE, null);
try {
setLoadViewDistance(2);
setTickViewDistance(2);
setSendViewDistance(2);
} catch (Throwable ignored) {
// Doesn't exist on Spigot
}
fauxSleeping = true;
spawnIn(this.serverLevel);
moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
this.serverLevel.addNewPlayer(this);
super.setBoundingBox(this.boundingBox);
}
@Override
public UUID getUniqueId() {
return super.getUUID();
}
@Override
public void die() {
discard();
}
@Override
public AABB getBoundingBoxForCulling() {
return this.boundingBox;
}
@Override
public void remove(RemovalReason removalReason) {
if (!dieCall) {
dieCall = true;
this.serverLevel.removePlayerImmediately(this, RemovalReason.UNLOADED_WITH_PLAYER);
dieCall = false;
} else {
super.remove(removalReason);
}
}
@Override
public Location getLocation() {
return getBukkitEntity().getLocation();
}
@Override
public Player getPlayer() {
return getBukkitEntity();
}
@Override
public PlayerAdvancements getAdvancements() {
return this.advancements;
}
public static class DummyConnection extends Connection {
DummyConnection() {
super(PacketFlow.SERVERBOUND);
this.channel = new DummyChannel();
this.address = null;
}
@Override
public void setListener(PacketListener packetListener) {
// Do nothing.
}
}
public class DummyServerGamePacketListenerImpl extends ServerGamePacketListenerImpl {
DummyServerGamePacketListenerImpl(MinecraftServer minecraftServer, ServerPlayer serverPlayer) {
super(minecraftServer, new DummyConnection(), serverPlayer, CommonListenerCookie.createInitial(ChunkLoaderNPCImpl.this.getGameProfile()));
}
@Override
public void handleContainerClick(ServerboundContainerClickPacket containerClickPacket) {
// Do nothing.
}
@Override
public void handleMovePlayer(ServerboundMovePlayerPacket movePlayerPacket) {
// Do nothing.
}
@Override
public void handleSignUpdate(ServerboundSignUpdatePacket signUpdatePacket) {
// Do nothing.
}
@Override
public void handlePlayerAction(ServerboundPlayerActionPacket playerActionPacket) {
// Do nothing.
}
@Override
public void handleUseItem(ServerboundUseItemPacket useItemPacket) {
// Do nothing.
}
@Override
public void handleSetCarriedItem(ServerboundSetCarriedItemPacket setCarriedItemPacket) {
// Do nothing.
}
@Override
public void handleChat(ServerboundChatPacket chatPacket) {
// Do nothing.
}
@Override
public void disconnect(String s) {
// Do nothing.
}
public void send(Packet<?> packet) {
// Do nothing.
}
}
private static class DummyPlayerAdvancements extends PlayerAdvancements {
DummyPlayerAdvancements(MinecraftServer server, ServerPlayer serverPlayer) {
super(server.getFixerUpper(), server.getPlayerList(), server.getAdvancements(),
getAdvancementsFile(server, serverPlayer), serverPlayer);
}
private static Path getAdvancementsFile(MinecraftServer server, ServerPlayer serverPlayer) {
File advancementsDir = server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).toFile();
return new File(advancementsDir, serverPlayer.getUUID() + ".json").toPath();
}
@Override
public void setPlayer(ServerPlayer owner) {
// Do nothing.
}
@Override
public void stopListening() {
// Do nothing.
}
@Override
public void reload(ServerAdvancementManager advancementLoader) {
// Do nothing.
}
@Override
public void save() {
// Do nothing.
}
@Override
public boolean award(AdvancementHolder advancement, String criterionName) {
return false;
}
@Override
public boolean revoke(AdvancementHolder advancement, String criterionName) {
return false;
}
@Override
public void flushDirty(ServerPlayer player) {
// Do nothing.
}
@Override
public void setSelectedTab(@Nullable AdvancementHolder advancement) {
// Do nothing.
}
@Override
public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) {
return new AdvancementProgress();
}
}
}

View File

@ -0,0 +1,165 @@
package com.bgsoftware.wildloaders.nms.v1_20_3;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftArmorStand;
import org.bukkit.craftbukkit.v1_20_R3.entity.CraftEntity;
import org.bukkit.craftbukkit.v1_20_R3.util.CraftChatMessage;
public final class EntityHologram extends ArmorStand implements Hologram {
private static final AABB EMPTY_BOUND = new AABB(0D, 0D, 0D, 0D, 0D, 0D);
private CraftEntity bukkitEntity;
public EntityHologram(ServerLevel serverLevel, double x, double y, double z) {
super(serverLevel, x, y, z);
setInvisible(true);
setSmall(true);
setShowArms(false);
setNoGravity(true);
setNoBasePlate(true);
setMarker(true);
super.collides = false;
super.setCustomNameVisible(true); // Custom name visible
super.setBoundingBox(EMPTY_BOUND);
}
@Override
public void setHologramName(String name) {
super.setCustomName(CraftChatMessage.fromStringOrNull(name));
}
@Override
public void removeHologram() {
super.remove(RemovalReason.DISCARDED);
}
@Override
public org.bukkit.entity.Entity getEntity() {
return getBukkitEntity();
}
@Override
public void tick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void inactiveTick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void addAdditionalSaveData(CompoundTag compoundTag) {
// Do not save NBT.
}
@Override
public boolean saveAsPassenger(CompoundTag compoundTag) {
// Do not save NBT.
return false;
}
@Override
public CompoundTag saveWithoutId(CompoundTag compoundTag) {
// Do not save NBT.
return compoundTag;
}
@Override
public void readAdditionalSaveData(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public void load(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public boolean isInvulnerableTo(DamageSource source) {
/*
* The field Entity.invulnerable is private.
* It's only used while saving NBTTags, but since the entity would be killed
* on chunk unload, we prefer to override isInvulnerable().
*/
return true;
}
@Override
public boolean repositionEntityAfterLoad() {
return false;
}
@Override
public void setCustomName(Component component) {
// Locks the custom name.
}
@Override
public void setCustomNameVisible(boolean visible) {
// Locks the custom name.
}
@Override
public InteractionResult interactAt(Player player, Vec3 hitPos, InteractionHand hand) {
// Prevent stand being equipped
return InteractionResult.PASS;
}
@Override
public void setItemSlot(EquipmentSlot equipmentSlot, ItemStack itemStack, boolean silent) {
// Prevent stand being equipped
}
@Override
public AABB getBoundingBoxForCulling() {
return EMPTY_BOUND;
}
@Override
public void playSound(SoundEvent soundEvent, float volume, float pitch) {
// Remove sounds.
}
@Override
public void remove(RemovalReason removalReason) {
// Prevent being killed.
}
@Override
public CraftEntity getBukkitEntity() {
if (bukkitEntity == null) {
bukkitEntity = new CraftArmorStand((CraftServer) Bukkit.getServer(), this);
}
return bukkitEntity;
}
}

View File

@ -0,0 +1,170 @@
package com.bgsoftware.wildloaders.nms.v1_20_3;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_20_3.loader.ChunkLoaderBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.v1_20_R3.CraftServer;
import org.bukkit.craftbukkit.v1_20_R3.CraftWorld;
import org.bukkit.craftbukkit.v1_20_R3.inventory.CraftItemStack;
import java.util.UUID;
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 8) ? def : compoundTag.getString(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putString(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public long getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getTag();
return compoundTag == null || !compoundTag.contains(key, 4) ? def : compoundTag.getLong(key);
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
compoundTag.putLong(key, value);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack bukkitItem, String texture) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CompoundTag compoundTag = itemStack.getOrCreateTag();
CompoundTag skullOwner = compoundTag.contains("SkullOwner") ?
compoundTag.getCompound("SkullOwner") : new CompoundTag();
CompoundTag properties = new CompoundTag();
ListTag textures = new ListTag();
CompoundTag signature = new CompoundTag();
signature.putString("Value", texture);
textures.add(signature);
properties.put("textures", textures);
skullOwner.put("Properties", properties);
skullOwner.putString("Id", UUID.randomUUID().toString());
compoundTag.put("SkullOwner", skullOwner);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC createNPC(Location location, UUID uuid) {
return new ChunkLoaderNPCImpl(((CraftServer) Bukkit.getServer()).getServer(), location, uuid);
}
@Override
public ITileEntityChunkLoader createLoader(ChunkLoader chunkLoader) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot create loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
ChunkLoaderBlockEntity ChunkLoaderBlockEntity = new ChunkLoaderBlockEntity(chunkLoader, serverLevel, blockPos);
serverLevel.addBlockEntityTicker(ChunkLoaderBlockEntity.getTicker());
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = -1;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, true);
}
return ChunkLoaderBlockEntity;
}
@Override
public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
ChunkLoaderBlockEntity chunkLoaderBlockEntity = ChunkLoaderBlockEntity.chunkLoaderBlockEntityMap.remove(chunkPosLong);
if (chunkLoaderBlockEntity != null) {
chunkLoaderBlockEntity.holograms.forEach(EntityHologram::removeHologram);
chunkLoaderBlockEntity.removed = true;
}
if (spawnParticle)
serverLevel.levelEvent(null, 2001, blockPos, Block.getId(serverLevel.getBlockState(blockPos)));
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = 16;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, false);
}
}
@Override
public void updateSpawner(Location location, boolean reset) {
World bukkitWorld = location.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos);
if (blockEntity instanceof SpawnerBlockEntity spawnerBlockEntity)
spawnerBlockEntity.getSpawner().requiredPlayerRange = reset ? 16 : -1;
}
}

View File

@ -0,0 +1,149 @@
package com.bgsoftware.wildloaders.nms.v1_20_3.loader;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.loaders.WChunkLoader;
import com.bgsoftware.wildloaders.nms.v1_20_3.EntityHologram;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class ChunkLoaderBlockEntity extends BlockEntity implements ITileEntityChunkLoader {
public static final Map<Long, ChunkLoaderBlockEntity> chunkLoaderBlockEntityMap = new HashMap<>();
public final List<EntityHologram> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final ChunkLoaderBlockEntityTicker ticker;
private final ServerLevel serverLevel;
private final BlockPos blockPos;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
public boolean removed = false;
public ChunkLoaderBlockEntity(ChunkLoader chunkLoader, ServerLevel serverLevel, BlockPos blockPos) {
super(BlockEntityType.COMMAND_BLOCK, blockPos, serverLevel.getBlockState(blockPos));
this.chunkLoader = (WChunkLoader) chunkLoader;
this.ticker = new ChunkLoaderBlockEntityTicker(this);
this.blockPos = blockPos;
this.serverLevel = serverLevel;
setLevel(serverLevel);
loaderBlock = serverLevel.getBlockState(blockPos).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
daysAmount = (short) (timeLeft / 86400);
timeLeft = timeLeft % 86400;
hoursAmount = (short) (timeLeft / 3600);
timeLeft = timeLeft % 3600;
minutesAmount = (short) (timeLeft / 60);
timeLeft = timeLeft % 60;
secondsAmount = (short) timeLeft;
}
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
chunkLoaderBlockEntityMap.put(chunkPosLong, this);
List<String> hologramLines = this.chunkLoader.getHologramLines();
double currentY = blockPos.getY() + 1;
for (int i = hologramLines.size(); i > 0; i--) {
EntityHologram hologram = new EntityHologram(serverLevel, blockPos.getX() + 0.5, currentY, blockPos.getZ() + 0.5);
updateName(hologram, hologramLines.get(i - 1));
serverLevel.addFreshEntity(hologram);
currentY += 0.23;
holograms.add(hologram);
}
}
public void tick() {
if (removed || ++currentTick <= 20)
return;
currentTick = 0;
if (chunkLoader.isNotActive() || this.serverLevel.getBlockState(this.blockPos).getBlock() != loaderBlock) {
chunkLoader.remove();
return;
}
if (chunkLoader.isInfinite())
return;
List<String> hologramLines = chunkLoader.getHologramLines();
int hologramsAmount = holograms.size();
for (int i = hologramsAmount; i > 0; i--) {
EntityHologram hologram = holograms.get(hologramsAmount - i);
updateName(hologram, hologramLines.get(i - 1));
}
chunkLoader.tick();
if (!removed) {
secondsAmount--;
if (secondsAmount < 0) {
secondsAmount = 59;
minutesAmount--;
if (minutesAmount < 0) {
minutesAmount = 59;
hoursAmount--;
if (hoursAmount < 0) {
hoursAmount = 23;
daysAmount--;
}
}
}
}
}
@Override
public Collection<Hologram> getHolograms() {
return Collections.unmodifiableList(holograms);
}
@Override
public boolean isRemoved() {
return removed || super.isRemoved();
}
public ChunkLoaderBlockEntityTicker getTicker() {
return ticker;
}
private void updateName(EntityHologram hologram, String line) {
hologram.setHologramName(line
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")
.replace("{4}", secondsAmount + "")
);
}
}

View File

@ -0,0 +1,30 @@
package com.bgsoftware.wildloaders.nms.v1_20_3.loader;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
public record ChunkLoaderBlockEntityTicker(
ChunkLoaderBlockEntity chunkLoaderBlockEntity) implements TickingBlockEntity {
@Override
public void tick() {
chunkLoaderBlockEntity.tick();
}
@Override
public boolean isRemoved() {
return chunkLoaderBlockEntity.isRemoved();
}
@Override
public BlockPos getPos() {
return chunkLoaderBlockEntity.getBlockPos();
}
@Override
public String getType() {
return BlockEntityType.getKey(chunkLoaderBlockEntity.getType()) + "";
}
}

36
NMS/v1_20_4/build.gradle Normal file
View File

@ -0,0 +1,36 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.6.0"
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
group 'NMS:v1_20_4'
dependencies {
paperweightDevelopmentBundle("io.papermc.paper:dev-bundle:1.20.6-R0.1-SNAPSHOT")
compileOnly project(":API")
compileOnly rootProject
}
shadowJar {
archiveFileName = "${project.name}-exclude.jar"
}
assemble {
dependsOn(reobfJar)
}
tasks {
reobfJar {
File outputFile = new File(rootProject.archiveFolder, "reobf/${project.name}.jar")
outputJar.set(layout.buildDirectory.file(outputFile.getPath()))
}
}
if (project.hasProperty('nms.compile_v1_20') && !Boolean.valueOf(project.findProperty("nms.compile_v1_20").toString())) {
project.tasks.all { task -> task.enabled = false }
}

View File

@ -0,0 +1,252 @@
package com.bgsoftware.wildloaders.nms.v1_20_4;
import com.bgsoftware.common.reflection.ReflectMethod;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import com.bgsoftware.wildloaders.handlers.NPCHandler;
import com.bgsoftware.wildloaders.npc.DummyChannel;
import com.mojang.authlib.GameProfile;
import net.minecraft.advancements.AdvancementHolder;
import net.minecraft.advancements.AdvancementProgress;
import net.minecraft.core.BlockPos;
import net.minecraft.network.Connection;
import net.minecraft.network.PacketListener;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.PacketFlow;
import net.minecraft.network.protocol.game.ServerboundChatPacket;
import net.minecraft.network.protocol.game.ServerboundContainerClickPacket;
import net.minecraft.network.protocol.game.ServerboundMovePlayerPacket;
import net.minecraft.network.protocol.game.ServerboundPlayerActionPacket;
import net.minecraft.network.protocol.game.ServerboundSetCarriedItemPacket;
import net.minecraft.network.protocol.game.ServerboundSignUpdatePacket;
import net.minecraft.network.protocol.game.ServerboundUseItemPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerAdvancements;
import net.minecraft.server.ServerAdvancementManager;
import net.minecraft.server.level.ClientInformation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.level.ServerPlayerGameMode;
import net.minecraft.server.network.CommonListenerCookie;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.level.GameType;
import net.minecraft.world.level.storage.LevelResource;
import net.minecraft.world.phys.AABB;
import org.bukkit.Location;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.nio.file.Path;
import java.util.UUID;
public final class ChunkLoaderNPCImpl extends ServerPlayer implements ChunkLoaderNPC {
private static final ReflectMethod<Void> SET_GAMEMODE = new ReflectMethod<>(ServerPlayerGameMode.class,
1, GameType.class, GameType.class);
private final ServerLevel serverLevel;
private final AABB boundingBox;
private final PlayerAdvancements advancements;
private boolean dieCall = false;
public ChunkLoaderNPCImpl(MinecraftServer minecraftServer, Location location, UUID uuid) {
super(minecraftServer, ((CraftWorld) location.getWorld()).getHandle(),
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())),
ClientInformation.createDefault());
this.serverLevel = serverLevel();
this.boundingBox = new AABB(new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ()));
this.connection = new DummyServerGamePacketListenerImpl(minecraftServer, this);
this.advancements = new DummyPlayerAdvancements(minecraftServer, this);
SET_GAMEMODE.invoke(this.gameMode, GameType.CREATIVE, null);
try {
setLoadViewDistance(2);
setTickViewDistance(2);
setSendViewDistance(2);
} catch (Throwable ignored) {
// Doesn't exist on Spigot
}
fauxSleeping = true;
spawnIn(this.serverLevel);
moveTo(location.getX(), location.getY(), location.getZ(), location.getYaw(), location.getPitch());
this.serverLevel.addNewPlayer(this);
super.setBoundingBox(this.boundingBox);
}
@Override
public UUID getUniqueId() {
return super.getUUID();
}
@Override
public void die() {
discard();
}
@Override
public AABB getBoundingBoxForCulling() {
return this.boundingBox;
}
@Override
public void remove(RemovalReason removalReason) {
if (!dieCall) {
dieCall = true;
this.serverLevel.removePlayerImmediately(this, RemovalReason.UNLOADED_WITH_PLAYER);
dieCall = false;
} else {
super.remove(removalReason);
}
}
@Override
public Location getLocation() {
return getBukkitEntity().getLocation();
}
@Override
public Player getPlayer() {
return getBukkitEntity();
}
@Override
public PlayerAdvancements getAdvancements() {
return this.advancements;
}
public static class DummyConnection extends Connection {
DummyConnection() {
super(PacketFlow.SERVERBOUND);
this.channel = new DummyChannel();
this.address = null;
}
@Override
public void setListenerForServerboundHandshake(PacketListener packetListener) {
// Do nothing.
}
}
public class DummyServerGamePacketListenerImpl extends ServerGamePacketListenerImpl {
DummyServerGamePacketListenerImpl(MinecraftServer minecraftServer, ServerPlayer serverPlayer) {
super(minecraftServer, new DummyConnection(), serverPlayer,
CommonListenerCookie.createInitial(ChunkLoaderNPCImpl.this.getGameProfile(), false));
}
@Override
public void handleContainerClick(ServerboundContainerClickPacket containerClickPacket) {
// Do nothing.
}
@Override
public void handleMovePlayer(ServerboundMovePlayerPacket movePlayerPacket) {
// Do nothing.
}
@Override
public void handleSignUpdate(ServerboundSignUpdatePacket signUpdatePacket) {
// Do nothing.
}
@Override
public void handlePlayerAction(ServerboundPlayerActionPacket playerActionPacket) {
// Do nothing.
}
@Override
public void handleUseItem(ServerboundUseItemPacket useItemPacket) {
// Do nothing.
}
@Override
public void handleSetCarriedItem(ServerboundSetCarriedItemPacket setCarriedItemPacket) {
// Do nothing.
}
@Override
public void handleChat(ServerboundChatPacket chatPacket) {
// Do nothing.
}
@Override
public void disconnect(String s) {
// Do nothing.
}
public void send(Packet<?> packet) {
// Do nothing.
}
}
private static class DummyPlayerAdvancements extends PlayerAdvancements {
DummyPlayerAdvancements(MinecraftServer server, ServerPlayer serverPlayer) {
super(server.getFixerUpper(), server.getPlayerList(), server.getAdvancements(),
getAdvancementsFile(server, serverPlayer), serverPlayer);
}
private static Path getAdvancementsFile(MinecraftServer server, ServerPlayer serverPlayer) {
File advancementsDir = server.getWorldPath(LevelResource.PLAYER_ADVANCEMENTS_DIR).toFile();
return new File(advancementsDir, serverPlayer.getUUID() + ".json").toPath();
}
@Override
public void setPlayer(ServerPlayer owner) {
// Do nothing.
}
@Override
public void stopListening() {
// Do nothing.
}
@Override
public void reload(ServerAdvancementManager advancementLoader) {
// Do nothing.
}
@Override
public void save() {
// Do nothing.
}
@Override
public boolean award(AdvancementHolder advancement, String criterionName) {
return false;
}
@Override
public boolean revoke(AdvancementHolder advancement, String criterionName) {
return false;
}
@Override
public void flushDirty(ServerPlayer player) {
// Do nothing.
}
@Override
public void setSelectedTab(@Nullable AdvancementHolder advancement) {
// Do nothing.
}
@Override
public AdvancementProgress getOrStartProgress(AdvancementHolder advancement) {
return new AdvancementProgress();
}
}
}

View File

@ -0,0 +1,165 @@
package com.bgsoftware.wildloaders.nms.v1_20_4;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.decoration.ArmorStand;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.entity.CraftArmorStand;
import org.bukkit.craftbukkit.entity.CraftEntity;
import org.bukkit.craftbukkit.util.CraftChatMessage;
public final class EntityHologram extends ArmorStand implements Hologram {
private static final AABB EMPTY_BOUND = new AABB(0D, 0D, 0D, 0D, 0D, 0D);
private CraftEntity bukkitEntity;
public EntityHologram(ServerLevel serverLevel, double x, double y, double z) {
super(serverLevel, x, y, z);
setInvisible(true);
setSmall(true);
setShowArms(false);
setNoGravity(true);
setNoBasePlate(true);
setMarker(true);
super.collides = false;
super.setCustomNameVisible(true); // Custom name visible
super.setBoundingBox(EMPTY_BOUND);
}
@Override
public void setHologramName(String name) {
super.setCustomName(CraftChatMessage.fromStringOrNull(name));
}
@Override
public void removeHologram() {
super.remove(RemovalReason.DISCARDED);
}
@Override
public org.bukkit.entity.Entity getEntity() {
return getBukkitEntity();
}
@Override
public void tick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void inactiveTick() {
// Disable normal ticking for this entity.
// Workaround to force EntityTrackerEntry to send a teleport packet immediately after spawning this entity.
if (this.onGround) {
this.onGround = false;
}
}
@Override
public void addAdditionalSaveData(CompoundTag compoundTag) {
// Do not save NBT.
}
@Override
public boolean saveAsPassenger(CompoundTag compoundTag) {
// Do not save NBT.
return false;
}
@Override
public CompoundTag saveWithoutId(CompoundTag compoundTag) {
// Do not save NBT.
return compoundTag;
}
@Override
public void readAdditionalSaveData(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public void load(CompoundTag compoundTag) {
// Do not load NBT.
}
@Override
public boolean isInvulnerableTo(DamageSource source) {
/*
* The field Entity.invulnerable is private.
* It's only used while saving NBTTags, but since the entity would be killed
* on chunk unload, we prefer to override isInvulnerable().
*/
return true;
}
@Override
public boolean repositionEntityAfterLoad() {
return false;
}
@Override
public void setCustomName(Component component) {
// Locks the custom name.
}
@Override
public void setCustomNameVisible(boolean visible) {
// Locks the custom name.
}
@Override
public InteractionResult interactAt(Player player, Vec3 hitPos, InteractionHand hand) {
// Prevent stand being equipped
return InteractionResult.PASS;
}
@Override
public void setItemSlot(EquipmentSlot equipmentSlot, ItemStack itemStack, boolean silent) {
// Prevent stand being equipped
}
@Override
public AABB getBoundingBoxForCulling() {
return EMPTY_BOUND;
}
@Override
public void playSound(SoundEvent soundEvent, float volume, float pitch) {
// Remove sounds.
}
@Override
public void remove(RemovalReason removalReason) {
// Prevent being killed.
}
@Override
public CraftEntity getBukkitEntity() {
if (bukkitEntity == null) {
bukkitEntity = new CraftArmorStand((CraftServer) Bukkit.getServer(), this);
}
return bukkitEntity;
}
}

View File

@ -0,0 +1,176 @@
package com.bgsoftware.wildloaders.nms.v1_20_4;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_20_4.loader.ChunkLoaderBlockEntity;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.item.component.ResolvableProfile;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.SpawnerBlockEntity;
import net.minecraft.world.level.chunk.LevelChunk;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.craftbukkit.CraftServer;
import org.bukkit.craftbukkit.CraftWorld;
import org.bukkit.craftbukkit.inventory.CraftItemStack;
import java.util.Optional;
import java.util.UUID;
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CustomData customData = itemStack.get(DataComponents.CUSTOM_DATA);
if (customData != null) {
CompoundTag compoundTag = customData.getUnsafe();
if (compoundTag.contains(key, 8))
return compoundTag.getString(key);
}
return def;
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, String value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CustomData customData = itemStack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
customData = customData.update(compoundTag -> compoundTag.putString(key, value));
itemStack.set(DataComponents.CUSTOM_DATA, customData);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public long getTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long def) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CustomData customData = itemStack.get(DataComponents.CUSTOM_DATA);
if (customData != null) {
CompoundTag compoundTag = customData.getUnsafe();
if (compoundTag.contains(key, 4))
return compoundTag.getLong(key);
}
return def;
}
@Override
public org.bukkit.inventory.ItemStack setTag(org.bukkit.inventory.ItemStack bukkitItem, String key, long value) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
CustomData customData = itemStack.getOrDefault(DataComponents.CUSTOM_DATA, CustomData.EMPTY);
customData = customData.update(compoundTag -> compoundTag.putLong(key, value));
itemStack.set(DataComponents.CUSTOM_DATA, customData);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public org.bukkit.inventory.ItemStack getPlayerSkull(org.bukkit.inventory.ItemStack bukkitItem, String texture) {
ItemStack itemStack = CraftItemStack.asNMSCopy(bukkitItem);
PropertyMap propertyMap = new PropertyMap();
propertyMap.put("textures", new Property("textures", texture));
ResolvableProfile resolvableProfile = new ResolvableProfile(Optional.empty(), Optional.empty(), propertyMap);
itemStack.set(DataComponents.PROFILE, resolvableProfile);
return CraftItemStack.asBukkitCopy(itemStack);
}
@Override
public com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC createNPC(Location location, UUID uuid) {
return new ChunkLoaderNPCImpl(((CraftServer) Bukkit.getServer()).getServer(), location, uuid);
}
@Override
public ITileEntityChunkLoader createLoader(ChunkLoader chunkLoader) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot create loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
ChunkLoaderBlockEntity ChunkLoaderBlockEntity = new ChunkLoaderBlockEntity(chunkLoader, serverLevel, blockPos);
serverLevel.addBlockEntityTicker(ChunkLoaderBlockEntity.getTicker());
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = -1;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, true);
}
return ChunkLoaderBlockEntity;
}
@Override
public void removeLoader(ChunkLoader chunkLoader, boolean spawnParticle) {
Location loaderLoc = chunkLoader.getLocation();
World bukkitWorld = loaderLoc.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(loaderLoc.getBlockX(), loaderLoc.getBlockY(), loaderLoc.getBlockZ());
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
ChunkLoaderBlockEntity chunkLoaderBlockEntity = ChunkLoaderBlockEntity.chunkLoaderBlockEntityMap.remove(chunkPosLong);
if (chunkLoaderBlockEntity != null) {
chunkLoaderBlockEntity.holograms.forEach(EntityHologram::removeHologram);
chunkLoaderBlockEntity.removed = true;
}
if (spawnParticle)
serverLevel.levelEvent(null, 2001, blockPos, Block.getId(serverLevel.getBlockState(blockPos)));
for (org.bukkit.Chunk bukkitChunk : chunkLoader.getLoadedChunks()) {
LevelChunk levelChunk = serverLevel.getChunk(bukkitChunk.getX(), bukkitChunk.getZ());
levelChunk.getBlockEntities().values().stream()
.filter(blockEntity -> blockEntity instanceof SpawnerBlockEntity)
.forEach(blockEntity -> {
((SpawnerBlockEntity) blockEntity).getSpawner().requiredPlayerRange = 16;
});
ChunkPos chunkPos = levelChunk.getPos();
serverLevel.setChunkForced(chunkPos.x, chunkPos.z, false);
}
}
@Override
public void updateSpawner(Location location, boolean reset) {
World bukkitWorld = location.getWorld();
if (bukkitWorld == null)
throw new IllegalArgumentException("Cannot remove loader in null world.");
ServerLevel serverLevel = ((CraftWorld) bukkitWorld).getHandle();
BlockPos blockPos = new BlockPos(location.getBlockX(), location.getBlockY(), location.getBlockZ());
BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos);
if (blockEntity instanceof SpawnerBlockEntity spawnerBlockEntity)
spawnerBlockEntity.getSpawner().requiredPlayerRange = reset ? 16 : -1;
}
}

View File

@ -0,0 +1,149 @@
package com.bgsoftware.wildloaders.nms.v1_20_4.loader;
import com.bgsoftware.wildloaders.api.holograms.Hologram;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.loaders.WChunkLoader;
import com.bgsoftware.wildloaders.nms.v1_20_4.EntityHologram;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class ChunkLoaderBlockEntity extends BlockEntity implements ITileEntityChunkLoader {
public static final Map<Long, ChunkLoaderBlockEntity> chunkLoaderBlockEntityMap = new HashMap<>();
public final List<EntityHologram> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final ChunkLoaderBlockEntityTicker ticker;
private final ServerLevel serverLevel;
private final BlockPos blockPos;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
public boolean removed = false;
public ChunkLoaderBlockEntity(ChunkLoader chunkLoader, ServerLevel serverLevel, BlockPos blockPos) {
super(BlockEntityType.COMMAND_BLOCK, blockPos, serverLevel.getBlockState(blockPos));
this.chunkLoader = (WChunkLoader) chunkLoader;
this.ticker = new ChunkLoaderBlockEntityTicker(this);
this.blockPos = blockPos;
this.serverLevel = serverLevel;
setLevel(serverLevel);
loaderBlock = serverLevel.getBlockState(blockPos).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
daysAmount = (short) (timeLeft / 86400);
timeLeft = timeLeft % 86400;
hoursAmount = (short) (timeLeft / 3600);
timeLeft = timeLeft % 3600;
minutesAmount = (short) (timeLeft / 60);
timeLeft = timeLeft % 60;
secondsAmount = (short) timeLeft;
}
long chunkPosLong = ChunkPos.asLong(blockPos.getX() >> 4, blockPos.getZ() >> 4);
chunkLoaderBlockEntityMap.put(chunkPosLong, this);
List<String> hologramLines = this.chunkLoader.getHologramLines();
double currentY = blockPos.getY() + 1;
for (int i = hologramLines.size(); i > 0; i--) {
EntityHologram hologram = new EntityHologram(serverLevel, blockPos.getX() + 0.5, currentY, blockPos.getZ() + 0.5);
updateName(hologram, hologramLines.get(i - 1));
serverLevel.addFreshEntity(hologram);
currentY += 0.23;
holograms.add(hologram);
}
}
public void tick() {
if (removed || ++currentTick <= 20)
return;
currentTick = 0;
if (chunkLoader.isNotActive() || this.serverLevel.getBlockState(this.blockPos).getBlock() != loaderBlock) {
chunkLoader.remove();
return;
}
if (chunkLoader.isInfinite())
return;
List<String> hologramLines = chunkLoader.getHologramLines();
int hologramsAmount = holograms.size();
for (int i = hologramsAmount; i > 0; i--) {
EntityHologram hologram = holograms.get(hologramsAmount - i);
updateName(hologram, hologramLines.get(i - 1));
}
chunkLoader.tick();
if (!removed) {
secondsAmount--;
if (secondsAmount < 0) {
secondsAmount = 59;
minutesAmount--;
if (minutesAmount < 0) {
minutesAmount = 59;
hoursAmount--;
if (hoursAmount < 0) {
hoursAmount = 23;
daysAmount--;
}
}
}
}
}
@Override
public Collection<Hologram> getHolograms() {
return Collections.unmodifiableList(holograms);
}
@Override
public boolean isRemoved() {
return removed || super.isRemoved();
}
public ChunkLoaderBlockEntityTicker getTicker() {
return ticker;
}
private void updateName(EntityHologram hologram, String line) {
hologram.setHologramName(line
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")
.replace("{4}", secondsAmount + "")
);
}
}

View File

@ -0,0 +1,30 @@
package com.bgsoftware.wildloaders.nms.v1_20_4.loader;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.entity.TickingBlockEntity;
public record ChunkLoaderBlockEntityTicker(
ChunkLoaderBlockEntity chunkLoaderBlockEntity) implements TickingBlockEntity {
@Override
public void tick() {
chunkLoaderBlockEntity.tick();
}
@Override
public boolean isRemoved() {
return chunkLoaderBlockEntity.isRemoved();
}
@Override
public BlockPos getPos() {
return chunkLoaderBlockEntity.getBlockPos();
}
@Override
public String getType() {
return BlockEntityType.getKey(chunkLoaderBlockEntity.getType()) + "";
}
}

View File

@ -1,9 +1,9 @@
group 'v1_7_R4'
group 'NMS:v1_7_R4'
dependencies {
compileOnly "org.spigotmc:v1_7_R4:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('nms.compile_v1_7') && !Boolean.valueOf(project.findProperty("nms.compile_v1_7").toString())) {

View File

@ -18,6 +18,7 @@ import net.minecraft.server.v1_7_R4.PacketPlayInUpdateSign;
import net.minecraft.server.v1_7_R4.PacketPlayInWindowClick;
import net.minecraft.server.v1_7_R4.PlayerConnection;
import net.minecraft.server.v1_7_R4.PlayerInteractManager;
import net.minecraft.server.v1_7_R4.Statistic;
import net.minecraft.server.v1_7_R4.WorldServer;
import net.minecraft.util.com.mojang.authlib.GameProfile;
import org.bukkit.Bukkit;
@ -74,6 +75,16 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
return false;
}
@Override
public void a(Statistic statistic, int i) {
// Prevent achievements from being given to NPCs.
}
@Override
public void a(Statistic statistic) {
// Prevent achievements from being given to NPCs.
}
@Override
public Player getPlayer() {
return getBukkitEntity();

View File

@ -1,8 +1,8 @@
package com.bgsoftware.wildloaders.nms.v1_7_R4;
import com.bgsoftware.wildloaders.WildLoadersPlugin;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_7_R4.loader.TileEntityChunkLoader;
import net.minecraft.server.v1_7_R4.Block;
import net.minecraft.server.v1_7_R4.Chunk;
@ -22,15 +22,7 @@ import org.bukkit.craftbukkit.v1_7_R4.util.LongHash;
import java.util.UUID;
@SuppressWarnings("unused")
public final class NMSAdapter implements com.bgsoftware.wildloaders.nms.NMSAdapter {
private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin();
@Override
public boolean isMappingsSupported() {
return true;
}
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) {

View File

@ -1,9 +1,9 @@
group 'v1_8_R3'
group 'NMS:v1_8_R3'
dependencies {
compileOnly "org.spigotmc:v1_8_R3-Taco:latest"
compileOnly project(":API")
compileOnly parent
compileOnly rootProject
}
if (project.hasProperty('nms.compile_v1_8') && !Boolean.valueOf(project.findProperty("nms.compile_v1_8").toString())) {

View File

@ -20,6 +20,7 @@ import net.minecraft.server.v1_8_R3.PacketPlayInUpdateSign;
import net.minecraft.server.v1_8_R3.PacketPlayInWindowClick;
import net.minecraft.server.v1_8_R3.PlayerConnection;
import net.minecraft.server.v1_8_R3.PlayerInteractManager;
import net.minecraft.server.v1_8_R3.Statistic;
import net.minecraft.server.v1_8_R3.WorldServer;
import net.minecraft.server.v1_8_R3.WorldSettings;
import org.bukkit.Bukkit;
@ -33,7 +34,7 @@ import java.util.UUID;
public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC {
private static final AxisAlignedBB EMPTY_BOUND = new AxisAlignedBB(0D, 0D, 0D, 0D, 0D, 0D);
private final AxisAlignedBB boundingBox;
public ChunkLoaderNPC(Location location, UUID uuid) {
super(((CraftServer) Bukkit.getServer()).getServer(),
@ -41,6 +42,9 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
new GameProfile(uuid, NPCHandler.getName(location.getWorld().getName())),
new PlayerInteractManager(((CraftWorld) location.getWorld()).getHandle()));
this.boundingBox = new AxisAlignedBB(location.getX(), location.getY(), location.getZ(),
location.getX() + 1, location.getY() + 1, location.getZ() + 1);
playerConnection = new DummyPlayerConnection(server, this);
playerInteractManager.setGameMode(WorldSettings.EnumGamemode.CREATIVE);
@ -60,7 +64,7 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
world.players.add(this);
((WorldServer) world).getPlayerChunkMap().addPlayer(this);
super.a(EMPTY_BOUND);
super.a(this.boundingBox);
}
@Override
@ -70,7 +74,9 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
@Override
public AxisAlignedBB getBoundingBox() {
return EMPTY_BOUND;
// boundingBox is null on initialization of the class, which fixes:
// https://github.com/BG-Software-LLC/WildLoaders/issues/61
return this.boundingBox == null ? super.getBoundingBox() : this.boundingBox;
}
@Override
@ -90,6 +96,16 @@ public final class ChunkLoaderNPC extends EntityPlayer implements com.bgsoftware
return false;
}
@Override
public void a(Statistic statistic, int i) {
// Prevent achievements from being given to NPCs.
}
@Override
public void a(Statistic statistic) {
// Prevent achievements from being given to NPCs.
}
@Override
public Player getPlayer() {
return getBukkitEntity();

View File

@ -1,8 +1,8 @@
package com.bgsoftware.wildloaders.nms.v1_8_R3;
import com.bgsoftware.wildloaders.WildLoadersPlugin;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.loaders.ITileEntityChunkLoader;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.nms.v1_8_R3.loader.TileEntityChunkLoader;
import net.minecraft.server.v1_8_R3.Block;
import net.minecraft.server.v1_8_R3.BlockPosition;
@ -22,15 +22,7 @@ import org.bukkit.craftbukkit.v1_8_R3.util.LongHash;
import java.util.UUID;
@SuppressWarnings("unused")
public final class NMSAdapter implements com.bgsoftware.wildloaders.nms.NMSAdapter {
private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin();
@Override
public boolean isMappingsSupported() {
return true;
}
public final class NMSAdapterImpl implements NMSAdapter {
@Override
public String getTag(org.bukkit.inventory.ItemStack itemStack, String key, String def) {

View File

@ -18,6 +18,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public final class TileEntityChunkLoader extends TileEntity implements IUpdatePlayerListBox, ITileEntityChunkLoader {
@ -26,6 +27,7 @@ public final class TileEntityChunkLoader extends TileEntity implements IUpdatePl
public final List<EntityHolograms> holograms = new ArrayList<>();
private final WChunkLoader chunkLoader;
private final Block loaderBlock;
private final String cachedPlacerName;
private short currentTick = 20;
private short daysAmount, hoursAmount, minutesAmount, secondsAmount;
@ -39,6 +41,8 @@ public final class TileEntityChunkLoader extends TileEntity implements IUpdatePl
loaderBlock = world.getType(blockPosition).getBlock();
this.cachedPlacerName = Optional.ofNullable(this.chunkLoader.getWhoPlaced().getName()).orElse("");
if (!this.chunkLoader.isInfinite()) {
long timeLeft = chunkLoader.getTimeLeft();
@ -117,7 +121,7 @@ public final class TileEntityChunkLoader extends TileEntity implements IUpdatePl
private void updateName(EntityHolograms hologram, String line) {
hologram.setHologramName(line
.replace("{0}", chunkLoader.getWhoPlaced().getName())
.replace("{0}", this.cachedPlacerName)
.replace("{1}", daysAmount + "")
.replace("{2}", hoursAmount + "")
.replace("{3}", minutesAmount + "")

View File

@ -6,7 +6,7 @@
<p align="center">
<a href="https://bg-software.com/discord/"><img src="https://img.shields.io/discord/293212540723396608?color=7289DA&label=Discord&logo=discord&logoColor=7289DA&link=https://bg-software.com/discord/"></a>
<a href="https://bg-software.com/patreon/"><img src="https://img.shields.io/badge/-Support_on_Patreon-F96854.svg?logo=patreon&style=flat&logoColor=white&link=https://bg-software.com/patreon/"></a><br>
<a href=""><img src="https://img.shields.io/maintenance/yes/2022"></a>
<a href=""><img src="https://img.shields.io/maintenance/yes/2024"></a>
<a href="https://www.codacy.com/gh/BG-Software-LLC/WildLoaders/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=BG-Software-LLC/WildLoaders&amp;utm_campaign=Badge_Grade"><img src="https://app.codacy.com/project/badge/Grade/cbf4b6dc2e73440c941d021f1255cc95"/></a>
</p>
@ -16,19 +16,6 @@ You can compile the project using gradlew.<br>
Run `gradlew build` in console to build the project.<br>
You can find already compiled jars on our [Jenkins](https://hub.bg-software.com/) hub!<br>
When compiling you will receive errors about missing dependencies.<br>
These dependencies are premium plugins that cannot be published on a public repository.<br>
You can do either of the followings in order to solve it:
- Add manually all the jar files of the premium plugins.
- Purchase access to our private repository.
- Disabling compiling of the modules of these dependencies in the `gradle.properties` file.
<br>
##### Private Jars:
- EpicSpawners by Songoda [[link]](https://songoda.com/marketplace/product/13)
## API
The plugin is packed with a rich API for interacting with chunk loaders and more. When hooking into the plugin, it's highly recommended to only use the API and not the compiled plugin, as the API methods are not only commented, but also will not get removed or changed unless they are marked as deprecated. This means that when using the API, you won't have to do any additional changes to your code between updates.

View File

@ -1,41 +1,51 @@
plugins {
id 'java'
id 'com.github.johnrengelman.shadow' version '7.0.0'
id 'io.github.goooler.shadow' version '8.1.7'
id 'maven-publish'
}
group 'WildLoaders'
version = "1.2.1"
version = "2024.1"
project.ext {
archiveFolder = file("archive/")
targetFolder = file("target/")
buildVersion = System.getenv("BUILD_NUMBER") == null || Boolean.parseBoolean(System.getenv("STABLE_BUILD")) ?
version : version + "-b" + System.getenv("BUILD_NUMBER")
}
allprojects {
apply plugin: 'java'
apply plugin: 'com.github.johnrengelman.shadow'
apply plugin: 'io.github.goooler.shadow'
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}
repositories {
maven { url 'https://repo.bg-software.com/repository/nms/' }
maven { url 'https://repo.bg-software.com/repository/api/' }
maven { url 'https://repo.bg-software.com/repository/common/' }
maven { url 'https://repo.bg-software.com/repository/public-libs/' }
String mavenUsername = System.getenv('mavenUsername') == null ? project.mavenUsername : System.getenv('mavenUsername');
String mavenPassword = System.getenv('mavenPassword') == null ? project.mavenUsername : System.getenv('mavenPassword');
if (mavenUsername != null && mavenPassword != null) {
maven {
url 'https://repo.bg-software.com/repository/private-libs/'
credentials {
username mavenUsername
password mavenPassword
}
}
}
maven { url 'https://repo.bg-software.com/repository/dependencies/' }
}
dependencies {
compileOnly "com.bgsoftware.common.reflection:ReflectionUtils:latest"
compileOnly "com.bgsoftware.common.reflection:ReflectionUtils:b5"
}
task checkDebug() {
Set<File> filesWithDebug = fileTree('src/main/java').filter { file ->
file.text.contains('Bukkit.broadcastMessage')
}.getFiles()
if(!filesWithDebug.isEmpty())
throw new GradleException("Found debug messages: " + filesWithDebug)
}
build {
dependsOn checkDebug
dependsOn shadowJar
}
}
@ -43,16 +53,25 @@ allprojects {
subprojects {
shadowJar {
archiveFileName = "${project.name}.jar"
destinationDirectory = file("../archive/")
destinationDirectory = rootProject.archiveFolder
}
}
dependencies {
implementation project(":API")
implementation fileTree("archive")
repositories {
mavenCentral()
}
implementation 'com.bgsoftware.common.reflection:ReflectionUtils:latest'
implementation 'com.bgsoftware.common.config:CommentedConfiguration:1.0.3'
dependencies {
implementation fileTree(rootProject.archiveFolder.getAbsolutePath())
implementation project(":API")
implementation 'com.bgsoftware.common.reflection:ReflectionUtils:b5'
implementation 'com.bgsoftware.common.updater:Updater:b1'
implementation 'com.bgsoftware.common.config:CommentedConfiguration:b1'
implementation 'com.bgsoftware.common.dependencies:DependenciesManager:b2'
implementation 'com.bgsoftware.common.nmsloader:NMSLoader:b4'
implementation 'org.bstats:bstats-bukkit:3.0.0'
// Spigot jars
compileOnly "org.spigotmc:v1_8_R3:latest"
@ -61,19 +80,18 @@ dependencies {
jar {
from {
for (File file : new File("archive").listFiles()) {
for (File file : rootProject.archiveFolder.listFiles()) {
zipTree(file)
}
}
}
processResources {
outputs.upToDateWhen {false}
String versionNumber = System.getenv("BUILD_NUMBER") == null ? version : version + "." + System.getenv("BUILD_NUMBER")
outputs.upToDateWhen { false }
eachFile { details ->
if (details.name.contentEquals('plugin.yml')) {
filter { String line ->
line.replace('${project.version}', versionNumber)
line.replace('${project.version}', rootProject.buildVersion)
}
}
}
@ -82,30 +100,28 @@ processResources {
shadowJar {
dependsOn(jar)
if(System.getenv("BUILD_NUMBER") == null){
archiveName = rootProject.name + "-" + version + ".jar"
}
else{
archiveName = rootProject.name + "-" + version + "-b" + System.getenv("BUILD_NUMBER") + ".jar"
}
relocate 'org.bstats', 'com.bgsoftware.wildloaders.libs.org.bstats'
relocate 'com.bgsoftware.common', 'com.bgsoftware.wildloaders.libs.com.bgsoftware.common'
delete fileTree('./target/') {
exclude archiveName
}
archiveFileName = rootProject.name + "-" + rootProject.buildVersion + ".jar"
destinationDir = file("./target/")
delete fileTree(rootProject.targetFolder.getAbsolutePath())
exclude '*exclude.jar'
destinationDirectory = rootProject.targetFolder
from sourceSets.getByName("main").output
configurations = [project.configurations.getByName("runtimeClasspath")]
}
task copyAPI(type: Copy) {
from './archive/API.jar'
into './target/'
task copyAPI(type: Copy, dependsOn: ':API:build') {
from rootProject.archiveFolder.getAbsolutePath() + '/API.jar'
into rootProject.targetFolder.getAbsolutePath()
rename('API.jar', rootProject.name + 'API.jar')
}
clean {
delete file('./archive/')
delete rootProject.archiveFolder
}
build {
@ -116,4 +132,8 @@ build {
publish.shouldRunAfter shadowJar
shadowJar.shouldRunAfter build
build.shouldRunAfter subprojects.build
build.shouldRunAfter subprojects.build
clean.shouldRunAfter copyAPI
shadowJar.dependsOn subprojects.build
compileJava.dependsOn childProjects.values().shadowJar

View File

@ -1,7 +1,3 @@
# Maven related settings
# Used to access to the private-repository
mavenUsername=''
mavenPassword=''
# Compiling settings
# You can turn-off compiling of specific modules here by setting them to `false`
nms.compile_v1_7=true
@ -11,6 +7,7 @@ nms.compile_v1_16=true
nms.compile_v1_17=true
nms.compile_v1_18=true
nms.compile_v1_19=true
nms.compile_v1_20=true
hook.compile_epicspawners6=true
hook.compile_epicspawners7=true
hook.compile_factionsuuid=true

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1,19 +1,32 @@
rootProject.name = 'WildLoaders'
include 'API'
include 'Hook_EpicSpawners6'
include 'Hook_EpicSpawners7'
include 'Hook_FactionsUUID'
include 'Hook_FactionsX'
include 'Hook_MassiveFactions'
include 'Hook_SuperiorSkyblock'
include 'v1_7_R4'
include 'v1_8_R3'
include 'v1_12_R1'
include 'v1_16_R3'
include 'v1_17_R1'
include 'v1_18_R1'
include 'Hook_EpicSpawners7'
include 'v1_18_R2'
include 'Hook_Lands'
include 'v1_19_R1'
pluginManagement {
repositories {
gradlePluginPortal()
maven { url "https://repo.papermc.io/repository/maven-public/" }
}
}
rootProject.name = 'WildLoaders'
include 'API'
include 'Hooks'
include 'Hooks:EpicSpawners6'
include 'Hooks:EpicSpawners7'
include 'Hooks:EpicSpawners8'
include 'Hooks:FactionsUUID'
include 'Hooks:FactionsX'
include 'Hooks:Lands'
include 'Hooks:MassiveFactions'
include 'Hooks:SuperiorSkyblock'
include 'NMS'
include 'NMS:v1_7_R4'
include 'NMS:v1_8_R3'
include 'NMS:v1_12_R1'
include 'NMS:v1_16_R3'
include 'NMS:v1_17'
include 'NMS:v1_18'
include 'NMS:v1_19'
include 'NMS:v1_20_1'
include 'NMS:v1_20_2'
include 'NMS:v1_20_3'
include 'NMS:v1_20_4'

View File

@ -1,71 +0,0 @@
package com.bgsoftware.wildloaders;
import javax.net.ssl.HttpsURLConnection;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URL;
public final class Updater {
private static final WildLoadersPlugin plugin = WildLoadersPlugin.getPlugin();
private static String latestVersion, versionDescription;
static{
setLatestVersion();
}
//Just so no one would be able to call the constructor
private Updater(){}
public static boolean isOutdated(){
return !plugin.getDescription().getVersion().startsWith(latestVersion);
}
public static String getLatestVersion(){
return latestVersion;
}
static String getVersionDescription(){
return versionDescription;
}
private static void setLatestVersion(){
try {
HttpsURLConnection connection = (HttpsURLConnection) new URL("https://bg-software.com/versions.json").openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-GB; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13 (.NET CLR 3.5.30729)");
connection.setDoInput(true);
try(InputStream reader = connection.getInputStream()){
Class<?> jsonObjectClass, gsonClass;
try{
jsonObjectClass = Class.forName("net.minecraft.util.com.google.gson.JsonObject");
gsonClass = Class.forName("net.minecraft.util.com.google.gson.Gson");
}catch(ClassNotFoundException ex){
jsonObjectClass = Class.forName("com.google.gson.JsonObject");
gsonClass = Class.forName("com.google.gson.Gson");
}
Object jsonObject = gsonClass.getMethod("fromJson", Reader.class, Class.class)
.invoke(gsonClass.newInstance(), new InputStreamReader(reader), jsonObjectClass);
Object jsonElement = jsonObjectClass.getMethod("get", String.class).invoke(jsonObject, "wildloaders");
Object plugin = jsonElement.getClass().getMethod("getAsJsonObject").invoke(jsonElement);
Object versionElement = plugin.getClass().getMethod("get", String.class).invoke(plugin, "version");
Object descriptionElement = plugin.getClass().getMethod("get", String.class).invoke(plugin, "description");
latestVersion = (String) versionElement.getClass().getMethod("getAsString").invoke(versionElement);
versionDescription = (String) descriptionElement.getClass().getMethod("getAsString").invoke(descriptionElement);
}
} catch(Exception ex){
//Something went wrong...
latestVersion = plugin.getDescription().getVersion();
}
}
}

View File

@ -1,5 +1,10 @@
package com.bgsoftware.wildloaders;
import com.bgsoftware.common.dependencies.DependenciesManager;
import com.bgsoftware.common.nmsloader.INMSLoader;
import com.bgsoftware.common.nmsloader.NMSHandlersFactory;
import com.bgsoftware.common.nmsloader.NMSLoadException;
import com.bgsoftware.common.updater.Updater;
import com.bgsoftware.wildloaders.api.WildLoaders;
import com.bgsoftware.wildloaders.api.WildLoadersAPI;
import com.bgsoftware.wildloaders.command.CommandsHandler;
@ -11,9 +16,9 @@ import com.bgsoftware.wildloaders.handlers.SettingsHandler;
import com.bgsoftware.wildloaders.listeners.BlocksListener;
import com.bgsoftware.wildloaders.listeners.ChunksListener;
import com.bgsoftware.wildloaders.listeners.PlayersListener;
import com.bgsoftware.wildloaders.metrics.Metrics;
import com.bgsoftware.wildloaders.nms.NMSAdapter;
import com.bgsoftware.wildloaders.utils.database.Database;
import org.bstats.bukkit.Metrics;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
@ -21,6 +26,8 @@ import java.lang.reflect.Field;
public final class WildLoadersPlugin extends JavaPlugin implements WildLoaders {
private final Updater updater = new Updater(this, "wildloaders");
private static WildLoadersPlugin plugin;
private SettingsHandler settingsHandler;
@ -36,9 +43,12 @@ public final class WildLoadersPlugin extends JavaPlugin implements WildLoaders {
@Override
public void onLoad() {
plugin = this;
new Metrics(this);
loadNMSAdapter();
DependenciesManager.inject(this);
new Metrics(this, 21732);
shouldEnable = loadNMSAdapter();
loadAPI();
if (!shouldEnable)
@ -70,10 +80,10 @@ public final class WildLoadersPlugin extends JavaPlugin implements WildLoaders {
Locale.reload();
if (Updater.isOutdated()) {
if (updater.isOutdated()) {
log("");
log("A new version is available (v" + Updater.getLatestVersion() + ")!");
log("Version's description: \"" + Updater.getVersionDescription() + "\"");
log("A new version is available (v" + updater.getLatestVersion() + ")!");
log("Version's description: \"" + updater.getVersionDescription() + "\"");
log("");
}
@ -89,18 +99,18 @@ public final class WildLoadersPlugin extends JavaPlugin implements WildLoaders {
}
}
private void loadNMSAdapter() {
String version = getServer().getClass().getPackage().getName().split("\\.")[3];
private boolean loadNMSAdapter() {
try {
nmsAdapter = (NMSAdapter) Class.forName(String.format("com.bgsoftware.wildloaders.nms.%s.NMSAdapter", version)).newInstance();
if (!nmsAdapter.isMappingsSupported()) {
log("WildLoaders does not support your version mappings... Please contact @Ome_R");
shouldEnable = false;
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
shouldEnable = false;
log("Couldn't load up with an adapter " + version + ". Please contact @Ome_R");
getServer().getPluginManager().disablePlugin(this);
INMSLoader nmsLoader = NMSHandlersFactory.createNMSLoader(this);
this.nmsAdapter = nmsLoader.loadNMSHandler(NMSAdapter.class);
return true;
} catch (NMSLoadException error) {
log("&cThe plugin doesn't support your minecraft version.");
log("&cPlease try a different version.");
error.printStackTrace();
return false;
}
}
@ -143,6 +153,10 @@ public final class WildLoadersPlugin extends JavaPlugin implements WildLoaders {
return dataHandler;
}
public Updater getUpdater() {
return updater;
}
public static void log(String message) {
plugin.getLogger().info(message);
}

View File

@ -76,7 +76,7 @@ public final class CmdGive implements ICommand {
int amount = 1;
if(args.length == 4){
if(args.length >= 4){
try{
amount = Integer.parseInt(args[3]);
} catch (IllegalArgumentException e){

View File

@ -2,15 +2,18 @@ package com.bgsoftware.wildloaders.handlers;
import com.bgsoftware.wildloaders.WildLoadersPlugin;
import com.bgsoftware.wildloaders.api.loaders.LoaderData;
import com.bgsoftware.wildloaders.utils.ChunkLoaderChunks;
import com.bgsoftware.wildloaders.utils.ServerVersion;
import com.bgsoftware.wildloaders.utils.database.Database;
import com.bgsoftware.wildloaders.utils.locations.LocationUtils;
import com.bgsoftware.wildloaders.utils.threads.Executor;
import org.bukkit.Bukkit;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.Material;
import java.io.File;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
@ -18,20 +21,20 @@ public final class DataHandler {
private final WildLoadersPlugin plugin;
public DataHandler(WildLoadersPlugin plugin){
public DataHandler(WildLoadersPlugin plugin) {
this.plugin = plugin;
Executor.sync(() -> {
try {
Database.start(new File(plugin.getDataFolder(), "database.db"));
loadDatabase();
}catch (Exception ex){
} catch (Exception ex) {
ex.printStackTrace();
Bukkit.getPluginManager().disablePlugin(plugin);
}
}, 2L);
}
public void loadDatabase(){
public void loadDatabase() {
Database.executeUpdate("CREATE TABLE IF NOT EXISTS npc_identifiers (location TEXT NOT NULL PRIMARY KEY, uuid TEXT NOT NULL);");
Database.executeQuery("SELECT * FROM npc_identifiers;", resultSet -> {
while (resultSet.next()) {
@ -43,27 +46,30 @@ public final class DataHandler {
Database.executeUpdate("CREATE TABLE IF NOT EXISTS chunk_loaders (location TEXT NOT NULL PRIMARY KEY, placer TEXT NOT NULL, loader_data TEXT NOT NULL, timeLeft BIGINT NOT NULL);");
Database.executeQuery("SELECT * FROM chunk_loaders;", resultSet -> {
while(resultSet.next()){
while (resultSet.next()) {
Location location = LocationUtils.getLocation(resultSet.getString("location"));
UUID placer = UUID.fromString(resultSet.getString("placer"));
Optional<LoaderData> loaderData = plugin.getLoaders().getLoaderData(resultSet.getString("loader_data"));
long timeLeft = resultSet.getLong("timeLeft");
if(!loaderData.isPresent())
if (!loaderData.isPresent())
continue;
Material blockType = location.getBlock().getType();
if(ServerVersion.isLegacy() && blockType == Material.CAULDRON){
if (ServerVersion.isLegacy() && blockType == Material.CAULDRON) {
blockType = Material.CAULDRON_ITEM;
}
if(blockType != loaderData.get().getLoaderItem().getType()){
if (blockType != loaderData.get().getLoaderItem().getType()) {
WildLoadersPlugin.log("The chunk-loader at " + LocationUtils.getLocation(location) + " is invalid.");
continue;
}
plugin.getLoaders().addChunkLoader(loaderData.get(), placer, location, timeLeft);
List<Chunk> chunksToLoad = ChunkLoaderChunks.calculateChunks(loaderData.get(), placer, location);
chunksToLoad.removeIf(chunk -> plugin.getLoaders().getChunkLoader(chunk).isPresent());
plugin.getLoaders().addChunkLoaderWithoutDBSave(loaderData.get(), placer, location, timeLeft, chunksToLoad);
}
});
}

View File

@ -1,11 +1,12 @@
package com.bgsoftware.wildloaders.handlers;
import com.bgsoftware.wildloaders.WildLoadersPlugin;
import com.bgsoftware.wildloaders.api.managers.LoadersManager;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.api.loaders.LoaderData;
import com.bgsoftware.wildloaders.api.managers.LoadersManager;
import com.bgsoftware.wildloaders.loaders.WChunkLoader;
import com.bgsoftware.wildloaders.loaders.WLoaderData;
import com.bgsoftware.wildloaders.utils.ChunkLoaderChunks;
import com.bgsoftware.wildloaders.utils.chunks.ChunkPosition;
import com.bgsoftware.wildloaders.utils.database.Query;
import com.google.common.collect.Maps;
@ -28,7 +29,7 @@ public final class LoadersHandler implements LoadersManager {
private final Map<String, LoaderData> loadersData = Maps.newConcurrentMap();
private final WildLoadersPlugin plugin;
public LoadersHandler(WildLoadersPlugin plugin){
public LoadersHandler(WildLoadersPlugin plugin) {
this.plugin = plugin;
}
@ -59,7 +60,8 @@ public final class LoadersHandler implements LoadersManager {
@Override
public ChunkLoader addChunkLoader(LoaderData loaderData, Player whoPlaced, Location location, long timeLeft) {
WChunkLoader chunkLoader = addChunkLoader(loaderData, whoPlaced.getUniqueId(), location, timeLeft);
WChunkLoader chunkLoader = addChunkLoaderWithoutDBSave(loaderData, whoPlaced.getUniqueId(), location, timeLeft,
ChunkLoaderChunks.calculateChunks(loaderData, whoPlaced.getUniqueId(), location));
Query.INSERT_CHUNK_LOADER.insertParameters()
.setLocation(location)
@ -71,8 +73,8 @@ public final class LoadersHandler implements LoadersManager {
return chunkLoader;
}
public WChunkLoader addChunkLoader(LoaderData loaderData, UUID placer, Location location, long timeLeft){
WChunkLoader chunkLoader = new WChunkLoader(loaderData, placer, location, timeLeft);
public WChunkLoader addChunkLoaderWithoutDBSave(LoaderData loaderData, UUID placer, Location location, long timeLeft, List<Chunk> loadedChunks) {
WChunkLoader chunkLoader = new WChunkLoader(loaderData, placer, location, loadedChunks.toArray(new Chunk[0]), timeLeft);
chunkLoaders.put(location, chunkLoader);
for (Chunk loadedChunk : chunkLoader.getLoadedChunks()) {
chunkLoadersByChunks.put(ChunkPosition.of(loadedChunk), chunkLoader);

View File

@ -60,12 +60,16 @@ public final class ProvidersHandler implements ProvidersManager {
// Loading the tickable providers
if (Bukkit.getPluginManager().isPluginEnabled("EpicSpawners")) {
Plugin epicSpawners = Bukkit.getPluginManager().getPlugin("EpicSpawners");
if (epicSpawners.getDescription().getVersion().startsWith("6")) {
String version = epicSpawners.getDescription().getVersion();
if (version.startsWith("6")) {
Optional<TickableProvider> tickableProvider = createInstance("TickableProvider_EpicSpawners6");
tickableProvider.ifPresent(this::addTickableProvider);
} else {
} else if(version.startsWith("7")) {
Optional<TickableProvider> tickableProvider = createInstance("TickableProvider_EpicSpawners7");
tickableProvider.ifPresent(this::addTickableProvider);
} else {
Optional<TickableProvider> tickableProvider = createInstance("TickableProvider_EpicSpawners8");
tickableProvider.ifPresent(this::addTickableProvider);
}
}
}

View File

@ -4,8 +4,10 @@ import com.bgsoftware.wildloaders.Locale;
import com.bgsoftware.wildloaders.WildLoadersPlugin;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.api.loaders.LoaderData;
import com.bgsoftware.wildloaders.utils.ChunkLoaderChunks;
import com.bgsoftware.wildloaders.utils.chunks.ChunkPosition;
import com.bgsoftware.wildloaders.utils.legacy.Materials;
import org.bukkit.Chunk;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.block.Block;
@ -27,32 +29,34 @@ public final class BlocksListener implements Listener {
private final WildLoadersPlugin plugin;
public BlocksListener(WildLoadersPlugin plugin){
public BlocksListener(WildLoadersPlugin plugin) {
this.plugin = plugin;
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onLoaderPlace(BlockPlaceEvent e){
public void onLoaderPlace(BlockPlaceEvent e) {
String loaderName = plugin.getNMSAdapter().getTag(e.getItemInHand(), "loader-name", "");
Optional<LoaderData> optionalLoaderData = plugin.getLoaders().getLoaderData(loaderName);
if(!optionalLoaderData.isPresent())
if (!optionalLoaderData.isPresent())
return;
if(!e.getPlayer().hasPermission("wildloaders.use")) {
if (!e.getPlayer().hasPermission("wildloaders.use")) {
e.setCancelled(true);
Locale.NO_PLACE_PERMISSION.send(e.getPlayer());
return;
}
if(plugin.getLoaders().getChunkLoader(e.getBlock().getLocation().getChunk()).isPresent()){
e.setCancelled(true);
Locale.ALREADY_LOADED.send(e.getPlayer());
return;
}
LoaderData loaderData = optionalLoaderData.get();
for (Chunk chunk : ChunkLoaderChunks.calculateChunks(loaderData, e.getPlayer().getUniqueId(), e.getBlock().getLocation())) {
if (plugin.getLoaders().getChunkLoader(chunk).isPresent()) {
e.setCancelled(true);
Locale.ALREADY_LOADED.send(e.getPlayer());
return;
}
}
long timeLeft = plugin.getNMSAdapter().getTag(e.getItemInHand(), "loader-time", loaderData.getTimeLeft());
plugin.getLoaders().addChunkLoader(loaderData, e.getPlayer(), e.getBlock().getLocation(), timeLeft);
@ -61,33 +65,33 @@ public final class BlocksListener implements Listener {
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onLoaderBreak(BlockBreakEvent e){
if(handleLoaderBreak(e.getBlock(), e.getPlayer().getGameMode() != GameMode.CREATIVE))
public void onLoaderBreak(BlockBreakEvent e) {
if (handleLoaderBreak(e.getBlock(), e.getPlayer().getGameMode() != GameMode.CREATIVE))
Locale.BROKE_LOADER.send(e.getPlayer());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onLoaderExplode(EntityExplodeEvent e){
public void onLoaderExplode(EntityExplodeEvent e) {
e.blockList().removeIf(block -> handleLoaderBreak(block, true));
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onSpawnerPlace(BlockPlaceEvent e){
if(e.getBlock().getType() != Materials.SPAWNER.toBukkitType())
public void onSpawnerPlace(BlockPlaceEvent e) {
if (e.getBlock().getType() != Materials.SPAWNER.toBukkitType())
return;
if(!plugin.getLoaders().getChunkLoader(e.getBlock().getChunk()).isPresent())
if (!plugin.getLoaders().getChunkLoader(e.getBlock().getChunk()).isPresent())
return;
plugin.getNMSAdapter().updateSpawner(e.getBlock().getLocation(), false);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onLoaderInteract(PlayerInteractEvent e){
if(e.getAction() != Action.RIGHT_CLICK_BLOCK)
public void onLoaderInteract(PlayerInteractEvent e) {
if (e.getAction() != Action.RIGHT_CLICK_BLOCK)
return;
if(plugin.getLoaders().getChunkLoader(e.getClickedBlock().getLocation()).isPresent())
if (plugin.getLoaders().getChunkLoader(e.getClickedBlock().getLocation()).isPresent())
e.setCancelled(true);
}
@ -95,37 +99,39 @@ public final class BlocksListener implements Listener {
public void onLoaderPistonRetract(BlockPistonRetractEvent e) {
try {
for (Block block : e.getBlocks()) {
if(plugin.getLoaders().getChunkLoader(block.getLocation()).isPresent()) {
if (plugin.getLoaders().getChunkLoader(block.getLocation()).isPresent()) {
e.setCancelled(true);
return;
}
}
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onLoaderPistonExtend(BlockPistonExtendEvent e) {
try {
for (Block block : e.getBlocks()) {
if(plugin.getLoaders().getChunkLoader(block.getLocation()).isPresent()) {
if (plugin.getLoaders().getChunkLoader(block.getLocation()).isPresent()) {
e.setCancelled(true);
return;
}
}
} catch (Throwable ignored) {}
} catch (Throwable ignored) {
}
}
private boolean handleLoaderBreak(Block block, boolean dropItem){
private boolean handleLoaderBreak(Block block, boolean dropItem) {
Location blockLoc = block.getLocation();
Optional<ChunkLoader> optionalChunkLoader = plugin.getLoaders().getChunkLoader(blockLoc);
if(!optionalChunkLoader.isPresent())
if (!optionalChunkLoader.isPresent())
return false;
ChunkLoader chunkLoader = optionalChunkLoader.get();
chunkLoader.remove();
if(dropItem)
if (dropItem)
blockLoc.getWorld().dropItemNaturally(blockLoc, chunkLoader.getLoaderItem());
return true;

View File

@ -1,6 +1,5 @@
package com.bgsoftware.wildloaders.listeners;
import com.bgsoftware.wildloaders.Updater;
import com.bgsoftware.wildloaders.WildLoadersPlugin;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import com.bgsoftware.wildloaders.utils.threads.Executor;
@ -37,9 +36,9 @@ public final class PlayersListener implements Listener {
ChatColor.GRAY + "This server is using WildLoaders v" + plugin.getDescription().getVersion()), 5L);
}
if (e.getPlayer().isOp() && Updater.isOutdated()) {
if (e.getPlayer().isOp() && plugin.getUpdater().isOutdated()) {
Executor.sync(() -> e.getPlayer().sendMessage(ChatColor.GREEN + "" + ChatColor.BOLD + "WildLoaders" +
ChatColor.GRAY + " A new version is available (v" + Updater.getLatestVersion() + ")!"), 20L);
ChatColor.GRAY + " A new version is available (v" + plugin.getUpdater().getLatestVersion() + ")!"), 20L);
}
}

View File

@ -5,6 +5,7 @@ import com.bgsoftware.wildloaders.api.holograms.Hologram;
import com.bgsoftware.wildloaders.api.loaders.ChunkLoader;
import com.bgsoftware.wildloaders.api.loaders.LoaderData;
import com.bgsoftware.wildloaders.api.npc.ChunkLoaderNPC;
import com.bgsoftware.wildloaders.utils.ChunkLoaderChunks;
import com.bgsoftware.wildloaders.utils.database.Query;
import com.bgsoftware.wildloaders.utils.threads.Executor;
import org.bukkit.Bukkit;
@ -14,7 +15,6 @@ import org.bukkit.Material;
import org.bukkit.OfflinePlayer;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
@ -33,11 +33,11 @@ public final class WChunkLoader implements ChunkLoader {
private boolean active = true;
private long timeLeft;
public WChunkLoader(LoaderData loaderData, UUID whoPlaced, Location location, long timeLeft) {
public WChunkLoader(LoaderData loaderData, UUID whoPlaced, Location location, Chunk[] loadedChunks, long timeLeft) {
this.loaderName = loaderData.getName();
this.whoPlaced = whoPlaced;
this.location = location.clone();
this.loadedChunks = calculateChunks(loaderData, whoPlaced, this.location);
this.loadedChunks = loadedChunks;
this.timeLeft = timeLeft;
this.tileEntityChunkLoader = plugin.getNMSAdapter().createLoader(this);
}
@ -125,39 +125,5 @@ public final class WChunkLoader implements ChunkLoader {
return isInfinite() ? plugin.getSettings().infiniteHologramLines : plugin.getSettings().hologramLines;
}
private static Chunk[] calculateChunks(LoaderData loaderData, UUID whoPlaced, Location original) {
List<Chunk> chunkList = new ArrayList<>();
if (loaderData.isChunksSpread()) {
calculateClaimChunks(original.getChunk(), whoPlaced, chunkList);
}
if (chunkList.isEmpty()) {
int chunkX = original.getBlockX() >> 4, chunkZ = original.getBlockZ() >> 4;
for (int x = -loaderData.getChunksRadius(); x <= loaderData.getChunksRadius(); x++)
for (int z = -loaderData.getChunksRadius(); z <= loaderData.getChunksRadius(); z++)
chunkList.add(original.getWorld().getChunkAt(chunkX + x, chunkZ + z));
}
return chunkList.toArray(new Chunk[0]);
}
private static void calculateClaimChunks(Chunk originalChunk, UUID whoPlaced, List<Chunk> chunkList) {
if (!plugin.getProviders().hasChunkAccess(whoPlaced, originalChunk))
return;
chunkList.add(originalChunk);
int chunkX = originalChunk.getX(), chunkZ = originalChunk.getZ();
for (int x = -1; x <= 1; x++) {
for (int z = -1; z <= 1; z++) {
if (x != 0 || z != 0) // We don't want to add the originalChunk again.
calculateClaimChunks(originalChunk.getWorld().getChunkAt(chunkX + x, chunkZ + z), whoPlaced, chunkList);
}
}
}
}

View File

@ -1,707 +0,0 @@
package com.bgsoftware.wildloaders.metrics;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.ServicePriority;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import javax.net.ssl.HttpsURLConnection;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.zip.GZIPOutputStream;
/**
* bStats collects some data for plugin authors.
* <p>
* Check out https://bStats.org/ to learn more about bStats!
*/
@SuppressWarnings("all")
public class Metrics {
static {
// You can use the property to disable the check in your test environment
if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
// Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
final String defaultPackage = new String(
new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
// We want to make sure nobody just copy & pastes the example and use the wrong package names
if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
}
}
}
// The version of this bStats class
public static final int B_STATS_VERSION = 1;
// The url to which the data is sent
private static final String URL = "https://bStats.org/submitData/bukkit";
// Is bStats enabled on this server?
private boolean enabled;
// Should failed requests be logged?
private static boolean logFailedRequests;
// Should the sent data be logged?
private static boolean logSentData;
// Should the response text be logged?
private static boolean logResponseStatusText;
// The uuid of the server
private static String serverUUID;
// The plugin
private final Plugin plugin;
// A list with all custom charts
private final List<CustomChart> charts = new ArrayList<>();
/**
* Class constructor.
*
* @param plugin The plugin which stats should be submitted.
*/
public Metrics(Plugin plugin) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
File configFile = new File(bStatsFolder, "config.yml");
YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
// Check if the config file exists
if (!config.isSet("serverUuid")) {
// Add default values
config.addDefault("enabled", true);
// Every server gets it's unique random id.
config.addDefault("serverUuid", UUID.randomUUID().toString());
// Should failed request be logged?
config.addDefault("logFailedRequests", false);
// Should the sent data be logged?
config.addDefault("logSentData", false);
// Should the response text be logged?
config.addDefault("logResponseStatusText", false);
// Inform the server owners about bStats
config.options().header(
"bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
"To honor their work, you should not disable it.\n" +
"This has nearly no effect on the server performance!\n" +
"Check out https://bStats.org/ to learn more :)"
).copyDefaults(true);
try {
config.save(configFile);
} catch (IOException ignored) { }
}
// Load the data
enabled = config.getBoolean("enabled", true);
serverUUID = config.getString("serverUuid");
logFailedRequests = config.getBoolean("logFailedRequests", false);
logSentData = config.getBoolean("logSentData", false);
logResponseStatusText = config.getBoolean("logResponseStatusText", false);
if (enabled) {
boolean found = false;
// Search for all other bStats Metrics classes to see if we are the first one
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
found = true; // We aren't the first
break;
} catch (NoSuchFieldException ignored) { }
}
// Register our service
Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
if (!found) {
// We are the first!
startSubmitting();
}
}
}
/**
* Checks if bStats is enabled.
*
* @return Whether bStats is enabled or not.
*/
public boolean isEnabled() {
return enabled;
}
/**
* Adds a custom chart.
*
* @param chart The chart to add.
*/
public void addCustomChart(CustomChart chart) {
if (chart == null) {
throw new IllegalArgumentException("Chart cannot be null!");
}
charts.add(chart);
}
/**
* Starts the Scheduler which submits our data every 30 minutes.
*/
private void startSubmitting() {
final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
if (!plugin.isEnabled()) { // Plugin was disabled
timer.cancel();
return;
}
// Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
// Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
Bukkit.getScheduler().runTask(plugin, () -> submitData());
}
}, 1000 * 60 * 5, 1000 * 60 * 30);
// Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
// WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
// WARNING: Just don't do it!
}
/**
* Gets the plugin specific data.
* This method is called using Reflection.
*
* @return The plugin specific data.
*/
public JSONObject getPluginData() {
JSONObject data = new JSONObject();
String pluginName = plugin.getDescription().getName();
String pluginVersion = plugin.getDescription().getVersion();
data.put("pluginName", pluginName); // Append the name of the plugin
data.put("pluginVersion", pluginVersion); // Append the version of the plugin
JSONArray customCharts = new JSONArray();
for (CustomChart customChart : charts) {
// Add the data of the custom charts
JSONObject chart = customChart.getRequestJsonObject();
if (chart == null) { // If the chart is null, we skip it
continue;
}
customCharts.add(chart);
}
data.put("customCharts", customCharts);
return data;
}
/**
* Gets the server specific data.
*
* @return The server specific data.
*/
private JSONObject getServerData() {
// Minecraft specific data
int playerAmount;
try {
// Around MC 1.8 the return type was changed to a collection from an array,
// This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
: ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
} catch (Exception e) {
playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
}
int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
String bukkitVersion = Bukkit.getVersion();
// OS/Java specific data
String javaVersion = System.getProperty("java.version");
String osName = System.getProperty("os.name");
String osArch = System.getProperty("os.arch");
String osVersion = System.getProperty("os.version");
int coreCount = Runtime.getRuntime().availableProcessors();
JSONObject data = new JSONObject();
data.put("serverUUID", serverUUID);
data.put("playerAmount", playerAmount);
data.put("onlineMode", onlineMode);
data.put("bukkitVersion", bukkitVersion);
data.put("javaVersion", javaVersion);
data.put("osName", osName);
data.put("osArch", osArch);
data.put("osVersion", osVersion);
data.put("coreCount", coreCount);
return data;
}
/**
* Collects the data and sends it afterwards.
*/
private void submitData() {
final JSONObject data = getServerData();
JSONArray pluginData = new JSONArray();
// Search for all other bStats Metrics classes to get their plugin data
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Our identifier :)
for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
try {
pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
}
} catch (NoSuchFieldException ignored) { }
}
data.put("plugins", pluginData);
// Create a new thread for the connection to the bStats server
new Thread(new Runnable() {
@Override
public void run() {
try {
// Send the data
sendData(plugin, data);
} catch (Exception e) {
// Something went wrong! :(
if (logFailedRequests) {
plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
}
}
}
}).start();
}
/**
* Sends the data to the bStats server.
*
* @param plugin Any plugin. It's just used to get a logger instance.
* @param data The data to send.
* @throws Exception If the request failed.
*/
private static void sendData(Plugin plugin, JSONObject data) throws Exception {
if (data == null) {
throw new IllegalArgumentException("Data cannot be null!");
}
if (Bukkit.isPrimaryThread()) {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
plugin.getLogger().info("Sending data to bStats: " + data.toString());
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
// Compress the data to save bandwidth
byte[] compressedData = compress(data.toString());
// Add headers
connection.setRequestMethod("POST");
connection.addRequestProperty("Accept", "application/json");
connection.addRequestProperty("Connection", "close");
connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
InputStream inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
StringBuilder builder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
bufferedReader.close();
if (logResponseStatusText) {
plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
}
}
/**
* Gzips the given String.
*
* @param str The string to gzip.
* @return The gzipped String.
* @throws IOException If the compression failed.
*/
private static byte[] compress(final String str) throws IOException {
if (str == null) {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes(StandardCharsets.UTF_8));
gzip.close();
return outputStream.toByteArray();
}
/**
* Represents a custom chart.
*/
public static abstract class CustomChart {
// The id of the chart
final String chartId;
/**
* Class constructor.
*
* @param chartId The id of the chart.
*/
CustomChart(String chartId) {
if (chartId == null || chartId.isEmpty()) {
throw new IllegalArgumentException("ChartId cannot be null or empty!");
}
this.chartId = chartId;
}
private JSONObject getRequestJsonObject() {
JSONObject chart = new JSONObject();
chart.put("chartId", chartId);
try {
JSONObject data = getChartData();
if (data == null) {
// If the data is null we don't send the chart.
return null;
}
chart.put("data", data);
} catch (Throwable t) {
if (logFailedRequests) {
Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
}
return null;
}
return chart;
}
protected abstract JSONObject getChartData() throws Exception;
}
/**
* Represents a custom simple pie.
*/
public static class SimplePie extends CustomChart {
private final Callable<String> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimplePie(String chartId, Callable<String> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JSONObject getChartData() throws Exception {
JSONObject data = new JSONObject();
String value = callable.call();
if (value == null || value.isEmpty()) {
// Null = skip the chart
return null;
}
data.put("value", value);
return data;
}
}
/**
* Represents a custom advanced pie.
*/
public static class AdvancedPie extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JSONObject getChartData() throws Exception {
JSONObject data = new JSONObject();
JSONObject values = new JSONObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.put(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.put("values", values);
return data;
}
}
/**
* Represents a custom drilldown pie.
*/
public static class DrilldownPie extends CustomChart {
private final Callable<Map<String, Map<String, Integer>>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
super(chartId);
this.callable = callable;
}
@Override
public JSONObject getChartData() throws Exception {
JSONObject data = new JSONObject();
JSONObject values = new JSONObject();
Map<String, Map<String, Integer>> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean reallyAllSkipped = true;
for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
JSONObject value = new JSONObject();
boolean allSkipped = true;
for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
value.put(valueEntry.getKey(), valueEntry.getValue());
allSkipped = false;
}
if (!allSkipped) {
reallyAllSkipped = false;
values.put(entryValues.getKey(), value);
}
}
if (reallyAllSkipped) {
// Null = skip the chart
return null;
}
data.put("values", values);
return data;
}
}
/**
* Represents a custom single line chart.
*/
public static class SingleLineChart extends CustomChart {
private final Callable<Integer> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SingleLineChart(String chartId, Callable<Integer> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JSONObject getChartData() throws Exception {
JSONObject data = new JSONObject();
int value = callable.call();
if (value == 0) {
// Null = skip the chart
return null;
}
data.put("value", value);
return data;
}
}
/**
* Represents a custom multi line chart.
*/
public static class MultiLineChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JSONObject getChartData() throws Exception {
JSONObject data = new JSONObject();
JSONObject values = new JSONObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, Integer> entry : map.entrySet()) {
if (entry.getValue() == 0) {
continue; // Skip this invalid
}
allSkipped = false;
values.put(entry.getKey(), entry.getValue());
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.put("values", values);
return data;
}
}
/**
* Represents a custom simple bar chart.
*/
public static class SimpleBarChart extends CustomChart {
private final Callable<Map<String, Integer>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JSONObject getChartData() throws Exception {
JSONObject data = new JSONObject();
JSONObject values = new JSONObject();
Map<String, Integer> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
JSONArray categoryValues = new JSONArray();
categoryValues.add(entry.getValue());
values.put(entry.getKey(), categoryValues);
}
data.put("values", values);
return data;
}
}
/**
* Represents a custom advanced bar chart.
*/
public static class AdvancedBarChart extends CustomChart {
private final Callable<Map<String, int[]>> callable;
/**
* Class constructor.
*
* @param chartId The id of the chart.
* @param callable The callable which is used to request the chart data.
*/
public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
super(chartId);
this.callable = callable;
}
@Override
protected JSONObject getChartData() throws Exception {
JSONObject data = new JSONObject();
JSONObject values = new JSONObject();
Map<String, int[]> map = callable.call();
if (map == null || map.isEmpty()) {
// Null = skip the chart
return null;
}
boolean allSkipped = true;
for (Map.Entry<String, int[]> entry : map.entrySet()) {
if (entry.getValue().length == 0) {
continue; // Skip this invalid
}
allSkipped = false;
JSONArray categoryValues = new JSONArray();
for (int categoryValue : entry.getValue()) {
categoryValues.add(categoryValue);
}
values.put(entry.getKey(), categoryValues);
}
if (allSkipped) {
// Null = skip the chart
return null;
}
data.put("values", values);
return data;
}
}
}

View File

@ -10,8 +10,6 @@ import java.util.UUID;
public interface NMSAdapter {
boolean isMappingsSupported();
String getTag(ItemStack itemStack, String key, String def);
ItemStack setTag(ItemStack itemStack, String key, String value);

View File

@ -1,55 +0,0 @@
package com.bgsoftware.wildloaders.nms.mapping;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MappingParser {
private static final Pattern NEW_CLASS_REMAP_PATTERN = Pattern.compile("^([^ ]+) -> [^ ]+:$");
private static final Pattern FIELD_REMAP_PATTERN = Pattern.compile("^[ ]{4}[^ ]+ ([^ (]+)+ -> ([^ ]+)$");
private static final Pattern METHOD_REMAP_PATTERN = Pattern.compile("^[ ]{4}[^ ]+ ([^ ]+)\\(.*\\) -> ([^ ]+)$");
private MappingParser() {
}
public static Map<String, Remapped> parseRemappedMap(File mappingsFile) throws IOException {
Map<String, Remapped> remappedMap = new HashMap<>();
try (BufferedReader reader = new BufferedReader(new FileReader(mappingsFile))) {
Remapped currentRemapped = null;
String currentLine;
while ((currentLine = reader.readLine()) != null) {
if (currentLine.startsWith("#")) // Comment
continue;
Matcher matcher;
if ((matcher = NEW_CLASS_REMAP_PATTERN.matcher(currentLine)).matches()) {
currentRemapped = new Remapped();
remappedMap.put(matcher.group(1), currentRemapped);
} else if (currentRemapped != null) {
if ((matcher = FIELD_REMAP_PATTERN.matcher(currentLine)).matches()) {
String fieldName = matcher.group(1);
String obfuscatedName = matcher.group(2);
currentRemapped.put(Remap.Type.FIELD, fieldName, obfuscatedName);
} else if ((matcher = METHOD_REMAP_PATTERN.matcher(currentLine)).matches()) {
String methodName = matcher.group(1);
String obfuscatedName = matcher.group(2);
currentRemapped.put(Remap.Type.METHOD, methodName, obfuscatedName);
}
}
}
}
return remappedMap;
}
}

View File

@ -1,30 +0,0 @@
package com.bgsoftware.wildloaders.nms.mapping;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(Remaps.class)
public @interface Remap {
String classPath();
String name();
Type type();
String remappedName() default "";
enum Type {
FIELD,
METHOD
}
}

View File

@ -1,9 +0,0 @@
package com.bgsoftware.wildloaders.nms.mapping;
public class RemapFailure extends RuntimeException {
public RemapFailure(String message) {
super(message);
}
}

Some files were not shown because too many files have changed in this diff Show More