added world_time and day_time. Add ability to store app data in shared space. as well as store in file hierarchies. Fixes #215

This commit is contained in:
gnembon committed 2020-04-15 01:58:46 -03:00
1 parent 3c25110b50
commit 8dbf269fde
8 files changed
+150 -51

No files matched your search

+37 -7
View File
@@ -3007,23 +3007,52 @@ run('give @s stone 4') -> 1 // this operation was successful once
Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled due to
performance reasons and saves the world only on demand.
### `load_app_data(), load_app_data(file)`
### `load_app_data(), load_app_data(file), load_app_data(file, shared?)`
Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory
managed and buffered / throttled NBT tag. With a file name - reads explicitly a file with that name from the
scripts folder.
scripts folder that belongs exclusively to the app. if `shared` is true, the file location is not exclusive
to the app anymore, but located in a shared app space.
File descriptor can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped
from the name before saving/loading. Empty descriptors are invalid.
Function returns nbt value with the file content, or `null` if the file is missing or there were problems
with retrieving the data.
The default no-name app, via `/script run` command can only save/load file from the shared data location.
If the app's name is `'foo'`, the script location would
be `world/scripts/foo.sc`, system-managed default app data storage is in `world/scripts/foo.data.nbt`, app
specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and shared data space is under
`world/scripts/shared/bar/../baz.nbt`.
You can use app data to save non-vanilla information separately from the world and other scripts.
### `store_app_data(tag), store_app_data(tag, file)`
### `store_app_data(tag), store_app_data(tag, file), store_app_data(tag, file, shared?)`
Stores the app data associated with the app from the world /scripts folder. With the `file` parameter saves
immediately and with every call, without `file` parameter, it may take up to 10 seconds for the output file
Stores the app data associated with the app from the world `/scripts` folder. With the `file` parameter saves
immediately and with every call to a specific file defined by the `file`, either in app space, or in the scripts
shared space if `shared` is true. Without `file` parameter, it may take up to 10
seconds for the output file
to sync preventing flickering in case this tag changes frequently. It will be synced when server closes.
Returns `true` if the file was saved successfully, `false` otherwise.
Uses the same file structure for exclusive app data, and shared data folder as `load_app_data`.
### `tick_time()`
Returns game tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
### `world_time()`
Returns dimension-specific tick counter.
### `day_time(new_time?)`
Returns current daytime clock value. If `new_time` is specified, sets a new clock
to that value. Daytime clocks are shared between all dimensions.
### `game_tick(mstime?)`
@@ -3074,7 +3103,8 @@ For the options of `entry`, consult your statistics page, or give it a guess.
The call will return `null` if the statistics options are incorrect, or player didn't get these in their history.
If the player encountered the statistic, or game created for him empty one, it will return a number.
Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response
it could either mean your input is wrong, or statistic has effectively a value of `0`.# `/script run` command
it could either mean your input is wrong, or statistic has effectively a value of `0`.
# `/script run` command
Primary way to input commands. The command executes in the context, position, and dimension of the executing player,
commandblock, etc... The command receives 4 variables, `x`, `y`, `z` and `p` indicating position and
+36 -7
View File
@@ -79,23 +79,52 @@ run('give @s stone 4') -> 1 // this operation was successful once
Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled due to
performance reasons and saves the world only on demand.
### `load_app_data(), load_app_data(file)`
### `load_app_data(), load_app_data(file), load_app_data(file, shared?)`
Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory
managed and buffered / throttled NBT tag. With a file name - reads explicitly a file with that name from the
scripts folder.
scripts folder that belongs exclusively to the app. if `shared` is true, the file location is not exclusive
to the app anymore, but located in a shared app space.
File descriptor can contain letters, numbers and folder separator: `'/'`. Any other characters are stripped
from the name before saving/loading. Empty descriptors are invalid.
Function returns nbt value with the file content, or `null` if the file is missing or there were problems
with retrieving the data.
The default no-name app, via `/script run` command can only save/load file from the shared data location.
If the app's name is `'foo'`, the script location would
be `world/scripts/foo.sc`, system-managed default app data storage is in `world/scripts/foo.data.nbt`, app
specific data directory is under `world/scripts/foo.data/bar/../baz.nbt`, and shared data space is under
`world/scripts/shared/bar/../baz.nbt`.
You can use app data to save non-vanilla information separately from the world and other scripts.
### `store_app_data(tag), store_app_data(tag, file)`
### `store_app_data(tag), store_app_data(tag, file), store_app_data(tag, file, shared?)`
Stores the app data associated with the app from the world /scripts folder. With the `file` parameter saves
immediately and with every call, without `file` parameter, it may take up to 10 seconds for the output file
Stores the app data associated with the app from the world `/scripts` folder. With the `file` parameter saves
immediately and with every call to a specific file defined by the `file`, either in app space, or in the scripts
shared space if `shared` is true. Without `file` parameter, it may take up to 10
seconds for the output file
to sync preventing flickering in case this tag changes frequently. It will be synced when server closes.
Returns `true` if the file was saved successfully, `false` otherwise.
Uses the same file structure for exclusive app data, and shared data folder as `load_app_data`.
### `tick_time()`
Returns game tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
### `world_time()`
Returns dimension-specific tick counter.
### `day_time(new_time?)`
Returns current daytime clock value. If `new_time` is specified, sets a new clock
to that value. Daytime clocks are shared between all dimensions.
### `game_tick(mstime?)`
@@ -146,4 +175,4 @@ For the options of `entry`, consult your statistics page, or give it a guess.
The call will return `null` if the statistics options are incorrect, or player didn't get these in their history.
If the player encountered the statistic, or game created for him empty one, it will return a number.
Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response
it could either mean your input is wrong, or statistic has effectively a value of `0`.
it could either mean your input is wrong, or statistic has effectively a value of `0`.
+3 -1
View File
@@ -13,6 +13,7 @@ brightness
chunk_tickets
create_marker
current_dimension
day_time
destroy
diamond
drop_item
@@ -95,4 +96,5 @@ top
transparent
update
volume
without_updates
without_updates
world_time
@@ -67,7 +67,7 @@
logger sound particle particle_line particle_rect
run save tick_time game_tick seed current_dimension in_dimension
run save tick_time world_time day_time game_tick seed current_dimension in_dimension
plop harvest destroy schedule nbt create_marker remove_all_markers load_app_data store_app_data escape_nbt statistic
</Keywords>
@@ -2669,6 +2669,24 @@ public class CarpetExpression
return (cc, tt) -> time;
});
this.expr.addLazyFunction("world_time", 0, (c, t, lv) ->
{
Value time = new NumericValue(((CarpetContext) c).s.getWorld().getTime());
return (cc, tt) -> time;
});
this.expr.addLazyFunction("day_time", -1, (c, t, lv) ->
{
Value time = new NumericValue(((CarpetContext) c).s.getWorld().getTimeOfDay());
if (lv.size() > 0)
{
long newTime = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
if (newTime < 0) newTime = 0;
((CarpetContext) c).s.getWorld().setTimeOfDay(newTime);
}
return (cc, tt) -> time;
});
this.expr.addLazyFunction("game_tick", -1, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
if (!s.getMinecraftServer().isOnThread()) throw new InternalExpressionException("Unable to run ticks from threads");
@@ -2805,16 +2823,22 @@ public class CarpetExpression
this.expr.addLazyFunction("load_app_data", -1, (c, t, lv) ->
{
String file = null;
boolean shared = false;
if (lv.size()>0)
{
String origfile = lv.get(0).evalValue(c).getString();
file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9]", "");
file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9/]", "");
file = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/"));
if (file.isEmpty())
{
throw new InternalExpressionException("Cannot use "+file+" as resource name - must have some letters and numbers");
throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have at least some letters and numbers");
}
if (lv.size() > 1)
{
shared = lv.get(1).evalValue(c).getBoolean();
}
}
Tag state = ((CarpetScriptHost)((CarpetContext)c).host).getGlobalState(file);
Tag state = ((CarpetScriptHost)((CarpetContext)c).host).getGlobalState(file, shared);
if (state == null)
return (cc, tt) -> Value.NULL;
Value retVal = new NBTSerializableValue(state);
@@ -2827,21 +2851,27 @@ public class CarpetExpression
throw new InternalExpressionException("'store_app_data' needs NBT tag and an optional file");
Value val = lv.get(0).evalValue(c);
String file = null;
boolean shared = false;
if (lv.size()>1)
{
String origfile = lv.get(1).evalValue(c).getString();
file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9]", "");
file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9/]", "");
file = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/"));
if (file.isEmpty())
{
throw new InternalExpressionException("Cannot use "+file+" as resource name - must have some letters and numbers");
throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have some letters and numbers");
}
if (lv.size() > 2)
{
shared = lv.get(2).evalValue(c).getBoolean();
}
}
NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
? (NBTSerializableValue) val
: new NBTSerializableValue(val.getString());
Tag tag = tagValue.getTag();
((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file);
return (cc, tt) -> Value.NULL;
boolean success = ((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file, shared);
return success?LazyValue.TRUE:LazyValue.FALSE;
});
this.expr.addLazyFunction("statistic", 3, (c, t, lv) ->
@@ -361,32 +361,31 @@ public class CarpetScriptHost extends ScriptHost
private void dumpState()
{
main.saveData(null, globalState);
Module.saveData(main, null, globalState, false);
}
private Tag loadState()
{
return main.getData(null);
return Module.getData(main, null, false);
}
public Tag getGlobalState(String file)
public Tag getGlobalState(String file, boolean isShared)
{
if (getName() == null ) return null;
if (getName() == null && !isShared) return null;
if (file != null)
return main.getData(file);
return Module.getData(main, file, isShared);
if (parent == null)
return globalState;
return ((CarpetScriptHost)parent).globalState;
}
public void setGlobalState(Tag tag, String file)
public boolean setGlobalState(Tag tag, String file, boolean isShared)
{
if (getName() == null ) return;
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
if (file!= null)
{
main.saveData(file, tag);
return;
return Module.saveData(main, file, tag, isShared);
}
CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this;
@@ -396,6 +395,7 @@ public class CarpetScriptHost extends ScriptHost
responsibleHost.dumpState();
responsibleHost.saveTimeout = 200;
}
return true;
}
public void tick()
@@ -74,7 +74,7 @@ public class FileModule extends Module
}
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
public static void write(Tag tag_1, File file)
public static boolean write(Tag tag_1, File file)
{
File file_2 = new File(file.getAbsolutePath() + "_tmp");
if (file_2.exists()) file_2.delete();
@@ -90,9 +90,10 @@ public class FileModule extends Module
}
catch (IOException e)
{
return;
return false;
}
if (file.exists()) file.delete();
if (!file.exists()) file_2.renameTo(file);
return true;
}
}
+24 -17
View File
@@ -4,7 +4,6 @@ import carpet.CarpetServer;
import net.minecraft.nbt.Tag;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import static carpet.script.bundled.FileModule.read;
@@ -16,30 +15,38 @@ public abstract class Module
public abstract String getCode();
public abstract boolean isLibrary();
public Tag getData(String file)
private static String getDescriptor(Module module, String file, boolean isShared)
{
if (isShared)
{
return "shared/"+file;
}
else if (module != null && module.getName() != null) // appdata
{
return module.getName()+".data"+(file==null?"":"/"+file);
}
else
{
throw new RuntimeException("Invalid file descriptor: "+file);
}
}
public static Tag getData(Module module, String file, boolean isShared)
{
if (!isShared && (module == null || module.getName() == null)) return null;
File dataFile = CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getDescriptor(module, file, isShared)+".nbt");
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
return read(dataFile);
}
public void saveData(String file, Tag globalState)
public static boolean saveData(Module module, String file, Tag globalState, boolean isShared)
{
if (!isShared && (module == null || module.getName() == null)) return false;
File dataFile =CarpetServer.minecraft_server.getLevelStorage().resolveFile(
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt");
if (!Files.exists(dataFile.toPath().getParent()))
{
try
{
Files.createDirectory(dataFile.toPath().getParent());
}
catch (IOException ignored)
{
}
}
write(globalState, dataFile);
CarpetServer.minecraft_server.getLevelName(), "scripts/"+getDescriptor(module, file, isShared)+".nbt");
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
return write(globalState, dataFile);
}
@Override