Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions base/data/gm4/function/announce_echeck_results.mcfunction
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Copies the results of environment checks into the log queue to prepare for printing to chat
# @s = none
# at unspecified
# run from await_echeck_results

# if no check failed, no need to print
execute unless data storage gm4:log echecks[{result:{passed:0}}] unless data storage gm4:log echecks[{result:{passed:-1}}] run return 0

# copy results into queue, KEEP VERSION IN echecks, SO WE CAN INSPECT IT FOR DEBUGGING
data modify storage gm4:log queue set from storage gm4:log echecks
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels really weird and hacky. The NBT objects in echecks don't have the same shape as what is expected by queue. This could cause subtle bugs in the future.

This command also completely overwrites the queue field. That feels wrong and might cause problems. For example GM4 base log printing is already delayed to wait for at least one player. Environment checks could finish in between and cause the normal logs to be overwritten.

I would suggest adding a separate recursive function that iterates over the echecks list, or alternatively appending to the existing queue and making sure that it runs a second time if necessary.


# add extra text around warnings
data modify storage gm4:log queue prepend value {type:"text",message:{"text":"[GM4]: Some environment checks have not succeeded:","color":"#4AA0C7"}}
data modify storage gm4:log queue append value {type:"text",message:{"text":"[GM4]: This may lead to unintended behavior.","color":"#4AA0C7"}}

# start announcing
function gm4:log_start
12 changes: 12 additions & 0 deletions base/data/gm4/function/await_echeck_results.mcfunction
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Waits until all environment checks have returned their results
# run from post_load

# maintain timeout timer
scoreboard players remove $echeck_timeout gm4_data 1
execute if score $echeck_timeout gm4_data matches ..0 run return run function gm4:announce_echeck_results

# Peek if any environment checks are still pending (passed:-1), if so, continue waiting
execute if data storage gm4:log echecks[{result:{passed:-1}}] run return run schedule function gm4:await_echeck_results 1t

# all test have returned, print to chat
function gm4:announce_echeck_results
17 changes: 17 additions & 0 deletions base/data/gm4/function/echeck/assign_score.mcfunction
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Assigns a score to this marker and reads it back. Kills the marker afterwards
# @s = test marker, just summoned
# at @s
# run from gm4:echeck/non_player_entity_has_score
# set up marker & run test
scoreboard players set @s gm4_data 1
# depending on test outcome, set the 'result' object in storage. Base uses this to test if the check has completed.

# if the score is present, mark the check as passed by removing it from storage
execute if score @s gm4_data matches 1 run data modify storage gm4:log echecks[{echeck_id:"gm4:echeck/non_player_entity_has_score"}].result set value {passed:1,probable_cause:""}

# if no score is present, we don't know why this would happen. Good luck.
execute unless score @s gm4_data matches 1 run data modify storage gm4:log echecks[{echeck_id:"gm4:echeck/non_player_entity_has_score"}].result set value {passed:0,probable_cause:""}

# clean up marker
scoreboard players reset @s gm4_data
kill @s
19 changes: 19 additions & 0 deletions base/data/gm4/function/echeck/join_team.mcfunction
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Assigns a score to this marker and reads it back. Kills the marker afterwards
# @s = test marker, just summoned
# at @s
# run from gm4:echeck/non_player_entity_has_score
# set up marker & run test
team add gm4_echeck_non_player_entity_on_team_temp
team join gm4_echeck_non_player_entity_on_team_temp @s

# depending on test outcome, set the 'result' object in storage. Base uses this to test if the check has completed.

# if the marker is now on the team, mark the check as passed by removing it from storage
execute if entity @s[team=gm4_echeck_non_player_entity_on_team_temp] run data modify storage gm4:log echecks[{echeck_id:"gm4:echeck/non_player_entity_has_score"}].result set value {passed:1,probable_cause:""}

# if the marker is not on the team, provide a probable cause message to the user
execute unless entity @s[team=gm4_echeck_non_player_entity_on_team_temp] run data modify storage gm4:log echecks[{echeck_id:"gm4:echeck/non_player_entity_has_score"}].result set value {passed:0,probable_cause:"This may be caused by the Paper/Spigot setting 'scoreboards.allow-non-player-entities-on-scoreboards=false'."}

# clean up
team remove gm4_echeck_non_player_entity_on_team_temp
kill @s
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Tests if non-player entities can hold scores. We think spigot might have done this at some point, but are not sure.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After thinking about this again, we should probably just remove this check. I'll get annoying adding this to every module when it doesn't even matter in the current versions.

# @s = unspecified
# at unspecified
# run from modules which require this environment check

# start environment check as marker
execute summon marker run function gm4:echeck/assign_score
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Tests if non-player entities can be on teams. Spigot prevents non-players on teams if the config is changed.
# @s = unspecified
# at unspecified
# run from modules which require this environment check

