Skip to content

Commit 34e6d56

Browse files
committed
Reporting of the previous reusable code
1 parent b223a98 commit 34e6d56

10 files changed

Lines changed: 713 additions & 3 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package me.tofaa.entitylib.spawn;
2+
3+
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
4+
import me.tofaa.entitylib.wrapper.WrapperEntity;
5+
import org.jetbrains.annotations.NotNull;
6+
7+
@FunctionalInterface
8+
public interface SpawnPacketProvider {
9+
10+
@NotNull PacketWrapper<?> provide(@NotNull WrapperEntity entity, int protocolVersion);
11+
12+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package me.tofaa.entitylib.spawn;
2+
3+
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
4+
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
5+
import com.github.retrooper.packetevents.protocol.world.Location;
6+
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
7+
import com.github.retrooper.packetevents.wrapper.play.server.*;
8+
import me.tofaa.entitylib.wrapper.WrapperEntity;
9+
import me.tofaa.entitylib.wrapper.WrapperLivingEntity;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.Optional;
15+
import java.util.concurrent.ConcurrentHashMap;
16+
17+
public final class SpawnPacketProviders {
18+
19+
public static final int MODERN_SPAWN_PROTOCOL = 761;
20+
21+
private static final Map<EntityType, SpawnPacketProvider> REGISTRY = new ConcurrentHashMap<>();
22+
23+
private static final SpawnPacketProvider EXPERIENCE_ORB = (entity, proto) -> {
24+
int exp = entity instanceof WrapperLivingEntity
25+
? ((WrapperLivingEntity) entity).getExperience()
26+
: 0;
27+
return new WrapperPlayServerSpawnExperienceOrb(
28+
entity.getEntityId(),
29+
entity.getLocation().getX(),
30+
entity.getLocation().getY(),
31+
entity.getLocation().getZ(),
32+
(short) exp
33+
);
34+
};
35+
36+
private static final SpawnPacketProvider PAINTING = (entity, proto) -> {
37+
Location loc = entity.getLocation();
38+
return new WrapperPlayServerSpawnPainting(
39+
entity.getEntityId(),
40+
entity.getUuid(),
41+
loc.getPosition().toVector3i(),
42+
entity.getDirection()
43+
);
44+
};
45+
46+
private static final SpawnPacketProvider WEATHER = (entity, proto) ->
47+
new WrapperPlayServerSpawnWeatherEntity(
48+
entity.getEntityId(),
49+
(byte) 0,
50+
entity.getLocation().getX(),
51+
entity.getLocation().getY(),
52+
entity.getLocation().getZ()
53+
);
54+
55+
private static final SpawnPacketProvider PLAYER = (entity, proto) ->
56+
new WrapperPlayServerSpawnPlayer(
57+
entity.getEntityId(),
58+
entity.getUuid(),
59+
entity.getLocation(),
60+
List.of()
61+
);
62+
63+
private static SpawnPacketProvider livingModernProvider(EntityType entityType) {
64+
return (entity, proto) -> {
65+
Location loc = entity.getLocation();
66+
return new WrapperPlayServerSpawnEntity(
67+
entity.getEntityId(),
68+
Optional.of(entity.getUuid()),
69+
entityType,
70+
loc.getPosition(),
71+
loc.getPitch(),
72+
loc.getYaw(),
73+
loc.getYaw(),
74+
entity.getSpawnData(),
75+
Optional.of(entity.getVelocity())
76+
);
77+
};
78+
}
79+
80+
private static SpawnPacketProvider livingLegacyProvider(EntityType entityType) {
81+
return (entity, proto) -> {
82+
Location loc = entity.getLocation();
83+
return new WrapperPlayServerSpawnLivingEntity(
84+
entity.getEntityId(),
85+
entity.getUuid(),
86+
entityType,
87+
loc.getPosition(),
88+
loc.getYaw(),
89+
loc.getPitch(),
90+
loc.getPitch(),
91+
entity.getVelocity(),
92+
List.of()
93+
);
94+
};
95+
}
96+
97+
private static SpawnPacketProvider objectModernProvider(EntityType entityType) {
98+
return (entity, proto) -> {
99+
Location loc = entity.getLocation();
100+
return new WrapperPlayServerSpawnEntity(
101+
entity.getEntityId(),
102+
Optional.of(entity.getUuid()),
103+
entityType,
104+
loc.getPosition(),
105+
loc.getPitch(),
106+
loc.getYaw(),
107+
loc.getYaw(),
108+
entity.getSpawnData(),
109+
Optional.of(entity.getVelocity())
110+
);
111+
};
112+
}
113+
114+
private static SpawnPacketProvider objectLegacyProvider(EntityType entityType) {
115+
return (entity, proto) -> new WrapperPlayServerSpawnEntity(
116+
entity.getEntityId(),
117+
entity.getUuid(),
118+
entityType,
119+
entity.getLocation(),
120+
entity.getLocation().getYaw(),
121+
entity.getSpawnData(),
122+
entity.getVelocity()
123+
);
124+
}
125+
126+
static {
127+
REGISTRY.put(EntityTypes.EXPERIENCE_ORB, EXPERIENCE_ORB);
128+
REGISTRY.put(EntityTypes.PAINTING, PAINTING);
129+
REGISTRY.put(EntityTypes.LIGHTNING_BOLT, WEATHER);
130+
}
131+
132+
private SpawnPacketProviders() {}
133+
134+
public static @NotNull SpawnPacketProvider forEntity(@NotNull EntityType entityType) {
135+
SpawnPacketProvider provider = REGISTRY.get(entityType);
136+
if (provider != null) return provider;
137+
return defaultFor(entityType);
138+
}
139+
140+
public static void register(@NotNull EntityType entityType, @NotNull SpawnPacketProvider provider) {
141+
REGISTRY.put(entityType, provider);
142+
}
143+
144+
public static void unregister(@NotNull EntityType entityType) {
145+
REGISTRY.remove(entityType);
146+
}
147+
148+
private static SpawnPacketProvider defaultFor(EntityType entityType) {
149+
boolean isLiving = entityType.isInstanceOf(EntityTypes.LIVINGENTITY);
150+
if (isLiving) {
151+
return (entity, proto) -> {
152+
if (proto >= MODERN_SPAWN_PROTOCOL) {
153+
return livingModernProvider(entityType).provide(entity, proto);
154+
}
155+
return livingLegacyProvider(entityType).provide(entity, proto);
156+
};
157+
}
158+
return (entity, proto) -> {
159+
if (proto >= MODERN_SPAWN_PROTOCOL) {
160+
return objectModernProvider(entityType).provide(entity, proto);
161+
}
162+
return objectLegacyProvider(entityType).provide(entity, proto);
163+
};
164+
}
165+
}
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package me.tofaa.entitylib.ve;
2+
3+
import com.github.retrooper.packetevents.event.PacketListenerAbstract;
4+
import com.github.retrooper.packetevents.event.PacketSendEvent;
5+
import com.github.retrooper.packetevents.protocol.packettype.PacketType;
6+
import com.github.retrooper.packetevents.protocol.packettype.PacketTypeCommon;
7+
import com.github.retrooper.packetevents.protocol.player.User;
8+
import com.github.retrooper.packetevents.wrapper.play.server.*;
9+
import me.tofaa.entitylib.wrapper.WrapperEntity;
10+
import org.jetbrains.annotations.ApiStatus;
11+
import org.jetbrains.annotations.NotNull;
12+
import org.jetbrains.annotations.Nullable;
13+
import org.jetbrains.annotations.UnmodifiableView;
14+
15+
import java.util.*;
16+
import java.util.concurrent.ConcurrentHashMap;
17+
import java.util.concurrent.CopyOnWriteArrayList;
18+
import java.util.concurrent.Executor;
19+
import java.util.concurrent.Executors;
20+
21+
@ApiStatus.Experimental
22+
public class ViewerEngine {
23+
24+
private final List<ViewerRule> globalRules = new CopyOnWriteArrayList<>();
25+
private final Map<Integer, WrapperEntity> tracked = new ConcurrentHashMap<>();
26+
private final PacketListener listener = new PacketListener();
27+
private Executor executor;
28+
private boolean enabled = false;
29+
30+
public ViewerEngine() {
31+
this(Executors.newSingleThreadExecutor());
32+
}
33+
34+
public ViewerEngine(@NotNull Executor executor) {
35+
this.executor = executor;
36+
}
37+
38+
public void enable() {
39+
if (enabled) return;
40+
enabled = true;
41+
}
42+
43+
public void disable() {
44+
if (!enabled) return;
45+
enabled = false;
46+
}
47+
48+
public void refresh() {
49+
for (WrapperEntity entity : tracked.values()) {
50+
for (UUID viewer : entity.getViewers()) {
51+
if (!canSpawnFor(viewer, entity)) {
52+
entity.removeViewer(viewer);
53+
}
54+
}
55+
}
56+
}
57+
58+
public @NotNull Executor getExecutor() {
59+
return executor;
60+
}
61+
62+
public void setExecutor(@NotNull Executor executor) {
63+
this.executor = executor;
64+
}
65+
66+
public void track(@NotNull WrapperEntity entity) {
67+
tracked.put(entity.getEntityId(), entity);
68+
}
69+
70+
public void untrack(@NotNull WrapperEntity entity) {
71+
tracked.remove(entity.getEntityId());
72+
}
73+
74+
public void clearTracked() {
75+
tracked.clear();
76+
}
77+
78+
public boolean canSpawnFor(@NotNull User user, @NotNull WrapperEntity entity) {
79+
return entity.getViewerRules().stream().allMatch(r -> r.shouldSee(user))
80+
&& globalRules.stream().allMatch(r -> r.shouldSee(user));
81+
}
82+
83+
public boolean canSpawnFor(@NotNull UUID userId, @NotNull WrapperEntity entity) {
84+
return entity.getViewerRules().stream().allMatch(r -> true)
85+
&& globalRules.stream().allMatch(r -> true);
86+
}
87+
88+
public @UnmodifiableView Collection<WrapperEntity> getTracked() {
89+
return Collections.unmodifiableCollection(tracked.values());
90+
}
91+
92+
Map<Integer, WrapperEntity> getTracked0() {
93+
return tracked;
94+
}
95+
96+
public void addViewerRule(@NotNull ViewerRule rule) {
97+
globalRules.add(rule);
98+
}
99+
100+
public void removeViewerRule(@NotNull ViewerRule rule) {
101+
globalRules.remove(rule);
102+
}
103+
104+
public void removeViewerRule(int index) {
105+
globalRules.remove(index);
106+
}
107+
108+
public void clearViewerRules() {
109+
globalRules.clear();
110+
}
111+
112+
public @UnmodifiableView Collection<ViewerRule> getViewerRules() {
113+
return Collections.unmodifiableCollection(globalRules);
114+
}
115+
116+
public @Nullable ViewerRule getViewerRule(int index) {
117+
if (index < 0 || index >= globalRules.size()) return null;
118+
return globalRules.get(index);
119+
}
120+
121+
private final class PacketListener extends PacketListenerAbstract {
122+
123+
@Override
124+
public void onPacketSend(PacketSendEvent event) {
125+
PacketTypeCommon type = event.getPacketType();
126+
if (type == PacketType.Play.Server.UNLOAD_CHUNK) {
127+
exec(event, e -> {
128+
var packet = new WrapperPlayServerUnloadChunk(e);
129+
int cx = packet.getChunkX();
130+
int cz = packet.getChunkZ();
131+
for (WrapperEntity entity : tracked.values()) {
132+
if (inChunk(entity.getLocation(), cx, cz)) {
133+
entity.removeViewer(e.getUser().getUUID());
134+
}
135+
}
136+
});
137+
} else if (type == PacketType.Play.Server.CHUNK_DATA) {
138+
exec(event, e -> {
139+
var packet = new WrapperPlayServerChunkData(e);
140+
int cx = packet.getColumn().getX();
141+
int cz = packet.getColumn().getZ();
142+
for (WrapperEntity entity : tracked.values()) {
143+
if (!inChunk(entity.getLocation(), cx, cz)) continue;
144+
if (entity.hasViewer(e.getUser().getUUID())) continue;
145+
entity.addViewer(e.getUser().getUUID());
146+
}
147+
});
148+
}
149+
}
150+
151+
private void exec(PacketSendEvent event, java.util.function.Consumer<PacketSendEvent> runnable) {
152+
PacketSendEvent copy = event.clone();
153+
executor.execute(() -> {
154+
runnable.accept(copy);
155+
copy.cleanUp();
156+
});
157+
}
158+
159+
private boolean inChunk(com.github.retrooper.packetevents.protocol.world.Location loc, int chunkX, int chunkZ) {
160+
return ((int) Math.floor(loc.getX())) >> 4 == chunkX
161+
&& ((int) Math.floor(loc.getZ())) >> 4 == chunkZ;
162+
}
163+
}
164+
165+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package me.tofaa.entitylib.ve;
2+
3+
import com.github.retrooper.packetevents.protocol.player.User;
4+
5+
@FunctionalInterface
6+
public interface ViewerRule {
7+
8+
boolean shouldSee(User user);
9+
10+
}

0 commit comments

Comments
 (0)