NeoForge Platform Support (#3781)

* Initial work on Forge platform

* Rework modded platforms to use a common module

* Add support for integrated worlds on modded platforms

* Fix classload errors and move mixins to shared module

* Fix Fabric mixins and check min height in mod world manager

* Add Forge command support

* Add back modrinth publishing

* Don't apply application plugin to shared mod sources

* Fix docs

* Delete unused class

* Clean up repositories

* - Update to 1.20.2
- set custom refmap name
- fixed console commands crashing the server (hasPermission now accepts CommandSourceStack instead of Player)
- Forge wants fastutil relocated, so be it

Current issues:
- ClassNotFound exceptions with classes that are clearly present

* - Fix ClassNotFound errors on Forge due to weird Classloader
- Dont relocate gson

* merge upstream

* oh no

* Bump lombok, architectury-loom

* init: neoforge 1.20.4 support

* NeoForge builds

Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com>

* Archive neoforge artifacts

* transformForge -> transformNeoForge

* Neoforge boots!
* Fix mixins on neoforge
* Update build/pr file names
* Update mods.toml to new neoforge standard
* Fix refmap naming

* more fixes
- no need to include gson
- cleanup nullable/nonnull annotations
- add more info to geyser dumps on neoforge

* yeet platform executor

* yet another temp branch to figure out the runServer task

* yeet transitive dependency, that cant be right

* Attempt at getting the runServer task to work, part two

* Revert the changes for the runServer task, try and shut down the injector

* Remove spigot weird bug workaround, shut down properly
Also add a compileOnly dependency for the mod module to get rid of spammy false warnings

* Update to latest restart changes
- fix duplicate nodes crashing neoforge
- connector -> geyser in GeyserModCommandExecutor
- create command manager early to fix issues with permission gather event

* Consistent NeoForge spelling, move some dependencies to the version toml

* Add lombok to version catalogue

* Add plugins to version catalogue

* revert move to buildSrc

* Create `assets/geyser/icon.png` to reference icon from a single file on standalone/neoforge/fabric

* add fabric permissions api to libs.versions.toml

---------

Signed-off-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com>
Co-authored-by: onebeastchris <github@onechris.mozmail.com>
Co-authored-by: Joshua Castle <26531652+Kas-tle@users.noreply.github.com>
This commit is contained in:
Redned 2024-02-23 16:58:39 +00:00 committed by GitHub
parent aca368e332
commit 97fc2de42f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
55 changed files with 1504 additions and 243 deletions

View file

@ -1,124 +0,0 @@
import net.fabricmc.loom.task.RemapJarTask
plugins {
id("fabric-loom") version "1.0-SNAPSHOT"
id("com.modrinth.minotaur") version "2.+"
}
dependencies {
//to change the versions see the gradle.properties file
minecraft(libs.fabric.minecraft)
mappings(loom.officialMojangMappings())
modImplementation(libs.fabric.loader)
// Fabric API. This is technically optional, but you probably want it anyway.
modImplementation(libs.fabric.api)
// This should be in the libs TOML, but something about modImplementation AND include just doesn't work
include(modImplementation("me.lucko", "fabric-permissions-api", "0.2-SNAPSHOT"))
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
api(projects.core)
shadow(projects.core) {
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.code.gson", module = "gson")
exclude(group = "org.slf4j")
exclude(group = "com.nukkitx.fastutil")
exclude(group = "io.netty.incubator")
}
}
loom {
mixin.defaultRefmapName.set("geyser-fabric-refmap.json")
}
repositories {
mavenLocal()
maven("https://repo.opencollab.dev/maven-releases/")
maven("https://repo.opencollab.dev/maven-snapshots/")
maven("https://jitpack.io")
maven("https://oss.sonatype.org/content/repositories/snapshots/")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
application {
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
}
tasks {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this task, sources will not be generated.
sourcesJar {
archiveClassifier.set("sources")
from(sourceSets.main.get().allSource)
}
shadowJar {
// Mirrors the example fabric project, otherwise tons of dependencies are shaded that shouldn't be
configurations = listOf(project.configurations.shadow.get())
// The remapped shadowJar is the final desired Geyser-Fabric.jar
archiveVersion.set(project.version.toString())
archiveClassifier.set("shaded")
relocate("org.objectweb.asm", "org.geysermc.relocate.asm")
relocate("org.yaml", "org.geysermc.relocate.yaml") // https://github.com/CardboardPowered/cardboard/issues/139
relocate("com.fasterxml.jackson", "org.geysermc.relocate.jackson")
relocate("net.kyori", "org.geysermc.relocate.kyori")
dependencies {
// Exclude everything EXCEPT some DNS stuff required for HAProxy
exclude(dependency("io.netty:netty-transport-classes-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-epoll:.*"))
exclude(dependency("io.netty:netty-transport-native-unix-common:.*"))
exclude(dependency("io.netty:netty-transport-classes-kqueue:.*"))
exclude(dependency("io.netty:netty-transport-native-kqueue:.*"))
exclude(dependency("io.netty:netty-handler:.*"))
exclude(dependency("io.netty:netty-common:.*"))
exclude(dependency("io.netty:netty-buffer:.*"))
exclude(dependency("io.netty:netty-resolver:.*"))
exclude(dependency("io.netty:netty-transport:.*"))
exclude(dependency("io.netty:netty-codec:.*"))
exclude(dependency("io.netty:netty-resolver-dns:.*"))
exclude(dependency("io.netty:netty-resolver-dns-native-macos:.*"))
}
}
remapJar {
dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile)
archiveBaseName.set("Geyser-Fabric")
archiveVersion.set("")
archiveClassifier.set("")
}
register("remapModrinthJar", RemapJarTask::class) {
dependsOn(shadowJar)
inputFile.set(shadowJar.get().archiveFile)
archiveBaseName.set("geyser-fabric")
archiveVersion.set(project.version.toString() + "+build." + System.getenv("GITHUB_RUN_NUMBER"))
archiveClassifier.set("")
}
}
modrinth {
token.set(System.getenv("MODRINTH_TOKEN")) // Even though this is the default value, apparently this prevents GitHub Actions caching the token?
projectId.set("wKkoqHrH")
versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER"))
versionType.set("beta")
changelog.set("A changelog can be found at https://github.com/GeyserMC/Geyser/commits")
syncBodyFrom.set(rootProject.file("README.md").readText())
uploadFile.set(tasks.getByPath("remapModrinthJar"))
gameVersions.addAll("1.20.4")
loaders.add("fabric")
failSilently.set(true)
dependencies {
required.project("fabric-api")
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

View file

@ -1,15 +0,0 @@
{
"required": true,
"package": "org.geysermc.geyser.platform.fabric.mixin",
"compatibilityLevel": "JAVA_16",
"refmap": "geyser-fabric-refmap.json",
"client": [
"client.IntegratedServerMixin"
],
"server": [
"server.MinecraftDedicatedServerMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -0,0 +1,15 @@
architectury {
common("neoforge", "fabric")
}
loom {
mixin.defaultRefmapName.set("geyser-refmap.json")
}
dependencies {
api(projects.core)
compileOnly(libs.mixin)
// Only here to suppress "unknown enum constant EnvType.CLIENT" warnings. DO NOT USE!
compileOnly(libs.fabric.loader)
}

View file

@ -0,0 +1,49 @@
plugins {
application
}
architectury {
platformSetupLoomIde()
fabric()
}
dependencies {
modImplementation(libs.fabric.loader)
modApi(libs.fabric.api)
api(project(":mod", configuration = "namedElements"))
shadow(project(path = ":mod", configuration = "transformProductionFabric")) {
isTransitive = false
}
shadow(projects.core) {
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.code.gson", module = "gson")
exclude(group = "org.slf4j")
exclude(group = "com.nukkitx.fastutil")
exclude(group = "io.netty.incubator")
}
modImplementation(libs.fabric.permissions)
include(libs.fabric.permissions)
}
application {
mainClass.set("org.geysermc.geyser.platform.fabric.GeyserFabricMain")
}
tasks {
remapJar {
archiveBaseName.set("Geyser-Fabric")
}
remapModrinthJar {
archiveBaseName.set("geyser-fabric")
}
}
modrinth {
loaders.add("fabric")
dependencies {
required.project("fabric-api")
}
}

View file

@ -0,0 +1 @@
loom.platform=fabric

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
import org.checkerframework.checker.nullness.qual.NonNull;
public class GeyserFabricBootstrap extends GeyserModBootstrap implements ModInitializer {
public GeyserFabricBootstrap() {
super(new GeyserFabricPlatform());
}
@Override
public void onInitialize() {
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
// Set as an event, so we can get the proper IP and port if needed
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
this.setServer(server);
onGeyserEnable();
});
}
// These are only registered once
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onGeyserShutdown());
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserModUpdateListener.onPlayReady(handler.getPlayer()));
this.onGeyserInitialize();
}
@Override
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
return Permissions.check(source, permissionNode);
}
@Override
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
return Permissions.check(source, permissionNode, permissionLevel);
}
}

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.server.MinecraftServer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Optional;
public class GeyserFabricPlatform implements GeyserModPlatform {
private final ModContainer mod;
public GeyserFabricPlatform() {
this.mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
}
@Override
public @NonNull PlatformType platformType() {
return PlatformType.FABRIC;
}
@Override
public @NonNull String configPath() {
return "Geyser-Fabric";
}
@Override
public @NonNull Path dataFolder(@NonNull String modId) {
return FabricLoader.getInstance().getConfigDir().resolve(modId);
}
@Override
public @NonNull BootstrapDumpInfo dumpInfo(@NonNull MinecraftServer server) {
return new GeyserFabricDumpInfo(server);
}
@Override
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
if (floodgate.isPresent()) {
Path floodgateDataFolder = FabricLoader.getInstance().getConfigDir().resolve("floodgate");
bootstrap.getGeyserConfig().loadFloodgate(bootstrap, floodgateDataFolder);
return true;
}
return false;
}
@Override
public @Nullable InputStream resolveResource(@NonNull String resource) {
// We need to handle this differently, because Fabric shares the classloader across multiple mods
Path path = this.mod.findPath(resource).orElse(null);
if (path == null) {
return null;
}
try {
return path.getFileSystem()
.provider()
.newInputStream(path);
} catch (IOException e) {
return null;
}
}
}