# start environment check as marker
execute summon marker run function gm4:echeck/join_team
2 changes: 2 additions & 0 deletions base/data/gm4/function/load.mcfunction
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
data merge storage gm4:log {queue:[],versions:[]}
data modify storage gm4:log echecks set value []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This command can be combined into the above command

schedule clear gm4:await_echeck_results
data modify storage gm4:log queue append value {type:"text",message:{"text":"[GM4]: Checking for updates...","color":"#4AA0C7"}}

scoreboard objectives add gm4_modules dummy
Expand Down
2 changes: 2 additions & 0 deletions base/data/gm4/function/log.mcfunction
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ execute if data storage gm4:log log{type:"install"} run tellraw @a[tag=gm4_show_
execute if data storage gm4:log log{type:"missing"} run tellraw @a[tag=gm4_show_log] [{"nbt":"log.module","storage":"gm4:log","color":"red"},{"text":" is disabled because ","color":"red"},{"nbt":"log.require","storage":"gm4:log","color":"red"},{"text":" is not installed."}]
execute if data storage gm4:log log{type:"outdated"} run function gm4:outdated_logs/outdated_start
execute if data storage gm4:log log{type:"version_conflict"} run function gm4:conflict_logs/version_conflict_start
execute if data storage gm4:log log.echeck_id if data storage gm4:log log.result{passed:-1} run tellraw @a[tag=gm4_show_log] [{"text":"-","color":"white"},{"nbt":"log.echeck_id","storage":"gm4:log","interpret":true, "color": "yellow"},{"text":" has timed out!","color":"white"}]
execute if data storage gm4:log log.echeck_id if data storage gm4:log log.result{passed:0} run tellraw @a[tag=gm4_show_log] [{"text":"-","color":"white"},{"nbt":"log.echeck_id","storage":"gm4:log","interpret":true, "color": "yellow"},{"text":" has failed! ","color":"white"},{"nbt":"log.result.probable_cause","storage":"gm4:log","interpret":true,"color":"white"}]

data remove storage gm4:log queue[0]
execute store result score #log_size gm4_data run data get storage gm4:log queue
Expand Down
5 changes: 5 additions & 0 deletions base/data/gm4/function/post_load.mcfunction
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
scoreboard players reset * gm4.echeck_results
execute unless data storage gm4:log queue[{type:"install"}] run data modify storage gm4:log queue append value {type:"text",message:{"text":"[GM4]: No updates found.","color":"#4AA0C7"}}
execute if data storage gm4:log queue[{type:"install"}] run data modify storage gm4:log queue append value {type:"text",message:{"text":"[GM4]: Updates completed.","color":"#4AA0C7"}}

function gm4:log_wait

function #gm4:evaluate_echecks
scoreboard players set $echeck_timeout gm4_data 600
schedule function gm4:await_echeck_results 1t
5 changes: 5 additions & 0 deletions base/data/gm4/tags/function/evaluate_echecks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"values": [
"gm4:echeck/non_player_entity_has_score"
]
}
3 changes: 3 additions & 0 deletions docs/making-a-module.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ meta:
- main # namespace assumed to be the module id
- gm4_bat_grenades:tick # but one can be manually specified

# A list of checks to run on reload while the module is installed. May be omitted.
echecks: [gm4:non_player_entity_has_score]

website:
# A description. This should be a good summary of what this module adds or achieves, to get someone interested in this module
description: Break apart gold and iron tools and weapons for materials. Attach this to a mobfarm to finally make use of those extra armour sets!
Expand Down
130 changes: 130 additions & 0 deletions docs/utilities.md
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll want to update the tag lists in this file to reflect changes in #1264

Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
# Utilities
Chances are, the tough problem you are trying to tackle has been solved by someone else working on Gamemode 4 long before you did.
This document will give you an overview of utilities and tools that have been developed over the years and which may help you when making a module.

