#Version V2.0.0 #Requirements #Modern Skript, 2.9+ (mostly tested on 2.11.2 and 2.13.2) #Skript-Reflect #SkBee #PaperMC or derivatives for NMS based things # Player vision changing doesn't work with 1.21.9+ clients. The bug was patched :( #Change this to your plugin/script's name #If you change this after storing data with tags, they will no longer work options: pluginname: hyperslib #Reminds the user to enable memory variables once they restart the server with the library for the first time and then later. on skript start: if {-Memory_variable} is set: send "&ePlease enable memory variables for much better performance and near 0 lag, they are easy to enable." to console send "&ePlease read https://sovdee.gitbook.io/skript-tutorials/core-concepts/variables/memory-variables-metadata-and-alternatives" to console send "&eIf you still don't know how to, DM hyperdondonreal on Discord and ask for help." to console set {-Memory_variable} to true #Import Java classes to be able to use import: java.time.Duration java.lang.System org.bukkit.Bukkit java.lang.Character org.bukkit.event.player.PlayerInteractEntityEvent org.bukkit.event.player.PlayerInteractEvent org.bukkit.inventory.EquipmentSlot org.bukkit.persistence.PersistentDataHolder org.bukkit.persistence.PersistentDataContainer org.bukkit.persistence.PersistentDataType org.bukkit.event.block.Action #NMS. We are using Mojang mappings, so, PaperMC is required. #Entities net.minecraft.world.entity.EntityType net.minecraft.world.entity.monster.EnderMan net.minecraft.world.entity.monster.Creeper #Packets net.minecraft.network.protocol.game.ClientboundAddEntityPacket net.minecraft.network.protocol.game.ClientboundSetCameraPacket net.minecraft.network.protocol.game.ClientboundRespawnPacket net.minecraft.network.protocol.game.ClientboundSetPassengersPacket net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket net.minecraft.network.protocol.game.ClientboundCooldownPacket #Other Objects net.minecraft.core.BlockPos net.minecraft.world.phys.AABB #Not NMS, used for NMS parts org.bukkit.entity.Mob java.lang.Class on load: set {-{@pluginname}::APIChange} to isAtAPIChange() if isAt1i21i11() is true: set {-{@pluginname}::SpiderClass} to class "net.minecraft.world.entity.monster.spider.Spider" set {-{@pluginname}::CaveSpiderClass} to class "net.minecraft.world.entity.monster.spider.CaveSpider" else: set {-{@pluginname}::SpiderClass} to class "net.minecraft.world.entity.monster.Spider" set {-{@pluginname}::CaveSpiderClass} to class "net.minecraft.world.entity.monster.CaveSpider" #Changes number without a decimal number to have one. function formatDouble(num: number) :: string: if mod({_num}, 1) is 0: return "%{_num}%.0" else: return "%{_num}%" #Checks if version is 1.20.4 or lower. At 1.20.5, Mojang has changed the NBT formatDouble and so has the server APIs. The change to the Bukkit API have been drastic. local function isAtAPIChange() :: boolean: if any: Bukkit.getBukkitVersion() contains "1.12" Bukkit.getBukkitVersion() contains "1.13" Bukkit.getBukkitVersion() contains "1.14" Bukkit.getBukkitVersion() contains "1.15" Bukkit.getBukkitVersion() contains "1.16" Bukkit.getBukkitVersion() contains "1.17" Bukkit.getBukkitVersion() contains "1.18" Bukkit.getBukkitVersion() contains "1.19" Bukkit.getBukkitVersion() contains "1.20.1" Bukkit.getBukkitVersion() contains "1.20.2" Bukkit.getBukkitVersion() contains "1.20.3" Bukkit.getBukkitVersion() contains "1.20.4" then: return false return true condition server[(-| )version] is[(negative:( not|n't))] at [bukkit(-| )](API|api) change: check: if first element of parser tags is "negative": {-{@pluginname}::APIChange} is false continue else: {-{@pluginname}::APIChange} is true continue local function isAt1i21i11() :: boolean: set {_newSpiderClass} to class "net.minecraft.world.entity.monster.spider.Spider" if {_newSpiderClass} is set: return true else: return false expression cooldown %string% [for %player%]: get: if expr-2 is set: set {_id} to "%uuid of expr-2%:%expr-1%" else: set {_id} to expr-1 return getActionBarCooldown({_id}) set: if expr-2 is set: set {_id} to "%uuid of expr-2%:%expr-1%" else: set {_id} to expr-1 set {-{@pluginname}::Cooldown::%{_id}%} to System.currentTimeMillis() + ticks of (change value * 50) condition cooldown %string% [for %player%] is[(negative:( not|n't))] ready: check: if expr-2 is set: set {_id} to "%uuid of expr-2%:%expr-1%" else: set {_id} to expr-1 if first element of parser tags is "negative": isCooldownReady({_id}) is not true continue else: isCooldownReady({_id}) is true continue #Gets the cooldown's seconds, does not return the whole cooldown in seconds. local function getCooldownSeconds(cooldown: text) :: int: add Duration.ofMillis({-{@pluginname}::Cooldown::%{_cooldown}%} - System.currentTimeMillis()).toSecondsPart() to {_coo} if {_coo} is 60: set {_coo} to 0 return {_coo} #Gets the cooldown's minutes. local function getCooldownMinutes(cooldown: text) :: int: set {_coo} to Duration.ofMillis({-{@pluginname}::Cooldown::%{_cooldown}%} - System.currentTimeMillis()).toMinutes() if Duration.ofMillis({-{@pluginname}::Cooldown::%{_cooldown}%} - System.currentTimeMillis()).toSecondsPart() is 60: add 1 to {_coo} return {_coo} #Checks if a cooldown is ready to use local function isCooldownReady(cooldown: text) :: boolean: if {-{@pluginname}::Cooldown::%{_cooldown}%} is not set: return true if {-{@pluginname}::Cooldown::%{_cooldown}%} - System.currentTimeMillis() <= 1000: return true else: return false #Returns cooldown in how it would look like in an action bar. local function getActionBarCooldown(cooldow: text, show_decimal: boolean = false) :: text: if isCooldownReady({_cooldow}) is true: set {_cooldown} to "&aReady!" else: if {_show_decimal} is false: set {_cooldown_seconds} to getCooldownSeconds({_cooldow}) set {_cooldown_minutes} to getCooldownMinutes({_cooldow}) else: set {_cooldown_seconds} to formatDouble(getCooldownSeconds({_cooldow})) set {_cooldown_minutes} to formatDouble(getCooldownMinutes({_cooldow})) if {_cooldown_minutes} > 0: if {_cooldown_seconds} < 1: set {_cooldown} to "&b%{_cooldown_minutes}%m" else: set {_cooldown} to "&b%{_cooldown_minutes}%m %{_cooldown_seconds}%s" if {_cooldown_minutes} < 1: if {_cooldown_seconds} > 0: set {_cooldown} to "&b%{_cooldown_seconds}%s" if {_cooldown} is not set: set {_cooldown} to "&aReady!" if {_show_decimal} is true: replace all "m" and "s" with "" in {_cooldown} return {_cooldown} expression [(a|an)] unremovable %itemtype%: return type: item get: return expr-1 with boolean tag data "unremovable" set to true #Checks if an item is removable (duh) local function isUnremovable(item: item) :: boolean: #%itemstack% (positive: has|negative: (doesn't|does not) have) [%-nbttype%] [custom] [nbt] data %string% [(as|[set] to) %-object%] {_item} has boolean tag data "unremovable": return true return false on inventory click: if player's inventory is event-inventory: if any: click action is shift+lmb click action is shift+rmb then: if isUnremovable(event-item) is true: cancel event on inventory click: if player's open inventory is event-inventory: if any: click action is lmb click action is rmb then: if isUnremovable(player's cursor slot) is true: cancel event on inventory click: if player's open inventory is event-inventory: if click action is number key: if isUnremovable(player.getInventory().getItem(event.getHotbarButton())) is true: cancel event if click action is swap offhand: if isUnremovable(player's off hand item) is true: cancel event on inventory drag: set {_slots::*} to event.getRawSlots() loop {_slots::*}: set {_slot} to "%loop-value%" replace all "[" with "" in {_slot} replace all "]" with "" in {_slot} set {_list::*} to split {_slot} at ", " loop {_list::*}: set {_num} to loop-value parsed as integer if player's open inventory is event.getView().getInventory({_num}): if isUnremovable(past event-item) is true: cancel event on death: loop the drops: isUnremovable(loop-value) is true remove loop-value from the drops on drop: isUnremovable(event-item) is true cancel event on inventory click: isUnremovable(event-item) is true event-inventory is player's inventory player cannot hold 1 water bucket #water buckets dont stack, good for this case cancel event on PlayerInteractEntityEvent: if event.getHand() is EquipmentSlot.HAND: isUnremovable(event.getPlayer()'s held item) is true event.setCancelled(true) if event.getHand() is EquipmentSlot.OFF_HAND: isUnremovable(event.getPlayer()'s off hand item) is true event.setCancelled(true) on PlayerInteractEvent: if event.getAction() is not Action.RIGHT_CLICK_BLOCK: stop if event.getHand() is EquipmentSlot.HAND: isUnremovable(event.getPlayer()'s held item) is true event.setCancelled(true) if event.getHand() is EquipmentSlot.OFF_HAND: isUnremovable(event.getPlayer()'s off hand item) is true event.setCancelled(true) #SkQuery port expression char[acter][s] at [index] %numbers% (within|in) %string%: return type: string get: set {_finalString} to "" set {_indexes::*} to expr-1 set {_strings::*} to expr-2 loop {_indexes::*}: loop {_strings::*}: add loop-value-2.charAt(loop-value-1) to {_characters::*} loop {_characters::*}: loop-value is set set {_finalString} to "%{_finalString}%%loop-value%" return {_finalString} local function toSmallCaps(string: text) :: text: set {_normalText::*} to "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y" and "Z" #!RAT was here, Added a Y, you dumbass hyper. set {_smallText::*} to "ᴀ", "ʙ", "ᴄ", "ᴅ", "ᴇ", "ғ", "ɢ", "ʜ", "ɪ", "ᴊ", "ᴋ", "ʟ", "ᴍ", "ɴ", "ᴏ", "ᴘ", "ǫ", "ʀ", "s", "ᴛ", "ᴜ", "ᴠ", "ᴡ", "x", "ʏ" and "ᴢ" set {_finalString} to "" set {_skipAmount} to 0 set {_string} to raw {_string} loop {_string}'s length times: set {_index} to loop-iteration - 1 set {_char} to char at {_index} in {_string} if {_skipAmount} > 0: add {_char} to {_finalString} remove 1 from {_skipAmount} continue loop {_normalText::*}: if {_char} is "<": set {_bracketIndex} to {_index} while char at {_bracketIndex} in {_string} is not ">": if {_bracketIndex} > {_string}'s length - 1: exit 3 sections add 1 to {_bracketIndex} add 1 to {_skipAmount} if {_char} is "&": if {_string}'s length <= {_index} + 1: exit 2 sections char at {_index} + 1 in {_string} is not "&" char at {_index} + 1 in {_string} is not " " add 1 to {_skipAmount} if Character.isLetter({_char}.toUpperCase()) is not true: add {_char} to {_finalString} exit 2 sections if loop-value-2 is {_char}: add {_smallText::%loop-iteration-2%} to {_finalString} return colored {_finalString} expression: patterns: small caps from %strings% %strings% (as|to|in) small caps get: set {_strings::*} to expr-1 loop {_strings::*}: add toSmallCaps(loop-value) to {_list::*} return {_list::*} #All geyser/bedrock players have a uuid that start with 00000000-0000-0000-000 #No Java player would be able to get this uuid, it's not a valid UUID that could be generated from Mojang. condition %player% is[(negative:( not|n't))] [a] (bedrock|geyser|floodgate) player: check: if first element of parser tags is "negative": expr-1's uuid doesn't start with "00000000" continue else: expr-1's uuid starts with "00000000" continue #We are using NMS here. Code from PoaSK. #Only supports enderman, creeper, spider, cave spider, and player (default for players, duh) effect: patterns: change vision of %players% to [entity] %entitytype% change %players%'s vision to [entity] %entitytype% parse: #Create errors for unsupported entities if any: "%expr-2%" is "enderman" "%expr-2%" is "creeper" "%expr-2%" is "spider" "%expr-2%" is "cave spider" "%expr-2%" is "player" then: continue trigger: set {_players::*} to expr-1 loop {_players::*}: setVision(loop-value, expr-2) local function setVision(player: player, entity: object): set {_serverPlayer} to {_player}.getHandle() set {_serverLevel} to {_player}.getWorld().getHandle() if "%{_entity}%" is "enderman": set {_NMSEntity} to new EnderMan(EntityType.ENDERMAN, {_serverLevel}) else if "%{_entity}%" is "creeper": set {_NMSEntity} to new Creeper(EntityType.CREEPER, {_serverLevel}) else if "%{_entity}%" is "spider": set {_NMSEntity} to new {-{@pluginname}::SpiderClass}(EntityType.SPIDER, {_serverLevel}) # Ignore error, done to add compatibility with newer versions else if any: "%{_entity}%" is "cave_spider" "%{_entity}%" is "cave spider" then: set {_NMSEntity} to new {-{@pluginname}::CaveSpiderClass}(EntityType.CAVE_SPIDER, {_serverLevel}) # Ignore error, done to add compatibility with newer versions else if "%{_entity}%" is "player": {_serverPlayer}.connection.send(new ClientboundSetCameraPacket({_serverPlayer})) stop {_serverPlayer}.connection.send(new ClientboundAddEntityPacket({_NMSEntity}, 0, BlockPos.ZERO)) {_serverPlayer}.connection.send(new ClientboundSetCameraPacket({_NMSEntity})) refreshPlayer({_player}) effect refresh %players%: trigger: set {_players::*} to expr-1 loop {_players::*}: refreshPlayer(loop-value) local function refreshPlayer(player: player): set {_serverPlayer} to {_player}.getHandle() set {_serverLevel} to {_player}.getWorld().getHandle() {_serverPlayer}.connection.send(new ClientboundRespawnPacket({_serverPlayer}.createCommonSpawnInfo({_serverLevel}), ClientboundRespawnPacket.KEEP_ALL_DATA)) set {_loc} to location of {_player} set {_x} to x coord of {_loc} set {_y} to y coord of {_loc} set {_z} to z coord of {_loc} set {_yaw} to yaw of {_loc} set {_pitch} to pitch of {_loc} {_serverPlayer}.connection.teleport({_x}, {_y}, {_z}, {_yaw}, {_pitch}) if ({_serverPlayer}.isPassenger()): {_serverPlayer}.connection.send(new ClientboundSetPassengersPacket({_serverPlayer}.getVehicle())) if ({_serverPlayer}.isVehicle()): {_serverPlayer}.connection.send(new ClientboundSetPassengersPacket({_serverPlayer})) set {_entities::*} to {_player}.getNearbyEntities(10, 10, 10) loop {_entities::*}: loop-value is an instance of Mob set {_NMSMob} to loop-value.getHandle() {_serverPlayer}.equals({_NMSMob}.getLeashHolder()) {_serverPlayer}.connection.send(new ClientboundSetEntityLinkPacket({_NMSMob}, {_serverPlayer})) if {_serverPlayer}.getCooldowns().cooldowns.isEmpty() is not true: set {_tickCount} to {_serverPlayer}.getCooldowns().tickCount set {_cooldowns::*} to {_serverPlayer}.getCooldowns().cooldowns.entrySet() loop {_cooldowns::*}: {_serverPlayer}.connection.send(new ClientboundCooldownPacket(loop-value.getKey(), loop-value.getValue().endTime - {_tickCount})) {_serverPlayer}.onUpdateAbilities() #in 1.21.5, they made the server field private, this should not affect us here. As said here: https://tpgamesnl.gitbook.io/skript-reflect/code-conventions#:~:text=private%20members%20are%20visible%20and%20accessible%20by%20default. {_serverPlayer}.server.getPlayerList().sendPlayerPermissionLevel({_serverPlayer}) {_serverPlayer}.server.getPlayerList().sendLevelInfo({_serverPlayer}, {_serverLevel}) {_serverPlayer}.server.getPlayerList().sendAllPlayerInfo({_serverPlayer}) #Our own boss bar syntax, uses SkBee's boss bar system expression [new] boss[ ]bar [(named|(with|of) id) %-string%] [(with title|titled) %-string%] [[with (color|colour)|colo[u]red] %-color%] [(with style|styled) %-bossbarstyle%] [with progress %-number%] [lasting [for] %-timespan%] [updating every %-timespan%] [counting (:(up|down))] for %players%: return type: bossbar get: if size of {_players::*} is 1: set {_id} to expr-1 ? "%uuid of {_players::1}%:%random uuid%" else: set {_id} to expr-1 ? "%random uuid%" set {_title} to expr-2 ? "Bossbar" set {_color} to expr-3 ? red set {_style} to expr-4 ? solid_bar set {_progress} to expr-5 ? 100 set {_lasting} to expr-6 set {_updating} to expr-7 ? 1 tick set {_counting} to the first element of parser tags ? "down" #defaults to counting down set {_players::*} to expr-8 #for knowing the ids, refer to this. make sure to set an id to change progress youself. set {_bar} to boss bar with id "%{_id}%" with title {_title} with color {_color} with style {_style} with progress {_progress} add {_players::*} to bar players of {_bar} if {_lasting} is set: updateActionBar({_bar}, {_updating}, {_lasting}, {_counting}, {_id}) return {_bar} local function updateActionBar(bar: bossbar, updating: timespan, lasting: timespan, counting: text, id: text): #convert to milliseconds set {_time} to ((ticks of {_lasting}) / 20) + unix timestamp of now set {_change} to 100 * ((ticks of {_updating}) / (ticks of {_lasting})) while {_time} > unix timestamp of now: if {_counting} is "up": add {_change} to bar progress of {_bar} else if {_counting} is "down": remove {_change} from bar progress of {_bar} if bar players of {_bar} is not set: exit 2 sections wait {_updating} remove bar players of {_bar} from bar players of {_bar} delete boss bar with id {_id} #NBT local function getTagTypes() :: nbttypes: return string tag, boolean tag, int tag, byte array tag, byte tag, compound list tag, compound tag, double list tag, double tag, float list tag, float tag, int array tag, int list tag, long list tag, long tag, short tag, string list tag, and uuid tag local function getTagTypeFromObject(object: object) :: nbttype: if {_object} is a text: return string tag else if {_object} is a number: if {_object} > 2147483647: return long tag return int tag if {_object} is a boolean: return boolean tag local function tagProperty(item: item, data: string, type: nbttype) :: object: return {_item}'s {_data} {_type} data # We're not able to use the syntax within the syntax. So we have to resort to doing this. item property %string% ([%-nbttype%] [custom] [nbt] data|%-nbttype% [custom] [nbt] [data]): get: set {_item} to expr-1 set {_data} to expr-2 set {_type} to expr-3 if {_type} is set: if server is not at api change: set {_tag} to "PublicBukkitValues;{@pluginname}:%{_data}%" else: set {_tag} to "minecraft:custom_data;PublicBukkitValues;{@pluginname}:%{_data}%" set {_nbt} to {_type} {_tag} of nbt of {_item} return {_nbt} else: loop getTagTypes(): set {_returnedData} to tagProperty({_item}, {_data}, loop-value) if {_returnedData} is set: return {_returnedData} return {_null} # We're not able to use the syntax within the syntax. So we have to resort to doing this. local function tagCondition(item: item, type: nbttype, name: string, object: object) :: boolean: if {_item} has {_type} data {_name} set to {_object}: return true return false condition %item% (positive: has|negative: ((doesn't|does not) have)) [%-nbttype%] [custom] [nbt] data %string% [(as|[set] to) %-object%]: check: set {_item} to expr-1 set {_type} to expr-2 set {_name} to expr-3 set {_object} to expr-4 if {_type} is set: if "%parser tags%" contains "positive": {_item}'s {_name} {_type} data is set if {_object} is set: {_item}'s {_name} {_type} is {_object} continue else: continue else: {_item}'s {_name} {_type} data is not set if {_object} is set: {_item}'s {_name} {_type} is not {_object} continue else: continue else: loop getTagTypes(): if tagCondition({_item}, loop-value, {_name}, {_object}) is true: set {_has} to true exit 2 sections if "%parser tags%" contains "positive": {_has} is set continue else: {_has} is not set continue #Simple tag syntax for items, used to store data easily expression ([a[n]]|[the]) %itemtype% with [%-nbttype%] [custom] [nbt] data %string% (as|to| set to) %object%: return type: item get: set {_item} to expr-1 set {_name} to expr-3 set {_value} to expr-4 set {_type} to expr-2 ? getTagTypeFromObject({_value}) if server is not at api change: set {_type} "PublicBukkitValues;{@pluginname}:%{_name}%" of nbt of {_item} to {_value} else: set {_type} "minecraft:custom_data;PublicBukkitValues;{@pluginname}:%{_name}%" of nbt of {_item} to {_value} return {_item}