Merge branch 'master' into 1.16

# Conflicts:
#	src/main/java/carpet/script/bundled/Module.java
This commit is contained in:
gnembon committed 2020-04-15 02:00:31 -03:00
commit a757c6f88a
15 files changed
+219 -64

No files matched your search

+43 -10
View File
@@ -2932,10 +2932,13 @@ Collection of other methods that control smaller, yet still important aspects of
## Sounds
### `sound(name, pos, volume?, pitch?)`
### `sound(name, pos, volume?, pitch?, mixer?)`
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`. `pos` can be
either a block, triple of coords, or a list of thee numbers. Uses the same options as a corresponding `playsound` command.
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`, and under
optional `mixer`. Default values for `volume`, `pitch` and `mixer` are `1.0`, `1.0`, and `master`.
Valid mixer options are `master`, `music`, `record`, `weather`, `block`, `hostile`,`neutral`, `player`, `ambient`
and `voice`. `pos` can be either a block, triple of coords, or a list of thee numbers. Uses the same options as a
corresponding `playsound` command.
## Particles
@@ -3004,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?)`
@@ -3071,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
+42 -10
View File
@@ -4,10 +4,13 @@ Collection of other methods that control smaller, yet still important aspects of
## Sounds
### `sound(name, pos, volume?, pitch?)`
### `sound(name, pos, volume?, pitch?, mixer?)`
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`. `pos` can be
either a block, triple of coords, or a list of thee numbers. Uses the same options as a corresponding `playsound` command.
Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`, and under
optional `mixer`. Default values for `volume`, `pitch` and `mixer` are `1.0`, `1.0`, and `master`.
Valid mixer options are `master`, `music`, `record`, `weather`, `block`, `hostile`,`neutral`, `player`, `ambient`
and `voice`. `pos` can be either a block, triple of coords, or a list of thee numbers. Uses the same options as a
corresponding `playsound` command.
## Particles
@@ -76,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?)`
@@ -143,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>
@@ -43,7 +43,8 @@ public class TickCommand
c.getSource(),
getInteger(c,"ticks"),
getString(c, "tail command")))))).
then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource()))).
then(literal("freeze").executes( (c)-> toggleFreeze(c.getSource(), false)).
then(literal("deep").executes( (c)-> toggleFreeze(c.getSource(), true)))).
then(literal("step").
executes((c) -> step(1)).
then(argument("ticks", integer(1,72000)).
@@ -95,15 +96,18 @@ public class TickCommand
return 1;
}
private static int toggleFreeze(ServerCommandSource source)
private static int toggleFreeze(ServerCommandSource source, boolean isDeep)
{
TickSpeed.is_paused = !TickSpeed.is_paused;
if (TickSpeed.is_paused)
{
Messenger.m(source, "gi Game is paused");
TickSpeed.deepFreeze = isDeep;
Messenger.m(source, "gi Game is "+(isDeep?"deeply ":"")+"frozen");
}
else
{
TickSpeed.deepFreeze = false;
Messenger.m(source, "gi Game runs normally");
}
return 1;
@@ -25,6 +25,7 @@ public class TickSpeed
public static ServerCommandSource tick_warp_sender = null;
public static int player_active_timeout = 0;
public static boolean process_entities = true;
public static boolean deepFreeze = false;
public static boolean is_paused = false;
public static boolean is_superHot = false;
@@ -9,6 +9,8 @@ public class TNTLogHelper
{
public boolean initialized;
private double primedX, primedY, primedZ;
private static long lastGametime = 0;
private static int tntCount = 0;
private Vec3d primedAngle;
/**
* Runs when the TNT is primed. Expects the position and motion angle of the TNT.
@@ -24,8 +26,13 @@ public class TNTLogHelper
/**
* Runs when the TNT explodes. Expects the position of the TNT.
*/
public void onExploded(double x, double y, double z)
public void onExploded(double x, double y, double z, long gametime)
{
if (!(lastGametime == gametime)){
tntCount = 0;
lastGametime = gametime;
}
tntCount++;
LoggerRegistry.getLogger("tnt").log( (option) -> {
switch (option)
{
@@ -36,6 +43,10 @@ public class TNTLogHelper
"r E ",Messenger.dblt("r",x, y, z))};
case "full":
return new BaseText[]{Messenger.c(
"g #" + String.valueOf(tntCount),
"m @" + String.valueOf(gametime),
"g : ",
"g #" + String.valueOf(tntCount) + ": ",
"l P ",Messenger.dblf("l",primedX,primedY,primedZ),
"w ",Messenger.dblf("l", primedAngle.x, primedAngle.y, primedAngle.z),
"r E ",Messenger.dblf("r",x, y, z))};
@@ -0,0 +1,23 @@
package carpet.mixins;
import carpet.helpers.TickSpeed;
import net.minecraft.server.world.ChunkTicketManager;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ChunkTicketManager.class)
public class ChunkTicketManager_tickMixin
{
@Shadow private long age;
@Inject(method = "purge", at = @At("HEAD"), cancellable = true)
private void pauseTicketSystem(CallbackInfo ci)
{
// pausing expiry of tickets
// that will prevent also chunks from unloading, so require a deep frozen state
if (!TickSpeed.process_entities && TickSpeed.deepFreeze) age--;
}
}
@@ -117,7 +117,7 @@ public abstract class MinecraftServer_tickspeedMixin extends ReentrantThreadExec
this.startMonitor(tickDurationMonitor);
this.profiler.startTick();
this.profiler.push("tick");
this.tick(TickSpeed.time_warp_start_time != 0 ? ()->true : this::shouldKeepTicking);
this.tick(TickSpeed.time_warp_start_time != 0 ? ()->!hasRunningTasks() : this::shouldKeepTicking);
this.profiler.swap("nextTickWait");
this.waitingForNextTick = true;
this.field_19248 = Math.max(Util.getMeasuringTimeMs() + /*50L*/ msThisTick, this.timeReference);
@@ -72,7 +72,7 @@ public abstract class TntEntityMixin extends Entity implements TntEntityInterfac
private void onExplode(CallbackInfo ci)
{
if (LoggerRegistry.__tnt && logHelper != null)
logHelper.onExploded(getX(), getY(), getZ());
logHelper.onExploded(getX(), getY(), getZ(), this.world.getTime());
if (mergedTNT > 1)
for (int i = 0; i < mergedTNT - 1; i++)
@@ -131,6 +131,7 @@ import java.util.Optional;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.function.Predicate;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import static carpet.script.utils.WorldTools.canHasChunk;
@@ -2377,6 +2378,8 @@ public class CarpetExpression
});
}
private static Map<String,SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k));
private void API_AuxiliaryAspects()
{
this.expr.addLazyFunction("sound", -1, (c, t, lv) -> {
@@ -2387,12 +2390,19 @@ public class CarpetExpression
throw new InternalExpressionException("No such sound: "+soundName.getPath());
float volume = 1.0F;
float pitch = 1.0F;
SoundCategory mixer = SoundCategory.MASTER;
if (lv.size() > 0+locator.offset)
{
volume = (float) NumericValue.asNumber(lv.get(0+locator.offset).evalValue(c)).getDouble();
if (lv.size() > 1+locator.offset)
{
pitch = (float) NumericValue.asNumber(lv.get(1+locator.offset).evalValue(c)).getDouble();
if (lv.size() > 2+locator.offset)
{
String mixerName = lv.get(2+locator.offset).evalValue(c).getString();
mixer = mixerMap.get(mixerName.toLowerCase(Locale.ROOT));
if (mixer == null) throw new InternalExpressionException(mixerName +" is not a valid mixer name");
}
}
}
Vec3d vec = locator.vec;
@@ -2401,7 +2411,7 @@ public class CarpetExpression
for (ServerPlayerEntity player : cc.s.getWorld().getPlayers( (p) -> p.squaredDistanceTo(vec) < d0))
{
count++;
player.networkHandler.sendPacket(new PlaySoundIdS2CPacket(soundName, SoundCategory.PLAYERS, vec, volume, pitch));
player.networkHandler.sendPacket(new PlaySoundIdS2CPacket(soundName, mixer, vec, volume, pitch));
}
int totalPlayed = count;
return (_c, _t) -> new NumericValue(totalPlayed);
@@ -2658,6 +2668,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");
@@ -2794,16 +2822,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);
@@ -2816,21 +2850,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,28 +15,36 @@ 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)
{
File dataFile = CarpetServer.minecraft_server.method_27050().resolve("scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt").toFile();
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.method_27050().resolve("scripts/"+getDescriptor(module, file, isShared)+".nbt").toFile();
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)
{
File dataFile =CarpetServer.minecraft_server.method_27050().resolve("scripts/"+getName()+".data"+(file==null?"":"."+file)+".nbt").toFile();
if (!Files.exists(dataFile.toPath().getParent()))
{
try
{
Files.createDirectory(dataFile.toPath().getParent());
}
catch (IOException ignored)
{
}
}
write(globalState, dataFile);
if (!isShared && (module == null || module.getName() == null)) return false;
File dataFile =CarpetServer.minecraft_server.method_27050().resolve("scripts/"+getDescriptor(module, file, isShared)+".nbt").toFile();
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
return write(globalState, dataFile);
}
@Override
+1
View File
@@ -25,6 +25,7 @@
"ServerWorld_tickMixin",
"ServerChunkManager_tickMixin",
"ThreadedAnvilChunkStorage_tickMixin",
"ChunkTicketManager_tickMixin",
"ThreadedAnvilChunkStorage_scarpetChunkCreationMixin",
"ChunkHolder_scarpetChunkCreationMixin",