package ganarchy.friendcode.client; import ganarchy.friendcode.FriendCode; import ganarchy.friendcode.mixin.FriendCodeIntegratedServerExt; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.OpenToLanScreen; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.CyclingButtonWidget; import net.minecraft.client.util.NetworkUtils; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.screen.ScreenTexts; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.world.GameMode; import java.io.IOException; @Environment(EnvType.CLIENT) public class FriendCodeScreen extends Screen { private static final Text ALLOW_COMMANDS_TEXT = Text.translatable("selectWorld.allowCommands"); private static final Text GAME_MODE_TEXT = Text.translatable("selectWorld.gameMode"); private static final Text OTHER_PLAYERS_TEXT = Text.translatable("lanServer.otherPlayers"); private final Screen parent; private GameMode gameMode = GameMode.SURVIVAL; private boolean allowCommands; private static final Text START_SHARING = Text.translatable("friendcode.start"); public FriendCodeScreen(Screen parent) { super(Text.translatable("friendcode.title")); this.parent = parent; } @Override protected void init() { // title button allows going back to LAN screen this.addDrawableChild(new ButtonWidget( this.width / 2 - 155, 50, 310, 20, this.title, button -> this.client.setScreen(new OpenToLanScreen(this.parent)) )); // game setting buttons this.addDrawableChild( CyclingButtonWidget .builder(GameMode::getSimpleTranslatableName) .values(GameMode.SURVIVAL, GameMode.SPECTATOR, GameMode.CREATIVE, GameMode.ADVENTURE) .initially(this.gameMode) .build( this.width / 2 - 155, 100, 150, 20, GAME_MODE_TEXT, (button, gameMode) -> { this.gameMode = gameMode; } ) ); this.addDrawableChild( CyclingButtonWidget .onOffBuilder(this.allowCommands) .build( this.width / 2 + 5, 100, 150, 20, ALLOW_COMMANDS_TEXT, (button, allowCommands) -> { this.allowCommands = allowCommands; } ) ); // start sharing this.addDrawableChild(new ButtonWidget( this.width / 2 - 155, this.height - 28, 150, 20, START_SHARING, button -> { // FIXME int i = NetworkUtils.findLocalPort(); var samPinger = openToFriends(this.client, this.gameMode, this.allowCommands, i); MutableText text = samPinger != null ? Text.translatable("commands.publish.started", i) : Text.translatable("commands.publish.failed"); this.client.inGameHud.getChatHud().addMessage(text); this.client.updateWindowTitle(); this.client.setScreen(new WaitingForFriendCodeScreen(samPinger)); } )); // go back to options this.addDrawableChild(new ButtonWidget( this.width / 2 + 5, this.height - 28, 150, 20, ScreenTexts.CANCEL, button -> this.client.setScreen(this.parent) )); } private static I2PSamPinger openToFriends(MinecraftClient client, GameMode gameMode, boolean allowCommands, int port) { try { client.loadBlockList(); client.getServer().getNetworkIo().bind(null, port); FriendCode.LOGGER.info("Started serving on {}", port); ((FriendCodeIntegratedServerExt) client.getServer()).lanPort(port); // reuse LAN pinger machinery instead of rolling our own var lanPinger = new I2PSamPinger(client.getServer().getServerMotd(), "" + port); ((FriendCodeIntegratedServerExt) client.getServer()).lanPinger(lanPinger); lanPinger.start(); ((FriendCodeIntegratedServerExt) client.getServer()).forcedGameMode(gameMode); client.getServer().getPlayerManager().setCheatsAllowed(allowCommands); int i = client.getServer().getPermissionLevel(client.player.getGameProfile()); client.player.setClientPermissionLevel(i); for (ServerPlayerEntity serverPlayerEntity : client.getServer().getPlayerManager().getPlayerList()) { client.getServer().getCommandManager().sendCommandTree(serverPlayerEntity); } return lanPinger; } catch (IOException iOException) { return null; } } @Override public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { this.renderBackground(matrices); FriendCodeScreen.drawCenteredText(matrices, this.textRenderer, OTHER_PLAYERS_TEXT, this.width / 2, 82, 0xFFFFFF); super.render(matrices, mouseX, mouseY, delta); } }