Skip to content

Commit 494e18b

Browse files
authored
Merge pull request #94 from josemmo/develop
v1.2.10
2 parents a786471 + 8eed5bd commit 494e18b

File tree

10 files changed

+194
-77
lines changed

10 files changed

+194
-77
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ jobs:
8181
VERSION: ${{ matrix.version }}
8282
run: |
8383
if [ $VERSION == "1.19.4" ]; then
84-
url="https://ci.dmulloy2.net/job/ProtocolLib/lastStableBuild/artifact/target/ProtocolLib.jar"
84+
url="https://ci.dmulloy2.net/job/ProtocolLib/lastSuccessfulBuild/artifact/build/libs/ProtocolLib.jar"
8585
else
8686
url="https://github.com/dmulloy2/ProtocolLib/releases/download/4.8.0/ProtocolLib.jar"
8787
fi

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![bStats Servers](https://img.shields.io/bstats/servers/10243)](https://bstats.org/plugin/bukkit/Yamipa/10243)
66
[![License](https://img.shields.io/github/license/josemmo/yamipa)](LICENSE)
77

8-
Yamipa is an Spigot plugin that allows players to place images (even **animated**!) on any surface in your Minecraft server
8+
Yamipa is a Spigot plugin that allows players to place images (even **animated**!) on any surface in your Minecraft server
99
without having to install any local client mod.
1010

1111
It is designed with performance and compatibility in mind, so even the most low-specs servers should be able to run it.
@@ -24,8 +24,9 @@ Download the JAR file for the [latest release](https://github.com/josemmo/yamipa
2424
### Requirements
2525
Before installing Yamipa make sure you meet the following requirements:
2626

27-
- CraftBukkit, Spigot or PaperMC v1.16 or higher
28-
- [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) v4.6.1 or higher
27+
- CraftBukkit, Spigot or PaperMC 1.16 or higher
28+
- [ProtocolLib](https://www.spigotmc.org/resources/protocollib.1997/) v4.8.0 or higher
29+
(latest [dev build](https://ci.dmulloy2.net/job/ProtocolLib/lastSuccessfulBuild/) for 1.19.4)
2930

3031
Here are the Minecraft distributions where Yamipa should be able to run:
3132
| Minecraft version | CraftBukkit | Spigot | PaperMC |
@@ -132,6 +133,7 @@ The supported plugins are:
132133

133134
- [WorldGuard](https://enginehub.org/worldguard/)
134135
- [GriefPrevention](https://www.spigotmc.org/resources/griefprevention.1884/)
136+
- [Towny Advanced](https://townyadvanced.github.io/)
135137

136138
## Flags
137139
Images from this plugin have a set of boolean attributes called "flags" that modify its behavior. Possible values are:

pom.xml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>io.josemmo.bukkit.plugin</groupId>
88
<artifactId>YamipaPlugin</artifactId>
9-
<version>1.2.9</version>
9+
<version>1.2.10</version>
1010

1111
<properties>
1212
<maven.compiler.source>8</maven.compiler.source>
@@ -59,7 +59,7 @@
5959
<dependency>
6060
<groupId>org.bstats</groupId>
6161
<artifactId>bstats-bukkit</artifactId>
62-
<version>3.0.1</version>
62+
<version>3.0.2</version>
6363
</dependency>
6464
<dependency>
6565
<groupId>com.sk89q.worldguard</groupId>
@@ -73,6 +73,12 @@
7373
<version>16.18.1</version>
7474
<scope>provided</scope>
7575
</dependency>
76+
<dependency>
77+
<groupId>com.github.TownyAdvanced</groupId>
78+
<artifactId>towny</artifactId>
79+
<version>0.98.6.25</version>
80+
<scope>provided</scope>
81+
</dependency>
7682
<dependency>
7783
<groupId>org.jetbrains</groupId>
7884
<artifactId>annotations</artifactId>

src/main/java/io/josemmo/bukkit/plugin/commands/ImageCommand.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ public static boolean placeImage(
212212

213213
// Make sure image can be placed
214214
for (Location loc : fakeImage.getAllLocations()) {
215-
if (!Permissions.canEditBlock(player, loc)) {
215+
if (!Permissions.canBuild(player, loc)) {
216216
ActionBar.send(player, ChatColor.RED + "You're not allowed to place an image here!");
217217
return false;
218218
}
@@ -260,7 +260,7 @@ public static void removeImage(@NotNull Player player) {
260260
public static boolean removeImage(@NotNull Player player, @NotNull FakeImage image) {
261261
// Check block permissions
262262
for (Location loc : image.getAllLocations()) {
263-
if (!Permissions.canEditBlock(player, loc)) {
263+
if (!Permissions.canDestroy(player, loc)) {
264264
ActionBar.send(player, ChatColor.RED + "You're not allowed to remove this image!");
265265
return false;
266266
}
@@ -299,7 +299,7 @@ public static void clearImages(
299299
Player senderAsPlayer = (Player) sender;
300300
images.removeIf(image -> {
301301
for (Location loc : image.getAllLocations()) {
302-
if (!Permissions.canEditBlock(senderAsPlayer, loc)) {
302+
if (!Permissions.canDestroy(senderAsPlayer, loc)) {
303303
return true;
304304
}
305305
}

src/main/java/io/josemmo/bukkit/plugin/renderer/FakeEntity.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package io.josemmo.bukkit.plugin.renderer;
22

3+
import com.comphenix.protocol.PacketType;
34
import com.comphenix.protocol.ProtocolLibrary;
45
import com.comphenix.protocol.ProtocolManager;
56
import com.comphenix.protocol.events.PacketContainer;
67
import com.comphenix.protocol.injector.player.PlayerInjectionHandler;
78
import com.comphenix.protocol.wrappers.WrappedDataWatcher;
89
import io.josemmo.bukkit.plugin.YamipaPlugin;
10+
import io.josemmo.bukkit.plugin.utils.Internals;
911
import org.bukkit.entity.Player;
1012
import org.jetbrains.annotations.NotNull;
1113
import java.lang.reflect.Field;
@@ -94,6 +96,23 @@ protected static void tryToSendPacket(@NotNull Player player, @NotNull PacketCon
9496
}
9597
}
9698

99+
/**
100+
* Try to send several packets
101+
* @param player Player who will receive the packets
102+
* @param packets Packets to send
103+
*/
104+
protected static void tryToSendPackets(@NotNull Player player, @NotNull Iterable<PacketContainer> packets) {
105+
if (Internals.MINECRAFT_VERSION < 19.4f) {
106+
for (PacketContainer packet : packets) {
107+
tryToSendPacket(player, packet);
108+
}
109+
} else {
110+
PacketContainer container = new PacketContainer(PacketType.Play.Server.BUNDLE);
111+
container.getPacketBundles().write(0, packets);
112+
tryToSendPacket(player, container);
113+
}
114+
}
115+
97116
/**
98117
* Try to run asynchronous task
99118
* @param callback Callback to execute

src/main/java/io/josemmo/bukkit/plugin/renderer/FakeImage.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.josemmo.bukkit.plugin.renderer;
22

3+
import com.comphenix.protocol.events.PacketContainer;
34
import io.josemmo.bukkit.plugin.storage.ImageFile;
45
import io.josemmo.bukkit.plugin.utils.DirectionUtils;
56
import org.bukkit.Location;
@@ -12,6 +13,7 @@
1213
import org.jetbrains.annotations.Nullable;
1314
import java.awt.*;
1415
import java.util.*;
16+
import java.util.List;
1517
import java.util.concurrent.ScheduledFuture;
1618
import java.util.concurrent.TimeUnit;
1719
import java.util.function.BiFunction;
@@ -429,11 +431,19 @@ public void spawn(@NotNull Player player) {
429431
* @param player Player instance
430432
*/
431433
private void spawnOnceLoaded(@NotNull Player player) {
434+
String playerName = player.getName();
432435
observingPlayers.add(player);
436+
437+
// Prepare packets to send
438+
List<PacketContainer> packets = new ArrayList<>();
433439
for (FakeItemFrame frame : frames) {
434-
frame.spawn(player);
435-
frame.render(player, 0);
440+
packets.add(frame.getSpawnPacket());
441+
packets.addAll(frame.getRenderPackets(player, 0));
442+
plugin.fine("Spawned FakeItemFrame#" + frame.getId() + " for Player#" + playerName);
436443
}
444+
445+
// Send packets
446+
tryToSendPackets(player, packets);
437447
}
438448

439449
/**
@@ -459,9 +469,13 @@ public void destroy(@Nullable Player player) {
459469
if (frames != null) {
460470
Set<Player> targets = (player == null) ? observingPlayers : Collections.singleton(player);
461471
for (Player target : targets) {
472+
String targetName = target.getName();
473+
List<PacketContainer> packets = new ArrayList<>();
462474
for (FakeItemFrame frame : frames) {
463-
frame.destroy(target);
475+
packets.add(frame.getDestroyPacket());
476+
plugin.fine("Destroyed FakeItemFrame#" + frame.getId() + " for Player#" + targetName);
464477
}
478+
tryToSendPackets(target, packets);
465479
}
466480
}
467481

@@ -521,9 +535,11 @@ private void nextStep() {
521535
currentStep = (currentStep + 1) % numOfSteps;
522536
try {
523537
for (Player player : observingPlayers) {
538+
List<PacketContainer> packets = new ArrayList<>();
524539
for (FakeItemFrame frame : frames) {
525-
frame.render(player, currentStep);
540+
packets.addAll(frame.getRenderPackets(player, currentStep));
526541
}
542+
tryToSendPackets(player, packets);
527543
}
528544
} catch (ConcurrentModificationException e) {
529545
// We can safely ignore this exception as it will just result

src/main/java/io/josemmo/bukkit/plugin/renderer/FakeItemFrame.java

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.josemmo.bukkit.plugin.renderer;
22

3+
import com.comphenix.protocol.events.PacketContainer;
34
import com.comphenix.protocol.utility.MinecraftReflection;
45
import com.comphenix.protocol.wrappers.nbt.NbtCompound;
56
import com.comphenix.protocol.wrappers.nbt.NbtFactory;
@@ -15,6 +16,8 @@
1516
import org.bukkit.entity.Player;
1617
import org.bukkit.inventory.ItemStack;
1718
import org.jetbrains.annotations.NotNull;
19+
import java.util.ArrayList;
20+
import java.util.List;
1821
import java.util.concurrent.atomic.AtomicInteger;
1922

2023
public class FakeItemFrame extends FakeEntity {
@@ -67,10 +70,18 @@ public FakeItemFrame(
6770
}
6871

6972
/**
70-
* Spawn empty item frame in player's client
71-
* @param player Player instance
73+
* Get frame ID
74+
* @return Frame ID
7275
*/
73-
public void spawn(@NotNull Player player) {
76+
public int getId() {
77+
return id;
78+
}
79+
80+
/**
81+
* Get entity spawn packet
82+
* @return Spawn packet
83+
*/
84+
public @NotNull SpawnEntityPacket getSpawnPacket() {
7485
// Calculate frame position in relation to target block
7586
double x = location.getBlockX();
7687
double y = location.getBlockY();
@@ -115,18 +126,23 @@ public void spawn(@NotNull Player player) {
115126
.setPosition(x, y, z)
116127
.setRotation(pitch, yaw)
117128
.setData(orientation);
118-
tryToSendPacket(player, framePacket);
119-
plugin.fine("Spawned FakeItemFrame#" + this.id + " for Player#" + player.getName());
129+
130+
return framePacket;
120131
}
121132

122133
/**
123-
* Send frame of animation to player
124-
* @param player Player instance
125-
* @param step Map step to send
134+
* Get frame of animation packets
135+
* @param player Player who is expected to receive packets (for caching reasons)
136+
* @param step Map step
126137
*/
127-
public void render(@NotNull Player player, int step) {
128-
// Send map pixels
129-
maps[step].sendPixels(player);
138+
public @NotNull List<PacketContainer> getRenderPackets(@NotNull Player player, int step) {
139+
List<PacketContainer> packets = new ArrayList<>(2);
140+
141+
// Enqueue map pixels packet (if needed)
142+
boolean mustSendPixels = maps[step].requestResend(player);
143+
if (mustSendPixels) {
144+
packets.add(maps[step].getPixelsPacket());
145+
}
130146

131147
// Create and attach filled map
132148
ItemStack itemStack = MinecraftReflection.getBukkitItemStack(new ItemStack(Material.FILLED_MAP));
@@ -135,25 +151,24 @@ public void render(@NotNull Player player, int step) {
135151
NbtFactory.setItemTag(itemStack, itemStackNbt);
136152

137153
// Build entity metadata packet
138-
EntityMetadataPacket mapPacket = new EntityMetadataPacket();
139-
mapPacket.setId(id)
154+
EntityMetadataPacket metadataPacket = new EntityMetadataPacket();
155+
metadataPacket.setId(id)
140156
.setInvisible(true)
141157
.setItem(itemStack)
142158
.setRotation(rotation)
143159
.build();
160+
packets.add(metadataPacket);
144161

145-
// Send animation status update
146-
tryToSendPacket(player, mapPacket);
162+
return packets;
147163
}
148164

149165
/**
150-
* Destroy item frame from player's client
151-
* @param player Player instance
166+
* Get destroy item frame packet
167+
* @return Destroy packet
152168
*/
153-
public void destroy(@NotNull Player player) {
169+
public @NotNull DestroyEntityPacket getDestroyPacket() {
154170
DestroyEntityPacket destroyPacket = new DestroyEntityPacket();
155171
destroyPacket.setId(id);
156-
tryToSendPacket(player, destroyPacket);
157-
plugin.fine("Destroyed FakeItemFrame#" + this.id + " for Player#" + player.getName());
172+
return destroyPacket;
158173
}
159174
}

src/main/java/io/josemmo/bukkit/plugin/renderer/FakeMap.java

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,30 +120,37 @@ public byte[] getPixels() {
120120
}
121121

122122
/**
123-
* Send map pixels to player
124-
* @param player Player instance
123+
* Request re-send of map pixels
124+
* @param player Player who is expected to receive pixels
125+
* @return Whether re-send authorization was granted or not
125126
*/
126-
public void sendPixels(@NotNull Player player) {
127+
public boolean requestResend(@NotNull Player player) {
127128
UUID uuid = player.getUniqueId();
128129
long now = Instant.now().getEpochSecond();
129130

130-
// Avoid re-sending pixels too frequently
131+
// Has enough time passed since last re-send?
131132
long last = lastPlayerSendTime.getOrDefault(uuid, 0L);
132133
if ((now-last) <= RESEND_THRESHOLD && (player.getLastPlayed()/1000) < last) {
133-
return;
134+
return false;
134135
}
135136

136-
// Create map data packet
137+
// Authorize re-send and update latest timestamp
138+
lastPlayerSendTime.put(uuid, now);
139+
plugin.fine("Granted sending pixels for FakeMap#" + id + " to Player#" + player.getName());
140+
return true;
141+
}
142+
143+
/**
144+
* Get map pixels packet
145+
* @return Map pixels packet
146+
*/
147+
public @NotNull MapDataPacket getPixelsPacket() {
137148
MapDataPacket mapDataPacket = new MapDataPacket();
138149
mapDataPacket.setId(id)
139150
.setScale(0) // Fully zoomed-in
140151
.setLocked(true)
141152
.setArea(DIMENSION, DIMENSION, 0, 0)
142153
.setPixels(pixels);
143-
144-
// Send packet
145-
tryToSendPacket(player, mapDataPacket);
146-
lastPlayerSendTime.put(uuid, now);
147-
plugin.fine("Sent pixels for FakeMap#" + id + " to Player#" + player.getName());
154+
return mapDataPacket;
148155
}
149156
}

0 commit comments

Comments
 (0)