View file

@ -9,18 +9,18 @@
],
"contact": {
"website": "${url}",
"repo": "https://github.com/GeyserMC/Geyser-Fabric"
"repo": "https://github.com/GeyserMC/Geyser"
},
"license": "MIT",
"icon": "assets/geyser-fabric/icon.png",
"icon": "assets/geyser/icon.png",
"environment": "*",
"entrypoints": {
"main": [
"org.geysermc.geyser.platform.fabric.GeyserFabricMod"
"org.geysermc.geyser.platform.fabric.GeyserFabricBootstrap"
]
},
"mixins": [
"geyser-fabric.mixins.json"
"geyser.mixins.json"
],
"depends": {
"fabricloader": ">=0.15.2",

View file

@ -0,0 +1,52 @@
plugins {
application
}
architectury {
platformSetupLoomIde()
neoForge()
}
dependencies {
// See https://github.com/google/guava/issues/6618
modules {
module("com.google.guava:listenablefuture") {
replacedBy("com.google.guava:guava", "listenablefuture is part of guava")
}
}
neoForge(libs.neoforge.minecraft)
api(project(":mod", configuration = "namedElements"))
shadow(project(path = ":mod", configuration = "transformProductionNeoForge")) {
isTransitive = false
}
shadow(projects.core) {
exclude(group = "com.google.guava", module = "guava")
exclude(group = "com.google.code.gson", module = "gson")
exclude(group = "org.slf4j")
exclude(group = "io.netty.incubator")
}
}
application {
mainClass.set("org.geysermc.geyser.platform.forge.GeyserNeoForgeMain")
}
tasks {
shadowJar {
relocate("it.unimi.dsi.fastutil", "org.geysermc.relocate.fastutil")
}
remapJar {
archiveBaseName.set("Geyser-NeoForge")
}
remapModrinthJar {
archiveBaseName.set("geyser-neoforge")
}
}
modrinth {
loaders.add("neoforge")
}

View file

@ -0,0 +1 @@
loom.platform=neoforge

View file

@ -0,0 +1,83 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.GeyserModUpdateListener;
@Mod(ModConstants.MOD_ID)
public class GeyserNeoForgeBootstrap extends GeyserModBootstrap {
private final GeyserNeoForgePermissionHandler permissionHandler = new GeyserNeoForgePermissionHandler();
public GeyserNeoForgeBootstrap() {
super(new GeyserNeoForgePlatform());
if (FMLLoader.getDist() == Dist.DEDICATED_SERVER) {
// Set as an event so we can get the proper IP and port if needed
NeoForge.EVENT_BUS.addListener(this::onServerStarted);
}
NeoForge.EVENT_BUS.addListener(this::onServerStopping);
NeoForge.EVENT_BUS.addListener(this::onPlayerJoin);
NeoForge.EVENT_BUS.addListener(this.permissionHandler::onPermissionGather);
this.onGeyserInitialize();
}
private void onServerStarted(ServerStartedEvent event) {
this.setServer(event.getServer());
this.onGeyserEnable();
}
private void onServerStopping(ServerStoppingEvent event) {
this.onGeyserShutdown();
}
private void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
GeyserModUpdateListener.onPlayReady(event.getEntity());
}
@Override
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
return this.permissionHandler.hasPermission(source, permissionNode);
}
@Override
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
return this.permissionHandler.hasPermission(source, permissionNode, permissionLevel);
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.minecraft.server.MinecraftServer;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.fml.ModList;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforgespi.language.IModInfo;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.text.AsteriskSerializer;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
@Getter
public class GeyserNeoForgeDumpInfo extends BootstrapDumpInfo {
private final String platformName;
private final String platformVersion;
private final String minecraftVersion;
private final Dist dist;
@AsteriskSerializer.Asterisk(isIp = true)
private final String serverIP;
private final int serverPort;
private final boolean onlineMode;
private final List<ModInfo> mods;
public GeyserNeoForgeDumpInfo(MinecraftServer server) {
this.platformName = FMLLoader.launcherHandlerName();
this.platformVersion = FMLLoader.versionInfo().neoForgeVersion();
this.minecraftVersion = FMLLoader.versionInfo().mcVersion();
this.dist = FMLLoader.getDist();
this.serverIP = server.getLocalIp() == null ? "unknown" : server.getLocalIp();
this.serverPort = server.getPort();
this.onlineMode = server.usesAuthentication();
this.mods = new ArrayList<>();
for (IModInfo mod : ModList.get().getMods()) {
this.mods.add(new ModInfo(
ModList.get().isLoaded(mod.getModId()),
mod.getModId(),
mod.getVersion().toString(),
mod.getModURL().map(URL::toString).orElse("")
));
}
}
@Getter
@AllArgsConstructor
public static class ModInfo {
public boolean enabled;
public String name;
public String version;
public String url;
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import org.geysermc.geyser.GeyserMain;
public class GeyserNeoForgeMain extends GeyserMain {
public static void main(String[] args) {
new GeyserNeoForgeMain().displayMessage();
}
@Override
public String getPluginType() {
return "NeoForge";
}
@Override
public String getPluginFolder() {
return "mods";
}
}

View file

@ -0,0 +1,149 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.neoforged.neoforge.server.permission.PermissionAPI;
import net.neoforged.neoforge.server.permission.events.PermissionGatherEvent;
import net.neoforged.neoforge.server.permission.nodes.PermissionDynamicContextKey;
import net.neoforged.neoforge.server.permission.nodes.PermissionNode;
import net.neoforged.neoforge.server.permission.nodes.PermissionType;
import net.neoforged.neoforge.server.permission.nodes.PermissionTypes;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.command.GeyserCommandManager;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
public class GeyserNeoForgePermissionHandler {
private static final Constructor<?> PERMISSION_NODE_CONSTRUCTOR;
static {
try {
@SuppressWarnings("rawtypes")
Constructor<PermissionNode> constructor = PermissionNode.class.getDeclaredConstructor(
String.class,
PermissionType.class,
PermissionNode.PermissionResolver.class,
PermissionDynamicContextKey[].class
);
constructor.setAccessible(true);
PERMISSION_NODE_CONSTRUCTOR = constructor;
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to construct PermissionNode!", e);
}
}
private final Map<String, PermissionNode<Boolean>> permissionNodes = new HashMap<>();
public void onPermissionGather(PermissionGatherEvent.Nodes event) {
this.registerNode(Constants.UPDATE_PERMISSION, event);
GeyserCommandManager commandManager = GeyserImpl.getInstance().commandManager();
for (Map.Entry<String, Command> entry : commandManager.commands().entrySet()) {
Command command = entry.getValue();
// Don't register aliases
if (!command.name().equals(entry.getKey())) {
continue;
}
this.registerNode(command.permission(), event);
}
for (Map<String, Command> commands : commandManager.extensionCommands().values()) {
for (Map.Entry<String, Command> entry : commands.entrySet()) {
Command command = entry.getValue();
// Don't register aliases
if (!command.name().equals(entry.getKey())) {
continue;
}
this.registerNode(command.permission(), event);
}
}
}
public boolean hasPermission(@NonNull Player source, @NonNull String permissionNode) {
PermissionNode<Boolean> node = this.permissionNodes.get(permissionNode);
if (node == null) {
GeyserImpl.getInstance().getLogger().warning("Unable to find permission node " + permissionNode);
return false;
}
return PermissionAPI.getPermission((ServerPlayer) source, node);
}
public boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel) {
if (!source.isPlayer()) {
return true;
}
assert source.getPlayer() != null;
boolean permission = this.hasPermission(source.getPlayer(), permissionNode);
if (!permission) {
return source.getPlayer().hasPermissions(permissionLevel);
}
return true;
}
private void registerNode(String node, PermissionGatherEvent.Nodes event) {
PermissionNode<Boolean> permissionNode = this.createNode(node);
// NeoForge likes to crash if you try and register a duplicate node
if (!event.getNodes().contains(permissionNode)) {
event.addNodes(permissionNode);
this.permissionNodes.put(node, permissionNode);
}
}
@SuppressWarnings("unchecked")
private PermissionNode<Boolean> createNode(String node) {
// The typical constructors in PermissionNode require a
// mod id, which means our permission nodes end up becoming
// geyser_neoforge.<node> instead of just <node>. We work around
// this by using reflection to access the constructor that
// doesn't require a mod id or ResourceLocation.
try {
return (PermissionNode<Boolean>) PERMISSION_NODE_CONSTRUCTOR.newInstance(
node,
PermissionTypes.BOOLEAN,
(PermissionNode.PermissionResolver<Boolean>) (player, playerUUID, context) -> false,
new PermissionDynamicContextKey[0]
);
} catch (Exception e) {
throw new RuntimeException("Unable to create permission node " + node, e);
}
}
}

View file

@ -0,0 +1,72 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
import net.minecraft.server.MinecraftServer;
import net.neoforged.fml.loading.FMLPaths;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import java.io.InputStream;
import java.nio.file.Path;
public class GeyserNeoForgePlatform implements GeyserModPlatform {
@Override
public @NonNull PlatformType platformType() {
return PlatformType.NEOFORGE;
}
@Override
public @NonNull String configPath() {
return "Geyser-NeoForge";
}
@Override
public @NonNull Path dataFolder(@NonNull String modId) {
return FMLPaths.CONFIGDIR.get().resolve(modId);
}
@Override
public @NonNull BootstrapDumpInfo dumpInfo(@NonNull MinecraftServer server) {
return new GeyserNeoForgeDumpInfo(server);
}
@Override
public boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap) {
return false; // No Floodgate mod for NeoForge yet
}
@Override
public @Nullable InputStream resolveResource(@NonNull String resource) {
return GeyserBootstrap.class.getClassLoader().getResourceAsStream(resource);
}
}

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.neoforge;
public class ModConstants {
public static final String MOD_ID = "geyser_neoforge";
}

