Added HTTP API and Event Item Rewards

This commit is contained in:
Elias Müller 2023-12-10 21:34:23 +01:00
parent 9278af6071
commit 2be1865263
7 changed files with 152 additions and 9 deletions
build.gradle
src/main/java/eu/mhsl/craftattack/spawn

@ -22,9 +22,10 @@ repositories {
}
dependencies {
compileOnly "io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT"
compileOnly 'io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT'
compileOnly 'org.geysermc.floodgate:api:2.2.2-SNAPSHOT'
compileOnly 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'com.sparkjava:spark-core:2.9.4'
}
def targetJavaVersion = 17
@ -43,9 +44,17 @@ tasks.withType(JavaCompile).configureEach {
}
}
tasks.register('copyJarToServer', Exec) {
dependsOn jar
mustRunAfter jar
commandLine 'scp', 'build/libs/spawn-1.0.jar', 'root@10.20.6.1:/root/server/plugins'
configurations {
shadowImplementation.extendsFrom implementation
}
shadowJar {
configurations = [project.configurations.shadowImplementation]
}
tasks.register('copyJarToServer', Exec) {
dependsOn shadowJar
mustRunAfter shadowJar
commandLine 'scp', 'build/libs/spawn-1.0-all.jar', 'root@10.20.6.1:/root/server/plugins'
}

@ -1,5 +1,6 @@
package eu.mhsl.craftattack.spawn;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliances.adminMarker.AdminMarker;
import eu.mhsl.craftattack.spawn.appliances.kick.Kick;
@ -28,6 +29,7 @@ public final class Main extends JavaPlugin {
private static Main instance;
private List<Appliance> appliances;
private HttpServer httpApi;
@Override
public void onEnable() {
instance = this;
@ -59,6 +61,9 @@ public final class Main extends JavaPlugin {
});
Bukkit.getLogger().info("Loaded " + appliances.size() + " appliances!");
Bukkit.getLogger().info("Starting HTTP API");
this.httpApi = new HttpServer();
getServer().getMessenger().registerOutgoingPluginChannel(this, "BungeeCord");
}

@ -0,0 +1,53 @@
package eu.mhsl.craftattack.spawn.api;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import org.bukkit.configuration.ConfigurationSection;
import spark.Request;
import spark.Spark;
import java.util.function.Function;
public class HttpServer {
private final ConfigurationSection apiConf = Main.instance().getConfig().getConfigurationSection("api");
protected final Gson gson = new Gson();
public static Object nothing = "";
public HttpServer() {
Spark.port(8080);
Spark.get("/ping", (request, response) -> System.currentTimeMillis());
Main.instance().getAppliances().forEach(appliance -> appliance.httpApi(new ApiBuilder(appliance)));
}
public class ApiBuilder {
@FunctionalInterface
public interface RequestProvider<TParsed, TOriginal, TResponse> {
TResponse apply(TParsed parsed, TOriginal original);
}
private final String applianceName;
private ApiBuilder(Appliance appliance) {
this.applianceName = appliance.getClass().getSimpleName().toLowerCase();
}
public void get(String path, Function<Request, Object> onCall) {
Spark.get(this.buildRoute(path), (req, resp) -> HttpServer.this.gson.toJson(onCall.apply(req)));
}
public <TRequest> void post(String path, Class<TRequest> clazz, RequestProvider<TRequest, Request, Object> onCall) {
Spark.post(this.buildRoute(path), (req, resp) -> {
TRequest parsed = new Gson().fromJson(req.body(), clazz);
Object response = onCall.apply(parsed, req);
return HttpServer.this.gson.toJson(response);
});
}
public String buildRoute(String path) {
return String.format("/api/%s/%s", this.applianceName, path);
}
}
}

@ -1,5 +1,6 @@
package eu.mhsl.craftattack.spawn.appliance;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.config.Configuration;
import org.bukkit.Bukkit;
import org.bukkit.command.PluginCommand;
@ -8,7 +9,6 @@ import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@ -50,6 +50,13 @@ public abstract class Appliance {
return new ArrayList<>();
}
/**
* Called on initialization to add all needed API Routes.
* The routeBuilder can be used to get the correct Path prefixes
* @param apiBuilder holds data for needed route prefixes.
*/
public void httpApi(HttpServer.ApiBuilder apiBuilder) {}
/**
* Provides a localized config section. Path can be set in appliance constructor.
* @return Section of configuration for your appliance

@ -1,21 +1,27 @@
package eu.mhsl.craftattack.spawn.appliances.event;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.api.HttpServer;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.appliance.ApplianceCommand;
import eu.mhsl.craftattack.spawn.appliances.event.command.EventCommand;
import eu.mhsl.craftattack.spawn.appliances.event.command.EventEndSessionCommand;
import eu.mhsl.craftattack.spawn.appliances.event.command.EventOpenSessionCommand;
import eu.mhsl.craftattack.spawn.appliances.event.command.MoveEventVillagerCommand;
import eu.mhsl.craftattack.spawn.appliances.event.listener.ApplyPendingRewardsListener;
import eu.mhsl.craftattack.spawn.util.entity.DisplayVillager;
import eu.mhsl.craftattack.spawn.util.server.PluginMessage;
import eu.mhsl.craftattack.spawn.util.listener.DismissInventoryOpenFromHolder;
import eu.mhsl.craftattack.spawn.util.listener.PlayerInteractAtEntityEventListener;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.entity.Villager;
import org.bukkit.event.Listener;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.io.IOException;
@ -24,15 +30,20 @@ import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class Event extends Appliance {
public DisplayVillager.ConfigBound villager;
private boolean isOpen = false;
private UUID roomId;
private HttpClient eventServerClient = HttpClient.newHttpClient();
private final HttpClient eventServerClient = HttpClient.newHttpClient();
private final List<Reward> pendingRewards = new ArrayList<>();
record RewardConfiguration(String material, Map<UUID, Integer> rewards) {}
record Reward(UUID playerUuid, Material material, int amount) {}
public Event() {
super("event");
@ -103,6 +114,47 @@ public class Event extends Appliance {
isOpen = false;
}
private void rewardPlayers(RewardConfiguration rewardConfiguration) {
rewardConfiguration.rewards.forEach((uuid, amount) -> {
Reward reward = new Reward(uuid, Material.matchMaterial(rewardConfiguration.material), amount);
if(reward.material == null) throw new RuntimeException("Material not found");
if(Bukkit.getPlayer(uuid) == null) {
pendingRewards.add(reward);
return;
}
giveReward(reward);
});
}
private void giveReward(Reward reward) {
Player player = Bukkit.getPlayer(reward.playerUuid);
if(player == null) throw new RuntimeException("Cannot reward offline playerUuid!");
ItemStack rewardStack = new ItemStack(reward.material, reward.amount);
Map<Integer, ItemStack> remaining = player.getInventory().addItem(rewardStack);
Bukkit.getScheduler().runTask(
Main.instance(),
() -> remaining.values().forEach(remainingStack -> player.getWorld().dropItem(player.getLocation(), remainingStack))
);
}
public void applyPendingRewards(Player player) {
if(this.pendingRewards.isEmpty()) return;
this.pendingRewards.stream()
.filter(reward -> reward.playerUuid.equals(player.getUniqueId()))
.forEach(this::giveReward);
}
@Override
public void httpApi(HttpServer.ApiBuilder apiBuilder) {
apiBuilder.post("reward", RewardConfiguration.class, (rewardConfiguration, request) -> {
this.rewardPlayers(rewardConfiguration);
return HttpServer.nothing;
});
}
@Override
protected @NotNull List<ApplianceCommand<?>> commands() {
return List.of(
@ -116,6 +168,7 @@ public class Event extends Appliance {
@Override
protected @NotNull List<Listener> eventHandlers() {
return List.of(
new ApplyPendingRewardsListener(),
new PlayerInteractAtEntityEventListener(this.villager.getUniqueId(), playerInteractAtEntityEvent -> joinEvent(playerInteractAtEntityEvent.getPlayer())),
new DismissInventoryOpenFromHolder(this.villager.getUniqueId())
);

@ -0,0 +1,13 @@
package eu.mhsl.craftattack.spawn.appliances.event.listener;
import eu.mhsl.craftattack.spawn.appliance.ApplianceListener;
import eu.mhsl.craftattack.spawn.appliances.event.Event;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
public class ApplyPendingRewardsListener extends ApplianceListener<Event> {
@EventHandler
public void onJoin(PlayerJoinEvent event) {
getAppliance().applyPendingRewards(event.getPlayer());
}
}

@ -1,6 +1,7 @@
package eu.mhsl.craftattack.spawn.appliances.whitelist;
import com.google.gson.Gson;
import eu.mhsl.craftattack.spawn.Main;
import eu.mhsl.craftattack.spawn.appliance.Appliance;
import eu.mhsl.craftattack.spawn.util.server.Floodgate;
import eu.mhsl.craftattack.spawn.util.text.DisconnectInfo;
@ -18,6 +19,7 @@ import java.net.http.HttpResponse;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.logging.Level;
public class Whitelist extends Appliance {
private record UserData(UUID uuid, String username, String firstname, String lastname) {}
@ -50,6 +52,7 @@ public class Whitelist extends Appliance {
} catch (DisconnectInfo.Throwable e) {
throw e;
} catch (Exception e) {
Main.instance().getLogger().log(Level.SEVERE, e, e::getMessage);
throw new DisconnectInfo.Throwable(
"Interner Serverfehler",
"Deine Zugangsdaten konnten nicht abgerufen werden.",