Skip to content

Commit 3235f43

Browse files
committed
Updates
1 parent 7b6379b commit 3235f43

6 files changed

Lines changed: 230 additions & 60 deletions

File tree

src/main/java/de/kcodeyt/headsdb/HeadsDB.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,21 @@
88
@Getter
99
public class HeadsDB extends PluginBase {
1010

11+
public static final String MHF_QUESTION_TEXTURE_ID = "d34e063cafb467a5c8de43ec78619399f369f4a52434da8017a983cdd92516a0";
12+
1113
private final Database database = new Database();
1214

1315
@Override
1416
public void onEnable() {
1517
this.getServer().getCommandMap().register("headsdb", new HeadDBCommand(this));
16-
if(!this.database.load()) {
17-
this.getLogger().error("Could not load heads database!");
18-
this.getServer().getPluginManager().disablePlugin(this);
19-
}
18+
this.database.load().whenComplete((success, error) -> {
19+
if(!success || error != null) {
20+
this.getLogger().error("Could not load heads database!", error);
21+
this.getServer().getPluginManager().disablePlugin(this);
22+
} else {
23+
this.getLogger().info("Successfully load " + this.database.getHeadEntries().size() + " Heads!");
24+
}
25+
});
2026
}
2127

2228
@Override

src/main/java/de/kcodeyt/headsdb/command/HeadDBCommand.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,16 @@ public boolean execute(CommandSender sender, String commandLabel, String[] args)
3030
switch(subCommand) {
3131
case "reload":
3232
if(sender.isOp()) {
33-
if(this.headsDB.getDatabase().reload())
34-
sender.sendMessage("§aReload the heads database successfully!");
35-
else
36-
sender.sendMessage("§cCould not reload the database!");
33+
sender.sendMessage("§7Reload the heads database...");
34+
this.headsDB.getDatabase().reload().whenComplete((result, error) -> {
35+
if(!result || error != null) {
36+
this.headsDB.getLogger().error("Could not load heads database!", error);
37+
sender.sendMessage("§cCould not reload the database!");
38+
} else {
39+
sender.sendMessage("§aSuccessfully reload the head database!");
40+
this.headsDB.getLogger().info("Successfully load " + this.headsDB.getDatabase().getHeadEntries().size() + " Heads!");
41+
}
42+
});
3743
return true;
3844
}
3945
break;

src/main/java/de/kcodeyt/headsdb/database/Database.java

Lines changed: 184 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,30 @@
11
package de.kcodeyt.headsdb.database;
22

33
import cn.nukkit.Player;
4-
import cn.nukkit.Server;
54
import cn.nukkit.form.element.ElementButton;
5+
import cn.nukkit.form.element.ElementInput;
6+
import cn.nukkit.form.window.FormWindow;
7+
import cn.nukkit.form.window.FormWindowCustom;
68
import cn.nukkit.form.window.FormWindowSimple;
79
import cn.nukkit.item.Item;
10+
import cn.nukkit.utils.TextFormat;
811
import com.google.common.collect.Iterables;
912
import com.google.gson.Gson;
1013
import de.kcodeyt.heads.Heads;
1114
import de.kcodeyt.heads.util.HeadInput;
15+
import de.kcodeyt.heads.util.ScheduledFuture;
16+
import de.kcodeyt.headsdb.HeadsDB;
1217
import de.kcodeyt.headsdb.util.FormAPI;
1318
import de.kcodeyt.headsdb.util.HeadRender;
1419
import lombok.Getter;
1520

16-
import java.io.IOException;
1721
import java.io.InputStream;
1822
import java.io.InputStreamReader;
1923
import java.io.Reader;
2024
import java.net.HttpURLConnection;
2125
import java.net.URL;
2226
import java.util.*;
27+
import java.util.concurrent.CompletionException;
2328
import java.util.stream.Collectors;
2429

2530
@Getter
@@ -31,96 +36,229 @@ public class Database {
3136
private final Map<String, Integer> pageCount;
3237
private final List<Category> categories;
3338
private final List<HeadEntry> headEntries;
39+
private ScheduledFuture<Boolean> loadFuture;
3440

3541
public Database() {
3642
this.pageCount = new HashMap<>();
3743
this.categories = new ArrayList<>();
3844
this.headEntries = new ArrayList<>();
3945
}
4046

41-
public boolean reload() {
42-
this.categories.clear();
43-
this.headEntries.clear();
44-
return this.load();
47+
public ScheduledFuture<Boolean> reload() {
48+
if(this.loadFuture != null)
49+
return this.loadFuture;
50+
return this.load(true);
4551
}
4652

47-
public boolean load() {
48-
try {
49-
for(final CategoryEnum category : CategoryEnum.values()) {
50-
final HttpURLConnection connection = (HttpURLConnection) new URL(API_URL + "?cat=" + category.getIdentifier()).openConnection();
51-
connection.setRequestProperty("User-Agent", "Chrome");
52-
connection.connect();
53-
54-
if(connection.getResponseCode() == 200) {
55-
try(final InputStream inputStream = connection.getInputStream();
56-
final Reader reader = new InputStreamReader(inputStream)) {
57-
final List<Map<String, String>> values = GSON.<List<Map<String, String>>>fromJson(reader, List.class);
58-
final List<HeadEntry> headEntries = values.stream().map(map -> new HeadEntry(map.get("name"), map.get("uuid"), map.get("value"))).collect(Collectors.toList());
59-
this.headEntries.addAll(headEntries);
60-
this.categories.add(new Category(category, category.getDisplayName(), Iterables.getLast(headEntries).getTexture(), Collections.unmodifiableList(headEntries)));
53+
public ScheduledFuture<Boolean> load() {
54+
return this.load(false);
55+
}
56+
57+
private ScheduledFuture<Boolean> load(boolean clear) {
58+
if(this.loadFuture != null)
59+
return this.loadFuture;
60+
final List<HeadEntry> localHeadEntries = new ArrayList<>();
61+
final List<Category> localCategories = new ArrayList<>();
62+
return this.loadFuture = ScheduledFuture.supplyAsync(() -> {
63+
try {
64+
for(final CategoryEnum category : CategoryEnum.values()) {
65+
final HttpURLConnection connection = (HttpURLConnection) new URL(API_URL + "?cat=" + category.getIdentifier() + "&tags=true").openConnection();
66+
connection.setRequestProperty("User-Agent", "Chrome");
67+
connection.connect();
68+
69+
if(connection.getResponseCode() == 200) {
70+
try(final InputStream inputStream = connection.getInputStream();
71+
final Reader reader = new InputStreamReader(inputStream)) {
72+
final List<Map<String, String>> values = GSON.<List<Map<String, String>>>fromJson(reader, List.class);
73+
final List<HeadEntry> headEntries = values.stream().
74+
map(map -> new HeadEntry(map.get("name"), map.get("uuid"), map.get("value"), map.get("tags"))).
75+
sorted(Comparator.comparing(HeadEntry::getName)).
76+
collect(Collectors.toList());
77+
localHeadEntries.addAll(headEntries);
78+
localCategories.add(new Category(category, category.getDisplayName(), Iterables.getLast(headEntries).getTexture(), Collections.unmodifiableList(headEntries)));
79+
}
6180
}
81+
82+
connection.disconnect();
6283
}
6384

64-
connection.disconnect();
85+
return true;
86+
} catch(Exception e) {
87+
throw new CompletionException(e);
6588
}
89+
}).whenComplete((result, error) -> {
90+
this.loadFuture = null;
91+
if(result) {
92+
if(clear) {
93+
this.categories.clear();
94+
this.headEntries.clear();
95+
}
6696

67-
return true;
68-
} catch(IOException e) {
69-
Server.getInstance().getLogger().error("Error whilst loading heads from database api!", e);
70-
e.printStackTrace();
71-
return false;
72-
}
97+
this.headEntries.addAll(localHeadEntries);
98+
this.categories.addAll(localCategories);
99+
}
100+
});
73101
}
74102

75-
private List<List<HeadEntry>> toPages(Category category, int count) {
76-
final List<HeadEntry> headEntries = category.getEntries();
77-
final int dbSize = headEntries.size();
103+
private <T> List<List<T>> toPages(List<T> entries, int count) {
104+
final int dbSize = entries.size();
78105
if(dbSize <= 0)
79106
return Collections.emptyList();
80-
final List<List<HeadEntry>> pages = new ArrayList<>();
107+
final List<List<T>> pages = new ArrayList<>();
81108
final int chunks = (dbSize - 1) / count;
82109
for(int i = 0; i <= chunks; i++)
83-
pages.add(headEntries.subList(i * count, i == chunks ? dbSize : (i + 1) * count));
110+
pages.add(entries.subList(i * count, i == chunks ? dbSize : (i + 1) * count));
84111
return Collections.unmodifiableList(pages);
85112
}
86113

87114
public void showForm(Player player) {
88-
final FormWindowSimple categoriesForm = new FormWindowSimple("Select a category", "");
115+
final FormWindowSimple categoriesForm = new FormWindowSimple("§lSelect a category", "");
89116
final List<Category> categories = Collections.unmodifiableList(new ArrayList<>(this.categories));
117+
categoriesForm.addButton(new ElementButton("Search a head", HeadRender.createButtonImageById(HeadsDB.MHF_QUESTION_TEXTURE_ID)));
90118
for(final Category category : categories)
91119
categoriesForm.addButton(new ElementButton(category.getDisplayName(), HeadRender.createButtonImage(category.getDisplaySkin())));
92120
FormAPI.create(player, categoriesForm, () -> {
93121
if(categoriesForm.wasClosed())
94122
return;
95-
final Category category = categories.get(categoriesForm.getResponse().getClickedButtonId());
96-
if(category == null)
97-
return;
98-
final FormWindowSimple pagesForm = new FormWindowSimple("Select a page", "");
99-
final List<List<HeadEntry>> pages = this.toPages(category, this.pageCount.getOrDefault(player.getName(), 40));
123+
124+
if(categoriesForm.getResponse().getClickedButtonId() == 0) {
125+
final FormWindowCustom searchForm = new FormWindowCustom("§lSearch a head");
126+
searchForm.addElement(new ElementInput("Search head..."));
127+
FormAPI.create(player, searchForm, () -> {
128+
if(searchForm.wasClosed()) {
129+
FormAPI.createLast(player, categoriesForm);
130+
return;
131+
}
132+
133+
final String searchInput = TextFormat.clean(Objects.toString(searchForm.getResponse().getInputResponse(0), "")).trim();
134+
if(searchInput.isEmpty()) {
135+
player.sendMessage("§cCould not search with empty search input!");
136+
return;
137+
}
138+
139+
final String[] searchArgs = Arrays.stream(searchInput.split(",")).
140+
map(String::trim).map(String::toLowerCase).toArray(String[]::new);
141+
142+
final List<HeadEntry> foundEntries = new ArrayList<>();
143+
next_head:
144+
for(final HeadEntry headEntry : this.headEntries) {
145+
final String name = headEntry.getName().toLowerCase(Locale.ROOT);
146+
final String[] tags = headEntry.getTags() != null ?
147+
Arrays.stream(headEntry.getTags().split(",")).
148+
map(String::toLowerCase).toArray(String[]::new) : null;
149+
150+
for(final String searchArg : searchArgs) {
151+
if(name.startsWith(searchArg) || name.endsWith(searchArg) || name.contains(searchArg)) {
152+
foundEntries.add(headEntry);
153+
continue next_head;
154+
} else if(tags != null)
155+
for(final String tag : tags) {
156+
if(tag.startsWith(searchArg) || tag.endsWith(searchArg) || tag.contains(searchArg)) {
157+
foundEntries.add(headEntry);
158+
continue next_head;
159+
}
160+
}
161+
}
162+
}
163+
164+
this.showForm(player, searchForm, foundEntries, "§lSearch: " + searchInput);
165+
});
166+
} else {
167+
final Category category = categories.get(categoriesForm.getResponse().getClickedButtonId() - 1);
168+
if(category == null)
169+
return;
170+
this.showForm(player, categoriesForm, category.getEntries(), "§l" + category.getDisplayName());
171+
}
172+
});
173+
}
174+
175+
private void showForm(Player player, FormWindow lastWindow, List<HeadEntry> headEntries, String title) {
176+
final int pageCount = this.pageCount.getOrDefault(player.getName(), 40);
177+
if(headEntries.size() > pageCount * pageCount) {
178+
final FormWindowSimple pagesForm = new FormWindowSimple("§lSelect a page", "");
179+
final List<List<List<HeadEntry>>> pages = this.toPages(this.toPages(headEntries, pageCount), pageCount);
180+
for(int i = 0; i < pages.size(); i++)
181+
pagesForm.addButton(new ElementButton("Page " + (i + 1), HeadRender.createButtonImage(Iterables.getLast(Iterables.getLast(pages.get(i))).getTexture())));
182+
FormAPI.create(player, pagesForm, () -> {
183+
if(pagesForm.wasClosed()) {
184+
FormAPI.createLast(player, lastWindow);
185+
return;
186+
}
187+
188+
final FormWindowSimple subPagesForm = new FormWindowSimple("§lSelect a subpage", "");
189+
final List<List<HeadEntry>> subPages = pages.get(pagesForm.getResponse().getClickedButtonId());
190+
for(int i = 0; i < subPages.size(); i++)
191+
subPagesForm.addButton(new ElementButton("Subpage " + (i + 1), HeadRender.createButtonImage(Iterables.getLast(subPages.get(i)).getTexture())));
192+
FormAPI.create(player, subPagesForm, () -> {
193+
if(subPagesForm.wasClosed()) {
194+
FormAPI.createLast(player, pagesForm);
195+
return;
196+
}
197+
198+
final List<HeadEntry> headEntries0 = subPages.get(subPagesForm.getResponse().getClickedButtonId());
199+
if(headEntries0 == null)
200+
return;
201+
final FormWindowSimple subForm = new FormWindowSimple(title, "");
202+
for(final HeadEntry headEntry : headEntries0)
203+
subForm.addButton(new ElementButton(headEntry.getName(), HeadRender.createButtonImage(headEntry.getTexture())));
204+
FormAPI.create(player, subForm, () -> {
205+
if(subForm.wasClosed()) {
206+
FormAPI.createLast(player, subPagesForm);
207+
return;
208+
}
209+
210+
final HeadEntry headEntry = headEntries0.get(subForm.getResponse().getClickedButtonId());
211+
if(headEntry == null)
212+
return;
213+
this.giveItem(player, headEntry);
214+
});
215+
});
216+
});
217+
} else if(headEntries.size() > pageCount) {
218+
final FormWindowSimple pagesForm = new FormWindowSimple("§lSelect a page", "");
219+
final List<List<HeadEntry>> pages = this.toPages(headEntries, pageCount);
100220
for(int i = 0; i < pages.size(); i++)
101221
pagesForm.addButton(new ElementButton("Page " + (i + 1), HeadRender.createButtonImage(Iterables.getLast(pages.get(i)).getTexture())));
102222
FormAPI.create(player, pagesForm, () -> {
103223
if(pagesForm.wasClosed()) {
104-
this.showForm(player);
224+
FormAPI.createLast(player, lastWindow);
105225
return;
106226
}
107227

108-
final List<HeadEntry> headEntries = pages.get(pagesForm.getResponse().getClickedButtonId());
109-
if(headEntries == null)
228+
final List<HeadEntry> headEntries0 = pages.get(pagesForm.getResponse().getClickedButtonId());
229+
if(headEntries0 == null)
110230
return;
111-
final FormWindowSimple subForm = new FormWindowSimple(category.getDisplayName(), "");
112-
for(final HeadEntry headEntry : headEntries)
231+
final FormWindowSimple subForm = new FormWindowSimple(title, "");
232+
for(final HeadEntry headEntry : headEntries0)
113233
subForm.addButton(new ElementButton(headEntry.getName(), HeadRender.createButtonImage(headEntry.getTexture())));
114234
FormAPI.create(player, subForm, () -> {
115-
if(subForm.wasClosed())
235+
if(subForm.wasClosed()) {
236+
FormAPI.createLast(player, pagesForm);
116237
return;
117-
final HeadEntry headEntry = headEntries.get(subForm.getResponse().getClickedButtonId());
238+
}
239+
240+
final HeadEntry headEntry = headEntries0.get(subForm.getResponse().getClickedButtonId());
118241
if(headEntry == null)
119242
return;
120243
this.giveItem(player, headEntry);
121244
});
122245
});
123-
});
246+
} else {
247+
final FormWindowSimple subForm = new FormWindowSimple(title, "");
248+
for(final HeadEntry headEntry : headEntries)
249+
subForm.addButton(new ElementButton(headEntry.getName(), HeadRender.createButtonImage(headEntry.getTexture())));
250+
FormAPI.create(player, subForm, () -> {
251+
if(subForm.wasClosed()) {
252+
FormAPI.createLast(player, lastWindow);
253+
return;
254+
}
255+
256+
final HeadEntry headEntry = headEntries.get(subForm.getResponse().getClickedButtonId());
257+
if(headEntry == null)
258+
return;
259+
this.giveItem(player, headEntry);
260+
});
261+
}
124262
}
125263

126264
public void giveItem(Player player, HeadEntry headEntry) {

src/main/java/de/kcodeyt/headsdb/database/HeadEntry.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ public class HeadEntry {
1212
private final String name;
1313
private final String id;
1414
private final String texture;
15+
private final String tags;
1516

1617
}

0 commit comments

Comments
 (0)