View file

@ -0,0 +1,25 @@
modLoader="javafml"
loaderVersion="[1,)"
license="MIT"
[[mods]]
modId="geyser_neoforge"
version="${version}"
displayName="Geyser"
displayURL="https://geysermc.org/"
logoFile= "../assets/geyser/icon.png"
authors="GeyserMC"
description="${description}"
[[mixins]]
config = "geyser.mixins.json"
[[dependencies.geyser_neoforge]]
modId="neoforge"
type="required"
versionRange="[20.4.48-beta,)"
ordering="NONE"
side="BOTH"
[[dependencies.geyser_neoforge]]
modId="minecraft"
type="required"
versionRange="[1.20,1.21)"
ordering="NONE"
side="BOTH"

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.mod;
import io.netty.channel.ChannelFuture;
import java.util.List;
/**
* Represents a getter to the server channels in the connection listener class.
*/
public interface GeyserChannelGetter {
/**
* Returns the channels.
*
* @return The channels.
*/
List<ChannelFuture> geyser$getChannels();
}

View file

@ -23,21 +23,17 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player;
import org.apache.logging.log4j.LogManager;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
@ -46,7 +42,6 @@ import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.api.command.Command;
import org.geysermc.geyser.api.extension.Extension;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandManager;
import org.geysermc.geyser.configuration.GeyserConfiguration;
@ -54,8 +49,9 @@ import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.level.WorldManager;
import org.geysermc.geyser.ping.GeyserLegacyPingPassthrough;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.geysermc.geyser.platform.fabric.command.GeyserFabricCommandExecutor;
import org.geysermc.geyser.platform.fabric.world.GeyserFabricWorldManager;
import org.geysermc.geyser.platform.mod.command.GeyserModCommandExecutor;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import org.geysermc.geyser.platform.mod.world.GeyserModWorldManager;
import org.geysermc.geyser.text.GeyserLocale;
import org.geysermc.geyser.util.FileUtils;
@ -64,58 +60,46 @@ import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
@RequiredArgsConstructor
public abstract class GeyserModBootstrap implements GeyserBootstrap {
@Getter
private static GeyserFabricMod instance;
private static GeyserModBootstrap instance;
private final GeyserModPlatform platform;
private GeyserImpl geyser;
private ModContainer mod;
private Path dataFolder;
@Setter
private MinecraftServer server;
private GeyserCommandManager geyserCommandManager;
private GeyserFabricConfiguration geyserConfig;
private GeyserFabricLogger geyserLogger;
private GeyserModConfiguration geyserConfig;
private GeyserModInjector geyserInjector;
private GeyserModLogger geyserLogger;
private IGeyserPingPassthrough geyserPingPassthrough;
private WorldManager geyserWorldManager;
@Override
public void onInitialize() {
instance = this;
mod = FabricLoader.getInstance().getModContainer("geyser-fabric").orElseThrow();
onGeyserInitialize();
}
@Override
public void onGeyserInitialize() {
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.SERVER) {
// Set as an event, so we can get the proper IP and port if needed
ServerLifecycleEvents.SERVER_STARTED.register((server) -> {
this.server = server;
onGeyserEnable();
});
}
// These are only registered once
ServerLifecycleEvents.SERVER_STOPPING.register((server) -> onGeyserShutdown());
ServerPlayConnectionEvents.JOIN.register((handler, $, $$) -> GeyserFabricUpdateListener.onPlayReady(handler));
dataFolder = FabricLoader.getInstance().getConfigDir().resolve("Geyser-Fabric");
instance = this;
dataFolder = this.platform.dataFolder(this.platform.configPath());
GeyserLocale.init(this);
if (!loadConfig()) {
return;
}
this.geyserLogger = new GeyserFabricLogger(geyserConfig.isDebugMode());
this.geyserLogger = new GeyserModLogger(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
this.geyser = GeyserImpl.load(PlatformType.FABRIC, this);
this.geyser = GeyserImpl.load(this.platform.platformType(), this);
// Create command manager here, since the permission handler on neo needs it
this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init();
}
@Override
public void onGeyserEnable() {
if (GeyserImpl.getInstance().isReloading()) {
if (!loadConfig()) {
@ -123,35 +107,37 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
}
this.geyserLogger.setDebug(geyserConfig.isDebugMode());
GeyserConfiguration.checkGeyserConfiguration(geyserConfig, geyserLogger);
} else {
this.geyserCommandManager = new GeyserCommandManager(geyser);
this.geyserCommandManager.init();
}
GeyserImpl.start();
if (geyserConfig.isLegacyPingPassthrough()) {
this.geyserPingPassthrough = GeyserLegacyPingPassthrough.init(geyser);
} else {
this.geyserPingPassthrough = new ModPingPassthrough(server, geyserLogger);
}
GeyserImpl.start();
// No need to re-register commands, or re-recreate the world manager when reloading
// No need to re-register commands, or try to re-inject
if (GeyserImpl.getInstance().isReloading()) {
return;
}
this.geyserWorldManager = new GeyserFabricWorldManager(server);
this.geyserWorldManager = new GeyserModWorldManager(server);
// We want to do this late in the server startup process to allow other mods
// To do their job injecting, then connect into *that*
this.geyserInjector = new GeyserModInjector(server, this.platform);
this.geyserInjector.initializeLocalChannel(this);
// Start command building
// Set just "geyser" as the help command
GeyserFabricCommandExecutor helpExecutor = new GeyserFabricCommandExecutor(geyser,
GeyserModCommandExecutor helpExecutor = new GeyserModCommandExecutor(geyser,
(GeyserCommand) geyser.commandManager().getCommands().get("help"));
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal("geyser").executes(helpExecutor);
// Register all subcommands as valid
for (Map.Entry<String, Command> command : geyser.commandManager().getCommands().entrySet()) {
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(geyser, (GeyserCommand) command.getValue());
GeyserModCommandExecutor executor = new GeyserModCommandExecutor(geyser, (GeyserCommand) command.getValue());
builder.then(Commands.literal(command.getKey())
.executes(executor)
// Could also test for Bedrock but depending on when this is called it may backfire
@ -171,12 +157,12 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
}
// Register help command for just "/<extensionId>"
GeyserFabricCommandExecutor extensionHelpExecutor = new GeyserFabricCommandExecutor(geyser,
GeyserModCommandExecutor extensionHelpExecutor = new GeyserModCommandExecutor(geyser,
(GeyserCommand) extensionCommands.get("help"));
LiteralArgumentBuilder<CommandSourceStack> extCmdBuilder = Commands.literal(extensionMapEntry.getKey().description().id()).executes(extensionHelpExecutor);
for (Map.Entry<String, Command> command : extensionCommands.entrySet()) {
GeyserFabricCommandExecutor executor = new GeyserFabricCommandExecutor(geyser, (GeyserCommand) command.getValue());
GeyserModCommandExecutor executor = new GeyserModCommandExecutor(geyser, (GeyserCommand) command.getValue());
extCmdBuilder.then(Commands.literal(command.getKey())
.executes(executor)
.requires(executor::testPermission)
@ -201,11 +187,14 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
geyser.shutdown();
geyser = null;
}
this.server = null;
if (geyserInjector != null) {
geyserInjector.shutdown();
this.server = null;
}
}
@Override
public GeyserConfiguration getGeyserConfig() {
public GeyserModConfiguration getGeyserConfig() {
return geyserConfig;
}
@ -236,7 +225,7 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
@Override
public BootstrapDumpInfo getDumpInfo() {
return new GeyserFabricDumpInfo(server);
return this.platform.dumpInfo(this.server);
}
@Override
@ -258,32 +247,19 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
@Override
public boolean testFloodgatePluginPresent() {
Optional<ModContainer> floodgate = FabricLoader.getInstance().getModContainer("floodgate");
if (floodgate.isPresent()) {
geyserConfig.loadFloodgate(this, floodgate.orElse(null));
return true;
}
return false;
return this.platform.testFloodgatePluginPresent(this);
}
@Nullable
@Override
public InputStream getResourceOrNull(String resource) {
// We need to handle this differently, because Fabric shares the classloader across multiple mods
Path path = this.mod.findPath(resource).orElse(null);
if (path == null) {
return null;
}
try {
return path.getFileSystem()
.provider()
.newInputStream(path);
} catch (IOException e) {
return null;
}
return this.platform.resolveResource(resource);
}
public abstract boolean hasPermission(@NonNull Player source, @NonNull String permissionNode);
public abstract boolean hasPermission(@NonNull CommandSourceStack source, @NonNull String permissionNode, int permissionLevel);
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean loadConfig() {
try {
@ -294,10 +270,10 @@ public class GeyserFabricMod implements ModInitializer, GeyserBootstrap {
File configFile = FileUtils.fileOrCopiedFromResource(dataFolder.resolve("config.yml").toFile(), "config.yml",
(x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()), this);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserFabricConfiguration.class);
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserModConfiguration.class);
return true;
} catch (IOException ex) {
LogManager.getLogger("geyser-fabric").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
LogManager.getLogger("geyser").error(GeyserLocale.getLocaleStringLog("geyser.config.failed"), ex);
ex.printStackTrace();
return false;
}

View file

@ -0,0 +1,56 @@
/*
* Copyright (c) 2019-2021 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.mod;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import net.minecraft.network.protocol.login.ClientboundGameProfilePacket;
import net.minecraft.network.protocol.login.ClientboundLoginCompressionPacket;
/**
* Disables the compression packet (and the compression handlers from being added to the pipeline) for Geyser clients
* that won't be receiving the data over the network.
* <p>
* As of 1.8 - 1.17.1, compression is enabled in the Netty pipeline by adding a listener after a packet is written.
* If we simply "cancel" or don't forward the packet, then the listener is never called.
*/
public class GeyserModCompressionDisabler extends ChannelOutboundHandlerAdapter {
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
Class<?> msgClass = msg.getClass();
// Don't let any compression packet get through
if (!ClientboundLoginCompressionPacket.class.isAssignableFrom(msgClass)) {
if (ClientboundGameProfilePacket.class.isAssignableFrom(msgClass)) {
// We're past the point that a compression packet can be sent, so we can safely yeet ourselves away
ctx.channel().pipeline().remove(this);
}
super.write(ctx, msg, promise);
}
}
}

View file

@ -23,23 +23,20 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import com.fasterxml.jackson.annotation.JsonIgnore;
import net.fabricmc.loader.api.FabricLoader;
import net.fabricmc.loader.api.ModContainer;
import org.geysermc.geyser.FloodgateKeyLoader;
import org.geysermc.geyser.configuration.GeyserJacksonConfiguration;
import java.nio.file.Path;
public class GeyserFabricConfiguration extends GeyserJacksonConfiguration {
public class GeyserModConfiguration extends GeyserJacksonConfiguration {
@JsonIgnore
private Path floodgateKeyPath;
public void loadFloodgate(GeyserFabricMod geyser, ModContainer floodgate) {
public void loadFloodgate(GeyserModBootstrap geyser, Path floodgateDataFolder) {
Path geyserDataFolder = geyser.getConfigFolder();
Path floodgateDataFolder = floodgate != null ? FabricLoader.getInstance().getConfigDir().resolve("floodgate") : null;
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgateDataFolder, geyserDataFolder, geyser.getGeyserLogger());
}

View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.mod;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.DefaultEventLoopGroup;
import io.netty.channel.local.LocalAddress;
import io.netty.util.concurrent.DefaultThreadFactory;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerConnectionListener;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.GeyserBootstrap;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.network.netty.GeyserInjector;
import org.geysermc.geyser.network.netty.LocalServerChannelWrapper;
import org.geysermc.geyser.platform.mod.platform.GeyserModPlatform;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
public class GeyserModInjector extends GeyserInjector {
private final MinecraftServer server;
private final GeyserModPlatform platform;
private DefaultEventLoopGroup eventLoopGroup;
/**
* Used to uninject ourselves on shutdown.
*/
private List<ChannelFuture> allServerChannels;
public GeyserModInjector(MinecraftServer server, GeyserModPlatform platform) {
this.server = server;
this.platform = platform;
}
@Override
protected void initializeLocalChannel0(GeyserBootstrap bootstrap) throws Exception {
ServerConnectionListener connection = this.server.getConnection();
// Find the channel that Minecraft uses to listen to connections
ChannelFuture listeningChannel = null;
this.allServerChannels = ((GeyserChannelGetter) connection).geyser$getChannels();
for (ChannelFuture o : allServerChannels) {
listeningChannel = o;
break;
}
if (listeningChannel == null) {
throw new RuntimeException("Unable to find listening channel!");
}
// Making this a function prevents childHandler from being treated as a non-final variable
ChannelInitializer<Channel> childHandler = getChildHandler(bootstrap, listeningChannel);
// This method is what initializes the connection in Java Edition, after Netty is all set.
Method initChannel = childHandler.getClass().getDeclaredMethod("initChannel", Channel.class);
initChannel.setAccessible(true);
// Separate variable so we can shut it down later
eventLoopGroup = new DefaultEventLoopGroup(0, new DefaultThreadFactory("Geyser " + this.platform.platformType().platformName() + " connection thread", Thread.MAX_PRIORITY));
ChannelFuture channelFuture = (new ServerBootstrap()
.channel(LocalServerChannelWrapper.class)
.childHandler(new ChannelInitializer<>() {
@Override
protected void initChannel(@NonNull Channel ch) throws Exception {
initChannel.invoke(childHandler, ch);
if (bootstrap.getGeyserConfig().isDisableCompression()) {
ch.pipeline().addAfter("encoder", "geyser-compression-disabler", new GeyserModCompressionDisabler());
}
}
})
// Set to MAX_PRIORITY as MultithreadEventLoopGroup#newDefaultThreadFactory which DefaultEventLoopGroup implements does by default
.group(eventLoopGroup)
.localAddress(LocalAddress.ANY))
.bind()
.syncUninterruptibly();
// We don't need to add to the list, but plugins like ProtocolSupport and ProtocolLib that add to the main pipeline
// will work when we add to the list.
allServerChannels.add(channelFuture);
this.localChannel = channelFuture;
this.serverSocketAddress = channelFuture.channel().localAddress();
}
@SuppressWarnings("unchecked")
private ChannelInitializer<Channel> getChildHandler(GeyserBootstrap bootstrap, ChannelFuture listeningChannel) {
List<String> names = listeningChannel.channel().pipeline().names();
ChannelInitializer<Channel> childHandler = null;
for (String name : names) {
ChannelHandler handler = listeningChannel.channel().pipeline().get(name);
try {
Field childHandlerField = handler.getClass().getDeclaredField("childHandler");
childHandlerField.setAccessible(true);
childHandler = (ChannelInitializer<Channel>) childHandlerField.get(handler);
break;
} catch (Exception e) {
if (bootstrap.getGeyserConfig().isDebugMode()) {
bootstrap.getGeyserLogger().debug("The handler " + name + " isn't a ChannelInitializer. THIS ERROR IS SAFE TO IGNORE!");
e.printStackTrace();
}
}
}
if (childHandler == null) {
throw new RuntimeException();
}
return childHandler;
}
@Override
public void shutdown() {
if (this.allServerChannels != null) {
this.allServerChannels.remove(this.localChannel);
this.allServerChannels = null;
}
if (eventLoopGroup != null) {
try {
eventLoopGroup.shutdownGracefully().sync();
eventLoopGroup = null;
} catch (Exception e) {
GeyserImpl.getInstance().getLogger().error("Unable to shut down injector! " + e.getMessage());
e.printStackTrace();
}
}
super.shutdown();
}
}

View file

@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
@ -32,12 +32,12 @@ import org.apache.logging.log4j.Logger;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.text.ChatColor;
public class GeyserFabricLogger implements GeyserLogger {
private final Logger logger = LogManager.getLogger("geyser-fabric");
public class GeyserModLogger implements GeyserLogger {
private final Logger logger = LogManager.getLogger("geyser");
private boolean debug;
public GeyserFabricLogger(boolean isDebug) {
public GeyserModLogger(boolean isDebug) {
debug = isDebug;
}
@ -73,7 +73,7 @@ public class GeyserFabricLogger implements GeyserLogger {
@Override
public void sendMessage(Component message) {
// As of Java Edition 1.19.2, Fabric's console doesn't natively support legacy format
// As of Java Edition 1.19.2, Minecraft's console doesn't natively support legacy format
String flattened = LegacyComponentSerializer.legacySection().serialize(message);
// Add the reset at the end, or else format will persist... forever.
// https://cdn.discordapp.com/attachments/573909525132738590/1033904509170225242/unknown.png

View file

@ -23,21 +23,22 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.world.entity.player.Player;
import org.geysermc.geyser.Constants;
import org.geysermc.geyser.platform.fabric.command.FabricCommandSender;
import org.geysermc.geyser.platform.mod.command.ModCommandSender;
import org.geysermc.geyser.util.VersionCheckUtils;
public final class GeyserFabricUpdateListener {
public static void onPlayReady(ServerGamePacketListenerImpl handler) {
if (Permissions.check(handler.player, Constants.UPDATE_PERMISSION, 2)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new FabricCommandSender(handler.player.createCommandSourceStack()));
public final class GeyserModUpdateListener {
public static void onPlayReady(Player player) {
CommandSourceStack stack = player.createCommandSourceStack();
if (GeyserModBootstrap.getInstance().hasPermission(stack, Constants.UPDATE_PERMISSION, 2)) {
VersionCheckUtils.checkForGeyserUpdate(() -> new ModCommandSender(stack));
}
}
private GeyserFabricUpdateListener() {
private GeyserModUpdateListener() {
}
}

View file

@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import net.minecraft.server.MinecraftServer;

View file

@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric;
package org.geysermc.geyser.platform.mod;
import lombok.AllArgsConstructor;
import net.kyori.adventure.text.Component;
@ -39,10 +39,11 @@ import net.minecraft.network.protocol.status.ServerStatusPacketListener;
import net.minecraft.network.protocol.status.ServerboundStatusRequestPacket;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerStatusPacketListenerImpl;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.GeyserLogger;
import org.geysermc.geyser.ping.GeyserPingInfo;
import org.geysermc.geyser.ping.IGeyserPingPassthrough;
import org.jetbrains.annotations.Nullable;
import java.net.InetSocketAddress;
import java.util.Objects;
@ -100,7 +101,7 @@ public class ModPingPassthrough implements IGeyserPingPassthrough {
}
@Override
public void send(Packet<?> packet, @Nullable PacketSendListener packetSendListener, boolean bl) {
public void send(@NonNull Packet<?> packet, @Nullable PacketSendListener packetSendListener, boolean bl) {
if (packet instanceof ClientboundStatusResponsePacket statusResponse) {
status = statusResponse.status();
}

View file

@ -23,31 +23,31 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.command;
package org.geysermc.geyser.platform.mod.command;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.context.CommandContext;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.commands.CommandSourceStack;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommand;
import org.geysermc.geyser.command.GeyserCommandExecutor;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.text.ChatColor;
import org.geysermc.geyser.text.GeyserLocale;
import java.util.Collections;
public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implements Command<CommandSourceStack> {
public class GeyserModCommandExecutor extends GeyserCommandExecutor implements Command<CommandSourceStack> {
private final GeyserCommand command;
public GeyserFabricCommandExecutor(GeyserImpl connector, GeyserCommand command) {
super(connector, Collections.singletonMap(command.name(), command));
public GeyserModCommandExecutor(GeyserImpl geyser, GeyserCommand command) {
super(geyser, Collections.singletonMap(command.name(), command));
this.command = command;
}
public boolean testPermission(CommandSourceStack source) {
return Permissions.check(source, command.permission(), command.isSuggestedOpOnly() ? 2 : 0);
return GeyserModBootstrap.getInstance().hasPermission(source, command.permission(), command.isSuggestedOpOnly() ? 2 : 0);
}
@Override
@ -57,7 +57,7 @@ public class GeyserFabricCommandExecutor extends GeyserCommandExecutor implement
public int runWithArgs(CommandContext<CommandSourceStack> context, String args) {
CommandSourceStack source = context.getSource();
FabricCommandSender sender = new FabricCommandSender(source);
ModCommandSender sender = new ModCommandSender(source);
GeyserSession session = getGeyserSession(sender);
if (!testPermission(source)) {
sender.sendMessage(ChatColor.RED + GeyserLocale.getPlayerLocaleString("geyser.bootstrap.command.permission_fail", sender.locale()));

View file

@ -23,9 +23,8 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.command;
package org.geysermc.geyser.platform.mod.command;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.kyori.adventure.text.serializer.gson.GsonComponentSerializer;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.network.chat.Component;
@ -33,15 +32,16 @@ import net.minecraft.server.level.ServerPlayer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.command.GeyserCommandSource;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.text.ChatColor;
import java.util.Objects;
public class FabricCommandSender implements GeyserCommandSource {
public class ModCommandSender implements GeyserCommandSource {
private final CommandSourceStack source;
public FabricCommandSender(CommandSourceStack source) {
public ModCommandSender(CommandSourceStack source) {
this.source = source;
}
@ -76,6 +76,6 @@ public class FabricCommandSender implements GeyserCommandSource {
@Override
public boolean hasPermission(String permission) {
return Permissions.check(source, permission, source.getServer().getOperatorUserPermissionLevel());
return GeyserModBootstrap.getInstance().hasPermission(source, permission, source.getServer().getOperatorUserPermissionLevel());
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,18 +23,16 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.mixin.client;
package org.geysermc.geyser.platform.mod.mixin.client;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.minecraft.client.Minecraft;
import net.minecraft.client.server.IntegratedServer;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.level.GameType;
import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.platform.fabric.GeyserFabricMod;
import org.geysermc.geyser.platform.fabric.GeyserServerPortGetter;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.platform.mod.GeyserServerPortGetter;
import org.geysermc.geyser.text.GeyserLocale;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
@ -45,7 +43,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Objects;
@Environment(EnvType.CLIENT)
@Mixin(IntegratedServer.class)
public class IntegratedServerMixin implements GeyserServerPortGetter {
@Shadow
@ -57,8 +54,8 @@ public class IntegratedServerMixin implements GeyserServerPortGetter {
private void onOpenToLan(GameType gameType, boolean cheatsAllowed, int port, CallbackInfoReturnable<Boolean> cir) {
if (cir.getReturnValueZ()) {
// If the LAN is opened, starts Geyser.
GeyserFabricMod.getInstance().setServer((MinecraftServer) (Object) this);
GeyserFabricMod.getInstance().onGeyserEnable();
GeyserModBootstrap.getInstance().setServer((MinecraftServer) (Object) this);
GeyserModBootstrap.getInstance().onGeyserEnable();
// Ensure player locale has been loaded, in case it's different from Java system language
GeyserLocale.loadGeyserLocale(this.minecraft.options.languageCode);
// Give indication that Geyser is loaded

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019-2022 GeyserMC. http://geysermc.org
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
@ -23,7 +23,7 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.mixin.server;
package org.geysermc.geyser.platform.mod.mixin.server;
import com.mojang.datafixers.DataFixer;
import net.minecraft.server.MinecraftServer;
@ -33,14 +33,14 @@ import net.minecraft.server.dedicated.DedicatedServer;
import net.minecraft.server.level.progress.ChunkProgressListenerFactory;
import net.minecraft.server.packs.repository.PackRepository;
import net.minecraft.world.level.storage.LevelStorageSource;
import org.geysermc.geyser.platform.fabric.GeyserServerPortGetter;
import org.geysermc.geyser.platform.mod.GeyserServerPortGetter;
import org.spongepowered.asm.mixin.Mixin;
import java.net.Proxy;
@Mixin(DedicatedServer.class)
public abstract class MinecraftDedicatedServerMixin extends MinecraftServer implements GeyserServerPortGetter {
public MinecraftDedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer dataFixer, Services services, ChunkProgressListenerFactory chunkProgressListenerFactory) {
public abstract class DedicatedServerMixin extends MinecraftServer implements GeyserServerPortGetter {
public DedicatedServerMixin(Thread thread, LevelStorageSource.LevelStorageAccess levelStorageAccess, PackRepository packRepository, WorldStem worldStem, Proxy proxy, DataFixer dataFixer, Services services, ChunkProgressListenerFactory chunkProgressListenerFactory) {
super(thread, levelStorageAccess, packRepository, worldStem, proxy, dataFixer, services, chunkProgressListenerFactory);
}

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.mod.mixin.server;
import io.netty.channel.ChannelFuture;
import net.minecraft.server.network.ServerConnectionListener;
import org.geysermc.geyser.platform.mod.GeyserChannelGetter;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.List;
@Mixin(ServerConnectionListener.class)
public abstract class ServerConnectionListenerMixin implements GeyserChannelGetter {
@Shadow @Final private List<ChannelFuture> channels;
@Override
public List<ChannelFuture> geyser$getChannels() {
return this.channels;
}
}

View file

@ -0,0 +1,92 @@
/*
* Copyright (c) 2019-2023 GeyserMC. http://geysermc.org
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* @author GeyserMC
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.mod.platform;
import net.minecraft.server.MinecraftServer;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.geysermc.geyser.api.util.PlatformType;
import org.geysermc.geyser.dump.BootstrapDumpInfo;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import java.io.InputStream;
import java.nio.file.Path;
/**
* An interface which holds common methods that have different
* APIs on their respective mod platforms.
*/
public interface GeyserModPlatform {
/**
* Gets the {@link PlatformType} of the mod platform.
*
* @return the platform type of the mod platform
*/
@NonNull
PlatformType platformType();
/**
* Gets the config path of the mod platform.
*
* @return the config path of the mod platform
*/
@NonNull
String configPath();
/**
* Gets the data folder of the mod platform.
*
* @return the data folder of the mod platform
*/
@NonNull
Path dataFolder(@NonNull String modId);
/**
* Gets the dump info of the mod platform.
*
* @param server the server to get the dump info from
* @return the dump info of the mod platform
*/
@NonNull
BootstrapDumpInfo dumpInfo(@NonNull MinecraftServer server);
/**
* Tests if the Floodgate plugin is present on the mod platform.
*
* @return {@code true} if the Floodgate plugin is present on the mod platform, {@code false} otherwise
*/
boolean testFloodgatePluginPresent(@NonNull GeyserModBootstrap bootstrap);
/**
* Resolves a resource from the mod jar.
*
* @param resource the name of the resource
* @return the input stream of the resource
*/
@Nullable
InputStream resolveResource(@NonNull String resource);
}

View file

@ -23,22 +23,41 @@
* @link https://github.com/GeyserMC/Geyser
*/
package org.geysermc.geyser.platform.fabric.world;
package org.geysermc.geyser.platform.mod.world;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
import me.lucko.fabric.api.permissions.v0.Permissions;
import net.minecraft.SharedConstants;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.*;
import net.minecraft.nbt.ByteArrayTag;
import net.minecraft.nbt.ByteTag;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.EndTag;
import net.minecraft.nbt.FloatTag;
import net.minecraft.nbt.IntArrayTag;
import net.minecraft.nbt.IntTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.LongArrayTag;
import net.minecraft.nbt.LongTag;
import net.minecraft.nbt.ShortTag;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.TagVisitor;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.WritableBookItem;
import net.minecraft.world.item.WrittenBookItem;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BannerBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.LecternBlockEntity;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.chunk.LevelChunkSection;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.cloudburstmc.math.vector.Vector3i;
import org.cloudburstmc.nbt.NbtMap;
@ -46,6 +65,8 @@ import org.cloudburstmc.nbt.NbtMapBuilder;
import org.cloudburstmc.nbt.NbtType;
import org.geysermc.erosion.util.LecternUtils;
import org.geysermc.geyser.level.GeyserWorldManager;
import org.geysermc.geyser.network.GameProtocol;
import org.geysermc.geyser.platform.mod.GeyserModBootstrap;
import org.geysermc.geyser.session.GeyserSession;
import org.geysermc.geyser.util.BlockEntityUtils;
@ -54,13 +75,54 @@ import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
public class GeyserFabricWorldManager extends GeyserWorldManager {
public class GeyserModWorldManager extends GeyserWorldManager {
private final MinecraftServer server;
public GeyserFabricWorldManager(MinecraftServer server) {
public GeyserModWorldManager(MinecraftServer server) {
this.server = server;
}
@Override
public int getBlockAt(GeyserSession session, int x, int y, int z) {
// If the protocol version of Geyser and the server are not the
// same, fallback to the chunk cache. May be able to update this
// in the future to use ViaVersion however, like Spigot does.
if (SharedConstants.getCurrentVersion().getProtocolVersion() != GameProtocol.getJavaProtocolVersion()) {
return super.getBlockAt(session, x, y, z);
}
ServerPlayer player = this.getPlayer(session);
if (player == null) {
return 0;
}
Level level = player.level();
if (y < level.getMinBuildHeight()) {
return 0;
}
ChunkAccess chunk = level.getChunkSource().getChunk(x >> 4, z >> 4, ChunkStatus.FULL, false);
if (chunk == null) {
return 0;
}
int worldOffset = level.getMinBuildHeight() >> 4;
int chunkOffset = (y >> 4) - worldOffset;
if (chunkOffset < chunk.getSections().length) {
LevelChunkSection section = chunk.getSections()[chunkOffset];
if (section != null && !section.hasOnlyAir()) {
return Block.getId(section.getBlockState(x & 15, y & 15, z & 15));
}
}
return 0;
}
@Override
public boolean hasOwnChunkCache() {
return SharedConstants.getCurrentVersion().getProtocolVersion() == GameProtocol.getJavaProtocolVersion();
}
@Override
public boolean shouldExpectLecternHandled(GeyserSession session) {
return true;
@ -154,7 +216,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
@Override
public boolean hasPermission(GeyserSession session, String permission) {
ServerPlayer player = getPlayer(session);
return Permissions.check(player, permission);
return GeyserModBootstrap.getInstance().hasPermission(player, permission);
}
@Override

View file

@ -0,0 +1,18 @@
{
"required": true,
"minVersion": "0.8",
"package": "org.geysermc.geyser.platform.mod.mixin",
"compatibilityLevel": "JAVA_17",
"mixins": [
"server.ServerConnectionListenerMixin"
],
"server": [
"server.DedicatedServerMixin"
],
"client": [
"client.IntegratedServerMixin"
],
"injectors": {
"defaultRequire": 1
}
}

View file

@ -100,7 +100,7 @@ public class GeyserStandaloneGUI {
Container cp = frame.getContentPane();
// Fetch and set the icon for the frame
URL image = getClass().getClassLoader().getResource("icon.png");
URL image = getClass().getClassLoader().getResource("assets/geyser/icon.png");
if (image != null) {
ImageIcon icon = new ImageIcon(image);
frame.setIconImage(icon.getImage());