Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions velocity/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ dependencies {
}
}

tasks.shadowJar {
minimize {
exclude(project(":webserver"))
tasks{
compileJava {
options.release.set(17)
}

shadowJar {
minimize {
exclude(project(":webserver"))
}
mergeServiceFiles()
relocate("io.leangen.geantyref", "forcepack.libs.geantyref")
relocate("org.bstats", "com.convallyria.forcepack.velocity.libs.bstats")
relocate("org.glassfish.jaxb", "com.convallyria.forcepack.libs.jaxb")
relocate("org.objectweb.asm", "com.convallyria.forcepack.libs.asm")
}
mergeServiceFiles()
relocate("io.leangen.geantyref", "forcepack.libs.geantyref")
relocate("org.bstats", "com.convallyria.forcepack.velocity.libs.bstats")
relocate("org.glassfish.jaxb", "com.convallyria.forcepack.libs.jaxb")
relocate("org.objectweb.asm", "com.convallyria.forcepack.libs.asm")
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.convallyria.forcepack.velocity.config.VelocityConfig;
import com.convallyria.forcepack.velocity.handler.PackHandler;
import com.convallyria.forcepack.velocity.listener.ResourcePackListener;
import com.convallyria.forcepack.velocity.listener.ServerRegistrationListener;
import com.convallyria.forcepack.velocity.resourcepack.VelocityResourcePack;
import com.convallyria.forcepack.velocity.schedule.VelocityScheduler;
import com.convallyria.forcepack.webserver.ForcePackWebServer;
Expand Down Expand Up @@ -53,6 +54,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -162,6 +164,7 @@ public void reloadConfig() {
private void registerListeners() {
final EventManager eventManager = server.getEventManager();
eventManager.register(this, new ResourcePackListener(this));
eventManager.register(this, new ServerRegistrationListener(this));

if (!getConfig().getBoolean("disable-commands-until-loaded", false)) return;
eventManager.register(this, CommandExecuteEvent.class, event -> {
Expand Down Expand Up @@ -214,17 +217,7 @@ private void addResourcePacks(@Nullable Player player, String rootName) {
for (String name : root.getKeys()) {
log("Checking %s - %s", typeName, name);
final VelocityConfig serverConfig = root.getConfig(name);
final Map<String, VelocityConfig> configs = new HashMap<>();
// Add the default fallback
configs.put("default", serverConfig.getConfig("resourcepack"));
final VelocityConfig versionConfig = serverConfig.getConfig("version");
if (versionConfig != null) {
log("Detected versioned resource packs for %s", name);
for (String versionId : versionConfig.getKeys()) {
configs.put(versionId, versionConfig.getConfig(versionId).getConfig("resourcepack"));
log("Added version config %s for %s", versionId, name);
}
}
final Map<String, VelocityConfig> configs = getPackConfigs(serverConfig, name);

configs.forEach((id, config) -> {
if (config == null) {
Expand All @@ -237,7 +230,68 @@ private void addResourcePacks(@Nullable Player player, String rootName) {
}
}

private void registerResourcePack(VelocityConfig rootServerConfig, VelocityConfig resourcePack, String id, String name, String typeName, boolean groups, boolean verifyPacks, @Nullable Player player) {
public Map<String, VelocityConfig> getPackConfigs(VelocityConfig serverConfig, String name) {
final Map<String, VelocityConfig> configs = new HashMap<>();
// Add the default fallback
configs.put("default", serverConfig.getConfig("resourcepack"));
final VelocityConfig versionConfig = serverConfig.getConfig("version");
if (versionConfig != null) {
log("Detected versioned resource packs for %s", name);
for (String versionId : versionConfig.getKeys()) {
configs.put(versionId, versionConfig.getConfig(versionId).getConfig("resourcepack"));
log("Added version config %s for %s", versionId, name);
}
}
return configs;
}

public void registerResourcePack(VelocityConfig rootServerConfig, VelocityConfig resourcePack, String id, String name, String typeName, boolean groups, boolean verifyPacks, @Nullable Player player) {
processResourcePackConfig(resourcePack, id, name, (url, hash) -> {
this.handleRegister(rootServerConfig, resourcePack, name, id, typeName, url, hash, groups, verifyPacks, player);
});
}

private void handleRegister(VelocityConfig rootServerConfig, VelocityConfig resourcePack, String name, String id, String typeName, String url, @Nullable String hash, boolean groups, boolean verifyPacks, @Nullable Player player) {
if (groups) {
final boolean exact = rootServerConfig.getBoolean("exact-match");
for (String serverName : rootServerConfig.getStringList("servers")) {
for (RegisteredServer registeredServer : server.getAllServers()) {
final String serverInfoName = registeredServer.getServerInfo().getName();
final boolean matches = exact ?
serverInfoName.equals(serverName) :
serverInfoName.contains(serverName);
if (!matches) continue;

VelocityResourcePack pack = createResourcePack(resourcePack, name, id, typeName, url, hash, verifyPacks, serverInfoName, name, player);
if (pack != null) {
resourcePacks.add(pack);
log("Added resource pack for server %s (%s)", serverInfoName, pack.getUUID().toString());
}
}
}
} else {
VelocityResourcePack pack = createResourcePack(resourcePack, name, id, typeName, url, hash, verifyPacks, name, null, player);
if (pack != null) {
resourcePacks.add(pack);
}
}
}

public void registerResourcePackForServer(VelocityConfig resourcePack, String id, String groupName, String typeName, boolean verifyPacks, String targetServerName, @Nullable Player player) {
processResourcePackConfig(resourcePack, id, groupName, (url, hash) -> {
this.handleRegisterForServer(resourcePack, groupName, id, typeName, url, hash, verifyPacks, targetServerName, player);
});
}

private void handleRegisterForServer(VelocityConfig resourcePack, String name, String id, String typeName, String url, @Nullable String hash, boolean verifyPacks, String targetServerName, @Nullable Player player) {
VelocityResourcePack pack = createResourcePack(resourcePack, name, id, typeName, url, hash, verifyPacks, targetServerName, name, player);
if (pack != null) {
resourcePacks.add(pack);
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The resourcePacks HashSet is not thread-safe, but it can be modified concurrently from both the main initialization thread and asynchronously from the ServerRegisteredEvent listener. This could lead to race conditions, data corruption, or ConcurrentModificationException. Consider using a thread-safe collection like ConcurrentHashMap.newKeySet() or Collections.synchronizedSet() to wrap the HashSet, or ensure all modifications happen on a single thread using proper synchronization.

Copilot uses AI. Check for mistakes.
log("Added resource pack for server %s (%s)", targetServerName, pack.getUUID().toString());
}
}

private void processResourcePackConfig(VelocityConfig resourcePack, String id, String name, BiConsumer<String, String> action) {
List<String> urls = resourcePack.getStringList("urls");
if (urls.isEmpty()) {
urls = List.of(resourcePack.getString("url", ""));
Expand All @@ -257,11 +311,12 @@ private void registerResourcePack(VelocityConfig rootServerConfig, VelocityConfi
for (int i = 0; i < urls.size(); i++) {
final String url = urls.get(i);
final String hash = i >= hashes.size() ? null : hashes.get(i);
this.handleRegister(rootServerConfig, resourcePack, name, id, typeName, url, hash, groups, verifyPacks, player);
action.accept(url, hash);
}
}

private void handleRegister(VelocityConfig rootServerConfig, VelocityConfig resourcePack, String name, String id, String typeName, String url, @Nullable String hash, boolean groups, boolean verifyPacks, @Nullable Player player) {
@Nullable
private VelocityResourcePack createResourcePack(VelocityConfig resourcePack, String name, String id, String typeName, String url, @Nullable String hash, boolean verifyPacks, String targetServerName, @Nullable String groupName, @Nullable Player player) {
final ConsoleCommandSource consoleSender = this.getServer().getConsoleCommandSource();
if (url.isEmpty()) {
logger.error("No URL found for {}. Did you set up the config correctly?", name);
Expand Down Expand Up @@ -310,7 +365,7 @@ private void handleRegister(VelocityConfig rootServerConfig, VelocityConfig reso
this.getLogger().error("Your config hash returned: {}", data.getConfigHash());
this.getLogger().error("Please provide a correct SHA-1 hash!");
this.getLogger().error("-----------------------------------------------");
return;
return null;
} else {
Component hashMsg = Component.text("Hash verification complete for " + typeName + " " + name + " (" + id + ").").color(NamedTextColor.GREEN);
consoleSender.sendMessage(hashMsg);
Expand All @@ -326,23 +381,7 @@ private void handleRegister(VelocityConfig rootServerConfig, VelocityConfig reso
version = getVersionFromId(id);
} catch (IllegalArgumentException ignored) {}

if (groups) {
final boolean exact = rootServerConfig.getBoolean("exact-match");
for (String serverName : rootServerConfig.getStringList("servers")) {
for (RegisteredServer registeredServer : server.getAllServers()) {
final String serverInfoName = registeredServer.getServerInfo().getName();
final boolean matches = exact ?
serverInfoName.equals(serverName) :
serverInfoName.contains(serverName);
if (!matches) continue;
final VelocityResourcePack pack = new VelocityResourcePack(this, serverInfoName, url, hash, sizeInMB.get(), name, version);
resourcePacks.add(pack);
log("Added resource pack for server %s (%s)", serverInfoName, pack.getUUID().toString());
}
}
} else {
resourcePacks.add(new VelocityResourcePack(this, name, url, hash, sizeInMB.get(), null, version));
}
return new VelocityResourcePack(this, targetServerName, url, hash, sizeInMB.get(), groupName, version);
}

private void checkUnload() {
Expand Down Expand Up @@ -395,27 +434,8 @@ private void checkGlobal() {
}

configs.forEach((id, config) -> {
List<String> urls = config.getStringList("urls");
if (urls.isEmpty()) {
urls = List.of(config.getString("url", ""));
}

List<String> hashes = config.getStringList("hashes");
if (hashes.isEmpty()) {
hashes = List.of(config.getString("hash", ""));
}

final boolean generateHash = config.getBoolean("generate-hash", false);
if (!generateHash && urls.size() != hashes.size()) {
getLogger().error("[global-pack] There are not the same amount of URLs and hashes! Please provide a hash for every resource pack URL! ({})", id);
getLogger().error("Hint: Enabling generate-hash will auto-generate missing hashes");
}

for (int i = 0; i < urls.size(); i++) {
final String url = urls.get(i);
final String hash = i >= hashes.size() ? null : hashes.get(i);
this.registerGlobalResourcePack(config, id, url, hash);
}
processResourcePackConfig(config, id == null ? null : id.toString(), "global-pack",
(url, hash) -> this.registerGlobalResourcePack(config, id, url, hash));
});
}

Expand Down Expand Up @@ -592,4 +612,9 @@ public MiniMessage getMiniMessage() {
public void log(String info, Object... format) {
if (this.getConfig().getBoolean("debug")) this.getLogger().info(String.format(info, format));
}

public void addResourcePack(String serverName, VelocityResourcePack pack) {
resourcePacks.add(pack);
log("Added resource pack for server %s (%s)", serverName, pack.getUUID().toString());
}
Comment on lines +615 to +619
Copy link

Copilot AI Jan 31, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is defined but never called anywhere in the codebase. It appears to be dead code that should either be removed or properly integrated if it was intended to replace the direct resourcePacks.add() calls in handleRegisterForServer().

Suggested change
public void addResourcePack(String serverName, VelocityResourcePack pack) {
resourcePacks.add(pack);
log("Added resource pack for server %s (%s)", serverName, pack.getUUID().toString());
}

Copilot uses AI. Check for mistakes.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.convallyria.forcepack.velocity.listener;

import com.convallyria.forcepack.velocity.ForcePackVelocity;
import com.convallyria.forcepack.velocity.config.VelocityConfig;
import com.velocitypowered.api.event.Subscribe;
import com.velocitypowered.api.event.proxy.server.ServerRegisteredEvent;
import com.velocitypowered.api.proxy.server.RegisteredServer;

import java.util.List;
import java.util.Map;

public class ServerRegistrationListener {

private final ForcePackVelocity plugin;

public ServerRegistrationListener(ForcePackVelocity plugin) {
this.plugin = plugin;
}

@Subscribe
public void onServerRegistered(ServerRegisteredEvent event) {
RegisteredServer registeredServer = event.registeredServer();
String serverName = registeredServer.getServerInfo().getName();

// Check if we already have packs for this server
boolean hasPack = plugin.getResourcePacks().stream()
.anyMatch(pack -> pack.getServer().equals(serverName));

if (hasPack) return;

// Check the "servers" configuration first
VelocityConfig servers = plugin.getConfig().getConfig("servers");
if (servers != null) {
VelocityConfig serverConfig = servers.getConfig(serverName);
if (serverConfig != null) {
plugin.log("New server %s matches servers configuration, adding resource packs...", serverName);

final Map<String, VelocityConfig> configs = plugin.getPackConfigs(serverConfig, serverName);

configs.forEach((id, config) -> {
if (config == null) return;
// serverName is used both as the config name and target server name
plugin.registerResourcePackForServer(config, id, serverName, "server", plugin.getConfig().getBoolean("verify-resource-packs"), serverName, null);
});
return;
}
}

// Check the "groups" configuration
VelocityConfig groups = plugin.getConfig().getConfig("groups");
if (groups == null) return;

for (String groupName : groups.getKeys()) {
VelocityConfig groupConfig = groups.getConfig(groupName);
List<String> servers = groupConfig.getStringList("servers");
boolean exact = groupConfig.getBoolean("exact-match");

boolean matches = exact ?
servers.contains(serverName) :
servers.stream().anyMatch(serverName::contains);

if (matches) {
plugin.log("New server %s matches group %s, adding resource packs...", serverName, groupName);

final Map<String, VelocityConfig> configs = plugin.getPackConfigs(groupConfig, groupName);

configs.forEach((id, config) -> {
if (config == null) return;
plugin.registerResourcePackForServer(config, id, groupName, "group", plugin.getConfig().getBoolean("verify-resource-packs"), serverName, null);
});
}
}
}
Comment on lines 20 to 73
Copy link

Copilot AI Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ServerRegistrationListener only checks the "groups" configuration but not the "servers" configuration. According to the PR description, dynamic servers should be applied resource packs if they qualify for "groups/servers configs", but this implementation only handles groups. If a dynamically added server should also match entries in the "servers" section of the config, that logic is missing.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it failed to do that

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it failed to do that

fixed

}
Loading