FastWorldReset/fastworldreset.sk
2020-12-16 12:59:58 +02:00

727 lines
30 KiB
Plaintext

options:
#------------- Start of Configuration -------------
#recommended to have this as true for things which have to never save changes
#NOTE: this will function only if the world has been edited since last reset, if it hasn't been edited, to save performance it will not do useless reset. YAY!
ResetResettableWorldsOnRestart: true
AutoDeleteClonesOnRestart: true
#whether /fwr create will look for existing world folder or allow to create a new blank one.
#Creating blank world will not function for resetting (as there will be no template) until you update template by running /fwr create on the same world again.
#useful for when you want to copypaste a map into a newly created world and then make it resettable
#without having to go through another world management plugin to do it
AllowCreatingNewWorlds: false
#------------- End of Configuration -------------
#The script starts here, only edit if you know what you are doing.
import:
org.bukkit.Bukkit
org.bukkit.WorldCreator
java.lang.System
java.io.File
java.util.Date
org.apache.commons.io.FileUtils
org.apache.commons.io.comparator.LastModifiedFileComparator
java.util.Arrays
java.nio.file.Files
java.nio.file.Paths
on script load:
wait a tick #needed to wait for plugins to load
set {-fwrcache::bukkitgetserver} to Bukkit.getServer()
set {-fwrcache::mainworld} to {-fwrcache::bukkitgetserver}.getWorlds().get(0)
set {-fwrcache::fileseparator} to File.separator
set {-fwrcache::worlddir} to {-fwrcache::bukkitgetserver}.getWorldContainer().getPath()
delete {-govindask}
if {-fwrcache::bukkitgetserver}.getPluginManager().getPlugin("GovindaSK") is set:
send "[FastWorldReset] GovindaSK found! Enabling faster chunk unloads." to console
set {-govindask} to true
else:
send "[FastWorldReset] GovindaSK not found, falling back to a slower, reflection way of unloading chunks. (2 errors related to chunk unloading will appear, but you can safely ignore them)" to console
wait 5 seconds
#get list of world generators for /fwr generator command
delete {-worldgenerators::*}
set {_plugins::*} to ...{-fwrcache::bukkitgetserver}.getPluginManager().getPlugins()
loop {_plugins::*}:
set {_value} to loop-value.getDefaultWorldGenerator("%{-fwrcache::mainworld}%", "")
if {_value} is set:
delete {_value}
add 1 to {_i}
set {_name} to loop-value.getDescription().getName()
set {-worldgenerators::%{_name}%} to {_name}
event "world_reset_start":
patterns:
start of world reset
world reset start
event-values: world
event "world_reset_complete":
patterns:
complete of world reset
world reset complete
event-values: world
function unloadChunks(world: world, saving: boolean) :: boolean:
set {-resetting::%{_world}%} to true
set {_n} to now
#requires GovindaSK
if {-govindask} is set:
if {_saving} is true:
unload all chunks in {_world} with saving
else:
unload all chunks in {_world} without saving
#if GovindaSK is not found, fall back to skript-reflect way of unloading chunks
else:
loop ...{_world}.getLoadedChunks():
loop-value.unload({_saving})
#send "[FastWorldReset] &bUnloading chunks of &e%{_world}% &btook %difference between {_n} and now%" to console
delete {-resetting::%{_world}%}
if amount of ...{_world}.getLoadedChunks() is 0:
return true
else:
return false
function teleportOut(world: world) :: boolean:
if {fastworldreset::exitpoint} is not a location:
delete {fastworldreset::exitpoint}
set {_spawnpoint} to {fastworldreset::exitpoint} ? (spawn point of "%{-fwrcache::mainworld}%" parsed as a world)
loop all players in {_world}:
if passenger of loop-player is set:
loop passengers of loop-player:
make loop-value-2 dismount
teleport loop-player to {_spawnpoint}
return true
function unloadWorld(world: world, saving: boolean = true) :: boolean:
set {_n} to now
#teleport players out
teleportOut({_world}) = true
#unload the world
Bukkit.unloadWorld("%{_world}%", {_saving})
send "World unload took %difference between {_n} and now%" to console
return true
function resetWorld(input: text, sender: object):
#mark world save as non-set
if {-fastworldreset::shouldreset::%{_input}%} is set:
delete {-fastworldreset::shouldreset::%{_input}%}
set {_world} to {_input} parsed as a world
#stats for problem identification
add 1 to {-resetstats::%{_input}%::worldbased}
set {-resetstats::%{_input}%::last} to "world"
if {_world} is a world:
set {_input} to "%{_world}%"
unloadWorld({_world}, false) is not true:
send "&a&lFastWorldReset&2&l> &cThere was an error in unloading the world &e%{_world}%&c, see console and report any problems to author Govindas." to console
stop
set {-resetting::%{_input}%} to now
set {_worlddir} to {-fwrcache::worlddir}
if {fastworldresetclone::%{_input}%} is set:
set {_template} to {fastworldresetclone::%{_input}%}
else:
set {_template} to {_input}
set {_source} to new File("%{_worlddir}%/FastWorldReset/%{_template}%/region")
set {_target} to new File("%{_worlddir}%/%{_input}%/region")
create new section stored in {_section}:
FileUtils.copyDirectory({_source}, {_target})
run section {_section} async and wait
if {worldgenerator::%{_template}%} is set:
{-fwrcache::bukkitgetserver}.createWorld(new WorldCreator({_input}).generator({worldgenerator::%{_template}%}))
else:
{-fwrcache::bukkitgetserver}.createWorld(new WorldCreator({_input}))
set {fastworldreset::lastreset::%{_input}%} to new Date().getTime() #unix time, not using vanilla skript syntax as we need milliseconds support
send "&a&lFastWorldReset&2&l> &cReset &e%{_input}% &cvia world-based method." to {_sender}
delete {-resetting::%{_input}%}
set {_ev::world} to {_input} parsed as a world
set {_evt} to custom event "world_reset_complete" with {_ev::*}
call event {_evt}
import:
org.bukkit.World$Environment
on world load:
{-resetting::%event-world%} is set:
delete {-resetting::%event-world%}
#mark world as not saved
{-fastworldreset::shouldreset::%event-world%} is set:
delete {-fastworldreset::shouldreset::%event-world%}
#it looks like environment setting makes the world load.. different data, so things disappear on environment change, so I am not releasing it for now, just keeping this function.
function loadWorld(world: text, generator: text = "", environment: text = ""):
set {-initload::%{_world}%} to true
if {_generator} is "":
if {worldgenerator::%{_world}%} is set:
set {_generator} to {worldgenerator::%{_world}%}
if {_environment} is "":
set {_environment} to {worldenvironment::%{_world}%} ? "NORMAL"
{_generator} is not "":
{-fwrcache::bukkitgetserver}.createWorld(new WorldCreator({_world}).generator({_generator}).environment(Environment.valueOf({_environment} in uppercase)))
else:
{-fwrcache::bukkitgetserver}.createWorld(new WorldCreator({_world}))
#blacklist of things that can do bad stuff in windows/linux if used in folder names.
function blacklist(input: text) :: boolean:
if {_input} contains "/" or "\" or "*" or "." or ":" or "?" or "<" or ">" or "|" or """":
return true
if {_input} is "plugins" or "logs":
"%{-fwrcache::worlddir}%" is "."
return true
if {_input} is "CON" or "PRN" or "AUX" or "NUL" or "COM1" or "COM2" or "COM3" or "COM4" or "COM5" or "COM6" or "COM7" or "COM8" or "COM9" or "LPT1" or "LPT2" or "LPT3" or "LPT4" or "LPT5" or "LPT6" or "LPT7" or "LPT8" or "LPT9":
return true
if last character of {_input} = " " or ".":
return true
return false
command /fastworldreset [<text>] [<text>] [<text>]:
permission: fastworldreset.use
aliases: fwr
trigger:
if arg 1 is "create":
if arg 2 is not set:
send "&a&lFastWorldReset&2&l> &cPlease specify world name!"
stop
if blacklist(arg 2) is true:
send "&a&lFastWorldReset&2&l> &cYour input cannot use illegal characters!"
stop
if "%{-fwrcache::mainworld}%" = arg 2:
send "&a&lFastWorldReset&2&l> &cYou cannot use your server's main world for this!"
stop
set {_worlddir} to {-fwrcache::worlddir}
set {_w} to arg 2
set {_p} to sender
create section stored in {_section}:
Files.isDirectory(Paths.get("%{_worlddir}%/FastWorldReset/%{_w}%")) is true:
send "&a&lFastWorldReset&2&l> &e&l%{_w}% &6Hm.. This world is already in the system of FastWorldReset, but I'll continue, just in case if you are making a change to existing template." to {_p}
return true
else:
return false
run section {_section} async and store the result in {_notmissing} and wait
set {_world} to arg 2 parsed as a world
if {_world} is a world:
send "&a&lFastWorldReset&2&l> &6Hm.. seems like this world is already loaded, saving it!"
send "&a&lFastWorldReset&2&l> &eWaiting for world save..."
teleport (all players in {_world}) to (spawn point of "%{-fwrcache::mainworld}%" parsed as a world)
if {_world}.isAutoSave() is true:
{_world}.setAutoSave(false)
{_world}.save()
wait a tick
Bukkit.unloadWorld("%{_world}%", true)
while (arg 2 parsed as a world) is a world:
add 1 to {_maxtime}
if {_maxtime} is higher than 1000:
send "&cFailed to unload."
stop
wait a tick
#make reset backup of the world
set {_n} to now
if {_notmissing} is true:
set {_w} to arg 2
set {_source} to new File("%{_worlddir}%/%arg 2%/region")
set {_target} to new File("%{_worlddir}%/FastWorldReset/%arg 2%/region")
create new section stored in {_section}:
FileUtils.deleteDirectory(new File("%{_worlddir}%/FastWorldReset/%{_w}%/region"))
FileUtils.copyDirectory({_source}, {_target})
run section {_section} async and wait
else:
set {_source} to new File("%{_worlddir}%/%arg 2%")
set {_target} to new File("%{_worlddir}%/FastWorldReset/%arg 2%")
set {_targetpath} to "%{_worlddir}%/FastWorldReset/%arg 2%"
set {_p} to sender
create new section stored in {_section}:
FileUtils.copyDirectory({_source}, {_target})
if {@AllowCreatingNewWorlds} is false:
Files.isDirectory(Paths.get({_targetpath})) is false:
send "&a&lFastWorldReset&2&l> &cFailed to copy world! Are file permissions right? Check console for errors." to {_p}
return true
return false
run section {_section} async and store the result in {_failed} and wait
stop if {_failed} is true
set {fastworldreset::lastreset::%arg 2%} to new Date().getTime()
set {-fastworldresetworld::%arg 2%} to true
#load world back after reset has been made
if arg 3 is set:
set {worldgenerator::%arg 2%} to arg 3
if (arg 2 parsed as a world) is not a world:
loadWorld(arg 2)
send "&a&lFastWorldReset&2&l> &aSuccessfully copied &e&l%arg 2% &aand made it resettable in &e&l%difference between {_n} and now%&e! If it doesn't work check console for errors."
else if arg 1 is "reset":
if arg 2 is not set:
send "&a&lFastWorldReset&2&l> &cPlease specify world name!"
stop
set {_worlddir} to {-fwrcache::worlddir}
if {-fastworldresetworld::%arg 2%} is set:
set {_world} to arg 2 parsed as a world
if {_world} is not a world:
set {_worldreset} to true
else if {fastworldreset::chunkresetdisabled::%arg 2%} or {-fastworldreset::shouldreset::%arg 2%} is set:
set {_worldreset} to true
else if {fastworldreset::lastreset::%arg 2%} is not set:
set {_worldreset} to true
set {_template} to {fastworldresetclone::%arg 2%} ? arg 2
set {_ev::world} to {_world}
set {_w} to arg 2
call event (custom event "world_reset_start" with {_ev::*})
{_worldreset} is not set:
#detect world save as world save event doesn't always call
create section stored in {_section}:
if ((new File("%{_worlddir}%/%{_w}%/level.dat")).lastModified()) is higher than {fastworldreset::lastreset::%{_w}%}:
return true
run section {_section} async and store the result in {_worldreset} and wait
{_worldreset} is true:
resetWorld(arg 2, sender)
else:
teleportOut({_world}) = true
#needed to detect improperly reset world when a bad plugin is disturbing reset, such as FastAsyncWorldEdit
create section stored in {_section}:
set {_files} to new File("%{_worlddir}%/%{_world}%/region").listFiles()
Arrays.sort({_files}, LastModifiedFileComparator.LASTMODIFIED_REVERSE)
return (first element of ...{_files}).lastModified()
run section {_section} async and store the result in {_modify} and wait
if unloadChunks({_world}, false) is false:
send "&a&lFastWorldReset&2&l> &cFailed to unload chunks, falling back to world-based reset method."
resetWorld(arg 2, sender)
else:
wait a tick #needed to detect failed avoidance of chunk save caused by bad plugins
#run same section again for comparison
run section {_section} async and store the result in {_modify2} and wait
difference between {_modify} and {_modify2} is higher than 0:
send "&a&lFastWorldReset&2&l> &cFailed to not save chunks. Possibly using a bad plugin which does this? Falling back to world-based reset."
resetWorld(arg 2, sender)
add 1 to {-resetstats::%arg 2%::worldbased}
set {-resetstats::%arg 2%::last} to "world (after failed chunk save avoid, likely caused by bad plugin)"
else:
send "&a&lFastWorldReset&2&l> &cReset &e%arg 2% &cvia chunks-based method."
#stats for problem identification
add 1 to {-resetstats::%arg 2%::chunkbased}
set {-resetstats::%arg 2%::last} to "chunk"
set {_ev::world} to {_world}
set {_evt} to custom event "world_reset_complete" with {_ev::*}
call event {_evt}
else:
send "&a&lFastWorldReset&2&l> &e&l%arg 2% &cis not a FastWorldReset world!"
else if arg 1 is "load":
if blacklist(arg 2) is true:
send "&a&lFastWorldReset&2&l> &cYour input cannot use illegal characters!"
stop
if arg 2 is not set:
send "&a&lFastWorldReset&2&l> &cPlease specify world name!"
stop
if (arg 2 parsed as a world) is a world:
send "&a&lFastWorldReset&2&l> &cThe world &e%arg 2% &cis already loaded!"
stop
loadWorld(arg 2, (arg 3 ? ""))
(arg 2 parsed as a world) is a world:
send "&a&lFastWorldReset&2&l> &aSuccessfully loaded the world &e%arg 2%&a."
else if arg 1 is "clone":
if arg 2 or arg 3 is not set:
send "&cUsage: &e/fwr clone <world> <new-world>"
stop
if blacklist(arg 3) is true:
send "&a&lFastWorldReset&2&l> &cYour input cannot use illegal characters!"
stop
set {_worlddir} to {-fwrcache::worlddir}
if (arg 3 parsed as a world) is a world:
send formatted "&a&lFastWorldReset&2&l> &cThe specified world is already loaded! (%arg 3%)"
stop
set {_w} to arg 3
set {_templatedir} to "%{_worlddir}%/FastWorldReset/%arg 2%"
set {_p} to sender
create new section stored in {_section}:
Files.isDirectory(Paths.get({_templatedir})) is false:
send "&a&lFastWorldReset&2&l> &cThe specified world template is not saved in FastWorldReset folder, thus it cannot be used for cloning." to {_p}
return true
else if Files.isDirectory(Paths.get("%{_worlddir}%/%{_w}%")) is true:
send formatted "&a&lFastWorldReset&2&l> &6Hm... There's already %arg 3% world folder, I'll just load it then." to {_p}
else:
FileUtils.copyDirectory((new File("%{_templatedir}%")), (new File("%{_worlddir}%/%{_w}%")))
return false
run section {_section} async and store the result in {_stop} and wait
stop if {_stop} is true
set {-fastworldresetworld::%arg 3%} to true
set {fastworldresetclone::%arg 3%} to arg 2
set {fastworldresetclonename::%arg 3%} to arg 3
loadWorld(arg 3, {worldgenerator::%arg 2%} ? "")
if (arg 3 parsed as a world) is a world:
send formatted "&a&lFastWorldReset&2&l> &aSuccessfully cloned &e%arg 2% &aworld into &e%arg 3% &aworld!"
send "&cNote: the cloned world will not load automatically, it just has been loaded now. You have to use &e/fwr create &con it, if you want it to load automatically."
send "&bYou can configure if world folders of clones get deleted on server startup in the script file."
set {fastworldreset::lastreset::%arg 3%} to new Date().getTime()
else if arg 1 is "unload":
if arg 2 is not set:
send "&a&lFastWorldReset&2&l> &cPlease specify world name!"
stop
if "%{-fwrcache::mainworld}%" = arg 2:
send "&a&lFastWorldReset&2&l> &cYou cannot unload your server's main world!"
stop
set {_world} to arg 2 parsed as a world
{_world} is not a world:
send "&a&lFastWorldReset&2&l> &cThere is no world loaded by the name of &e%arg 2%&c."
stop
unloadWorld({_world}) = true:
send "&a&lFastWorldReset&2&l> &aSuccessfully unloaded the world &e%arg 2%&a."
else:
send "&a&lFastWorldReset&2&l> &cThere was an error in unloading the world &e%{_world}%&c, see console and report any problems to author Govindas."
else if arg 1 is "teleport" or "tp":
if arg 2 is not set:
send "&a&lFastWorldReset&2&l> &cUsage: &e/fwr teleport <world> [player]"
stop
if (arg 2 parsed as a world) is not a world:
send "&a&lFastWorldReset&2&l> &cThe specified world &e%arg 2% &cis not loaded."
stop
if arg 3 is not set:
set {_player} to player
else:
if arg 3 parsed as a player is offline:
send "&a&lFastWorldReset&2&l> &cThe specified player &e%{_player}% &cis offline."
stop
set {_player} to arg 3 parsed as a player
teleport {_player} to (spawn point of arg 2 parsed as a world)
else if arg 1 is "delete":
if arg 2 is not set:
send "&a&lFastWorldReset&2&l> &cPlease specify world name!"
stop
if {-fastworldresetworld::%arg 2%} is not set:
send "&a&lFastWorldReset&2&l> &cYou must specify existing world name."
stop
set {_worlddir} to {-fwrcache::worlddir}
{-confirmfwrdelete::%sender%::%arg 2%} is set:
if difference between {-confirmfwrdelete::%sender%::%arg 2%} and now is higher than 15 seconds:
delete {-confirmfwrdelete::%sender%::%arg 2%}
if {-confirmfwrdelete::%sender%::%arg 2%} is not set:
set {-confirmfwrdelete::%sender%::%arg 2%} to now
send colored "&a&lFastWorldReset&2&l> &6Are you sure you want to delete %arg 2%? Type the command again to confirm. (You have 15 seconds)"
stop
if {worldgenerator::%arg 2%} is set:
delete {worldgenerator::%arg 2%}
if {worldenvironment::%arg 2%} is set:
delete {worldenvironment::%arg 2%}
if {fastworldreset::chunkresetdisabled::%arg 2%} is set:
delete {fastworldreset::chunkresetdisabled::%arg 2%}
if {-resetstats::%arg 2%::*} is set:
delete {-resetstats::%arg 2%::*}
#just in case if it's a clone
if {fastworldresetclone::%arg 2%} is set:
delete {fastworldresetclone::%arg 2%}
set {_clone} to true
if {fastworldresetclonename::%arg 2%} is set:
delete {fastworldresetclonename::%arg 2%}
#
delete {-fastworldresetworld::%arg 2%}
if (arg 2 parsed as a world) is a world:
send "&a&lFastWorldReset&2&l> &6Hm.. Seems like this world is loaded, unloading it and saving!"
if unloadWorld((arg 2 parsed as a world), true) is true:
send "&a&lFastWorldReset&2&l> &6Unloaded successfully!"
else:
send "&a&lFastWorldReset&2&l> &cFailed to unload."
send "&a&lFastWorldReset&2&l> &aYou have removed world &e&l%arg 2% &afrom the system of FastWorldReset."
set {_w} to arg 2
set {_p} to sender
create section stored in {_section}:
if {_clone} is set:
FileUtils.deleteDirectory(new File("%{_worlddir}%/%{_w}%"))
send "&a&lFastWorldReset&2&l> &6Since this world is a clone, deleted it from the world folder." to {_p}
else:
FileUtils.deleteDirectory(new File("%{_worlddir}%/FastWorldReset/%{_w}%"))
send colored "&a&lFastWorldReset&2&l> &6Deleted the world template from %{_worlddir}%/FastWorldReset folder, but still kept the world in your world folder for manual deletion." to {_p}
run section {_section} async and wait
else if arg 1 is "exit":
if sender is not a player:
send "&a&lFastWorldReset&2&l> &cThis command is only executable as a player, if you want this to work on console, please contact the author Govindas."
stop
set {fastworldreset::exitpoint} to location of player
send "&a&lFastWorldReset&2&l> &aYou have successfully set exit point at &e&l%{fastworldreset::exitpoint}%"
else if arg 1 is "disable-chunk-reset":
if (arg 2 parsed as a world) is not a world:
send "&a&lFastWorldReset&2&l> &cThe specified world &e%arg 2% &cis not loaded."
stop
if {fastworldreset::chunkresetdisabled::%arg 2%} is not set:
set {fastworldreset::chunkresetdisabled::%arg 2%} to true
(arg 2 parsed as a world).setAutoSave(true)
send "&a&lFastWorldReset&2&l> &aYou have disabled chunk-based reset for the world &e%arg 2% &aplease note that this makes reset slower and more likely to cause lag spikes, but it makes chunks automatically unload in the world as usual, making the world lighter on RAM usage."
else:
send "&a&lFastWorldReset&2&l> &cChunk-based reset is already disabled in world &e%arg 2%&c!"
else if arg 1 is "enable-chunk-reset":
if (arg 2 parsed as a world) is not a world:
send "&a&lFastWorldReset&2&l> &cThe specified world &e%arg 2% &cis not loaded."
stop
if {fastworldreset::chunkresetdisabled::%arg 2%} is set:
delete {fastworldreset::chunkresetdisabled::%arg 2%}
send "&a&lFastWorldReset&2&l> &aYou have enabled chunk-based reset for the world &e%arg 2% &aagain! Yay, faster world reset!"
(arg 2 parsed as a world).setAutoSave(false)
else:
send "&a&lFastWorldReset&2&l> &cChunk-based reset is already enabled for the world &e%arg 2%&c!"
else if arg 1 is "worlds":
send " "
send "&6- &a&l Worlds &6-"
loop all worlds:
send formatted "&e%loop-world% &6- &e&l%amount of ...loop-world.getLoadedChunks()% &cchunks &e&l%amount of entities in loop-world% &centities"
else if arg 1 is "unload-chunks":
if (arg 2 parsed as a world) is not a world:
send "&a&lFastWorldReset&2&l> &cThe specified world &e%arg 2% &cis not loaded."
stop
if (arg 3 parsed as a boolean) is not a boolean:
send "&a&lFastWorldReset&2&l> &cUsage: &e/fwr unload-chunks <world> <boolean> &cThe boolean stands for saving true or saving false."
stop
set {_saving} to arg 3 parsed as a boolean
set {_world} to arg 2 parsed as a world
set {-unloading::%{_world}%} to true
unloadChunks(({_world}), {_saving}) = true
delete {-unloading::%{_world}%}
if amount of ...{_world}.getLoadedChunks() is not 0:
send "&a&lFastWorldReset&2&l> &cSome chunks have failed to unload, due to some plugin or players keeping chunks loaded."
else if arg 1 is "generator":
if arg 2 or arg 3 is not set:
send "&cUsage: &e/fwr generator <world> <generator>"
send "&cList of World Generators: &eDefault, %{-worldgenerators::*} ? """"%"
stop
set {_worlddir} to {-fwrcache::worlddir}
if {-fastworldresetworld::%arg 2%} is not set:
send "&a&lFastWorldReset&2&l> &e%arg 2% &cis not a FastWorldReset world."
stop
else:
set {_prev} to "%{worldgenerator::%arg 2%}%"
arg 3 is not "Default":
set {worldgenerator::%arg 2%} to arg 3
else:
delete {worldgenerator::%arg 2%}
if "%{worldgenerator::%arg 2%}%" is {_prev}:
send formatted "&a&lFastWorldReset&2&l> &e%arg 2% &cis already using &e%arg 3%&c!"
stop
send "&a&lFastWorldReset&2&l> &aYou have set the world generator of &e%arg 2% &ato &e%arg 3%"
if {-worldgenerators::%arg 3%} is not set:
send colored "&cWARNING: The specified generator (%arg 3%) has not been found on the server, but this may be a mistake."
else if arg 1 is "generators":
send colored "&a&lFastWorldReset&2&l> &cList of World Generators: &eDefault, %{-worldgenerators::*} ? """"%"
else if arg 1 is "resetstats":
if arg 2 is set:
set {_world} to arg 2
else:
set {_world} to "%player's world%"
if {-resetstats::%{_world}%::last} is set:
send "&cLast reset type: &e&l%{-resetstats::%{_world}%::last} ? ""Unknown""%"
send "&cWorld-based resets: &e&l%{-resetstats::%{_world}%::worldbased} ? 0%"
send "&cChunk-based resets: &e&l%{-resetstats::%{_world}%::chunkbased} ? 0%"
else:
send "&a&lFastWorldReset&2&l> &cNo reset data found for this world. Possibly it was never reset since server restart."
else if arg 1 is "info":
if arg 2 is not set:
send "&cUsage: &e/fwr info <world>"
stop
{-fastworldresetworld::%arg 2%} is set:
send formatted "&a&lWorld: &e%{-fastworldresetworld::%arg 2%}%"
if {fastworldreset::chunkresetdisabled::%{_world}%} is set:
set {_type} to "world-based"
else:
set {_type} to "chunk-based with fallback to world-based if it fails"
send formatted "&a&lReset Type: &e%{_type}%"
send formatted "&a&lGenerator: &e%{worldgenerator::%arg 2%} ? ""Default""%"
if arg 2 parsed as a world is a world:
set {_loaded} to true
else:
set {_loaded} to false
send formatted "&a&lIs Loaded: &e%{_loaded}%"
else:
send formatted "&a&lFastWorldReset&2&l> &cThe specified world (%arg 2%) is not a FastWorldReset world."
else:
send "&2&l--=-= &a&lFastWorldReset Commands &2&l=-=--"
send "&f/fwr create <world> [generator] &a- Add a world to FastWorldReset system or create a completely new world into the system."
send "&f/fwr delete <world> &a- Removes the world from FastWorldReset system, does not unload or delete the world."
send "&f/fwr clone <world> <new-world> &a- Clones a FWR world."
send "&f/fwr reset <world> &a- to reset a world. (Must be in FastWorldReset system)"
send "&f/fwr exit &a- sets exit point to where players get teleported when their world resets."
send "&f/fwr load <world> [generator] &a- loads a world. Optionally with a generator."
send "&f/fwr unload <world> [<true/false>] &a- loads a world. true/false if the world should be saved, defaults to true."
send formatted "<ttp:&f/fwr unload <world>%new line%&7May be useful for unloading worlds which you no longer need.>&f/fwr unload <world> &a- unload a world."
send "&f/fwr unload-chunks <world> <true/false> &a- unloads chunks in the specified world, with saving or without."
send "&f/fwr tp <world> [player] &a- teleport to a world or teleport another player to the world."
send formatted "<ttp:&f/fwr disable-chunk-reset <world>%{_newline}%&7May be useful in situations where the world uses very much RAM and you prefer slower reset than higher RAM usage>&f/fwr disable-chunk-reset <world> &a- disables chunk-based resetting of the specified world, which makes the chunks unload normally."
send "&f/fwr enable-chunk-reset <world> &a- enables chunk-based reset, this is enabled by default for fastest speed, see above to disable. (This is only meant for things like minigame maps, not where you need changes to persist across server restarts!)"
send "&f/fwr worlds &a- shows the list of all worlds and their data."
send "&f/fwr generator <world> <generator> &a- set a generator for a world."
send "&f/fwr generators &a- list all world generators detected on the server."
send "&f/fwr info <world> &a- see information about the specified world."
send "&f/fwr resetstats &a- useful for debugging of reset bugs."
send "&6Hover your mouse on the commands to see extra comments and tips."
function initWorlds():
#just in case if the user hasn't configured variables starting with "-" character to not save to database.
delete {-fastworldresetworld::*}, {-unloading::*}, {-resetting::*} and {-initload::*}
set {_worlddir} to {-fwrcache::worlddir}
create section stored in {_section}:
set {_files::*} to ...new File("%{_worlddir}%/FastWorldReset").listFiles()
loop {_files::*}:
Files.isDirectory(Paths.get("%loop-value%")) is true:
set {_value} to "%loop-value%"
set {_r::*} to split "%loop-value%" by "%{-fwrcache::fileseparator}%FastWorldReset%{-fwrcache::fileseparator}%"
set {_return::%loop-index%} to {_r::2}
return {_return::*}
run section {_section} async and store the result in {_worlds::*} and wait
loop {_worlds::*}:
set {-fastworldresetworld::%loop-value%} to loop-value
if {@ResetResettableWorldsOnRestart} is true:
make console execute command "/fastworldreset reset %loop-value%"
else if loop-value parsed as a world is not a world:
loadWorld(loop-value)
function deleteClones():
set {_worlddir} to {-fwrcache::worlddir}
create section stored in {_section}:
loop {fastworldresetclone::*}:
FileUtils.deleteDirectory(new File("%{_worlddir}%/%{fastworldresetclonename::%loop-index%}%"))
run section {_section} async and wait
delete {fastworldresetclone::*}
delete {fastworldresetclonename::*}
on skript load:
wait a second
initWorlds()
if {@AutoDeleteClonesOnRestart} is true:
deleteClones()
#chunk-based reset, which resets by not saving chunks and re-loading them instead of reloading the whole world. it reloads the world only if the world got saved by some system.
on chunk unload:
{-fastworldresetworld::%event-world%} is set
{fastworldreset::chunkresetdisabled::%event-world%} is not set
{-resetting::%event-world%} is not set
{-unloading::%event-world%} is not set
cancel event
on world init:
if {-initload::%event-world%} is set:
delete {-initload::%event-world%}
event-world.setKeepSpawnInMemory(false)
{fastworldresetclone::%event-world%} is set:
set {_options} to {fastworldresetclone::%event-world%}
else:
set {_options} to "%event-world%"
{-fastworldresetworld::%{_options}%} is set
{fastworldreset::chunkresetdisabled::%{_options}%} is not set:
event-world.setAutoSave(false)
#for Paper servers, to disable saving
#setting it to variable for better performance for doing it twice
set {_config} to event-world.getHandle().paperConfig
set {_config}.autoSavePeriod to 0
set {_config}.maxAutoSaveChunksPerTick to 0
#needed, because if it is set as true... entity ticking may totally bug out with the way FastWorldReset's chunk-based reset mehod works
#it may sometimes cause behavior like projectiles constantly teleporting back and never landing if set to true
set {_config}.skipEntityTickingInChunksScheduledForUnload to false
event-world.setKeepSpawnInMemory(false)
on world saving:
{fastworldreset::chunkresetdisabled::%event-world%} is not set
set {-fastworldreset::shouldreset::%event-world%} to true
on skript unload:
loop all worlds:
{-fastworldresetworld::%loop-world%} is set
{fastworldreset::chunkresetdisabled::%loop-world%} is not set
unloadWorld(loop-world, false)