Merge branch 'master' into future
This commit is contained in:
commit
74f6e57edc
12 files changed
+931
-5
No files matched your search
@@ -206,3 +206,272 @@ Returns size of the actual dropped items.
|
|||||||
inventory_drop(player(), 0, 1) => 1 // Q's one item on the ground
|
inventory_drop(player(), 0, 1) => 1 // Q's one item on the ground
|
||||||
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
|
## Screens
|
||||||
|
|
||||||
|
A screen is a value type used to open screens for a player and interact with them.
|
||||||
|
For example, this includes the chest inventory gui, the crafting table gui and many more.
|
||||||
|
|
||||||
|
### `create_screen(player, type, name, callback?)`
|
||||||
|
|
||||||
|
Creates and opens a screen for a `player`.
|
||||||
|
|
||||||
|
Available `type`s:
|
||||||
|
|
||||||
|
* `anvil`
|
||||||
|
* `beacon`
|
||||||
|
* `blast_furnace`
|
||||||
|
* `brewing_stand`
|
||||||
|
* `cartography_table`
|
||||||
|
* `crafting`
|
||||||
|
* `enchantment`
|
||||||
|
* `furnace`
|
||||||
|
* `generic_3x3`
|
||||||
|
* `generic_9x1`
|
||||||
|
* `generic_9x2`
|
||||||
|
* `generic_9x3`
|
||||||
|
* `generic_9x4`
|
||||||
|
* `generic_9x5`
|
||||||
|
* `generic_9x6`
|
||||||
|
* `grindstone`
|
||||||
|
* `hopper`
|
||||||
|
* `lectern`
|
||||||
|
* `loom`
|
||||||
|
* `merchant`
|
||||||
|
* `shulker_box`
|
||||||
|
* `smithing`
|
||||||
|
* `smoker`
|
||||||
|
* `stonecutter`
|
||||||
|
|
||||||
|
The `name` parameter can be a formatted text and will be displayed at the top of the screen.
|
||||||
|
Some screens like the lectern or beacon screen don't show it.
|
||||||
|
|
||||||
|
Optionally, a `callback` function can be passed as the fourth argument.
|
||||||
|
This functions needs to have four parameters:
|
||||||
|
`_(screen, player, action, data) -> ...`
|
||||||
|
|
||||||
|
The `screen` parameter is the screen value of the screen itself.
|
||||||
|
`player` is the player who interacted with the screen.
|
||||||
|
`action` is a string corresponding to the interaction type.
|
||||||
|
Can be any of the following:
|
||||||
|
|
||||||
|
Slot interactions:
|
||||||
|
|
||||||
|
* `pickup`
|
||||||
|
* `quick_move`
|
||||||
|
* `swap`
|
||||||
|
* `clone`
|
||||||
|
* `throw`
|
||||||
|
* `quick_craft`
|
||||||
|
* `pickup_all`
|
||||||
|
|
||||||
|
The `data` for this interaction is a map, with a `slot` and `button` value.
|
||||||
|
`slot` is the slot index of the slot that was clicked.
|
||||||
|
When holding an item in the cursor stack and clicking inside the screen,
|
||||||
|
but not in a slot, this is -1.
|
||||||
|
If clicked outside the screen (where it would drop held items), this value is null.
|
||||||
|
The `button` is the mouse button used to click the slot.
|
||||||
|
|
||||||
|
For the `swap` action, the `button` is the number key 0-8 for a certain hotbar slot.
|
||||||
|
|
||||||
|
For the `quick_craft` action, the `data` also contains the `quick_craft_stage`,
|
||||||
|
which is either 0 (beginning of quick crafting), 1 (adding item to slot) or 2 (end of quick crafting).
|
||||||
|
|
||||||
|
Other interactions:
|
||||||
|
|
||||||
|
* `button` Pressing a button in certain screens that have button elements (enchantment table, lectern, loom and stonecutter)
|
||||||
|
The `data` provides a `button`, which is the index of the button that was pressed.
|
||||||
|
Note that for lecterns, this index can be certain a value above 100, for jumping to a certain page.
|
||||||
|
This can come from formatted text inside the book, with a `change_page` click event action.
|
||||||
|
|
||||||
|
* `close` Triggers when the screen gets closed. No `data` provided.
|
||||||
|
|
||||||
|
* `select_recipe` When clicking on a recipe in the recipe book.
|
||||||
|
`data` contains a `recipe`, which is the identifier of the clicked recipe,
|
||||||
|
as well as `craft_all`, which is a boolean specifying whether
|
||||||
|
shift was pressed when selecting the recipe.
|
||||||
|
|
||||||
|
* `slot_update` Gets called **after** a slot has changed contents. `data` provides a `slot` and `stack`.
|
||||||
|
|
||||||
|
By returning a string `'cancel'` in the callback function,
|
||||||
|
the screen interaction can be cancelled.
|
||||||
|
This doesn't work for the `close` action.
|
||||||
|
|
||||||
|
The `create_screen` function returns a `screen` value,
|
||||||
|
which can be used in all inventory related functions to access the screens' slots.
|
||||||
|
The screen inventory covers all slots in the screen and the player inventory.
|
||||||
|
The last slot is the cursor stack of the screen,
|
||||||
|
meaning that using `-1` can be used to modify the stack the players' cursor is holding.
|
||||||
|
|
||||||
|
### `close_screen(screen)`
|
||||||
|
|
||||||
|
Closes the screen of the given screen value.
|
||||||
|
Returns `true` if the screen was closed.
|
||||||
|
If the screen is already closed, returns `false`.
|
||||||
|
|
||||||
|
### `screen_property(screen, property)`
|
||||||
|
|
||||||
|
### `screen_property(screen, property, value)`
|
||||||
|
|
||||||
|
Queries or modifies a certain `property` of a `screen`.
|
||||||
|
The `property` is a string with the name of the property.
|
||||||
|
When called with `screen` and `property` parameter, returns the current value of the property.
|
||||||
|
When specifying a `value`,
|
||||||
|
the property will be assigned the new `value` and synced with the client.
|
||||||
|
|
||||||
|
**Options for `property` string:**
|
||||||
|
|
||||||
|
| `property` | Required screen type | Type | Description |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `name` | **All** | text | The name of the screen, as specified in the `create_screen()` function. Can only be queried. |
|
||||||
|
| `open` | **All** | boolean | Returns `true` if the screen is open, `false` otherwise. Can only be queried. |
|
||||||
|
| `fuel_progress` | furnace/smoker/blast_furnace | number | Current value of the fuel indicator. |
|
||||||
|
| `max_fuel_progress` | furnace/smoker/blast_furnace | number | Maximum value for the full fuel indicator. |
|
||||||
|
| `cook_progress` | furnace/smoker/blast_furnace | number | Cooking progress indicator value. |
|
||||||
|
| `max_cook_progress` | furnace/smoker/blast_furnace | number | Maximum value for the cooking progress indicator. |
|
||||||
|
| `level_cost` | anvil | number | Displayed level cost for the anvil. |
|
||||||
|
| `page` | lectern | number | Opened page in the lectern screen. |
|
||||||
|
| `beacon_level` | beacon | number | The power level of the beacon screen. This affects how many effects under primary power are grayed out. Should be a value between 0-5. |
|
||||||
|
| `primary_effect` | beacon | number | The effect id of the primary effect. This changes the effect icon on the button on the secondary power side next to the regeneration effect. |
|
||||||
|
| `secondary_effect` | beacon | number | The effect id of the secondary effect. This seems to change nothing, but it exists. |
|
||||||
|
| `brew_time` | brewing_stand | number | The brewing time indicator value. This goes from 0 to 400. |
|
||||||
|
| `brewing_fuel` | brewing_stand | number | The fuel indicator progress. Values range between 0 to 20. |
|
||||||
|
| `enchantment_power_x` | enchantment | number | The level cost of the shown enchantment. Replace `x` with 1, 2 or 3 (e.g. `enchantment_power_2`) to target the first, second or third enchantment. |
|
||||||
|
| `enchantment_id_x` | enchantment | number | The id of the enchantment shown (replace `x` with the enchantment slot 1/2/3). |
|
||||||
|
| `enchantment_level_x` | enchantment | number | The enchantment level of the enchantment. |
|
||||||
|
| `enchantment_seed` | enchantment | number | The seed of the enchanting screen. This affects the text shown in the standard Galactic alphabet. |
|
||||||
|
| `banner_pattern` | loom | number | The selected banner pattern inside the loom. |
|
||||||
|
| `stonecutter_recipe` | stonecutter | number | The selected recipe in the stonecutter. |
|
||||||
|
|
||||||
|
### Screen example scripts
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Chest click event</summary>
|
||||||
|
|
||||||
|
```py
|
||||||
|
__command() -> (
|
||||||
|
create_screen(player(),'generic_9x6',format('db Test'),_(screen, player, action, data) -> (
|
||||||
|
print(player('all'),str('%s\n%s\n%s',player,action,data)); //for testing
|
||||||
|
if(action=='pickup',
|
||||||
|
inventory_set(screen,data:'slot',1,if(inventory_get(screen,data:'slot'),'air','red_stained_glass_pane'));
|
||||||
|
);
|
||||||
|
'cancel'
|
||||||
|
));
|
||||||
|
);
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Anvil text prompt</summary>
|
||||||
|
|
||||||
|
```py
|
||||||
|
// anvil text prompt gui
|
||||||
|
__command() -> (
|
||||||
|
global_screen = create_screen(player(),'anvil',format('r Enter a text'),_(screen, player, action, data)->(
|
||||||
|
if(action == 'pickup' && data:'slot' == 2,
|
||||||
|
renamed_item = inventory_get(screen,2);
|
||||||
|
nbt = renamed_item:2;
|
||||||
|
name = parse_nbt(nbt:'display':'Name'):'text';
|
||||||
|
if(!name, return('cancel')); //don't accept empty string
|
||||||
|
print(player,'Text: ' + name);
|
||||||
|
close_screen(screen);
|
||||||
|
);
|
||||||
|
'cancel'
|
||||||
|
));
|
||||||
|
inventory_set(global_screen,0,1,'paper','{display:{Name:\'{"text":""}\'}}');
|
||||||
|
);
|
||||||
|
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Lectern flip book</summary>
|
||||||
|
|
||||||
|
```py
|
||||||
|
// flip book lectern
|
||||||
|
|
||||||
|
global_fac = 256/60;
|
||||||
|
curve(v) -> (
|
||||||
|
v = v%360;
|
||||||
|
if(v<60,v*global_fac,v<180,255,v<240,255-(v-180)*global_fac,0);
|
||||||
|
);
|
||||||
|
|
||||||
|
hex_from_hue(hue) -> str('#%02X%02X%02X',curve(hue+120),curve(hue),curve(hue+240));
|
||||||
|
|
||||||
|
make_char(hue) -> str('{"text":"▉","color":"%s"}',hex_from_hue(hue));
|
||||||
|
|
||||||
|
make_page(hue) -> (
|
||||||
|
page = '[';
|
||||||
|
loop(15, //row
|
||||||
|
y = _;
|
||||||
|
loop(14, //col
|
||||||
|
x = _;
|
||||||
|
page += make_char(hue+x*4+y*4) + ',';
|
||||||
|
);
|
||||||
|
);
|
||||||
|
return(slice(page,0,-2)+']');
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
__command() -> (
|
||||||
|
screen = create_screen(player(),'lectern','Lectern example (this text is not visible)',_(screen, player, action, data)->(
|
||||||
|
if(action=='button',
|
||||||
|
print(player,'Button: ' + data:'button');
|
||||||
|
);
|
||||||
|
'cancel'
|
||||||
|
));
|
||||||
|
|
||||||
|
page_count = 60;
|
||||||
|
pages = [];
|
||||||
|
|
||||||
|
loop(page_count,
|
||||||
|
hue = _/page_count*360;
|
||||||
|
pages += make_page(hue);
|
||||||
|
);
|
||||||
|
|
||||||
|
nbt = encode_nbt({
|
||||||
|
'pages'-> pages,
|
||||||
|
'author'->'-',
|
||||||
|
'title'->'-',
|
||||||
|
'resolved'->1
|
||||||
|
});
|
||||||
|
|
||||||
|
inventory_set(screen,0,1,'written_book',nbt);
|
||||||
|
|
||||||
|
task(_(outer(screen),outer(page_count))->(
|
||||||
|
while(screen != null && screen_property(screen,'open'),100000,
|
||||||
|
p = (p+1)%page_count;
|
||||||
|
screen_property(screen,'page',p);
|
||||||
|
sleep(50);
|
||||||
|
);
|
||||||
|
));
|
||||||
|
);
|
||||||
|
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>generic_3x3 cursor stack</summary>
|
||||||
|
|
||||||
|
```py
|
||||||
|
__command() -> (
|
||||||
|
screen = create_screen(player(),'generic_3x3','Title',_(screen, player, action, data) -> (
|
||||||
|
if(action=='pickup',
|
||||||
|
// set slot to the cursor stack item
|
||||||
|
inventory_set(screen,data:'slot',1,inventory_get(screen,-1):0);
|
||||||
|
);
|
||||||
|
'cancel'
|
||||||
|
));
|
||||||
|
|
||||||
|
task(_(outer(screen))->(
|
||||||
|
// keep the cursor stack item blinking
|
||||||
|
while(screen_property(screen,'open'),100000,
|
||||||
|
inventory_set(screen,-1,1,'red_concrete');
|
||||||
|
sleep(500);
|
||||||
|
inventory_set(screen,-1,1,'lime_concrete');
|
||||||
|
sleep(500);
|
||||||
|
);
|
||||||
|
));
|
||||||
|
);
|
||||||
|
```
|
||||||
|
</details>
|
||||||
@@ -15,11 +15,13 @@ blocks_movement
|
|||||||
bossbar
|
bossbar
|
||||||
brightness
|
brightness
|
||||||
chunk_tickets
|
chunk_tickets
|
||||||
|
close_screen
|
||||||
convert_date
|
convert_date
|
||||||
crafting_remaining_item
|
crafting_remaining_item
|
||||||
create_datapack
|
create_datapack
|
||||||
create_explosion
|
create_explosion
|
||||||
create_marker
|
create_marker
|
||||||
|
create_screen
|
||||||
current_dimension
|
current_dimension
|
||||||
day_time
|
day_time
|
||||||
delete_file
|
delete_file
|
||||||
@@ -100,6 +102,7 @@ scoreboard_add
|
|||||||
scoreboard_remove
|
scoreboard_remove
|
||||||
scoreboard_display
|
scoreboard_display
|
||||||
scoreboard_property
|
scoreboard_property
|
||||||
|
screen_property
|
||||||
tag_matches
|
tag_matches
|
||||||
team_add
|
team_add
|
||||||
team_leave
|
team_leave
|
||||||
|
|||||||
@@ -71,6 +71,8 @@
|
|||||||
|
|
||||||
team_add team_list team_remove team_leave team_property
|
team_add team_list team_remove team_leave team_property
|
||||||
|
|
||||||
|
create_screen close_screen screen_property
|
||||||
|
|
||||||
bossbar
|
bossbar
|
||||||
|
|
||||||
player spawn entity_id entity_list entity_area entity_selector query modify entity_event entity_load_handler entity_types
|
player spawn entity_id entity_list entity_area entity_selector query modify entity_event entity_load_handler entity_types
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package carpet.fakes;
|
||||||
|
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.recipe.Recipe;
|
||||||
|
import net.minecraft.screen.Property;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
|
||||||
|
public interface ScreenHandlerInterface {
|
||||||
|
Property getProperty(int index);
|
||||||
|
boolean callButtonClickListener(int button, PlayerEntity player);
|
||||||
|
boolean callSelectRecipeListener(ServerPlayerEntity player, Recipe<?> recipe, boolean craftAll);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import carpet.fakes.ScreenHandlerInterface;
|
||||||
|
import net.minecraft.recipe.Recipe;
|
||||||
|
import net.minecraft.screen.AbstractRecipeScreenHandler;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
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.CallbackInfo;
|
||||||
|
|
||||||
|
@Mixin(AbstractRecipeScreenHandler.class)
|
||||||
|
public class AbstractRecipeScreenHandler_scarpetMixin {
|
||||||
|
@Inject(method = "fillInputSlots",at = @At("HEAD"), cancellable = true)
|
||||||
|
private void selectRecipeCallback(boolean craftAll, Recipe<?> recipe, ServerPlayerEntity player, CallbackInfo ci) {
|
||||||
|
if(((ScreenHandlerInterface) this).callSelectRecipeListener(player,recipe,craftAll))
|
||||||
|
ci.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
import carpet.fakes.ScreenHandlerInterface;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.screen.EnchantmentScreenHandler;
|
||||||
|
import net.minecraft.screen.LecternScreenHandler;
|
||||||
|
import net.minecraft.screen.LoomScreenHandler;
|
||||||
|
import net.minecraft.screen.ScreenHandler;
|
||||||
|
import net.minecraft.screen.ScreenHandlerType;
|
||||||
|
import net.minecraft.screen.StonecutterScreenHandler;
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
//classes that override onButtonClick
|
||||||
|
@Mixin({EnchantmentScreenHandler.class, LecternScreenHandler.class, LoomScreenHandler.class, StonecutterScreenHandler.class})
|
||||||
|
public abstract class ScreenHandlerSubclasses_scarpetMixin extends ScreenHandler {
|
||||||
|
protected ScreenHandlerSubclasses_scarpetMixin(ScreenHandlerType<?> type, int syncId) {
|
||||||
|
super(type, syncId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "onButtonClick", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void buttonClickCallback(PlayerEntity player, int id, CallbackInfoReturnable<Boolean> cir) {
|
||||||
|
if(((ScreenHandlerInterface) this).callButtonClickListener(id,player))
|
||||||
|
cir.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package carpet.mixins;
|
||||||
|
|
||||||
|
|
||||||
|
import carpet.fakes.ScreenHandlerInterface;
|
||||||
|
import carpet.script.value.ScreenValue;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.recipe.Recipe;
|
||||||
|
import net.minecraft.screen.Property;
|
||||||
|
import net.minecraft.screen.ScreenHandler;
|
||||||
|
import net.minecraft.screen.ScreenHandlerListener;
|
||||||
|
import net.minecraft.screen.slot.SlotActionType;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
|
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;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Mixin(ScreenHandler.class)
|
||||||
|
public abstract class ScreenHandler_scarpetMixin implements ScreenHandlerInterface
|
||||||
|
{
|
||||||
|
@Shadow @Final private List<ScreenHandlerListener> listeners;
|
||||||
|
@Shadow public abstract void syncState();
|
||||||
|
@Shadow @Final private List<Property> properties;
|
||||||
|
|
||||||
|
@Inject(method = "internalOnSlotClick", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void callSlotClickListener(int slotIndex, int button, SlotActionType actionType, PlayerEntity player, CallbackInfo ci) {
|
||||||
|
if(!(player instanceof ServerPlayerEntity serverPlayerEntity)) return;
|
||||||
|
for(ScreenHandlerListener screenHandlerListener : this.listeners) {
|
||||||
|
if(screenHandlerListener instanceof ScreenValue.ScarpetScreenHandlerListener scarpetScreenHandlerListener) {
|
||||||
|
if(scarpetScreenHandlerListener.onSlotClick(serverPlayerEntity, actionType, slotIndex, button)) {
|
||||||
|
ci.cancel();
|
||||||
|
syncState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "close", at = @At("HEAD"), cancellable = true)
|
||||||
|
private void callCloseListener(PlayerEntity player, CallbackInfo ci) {
|
||||||
|
if(!(player instanceof ServerPlayerEntity serverPlayerEntity)) return;
|
||||||
|
for(ScreenHandlerListener screenHandlerListener : this.listeners) {
|
||||||
|
if(screenHandlerListener instanceof ScreenValue.ScarpetScreenHandlerListener scarpetScreenHandlerListener) {
|
||||||
|
scarpetScreenHandlerListener.onClose(serverPlayerEntity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean callButtonClickListener(int button, PlayerEntity player) {
|
||||||
|
if(!(player instanceof ServerPlayerEntity serverPlayerEntity)) return false;
|
||||||
|
for(ScreenHandlerListener screenHandlerListener : listeners) {
|
||||||
|
if(screenHandlerListener instanceof ScreenValue.ScarpetScreenHandlerListener scarpetScreenHandlerListener) {
|
||||||
|
if(scarpetScreenHandlerListener.onButtonClick(serverPlayerEntity, button))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean callSelectRecipeListener(ServerPlayerEntity player, Recipe<?> recipe, boolean craftAll) {
|
||||||
|
for(ScreenHandlerListener screenHandlerListener : listeners) {
|
||||||
|
if(screenHandlerListener instanceof ScreenValue.ScarpetScreenHandlerListener scarpetScreenHandlerListener) {
|
||||||
|
if(scarpetScreenHandlerListener.onSelectRecipe(player, recipe, craftAll))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Property getProperty(int index) {
|
||||||
|
return this.properties.get(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,15 +4,20 @@ import carpet.fakes.IngredientInterface;
|
|||||||
import carpet.fakes.RecipeManagerInterface;
|
import carpet.fakes.RecipeManagerInterface;
|
||||||
import carpet.script.CarpetContext;
|
import carpet.script.CarpetContext;
|
||||||
import carpet.script.Expression;
|
import carpet.script.Expression;
|
||||||
|
import carpet.script.argument.FunctionArgument;
|
||||||
import carpet.script.exception.InternalExpressionException;
|
import carpet.script.exception.InternalExpressionException;
|
||||||
import carpet.script.exception.ThrowStatement;
|
import carpet.script.exception.ThrowStatement;
|
||||||
import carpet.script.exception.Throwables;
|
import carpet.script.exception.Throwables;
|
||||||
import carpet.script.utils.InputValidator;
|
import carpet.script.utils.InputValidator;
|
||||||
import carpet.script.value.BooleanValue;
|
import carpet.script.value.BooleanValue;
|
||||||
|
import carpet.script.value.EntityValue;
|
||||||
|
import carpet.script.value.FormattedTextValue;
|
||||||
|
import carpet.script.value.FunctionValue;
|
||||||
import carpet.script.value.ListValue;
|
import carpet.script.value.ListValue;
|
||||||
import carpet.script.value.NBTSerializableValue;
|
import carpet.script.value.NBTSerializableValue;
|
||||||
import carpet.script.value.NullValue;
|
import carpet.script.value.NullValue;
|
||||||
import carpet.script.value.NumericValue;
|
import carpet.script.value.NumericValue;
|
||||||
|
import carpet.script.value.ScreenValue;
|
||||||
import carpet.script.value.StringValue;
|
import carpet.script.value.StringValue;
|
||||||
import carpet.script.value.Value;
|
import carpet.script.value.Value;
|
||||||
import carpet.script.value.ValueConversions;
|
import carpet.script.value.ValueConversions;
|
||||||
@@ -25,7 +30,6 @@ import net.minecraft.entity.player.PlayerEntity;
|
|||||||
import net.minecraft.item.Item;
|
import net.minecraft.item.Item;
|
||||||
import net.minecraft.item.ItemGroup;
|
import net.minecraft.item.ItemGroup;
|
||||||
import net.minecraft.item.ItemStack;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.item.Items;
|
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.nbt.NbtCompound;
|
||||||
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
|
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
|
||||||
import net.minecraft.recipe.AbstractCookingRecipe;
|
import net.minecraft.recipe.AbstractCookingRecipe;
|
||||||
@@ -36,10 +40,9 @@ import net.minecraft.recipe.ShapedRecipe;
|
|||||||
import net.minecraft.recipe.ShapelessRecipe;
|
import net.minecraft.recipe.ShapelessRecipe;
|
||||||
import net.minecraft.recipe.SpecialCraftingRecipe;
|
import net.minecraft.recipe.SpecialCraftingRecipe;
|
||||||
import net.minecraft.server.network.ServerPlayerEntity;
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
import net.minecraft.tag.Tag;
|
|
||||||
import net.minecraft.tag.TagManager;
|
import net.minecraft.tag.TagManager;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
import net.minecraft.util.InvalidIdentifierException;
|
|
||||||
import net.minecraft.util.math.Vec3d;
|
import net.minecraft.util.math.Vec3d;
|
||||||
import net.minecraft.util.registry.Registry;
|
import net.minecraft.util.registry.Registry;
|
||||||
|
|
||||||
@@ -381,11 +384,50 @@ public class Inventories {
|
|||||||
}
|
}
|
||||||
return new NumericValue(item.getStack().getCount());
|
return new NumericValue(item.getStack().getCount());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
expression.addContextFunction("create_screen",-1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
if(lv.size() < 3) throw new InternalExpressionException("'create_screen' requires at least three arguments");
|
||||||
|
Value playerValue = lv.get(0);
|
||||||
|
ServerPlayerEntity player = EntityValue.getPlayerByValue(((CarpetContext) c).s.getServer(), playerValue);
|
||||||
|
if(player == null) throw new InternalExpressionException("'create_screen' requires a valid online player as the first argument.");
|
||||||
|
String type = lv.get(1).getString();
|
||||||
|
Text name = FormattedTextValue.getTextByValue(lv.get(2));
|
||||||
|
FunctionValue function = null;
|
||||||
|
if(lv.size() > 3)
|
||||||
|
function = FunctionArgument.findIn(c, expression.module, lv, 3, true, false).function;
|
||||||
|
|
||||||
|
return new ScreenValue(player,type,name,function,c);
|
||||||
|
});
|
||||||
|
|
||||||
|
expression.addContextFunction("close_screen",1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
Value value = lv.get(0);
|
||||||
|
if(!(value instanceof ScreenValue screenValue)) throw new InternalExpressionException("'close_screen' requires a screen value as the first argument.");
|
||||||
|
if(!screenValue.isOpen()) return Value.FALSE;
|
||||||
|
screenValue.close();
|
||||||
|
return Value.TRUE;
|
||||||
|
});
|
||||||
|
|
||||||
|
expression.addContextFunction("screen_property",-1, (c, t, lv) ->
|
||||||
|
{
|
||||||
|
if(lv.size()<2) throw new InternalExpressionException("'screen_property' requires at least a screen and a property name");
|
||||||
|
if(!(lv.get(0) instanceof ScreenValue screenValue)) throw new InternalExpressionException("'screen_property' requires a screen value as the first argument");
|
||||||
|
String propertyName = lv.get(1).getString();
|
||||||
|
if(lv.size()>=3)
|
||||||
|
{
|
||||||
|
return screenValue.modifyProperty(propertyName,lv.subList(2,lv.size()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return screenValue.queryProperty(propertyName);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void syncPlayerInventory(NBTSerializableValue.InventoryLocator inventory, int int_1)
|
private static void syncPlayerInventory(NBTSerializableValue.InventoryLocator inventory, int int_1)
|
||||||
{
|
{
|
||||||
if (inventory.owner() instanceof ServerPlayerEntity player && !inventory.isEnder())
|
if (inventory.owner() instanceof ServerPlayerEntity player && !inventory.isEnder() && !(inventory.inventory() instanceof ScreenValue.ScreenHandlerInventory))
|
||||||
{
|
{
|
||||||
player.networkHandler.sendPacket(new ScreenHandlerSlotUpdateS2CPacket(
|
player.networkHandler.sendPacket(new ScreenHandlerSlotUpdateS2CPacket(
|
||||||
-2, 0, // resolve mystery argument
|
-2, 0, // resolve mystery argument
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ public class Throwables {
|
|||||||
public static final Throwables UNKNOWN_DIMENSION = register("unknown_dimension", VALUE_EXCEPTION);
|
public static final Throwables UNKNOWN_DIMENSION = register("unknown_dimension", VALUE_EXCEPTION);
|
||||||
public static final Throwables UNKNOWN_STRUCTURE = register("unknown_structure", VALUE_EXCEPTION);
|
public static final Throwables UNKNOWN_STRUCTURE = register("unknown_structure", VALUE_EXCEPTION);
|
||||||
public static final Throwables UNKNOWN_CRITERION = register("unknown_criterion", VALUE_EXCEPTION);
|
public static final Throwables UNKNOWN_CRITERION = register("unknown_criterion", VALUE_EXCEPTION);
|
||||||
|
public static final Throwables UNKNOWN_SCREEN = register("unknown_screen", VALUE_EXCEPTION);
|
||||||
public static final Throwables IO_EXCEPTION = register("io_exception", THROWN_EXCEPTION_TYPE);
|
public static final Throwables IO_EXCEPTION = register("io_exception", THROWN_EXCEPTION_TYPE);
|
||||||
public static final Throwables NBT_ERROR = register("nbt_error", IO_EXCEPTION);
|
public static final Throwables NBT_ERROR = register("nbt_error", IO_EXCEPTION);
|
||||||
public static final Throwables JSON_ERROR = register("json_error", IO_EXCEPTION);
|
public static final Throwables JSON_ERROR = register("json_error", IO_EXCEPTION);
|
||||||
|
|||||||
@@ -266,6 +266,11 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
return null;
|
return null;
|
||||||
return new InventoryLocator(pos, pos, inv, offset+1);
|
return new InventoryLocator(pos, pos, inv, offset+1);
|
||||||
}
|
}
|
||||||
|
if (v1 instanceof ScreenValue screenValue)
|
||||||
|
{
|
||||||
|
if(!screenValue.isOpen()) return null;
|
||||||
|
return new InventoryLocator(screenValue.getPlayer(), screenValue.getPlayer().getBlockPos(), screenValue.getInventory(), offset+1);
|
||||||
|
}
|
||||||
BlockPos pos = new BlockPos(
|
BlockPos pos = new BlockPos(
|
||||||
NumericValue.asNumber(v1).getDouble(),
|
NumericValue.asNumber(v1).getDouble(),
|
||||||
NumericValue.asNumber(params.get(1 + offset)).getDouble(),
|
NumericValue.asNumber(params.get(1 + offset)).getDouble(),
|
||||||
@@ -277,7 +282,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
|||||||
}
|
}
|
||||||
catch (IndexOutOfBoundsException e)
|
catch (IndexOutOfBoundsException e)
|
||||||
{
|
{
|
||||||
throw new InternalExpressionException("Inventory should be defined either by three coordinates, a block value, or an entity");
|
throw new InternalExpressionException("Inventory should be defined either by three coordinates, a block value, an entity, or a screen");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,461 @@
|
|||||||
|
package carpet.script.value;
|
||||||
|
|
||||||
|
import carpet.CarpetServer;
|
||||||
|
import carpet.CarpetSettings;
|
||||||
|
import carpet.fakes.ScreenHandlerInterface;
|
||||||
|
|
||||||
|
import carpet.script.CarpetContext;
|
||||||
|
import carpet.script.CarpetScriptHost;
|
||||||
|
import carpet.script.CarpetScriptServer;
|
||||||
|
import carpet.script.Context;
|
||||||
|
import carpet.script.exception.IntegrityException;
|
||||||
|
import carpet.script.exception.InternalExpressionException;
|
||||||
|
import carpet.script.exception.InvalidCallbackException;
|
||||||
|
import carpet.script.exception.ThrowStatement;
|
||||||
|
import carpet.script.exception.Throwables;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.entity.player.PlayerInventory;
|
||||||
|
import net.minecraft.inventory.Inventory;
|
||||||
|
import net.minecraft.inventory.SimpleInventory;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.nbt.NbtCompound;
|
||||||
|
import net.minecraft.nbt.NbtElement;
|
||||||
|
import net.minecraft.nbt.NbtList;
|
||||||
|
import net.minecraft.recipe.Recipe;
|
||||||
|
import net.minecraft.screen.AbstractFurnaceScreenHandler;
|
||||||
|
import net.minecraft.screen.AnvilScreenHandler;
|
||||||
|
import net.minecraft.screen.ArrayPropertyDelegate;
|
||||||
|
import net.minecraft.screen.BeaconScreenHandler;
|
||||||
|
import net.minecraft.screen.BlastFurnaceScreenHandler;
|
||||||
|
import net.minecraft.screen.BrewingStandScreenHandler;
|
||||||
|
import net.minecraft.screen.CartographyTableScreenHandler;
|
||||||
|
import net.minecraft.screen.CraftingScreenHandler;
|
||||||
|
import net.minecraft.screen.EnchantmentScreenHandler;
|
||||||
|
import net.minecraft.screen.FurnaceScreenHandler;
|
||||||
|
import net.minecraft.screen.GenericContainerScreenHandler;
|
||||||
|
import net.minecraft.screen.GrindstoneScreenHandler;
|
||||||
|
import net.minecraft.screen.HopperScreenHandler;
|
||||||
|
import net.minecraft.screen.LecternScreenHandler;
|
||||||
|
import net.minecraft.screen.LoomScreenHandler;
|
||||||
|
import net.minecraft.screen.MerchantScreenHandler;
|
||||||
|
import net.minecraft.screen.NamedScreenHandlerFactory;
|
||||||
|
import net.minecraft.screen.Property;
|
||||||
|
import net.minecraft.screen.ScreenHandler;
|
||||||
|
import net.minecraft.screen.ScreenHandlerListener;
|
||||||
|
import net.minecraft.screen.ShulkerBoxScreenHandler;
|
||||||
|
import net.minecraft.screen.SimpleNamedScreenHandlerFactory;
|
||||||
|
import net.minecraft.screen.SmithingScreenHandler;
|
||||||
|
import net.minecraft.screen.SmokerScreenHandler;
|
||||||
|
import net.minecraft.screen.StonecutterScreenHandler;
|
||||||
|
import net.minecraft.screen.slot.Slot;
|
||||||
|
import net.minecraft.screen.slot.SlotActionType;
|
||||||
|
import net.minecraft.server.command.ServerCommandSource;
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity;
|
||||||
|
import net.minecraft.text.Text;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.OptionalInt;
|
||||||
|
|
||||||
|
import static net.minecraft.screen.ScreenHandlerType.*;
|
||||||
|
|
||||||
|
public class ScreenValue extends Value {
|
||||||
|
private ScreenHandler screenHandler;
|
||||||
|
private ScreenHandlerInventory inventory;
|
||||||
|
|
||||||
|
private final Text name;
|
||||||
|
private final String typestring;
|
||||||
|
private final FunctionValue callback;
|
||||||
|
private final String hostname;
|
||||||
|
private final ServerPlayerEntity player;
|
||||||
|
|
||||||
|
|
||||||
|
public static Map<String,ScarpetScreenHandlerFactory> screenHandlerFactories;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
screenHandlerFactories = new HashMap<>();
|
||||||
|
|
||||||
|
screenHandlerFactories.put("anvil",(syncId, playerInventory) -> new AnvilScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("beacon",(syncId, playerInventory) -> new BeaconScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("blast_furnace",(syncId, playerInventory) -> new BlastFurnaceScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("brewing_stand",(syncId, playerInventory) -> new BrewingStandScreenHandler(syncId,playerInventory,new SimpleInventory(5),new ArrayPropertyDelegate(2)));
|
||||||
|
screenHandlerFactories.put("cartography_table",(syncId, playerInventory) -> new CartographyTableScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("crafting",(syncId, playerInventory) -> new CraftingScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("enchantment",(syncId, playerInventory) -> new EnchantmentScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("furnace",(syncId, playerInventory) -> new FurnaceScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("generic_3x3",((syncId, playerInventory) -> new GenericContainerScreenHandler(GENERIC_3X3,syncId,playerInventory,new SimpleInventory(9),1)));
|
||||||
|
screenHandlerFactories.put("generic_9x1",((syncId, playerInventory) -> new GenericContainerScreenHandler(GENERIC_9X1,syncId,playerInventory,new SimpleInventory(9),1)));
|
||||||
|
screenHandlerFactories.put("generic_9x2",((syncId, playerInventory) -> new GenericContainerScreenHandler(GENERIC_9X2,syncId,playerInventory,new SimpleInventory(9*2),2)));
|
||||||
|
screenHandlerFactories.put("generic_9x3",((syncId, playerInventory) -> new GenericContainerScreenHandler(GENERIC_9X3,syncId,playerInventory,new SimpleInventory(9*3),3)));
|
||||||
|
screenHandlerFactories.put("generic_9x4",((syncId, playerInventory) -> new GenericContainerScreenHandler(GENERIC_9X4,syncId,playerInventory,new SimpleInventory(9*4),4)));
|
||||||
|
screenHandlerFactories.put("generic_9x5",((syncId, playerInventory) -> new GenericContainerScreenHandler(GENERIC_9X5,syncId,playerInventory,new SimpleInventory(9*5),5)));
|
||||||
|
screenHandlerFactories.put("generic_9x6",((syncId, playerInventory) -> new GenericContainerScreenHandler(GENERIC_9X6,syncId,playerInventory,new SimpleInventory(9*6),6)));
|
||||||
|
screenHandlerFactories.put("grindstone",(syncId, playerInventory) -> new GrindstoneScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("hopper",(syncId, playerInventory) -> new HopperScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("lectern",(syncId, playerInventory) -> new LecternScreenHandler(syncId,new SimpleInventory(1),new ArrayPropertyDelegate(1)));
|
||||||
|
screenHandlerFactories.put("loom",(syncId, playerInventory) -> new LoomScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("merchant",(syncId, playerInventory) -> new MerchantScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("shulker_box",(syncId, playerInventory) -> new ShulkerBoxScreenHandler(syncId,playerInventory,new SimpleInventory(9*3)));
|
||||||
|
screenHandlerFactories.put("smithing",(syncId, playerInventory) -> new SmithingScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("smoker",(syncId, playerInventory) -> new SmokerScreenHandler(syncId,playerInventory));
|
||||||
|
screenHandlerFactories.put("stonecutter",(syncId, playerInventory) -> new StonecutterScreenHandler(syncId,playerInventory));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected interface ScarpetScreenHandlerFactory {
|
||||||
|
ScreenHandler create(int syncId, PlayerInventory playerInventory);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public ScreenValue(ServerPlayerEntity player, String type, Text name, FunctionValue callback, Context c) {
|
||||||
|
this.name = name;
|
||||||
|
this.typestring = type.toLowerCase();
|
||||||
|
if(callback != null) callback.checkArgs(4);
|
||||||
|
this.callback = callback;
|
||||||
|
this.hostname = c.host.getName();
|
||||||
|
this.player = player;
|
||||||
|
NamedScreenHandlerFactory factory = this.createScreenHandlerFactory();
|
||||||
|
if(factory == null) throw new ThrowStatement(type, Throwables.UNKNOWN_SCREEN);
|
||||||
|
this.openScreen(factory);
|
||||||
|
this.inventory = new ScreenHandlerInventory(this.screenHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private NamedScreenHandlerFactory createScreenHandlerFactory() {
|
||||||
|
if(!screenHandlerFactories.containsKey(this.typestring)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SimpleNamedScreenHandlerFactory((i, playerInventory, playerEntity) -> {
|
||||||
|
ScreenHandler screen = screenHandlerFactories.get(ScreenValue.this.typestring).create(i,playerInventory);
|
||||||
|
ScreenValue.this.addListenerCallback(screen);
|
||||||
|
ScreenValue.this.screenHandler = screen;
|
||||||
|
return screen;
|
||||||
|
}, this.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openScreen(NamedScreenHandlerFactory factory) {
|
||||||
|
if(this.player == null) return;
|
||||||
|
OptionalInt optionalSyncId = this.player.openHandledScreen(factory);
|
||||||
|
if(optionalSyncId.isPresent() && this.player.currentScreenHandler.syncId == optionalSyncId.getAsInt()) {
|
||||||
|
this.screenHandler = this.player.currentScreenHandler;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if(this.player.currentScreenHandler != this.player.playerScreenHandler) {
|
||||||
|
//prevent recursion when closing screen in closing screen callback by doing this before triggering event
|
||||||
|
this.inventory = null;
|
||||||
|
this.player.currentScreenHandler = this.player.playerScreenHandler;
|
||||||
|
this.player.closeHandledScreen();
|
||||||
|
this.screenHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isOpen() {
|
||||||
|
if(this.screenHandler == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(this.player.currentScreenHandler.syncId == this.screenHandler.syncId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
this.screenHandler = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private boolean callListener(ServerPlayerEntity player, String action, Map<Value,Value> data) {
|
||||||
|
Value playerValue = EntityValue.of(player);
|
||||||
|
Value actionValue = StringValue.of(action);
|
||||||
|
Value dataValue = MapValue.wrap(data);
|
||||||
|
List<Value> args = Arrays.asList(this,playerValue,actionValue,dataValue);
|
||||||
|
CarpetScriptHost appHost = CarpetServer.scriptServer.getAppHostByName(this.hostname);
|
||||||
|
if(appHost == null) {
|
||||||
|
this.close();
|
||||||
|
this.screenHandler = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ServerCommandSource source = player.getCommandSource().withLevel(CarpetSettings.runPermissionLevel);
|
||||||
|
CarpetScriptHost executingHost = appHost.retrieveForExecution(source,player);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Value cancelValue = executingHost.callUDF(player.getBlockPos(), source.withLevel(CarpetSettings.runPermissionLevel), callback, args);
|
||||||
|
return cancelValue.getString().equals("cancel");
|
||||||
|
}
|
||||||
|
catch (NullPointerException | InvalidCallbackException | IntegrityException error)
|
||||||
|
{
|
||||||
|
CarpetScriptServer.LOG.error("Got exception when running screen event call ", error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addListenerCallback(ScreenHandler screenHandler) {
|
||||||
|
if(this.callback == null) return;
|
||||||
|
|
||||||
|
screenHandler.addListener(new ScarpetScreenHandlerListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onSlotClick(ServerPlayerEntity player, SlotActionType actionType, int slot, int button) {
|
||||||
|
Map<Value,Value> data = new HashMap<>();
|
||||||
|
data.put(StringValue.of("slot"),slot == ScreenHandler.EMPTY_SPACE_SLOT_INDEX ? Value.NULL : NumericValue.of(slot));
|
||||||
|
if(actionType == SlotActionType.QUICK_CRAFT) {
|
||||||
|
data.put(StringValue.of("quick_craft_stage"),NumericValue.of(ScreenHandler.unpackQuickCraftStage(button)));
|
||||||
|
button = ScreenHandler.unpackQuickCraftButton(button);
|
||||||
|
}
|
||||||
|
data.put(StringValue.of("button"),NumericValue.of(button));
|
||||||
|
return ScreenValue.this.callListener(player,actionTypeToString(actionType),data);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean onButtonClick(ServerPlayerEntity player, int button) {
|
||||||
|
Map<Value,Value> data = new HashMap<>();
|
||||||
|
data.put(StringValue.of("button"),NumericValue.of(button));
|
||||||
|
return ScreenValue.this.callListener(player,"button",data);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onClose(ServerPlayerEntity player) {
|
||||||
|
Map<Value,Value> data = new HashMap<>();
|
||||||
|
ScreenValue.this.callListener(player,"close",data);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public boolean onSelectRecipe(ServerPlayerEntity player, Recipe<?> recipe, boolean craftAll) {
|
||||||
|
Map<Value,Value> data = new HashMap<>();
|
||||||
|
data.put(StringValue.of("recipe"),StringValue.of(recipe.getId().toString()));
|
||||||
|
data.put(StringValue.of("craft_all"),BooleanValue.of(craftAll));
|
||||||
|
return ScreenValue.this.callListener(player,"select_recipe",data);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onSlotUpdate(ScreenHandler handler, int slotId, ItemStack stack) {
|
||||||
|
Map<Value,Value> data = new HashMap<>();
|
||||||
|
data.put(StringValue.of("slot"),NumericValue.of(slotId));
|
||||||
|
data.put(StringValue.of("stack"),ValueConversions.of(stack));
|
||||||
|
ScreenValue.this.callListener(ScreenValue.this.player,"slot_update",data);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onPropertyUpdate(ScreenHandler handler, int property, int value) {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private Property getPropertyForType(Class<? extends ScreenHandler> screenHandlerClass, String requiredType, int propertyIndex, String propertyName) {
|
||||||
|
if(screenHandlerClass.isInstance(this.screenHandler)) {
|
||||||
|
return ((ScreenHandlerInterface) this.screenHandler).getProperty(propertyIndex);
|
||||||
|
}
|
||||||
|
if(!this.isOpen()) {
|
||||||
|
throw new InternalExpressionException("Screen property cannot be accessed, because the screen is already closed");
|
||||||
|
}
|
||||||
|
throw new InternalExpressionException("Screen property " + propertyName + " expected a " + requiredType + " screen.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private Property getProperty(String propertyName) {
|
||||||
|
return switch (propertyName) {
|
||||||
|
case "fuel_progress" -> getPropertyForType(AbstractFurnaceScreenHandler.class, "furnace", 0, propertyName);
|
||||||
|
case "max_fuel_progress" -> getPropertyForType(AbstractFurnaceScreenHandler.class, "furnace", 1, propertyName);
|
||||||
|
case "cook_progress" -> getPropertyForType(AbstractFurnaceScreenHandler.class, "furnace", 2, propertyName);
|
||||||
|
case "max_cook_progress" -> getPropertyForType(AbstractFurnaceScreenHandler.class, "furnace", 3, propertyName);
|
||||||
|
|
||||||
|
case "level_cost" -> getPropertyForType(AnvilScreenHandler.class, "anvil", 0, propertyName);
|
||||||
|
|
||||||
|
case "page" -> getPropertyForType(LecternScreenHandler.class, "lectern", 0, propertyName);
|
||||||
|
|
||||||
|
case "beacon_level" -> getPropertyForType(BeaconScreenHandler.class, "beacon", 0, propertyName);
|
||||||
|
case "primary_effect" -> getPropertyForType(BeaconScreenHandler.class, "beacon", 1, propertyName);
|
||||||
|
case "secondary_effect" -> getPropertyForType(BeaconScreenHandler.class, "beacon", 2, propertyName);
|
||||||
|
|
||||||
|
case "brew_time" -> getPropertyForType(BrewingStandScreenHandler.class, "brewing_stand", 0, propertyName);
|
||||||
|
case "brewing_fuel" -> getPropertyForType(BrewingStandScreenHandler.class, "brewing_stand", 1, propertyName);
|
||||||
|
|
||||||
|
case "enchantment_power_1" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 0, propertyName);
|
||||||
|
case "enchantment_power_2" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 1, propertyName);
|
||||||
|
case "enchantment_power_3" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 2, propertyName);
|
||||||
|
case "enchantment_seed" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 3, propertyName);
|
||||||
|
case "enchantment_id_1" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 4, propertyName);
|
||||||
|
case "enchantment_id_2" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 5, propertyName);
|
||||||
|
case "enchantment_id_3" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 6, propertyName);
|
||||||
|
case "enchantment_level_1" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 7, propertyName);
|
||||||
|
case "enchantment_level_2" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 8, propertyName);
|
||||||
|
case "enchantment_level_3" -> getPropertyForType(EnchantmentScreenHandler.class, "enchantment", 9, propertyName);
|
||||||
|
|
||||||
|
case "banner_pattern" -> getPropertyForType(LoomScreenHandler.class, "loom", 0, propertyName);
|
||||||
|
|
||||||
|
case "stonecutter_recipe" -> getPropertyForType(StonecutterScreenHandler.class, "stonecutter", 0, propertyName);
|
||||||
|
|
||||||
|
default -> throw new InternalExpressionException("Invalid screen property: " + propertyName);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public Value queryProperty(String propertyName) {
|
||||||
|
if(propertyName.equals("name")) return FormattedTextValue.of(this.name);
|
||||||
|
if(propertyName.equals("open")) return BooleanValue.of(this.isOpen());
|
||||||
|
Property property = getProperty(propertyName);
|
||||||
|
return NumericValue.of(property.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Value modifyProperty(String propertyName, List<Value> lv) {
|
||||||
|
Property property = getProperty(propertyName);
|
||||||
|
int intValue = NumericValue.asNumber(lv.get(0)).getInt();
|
||||||
|
property.set(intValue);
|
||||||
|
this.screenHandler.syncState();
|
||||||
|
return Value.TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScreenHandler getScreenHandler() {
|
||||||
|
return this.screenHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ServerPlayerEntity getPlayer() {
|
||||||
|
return this.player;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Inventory getInventory() {
|
||||||
|
return this.inventory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString() {
|
||||||
|
return this.typestring + "_screen";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean getBoolean() {
|
||||||
|
return this.isOpen();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTypeString()
|
||||||
|
{
|
||||||
|
return "screen";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NbtElement toTag(boolean force) {
|
||||||
|
if(this.screenHandler == null) {
|
||||||
|
return Value.NULL.toTag(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
NbtList nbtList = new NbtList();
|
||||||
|
for(int i = 0; i < this.screenHandler.slots.size(); i++) {
|
||||||
|
ItemStack itemStack = this.screenHandler.getSlot(i).getStack();
|
||||||
|
nbtList.add(itemStack.writeNbt(new NbtCompound()));
|
||||||
|
}
|
||||||
|
return nbtList;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface ScarpetScreenHandlerListener extends ScreenHandlerListener {
|
||||||
|
boolean onSlotClick(ServerPlayerEntity player, SlotActionType actionType, int slot, int button);
|
||||||
|
boolean onButtonClick(ServerPlayerEntity player, int button);
|
||||||
|
void onClose(ServerPlayerEntity player);
|
||||||
|
boolean onSelectRecipe(ServerPlayerEntity player, Recipe<?> recipe, boolean craftAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class ScreenHandlerInventory implements Inventory {
|
||||||
|
|
||||||
|
protected ScreenHandler screenHandler;
|
||||||
|
|
||||||
|
public ScreenHandlerInventory(ScreenHandler screenHandler) {
|
||||||
|
this.screenHandler = screenHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return this.screenHandler.slots.size() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
for(Slot slot : this.screenHandler.slots) {
|
||||||
|
if(slot.hasStack() && !slot.getStack().isEmpty()) return false;
|
||||||
|
}
|
||||||
|
return this.screenHandler.getCursorStack().isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack getStack(int slot) {
|
||||||
|
if(slot == this.size()-1) return this.screenHandler.getCursorStack();
|
||||||
|
return slot >= -1 && slot < this.size() ? this.screenHandler.slots.get(slot).getStack() : ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack removeStack(int slot, int amount) {
|
||||||
|
ItemStack itemStack;
|
||||||
|
if(slot == this.size()-1)
|
||||||
|
itemStack = this.screenHandler.getCursorStack().split(amount);
|
||||||
|
else
|
||||||
|
itemStack = ScreenHandlerInventory.splitStack(this.screenHandler.slots, slot, amount);
|
||||||
|
if (!itemStack.isEmpty()) {
|
||||||
|
this.markDirty();
|
||||||
|
}
|
||||||
|
return itemStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ItemStack removeStack(int slot) {
|
||||||
|
ItemStack itemStack;
|
||||||
|
if(slot == this.size()-1)
|
||||||
|
itemStack = this.screenHandler.getCursorStack();
|
||||||
|
else
|
||||||
|
itemStack = this.screenHandler.slots.get(slot).getStack();
|
||||||
|
if (itemStack.isEmpty()) {
|
||||||
|
return ItemStack.EMPTY;
|
||||||
|
} else {
|
||||||
|
if(slot == this.size()-1)
|
||||||
|
this.screenHandler.setCursorStack(ItemStack.EMPTY);
|
||||||
|
else
|
||||||
|
this.screenHandler.slots.get(slot).setStack(ItemStack.EMPTY);
|
||||||
|
return itemStack;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStack(int slot, ItemStack stack) {
|
||||||
|
if(slot == this.size()-1)
|
||||||
|
this.screenHandler.setCursorStack(stack);
|
||||||
|
else
|
||||||
|
this.screenHandler.slots.get(slot).setStack(stack);
|
||||||
|
if (!stack.isEmpty() && stack.getCount() > this.getMaxCountPerStack()) {
|
||||||
|
stack.setCount(this.getMaxCountPerStack());
|
||||||
|
}
|
||||||
|
|
||||||
|
this.markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void markDirty() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPlayerUse(PlayerEntity player) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
for(Slot slot : this.screenHandler.slots) {
|
||||||
|
slot.setStack(ItemStack.EMPTY);
|
||||||
|
}
|
||||||
|
this.screenHandler.setCursorStack(ItemStack.EMPTY);
|
||||||
|
this.markDirty();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static ItemStack splitStack(List<Slot> slots, int slot, int amount) {
|
||||||
|
return slot >= 0 && slot < slots.size() && !slots.get(slot).getStack().isEmpty() && amount > 0 ? slots.get(slot).getStack().split(amount) : ItemStack.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String actionTypeToString(SlotActionType actionType) {
|
||||||
|
return switch (actionType) {
|
||||||
|
case PICKUP -> "pickup";
|
||||||
|
case QUICK_MOVE -> "quick_move";
|
||||||
|
case SWAP -> "swap";
|
||||||
|
case CLONE -> "clone";
|
||||||
|
case THROW -> "throw";
|
||||||
|
case QUICK_CRAFT -> "quick_craft";
|
||||||
|
case PICKUP_ALL -> "pickup_all";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -187,6 +187,10 @@
|
|||||||
"Scoreboard_scarpetMixin",
|
"Scoreboard_scarpetMixin",
|
||||||
"Team_scarpetMixin",
|
"Team_scarpetMixin",
|
||||||
"TradeOutputSlot_scarpetEventMixin",
|
"TradeOutputSlot_scarpetEventMixin",
|
||||||
|
"ScreenHandler_scarpetMixin",
|
||||||
|
"ScreenHandlerSubclasses_scarpetMixin",
|
||||||
|
"AbstractRecipeScreenHandler_scarpetMixin",
|
||||||
|
|
||||||
|
|
||||||
"ChunkTicketManager_scarpetMixin",
|
"ChunkTicketManager_scarpetMixin",
|
||||||
"PointOfInterest_scarpetMixin",
|
"PointOfInterest_scarpetMixin",
|
||||||
|
|||||||
Reference in new issue
Block a user