## Table of contents
* [Common Tags](#common-tags)
* [Blocks Tags](#block-tags)
* [Entity Tags](#entity-tags)
* [Environment Checks]()
* [Base Checks]()
* [Other Notable Checks]()
* [Creating New Checks]()
* [Upgrade Paths]()

## Common Tags
Selecting blocks or entities with common properties is an error prone task, and subsequent updates often miss module-specific, hardcoded tags.
As such, `base` provides various tags that are maintained through updates and which should be prioritized over module-specific solutions.

### Block Tags
Gamemode 4's default block tags are located at `base/data/gm4/tags/block/`.

| Tag Name | Source | Description |
|---------------------|---------------------|------------------------------------------------------------------------------------------------------|
| #gm4:air | air.json | All air types. |
| #gm4:foliage | foliage.json | Naturally generating decoration on surfaces, which are easily broken, i.e. are washed away by water. |
| #gm4:full_collision | full_collision.json | All blocks that have a full-block collision box. |
| #gm4:no_collision | no_collision.json | All blocks without any collision, including air. |
| #gm4:replaceable | replaceable.json | Blocks that can be replaced by placing another block inside it, including air. |
| #gm4:water | water.json | Blocks that act as a water source. |
| #gm4:waterloggable | waterloggable.json | Blocks which can be water-logged. |

### Entity Tags
Gamemode 4's default entity tags are located at `base/data/gm4/tags/block/`.

| Tag Name | Source | Description |
|----------------------|----------------------|--------------------------------------------------------------------------------------------------------------------------------------|
| #gm4:boats | boats.json | All boat variations, including rafts. |
| #gm4:boss | boss.json | Bosses, namely the Ender Dragon and the Wither. |
| #gm4:chest_boats | chest_boats.json | All boat variations with a chest. |
| #gm4:hostile | hostile.json | Living entities that are hostile towards player by default. |
| #gm4:minecarts | minecarts.json | All minecart variations. |
| #gm4:neutral_hostile | neutral_hostile.json | Hostile living entities that may be, given the right conditions, neutral towards the player by default but turn hostile if provoked. |
| #gm4:neutral_passive | neutral_passive.json | Entities that are normally neutral, but turn hostile if provoked. |
| #gm4:neutral | neutral.json | Entities that may be neutral given the right conditions. |
| #gm4:non-living | non-living.json | Entities that are not considered living. |
| #gm4:passive | passive.json | Entities that are normally friendly and do not turn hostile, even if provoked. |

## Environment Checks
The environment a data pack is installed into may affect its performance.
Servers may have command blocks disabled, or mods may change the way the game reacts to changes made by commands.
Not all users are aware of this, which can lead to rather frustrating debugging experiences.
To counteract this, modules may include environment checks which warn the user of the data pack if certain conditions are not met.

Environment checks are included by specifying them by name (including namespace) inside a module's `beet.yaml`, e.g.
```yaml
id: gm4_double_doors
name: Double Doors
version: 1.2.X

data_pack:
load: .

require:
- bolt

pipeline:
- gm4_double_doors.generate
- gm4.plugins.extend.module

meta:
gm4:
versioning:
schedule_loops: []

# List of environment checks to include
echecks: [gm4:non_player_entity_has_score]
website:
description: Tired of clicking twice to open a double door? Annoyed by the fact that doors are only two blocks tall? This data pack automatically opens adjacent doors, making double doors fully functional! Additionally, bottom trapdoors of matching wood type placed above a door are opened alongside the door when it is opened by a player.
recommended: []
notes: []
modrinth:
project_id: Vx4zJ1Np
smithed:
pack_id: gm4_double_doors
video: null
wiki: https://wiki.gm4.co/wiki/Double_Doors
credits:
Creator:
- Bloo
Icon Design:
- venomousbirds
```

Multiple checks may be included, however, the order they will be executed in is arbitrary:
```yaml
echecks: [gm4:non_player_entity_has_score, gm4_double_doors:bloo_is_not_online, lib_forceload:command_blocks_enabled]
```

On reload, `base` obtains a list of environment checks requested by all installed modules and libraries. During `post_load` this list is then executed.
Announcements of the test results are only made if at least one test did not succeed and may not be instantaneous, as environment checks are allowed 30s of runtime.
If any check does not return after 30s it is marked as "timed-out" and will be announced as such.

For debug purposes, you may inspect the contents of storage `gm4:log echecks` to see the results of the latest environment checks. This storage is only cleared before new checks are executed.

### Base Environment Checks
`base` comes with some fundamental environment checks that can be referenced by the `gm4:` namespace.

| Check Name | Description |
|--------------------------------|--------------------------------------------------------------------------------------------------------------------------|
| gm4:non_player_entity_has_score | Checks if non-player entities can be added to a scoreboard. Fails if a test marker's score can not be set and read back. |

### Creating New Environment Checks
Modules may also introduce their own environment checks. Environment checks are defined as a single `.mcfunction` entry point, but may call other `.mcfunction` files, predicates, etc. from their entry point.
Caching of environment check results is provided automatically, you do not have to implement caching.

When introducing an environment check, the corresponding module must add the environment check's entry point to the `#gm4:evaluate_echecks` function tag.
Upon completion of the test (failure or success), the environment check needs to add a `result` object to its entry in the `gm4:log echeck` storage.

For example, the test with entry point located at `gm4:echeck/non_player_entity_has_score` may indicate a test failure as follows
```mcfunction
data modify storage gm4:log echecks[{echeck_id:"gm4:echeck/non_player_entity_has_score"}].result set value {passed:0,probable_cause:"This may be caused by the Paper/Spigot setting 'scoreboards.allow-non-player-entities-on-scoreboards=false'."}
```
Or a test success as
```mcfunction
data modify storage gm4:log echecks[{echeck_id:"gm4:echeck/non_player_entity_has_score"}].result set value {passed:1,probable_cause:""}
```

The `result.probable_cause` is shown to the user in chat in case the test fails.

For a textbook example of an environment check, inspect `gm4:non_player_entity_has_score` in `base`.
Loading
Loading