Skip to content

Commit

Permalink
Dev service for minecraft working
Browse files Browse the repository at this point in the history
  • Loading branch information
holly-cummins committed Aug 11, 2024
1 parent 0de82b3 commit 6b08e6d
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 61 deletions.
84 changes: 44 additions & 40 deletions extension/deployment/pom.xml
Original file line number Diff line number Diff line change
@@ -1,44 +1,48 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.acme</groupId>
<artifactId>minecrafter-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>minecrafter-deployment</artifactId>
<name>Minecrafter - Deployment</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-reactive-jackson-deployment</artifactId>
</dependency>
<dependency>
<groupId>org.acme</groupId>
<artifactId>minecrafter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${quarkus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.acme</groupId>
<artifactId>minecrafter-parent</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>minecrafter-deployment</artifactId>
<name>Minecrafter - Deployment</name>
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-rest-client-reactive-jackson-deployment</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-devservices-common</artifactId>
</dependency>
<dependency>
<groupId>org.acme</groupId>
<artifactId>minecrafter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-internal</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-extension-processor</artifactId>
<version>${quarkus.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.acme.minecrafter.deployment;

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;

import java.util.ArrayList;
import java.util.List;

public class MinecraftContainer extends GenericContainer<MinecraftContainer> {
private static final int MINECRAFT_PORT = 25565;
static final int OBSERVABILITY_PORT = 8081;

public MinecraftContainer(DockerImageName image) {
super(image);

List<String> portBindings = new ArrayList<>();
portBindings.add("25565:25565"); // Make life easy for the minecraft client
setPortBindings(portBindings);
//withReuse(true);

// withExposedPorts(MINECRAFT_PORT);
// This is a bit of a cheat, since at this point the client isn't ready, but otherwise it's too slow
waitingFor(Wait.forLogMessage(".*" + "Preparing" + ".*", 1));
}


@Override
protected void configure() {
withNetwork(Network.SHARED);
addExposedPorts(OBSERVABILITY_PORT);
addExposedPorts(MINECRAFT_PORT);
}

public Integer getApiPort() {
return this.getMappedPort(OBSERVABILITY_PORT);
}

public Integer getGamePort() {
return this.getMappedPort(MINECRAFT_PORT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,15 @@
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.processor.AnnotationsTransformer;
import io.quarkus.deployment.IsNormal;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.DevServicesResultBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.LaunchModeBuildItem;
import io.quarkus.deployment.builditem.LogHandlerBuildItem;
import io.quarkus.deployment.dev.devservices.GlobalDevServicesConfig;
import io.quarkus.resteasy.reactive.spi.ExceptionMapperBuildItem;
import org.acme.minecrafter.runtime.HelloRecorder;
import org.acme.minecrafter.runtime.MinecraftLog;
Expand All @@ -17,8 +21,10 @@
import org.acme.minecrafter.runtime.MinecraftService;
import org.acme.minecrafter.runtime.RestExceptionMapper;
import org.jboss.jandex.DotName;
import org.testcontainers.utility.DockerImageName;

import javax.ws.rs.Priorities;
import java.util.Map;

import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;
Expand Down Expand Up @@ -63,8 +69,12 @@ public boolean appliesTo(org.jboss.jandex.AnnotationTarget.Kind kind) {
}

public void transform(TransformationContext context) {
if (context.getTarget().asMethod().hasAnnotation(JAX_RS_GET)) {
context.transform().add(MinecraftLog.class).done();
if (context.getTarget()
.asMethod()
.hasAnnotation(JAX_RS_GET)) {
context.transform()
.add(MinecraftLog.class)
.done();
}
}
});
Expand All @@ -76,4 +86,27 @@ ExceptionMapperBuildItem exceptionMappers() {
Exception.class.getName(), Priorities.USER + 100, true);
}

@BuildStep(onlyIfNot = IsNormal.class, onlyIf = GlobalDevServicesConfig.Enabled.class)
public DevServicesResultBuildItem createContainer(LaunchModeBuildItem launchMode) {
// Normally, this would be a remote image, but we need to build one with the right mods, so use a local one
DockerImageName dockerImageName = DockerImageName.parse("minecraft-server");

// Don't be tempted to put this in a try-with-resources block, even if the IDE advises it
// Otherwise the dev service gets shut down after startup :)
MinecraftContainer container = new MinecraftContainer(dockerImageName).withExposedPorts(8081, 25565);
container.start();

// Set a config property so that anything using the container can find it, even on the random port

Map<String, String> props = Map.of("quarkus.minecrafter.base-url",
"http://" + container.getHost() + ":" + container.getApiPort());

System.out.println("API port: " + "http://" + container.getHost() + ":" + container.getApiPort());
System.out.println("Game port: " + "http://" + container.getHost() + ":" + container.getGamePort());

return new DevServicesResultBuildItem.RunningDevService(FEATURE, container.getContainerId(),
container::close, props)
.toBuildItem();
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public void publish(LogRecord record) {
String formattedMessage = String.format(record.getMessage(), record.getParameters());
System.out.println("⛏️ [Minecrafter] " + formattedMessage);

minecraft.log(formattedMessage);
// TODO this hangs if the minecraft server is a dev service; make it fire-and-forget
// minecraft.log(formattedMessage);

}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,10 @@
package org.acme.minecrafter.runtime;

import javax.inject.Singleton;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.eclipse.microprofile.rest.client.RestClientBuilder;

import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.util.Set;

@Singleton
public class MinecraftService {
Expand All @@ -39,8 +28,10 @@ public void boom() {

public void log(String message) {
try {
client.target(minecrafterConfig.baseURL).path("log")
.request(MediaType.TEXT_PLAIN).post(Entity.text(message));
client.target(minecrafterConfig.baseURL)
.path("observability/log")
.request(MediaType.TEXT_PLAIN)
.post(Entity.text(message));
// Don't log anything back about the response or it ends up with too much circular logging
} catch (Throwable e) {
System.out.println("\uD83D\uDDE1️ [Minecrafter] Connection error: " + e);
Expand All @@ -49,9 +40,10 @@ public void log(String message) {

private void invokeMinecraft(String path) {
try {
String response = client.target(minecrafterConfig.baseURL).path(path)
.request(MediaType.TEXT_PLAIN)
.get(String.class);
String response = client.target(minecrafterConfig.baseURL)
.path("observability/" + path)
.request(MediaType.TEXT_PLAIN)
.get(String.class);

System.out.println("\uD83D\uDDE1️ [Minecrafter] Mod response: " + response);
} catch (Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ public class MinecrafterConfig {
/**
* The minecraft server's observability base URL
*/
@ConfigItem(defaultValue = "http://localhost:8081/observability/")
@ConfigItem(defaultValue = "http://localhost:8081/")
public String baseURL;
}
2 changes: 2 additions & 0 deletions modded-minecraft/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ FROM webhippie/minecraft-forge:43.0

# This is needed for the client launched with `./gradlew runClient` to be able to connect
ENV MINECRAFT_ONLINE_MODE=false
# For performance reasons, keep the world small
ENV MINECRAFT_MAX_WORLD_SIZE=299

RUN find / -name mods
EXPOSE 8081
Expand Down

0 comments on commit 6b08e6d

Please sign in to comment.