Merge branch 'master' into more-throwables

This commit is contained in:
gnembon authored and GitHub committed 2021-03-07 17:52:10 -04:00
commit 25eccc9569
36 files changed
+605 -124

No files matched your search

+78
View File
@@ -398,6 +398,83 @@ 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`.
### `create_datapack(name, data)`
Creates and loads custom datapack. The data has to be a map representing the file structure and the content of the
json files of the target pack.
Returns `null` if the pack with this name already exists or is loaded, meaning no change has been made.
Returns `false` if adding of the datapack wasn't successful.
Returns `true` if creation and loading of the datapack was successful. Loading of a datapack results in
reloading of all other datapacks (vanilla restrictions, identical to /datapack enable), however unlike with `/reload`
command, scarpet apps will not be reloaded by adding a datapack using `create_datapack`.
Currently, only json files are supported in the packs. `'pack.mcmeta'` file is added automatically.
Reloading of datapacks that define new dimensions is not implemented in vanilla. Vanilla game only loads
dimension information on server start. `create_datapack` is therefore a direct replacement of manually ploping of the specified
file structure in a datapack file and calling `/datapack enable` on the new datapack with all its quirks and sideeffects
(like no worldgen changes, reloading all other datapacks, etc.). To enable newly added custom dimensions, call much more
experimental `check_hidden_dimensions()` after adding a datapack if needed.
Synopsis:
<pre>
script run create_datapack('foo',
{
'foo' -> {
'bar.json' -> {
'c' -> true,
'd' -> false,
'e' -> {'foo' -> [1,2,3]},
'a' -> 'foobar',
'b' -> 5
}
}
})
script run create_datapack('funky_world', {
'data' -> {
'minecraft' -> {
'dimension' -> {
'custom_ow.json' -> {
'type' -> 'minecraft:the_end',
'generator' -> {
'biome_source' -> {
'seed' -> 0,
'large_biomes' -> false,
'type' -> 'minecraft:vanilla_layered'
},
'seed' -> 0,
'settings' -> 'minecraft:nether',
'type' -> 'minecraft:noise'
}
}
}
}
}
});
check_hidden_dimensions(); => ['funky_world']
</pre>
### `enable_hidden_dimensions()`
The function reads current datapack settings detecting new dimensions defined by these datapacks that have not yet been added
to the list of current dimensions and adds them so that they can be used and accessed right away. It doesn't matter how the
datapacks have been added to the game, either with 'create_datapack()' or manually by dropping a datapack file and calling
`/datapack enable` on it. Returns the list of valid dimension names / identifiers that has been added in the process.
Fine print: The function should be
considered experimental. There 'should not be' (famous last words) any side-effects if no worlds are added. Already connected
clients will not see suggestions for commands that use dimensions `/execute in <dim>` (vanilla client limitation)
but all commands should work just fine with
the new dimensions. Existing worlds that have gotten modified settings by the datapacks will not be reloaded or replaced.
The usability of the dimensions added this way has not been fully tested, but it seems it works just fine. Generator settings
for the new dimensions will not be added to `'level.dat'` but it will be added there automatically next time the game restarts by
vanilla. One could have said to use this method with caution, and the authors take no responsibility of any losses incurred due to
mis-handlilng of the temporary added dimensions, yet the feature itself (custom dimensions) is clearly experimental for Mojang
themselves, so that's about it.
### `tick_time()`
Returns server tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time.
@@ -517,6 +594,7 @@ Available options in the scarpet app space:
Relevant world related properties
* `world_name` - name of the world
* `world_seed` - a numeric seed of the world
* `world_dimensions` - a list of dimensions in the world
* `world_path` - full path to the world saves folder
* `world_folder` - name of the direct folder in the saves that holds world files
* `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event.
+3 -1
View File
@@ -792,7 +792,9 @@ All generated structures will retain their properties, like mob spawning, howeve
itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs,
because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether fortress.
### `custom_dimension(name, seed?)`
### (deprecated) `custom_dimension(name, seed?)`
Deprecated by `create_datapack()` which can be used to setup custom dimensions
Ensures the dimension with the given `'name'` is available and configured with the given seed. It merely sets the world
generator settings to the overworld, and the optional custom seed (or using current world seed, if not provided).
+1 -1
View File
@@ -177,7 +177,7 @@ Returns `true` if en entity is standing on firm ground and falling down due to t
### `query(e, 'name'), query(e, 'display_name'), query(e, 'custom_name'), query(e, 'type')`
String of entity name
String of entity name or formatted text in the case of `display_name`
<pre>
query(e,'name') => Leatherworker
+15 -15
View File
@@ -23,12 +23,12 @@ In case to simplify the access with nested objects, you can add chain of address
than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`.
<pre>
get(l(range(10)), 5) => 5
get(l(range(10)), -1) => 9
get(l(range(10)), 10) => 0
l(range(10)):93 => 3
get([range(10)], 5) => 5
get([range(10)], -1) => 9
get([range(10)], 10) => 0
[range(10)]:93 => 3
get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
get(m( l('foo',2), l('bar',3), l('baz',4) ), 'bar') => 3
get({ 'foo' -> 2, 'bar' -> 3, 'baz' -> 4 }, 'bar') => 3
</pre>
### `has(container, address, ...), has(lvalue)`
@@ -100,13 +100,13 @@ of the existing elements. Use `replace` to remove and replace existing element.
and merges keys from `value` with the compound tag under the path
<pre>
a = l(1, 2, 3); put(a, 1, 4); a => [1, 4, 3]
a = l(1, 2, 3); put(a, null, 4); a => [1, 2, 3, 4]
a = l(1, 2, 3); put(a, 1, 4, 'insert'); a => [1, 4, 2, 3]
a = l(1, 2, 3); put(a, null, l(4, 5, 6), 'extend'); a => [1, 2, 3, 4, 5, 6]
a = l(1, 2, 3); put(a, 1, l(4, 5, 6), 'extend'); a => [1, 4, 5, 6, 2, 3]
a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(a.1, 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
a = m(1,2,3,4); put(a, 5, null); a => {1: null, 2: null, 3: null, 4: null, 5: null}
a = [1, 2, 3]; put(a, 1, 4); a => [1, 4, 3]
a = [1, 2, 3]; put(a, null, 4); a => [1, 2, 3, 4]
a = [1, 2, 3]; put(a, 1, 4, 'insert'); a => [1, 4, 2, 3]
a = [1, 2, 3]; put(a, null, [4, 5, 6], 'extend'); a => [1, 2, 3, 4, 5, 6]
a = [1, 2, 3]; put(a, 1, [4, 5, 6], 'extend'); a => [1, 4, 5, 6, 2, 3]
a = [[0,0,0],[0,0,0],[0,0,0]]; put(a:1, 1, 1); a => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
a = {1,2,3,4}; put(a, 5, null); a => {1: null, 2: null, 3: null, 4: null, 5: null}
tag = nbt('{}'); put(tag, 'BlockData.Properties', '[1,2,3,4]'); tag => {BlockData:{Properties:[1,2,3,4]}}
tag = nbt('{a:[{lvl:3},{lvl:5},{lvl:2}]}'); put(tag, 'a[].lvl', 1); tag => {a:[{lvl:1},{lvl:1},{lvl:1}]}
tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
@@ -187,10 +187,10 @@ other parts. In that case consecutive calls to `slice` will refer to index `0` t
cannot go back nor track where they are in the sequence (see examples).
<pre>
slice([0,1,2,3,4,5], 1, 3) => [1, 2, 3]
slice([0,1,2,3,4,5], 1, 3) => [1, 2]
slice('foobar', 0, 1) => 'f'
slice('foobar', 3) => 'bar'
slice(range(10), 3, 5) => [3, 4, 5]
slice(range(10), 3, 5) => [3, 4]
slice(range(10), 5) => [5, 6, 7, 8, 9]
r = range(100); [slice(r, 5, 7), slice(r, 1, 3)] => [[5, 6], [8, 9]]
</pre>
@@ -225,7 +225,7 @@ Primarily to be used in higher order functions
<pre>
range(10) => [...]
l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[range(10)] => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
map(range(10),_*_) => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
reduce(range(10),_a+_, 0) => 45
range(5,10) => [5, 6, 7, 8, 9]
@@ -124,12 +124,12 @@ to `all(list,expr) <=> for(list,expr)==length(list)`. `expr` also receives bound
and `continue` have no sense and cannot be used inside of `expr` body.
<pre>
all([1,2,3], check_prime(_)) => 1
all(neighbours(x,y,z), _=='stone') => 1 // if all neighbours of [x, y, z] are stone
map(filter(rect(0,4,0,1000,0,1000), l(x,y,z)=pos(_); all(rect(x,y,z,1,0,1),_=='bedrock') ), pos(_) )
all([1,2,3], check_prime(_)) => true
all(neighbours(x,y,z), _=='stone') => true // if all neighbours of [x, y, z] are stone
map(filter(rect(0,4,0,1000,0,1000), [x,y,z]=pos(_); all(rect(x,y,z,1,0,1),_=='bedrock') ), pos(_) )
=> [[-298, 4, -703], [-287, 4, -156], [-269, 4, 104], [242, 4, 250], [-159, 4, 335], [-208, 4, 416], [-510, 4, 546], [376, 4, 806]]
// find all 3x3 bedrock structures in the top bedrock layer
map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), l(x,y,z)=pos(_);
map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), [x,y,z]=pos(_);
all(rect(x,y,z,1,0,1),_=='bedrock') && for(rect(x,y-1,z,1,1,1,1,0,1),_=='bedrock')<8),
pos(_) ) => [[343, 3, -642], [153, 3, -285], [674, 3, 167], [-710, 3, 398]]
// ditto, but requiring at most 7 bedrock block in the 18 blocks below them
+24 -23
View File
@@ -16,7 +16,7 @@ in [User Defined Functions and Program Control Flow](docs/scarpet/language/Funct
1357-5 => 1352
1357-'5' => 137
3*'foo'-'o' => 'fff'
l(1,3,5)+7 => l(8,10,12)
[1,3,5]+7 => [8,10,12]
</pre>
As you can see, values can behave differently when mixed with other types in the same expression.
@@ -76,8 +76,8 @@ In Minecraft API portion `entity ~ feature` is a shortcode for `query(entity,fea
any extra arguments.
<pre>
l(1,2,3) ~ 2 => 1
l(1,2,3) ~ 4 => null
[1,2,3] ~ 2 => 1
[1,2,3] ~ 4 => null
'foobar' ~ 'baz' => null
'foobar' ~ '.b' => 'ob'
@@ -103,10 +103,10 @@ Or an example to find if a player has specific enchantment on a held axe (either
<pre>
global_get_enchantment(p, ench) -> (
$ for(l('main','offhand'),
$ for(['main','offhand'],
$ holds = query(p, 'holds', _);
$ if( holds,
$ l(what, count, nbt) = holds;
$ [what, count, nbt] = holds;
$ if( what ~ '_axe' && nbt ~ ench,
$ lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
$ )
@@ -143,8 +143,8 @@ Examples:
'foo'*3 => 'foofoofoo'
'foofoofoo' / 3 => 'foo'
'foofoofoo'-'o' => 'fff'
l(1,2,3)+1 => l(2,3,4)
b = l(100,63,100); b+l(10,0,10) => l(110,63,110)
[1,2,3]+1 => [2,3,4]
b = [100,63,100]; b+[10,0,10] => [110,63,110]
{'a' -> 1} + {'b' -> 2} => {'a' -> 1, 'b' -> 2}
</pre>
@@ -186,11 +186,12 @@ second operand is not necessary, it won't be evaluated, which means one can use
case of success returns first positive operand (`||`) or last one (`&&`).
<pre>
true || false => 1
null || false => 0
null != false || run('kill gnembon') => 1 // gnembon survives
null != false && run('kill gnembon') => 0 // when cheats not allowed
null != false && run('kill gnembon') => 1 // gnembon dies, cheats allowed
true || false => true
null || false => false
false || null => null
null != false || run('kill gnembon') // gnembon survives
null != false && run('kill gnembon') // when cheats not allowed
null != false && run('kill gnembon') // gnembon dies, cheats allowed
</pre>
### `Assignment Operators = <> +=`
@@ -204,11 +205,11 @@ require rewriting of the array anyways.
<pre>
a = 5 => a == 5
l(a,b,c) = l(3,4,5) => a==3, b==4, c==5
l(minx,maxx) = sort(xi,xj); // minx assumes min(xi, xj) and maxx, max(xi, xj)
l(a,b,c,d,e,f) = l(range(6)); l(a,b,c) <> l(d,e,f); l(a,b,c,d,e,f) => [3,4,5,0,1,2]
a = l(1,2,3); a += 4 => [1,2,3,4]
a = l(1,2,3,4); a = filter(a,_!=2) => [1,3,4]
[a,b,c] = [3,4,5] => a==3, b==4, c==5
[minx,maxx] = sort(xi,xj); // minx assumes min(xi, xj) and maxx, max(xi, xj)
[a,b,c,d,e,f] = [range(6)]; [a,b,c] <> [d,e,f]; [a,b,c,d,e,f] => [3,4,5,0,1,2]
a = [1,2,3]; a += 4 => [1,2,3,4]
a = [1,2,3,4]; a = filter(a,_!=2) => [1,3,4]
</pre>
### `Unary Operators - +`
@@ -226,10 +227,10 @@ Require a number, flips the sign. One way to assert it's a number is by crashing
flips boolean condition of the expression. Equivalent of `bool(expr)==false`
<pre>
!true => 0
!false => 1
!null => 1
!5 => 0
!l() => 1
!l(null) => 0
!true => false
!false => true
!null => true
!5 => false
!l() => true
!l(null) => false
</pre>
+25 -24
View File
@@ -14,30 +14,31 @@ as well as minecraft related concepts like `block`, `entity`, `nbt`, `text`.
### `bool(expr)`
Returns a boolean context of the expression. Note that there are no true/false values in scarpet. `true` is
alias of 1, and `false` is 0\. Bool is also interpreting string values as boolean, which is different from other
Returns a boolean context of the expression.
Bool is also interpreting string values as boolean, which is different from other
places where boolean context can be used. This can be used in places where API functions return string values to
represent binary values
represent binary values.
<pre>
bool(pi) => 1
bool(false) => 0
bool('') => 0
bool(l()) => 0
bool(l('')) => 1
bool('foo') => 1
bool('false') => 0
bool('nulL') => 0
if('false',1,0) => 1
bool(pi) => true
bool(false) => false
bool('') => false
bool(l()) => false
bool(l('')) => true
bool('foo') => true
bool('false') => false
bool('nulL') => false
if('false',1,0) => true
</pre>
### `number(expr)`
Returns a numeric context of the expression. Can be used to read numbers from strings
Returns a numeric context of the expression. Can be used to read numbers from strings, or other types
<pre>
number(null) => null
number(null) => 0
number(false) => 0
number(true) => 1
number('') => null
number('3.14') => 3.14
number(l()) => 0
@@ -65,15 +66,15 @@ Supported types (with `"%<?>"` syntax):
* `%%`: '%' character
<pre>
str(null) => null
str(false) => 0
str('') => null
str('3.14') => 3.14
str(l()) => 0
str(l('')) => 1
str('foo') => null
str('3bar') => null
str(2)+str(2) => 22
str(null) => 'null'
str(false) => 'false'
str('') => ''
str('3.14') => '3.14'
str([]) => '[]'
str(['']) => '[]'
str('foo') => 'foo'
str('3bar') => '3bar'
str(2)+str(2) => '22'
str('pi: %.2f',pi) => 'pi: 3.14'
str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
</pre>
@@ -234,7 +235,7 @@ same random object. Scarpet keeps track of up to 1024 custom random number gener
<pre>
map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
map(range(10), bool(rand(2))) => [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
map(range(10), bool(rand(2))) => [false, false, true, false, false, false, true, false, true, false]
map(range(10), str('%.1f',rand(_))) => [0.0, 0.4, 0.6, 1.9, 2.8, 3.8, 5.3, 2.2, 1.6, 5.6]
</pre>
@@ -3,8 +3,8 @@
`scarpet` provides a number of constants that can be used literally in scripts
* `null`: nothing, zilch, not even false
* `true`: pure true, or just 1 (one)
* `false`: false truth, or true falsth, 0 (zero) actually
* `true`: pure true, can act as `1`
* `false`: false truth, or true falsth, equals to `0`
* `pi`: for the fans of perimeters, its a perimeter of an apple pi of diameter 1\. About 3.14
* `euler`: clever guy. Derivative of its exponent is goto 1\. About 2.72
+2 -1
View File
@@ -17,9 +17,9 @@ brightness
chunk_tickets
convert_date
crafting_remaining_item
create_datapack
create_marker
current_dimension
custom_dimension
day_time
delete_file
destroy
@@ -28,6 +28,7 @@ display_title
draw_shape
drop_item
emitted_light
enable_hidden_dimensions
encode_nbt
entity_area
entity_event
@@ -55,7 +55,7 @@
set without_updates
blocks_movement block_sound material map_colour block_state block_list block_tags block_data poi set_poi
place_item set_biome biome loaded_status generation_status inhabited_time spawn_potential chunk_tickets
structure_eligibility structures structure_references set_structure reset_chunk reload_chunk custom_dimension
structure_eligibility structures structure_references set_structure reset_chunk reload_chunk create_datapack enable_hidden_dimensions
add_chunk_ticket
convert_date unix_time
+1 -1
View File
@@ -10,7 +10,7 @@ org.gradle.jvmargs=-Xmx1G
fabric_version=0.29.4+1.16
# Mod Properties
mod_version = 1.4.27
mod_version = 1.4.28
maven_group = carpet
archives_base_name = fabric-carpet
-1
View File
@@ -1,6 +1,5 @@
pluginManagement {
repositories {
jcenter()
maven {
name = 'Fabric'
url = 'https://maven.fabricmc.net/'
+21 -7
View File
@@ -42,7 +42,7 @@ import static carpet.settings.RuleCategory.CLIENT;
@SuppressWarnings("CanBeFinal")
public class CarpetSettings
{
public static final String carpetVersion = "1.4.27+v210217";
public static final String carpetVersion = "1.4.28+v210224";
public static final Logger LOG = LogManager.getLogger("carpet");
public static boolean skipGenerationChecks = false;
public static boolean impendingFillSkipUpdates = false;
@@ -413,20 +413,26 @@ public class CarpetSettings
private static class ModulePermissionLevel extends Validator<String> {
@Override public String validate(ServerCommandSource source, ParsedRule<String> currentRule, String newValue, String string) {
CarpetSettings.runPermissionLevel = SettingsManager.getCommandLevel(newValue);
int permissionLevel = SettingsManager.getCommandLevel(newValue);
if (source != null && !source.hasPermissionLevel(permissionLevel))
return null;
CarpetSettings.runPermissionLevel = permissionLevel;
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
return newValue;
}
@Override
public String description() { return "Also controls permission level of commands executed via `run()`";}
public String description() { return "When changing the rule, you must at least have the permission level you are trying to give it";}
}
@Rule(
desc = "Enables restrictions for arbitrary code execution with scarpet",
extra = {
"Users that don't have this permission level",
"won't be able to load apps or /script run"
"won't be able to load apps or /script run.",
"It is also the permission level apps will",
"have when running commands with run()"
},
category = {COMMAND, SCARPET},
validate = ModulePermissionLevel.class
category = {SCARPET},
validate = {Validator._COMMAND_LEVEL_VALIDATOR.class, ModulePermissionLevel.class}
)
public static String commandScriptACE = "ops";
@@ -532,6 +538,14 @@ public class CarpetSettings
)
public static int maxEntityCollisions = 0;
@Rule(
desc = "Customizable server list ping (Multiplayer menu) playerlist sample limit",
options = {"0", "12", "20", "40"},
category = CREATIVE,
strict = false,
validate = Validator.NONNEGATIVE_NUMBER.class
)
public static int pingPlayerListLimit = 12;
/*
@Rule(
@@ -860,7 +874,7 @@ public class CarpetSettings
@Rule(
desc = "Lightning kills the items that drop when lightning kills an entity",
extra = {"Setting to true will prevent lightning from killing drops", "Fixes (https://bugs.mojang.com/browse/MC-195640)."},
extra = {"Setting to true will prevent lightning from killing drops", "Fixes [MC-206922](https://bugs.mojang.com/browse/MC-206922)."},
category = {BUGFIX}
)
public static boolean lightningKillsDropsFix = false;
@@ -23,6 +23,7 @@ import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;
@@ -271,7 +272,13 @@ public class PlayerCommand
Messenger.m(context.getSource(), "rb Player name: "+playerName+" is too long");
return 0;
}
MinecraftServer server = source.getMinecraftServer();
if (!World.isInBuildLimit(new BlockPos(pos.x, pos.y, pos.z)))
{
Messenger.m(context.getSource(), "rb Player "+playerName+" cannot be placed outside of the world");
return 0;
}
PlayerEntity player = EntityPlayerMPFake.createFake(playerName, server, pos.x, pos.y, pos.z, facing.y, facing.x, dimType, mode);
if (player == null)
{
@@ -1,5 +1,6 @@
package carpet.fakes;
import net.minecraft.resource.ServerResourceManager;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
@@ -13,4 +14,5 @@ public interface MinecraftServerInterface
void forceTick(BooleanSupplier sup);
LevelStorage.Session getCMSession();
Map<RegistryKey<World>, ServerWorld> getCMWorlds();
ServerResourceManager getResourceManager();
}
@@ -107,20 +107,22 @@ public class OptimizedExplosion
explosionSound++;
Entity explodingEntity = eAccess.getEntity();
for (int k2 = 0; k2 < entitylist.size(); ++k2) {
Entity entity = entitylist.get(k2);
if (entity == eAccess.getEntity()) {
if (entity == explodingEntity) {
// entitylist.remove(k2);
removeFast(entitylist, k2);
k2--;
continue;
}
if (entity instanceof TntEntity &&
entity.getX() == eAccess.getEntity().getX() &&
entity.getY() == eAccess.getEntity().getY() &&
entity.getZ() == eAccess.getEntity().getZ()) {
if (entity instanceof TntEntity && explodingEntity != null &&
entity.getX() == explodingEntity.getX() &&
entity.getY() == explodingEntity.getY() &&
entity.getZ() == explodingEntity.getZ()) {
if (eLogger != null) {
eLogger.onEntityImpacted(entity, new Vec3d(0,-0.9923437498509884d, 0));
}
@@ -0,0 +1,19 @@
package carpet.mixins;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
import carpet.CarpetSettings;
import net.minecraft.server.MinecraftServer;
@Mixin(MinecraftServer.class)
public abstract class MinecraftServer_pingPlayerSampleLimit
{
@ModifyConstant(method = "tick", constant = @Constant(intValue = 12), require = 0, allow = 1)
private int modifyPlayerSampleLimit(int value)
{
return CarpetSettings.pingPlayerListLimit;
}
}
@@ -2,6 +2,7 @@ package carpet.mixins;
import carpet.fakes.MinecraftServerInterface;
import carpet.helpers.TickSpeed;
import net.minecraft.resource.ServerResourceManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTask;
import net.minecraft.server.world.ServerWorld;
@@ -44,6 +45,8 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
@Shadow @Final private Map<RegistryKey<World>, ServerWorld> worlds;
@Shadow private ServerResourceManager serverResourceManager;
@Override
public void forceTick(BooleanSupplier isAhead)
{
@@ -58,6 +61,11 @@ public abstract class MinecraftServer_scarpetMixin extends ReentrantThreadExecut
return session;
}
@Override
public ServerResourceManager getResourceManager() {
return serverResourceManager;
}
@Override
public Map<RegistryKey<World>, ServerWorld> getCMWorlds()
{
@@ -25,10 +25,4 @@ public class PlayerManager_coreMixin
CarpetServer.onPlayerLoggedIn(player);
PLAYER_CONNECTS.onPlayerEvent(player);
}
@Inject(method = "onDataPacksReloaded", at = @At("HEAD"))
private void onResourcesRefreshedReloadCommand(CallbackInfo ci)
{
CarpetServer.onReload(server);
}
}
@@ -0,0 +1,23 @@
package carpet.mixins;
import carpet.CarpetServer;
import com.mojang.brigadier.context.CommandContext;
import net.minecraft.server.command.ReloadCommand;
import net.minecraft.server.command.ServerCommandSource;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ReloadCommand.class)
public class ReloadCommand_reloadAppsMixin {
//method_13530(Lcom/mojang/brigadier/context/CommandContext;)I
// internal of register.
@SuppressWarnings("UnresolvedMixinReference")
@Inject(method = "method_13530", at = @At("TAIL"))
private static void onReload(CommandContext<ServerCommandSource> context, CallbackInfoReturnable<Integer> cir)
{
// can't fetch here the reference to the server
CarpetServer.onReload(context.getSource().getMinecraftServer());
}
}
@@ -26,6 +26,7 @@ import net.minecraft.util.WorldSavePath;
import net.minecraft.util.math.BlockPos;
import java.io.IOException;
import java.nio.file.FileVisitOption;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
@@ -136,6 +137,7 @@ public class CarpetScriptServer
{
try {
Path folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
if (!Files.exists(folder))
Files.createDirectories(folder);
Optional<Path>
scriptPath = Files.list(folder)
@@ -149,6 +151,7 @@ public class CarpetScriptServer
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
{
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
if (!Files.exists(globalFolder))
Files.createDirectories(globalFolder);
scriptPath = Files.walk(globalFolder)
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
@@ -194,6 +197,7 @@ public class CarpetScriptServer
}
try {
Path worldScripts = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
if (!Files.exists(worldScripts))
Files.createDirectories(worldScripts);
Files.list(worldScripts)
.filter(f -> f.toString().endsWith(".sc"))
@@ -202,8 +206,9 @@ public class CarpetScriptServer
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
{
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
if (!Files.exists(globalScripts))
Files.createDirectories(globalScripts);
Files.walk(globalScripts)
Files.walk(globalScripts, FileVisitOption.FOLLOW_LINKS)
.filter(f -> f.toString().endsWith(".sc"))
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
}
+206 -3
View File
@@ -22,6 +22,7 @@ import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ThrowStatement;
import carpet.script.exception.Throwables;
import carpet.script.utils.FixedCommandSource;
import carpet.script.utils.InputValidator;
import carpet.script.utils.ScarpetJsonDeserializer;
import carpet.script.utils.ShapeDispatcher;
import carpet.script.utils.WorldTools;
@@ -37,6 +38,10 @@ import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import carpet.utils.Messenger;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import net.minecraft.SharedConstants;
import net.minecraft.block.BlockState;
import net.minecraft.command.DataCommandStorage;
import net.minecraft.entity.Entity;
@@ -47,12 +52,18 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket;
import net.minecraft.network.packet.s2c.play.TitleS2CPacket;
import net.minecraft.network.packet.s2c.play.TitleS2CPacket.Action;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.resource.DataPackSettings;
import net.minecraft.resource.ReloadableResourceManagerImpl;
import net.minecraft.resource.ResourcePackManager;
import net.minecraft.resource.ResourcePackProfile;
import net.minecraft.resource.ServerResourceManager;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
@@ -65,13 +76,27 @@ import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
import net.minecraft.util.crash.CrashException;
import net.minecraft.util.Util;
import net.minecraft.util.WorldSavePath;
import net.minecraft.util.dynamic.RegistryOps;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.EulerAngle;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.SaveProperties;
import net.minecraft.world.World;
import net.minecraft.world.biome.source.BiomeAccess;
import net.minecraft.world.border.WorldBorderListener;
import net.minecraft.world.dimension.DimensionOptions;
import net.minecraft.world.dimension.DimensionType;
import net.minecraft.world.gen.GeneratorOptions;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.level.UnmodifiableLevelProperties;
import net.minecraft.world.level.storage.LevelStorage;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
@@ -80,6 +105,13 @@ import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -98,7 +130,7 @@ import static java.lang.Math.min;
public class Auxiliary {
public static final String MARKER_STRING = "__scarpet_marker";
private static final Map<String, SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k));
public static final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
public static String recognizeResource(Value value, boolean isFloder)
{
@@ -849,7 +881,7 @@ public class Auxiliary {
exc = e.getCause();
throw new ThrowStatement("Failed to read JSON file: "+exc.getMessage(), Throwables.JSON_READ);
}
Value parsedJson = gson.fromJson(json, Value.class);
Value parsedJson = GSON.fromJson(json, Value.class);
if (parsedJson == null)
retVal = Value.NULL;
else
@@ -888,7 +920,7 @@ public class Auxiliary {
}
else if (fdesc.getMiddle().equals("json"))
{
List<String> data = Collections.singletonList(gson.toJson(lv.get(2).evalValue(c).toJson()));
List<String> data = Collections.singletonList(GSON.toJson(lv.get(2).evalValue(c).toJson()));
((CarpetScriptHost) c.host).removeResourceFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle());
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle(), data);
}
@@ -1066,7 +1098,178 @@ public class Auxiliary {
if (old_nbt == null) return LazyValue.NULL;
return (_c, _t) -> new NBTSerializableValue(old_nbt);
});
// script run create_datapack('foo', {'foo' -> {'bar.json' -> {'c' -> true,'d' -> false,'e' -> {'foo' -> [1,2,3]},'a' -> 'foobar','b' -> 5}}})
expression.addLazyFunction("create_datapack", 2, (c, t, lv) -> {
CarpetContext cc = (CarpetContext)c;
String origName = lv.get(0).evalValue(c).getString();
String name = InputValidator.validateSimpleString(origName, true);
MinecraftServer server = cc.s.getMinecraftServer();
for (String dpName : server.getDataPackManager().getNames())
{
if (dpName.equalsIgnoreCase("file/"+name+".zip") ||
dpName.equalsIgnoreCase("file/"+name))
return LazyValue.NULL;
}
Value dpdata = lv.get(1).evalValue(c);
if (!(dpdata instanceof MapValue))
throw new InternalExpressionException("datapack data needs to be a valid map type");
ResourcePackManager packManager = server.getDataPackManager();
Path dbFloder = server.getSavePath(WorldSavePath.DATAPACKS);
Path packFloder = dbFloder.resolve(name+".zip");
if (Files.exists(packFloder) || Files.exists(dbFloder.resolve(name))) return LazyValue.NULL;
Boolean [] successful = new Boolean[]{true};
server.submitAndJoin( () ->
{
try {
//Files.createDirectory(packFloder);
try (FileSystem zipfs = FileSystems.newFileSystem(URI.create("jar:" + packFloder.toUri().toString()), ImmutableMap.of("create", "true"))) {
Path zipRoot = zipfs.getPath("/");
zipValueToJson(zipRoot.resolve("pack.mcmeta"), MapValue.wrap(
ImmutableMap.of(StringValue.of("pack"), MapValue.wrap(ImmutableMap.of(
StringValue.of("pack_format"), new NumericValue(SharedConstants.getGameVersion().getPackVersion()),
StringValue.of("description"), StringValue.of(name),
StringValue.of("source"), StringValue.of("scarpet")
)))
));
walkTheDPMap((MapValue) dpdata, zipRoot);
}
packManager.scanPacks();
ResourcePackProfile resourcePackProfile = packManager.getProfile("file/" + name + ".zip");
if (resourcePackProfile == null || packManager.getEnabledProfiles().contains(resourcePackProfile)) {
throw new IOException();
}
List<ResourcePackProfile> list = Lists.newArrayList(packManager.getEnabledProfiles());
resourcePackProfile.getInitialPosition().insert(list, resourcePackProfile, p -> p, false);
server.reloadResources(list.stream().map(ResourcePackProfile::getName).collect(Collectors.toList())).
exceptionally(exc -> {
successful[0] = false;
return null;
}).join();
if (!successful[0]) {
throw new IOException();
}
} catch (IOException e)
{
successful[0] = false;
try {
FileUtils.forceDelete(packFloder.toFile());
} catch (IOException ignored) {
throw new InternalExpressionException("Failed to install a datapack and failed to clean up after it");
}
}
});
return successful[0]?LazyValue.TRUE:LazyValue.FALSE;
});
expression.addLazyFunction("enable_hidden_dimensions", 0, (c, t, lv) -> {
CarpetContext cc = (CarpetContext)c;
// from minecraft.server.Main.main
MinecraftServer server = cc.s.getMinecraftServer();
LevelStorage.Session session = ((MinecraftServerInterface)server).getCMSession();
DataPackSettings dataPackSettings = session.getDataPackSettings();
ResourcePackManager resourcePackManager = server.getDataPackManager();
DataPackSettings dataPackSettings2 = MinecraftServer.loadDataPacks(resourcePackManager, dataPackSettings == null ? DataPackSettings.SAFE_MODE : dataPackSettings, false);
ServerResourceManager serverRM = ((MinecraftServerInterface)server).getResourceManager();
ReloadableResourceManagerImpl resourceManager = (ReloadableResourceManagerImpl) serverRM.getResourceManager();
//believe the other one will fillup based on the datapacks only.
resourceManager.close();
resourcePackManager.createResourcePacks().forEach(resourceManager::addPack);
//not sure its needed, but doesn't seem to have a negative effect and might be used in some custom shtuff
serverRM.loadRegistryTags();
RegistryOps<Tag> registryOps = RegistryOps.of(NbtOps.INSTANCE, serverRM.getResourceManager(), (DynamicRegistryManager.Impl) server.getRegistryManager());
SaveProperties saveProperties = session.readLevelProperties(registryOps, dataPackSettings2);
if (saveProperties == null) return LazyValue.NULL;
//session.backupLevelDataFile(server.getRegistryManager(), saveProperties); // no need
// MinecraftServer.createWorlds
// save properties should now contain dimension settings
GeneratorOptions generatorOptions = saveProperties.getGeneratorOptions();
boolean bl = generatorOptions.isDebugWorld();
long l = generatorOptions.getSeed();
long m = BiomeAccess.hashSeed(l);
Map<RegistryKey<World>, ServerWorld> existing_worlds = ((MinecraftServerInterface)server).getCMWorlds();
List<Value> addeds = new ArrayList<>();
for (Map.Entry<RegistryKey<DimensionOptions>, DimensionOptions> entry : generatorOptions.getDimensions().getEntries()) {
RegistryKey<DimensionOptions> registryKey = entry.getKey();
if (!existing_worlds.containsKey(registryKey))
{
addeds.add(ValueConversions.of(registryKey.getValue()));
RegistryKey<World> registryKey2 = RegistryKey.of(Registry.DIMENSION, registryKey.getValue());
DimensionType dimensionType3 = entry.getValue().getDimensionType();
ChunkGenerator chunkGenerator3 = entry.getValue().getChunkGenerator();
UnmodifiableLevelProperties unmodifiableLevelProperties = new UnmodifiableLevelProperties(saveProperties, saveProperties.getMainWorldProperties());
ServerWorld serverWorld2 = new ServerWorld(server, Util.getMainWorkerExecutor(), session, unmodifiableLevelProperties, registryKey2, dimensionType3, WorldTools.NOOP_LISTENER, chunkGenerator3, bl, m, ImmutableList.of(), false);
server.getOverworld().getWorldBorder().addListener(new WorldBorderListener.WorldBorderSyncer(serverWorld2.getWorldBorder()));
existing_worlds.put(registryKey2, serverWorld2);
}
}
Value ret = ListValue.wrap(addeds);
return (_c, _t) -> ret;
});
}
private static void zipValueToJson(Path path, Value output) throws IOException
{
JsonElement element = output.toJson();
if (element == null)
throw new InternalExpressionException("Cannot interpret "+output.getPrettyString()+" as a json object");
String string = GSON.toJson(element);
Files.createDirectories(path.getParent());
BufferedWriter bufferedWriter = Files.newBufferedWriter(path);
Throwable incident = null;
try
{
bufferedWriter.write(string);
}
catch (Throwable shitHappened)
{
incident = shitHappened;
throw shitHappened;
}
finally
{
if (incident != null) {
try {
bufferedWriter.close();
} catch (Throwable otherShitHappened) {
incident.addSuppressed(otherShitHappened);
}
} else {
bufferedWriter.close();
}
}
}
private static void walkTheDPMap(MapValue node, Path path) throws IOException
{
Map<Value,Value> items = node.getMap();
for (Map.Entry<Value, Value> entry : items.entrySet())
{
Value val = entry.getValue();
String strkey = entry.getKey().getString();
Path child = path.resolve(strkey);
if (strkey.endsWith(".json"))
{
zipValueToJson(child, val);
}
else
{
if (!(val instanceof MapValue)) throw new InternalExpressionException("Value of "+strkey+" should be a map");
Files.createDirectory(child);
walkTheDPMap((MapValue) val, child);
}
}
}
private static <T> Stat<T> getStat(StatType<T> type, Identifier id)
{
@@ -93,7 +93,7 @@ public class Inventories {
Item item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString()).getItem();
if (lv.size() == 1)
{
Value ret = ListValue.wrap(tagManager.getItems().getTagsFor(item).stream().map(ValueConversions::of).collect(Collectors.toList()));
Value ret = ListValue.wrap(tagManager.getItems().getTags().entrySet().stream().filter(e -> e.getValue().contains(item)).map(e -> ValueConversions.of(e.getKey())).collect(Collectors.toList()));
return (_c, _t) -> ret;
}
String tag = lv.get(1).evalValue(c).getString();
@@ -1127,7 +1127,8 @@ public class WorldAccess {
BlockArgument blockLocator = BlockArgument.findIn(cc, lv, 0, true);
if (blockLocator.offset == lv.size())
{
Value ret = ListValue.wrap(tagManager.getBlocks().getTagsFor(blockLocator.block.getBlockState().getBlock()).stream().map(ValueConversions::of).collect(Collectors.toList()));
Block target = blockLocator.block.getBlockState().getBlock();
Value ret = ListValue.wrap(tagManager.getBlocks().getTags().entrySet().stream().filter(e -> e.getValue().contains(target)).map(e -> ValueConversions.of(e.getKey())).collect(Collectors.toList()));
return (_c, _t) -> ret;
}
String tag = lv.get(blockLocator.offset).evalValue(c).getString();
@@ -1381,6 +1382,7 @@ public class WorldAccess {
{
if (lv.size() == 0) throw new InternalExpressionException("'custom_dimension' requires at least one argument");
CarpetContext cc = (CarpetContext)c;
cc.host.issueDeprecation("custom_dimension()");
String worldKey = lv.get(0).evalValue(c).getString();
Long seed = null;
@@ -48,7 +48,7 @@ public class Sys {
return (cc, tt) -> Value.FALSE;
}
}
Value retval = new NumericValue(v.getBoolean());
Value retval = v.getBoolean()?Value.TRUE:Value.FALSE;
return (cc, tt) -> retval;
});
@@ -56,7 +56,9 @@ public class Sys {
{
if (v instanceof NumericValue)
{
return v;
NumericValue num = (NumericValue)v;
if (num.isInteger()) return new NumericValue(num.getLong());
return new NumericValue(num.getDouble());
}
try
{
@@ -0,0 +1,16 @@
package carpet.script.utils;
import carpet.script.exception.InternalExpressionException;
import java.util.Locale;
public class InputValidator {
public static String validateSimpleString(String input, boolean strict)
{
String simplified = input.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9+_]", "");
if (simplified.isEmpty() || (strict && !simplified.equals(input)))
throw new InternalExpressionException("simple name can only contain numbers, letter and _");
return simplified;
}
}
@@ -9,6 +9,7 @@ import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import carpet.settings.ParsedRule;
import carpet.settings.SettingsManager;
import com.sun.management.OperatingSystemMXBean;
@@ -49,6 +50,7 @@ public class SystemInfo {
String tlf = serverPath.getName(nodeCount-2).toString();
return StringValue.of(tlf);
});
put("world_dimensions", c -> ListValue.wrap(c.s.getMinecraftServer().getWorldRegistryKeys().stream().map(k -> ValueConversions.of(k.getValue())).collect(Collectors.toList())));
put("game_difficulty", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getDifficulty().getName()));
put("game_hardcore", c -> new NumericValue(c.s.getMinecraftServer().getSaveProperties().isHardcore()));
put("game_storage_format", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getFormatName(c.s.getMinecraftServer().getSaveProperties().getVersion())));
@@ -157,5 +157,5 @@ public class WorldTools
@Override public void stop() { }
}
private static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
public static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
}
@@ -0,0 +1,58 @@
package carpet.script.value;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import net.minecraft.nbt.ByteTag;
import net.minecraft.nbt.Tag;
public class BooleanValue extends NumericValue
{
public static final BooleanValue FALSE = new BooleanValue(false);
public static final BooleanValue TRUE = new BooleanValue(true);
boolean boolValue;
private BooleanValue(boolean boolval) {
super(boolval);
boolValue = boolval;
}
@Override
public String getString() {
return boolValue?"true":"false";
}
@Override
public String getPrettyString() {
return getString();
}
@Override
public String getTypeString() {
return "bool";
}
@Override
public Value clone() {
return new BooleanValue(boolValue);
}
@Override
public int hashCode() {
return Boolean.hashCode(boolValue);
}
@Override
public Tag toTag(boolean force) {
return ByteTag.of(boolValue);
}
@Override
public JsonElement toJson() {
return new JsonPrimitive(boolValue);
}
@Override
public boolean isInteger() {
return true;
}
}
@@ -416,7 +416,7 @@ public class EntityValue extends Value
put("motion_z", (e, a) -> new NumericValue(e.getVelocity().z));
put("on_ground", (e, a) -> new NumericValue(e.isOnGround()));
put("name", (e, a) -> new StringValue(e.getName().getString()));
put("display_name", (e, a) -> new StringValue(e.getDisplayName().getString()));
put("display_name", (e, a) -> new FormattedTextValue(e.getDisplayName()));
put("command_name", (e, a) -> new StringValue(e.getEntityName()));
put("custom_name", (e, a) -> e.hasCustomName()?new StringValue(e.getCustomName().getString()):Value.NULL);
put("type", (e, a) -> new StringValue(nameFromRegistryId(Registry.ENTITY_TYPE.getId(e.getType()))));
@@ -429,7 +429,7 @@ public class EntityValue extends Value
put("tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
put("scoreboard_tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTagsFor(e.getType()).stream().map(ValueConversions::of).collect(Collectors.toList())));
put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTags().entrySet().stream().filter(entry -> entry.getValue().contains(e.getType())).map(entry -> ValueConversions.of(entry.getKey())).collect(Collectors.toList())));
// deprecated
put("has_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString())));
@@ -462,7 +462,7 @@ public class EntityValue extends Value
return ListValue.of(new NumericValue(look.x),new NumericValue(look.y),new NumericValue(look.z));
});
put("is_burning", (e, a) -> new NumericValue(e.isOnFire()));
//put("fire", (e, a) -> new NumericValue(e.getFire())); needs mixing
put("fire", (e, a) -> new NumericValue(e.getFireTicks()));
put("silent", (e, a)-> new NumericValue(e.isSilent()));
put("gravity", (e, a) -> new NumericValue(!e.hasNoGravity()));
put("immune_to_fire", (e, a) -> new NumericValue(e.isFireImmune()));
@@ -341,7 +341,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
int from = normalizeIndex(fromDesc, size);
if (toDesc == null)
return new ListValue((Collection<? extends Value>) getItems().subList(from, size));
int to = normalizeIndex(toDesc, size);
int to = normalizeIndex(toDesc, size+1);
if (from > to)
return ListValue.of();
return new ListValue((Collection<? extends Value>) getItems().subList(from, to));
@@ -7,6 +7,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -16,7 +17,7 @@ import java.util.stream.Collectors;
public class MapValue extends AbstractListValue implements ContainerValueInterface
{
private Map<Value, Value> map;
private final Map<Value, Value> map;
private MapValue()
{
@@ -268,7 +269,9 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
public JsonElement toJson()
{
JsonObject jsonMap = new JsonObject();
map.forEach((k, v) -> jsonMap.add(k.getString(), v.toJson()));
List<Value> keys = new ArrayList<>(map.keySet());
Collections.sort(keys);
keys.forEach( k -> jsonMap.add(k.getString(), map.get(k).toJson()));
return jsonMap;
}
}
@@ -9,6 +9,7 @@ import net.minecraft.nbt.Tag;
public class NullValue extends NumericValue // TODO check nonsingleton code
{
public static final NullValue NULL = new NullValue();
@Override
public String getString()
{
@@ -32,7 +33,7 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
{
return new NullValue();
}
public NullValue() {super(0.0D);}
private NullValue() {super(false);}
@Override
public boolean equals(final Object o)
+6 -6
View File
@@ -15,12 +15,12 @@ import java.util.stream.Collectors;
public abstract class Value implements Comparable<Value>, Cloneable
{
public static NumericValue FALSE = new NumericValue(0);
public static NumericValue TRUE = new NumericValue(1);
public static NumericValue ZERO = FALSE;
public static NumericValue ONE = TRUE;
public static NumericValue FALSE = BooleanValue.FALSE;
public static NumericValue TRUE = BooleanValue.TRUE;
public static NumericValue ZERO = new NumericValue(0);
public static NumericValue ONE = new NumericValue(1);
public static NullValue NULL = new NullValue();
public static NullValue NULL = NullValue.NULL;
public String boundVariable;
@@ -186,7 +186,7 @@ public abstract class Value implements Comparable<Value>, Cloneable
int size = value.length();
int from = ListValue.normalizeIndex(fromDesc, size);
if (toDesc == null) return new StringValue(value.substring(from));
int to = ListValue.normalizeIndex(toDesc, size);
int to = ListValue.normalizeIndex(toDesc, size+1);
if (from > to) return StringValue.EMPTY;
return new StringValue(value.substring(from, to));
}
@@ -1,4 +1,4 @@
__command() ->
_command() ->
(
print('camera: scarpet app.');
print('-------------------');
@@ -11,7 +11,7 @@ __command() ->
print(' "/camera place_player - Move player to the selected point');
print(' "/camera move" - Move the selected point to players location');
print(' "/camera duration <X>" - Set new selected path duration');
print(' "/camera split_point>" - Split selected path in half.');
print(' "/camera split_point" - Split selected path in half.');
print(' "/camera delete_point" - Remove current key point');
print(' "/camera trim_path" - Remove all key points from selected up');
print('');
@@ -57,6 +57,47 @@ __command() ->
print('');
null
);
__config() ->{
'commands'->{
''->'_command',
'<command>'->'_call',
'add <seconds>'->'add',
'prepend <seconds>'->'prepend',
'duration <seconds>' -> 'duration',
'save_as <name>'->'save_as',
'load <name>'->'load',
'interpolation <interpolation>'->['__interpolation',true],
'interpolation gauss'->['__interpolation','gauss',true],
'interpolation gauss <float>'->_(float)->(__interpolation('gauss_'+str(float),true)),
'repeat <seconds> <last_delay>'->'repeat',
'stretch <factor>'->'stretch'
},
'arguments'->{
'seconds'->{'type'->'int','suggest'->[]},
'last_delay'->{'type'->'int','suggest'->[]},
'name'->{'type'->'string','suggest'->[]},
'interpolation'->{'type'->'term','options'->['linear','catmull_rom']},
'factor'->{'type'->'int','min'->25,'max'->400},
'command'->{'type'->'term','options'->[
'start',
'clear',
'select',
'place_player',
'move',
'split_point',
'delete_point',
'trim_path',
'transpose',
'play',
'show',
'hide',
'prefer_smooth_play',
'prefer_synced_play'
]}
}
};
_call(command)->call(command);
global_points = null;
global_dimension = null;
@@ -150,7 +191,6 @@ __assert_point_selected(validator) ->
);
//select a custom interpolation method
interpolation(method) -> __interpolation(method, true);
__interpolation(method, verbose) ->
(
__prepare_path_if_needed() -> __prepare_path_if_needed_generic();
@@ -159,7 +199,7 @@ __interpolation(method, verbose) ->
// or optionally __prepare_path_if_needed, if path is inefficient to compute point by point
global_interpolator = if (
method == 'linear', '__interpolator_linear',
method == 'cr', '__interpolator_cr',
method == 'catmull_rom', '__interpolator_cr',
method == 'gauss', _(s, p) -> __interpolator_gauB(s, p, 0),
method ~ '^gauss_',
(
@@ -167,13 +207,12 @@ __interpolation(method, verbose) ->
type = replace(type,'_','.');
variance = round(60*number(type));
_(s, p, outer(variance)) -> __interpolator_gauB(s, p, variance);
),
exit('Choose one of the following methods: linear, gauss, gauss_<deviation>, cr')
)
);
__update();
if(verbose, 'Interpolation changed to '+method, '');
);
__interpolation('cr', false);
__interpolation('catmull_rom', false);
// adds a point to the end of the path with delay in seconds
add(delay) ->
@@ -216,9 +255,6 @@ repeat(times, last_section_delay) ->
stretch(percentage) ->
(
if (err = __is_not_valid_for_motion(), exit(err));
if (percentage < 25 || percentage > 400,
exit('path speed can only be speed, or slowed down 4 times. Re-call command for larger changes')
);
ratio = percentage/100;
previous_path_length = global_points:(-1):1;
for(global_points, _:1 = _:1*ratio );
+2
View File
@@ -24,6 +24,7 @@
"ServerPlayNetworkHandler_interactionUpdatesMixin",
"ServerMetadata_motdMixin",
"MinecraftServer_pingPlayerSampleLimit",
"MinecraftServer_coreMixin",
@@ -175,6 +176,7 @@
"PointOfInterest_scarpetMixin",
"ServerWorld_scarpetMixin",
"HungerManagerMixin_scarpetEventsMixin",
"ReloadCommand_reloadAppsMixin",
"RecipeManager_scarpetMixin",
"Ingredient_scarpetMixin",