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:
1 parent
3c25110b50
commit
8dbf269fde
8 files changed
+148
-49
No files matched your search
+37
-7
@@ -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
|
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.
|
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
|
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
|
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.
|
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
|
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
|
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.
|
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()`
|
### `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?)`
|
### `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.
|
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.
|
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
|
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,
|
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
|
commandblock, etc... The command receives 4 variables, `x`, `y`, `z` and `p` indicating position and
|
||||||
|
|||||||
@@ -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
|
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.
|
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
|
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
|
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.
|
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
|
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
|
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.
|
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()`
|
### `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?)`
|
### `game_tick(mstime?)`
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ brightness
|
|||||||
chunk_tickets
|
chunk_tickets
|
||||||
create_marker
|
create_marker
|
||||||
current_dimension
|
current_dimension
|
||||||
|
day_time
|
||||||
destroy
|
destroy
|
||||||
diamond
|
diamond
|
||||||
drop_item
|
drop_item
|
||||||
@@ -96,3 +97,4 @@ transparent
|
|||||||
update
|
update
|
||||||
volume
|
volume
|
||||||
without_updates
|
without_updates
|
||||||
|
world_time
|
||||||
@@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
logger sound particle particle_line particle_rect
|
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
|
plop harvest destroy schedule nbt create_marker remove_all_markers load_app_data store_app_data escape_nbt statistic
|
||||||
</Keywords>
|
</Keywords>
|
||||||
|
|||||||
@@ -2669,6 +2669,24 @@ public class CarpetExpression
|
|||||||
return (cc, tt) -> time;
|
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) -> {
|
this.expr.addLazyFunction("game_tick", -1, (c, t, lv) -> {
|
||||||
ServerCommandSource s = ((CarpetContext)c).s;
|
ServerCommandSource s = ((CarpetContext)c).s;
|
||||||
if (!s.getMinecraftServer().isOnThread()) throw new InternalExpressionException("Unable to run ticks from threads");
|
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) ->
|
this.expr.addLazyFunction("load_app_data", -1, (c, t, lv) ->
|
||||||
{
|
{
|
||||||
String file = null;
|
String file = null;
|
||||||
|
boolean shared = false;
|
||||||
if (lv.size()>0)
|
if (lv.size()>0)
|
||||||
{
|
{
|
||||||
String origfile = lv.get(0).evalValue(c).getString();
|
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())
|
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)
|
if (state == null)
|
||||||
return (cc, tt) -> Value.NULL;
|
return (cc, tt) -> Value.NULL;
|
||||||
Value retVal = new NBTSerializableValue(state);
|
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");
|
throw new InternalExpressionException("'store_app_data' needs NBT tag and an optional file");
|
||||||
Value val = lv.get(0).evalValue(c);
|
Value val = lv.get(0).evalValue(c);
|
||||||
String file = null;
|
String file = null;
|
||||||
|
boolean shared = false;
|
||||||
if (lv.size()>1)
|
if (lv.size()>1)
|
||||||
{
|
{
|
||||||
String origfile = lv.get(1).evalValue(c).getString();
|
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())
|
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 tagValue = (val instanceof NBTSerializableValue)
|
||||||
? (NBTSerializableValue) val
|
? (NBTSerializableValue) val
|
||||||
: new NBTSerializableValue(val.getString());
|
: new NBTSerializableValue(val.getString());
|
||||||
Tag tag = tagValue.getTag();
|
Tag tag = tagValue.getTag();
|
||||||
((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file);
|
boolean success = ((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file, shared);
|
||||||
return (cc, tt) -> Value.NULL;
|
return success?LazyValue.TRUE:LazyValue.FALSE;
|
||||||
});
|
});
|
||||||
|
|
||||||
this.expr.addLazyFunction("statistic", 3, (c, t, lv) ->
|
this.expr.addLazyFunction("statistic", 3, (c, t, lv) ->
|
||||||
|
|||||||
@@ -361,32 +361,31 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
|
|
||||||
private void dumpState()
|
private void dumpState()
|
||||||
{
|
{
|
||||||
main.saveData(null, globalState);
|
Module.saveData(main, null, globalState, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tag loadState()
|
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)
|
if (file != null)
|
||||||
return main.getData(file);
|
return Module.getData(main, file, isShared);
|
||||||
if (parent == null)
|
if (parent == null)
|
||||||
return globalState;
|
return globalState;
|
||||||
return ((CarpetScriptHost)parent).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)
|
if (file!= null)
|
||||||
{
|
{
|
||||||
main.saveData(file, tag);
|
return Module.saveData(main, file, tag, isShared);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this;
|
CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this;
|
||||||
@@ -396,6 +395,7 @@ public class CarpetScriptHost extends ScriptHost
|
|||||||
responsibleHost.dumpState();
|
responsibleHost.dumpState();
|
||||||
responsibleHost.saveTimeout = 200;
|
responsibleHost.saveTimeout = 200;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick()
|
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
|
//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");
|
File file_2 = new File(file.getAbsolutePath() + "_tmp");
|
||||||
if (file_2.exists()) file_2.delete();
|
if (file_2.exists()) file_2.delete();
|
||||||
@@ -90,9 +90,10 @@ public class FileModule extends Module
|
|||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (file.exists()) file.delete();
|
if (file.exists()) file.delete();
|
||||||
if (!file.exists()) file_2.renameTo(file);
|
if (!file.exists()) file_2.renameTo(file);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,7 +4,6 @@ import carpet.CarpetServer;
|
|||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
||||||
import static carpet.script.bundled.FileModule.read;
|
import static carpet.script.bundled.FileModule.read;
|
||||||
@@ -16,30 +15,38 @@ public abstract class Module
|
|||||||
public abstract String getCode();
|
public abstract String getCode();
|
||||||
public abstract boolean isLibrary();
|
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(
|
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;
|
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
|
||||||
return read(dataFile);
|
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(
|
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().getParent()))
|
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
|
||||||
{
|
return write(globalState, dataFile);
|
||||||
try
|
|
||||||
{
|
|
||||||
Files.createDirectory(dataFile.toPath().getParent());
|
|
||||||
}
|
|
||||||
catch (IOException ignored)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write(globalState, dataFile);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in new issue
Block a user