Skip to content

Commit f7d8ec0

Browse files
committed
feat: c:
1 parent 0902bed commit f7d8ec0

17 files changed

Lines changed: 1289 additions & 85 deletions

README.md

Lines changed: 5 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,12 @@
1-
# Allay Java Plugin Template
1+
# Imager
22

3-
Welcome to the java plugin template for allay.
3+
Show image in your Allay server!
44

5-
## Prerequisites
5+
![img.png](img.png)
66

7-
- Java21 or higher.
8-
- Allay installed.
7+
## Command
98

10-
## Getting Started
11-
12-
1. **Clone this Repository**
13-
14-
```bash
15-
git clone https://github.com/AllayMC/JavaPluginTemplate.git
16-
```
17-
18-
2. **Navigate to the Cloned Directory**
19-
20-
```bash
21-
cd JavaPluginTemplate
22-
```
23-
24-
3. **Change Plugin Information**
25-
26-
- Rename package name from `org.allaymc.javaplugintemplate` to `your.group.name.and.pluginname`
27-
- Update [build.gradle.kts](build.gradle.kts) and [settings.gradle.kts](settings.gradle.kts)
28-
- Reload gradle
29-
30-
4. **Build and Run Your Plugin**
31-
32-
```bash
33-
gradlew shadowJar
34-
```
35-
36-
This command will produce a `.jar` file in the `build/libs` directory.
37-
Copy the `.jar` file to the `plugins` directory of your allay server.
38-
Start the allay server and check the logs to ensure your plugin loads and operates
39-
as expected.
40-
41-
5. **Test Your Plugin in Gradle**
42-
43-
```bash
44-
gradlew runServer
45-
```
46-
47-
This command will start an allay server with your plugin loaded.
48-
Then close allay server by clicking `X` in the dashboard window.
49-
50-
## Documentation
51-
52-
For a deeper dive into the Allay API and its functionalities, please refer to our [documentation](https://docs.allaymc.org) (WIP).
9+
- `/imager`
5310

5411
## License
5512

build.gradle.kts

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ plugins {
33
id("org.allaymc.gradle.plugin") version "0.2.0"
44
}
55

6-
// TODO: Update the group to yours (should be same to the package of the plugin main class)
7-
group = "org.allaymc.javaplugintemplate"
8-
// TODO: Update the description to yours
9-
description = "Java plugin template for allay server"
6+
group = "me.daoge.imager"
7+
description = "Show image in your Allay server!"
108
version = "0.1.0"
119

1210
java {
@@ -15,24 +13,19 @@ java {
1513
}
1614
}
1715

18-
// See also https://github.com/AllayMC/AllayGradle
1916
allay {
20-
// TODO: Update the api version to the latest
21-
// You can find the latest version here: https://central.sonatype.com/artifact/org.allaymc.allay/api
2217
api = "0.17.0"
2318

2419
plugin {
25-
// TODO: Update the entrance when you change your plugin main class
26-
// Same to `org.allaymc.javaplugintemplate.JavaPluginTemplate`
27-
entrance = ".JavaPluginTemplate"
28-
// TODO: Use your handsome name here
29-
authors += "YourNameHere"
30-
// TODO: Update the website to yours
31-
website = "https://github.com/AllayMC/JavaPluginTemplate"
20+
entrance = ".Imager"
21+
authors += "daoge_cmd"
22+
website = "https://github.com/smartcmd/Imager"
3223
}
3324
}
3425

3526
dependencies {
3627
compileOnly(group = "org.projectlombok", name = "lombok", version = "1.18.34")
28+
implementation(group = "net.coobird", name = "thumbnailator", version = "0.4.21")
29+
implementation(group = "commons-io", name = "commons-io", version = "2.21.0")
3730
annotationProcessor(group = "org.projectlombok", name = "lombok", version = "1.18.34")
3831
}

img.png

1.37 MB
Loading

settings.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
rootProject.name = "JavaPluginTemplate"
1+
rootProject.name = "Imager"
22

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
package me.daoge.imager;
2+
3+
import lombok.Getter;
4+
import me.daoge.imager.imagemap.ImageMap;
5+
import me.daoge.imager.imagemap.SplitMode;
6+
import me.daoge.imager.imagemapstorage.LocalImageMapStorage;
7+
import me.daoge.imager.imageprovider.LocalImageProvider;
8+
import me.daoge.imager.manager.ImageMapManager;
9+
import me.daoge.imager.manager.SimpleImageMapManager;
10+
import org.allaymc.api.block.data.BlockFace;
11+
import org.allaymc.api.block.dto.Block;
12+
import org.allaymc.api.block.dto.PlayerInteractInfo;
13+
import org.allaymc.api.block.type.BlockTypes;
14+
import org.allaymc.api.entity.interfaces.EntityPlayer;
15+
import org.allaymc.api.eventbus.EventHandler;
16+
import org.allaymc.api.eventbus.event.block.BlockBreakEvent;
17+
import org.allaymc.api.eventbus.event.block.BlockNeighborUpdateEvent;
18+
import org.allaymc.api.eventbus.event.block.ItemFrameUseEvent;
19+
import org.allaymc.api.eventbus.event.player.PlayerInteractBlockEvent;
20+
import org.allaymc.api.eventbus.event.server.PlayerQuitEvent;
21+
import org.allaymc.api.form.Forms;
22+
import org.allaymc.api.form.element.ImageData;
23+
import org.allaymc.api.item.type.ItemType;
24+
import org.allaymc.api.item.type.ItemTypes;
25+
import org.allaymc.api.math.position.Position3i;
26+
import org.allaymc.api.math.position.Position3ic;
27+
import org.allaymc.api.pdc.PersistentDataType;
28+
import org.allaymc.api.plugin.Plugin;
29+
import org.allaymc.api.registry.Registries;
30+
import org.allaymc.api.server.Server;
31+
import org.allaymc.api.utils.identifier.Identifier;
32+
import org.apache.commons.io.FileUtils;
33+
34+
import java.io.IOException;
35+
import java.net.URL;
36+
import java.util.Arrays;
37+
import java.util.HashMap;
38+
import java.util.List;
39+
import java.util.Map;
40+
41+
public class Imager extends Plugin {
42+
43+
public static final Identifier KEY_IMAGE_NAME = new Identifier("imager:image_name");
44+
public static final ItemType<?> IMAGE_ITEM = ItemTypes.PAINTING;
45+
protected static final int INTERACT_COOL_DOWN = 5;
46+
47+
@Getter
48+
protected static Imager instance;
49+
protected Map<EntityPlayer, Long> interactCoolDown = new HashMap<>();
50+
protected Map<EntityPlayer, Position3ic> pos1 = new HashMap<>();
51+
protected Map<EntityPlayer, BlockFace> pos1BlockFace = new HashMap<>();
52+
@Getter
53+
protected ImageMapManager imageMapManager;
54+
55+
{
56+
instance = this;
57+
}
58+
59+
@Override
60+
public void onEnable() {
61+
var dataPath = this.pluginContainer.dataFolder();
62+
var provider = new LocalImageProvider(dataPath.resolve("images"));
63+
provider.reload();
64+
var storage = new LocalImageMapStorage(dataPath.resolve("data.json"));
65+
this.imageMapManager = new SimpleImageMapManager(storage, provider);
66+
Server.getInstance().getEventBus().registerListener(this);
67+
Registries.COMMANDS.register(new ImagerCommand());
68+
}
69+
70+
@EventHandler
71+
protected void onPlayerInteractBlock(PlayerInteractBlockEvent event) {
72+
var currentTick = Server.getInstance().getTick();
73+
var player = event.getPlayer();
74+
PlayerInteractInfo interactInfo = event.getInteractInfo();
75+
var clickedBlock = interactInfo.getClickedBlock();
76+
77+
if (isImageMapBlock(clickedBlock)) {
78+
event.setCancelled(true);
79+
if (interactCoolDown.get(player) != null && currentTick - interactCoolDown.get(player) <= INTERACT_COOL_DOWN) {
80+
return;
81+
}
82+
83+
interactCoolDown.put(player, currentTick);
84+
if (!player.isActualPlayer() || !player.hasPermission("imager.query_image_info").asBoolean()) {
85+
return;
86+
}
87+
88+
var imageMap = imageMapManager.getImageMapInPosition(clickedBlock.getPosition());
89+
var pos1 = imageMap.getPos1();
90+
var pos2 = imageMap.getPos2();
91+
String info = "§fImageName: §a" + imageMap.getImageName() + "\n" +
92+
"§fImageId: §a" + imageMap.getId() + "\n" +
93+
"§fSplitMode: §a" + imageMap.getMode() + "\n" +
94+
"§fWorldName: §a" + imageMap.getWorldName() + "\n" +
95+
"§fDimId: §a" + imageMap.getDimensionId() + "\n" +
96+
"§fPos1: §a" + pos1.x() + " " + pos1.y() + " " + pos1.z() + "\n" +
97+
"§fPos2: §a" + pos2.x() + " " + pos2.y() + " " + pos2.z();
98+
99+
Forms.simple()
100+
.title("Imager")
101+
.content(info)
102+
.button("Remove This Image", ImageData.ImageType.PATH, "textures/ui/crossout.png")
103+
.onClick(button -> {
104+
if (imageMapManager.removeImageMap(imageMap.getId())) {
105+
player.sendMessage("[Imager] §aImage removed");
106+
}
107+
})
108+
.sendTo(player.getController());
109+
110+
return;
111+
}
112+
113+
var item = player.getItemInHand();
114+
if (!player.isActualPlayer() || item.getItemType() != IMAGE_ITEM || !item.getPersistentDataContainer().has(KEY_IMAGE_NAME)) {
115+
return;
116+
}
117+
118+
event.setCancelled(true);
119+
if (interactCoolDown.get(player) != null && currentTick - interactCoolDown.get(player) <= INTERACT_COOL_DOWN) {
120+
return;
121+
}
122+
123+
interactCoolDown.put(player, currentTick);
124+
var interactPos = new Position3i(interactInfo.blockFace().offsetPos(interactInfo.clickedBlockPos()), player.getDimension());
125+
if (!pos1.containsKey(player)) {
126+
//第一个点
127+
pos1.put(player, interactPos);
128+
pos1BlockFace.put(player, interactInfo.blockFace());
129+
player.sendMessage("[Imager] §aPos1 set at: §f(" + interactPos.x + ", " + interactPos.y + ", " + interactPos.z + ")§a, please set pos2");
130+
} else {
131+
//开始生成图片
132+
var clickedPos1 = pos1.remove(player);
133+
var clickedPos1BlockFace = pos1BlockFace.remove(player);
134+
//检查是否在一个世界
135+
if (!clickedPos1.dimension().getWorld().getName().equals(interactPos.dimension().getWorld().getName()) ||
136+
clickedPos1.dimension().getDimensionInfo().dimensionId() != interactPos.dimension().getDimensionInfo().dimensionId()) {
137+
player.sendMessage("[Imager] §cTwo pos must be in the same level");
138+
return;
139+
}
140+
//检查是否为有效的两个点
141+
if (clickedPos1BlockFace != event.getInteractInfo().blockFace()) {
142+
player.sendMessage("[Imager] §Illegal positions, the two positions must have the same block face");
143+
return;
144+
}
145+
player.sendMessage("[Imager] §aPos2 set at: §f(" + interactPos.x + ", " + interactPos.y + ", " + interactPos.z + ")§a, spawning...");
146+
147+
//模式细节介绍
148+
var info = new StringBuilder();
149+
for (var mode : SplitMode.values()) {
150+
var details = mode.details;
151+
info.append("§a").append(mode.name()).append(" - §f").append(details).append("\n");
152+
}
153+
154+
Forms.custom()
155+
.title("Imager")
156+
.input("Image Identifier: ")
157+
.dropdown("Image Mode: ", Arrays.stream(SplitMode.values()).map(Enum::name).toList())
158+
.slider("Compressibility", 0, 100, 1, 100)
159+
.label(info.toString())
160+
.onResponse(list -> {
161+
var id = list.get(0);
162+
if (imageMapManager.containImageMap(id)) {
163+
player.sendMessage("[Imager] §cDuplicate image id");
164+
return;
165+
}
166+
167+
var mode = SplitMode.values()[Integer.parseInt(list.get(1))];
168+
var compressibility = Double.parseDouble(list.get(2));
169+
//开始生成图片信息
170+
var imageMap = ImageMap
171+
.builder()
172+
.pos1(clickedPos1)
173+
.pos2(interactPos)
174+
.worldName(clickedPos1.dimension().getWorld().getName())
175+
.dimensionId(clickedPos1.dimension().getDimensionInfo().dimensionId())
176+
.imageName(item.getPersistentDataContainer().get(KEY_IMAGE_NAME, PersistentDataType.STRING))
177+
.id(id)
178+
.mode(mode)
179+
.compressibility(compressibility)
180+
.build();
181+
182+
//通知管理器生成图片
183+
if (imageMapManager.createImageMap(imageMap, event.getInteractInfo().blockFace(), player.getHorizontalFace())) {
184+
player.sendMessage("[Imager] §aSucceed!");
185+
} else {
186+
player.sendMessage("[Imager] §cFailed!");
187+
}
188+
})
189+
.sendTo(player.getController());
190+
}
191+
}
192+
193+
public void giveImageMapItem(EntityPlayer player, String imageName) {
194+
var item = IMAGE_ITEM.createItemStack();
195+
item.setCustomName(imageName);
196+
item.setLore(List.of("ImageName: " + imageName));
197+
item.getPersistentDataContainer().set(KEY_IMAGE_NAME, PersistentDataType.STRING, imageName);
198+
player.tryAddItem(item);
199+
}
200+
201+
/**
202+
* 注意此方法非异步,会阻塞线程
203+
*/
204+
public void downloadImage(URL url, String saveName) throws IOException {
205+
FileUtils.copyURLToFile(url, this.pluginContainer.dataFolder().resolve("images").resolve(saveName).toFile());
206+
}
207+
208+
@EventHandler
209+
protected void onPlayerQuit(PlayerQuitEvent event) {
210+
var player = event.getPlayer().getControlledEntity();
211+
//清除缓存信息
212+
interactCoolDown.remove(player);
213+
pos1.remove(player);
214+
pos1BlockFace.remove(player);
215+
}
216+
217+
@EventHandler
218+
protected void onBlockUpdate(BlockNeighborUpdateEvent event) {
219+
event.setCancelled(isImageMapBlock(event.getBlock()));
220+
}
221+
222+
@EventHandler
223+
protected void onBlockBreak(BlockBreakEvent event) {
224+
event.setCancelled(isImageMapBlock(event.getBlock()));
225+
}
226+
227+
@EventHandler
228+
protected void onItemFrameUse(ItemFrameUseEvent event) {
229+
event.setCancelled(isImageMapBlock(event.getBlock()));
230+
}
231+
232+
protected boolean isImageMapBlock(Block block) {
233+
return block.getBlockType() == BlockTypes.FRAME && imageMapManager.containImageMapInPosition(block.getPosition());
234+
}
235+
}

0 commit comments

Comments
 (0)