#------------- 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!
#whether it should only reset "region" world folder contents and not other things like level.dat (It's cheaper to reset only region folder, but it doesn't reset world metadata, only the chunks, which may be not what you wish)
#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
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
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}%}
#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)))
#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":
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)
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
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."
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:
send formatted "&a&lFastWorldReset&2&l> &cThe specified world (%arg 2%) is not a FastWorldReset world."
else if arg 1 is "optimize-template":
if arg 2 is not set:
send "&cUsage: &e/fwr optimize-template <world>"
stop
{-fastworldresetworld::%arg 2%} is set:
set {_worlddir} to {-fwrcache::worlddir}
set {_templatedir} to new File("%{_worlddir}%/FastWorldReset/%{-fastworldresetworld::%arg 2%}%")
set {_files::*} to ...{_templatedir}.listFiles()
loop {_files::*}:
if "%loop-value%" ends with "/region":
continue loop
else if "%loop-value%" ends with "/level.dat":
continue loop
add 1 to {_i}
set {_unneededfiles::%{_i}%} to "%loop-value%"
if amount of {_unneededfiles::*} is not 0:
send formatted "&a&lFastWorldReset&2&l> &cFound &e&l%amount of {_unneededfiles::*}% &cfiles that may be not needed for this world to function. It may be a good idea to delete them (unless they're needed for your world, maybe a plugin saved data here? but that's unlikely)"
send {_unneededfiles::*}
send formatted "&a&lFastWorldReset&2&l> &eYou can delete them manually by going to the directory. Only two things are required for world to function: the region folder and level.dat&e file. Make sure to not delete these two."
else:
send "&a&lFastWorldReset&2&l> &eThis template is already well optimized, no useless files found!"
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."
#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