Skip to content

Commit 1f83eb6

Browse files
authored
Rendering system / Snapshots (#33)
* Add docs on new immutable snapshots * Resolve open topics on rendering
1 parent 82d29d1 commit 1f83eb6

File tree

6 files changed

+323
-0
lines changed

6 files changed

+323
-0
lines changed
84.2 KB
Loading
119 KB
Loading
106 KB
Loading
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
As of version 1.21.3 Minecraft has introduced an immutable rendering state,
2+
this means that rendering only happens based off of immutable objects instead of
3+
taking e.g. the position of a player during the rendering of a frame. This greatly
4+
improves thread safety, but one must know how to make proper use of it.
5+
6+
In this section we want to introduce you to how you can add your own custom data to
7+
the LabyMod state of an Entity which you can then use during rendering.
8+
9+
## Snapshot extras
10+
11+
A LabyMod Snapshot consists of a Map with data called Extras. After its creation it
12+
cannot be modified anymore, it is **immutable**.
13+
14+
Typically, an addon that uses the snapshot feature contains two types in its api module:
15+
16+
=== ":octicons-file-code-16: ExampleExtraKeys"
17+
```java
18+
package org.example.myaddon.api.snapshot;
19+
20+
import net.labymod.api.laby3d.renderer.snapshot.ExtraKey;
21+
22+
public class ExampleExtraKeys {
23+
24+
// The key must be something unique within your addon; it will automatically include your addon's namespace
25+
public static final ExtraKey<ExampleUserSnapshot> EXAMPLE_USER = ExtraKey.of("example_user", ExampleUserSnapshot.class);
26+
27+
// ... add more keys as required
28+
}
29+
```
30+
31+
=== ":octicons-file-code-16: ExampleUserSnapshot"
32+
```java
33+
package org.example.myaddon.api.snapshot;
34+
35+
import net.labymod.api.client.component.Component;
36+
import net.labymod.api.laby3d.renderer.snapshot.LabySnapshot;
37+
38+
public interface ExampleUserSnapshot extends LabySnapshot {
39+
40+
Component tag(); // Example
41+
42+
// ... put in whatever custom data you want
43+
}
44+
```
45+
46+
## Snapshot implementation
47+
48+
A typical implementation looks like this, but of course it may also be implemented for
49+
any other Entity type other than Player.
50+
51+
By using `@AutoService` we don't need to manually register the these classes anywhere,
52+
the Annotation Processor does the work for us.
53+
54+
=== ":octicons-file-code-16: ExampleUserSnapshotFactory"
55+
```java
56+
package org.example.myaddon.snapshot;
57+
58+
import org.example.myaddon.api.snapshot.ExampleExtraKeys;
59+
import org.example.myaddon.api.snapshot.ExampleUserSnapshot;
60+
import net.labymod.api.client.entity.player.Player;
61+
import net.labymod.api.laby3d.renderer.snapshot.Extras;
62+
import net.labymod.api.laby3d.renderer.snapshot.LabySnapshotFactory;
63+
import net.labymod.api.service.annotation.AutoService;
64+
65+
@AutoService(LabySnapshotFactory.class)
66+
public class ExampleUserSnapshotFactory extends LabySnapshotFactory<Player, ExampleUserSnapshot> {
67+
68+
public ExampleUserSnapshotFactory() {
69+
super(ExampleExtraKeys.EXAMPLE_USER);
70+
}
71+
72+
@Override
73+
protected ExampleUserSnapshot create(Player player, Extras extras) {
74+
return new DefaultExampleUserSnapshot(player, extras);
75+
}
76+
}
77+
```
78+
79+
=== ":octicons-file-code-16: DefaultExampleUserSnapshot"
80+
```java
81+
package org.example.myaddon.snapshot;
82+
83+
import org.example.myaddon.api.snapshot.ExampleUserSnapshot;
84+
import net.labymod.api.client.component.Component;
85+
import net.labymod.api.client.entity.player.Player;
86+
import net.labymod.api.laby3d.renderer.snapshot.AbstractLabySnapshot;
87+
import net.labymod.api.laby3d.renderer.snapshot.Extras;
88+
import java.util.UUID;
89+
90+
public class DefaultExampleUserSnapshot extends AbstractLabySnapshot implements ExampleUserSnapshot {
91+
92+
private final Component tag;
93+
94+
public DefaultExampleUserSnapshot(Player player, Extras extras) {
95+
super(extras);
96+
97+
// You can get any data from the Entity you're getting from the Factory:
98+
UUID uniqueId = player.getUniqueId();
99+
100+
// ... Get any other data for the player as required (e.g. custom roles, tags, badges, ...)
101+
102+
// Example:
103+
this.tag = Component.text("My Custom Tag for ").append(player.nameComponent());
104+
}
105+
106+
@Override
107+
public Component tag() {
108+
return this.tag;
109+
}
110+
}
111+
```
112+
113+
=== ":octicons-file-code-16: PlayerSnapshotProcessor"
114+
```java
115+
package org.example.myaddon.snapshot;
116+
117+
import org.example.myaddon.api.snapshot.ExampleExtraKeys;
118+
import net.labymod.api.client.entity.Entity;
119+
import net.labymod.api.client.entity.player.Player;
120+
import net.labymod.api.client.render.state.entity.EntitySnapshotProcessor;
121+
import net.labymod.api.client.render.state.entity.EntitySnapshotRegistry;
122+
import net.labymod.api.laby3d.renderer.snapshot.ExtrasWriter;
123+
import net.labymod.api.service.annotation.AutoService;
124+
125+
@AutoService(EntitySnapshotProcessor.class)
126+
public class PlayerSnapshotProcessor extends EntitySnapshotProcessor<Player> {
127+
128+
public PlayerSnapshotProcessor(EntitySnapshotRegistry registry) {
129+
super(registry);
130+
}
131+
132+
@Override
133+
public boolean supports(Entity entity) {
134+
return entity instanceof Player;
135+
}
136+
137+
@Override
138+
public void process(Player player, float partialTicks, ExtrasWriter entityWriter) {
139+
this.registry().captureSnapshot(entityWriter, ExampleExtraKeys.EXAMPLE_USER, player);
140+
// ... you can also add more extras here as required
141+
}
142+
}
143+
```
144+
145+
## Use-cases
146+
147+
For an easy use-case check out the next section: [Entity Tags](entity-tags.md)
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
Just like integrated tags like the "LABY DEV/..." team tags addons can also add custom tags
2+
that seamlessly integrate with any other tags:
3+
4+
![Tags](../../../assets/files/screenshots/entity-tags-example.png)
5+
6+
## Snapshot extras
7+
8+
This is what our Snapshot extra will look like for this example. For simplicity the implementation
9+
details for the Snapshot extra will be skipped in this example, please make sure to read
10+
the previous page [Entity Snapshots](entity-snapshots.md) beforehand.
11+
12+
=== ":octicons-file-code-16: ExampleTagExtraKeys"
13+
```java
14+
package org.example.myaddon.api.snapshot;
15+
16+
import net.labymod.api.laby3d.renderer.snapshot.ExtraKey;
17+
18+
public class ExampleTagExtraKeys {
19+
20+
// The key must be something unique within your addon; it will automatically include your addon's namespace
21+
public static final ExtraKey<ExampleTagUserSnapshot> EXAMPLE_USER_TAG = ExtraKey.of("example_user_tag", ExampleTagUserSnapshot.class);
22+
23+
// ... add more keys as required
24+
}
25+
```
26+
27+
=== ":octicons-file-code-16: ExampleTagUserSnapshot"
28+
```java
29+
package org.example.myaddon.api.snapshot;
30+
31+
import net.labymod.api.client.component.Component;
32+
import net.labymod.api.laby3d.renderer.snapshot.LabySnapshot;
33+
34+
public interface ExampleTagUserSnapshot extends LabySnapshot {
35+
36+
int reports();
37+
38+
// ... put in whatever custom data you want
39+
}
40+
```
41+
42+
## Component NameTags
43+
44+
ComponentNameTags can be used to display a text component around the player name like so:
45+
46+
![Component Tag](../../../assets/files/screenshots/entity-tags-example-component.png)
47+
48+
=== ":octicons-file-code-16: ExampleAddon"
49+
```java
50+
package org.example.myaddon;
51+
52+
@AddonMain
53+
public class ExampleAddon extends LabyAddon<ExampleConfiguration> {
54+
55+
@Override
56+
protected void enable() {
57+
Laby.references().tagRegistry().register(
58+
"my_custom_component",
59+
PositionType.ABOVE_NAME,
60+
new MyCustomNameTag()
61+
);
62+
}
63+
}
64+
```
65+
66+
=== ":octicons-file-code-16: MyCustomNameTag"
67+
```java
68+
package org.example.myaddon.tag;
69+
70+
import org.example.myaddon.api.snapshot.ExampleTagExtraKeys;
71+
import org.example.myaddon.api.snapshot.ExampleUserSnapshot;
72+
import java.util.List;
73+
import net.labymod.api.client.component.Component;
74+
import net.labymod.api.client.entity.player.tag.tags.ComponentNameTag;
75+
import net.labymod.api.client.render.state.entity.EntitySnapshot;
76+
import org.jetbrains.annotations.NotNull;
77+
78+
public class MyCustomNameTag extends ComponentNameTag {
79+
80+
@Override
81+
protected @NotNull List<Component> buildComponents(EntitySnapshot snapshot) {
82+
if (!snapshot.has(ExampleTagExtraKeys.EXAMPLE_USER_TAG)) {
83+
return super.buildComponents(snapshot);
84+
}
85+
86+
ExampleTagUserSnapshot userSnapshot = snapshot.get(ExampleTagExtraKeys.EXAMPLE_USER_TAG);
87+
int reports = userSnapshot.reports();
88+
89+
if (reports > 0) {
90+
return List.of(Component.text("Reports: " + reports));
91+
}
92+
93+
return List.of();
94+
}
95+
96+
// You can override this method to include your own custom logic, but make sure
97+
// to include the call to super.isVisible();
98+
// The NameTag is always hidden as long as buildComponents() returns an empty list
99+
// @Override
100+
// public boolean isVisible() {
101+
// return super.isVisible() && <implement custom visibility logic>;
102+
// }
103+
104+
// You can override this method to change the scale of your NameTag, default is 1.0
105+
// @Override
106+
// public float getScale() {
107+
// return 0.5F;
108+
// }
109+
}
110+
```
111+
112+
## Icon Tags
113+
114+
IconTags can be used to display any icon around the player name like so:
115+
116+
![Icon Tag](../../../assets/files/screenshots/entity-tags-example-icon.png)
117+
118+
=== ":octicons-file-code-16: ExampleAddon"
119+
```java
120+
package org.example.myaddon;
121+
122+
@AddonMain
123+
public class ExampleAddon extends LabyAddon<ExampleConfiguration> {
124+
125+
@Override
126+
protected void enable() {
127+
Laby.references().tagRegistry().register(
128+
"my_custom_icon",
129+
PositionType.ABOVE_NAME,
130+
new MyCustomIconTag()
131+
);
132+
}
133+
}
134+
```
135+
136+
=== ":octicons-file-code-16: MyCustomIconTag"
137+
```java
138+
package org.example.myaddon.tag;
139+
140+
import org.example.myaddon.api.snapshot.ExampleTagExtraKeys;
141+
import net.labymod.api.client.entity.player.tag.tags.IconTag;
142+
import net.labymod.api.client.gui.icon.Icon;
143+
import net.labymod.api.client.render.state.entity.EntitySnapshot;
144+
145+
public class MyCustomIconTag extends IconTag {
146+
147+
public MyCustomIconTag() {
148+
super(36.5F, 8F); // width, height
149+
// super(8F); // or just size (if width == height)
150+
}
151+
152+
// You can override this method to include your own custom logic, but make sure
153+
// to include the call to super.isVisible();
154+
// The IconTag is always hidden as long as getIcon() returns null
155+
// @Override
156+
// public boolean isVisible() {
157+
// if (!this.snapshot.has(ExampleTagExtraKeys.EXAMPLE_USER_TAG)) {
158+
// return false;
159+
// }
160+
//
161+
// return super.isVisible() && this.snapshot.get(ExampleTagExtraKeys.EXAMPLE_USER_TAG).reports() > 0;
162+
// }
163+
164+
@Override
165+
public Icon getIcon(EntitySnapshot snapshot) {
166+
// Return any Icon that you want to be rendered,
167+
// or null to hide the Tag for this Entity.
168+
// If the Icon is static and the same for each Entity you may
169+
// also pass it in the constructor and don't implement getIcon().
170+
return Icon.url("https://labymod.net/page/tpl/assets/images/logo.png");
171+
}
172+
}
173+
```

mkdocs.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ nav:
6666
- Understand LSS: pages/addon/activities/lss.md
6767
- Create Custom Widgets: pages/addon/activities/custom-widgets.md
6868
- Theming: pages/addon/activities/themes.md
69+
- Rendering System:
70+
- Entity Snapshots: pages/addon/rendering/entity-snapshots.md
71+
- Entity Tags: pages/addon/rendering/entity-tags.md
6972
- Publishing:
7073
- Guidelines: pages/addon/publishing/guidelines.md
7174
- Publish Your Addon: pages/addon/publishing/publish.md

0 commit comments

Comments
 (0)