Skip to content

Commit

Permalink
initial support for multiple agents on dedicated server (#79)
Browse files Browse the repository at this point in the history
* access widener

* initial support for multiple agents on server-side

* initial support for connection to remote server

* bump version

* filter out debug logs from minecraft

* start server in ci

* add server-side mixins to server side

* more logs

* add fabric server launcher to fabric

* eula

* stop integrated server if connecting to remote

* set seed for server world
  • Loading branch information
noskill authored Dec 4, 2024
1 parent c81f540 commit e69599b
Show file tree
Hide file tree
Showing 16 changed files with 184 additions and 41 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ jobs:
env:
DISPLAY: :99
GITHUB_WORKSPACE: $GITHUB_WORKSPACE
- name: start minecraft server
run: cd server && ./launch.sh &
env:
GITHUB_WORKSPACE: $GITHUB_WORKSPACE
- name: checkout tagilmo
uses: actions/checkout@v3
with:
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.6
0.1.7
17 changes: 14 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
plugins {
id 'java'
id 'base'
id 'fabric-loom' version '1.6-SNAPSHOT'
id 'maven-publish'
id("com.github.bjornvester.xjc") version "1.6.0"
}

sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
java {
sourceCompatibility = JavaVersion.VERSION_21
targetCompatibility = JavaVersion.VERSION_21
}

version = rootProject.file('VERSION').text.trim()
archivesBaseName = project.archives_base_name + '-fabric_' + project.fabric_version
base {
archivesName = project.archives_base_name + '-fabric_' + project.fabric_version
}
group = project.maven_group


Expand Down Expand Up @@ -37,6 +43,7 @@ dependencies {
implementation 'jakarta.annotation:jakarta.annotation-api:2.0.0'
implementation 'jakarta.activation:jakarta.activation-api:2.0.1'
implementation 'org.json:json:20230227'
compileOnly 'com.mojang:authlib'

xjc "org.glassfish.jaxb:jaxb-runtime:3.0.2"
xjc 'org.glassfish.jaxb:jaxb-xjc:3.0.1'
Expand Down Expand Up @@ -80,6 +87,10 @@ jar {
}
jar.duplicatesStrategy = 'WARN'

loom {
accessWidenerPath = file("src/main/resources/vereya.accesswidener")
}

// configure the maven publication
publishing {
publications {
Expand Down
4 changes: 4 additions & 0 deletions config.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,9 @@
<AppenderRef ref="Console" level="info"/>
<AppenderRef ref="file" level="debug"/>
</Root>
<Logger name="net.minecraft" level="info" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="file"/>
</Logger>
</Loggers>
</Configuration>
Binary file not shown.
1 change: 1 addition & 0 deletions server/config.xml
3 changes: 3 additions & 0 deletions server/eula.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#By changing the setting below to TRUE you are indicating your agreement to our EULA (https://aka.ms/MinecraftEULA).
#Thu Dec 29 16:39:29 GMT+04:00 2022
eula=true
1 change: 1 addition & 0 deletions server/launch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8006 -Dlog4j.configurationFile=`pwd`/config.xml -Xmx2G -jar mods/fabric-server-mc.1.21-loader.0.16.5-launcher.1.0.1.jar --nogui
63 changes: 63 additions & 0 deletions server/server.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#Minecraft server properties
#Tue Oct 29 19:57:45 MSK 2024
accepts-transfers=false
allow-flight=false
allow-nether=true
broadcast-console-to-ops=true
broadcast-rcon-to-ops=true
bug-report-link=
difficulty=easy
enable-command-block=false
enable-jmx-monitoring=false
enable-query=false
enable-rcon=false
enable-status=true
enforce-secure-profile=false
enforce-whitelist=false
entity-broadcast-range-percentage=100
force-gamemode=false
function-permission-level=2
gamemode=survival
generate-structures=true
generator-settings={"biome"\:"minecraft\:desert","layers"\:[{"block"\:"minecraft\:bedrock","height"\:1},{"block"\:"minecraft\:stone","height"\:3},{"block"\:"minecraft\:sandstone","height"\:116}],"structures"\:{"village"\:{}}}
hardcore=false
hide-online-players=false
initial-disabled-packs=
initial-enabled-packs=vanilla
level-name=world
level-seed=43839877843298
level-type=minecraft\:normal
log-ips=true
max-chained-neighbor-updates=1000000
max-players=20
max-tick-time=60000
max-world-size=29999984
motd=A Minecraft Server
network-compression-threshold=256
online-mode=false
op-permission-level=4
player-idle-timeout=0
prevent-proxy-connections=false
pvp=true
query.port=25565
rate-limit=0
rcon.password=
rcon.port=25575
region-file-compression=deflate
require-resource-pack=false
resource-pack=
resource-pack-id=
resource-pack-prompt=
resource-pack-sha1=
server-ip=
server-port=25565
simulation-distance=10
spawn-animals=true
spawn-monsters=true
spawn-npcs=true
spawn-protection=16
sync-chunk-writes=true
text-filtering-config=
use-native-transport=true
view-distance=10
white-list=false
86 changes: 63 additions & 23 deletions src/main/java/io/singularitynet/Client/ClientStateMachine.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.GameMenuScreen;
import net.minecraft.client.gui.screen.Screen;
import net.minecraft.client.gui.screen.multiplayer.ConnectScreen;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.network.ServerAddress;
import net.minecraft.client.network.ServerInfo;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.Entity;
import net.minecraft.entity.mob.MobEntity;
Expand Down Expand Up @@ -550,6 +555,7 @@ public boolean onCommand(String command, String ipFrom, DataOutputStream dos)
{
LOGGER.info("Received from " + ipFrom + ":" +
command.substring(0, Math.min(command.length(), 1024)));

boolean keepProcessing = false;

// Possible commands:
Expand Down Expand Up @@ -849,6 +855,7 @@ private void checkForMissionCommand() throws Exception
{
missionInit.getClientAgentConnection().setAgentIPAddress(comip.ipAddress);
LOGGER.info("Mission received: " + missionInit.getMission().getAbout().getSummary());
LOGGER.debug(missionMessage);
csMachine.currentMissionInit = missionInit;
ClientStateMachine.this.createMissionControlSocket();
// Move on to next state:
Expand Down Expand Up @@ -1178,6 +1185,20 @@ protected void execute() {

boolean needsNewWorld = worldGenerator != null && worldGenerator.shouldCreateWorld(currentMissionInit(), genOptions);
boolean worldCurrentlyExists = world != null;
MinecraftServerConnection serverCon = currentMissionInit().getMinecraftServerConnection();
LOGGER.debug("checking for server connection in mission init: ", serverCon);
if (serverCon != null && serverCon.getAddress() != null && serverCon.getPort() != 0) {
LOGGER.debug("server connection info is provided " + serverCon.toString() +
" assume world already exists");
if (MinecraftClient.getInstance().isIntegratedServerRunning()){
LOGGER.debug("stopping integrated server");
MinecraftClient.getInstance().getServer().stop(true);
}
needsNewWorld = false;
worldCurrentlyExists = true;
episodeHasCompleted(ClientState.WAITING_FOR_SERVER_READY);
return;
}
List<AgentSection> agents = currentMissionInit().getMission().getAgentSection();
String agentName = agents.get(currentMissionInit().getClientRole()).getName();
if (worldCurrentlyExists) {
Expand All @@ -1203,10 +1224,10 @@ protected void execute() {
LOGGER.debug("needsNewWorld && worldCurrentlyExists");
episodeHasCompleted(ClientState.PAUSING_OLD_SERVER);
} else {
// We want a new world, and there is currently nothing running,
// so jump to world creation:
LOGGER.debug("needsNewWorld && not worldCurrentlyExists");
episodeHasCompleted(ClientState.CREATING_NEW_WORLD);
// We want a new world, and there is currently nothing running,
// so jump to world creation:
LOGGER.debug("needsNewWorld && not worldCurrentlyExists");
episodeHasCompleted(ClientState.CREATING_NEW_WORLD);
}
} else { // not needNewWorld
LOGGER.debug("not need new world");
Expand All @@ -1217,7 +1238,7 @@ protected void execute() {
as.getAgentStart().setPlacement(null);

ClientPlayerEntity player = MinecraftClient.getInstance().player;
if (player.isDead()) player.requestRespawn();
if (player != null && player.isDead()) player.requestRespawn();
/*
if (ClientStateMachine.this.serverHandlers == null) {
// We need to use the server's MissionHandlers here:
Expand All @@ -1234,6 +1255,7 @@ protected void execute() {
// boolean isConnectedToRealm = MinecraftClient.getInstance().isConnectedToRealms();
boolean isConnectedToLocal = MinecraftClient.getInstance().isConnectedToLocalServer();
boolean isIntegratedServerRunning = MinecraftClient.getInstance().isIntegratedServerRunning();
boolean isConnectedToRemote = !isIntegratedServerRunning && player != null;
// LOGGER.debug("isConnectedToRealm: " + isConnectedToRealm);
LOGGER.debug("isConnectedToLocal: " + isConnectedToLocal);
LOGGER.debug("isIntegratedServerRunning: " + isIntegratedServerRunning);
Expand All @@ -1258,21 +1280,8 @@ public void run() {
}
});
// Skip all the map loading stuff and go straight to waiting for the server:
episodeHasCompleted(ClientState.WAITING_FOR_SERVER_READY);
} else {
LOGGER.debug("sending mission to remote Server");
HashMap<String, String> map = new HashMap<String, String>();
// convert mission init with jaxb serializer
try {
String xmlData = SchemaHelper.serialiseObject(currentMissionInit(), MissionInit.class);
map.put("MissionInit", xmlData);
} catch (JAXBException e) {
episodeHasCompletedWithErrors(ClientState.ERROR_NO_WORLD, "exception while converting mission init to xml" + e.getMessage());
}
// send mission init to server
ClientPlayNetworking.send(new MessagePayload(new VereyaMessage(VereyaMessageType.CLIENT_MISSION_INIT, 0, map)));
episodeHasCompleted(ClientState.WAITING_FOR_SERVER_READY);
}
episodeHasCompleted(ClientState.WAITING_FOR_SERVER_READY);
} else { // not needNewWorld and no world: error
// Mission has requested no new world, but there is no current world to play in - this is an error:
episodeHasCompletedWithErrors(ClientState.ERROR_NO_WORLD, "We have no world to play in - check that your ServerHandlers section contains a world generator");
Expand All @@ -1294,6 +1303,7 @@ public class WaitingForServerEpisode extends ErrorAwareEpisode implements IVerey
boolean waitingForChunk = false;
boolean waitingForPlayer = true;
private boolean chunkReady = false;
private boolean sendToRemote = false;

protected WaitingForServerEpisode(ClientStateMachine machine)
{
Expand Down Expand Up @@ -1388,8 +1398,9 @@ public void onClientTick(MinecraftClient client)

if (agents.size() > 1 && currentMissionInit().getClientRole() != 0)
{
/*
throw new RuntimeException("Not implemented");
/*
// We are waiting to join an out-of-process server. Need to pay attention to what happens -
// if we can't join, for any reason, we should abort the mission.
GuiScreen screen = Minecraft.getMinecraft().currentScreen;
Expand All @@ -1413,8 +1424,7 @@ public void onClientTick(MinecraftClient client)
}

@Override
protected void execute() throws Exception
{
protected void execute() throws Exception {
totalTicks = 0;

// Minecraft.getMinecraft().displayGuiScreen(null); // Clear any menu screen that might confuse things.
Expand All @@ -1423,7 +1433,23 @@ protected void execute() throws Exception
//if (agents == null || agents.size() <= currentMissionInit().getClientRole())
// throw new Exception("No agent section for us!"); // TODO
this.agentName = agents.get(currentMissionInit().getClientRole()).getName();
MinecraftServerConnection serverCon = currentMissionInit().getMinecraftServerConnection();
PlayerEntity player = MinecraftClient.getInstance().player;
boolean isConnectedToRemote = player != null && !MinecraftClient.getInstance().isIntegratedServerRunning();
if (!isConnectedToRemote && serverCon != null && serverCon.getAddress() != null && serverCon.getPort() != 0) {
ServerAddress srv = new ServerAddress(serverCon.getAddress(), serverCon.getPort());
LOGGER.debug("connecting to " + srv.getAddress() + ":" + srv.getPort());
Screen parentScreen = new GameMenuScreen(true);
ServerInfo srvInfo = new ServerInfo("local", srv.getAddress(), ServerInfo.ServerType.LAN);
ConnectScreen.connect(parentScreen, MinecraftClient.getInstance(), srv, srvInfo, true, null);
this.sendToRemote = true;
}


if (isConnectedToRemote && currentMissionInit().getClientRole() == 0) {
this.sendToRemote = true;
}
/*
if (agents.size() > 1 && currentMissionInit().getClientRole() != 0)
{
// Multi-agent mission, we should be joining a server.
Expand All @@ -1445,11 +1471,25 @@ protected void execute() throws Exception
}
this.waitingForPlayer = false;
LOGGER.info("player exists and names match");
}
}*/
}

protected void handleLan()
{
if (this.sendToRemote) {
LOGGER.debug("sending mission to remote Server");
HashMap<String, String> map = new HashMap<String, String>();
// convert mission init with jaxb serializer
try {
String xmlData = SchemaHelper.serialiseObject(currentMissionInit(), MissionInit.class);
map.put("MissionInit", xmlData);
} catch (JAXBException e) {
episodeHasCompletedWithErrors(ClientState.ERROR_NO_WORLD, "exception while converting mission init to xml" + e.getMessage());
}
// send mission init to server
ClientPlayNetworking.send(new MessagePayload(new VereyaMessage(VereyaMessageType.CLIENT_MISSION_INIT, 0, map)));
sendToRemote = false;
}
// Get our name from the Mission:
/*List<AgentSection> agents = currentMissionInit().getMission().getAgentSection();
this.agentName = agents.get(currentMissionInit().getClientRole()).getName();
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/io/singularitynet/Client/VereyaModClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,12 @@ public void onKey(long window, int key, int scancode, int action, int modifiers)

@Override
public void onInitializeClient() {
LOGGER.info("initialising vereya mod client");
this.stateMachine = new ClientStateMachine(ClientState.WAITING_FOR_MOD_READY, (IMalmoModClient) this);
// subscribe to setScreen event
ScreenEvents.SET_SCREEN.register(this);
PayloadTypeRegistry.playS2C().register(MessagePayload.ID, MessagePayload.CODEC);
// register the instance for messages from Server to the Client
// this is registered in VereyaModServer even on client
// PayloadTypeRegistry.playS2C().register(MessagePayload.ID, MessagePayload.CODEC);
ClientPlayNetworking.registerGlobalReceiver(MessagePayload.ID,
(payload, context) -> { SidesMessageHandler.server2client.onMessage(payload, context) ; });
}
Expand Down
Loading

0 comments on commit e69599b

Please sign in to comment.