Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-11-19 22:40:18 +01:00
Merge remote-tracking branch 'upstream/master' into feature/blocky
Dieser Commit ist enthalten in:
Commit
024feed4c6
29
.github/workflows/build.yml
vendored
29
.github/workflows/build.yml
vendored
@ -23,25 +23,19 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Validate Gradle Wrapper
|
||||||
|
uses: gradle/wrapper-validation-action@v1
|
||||||
|
|
||||||
- uses: actions/setup-java@v3
|
- uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
java-version: 17
|
java-version: 17
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
|
|
||||||
- name: Cache Gradle Packages
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.m2
|
|
||||||
~/.gradle/caches
|
|
||||||
~/.gradle/wrapper
|
|
||||||
key: ${{ github.ref_name }}-gradle-${{ hashFiles('*.gradle.kts', 'gradle.properties', 'gradlew', 'gradle/*', 'gradle/**/*', 'build-logic/*', 'build-logic/**/**/**/*', '**/*.gradle.kts', '**/**/*.gradle.kts') }}
|
|
||||||
restore-keys: ${{ github.ref_name }}-gradle-
|
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
with:
|
with:
|
||||||
arguments: build --no-daemon
|
arguments: build
|
||||||
|
gradle-home-cache-cleanup: true
|
||||||
|
|
||||||
- name: Archive artifacts (Geyser Fabric)
|
- name: Archive artifacts (Geyser Fabric)
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
@ -87,7 +81,7 @@ jobs:
|
|||||||
if-no-files-found: error
|
if-no-files-found: error
|
||||||
|
|
||||||
- name: Publish to Maven Repository
|
- name: Publish to Maven Repository
|
||||||
if: ${{ job.status == 'success' && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
env:
|
env:
|
||||||
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
ORG_GRADLE_PROJECT_geysermcUsername: ${{ vars.DEPLOY_USER }}
|
||||||
@ -96,7 +90,7 @@ jobs:
|
|||||||
arguments: publish
|
arguments: publish
|
||||||
|
|
||||||
- name: Publish to Downloads API
|
- name: Publish to Downloads API
|
||||||
if: ${{ github.ref_name == 'master' && job.status == 'success' && github.repository == 'GeyserMC/Geyser' }}
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }}
|
DOWNLOADS_USERNAME: ${{ vars.DOWNLOADS_USERNAME }}
|
||||||
@ -119,6 +113,15 @@ jobs:
|
|||||||
echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json
|
echo "{\"project\": \"$project\", \"version\": \"$version\", \"id\": $GITHUB_RUN_NUMBER, \"commit\": \"$GITHUB_SHA\"}" > metadata.json
|
||||||
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
|
rsync -P -e "ssh -o StrictHostKeyChecking=no -i id_ecdsa" metadata.json $DOWNLOADS_USERNAME@$DOWNLOADS_SERVER_IP:~/uploads/$project/$GITHUB_RUN_NUMBER/
|
||||||
|
|
||||||
|
- name: Publish to Modrinth
|
||||||
|
uses: gradle/gradle-build-action@v2
|
||||||
|
if: ${{ success() && github.repository == 'GeyserMC/Geyser' && github.ref_name == 'master' }}
|
||||||
|
env:
|
||||||
|
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||||
|
with:
|
||||||
|
arguments: fabric:modrinth
|
||||||
|
gradle-home-cache-cleanup: true
|
||||||
|
|
||||||
- name: Notify Discord
|
- name: Notify Discord
|
||||||
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
if: ${{ (success() || failure()) && github.repository == 'GeyserMC/Geyser' }}
|
||||||
uses: Tim203/actions-git-discord-webhook@main
|
uses: Tim203/actions-git-discord-webhook@main
|
||||||
|
31
.github/workflows/publish.yml
vendored
31
.github/workflows/publish.yml
vendored
@ -1,31 +0,0 @@
|
|||||||
name: Publish
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
paths-ignore:
|
|
||||||
- '.github/ISSUE_TEMPLATE/*.yml'
|
|
||||||
- '.github/actions/pullrequest.yml'
|
|
||||||
- '.idea/copyright/*.xml'
|
|
||||||
- '.gitignore'
|
|
||||||
- 'CONTRIBUTING.md'
|
|
||||||
- 'LICENSE'
|
|
||||||
- 'Jenkinsfile '
|
|
||||||
- 'README.md'
|
|
||||||
- 'licenseheader.txt'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with:
|
|
||||||
submodules: recursive
|
|
||||||
- uses: gradle/wrapper-validation-action@v1
|
|
||||||
- uses: actions/setup-java@v3
|
|
||||||
with:
|
|
||||||
distribution: 'temurin'
|
|
||||||
java-version: 17
|
|
||||||
- name: Publish to Modrinth
|
|
||||||
env:
|
|
||||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
|
||||||
run: ./gradlew fabric:modrinth
|
|
@ -94,6 +94,7 @@ tasks {
|
|||||||
}
|
}
|
||||||
|
|
||||||
modrinth {
|
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")
|
projectId.set("wKkoqHrH")
|
||||||
versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER"))
|
versionNumber.set(project.version as String + "-" + System.getenv("GITHUB_RUN_NUMBER"))
|
||||||
versionType.set("beta")
|
versionType.set("beta")
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.fabric.world;
|
package org.geysermc.geyser.platform.fabric.world;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
import com.nukkitx.nbt.NbtMapBuilder;
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
@ -40,16 +41,16 @@ import net.minecraft.world.item.WrittenBookItem;
|
|||||||
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
import net.minecraft.world.level.block.entity.BannerBlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
import net.minecraft.world.level.block.entity.LecternBlockEntity;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import org.geysermc.erosion.util.LecternUtils;
|
||||||
import org.geysermc.geyser.level.GeyserWorldManager;
|
import org.geysermc.geyser.level.GeyserWorldManager;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
|
||||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
public class GeyserFabricWorldManager extends GeyserWorldManager {
|
public class GeyserFabricWorldManager extends GeyserWorldManager {
|
||||||
private final MinecraftServer server;
|
private final MinecraftServer server;
|
||||||
@ -59,24 +60,55 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldExpectLecternHandled() {
|
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
|
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
||||||
Runnable lecternGet = () -> {
|
server.execute(() -> {
|
||||||
// Mostly a reimplementation of Spigot lectern support
|
|
||||||
ServerPlayer player = getPlayer(session);
|
ServerPlayer player = getPlayer(session);
|
||||||
if (player != null) {
|
if (player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LevelChunk chunk = player.getLevel().getChunk(x, z);
|
||||||
|
final int chunkBlockX = x << 4;
|
||||||
|
final int chunkBlockZ = z << 4;
|
||||||
|
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||||
|
BlockEntityInfo blockEntityInfo = blockEntityInfos.get(i);
|
||||||
|
BlockEntity blockEntity = chunk.getBlockEntity(new BlockPos(chunkBlockX + blockEntityInfo.getX(),
|
||||||
|
blockEntityInfo.getY(), chunkBlockZ + blockEntityInfo.getZ()));
|
||||||
|
sendLecternData(session, blockEntity, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
||||||
|
server.execute(() -> {
|
||||||
|
ServerPlayer player = getPlayer(session);
|
||||||
|
if (player == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
BlockEntity blockEntity = player.level.getBlockEntity(new BlockPos(x, y, z));
|
BlockEntity blockEntity = player.level.getBlockEntity(new BlockPos(x, y, z));
|
||||||
|
sendLecternData(session, blockEntity, false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendLecternData(GeyserSession session, BlockEntity blockEntity, boolean isChunkLoad) {
|
||||||
if (!(blockEntity instanceof LecternBlockEntity lectern)) {
|
if (!(blockEntity instanceof LecternBlockEntity lectern)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int x = blockEntity.getBlockPos().getX();
|
||||||
|
int y = blockEntity.getBlockPos().getY();
|
||||||
|
int z = blockEntity.getBlockPos().getZ();
|
||||||
|
|
||||||
if (!lectern.hasBook()) {
|
if (!lectern.hasBook()) {
|
||||||
if (!isChunkLoad) {
|
if (!isChunkLoad) {
|
||||||
BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
|
BlockEntityUtils.updateBlockEntity(session, LecternUtils.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -84,7 +116,7 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
|||||||
ItemStack book = lectern.getBook();
|
ItemStack book = lectern.getBook();
|
||||||
int pageCount = WrittenBookItem.getPageCount(book);
|
int pageCount = WrittenBookItem.getPageCount(book);
|
||||||
boolean hasBookPages = pageCount > 0;
|
boolean hasBookPages = pageCount > 0;
|
||||||
NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
|
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, hasBookPages ? pageCount : 1);
|
||||||
lecternTag.putInt("page", lectern.getPage() / 2);
|
lecternTag.putInt("page", lectern.getPage() / 2);
|
||||||
NbtMapBuilder bookTag = NbtMap.builder()
|
NbtMapBuilder bookTag = NbtMap.builder()
|
||||||
.putByte("Count", (byte) book.getCount())
|
.putByte("Count", (byte) book.getCount())
|
||||||
@ -114,15 +146,6 @@ public class GeyserFabricWorldManager extends GeyserWorldManager {
|
|||||||
NbtMap blockEntityTag = lecternTag.build();
|
NbtMap blockEntityTag = lecternTag.build();
|
||||||
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
|
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
|
||||||
}
|
}
|
||||||
};
|
|
||||||
if (isChunkLoad) {
|
|
||||||
// Hacky hacks to allow lectern loading to be delayed
|
|
||||||
session.scheduleInEventLoop(() -> server.execute(lecternGet), 1, TimeUnit.SECONDS);
|
|
||||||
} else {
|
|
||||||
server.execute(lecternGet);
|
|
||||||
}
|
|
||||||
return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasPermission(GeyserSession session, String permission) {
|
public boolean hasPermission(GeyserSession session, String permission) {
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
dependencies {
|
dependencies {
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
|
api(libs.erosion.bukkit.common) {
|
||||||
|
isTransitive = false
|
||||||
|
}
|
||||||
|
|
||||||
implementation(libs.adapters.spigot)
|
implementation(libs.adapters.spigot)
|
||||||
|
|
||||||
@ -7,8 +10,8 @@ dependencies {
|
|||||||
|
|
||||||
implementation(libs.adventure.text.serializer.bungeecord)
|
implementation(libs.adventure.text.serializer.bungeecord)
|
||||||
|
|
||||||
// Both paper-api and paper-mojangapi only provide Java 17 versions for 1.19
|
// Both folia-api and paper-mojangapi only provide Java 17 versions for 1.19
|
||||||
compileOnly(libs.paper.api) {
|
compileOnly(libs.folia.api) {
|
||||||
attributes {
|
attributes {
|
||||||
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, 17)
|
||||||
}
|
}
|
||||||
|
@ -59,13 +59,13 @@ public final class GeyserPaperPingPassthrough implements IGeyserPingPassthrough
|
|||||||
// runtime because we still have to shade in our own Adventure class. For now.
|
// runtime because we still have to shade in our own Adventure class. For now.
|
||||||
PaperServerListPingEvent event;
|
PaperServerListPingEvent event;
|
||||||
if (OLD_CONSTRUCTOR != null) {
|
if (OLD_CONSTRUCTOR != null) {
|
||||||
// Approximately pre-1.19
|
// 1.19, removed in 1.19.4
|
||||||
event = OLD_CONSTRUCTOR.newInstance(new GeyserStatusClient(inetSocketAddress),
|
event = OLD_CONSTRUCTOR.newInstance(new GeyserStatusClient(inetSocketAddress),
|
||||||
Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(),
|
Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(),
|
||||||
Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null);
|
Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null);
|
||||||
} else {
|
} else {
|
||||||
event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress),
|
event = new PaperServerListPingEvent(new GeyserStatusClient(inetSocketAddress),
|
||||||
Bukkit.getMotd(), Bukkit.shouldSendChatPreviews(), Bukkit.getOnlinePlayers().size(),
|
Bukkit.getMotd(), Bukkit.getOnlinePlayers().size(),
|
||||||
Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null);
|
Bukkit.getMaxPlayers(), Bukkit.getVersion(), GameProtocol.getJavaProtocolVersion(), null);
|
||||||
}
|
}
|
||||||
Bukkit.getPluginManager().callEvent(event);
|
Bukkit.getPluginManager().callEvent(event);
|
||||||
|
@ -66,7 +66,7 @@ public class GeyserSpigotPingPassthrough implements IGeyserPingPassthrough {
|
|||||||
private static class GeyserPingEvent extends ServerListPingEvent {
|
private static class GeyserPingEvent extends ServerListPingEvent {
|
||||||
|
|
||||||
public GeyserPingEvent(InetAddress address, String motd, int numPlayers, int maxPlayers) {
|
public GeyserPingEvent(InetAddress address, String motd, int numPlayers, int maxPlayers) {
|
||||||
super(address, motd, Bukkit.shouldSendChatPreviews(), numPlayers, maxPlayers);
|
super("", address, motd, numPlayers, maxPlayers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,16 +60,16 @@ public final class ReflectedNames {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static Constructor<PaperServerListPingEvent> getOldPaperPingConstructor() {
|
static Constructor<PaperServerListPingEvent> getOldPaperPingConstructor() {
|
||||||
if (getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, boolean.class, int.class,
|
if (getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, int.class,
|
||||||
int.class, String.class, int.class, CachedServerIcon.class) != null) {
|
int.class, String.class, int.class, CachedServerIcon.class) != null) {
|
||||||
// @NotNull StatusClient client, @NotNull String motd, boolean shouldSendChatPreviews, int numPlayers, int maxPlayers,
|
// @NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers,
|
||||||
// @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon
|
// @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon
|
||||||
// New constructor is present
|
// New constructor is present
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// @NotNull StatusClient client, @NotNull String motd, int numPlayers, int maxPlayers,
|
// @NotNull StatusClient client, @NotNull String motd, boolean shouldSendChatPreviews, int numPlayers, int maxPlayers,
|
||||||
// @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon
|
// @NotNull String version, int protocolVersion, @Nullable CachedServerIcon favicon
|
||||||
return getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, int.class, int.class,
|
return getConstructor(PaperServerListPingEvent.class, StatusClient.class, String.class, boolean.class, int.class, int.class,
|
||||||
String.class, int.class, CachedServerIcon.class);
|
String.class, int.class, CachedServerIcon.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ package org.geysermc.geyser.platform.spigot.world;
|
|||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType;
|
import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
@ -85,7 +85,7 @@ public class GeyserPistonListener implements Listener {
|
|||||||
PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING;
|
PistonValueType type = isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING;
|
||||||
boolean sticky = event.isSticky();
|
boolean sticky = event.isSticky();
|
||||||
|
|
||||||
Object2IntMap<Vector3i> attachedBlocks = new Object2IntOpenHashMap<>();
|
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>();
|
||||||
boolean blocksFilled = false;
|
boolean blocksFilled = false;
|
||||||
|
|
||||||
for (Map.Entry<UUID, GeyserSession> entry : geyser.getSessionManager().getSessions().entrySet()) {
|
for (Map.Entry<UUID, GeyserSession> entry : geyser.getSessionManager().getSessions().entrySet()) {
|
||||||
|
@ -25,33 +25,28 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.platform.spigot.world.manager;
|
package org.geysermc.geyser.platform.spigot.world.manager;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.ListTag;
|
|
||||||
import com.github.steveice10.opennbt.tag.builtin.Tag;
|
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
import com.nukkitx.nbt.NbtMapBuilder;
|
|
||||||
import com.nukkitx.nbt.NbtType;
|
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.Chunk;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.block.*;
|
import org.bukkit.block.Block;
|
||||||
import org.bukkit.block.banner.Pattern;
|
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.bukkit.inventory.meta.BookMeta;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.geysermc.erosion.bukkit.BukkitLecterns;
|
||||||
|
import org.geysermc.erosion.bukkit.BukkitUtils;
|
||||||
|
import org.geysermc.erosion.bukkit.PickBlockUtils;
|
||||||
|
import org.geysermc.erosion.bukkit.SchedulerUtils;
|
||||||
import org.geysermc.geyser.level.GameRule;
|
import org.geysermc.geyser.level.GameRule;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
import org.geysermc.geyser.registry.BlockRegistries;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
|
||||||
import org.geysermc.geyser.translator.inventory.item.nbt.BannerTranslator;
|
|
||||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@ -60,9 +55,11 @@ import java.util.concurrent.CompletableFuture;
|
|||||||
*/
|
*/
|
||||||
public class GeyserSpigotWorldManager extends WorldManager {
|
public class GeyserSpigotWorldManager extends WorldManager {
|
||||||
private final Plugin plugin;
|
private final Plugin plugin;
|
||||||
|
private final BukkitLecterns lecterns;
|
||||||
|
|
||||||
public GeyserSpigotWorldManager(Plugin plugin) {
|
public GeyserSpigotWorldManager(Plugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
this.lecterns = new BukkitLecterns(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -81,6 +78,12 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public int getBlockNetworkId(Block block) {
|
public int getBlockNetworkId(Block block) {
|
||||||
|
if (SchedulerUtils.FOLIA && !Bukkit.isOwnedByCurrentRegion(block)) {
|
||||||
|
// Terrible behavior, but this is basically what's always been happening behind the scenes anyway.
|
||||||
|
CompletableFuture<String> blockData = new CompletableFuture<>();
|
||||||
|
Bukkit.getRegionScheduler().execute(this.plugin, block.getLocation(), () -> blockData.complete(block.getBlockData().getAsString()));
|
||||||
|
return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(blockData.join(), BlockStateValues.JAVA_AIR_ID);
|
||||||
|
}
|
||||||
return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(block.getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID);
|
return BlockRegistries.JAVA_IDENTIFIERS.getOrDefault(block.getBlockData().getAsString(), BlockStateValues.JAVA_AIR_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,71 +93,64 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
|
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
||||||
// Run as a task to prevent async issues
|
|
||||||
Runnable lecternInfoGet = () -> {
|
|
||||||
Player bukkitPlayer;
|
Player bukkitPlayer;
|
||||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
||||||
if (!(block.getState() instanceof Lectern lectern)) {
|
// Run as a task to prevent async issues
|
||||||
session.getGeyser().getLogger().error("Lectern expected at: " + Vector3i.from(x, y, z).toString() + " but was not! " + block.toString());
|
SchedulerUtils.runTask(this.plugin, () -> sendLecternData(session, block, false), block);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
||||||
|
Player bukkitPlayer;
|
||||||
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUsername())) == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (SchedulerUtils.FOLIA) {
|
||||||
ItemStack itemStack = lectern.getInventory().getItem(0);
|
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
|
||||||
if (itemStack == null || !(itemStack.getItemMeta() instanceof BookMeta bookMeta)) {
|
if (chunk == null) {
|
||||||
if (!isChunkLoad) {
|
|
||||||
// We need to update the lectern since it's not going to be updated otherwise
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(), Vector3i.from(x, y, z));
|
|
||||||
}
|
|
||||||
// We don't care; return
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Bukkit.getRegionScheduler().execute(this.plugin, bukkitPlayer.getWorld(), x, z, () ->
|
||||||
// On the count: allow the book to show/open even there are no pages. We know there is a book here, after all, and this matches Java behavior
|
sendLecternData(session, chunk, blockEntityInfos));
|
||||||
boolean hasBookPages = bookMeta.getPageCount() > 0;
|
|
||||||
NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, hasBookPages ? bookMeta.getPageCount() : 1);
|
|
||||||
lecternTag.putInt("page", lectern.getPage() / 2);
|
|
||||||
NbtMapBuilder bookTag = NbtMap.builder()
|
|
||||||
.putByte("Count", (byte) itemStack.getAmount())
|
|
||||||
.putShort("Damage", (short) 0)
|
|
||||||
.putString("Name", "minecraft:writable_book");
|
|
||||||
List<NbtMap> pages = new ArrayList<>(bookMeta.getPageCount());
|
|
||||||
if (hasBookPages) {
|
|
||||||
for (String page : bookMeta.getPages()) {
|
|
||||||
NbtMapBuilder pageBuilder = NbtMap.builder()
|
|
||||||
.putString("photoname", "")
|
|
||||||
.putString("text", page);
|
|
||||||
pages.add(pageBuilder.build());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Empty page
|
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
||||||
NbtMapBuilder pageBuilder = NbtMap.builder()
|
Chunk chunk = getChunk(bukkitPlayer.getWorld(), x, z);
|
||||||
.putString("photoname", "")
|
if (chunk == null) {
|
||||||
.putString("text", "");
|
return;
|
||||||
pages.add(pageBuilder.build());
|
}
|
||||||
|
sendLecternData(session, chunk, blockEntityInfos);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bookTag.putCompound("tag", NbtMap.builder().putList("pages", NbtType.COMPOUND, pages).build());
|
private Chunk getChunk(World world, int x, int z) {
|
||||||
lecternTag.putCompound("book", bookTag.build());
|
if (!world.isChunkLoaded(x, z)) {
|
||||||
NbtMap blockEntityTag = lecternTag.build();
|
return null;
|
||||||
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, Vector3i.from(x, y, z));
|
}
|
||||||
};
|
return world.getChunkAt(x, z);
|
||||||
|
}
|
||||||
if (isChunkLoad) {
|
|
||||||
// Delay to ensure the chunk is sent first, and then the lectern data
|
private void sendLecternData(GeyserSession session, Chunk chunk, List<BlockEntityInfo> blockEntityInfos) {
|
||||||
Bukkit.getScheduler().runTaskLater(this.plugin, lecternInfoGet, 5);
|
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||||
} else {
|
BlockEntityInfo info = blockEntityInfos.get(i);
|
||||||
Bukkit.getScheduler().runTask(this.plugin, lecternInfoGet);
|
Block block = chunk.getBlock(info.getX(), info.getY(), info.getZ());
|
||||||
|
sendLecternData(session, block, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sendLecternData(GeyserSession session, Block block, boolean isChunkLoad) {
|
||||||
|
NbtMap blockEntityTag = this.lecterns.getLecternData(block, isChunkLoad);
|
||||||
|
if (blockEntityTag != null) {
|
||||||
|
BlockEntityUtils.updateBlockEntity(session, blockEntityTag, BukkitUtils.getVector(block.getLocation()));
|
||||||
}
|
}
|
||||||
return LecternInventoryTranslator.getBaseLecternTag(x, y, z, 0).build(); // Will be updated later
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldExpectLecternHandled() {
|
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,40 +180,16 @@ public class GeyserSpigotWorldManager extends WorldManager {
|
|||||||
@Override
|
@Override
|
||||||
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||||
CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>();
|
CompletableFuture<@Nullable CompoundTag> future = new CompletableFuture<>();
|
||||||
// Paper 1.19.3 complains about async access otherwise.
|
|
||||||
// java.lang.IllegalStateException: Tile is null, asynchronous access?
|
|
||||||
Bukkit.getScheduler().runTask(this.plugin, () -> {
|
|
||||||
Player bukkitPlayer;
|
Player bukkitPlayer;
|
||||||
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
if ((bukkitPlayer = Bukkit.getPlayer(session.getPlayerEntity().getUuid())) == null) {
|
||||||
future.complete(null);
|
future.complete(null);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
|
||||||
BlockState state = block.getState();
|
|
||||||
if (state instanceof Banner banner) {
|
|
||||||
ListTag list = new ListTag("Patterns");
|
|
||||||
for (int i = 0; i < banner.numberOfPatterns(); i++) {
|
|
||||||
Pattern pattern = banner.getPattern(i);
|
|
||||||
list.add(BannerTranslator.getJavaPatternTag(pattern.getPattern().getIdentifier(), pattern.getColor().ordinal()));
|
|
||||||
}
|
|
||||||
|
|
||||||
CompoundTag root = addToBlockEntityTag(list);
|
|
||||||
|
|
||||||
future.complete(root);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
future.complete(null);
|
|
||||||
});
|
|
||||||
return future;
|
return future;
|
||||||
}
|
}
|
||||||
|
Block block = bukkitPlayer.getWorld().getBlockAt(x, y, z);
|
||||||
private CompoundTag addToBlockEntityTag(Tag tag) {
|
// Paper 1.19.3 complains about async access otherwise.
|
||||||
CompoundTag compoundTag = new CompoundTag("");
|
// java.lang.IllegalStateException: Tile is null, asynchronous access?
|
||||||
CompoundTag blockEntityTag = new CompoundTag("BlockEntityTag");
|
SchedulerUtils.runTask(this.plugin, () -> future.complete(PickBlockUtils.pickBlock(block)), block);
|
||||||
blockEntityTag.put(tag);
|
return future;
|
||||||
compoundTag.put(blockEntityTag);
|
|
||||||
return compoundTag;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -5,6 +5,7 @@ website: ${url}
|
|||||||
version: ${version}
|
version: ${version}
|
||||||
softdepend: ["ViaVersion", "floodgate"]
|
softdepend: ["ViaVersion", "floodgate"]
|
||||||
api-version: 1.13
|
api-version: 1.13
|
||||||
|
folia-supported: true
|
||||||
commands:
|
commands:
|
||||||
geyser:
|
geyser:
|
||||||
description: The main command for Geyser.
|
description: The main command for Geyser.
|
||||||
|
@ -49,6 +49,10 @@ dependencies {
|
|||||||
// Adventure text serialization
|
// Adventure text serialization
|
||||||
api(libs.bundles.adventure)
|
api(libs.bundles.adventure)
|
||||||
|
|
||||||
|
api(libs.erosion.common) {
|
||||||
|
isTransitive = false
|
||||||
|
}
|
||||||
|
|
||||||
// Test
|
// Test
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
|
|
||||||
|
@ -50,6 +50,7 @@ import org.geysermc.api.Geyser;
|
|||||||
import org.geysermc.common.PlatformType;
|
import org.geysermc.common.PlatformType;
|
||||||
import org.geysermc.cumulus.form.Form;
|
import org.geysermc.cumulus.form.Form;
|
||||||
import org.geysermc.cumulus.form.util.FormBuilder;
|
import org.geysermc.cumulus.form.util.FormBuilder;
|
||||||
|
import org.geysermc.erosion.packet.Packets;
|
||||||
import org.geysermc.floodgate.crypto.AesCipher;
|
import org.geysermc.floodgate.crypto.AesCipher;
|
||||||
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
import org.geysermc.floodgate.crypto.AesKeyProducer;
|
||||||
import org.geysermc.floodgate.crypto.Base64Topping;
|
import org.geysermc.floodgate.crypto.Base64Topping;
|
||||||
@ -67,6 +68,7 @@ import org.geysermc.geyser.api.network.RemoteServer;
|
|||||||
import org.geysermc.geyser.command.GeyserCommandManager;
|
import org.geysermc.geyser.command.GeyserCommandManager;
|
||||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
|
import org.geysermc.geyser.erosion.UnixSocketClientListener;
|
||||||
import org.geysermc.geyser.event.GeyserEventBus;
|
import org.geysermc.geyser.event.GeyserEventBus;
|
||||||
import org.geysermc.geyser.extension.GeyserExtensionManager;
|
import org.geysermc.geyser.extension.GeyserExtensionManager;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
@ -140,6 +142,8 @@ public class GeyserImpl implements GeyserApi {
|
|||||||
private FloodgateSkinUploader skinUploader;
|
private FloodgateSkinUploader skinUploader;
|
||||||
private NewsHandler newsHandler;
|
private NewsHandler newsHandler;
|
||||||
|
|
||||||
|
private UnixSocketClientListener erosionUnixListener;
|
||||||
|
|
||||||
private volatile boolean shuttingDown = false;
|
private volatile boolean shuttingDown = false;
|
||||||
|
|
||||||
private ScheduledExecutorService scheduledThread;
|
private ScheduledExecutorService scheduledThread;
|
||||||
@ -293,6 +297,14 @@ public class GeyserImpl implements GeyserApi {
|
|||||||
|
|
||||||
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
|
this.newsHandler = new NewsHandler(BRANCH, this.buildNumber());
|
||||||
|
|
||||||
|
Packets.initGeyser();
|
||||||
|
|
||||||
|
if (Epoll.isAvailable()) {
|
||||||
|
this.erosionUnixListener = new UnixSocketClientListener();
|
||||||
|
} else {
|
||||||
|
logger.debug("Epoll is not available; Erosion's Unix socket handling will not work.");
|
||||||
|
}
|
||||||
|
|
||||||
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
|
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
|
||||||
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
|
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
|
||||||
|
|
||||||
@ -570,6 +582,10 @@ public class GeyserImpl implements GeyserApi {
|
|||||||
newsHandler.shutdown();
|
newsHandler.shutdown();
|
||||||
this.commandManager().getCommands().clear();
|
this.commandManager().getCommands().clear();
|
||||||
|
|
||||||
|
if (this.erosionUnixListener != null) {
|
||||||
|
this.erosionUnixListener.close();
|
||||||
|
}
|
||||||
|
|
||||||
ResourcePack.PACKS.clear();
|
ResourcePack.PACKS.clear();
|
||||||
|
|
||||||
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
this.eventBus.fire(new GeyserShutdownEvent(this.extensionManager, this.eventBus));
|
||||||
|
@ -148,6 +148,7 @@ public final class EntityDefinitions {
|
|||||||
public static final EntityDefinition<AbstractSkeletonEntity> STRAY;
|
public static final EntityDefinition<AbstractSkeletonEntity> STRAY;
|
||||||
public static final EntityDefinition<StriderEntity> STRIDER;
|
public static final EntityDefinition<StriderEntity> STRIDER;
|
||||||
public static final EntityDefinition<TadpoleEntity> TADPOLE;
|
public static final EntityDefinition<TadpoleEntity> TADPOLE;
|
||||||
|
public static final EntityDefinition<TextDisplayEntity> TEXT_DISPLAY;
|
||||||
public static final EntityDefinition<TNTEntity> TNT;
|
public static final EntityDefinition<TNTEntity> TNT;
|
||||||
public static final EntityDefinition<MinecartEntity> TNT_MINECART;
|
public static final EntityDefinition<MinecartEntity> TNT_MINECART;
|
||||||
public static final EntityDefinition<TraderLlamaEntity> TRADER_LLAMA;
|
public static final EntityDefinition<TraderLlamaEntity> TRADER_LLAMA;
|
||||||
@ -295,6 +296,28 @@ public final class EntityDefinitions {
|
|||||||
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
|
.addTranslator(MetadataType.INT, TNTEntity::setFuseLength)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
EntityDefinition<Entity> displayBase = EntityDefinition.inherited(entityBase.factory(), entityBase)
|
||||||
|
.addTranslator(null) // Interpolation start ticks
|
||||||
|
.addTranslator(null) // Interpolation duration ID
|
||||||
|
.addTranslator(null) // Translation
|
||||||
|
.addTranslator(null) // Scale
|
||||||
|
.addTranslator(null) // Left rotation
|
||||||
|
.addTranslator(null) // Right rotation
|
||||||
|
.addTranslator(null) // Billboard render constraints
|
||||||
|
.addTranslator(null) // Brightness override
|
||||||
|
.addTranslator(null) // View range
|
||||||
|
.addTranslator(null) // Shadow radius
|
||||||
|
.addTranslator(null) // Shadow strength
|
||||||
|
.addTranslator(null) // Width
|
||||||
|
.addTranslator(null) // Height
|
||||||
|
.addTranslator(null) // Glow color override
|
||||||
|
.build();
|
||||||
|
TEXT_DISPLAY = EntityDefinition.inherited(TextDisplayEntity::new, displayBase)
|
||||||
|
.type(EntityType.TEXT_DISPLAY)
|
||||||
|
.identifier("minecraft:armor_stand")
|
||||||
|
.addTranslator(MetadataType.CHAT, TextDisplayEntity::setText)
|
||||||
|
.build();
|
||||||
|
|
||||||
EntityDefinition<FireballEntity> fireballBase = EntityDefinition.inherited(FireballEntity::new, entityBase)
|
EntityDefinition<FireballEntity> fireballBase = EntityDefinition.inherited(FireballEntity::new, entityBase)
|
||||||
.addTranslator(null) // Item
|
.addTranslator(null) // Item
|
||||||
.build();
|
.build();
|
||||||
|
@ -30,12 +30,11 @@ import com.nukkitx.math.vector.Vector3f;
|
|||||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
|
import com.nukkitx.protocol.bedrock.packet.PlaySoundPacket;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||||
import org.geysermc.geyser.level.block.BlockPositionIterator;
|
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
import org.geysermc.geyser.level.physics.BoundingBox;
|
import org.geysermc.geyser.level.physics.BoundingBox;
|
||||||
import org.geysermc.geyser.registry.BlockRegistries;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.collision.BlockCollision;
|
import org.geysermc.geyser.translator.collision.BlockCollision;
|
||||||
import org.geysermc.geyser.util.BlockUtils;
|
import org.geysermc.geyser.util.BlockUtils;
|
||||||
|
@ -40,11 +40,12 @@ import org.geysermc.geyser.session.GeyserSession;
|
|||||||
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
|
import org.geysermc.geyser.translator.inventory.item.ItemTranslator;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class ItemEntity extends ThrowableEntity {
|
public class ItemEntity extends ThrowableEntity {
|
||||||
protected ItemData item;
|
protected ItemData item;
|
||||||
|
|
||||||
private int waterLevel = -1;
|
private CompletableFuture<Integer> waterLevel = CompletableFuture.completedFuture(-1);
|
||||||
|
|
||||||
public ItemEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
public ItemEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||||
@ -111,15 +112,15 @@ public class ItemEntity extends ThrowableEntity {
|
|||||||
@Override
|
@Override
|
||||||
protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
|
protected void moveAbsoluteImmediate(Vector3f position, float yaw, float pitch, float headYaw, boolean isOnGround, boolean teleported) {
|
||||||
float offset = definition.offset();
|
float offset = definition.offset();
|
||||||
if (waterLevel == 0) { // Item is in a full block of water
|
if (waterLevel.join() == 0) { // Item is in a full block of water
|
||||||
// Move the item entity down so it doesn't float above the water
|
// Move the item entity down so it doesn't float above the water
|
||||||
offset = -definition.offset();
|
offset = -definition.offset();
|
||||||
}
|
}
|
||||||
super.moveAbsoluteImmediate(position.add(0, offset, 0), 0, 0, 0, isOnGround, teleported);
|
super.moveAbsoluteImmediate(position.add(0, offset, 0), 0, 0, 0, isOnGround, teleported);
|
||||||
this.position = position;
|
this.position = position;
|
||||||
|
|
||||||
int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
|
waterLevel = session.getGeyser().getWorldManager().getBlockAtAsync(session, position.getFloorX(), position.getFloorY(), position.getFloorZ())
|
||||||
waterLevel = BlockStateValues.getWaterLevel(block);
|
.thenApply(BlockStateValues::getWaterLevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -144,6 +145,6 @@ public class ItemEntity extends ThrowableEntity {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean isInWater() {
|
protected boolean isInWater() {
|
||||||
return waterLevel != -1;
|
return waterLevel.join() != -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* 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.entity.type;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
|
||||||
|
import com.nukkitx.math.vector.Vector3f;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.geysermc.geyser.entity.EntityDefinition;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.translator.text.MessageTranslator;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
// Note: 1.19.4 requires that the billboard is set to something in order to show, on Java Edition
|
||||||
|
public class TextDisplayEntity extends Entity {
|
||||||
|
public TextDisplayEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||||
|
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializeMetadata() {
|
||||||
|
super.initializeMetadata();
|
||||||
|
// Remove armor stand body
|
||||||
|
this.dirtyMetadata.put(EntityData.SCALE, 0f);
|
||||||
|
this.dirtyMetadata.put(EntityData.NAMETAG_ALWAYS_SHOW, (byte) 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(EntityMetadata<Component, ?> entityMetadata) {
|
||||||
|
this.dirtyMetadata.put(EntityData.NAMETAG, MessageTranslator.convertMessage(entityMetadata.getValue()));
|
||||||
|
}
|
||||||
|
}
|
@ -34,12 +34,13 @@ import org.geysermc.geyser.level.block.BlockStateValues;
|
|||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class SquidEntity extends WaterEntity implements Tickable {
|
public class SquidEntity extends WaterEntity implements Tickable {
|
||||||
private float targetPitch;
|
private float targetPitch;
|
||||||
private float targetYaw;
|
private float targetYaw;
|
||||||
|
|
||||||
private boolean inWater;
|
private CompletableFuture<Boolean> inWater = CompletableFuture.completedFuture(Boolean.FALSE);
|
||||||
|
|
||||||
public SquidEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
public SquidEntity(GeyserSession session, int entityId, long geyserId, UUID uuid, EntityDefinition<?> definition, Vector3f position, Vector3f motion, float yaw, float pitch, float headYaw) {
|
||||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||||
@ -50,7 +51,7 @@ public class SquidEntity extends WaterEntity implements Tickable {
|
|||||||
boolean pitchChanged;
|
boolean pitchChanged;
|
||||||
boolean yawChanged;
|
boolean yawChanged;
|
||||||
float oldPitch = pitch;
|
float oldPitch = pitch;
|
||||||
if (inWater) {
|
if (inWater.join()) {
|
||||||
float oldYaw = yaw;
|
float oldYaw = yaw;
|
||||||
pitch += (targetPitch - pitch) * 0.1f;
|
pitch += (targetPitch - pitch) * 0.1f;
|
||||||
yaw += (targetYaw - yaw) * 0.1f;
|
yaw += (targetYaw - yaw) * 0.1f;
|
||||||
@ -93,7 +94,7 @@ public class SquidEntity extends WaterEntity implements Tickable {
|
|||||||
@Override
|
@Override
|
||||||
public void setYaw(float yaw) {
|
public void setYaw(float yaw) {
|
||||||
// Let the Java server control yaw when the squid is out of water
|
// Let the Java server control yaw when the squid is out of water
|
||||||
if (!inWater) {
|
if (!inWater.join()) {
|
||||||
this.yaw = yaw;
|
this.yaw = yaw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,10 +128,10 @@ public class SquidEntity extends WaterEntity implements Tickable {
|
|||||||
|
|
||||||
private void checkInWater() {
|
private void checkInWater() {
|
||||||
if (getFlag(EntityFlag.RIDING)) {
|
if (getFlag(EntityFlag.RIDING)) {
|
||||||
inWater = false;
|
inWater = CompletableFuture.completedFuture(false);
|
||||||
} else {
|
} else {
|
||||||
int block = session.getGeyser().getWorldManager().getBlockAt(session, position.toInt());
|
inWater = session.getGeyser().getWorldManager().getBlockAtAsync(session, position.toInt())
|
||||||
inWater = BlockStateValues.getWaterLevel(block) != -1;
|
.thenApply(block -> BlockStateValues.getWaterLevel(block) != -1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,6 +43,15 @@ public class ShulkerEntity extends GolemEntity {
|
|||||||
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
super(session, entityId, geyserId, uuid, definition, position, motion, yaw, pitch, headYaw);
|
||||||
// Indicate that invisibility should be fixed through the resource pack
|
// Indicate that invisibility should be fixed through the resource pack
|
||||||
setFlag(EntityFlag.BRIBED, true);
|
setFlag(EntityFlag.BRIBED, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void initializeMetadata() {
|
||||||
|
super.initializeMetadata();
|
||||||
|
// As of 1.19.4, it seems Java no longer sends the shulker color if it's the default color on initial spawn
|
||||||
|
// We still need the special case for 16 color in setShulkerColor though as it will send it for an entity metadata update
|
||||||
|
dirtyMetadata.put(EntityData.VARIANT, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAttachedFace(EntityMetadata<Direction, ?> entityMetadata) {
|
public void setAttachedFace(EntityMetadata<Direction, ?> entityMetadata) {
|
||||||
|
@ -0,0 +1,88 @@
|
|||||||
|
/*
|
||||||
|
* 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.erosion;
|
||||||
|
|
||||||
|
import org.geysermc.erosion.packet.geyserbound.*;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public abstract class AbstractGeyserboundPacketHandler implements GeyserboundPacketHandler {
|
||||||
|
protected final GeyserSession session;
|
||||||
|
|
||||||
|
public AbstractGeyserboundPacketHandler(GeyserSession session) {
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBatchBlockId(GeyserboundBatchBlockIdPacket packet) {
|
||||||
|
illegalPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBlockEntity(GeyserboundBlockEntityPacket packet) {
|
||||||
|
illegalPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBlockId(GeyserboundBlockIdPacket packet) {
|
||||||
|
illegalPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBlockLookupFail(GeyserboundBlockLookupFailPacket packet) {
|
||||||
|
illegalPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBlockPlace(GeyserboundBlockPlacePacket packet) {
|
||||||
|
illegalPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePistonEvent(GeyserboundPistonEventPacket packet) {
|
||||||
|
illegalPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePickBlock(GeyserboundPickBlockPacket packet) {
|
||||||
|
illegalPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is this handler actually listening to any packets?
|
||||||
|
*/
|
||||||
|
public abstract boolean isActive();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public abstract GeyserboundPacketHandlerImpl getAsActive();
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final void illegalPacket(GeyserboundPacket packet) {
|
||||||
|
session.getGeyser().getLogger().warning("Illegal packet sent from backend server! " + packet);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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.erosion;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.Unpooled;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import org.geysermc.erosion.Constants;
|
||||||
|
import org.geysermc.erosion.packet.ErosionPacketSender;
|
||||||
|
import org.geysermc.erosion.packet.Packets;
|
||||||
|
import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.util.PluginMessageUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public record GeyserErosionPacketSender(GeyserSession session) implements ErosionPacketSender<BackendboundPacket> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendPacket(BackendboundPacket packet) {
|
||||||
|
ByteBuf buf = Unpooled.buffer();
|
||||||
|
try {
|
||||||
|
Packets.encode(buf, packet);
|
||||||
|
byte[] bytes = new byte[buf.readableBytes()];
|
||||||
|
buf.readBytes(bytes);
|
||||||
|
PluginMessageUtils.sendMessage(session, Constants.PLUGIN_MESSAGE, bytes);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
buf.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setChannel(Channel channel) {
|
||||||
|
}
|
||||||
|
}
|
@ -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.erosion;
|
||||||
|
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import org.geysermc.erosion.netty.NettyPacketSender;
|
||||||
|
import org.geysermc.erosion.packet.ErosionPacketHandler;
|
||||||
|
import org.geysermc.erosion.packet.geyserbound.GeyserboundHandshakePacket;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
public final class GeyserboundHandshakePacketHandler extends AbstractGeyserboundPacketHandler {
|
||||||
|
|
||||||
|
public GeyserboundHandshakePacketHandler(GeyserSession session) {
|
||||||
|
super(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHandshake(GeyserboundHandshakePacket packet) {
|
||||||
|
boolean useTcp = packet.getTransportType().getSocketAddress() == null;
|
||||||
|
GeyserboundPacketHandlerImpl handler = new GeyserboundPacketHandlerImpl(session, useTcp ? new GeyserErosionPacketSender(session) : new NettyPacketSender<>());
|
||||||
|
session.setErosionHandler(handler);
|
||||||
|
if (!useTcp) {
|
||||||
|
if (session.getGeyser().getErosionUnixListener() == null) {
|
||||||
|
session.disconnect("Erosion configurations using Unix socket handling are not supported on this hardware!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
session.getGeyser().getErosionUnixListener().createClient(handler, packet.getTransportType().getSocketAddress());
|
||||||
|
} else {
|
||||||
|
handler.onConnect();
|
||||||
|
}
|
||||||
|
session.ensureInEventLoop(() -> session.getChunkCache().clear());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable GeyserboundPacketHandlerImpl getAsActive() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ErosionPacketHandler setChannel(Channel channel) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,209 @@
|
|||||||
|
/*
|
||||||
|
* 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.erosion;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.level.block.value.PistonValueType;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import com.nukkitx.protocol.bedrock.data.SoundEvent;
|
||||||
|
import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMaps;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.IntArrays;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.geysermc.erosion.packet.ErosionPacketHandler;
|
||||||
|
import org.geysermc.erosion.packet.ErosionPacketSender;
|
||||||
|
import org.geysermc.erosion.packet.backendbound.BackendboundInitializePacket;
|
||||||
|
import org.geysermc.erosion.packet.backendbound.BackendboundPacket;
|
||||||
|
import org.geysermc.erosion.packet.geyserbound.*;
|
||||||
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
|
import org.geysermc.geyser.level.physics.Direction;
|
||||||
|
import org.geysermc.geyser.network.GameProtocol;
|
||||||
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
|
import org.geysermc.geyser.session.cache.PistonCache;
|
||||||
|
import org.geysermc.geyser.translator.level.block.entity.PistonBlockEntity;
|
||||||
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
public final class GeyserboundPacketHandlerImpl extends AbstractGeyserboundPacketHandler {
|
||||||
|
private final ErosionPacketSender<BackendboundPacket> packetSender;
|
||||||
|
@Setter
|
||||||
|
private CompletableFuture<Integer> pendingLookup = null;
|
||||||
|
@Getter
|
||||||
|
private final Int2ObjectMap<CompletableFuture<Integer>> asyncPendingLookups = Int2ObjectMaps.synchronize(new Int2ObjectOpenHashMap<>(4));
|
||||||
|
@Setter
|
||||||
|
private CompletableFuture<int[]> pendingBatchLookup = null;
|
||||||
|
@Setter
|
||||||
|
private CompletableFuture<CompoundTag> pickBlockLookup = null;
|
||||||
|
|
||||||
|
private final AtomicInteger nextTransactionId = new AtomicInteger(1);
|
||||||
|
|
||||||
|
public GeyserboundPacketHandlerImpl(GeyserSession session, ErosionPacketSender<BackendboundPacket> packetSender) {
|
||||||
|
super(session);
|
||||||
|
this.packetSender = packetSender;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBatchBlockId(GeyserboundBatchBlockIdPacket packet) {
|
||||||
|
if (this.pendingBatchLookup != null) {
|
||||||
|
this.pendingBatchLookup.complete(packet.getBlocks());
|
||||||
|
} else {
|
||||||
|
session.getGeyser().getLogger().warning("Batch block ID packet received with no future to complete.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBlockEntity(GeyserboundBlockEntityPacket packet) {
|
||||||
|
NbtMap nbt = packet.getNbt();
|
||||||
|
BlockEntityUtils.updateBlockEntity(session, nbt, Vector3i.from(nbt.getInt("x"), nbt.getInt("y"), nbt.getInt("z")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBlockId(GeyserboundBlockIdPacket packet) {
|
||||||
|
if (packet.getTransactionId() == 0) {
|
||||||
|
if (this.pendingLookup != null) {
|
||||||
|
this.pendingLookup.complete(packet.getBlockId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CompletableFuture<Integer> future = this.asyncPendingLookups.remove(packet.getTransactionId());
|
||||||
|
if (future != null) {
|
||||||
|
future.complete(packet.getBlockId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
session.getGeyser().getLogger().warning("Block ID packet received with no future to complete.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBlockLookupFail(GeyserboundBlockLookupFailPacket packet) {
|
||||||
|
if (packet.getTransactionId() == 0) {
|
||||||
|
if (this.pendingBatchLookup != null) {
|
||||||
|
this.pendingBatchLookup.complete(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int transactionId = packet.getTransactionId() - 1;
|
||||||
|
if (transactionId == 0) {
|
||||||
|
if (this.pendingLookup != null) {
|
||||||
|
this.pendingLookup.complete(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CompletableFuture<Integer> future = this.asyncPendingLookups.remove(transactionId);
|
||||||
|
if (future != null) {
|
||||||
|
future.complete(BlockStateValues.JAVA_AIR_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleBlockPlace(GeyserboundBlockPlacePacket packet) {
|
||||||
|
LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket();
|
||||||
|
placeBlockSoundPacket.setSound(SoundEvent.PLACE);
|
||||||
|
placeBlockSoundPacket.setPosition(packet.getPos().toFloat());
|
||||||
|
placeBlockSoundPacket.setBabySound(false);
|
||||||
|
placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(packet.getBlockId()));
|
||||||
|
placeBlockSoundPacket.setIdentifier(":");
|
||||||
|
session.sendUpstreamPacket(placeBlockSoundPacket);
|
||||||
|
session.setLastBlockPlacePosition(null);
|
||||||
|
session.setLastBlockPlacedId(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePickBlock(GeyserboundPickBlockPacket packet) {
|
||||||
|
if (this.pickBlockLookup != null) {
|
||||||
|
this.pickBlockLookup.complete(packet.getTag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handlePistonEvent(GeyserboundPistonEventPacket packet) {
|
||||||
|
Direction orientation = BlockStateValues.getPistonOrientation(packet.getBlockId());
|
||||||
|
Vector3i position = packet.getPos();
|
||||||
|
boolean isExtend = packet.isExtend();
|
||||||
|
|
||||||
|
var stream = packet.getAttachedBlocks()
|
||||||
|
.object2IntEntrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(entry -> BlockStateValues.canPistonMoveBlock(entry.getIntValue(), isExtend));
|
||||||
|
Object2IntMap<Vector3i> attachedBlocks = new Object2IntArrayMap<>();
|
||||||
|
stream.forEach(entry -> attachedBlocks.put(entry.getKey(), entry.getIntValue()));
|
||||||
|
|
||||||
|
session.executeInEventLoop(() -> {
|
||||||
|
PistonCache pistonCache = session.getPistonCache();
|
||||||
|
PistonBlockEntity blockEntity = pistonCache.getPistons().computeIfAbsent(position, pos ->
|
||||||
|
new PistonBlockEntity(session, position, orientation, packet.isSticky(), !isExtend));
|
||||||
|
blockEntity.setAction(isExtend ? PistonValueType.PUSHING : PistonValueType.PULLING, attachedBlocks);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleHandshake(GeyserboundHandshakePacket packet) {
|
||||||
|
this.close();
|
||||||
|
var handler = new GeyserboundHandshakePacketHandler(this.session);
|
||||||
|
session.setErosionHandler(handler);
|
||||||
|
handler.handleHandshake(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isActive() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public GeyserboundPacketHandlerImpl getAsActive() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConnect() {
|
||||||
|
sendPacket(new BackendboundInitializePacket(session.getPlayerEntity().getUuid(), GameProtocol.getJavaProtocolVersion()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendPacket(BackendboundPacket packet) {
|
||||||
|
this.packetSender.sendPacket(packet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
this.packetSender.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getNextTransactionId() {
|
||||||
|
return nextTransactionId.getAndIncrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ErosionPacketHandler setChannel(Channel channel) {
|
||||||
|
this.packetSender.setChannel(channel);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* 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.erosion;
|
||||||
|
|
||||||
|
import io.netty.bootstrap.Bootstrap;
|
||||||
|
import io.netty.channel.Channel;
|
||||||
|
import io.netty.channel.ChannelInitializer;
|
||||||
|
import io.netty.channel.EventLoopGroup;
|
||||||
|
import io.netty.channel.epoll.EpollDomainSocketChannel;
|
||||||
|
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||||
|
import org.geysermc.erosion.netty.impl.AbstractUnixSocketListener;
|
||||||
|
import org.geysermc.erosion.packet.geyserbound.GeyserboundPacketHandler;
|
||||||
|
|
||||||
|
import java.net.SocketAddress;
|
||||||
|
|
||||||
|
public final class UnixSocketClientListener extends AbstractUnixSocketListener {
|
||||||
|
private EventLoopGroup eventLoopGroup;
|
||||||
|
|
||||||
|
public void initializeEventLoopGroup() {
|
||||||
|
if (this.eventLoopGroup == null) {
|
||||||
|
this.eventLoopGroup = new EpollEventLoopGroup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void createClient(GeyserboundPacketHandler handler, SocketAddress address) {
|
||||||
|
initializeEventLoopGroup();
|
||||||
|
(new Bootstrap()
|
||||||
|
.channel(EpollDomainSocketChannel.class)
|
||||||
|
.handler(new ChannelInitializer<Channel>() {
|
||||||
|
@Override
|
||||||
|
protected void initChannel(Channel ch) {
|
||||||
|
initPipeline(ch, handler);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.group(this.eventLoopGroup.next())
|
||||||
|
.connect(address))
|
||||||
|
.syncUninterruptibly()
|
||||||
|
.channel();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (this.eventLoopGroup != null) {
|
||||||
|
this.eventLoopGroup.shutdownGracefully();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -25,20 +25,64 @@
|
|||||||
|
|
||||||
package org.geysermc.geyser.level;
|
package org.geysermc.geyser.level;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
|
||||||
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
import com.nukkitx.nbt.NbtMapBuilder;
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
|
import org.geysermc.erosion.packet.backendbound.*;
|
||||||
|
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||||
|
import org.geysermc.erosion.util.LecternUtils;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
public class GeyserWorldManager extends WorldManager {
|
public class GeyserWorldManager extends WorldManager {
|
||||||
private final Object2ObjectMap<String, String> gameruleCache = new Object2ObjectOpenHashMap<>();
|
private final Object2ObjectMap<String, String> gameruleCache = new Object2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
public int getBlockAt(GeyserSession session, int x, int y, int z) {
|
||||||
|
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||||
|
if (erosionHandler == null) {
|
||||||
return session.getChunkCache().getBlockAt(x, y, z);
|
return session.getChunkCache().getBlockAt(x, y, z);
|
||||||
}
|
}
|
||||||
|
CompletableFuture<Integer> future = new CompletableFuture<>(); // Boxes
|
||||||
|
erosionHandler.setPendingLookup(future);
|
||||||
|
erosionHandler.sendPacket(new BackendboundBlockRequestPacket(0, Vector3i.from(x, y, z)));
|
||||||
|
return future.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<Integer> getBlockAtAsync(GeyserSession session, int x, int y, int z) {
|
||||||
|
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||||
|
if (erosionHandler == null) {
|
||||||
|
return super.getBlockAtAsync(session, x, y, z);
|
||||||
|
}
|
||||||
|
CompletableFuture<Integer> future = new CompletableFuture<>(); // Boxes
|
||||||
|
int transactionId = erosionHandler.getNextTransactionId();
|
||||||
|
erosionHandler.getAsyncPendingLookups().put(transactionId, future);
|
||||||
|
erosionHandler.sendPacket(new BackendboundBlockRequestPacket(transactionId, Vector3i.from(x, y, z)));
|
||||||
|
return future;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getBlocksAt(GeyserSession session, BlockPositionIterator iter) {
|
||||||
|
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||||
|
if (erosionHandler == null) {
|
||||||
|
return super.getBlocksAt(session, iter);
|
||||||
|
}
|
||||||
|
CompletableFuture<int[]> future = new CompletableFuture<>();
|
||||||
|
erosionHandler.setPendingBatchLookup(future);
|
||||||
|
erosionHandler.sendPacket(new BackendboundBatchBlockRequestPacket(iter));
|
||||||
|
return future.join();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasOwnChunkCache() {
|
public boolean hasOwnChunkCache() {
|
||||||
@ -47,10 +91,31 @@ public class GeyserWorldManager extends WorldManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad) {
|
public void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos) {
|
||||||
|
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||||
|
if (erosionHandler == null) {
|
||||||
|
// No-op - don't send any additional information other than what the chunk has already sent
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
List<Vector3i> vectors = new ObjectArrayList<>(blockEntityInfos.size());
|
||||||
|
for (int i = 0; i < blockEntityInfos.size(); i++) {
|
||||||
|
BlockEntityInfo info = blockEntityInfos.get(i);
|
||||||
|
vectors.add(Vector3i.from(info.getX(), info.getY(), info.getZ()));
|
||||||
|
}
|
||||||
|
erosionHandler.sendPacket(new BackendboundBatchBlockEntityPacket(x, z, vectors));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendLecternData(GeyserSession session, int x, int y, int z) {
|
||||||
|
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||||
|
if (erosionHandler != null) {
|
||||||
|
erosionHandler.sendPacket(new BackendboundBlockEntityPacket(Vector3i.from(x, y, z)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Without direct server access, we can't get lectern information on-the-fly.
|
// Without direct server access, we can't get lectern information on-the-fly.
|
||||||
// I should have set this up so it's only called when there is a book in the block state. - Camotoy
|
// I should have set this up so it's only called when there is a book in the block state. - Camotoy
|
||||||
NbtMapBuilder lecternTag = LecternInventoryTranslator.getBaseLecternTag(x, y, z, 1);
|
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x, y, z, 1);
|
||||||
lecternTag.putCompound("book", NbtMap.builder()
|
lecternTag.putCompound("book", NbtMap.builder()
|
||||||
.putByte("Count", (byte) 1)
|
.putByte("Count", (byte) 1)
|
||||||
.putShort("Damage", (short) 0)
|
.putShort("Damage", (short) 0)
|
||||||
@ -61,12 +126,12 @@ public class GeyserWorldManager extends WorldManager {
|
|||||||
.build())
|
.build())
|
||||||
.build());
|
.build());
|
||||||
lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately. Usually.
|
lecternTag.putInt("page", -1); // I'm surprisingly glad this exists - it forces Bedrock to stop reading immediately. Usually.
|
||||||
return lecternTag.build();
|
BlockEntityUtils.updateBlockEntity(session, lecternTag.build(), Vector3i.from(x, y, z));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean shouldExpectLecternHandled() {
|
public boolean shouldExpectLecternHandled(GeyserSession session) {
|
||||||
return false;
|
return session.getErosionHandler().isActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -99,4 +164,17 @@ public class GeyserWorldManager extends WorldManager {
|
|||||||
public boolean hasPermission(GeyserSession session, String permission) {
|
public boolean hasPermission(GeyserSession session, String permission) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nonnull
|
||||||
|
@Override
|
||||||
|
public CompletableFuture<@Nullable CompoundTag> getPickItemNbt(GeyserSession session, int x, int y, int z, boolean addNbtData) {
|
||||||
|
var erosionHandler = session.getErosionHandler().getAsActive();
|
||||||
|
if (erosionHandler == null) {
|
||||||
|
return super.getPickItemNbt(session, x, y, z, addNbtData);
|
||||||
|
}
|
||||||
|
CompletableFuture<CompoundTag> future = new CompletableFuture<>();
|
||||||
|
erosionHandler.setPickBlockLookup(future);
|
||||||
|
erosionHandler.sendPacket(new BackendboundPickBlockPacket(Vector3i.from(x, y, z)));
|
||||||
|
return future;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,14 +26,16 @@
|
|||||||
package org.geysermc.geyser.level;
|
package org.geysermc.geyser.level;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.level.block.BlockEntityInfo;
|
||||||
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
|
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
|
||||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
@ -68,6 +70,23 @@ public abstract class WorldManager {
|
|||||||
*/
|
*/
|
||||||
public abstract int getBlockAt(GeyserSession session, int x, int y, int z);
|
public abstract int getBlockAt(GeyserSession session, int x, int y, int z);
|
||||||
|
|
||||||
|
public final CompletableFuture<Integer> getBlockAtAsync(GeyserSession session, Vector3i vector) {
|
||||||
|
return this.getBlockAtAsync(session, vector.getX(), vector.getY(), vector.getZ());
|
||||||
|
}
|
||||||
|
|
||||||
|
public CompletableFuture<Integer> getBlockAtAsync(GeyserSession session, int x, int y, int z) {
|
||||||
|
return CompletableFuture.completedFuture(this.getBlockAt(session, x, y, z));
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getBlocksAt(GeyserSession session, BlockPositionIterator iter) {
|
||||||
|
int[] blocks = new int[iter.getMaxIterations()];
|
||||||
|
for (; iter.hasNext(); iter.next()) {
|
||||||
|
int networkId = this.getBlockAt(session, iter.getX(), iter.getY(), iter.getZ());
|
||||||
|
blocks[iter.getIteration()] = networkId;
|
||||||
|
}
|
||||||
|
return blocks;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether or not this world manager requires a separate chunk cache/has access to more block data than the chunk cache.
|
* Checks whether or not this world manager requires a separate chunk cache/has access to more block data than the chunk cache.
|
||||||
* <p>
|
* <p>
|
||||||
@ -89,20 +108,28 @@ public abstract class WorldManager {
|
|||||||
* We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity
|
* We solve this problem by querying all loaded lecterns, where possible, and sending their information in a block entity
|
||||||
* tag.
|
* tag.
|
||||||
*
|
*
|
||||||
|
* Note that the lectern data may be sent asynchronously.
|
||||||
|
*
|
||||||
* @param session the session of the player
|
* @param session the session of the player
|
||||||
* @param x the x coordinate of the lectern
|
* @param x the x coordinate of the lectern
|
||||||
* @param y the y coordinate of the lectern
|
* @param y the y coordinate of the lectern
|
||||||
* @param z the z coordinate of the lectern
|
* @param z the z coordinate of the lectern
|
||||||
* @param isChunkLoad if this is called during a chunk load or not. Changes behavior in certain instances.
|
|
||||||
* @return the Bedrock lectern block entity tag. This may not be the exact block entity tag - for example, Spigot's
|
|
||||||
* block handled must be done on the server thread, so we send the tag manually there.
|
|
||||||
*/
|
*/
|
||||||
public abstract NbtMap getLecternDataAt(GeyserSession session, int x, int y, int z, boolean isChunkLoad);
|
public abstract void sendLecternData(GeyserSession session, int x, int y, int z);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link #sendLecternData(GeyserSession, int, int, int)} but batched for chunks.
|
||||||
|
*
|
||||||
|
* @param x chunk x
|
||||||
|
* @param z chunk z
|
||||||
|
* @param blockEntityInfos a list of coordinates (chunk local) to grab lecterns from.
|
||||||
|
*/
|
||||||
|
public abstract void sendLecternData(GeyserSession session, int x, int z, List<BlockEntityInfo> blockEntityInfos);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
|
* @return whether we should expect lectern data to update, or if we have to fall back on a workaround.
|
||||||
*/
|
*/
|
||||||
public abstract boolean shouldExpectLecternHandled();
|
public abstract boolean shouldExpectLecternHandled(GeyserSession session);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a gamerule value on the Java server
|
* Updates a gamerule value on the Java server
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2019-2022 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.level.block;
|
|
||||||
|
|
||||||
import com.nukkitx.network.util.Preconditions;
|
|
||||||
|
|
||||||
public class BlockPositionIterator {
|
|
||||||
private final int minX;
|
|
||||||
private final int minY;
|
|
||||||
private final int minZ;
|
|
||||||
|
|
||||||
private final int sizeX;
|
|
||||||
private final int sizeZ;
|
|
||||||
|
|
||||||
private int i = 0;
|
|
||||||
private final int maxI;
|
|
||||||
|
|
||||||
public BlockPositionIterator(int minX, int minY, int minZ, int maxX, int maxY, int maxZ) {
|
|
||||||
Preconditions.checkArgument(maxX >= minX, "maxX is not greater than or equal to minX");
|
|
||||||
Preconditions.checkArgument(maxY >= minY, "maxY is not greater than or equal to minY");
|
|
||||||
Preconditions.checkArgument(maxZ >= minZ, "maxZ is not greater than or equal to minZ");
|
|
||||||
|
|
||||||
this.minX = minX;
|
|
||||||
this.minY = minY;
|
|
||||||
this.minZ = minZ;
|
|
||||||
|
|
||||||
this.sizeX = maxX - minX + 1;
|
|
||||||
int sizeY = maxY - minY + 1;
|
|
||||||
this.sizeZ = maxZ - minZ + 1;
|
|
||||||
this.maxI = sizeX * sizeY * sizeZ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasNext() {
|
|
||||||
return i < maxI;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void next() {
|
|
||||||
// Iterate in zxy order
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getX() {
|
|
||||||
return ((i / sizeZ) % sizeX) + minX;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getY() {
|
|
||||||
return (i / sizeZ / sizeX) + minY;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getZ() {
|
|
||||||
return (i % sizeZ) + minZ;
|
|
||||||
}
|
|
||||||
}
|
|
@ -33,10 +33,10 @@ import com.nukkitx.protocol.bedrock.data.entity.EntityFlag;
|
|||||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import org.geysermc.erosion.util.BlockPositionIterator;
|
||||||
import org.geysermc.geyser.entity.EntityDefinitions;
|
import org.geysermc.geyser.entity.EntityDefinitions;
|
||||||
import org.geysermc.geyser.entity.type.Entity;
|
import org.geysermc.geyser.entity.type.Entity;
|
||||||
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
import org.geysermc.geyser.entity.type.player.PlayerEntity;
|
||||||
import org.geysermc.geyser.level.block.BlockPositionIterator;
|
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.session.cache.PistonCache;
|
import org.geysermc.geyser.session.cache.PistonCache;
|
||||||
@ -215,7 +215,7 @@ public class CollisionManager {
|
|||||||
int minCollisionZ = (int) Math.floor(position.getZ() - ((box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand));
|
int minCollisionZ = (int) Math.floor(position.getZ() - ((box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand));
|
||||||
int maxCollisionZ = (int) Math.floor(position.getZ() + (box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand);
|
int maxCollisionZ = (int) Math.floor(position.getZ() + (box.getSizeZ() / 2) + COLLISION_TOLERANCE + pistonExpand);
|
||||||
|
|
||||||
return new BlockPositionIterator(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ);
|
return BlockPositionIterator.fromMinMax(minCollisionX, minCollisionY, minCollisionZ, maxCollisionX, maxCollisionY, maxCollisionZ);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BlockPositionIterator playerCollidableBlocksIterator() {
|
public BlockPositionIterator playerCollidableBlocksIterator() {
|
||||||
@ -235,8 +235,9 @@ public class CollisionManager {
|
|||||||
|
|
||||||
// Used when correction code needs to be run before the main correction
|
// Used when correction code needs to be run before the main correction
|
||||||
BlockPositionIterator iter = session.getCollisionManager().playerCollidableBlocksIterator();
|
BlockPositionIterator iter = session.getCollisionManager().playerCollidableBlocksIterator();
|
||||||
for (; iter.hasNext(); iter.next()) {
|
int[] blocks = session.getGeyser().getWorldManager().getBlocksAt(session, iter);
|
||||||
BlockCollision blockCollision = BlockUtils.getCollisionAt(session, iter.getX(), iter.getY(), iter.getZ());
|
for (iter.reset(); iter.hasNext(); iter.next()) {
|
||||||
|
BlockCollision blockCollision = BlockUtils.getCollision(blocks[iter.getIteration()]);
|
||||||
if (blockCollision != null) {
|
if (blockCollision != null) {
|
||||||
blockCollision.beforeCorrectPosition(iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox);
|
blockCollision.beforeCorrectPosition(iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox);
|
||||||
}
|
}
|
||||||
@ -244,7 +245,7 @@ public class CollisionManager {
|
|||||||
|
|
||||||
// Main correction code
|
// Main correction code
|
||||||
for (iter.reset(); iter.hasNext(); iter.next()) {
|
for (iter.reset(); iter.hasNext(); iter.next()) {
|
||||||
BlockCollision blockCollision = BlockUtils.getCollisionAt(session, iter.getX(), iter.getY(), iter.getZ());
|
BlockCollision blockCollision = BlockUtils.getCollision(blocks[iter.getIteration()]);
|
||||||
if (blockCollision != null) {
|
if (blockCollision != null) {
|
||||||
if (!blockCollision.correctPosition(session, iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox)) {
|
if (!blockCollision.correctPosition(session, iter.getX(), iter.getY(), iter.getZ(), playerBoundingBox)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -108,7 +108,14 @@ public class ResourcePack {
|
|||||||
try (ZipFile zip = new ZipFile(file);
|
try (ZipFile zip = new ZipFile(file);
|
||||||
Stream<? extends ZipEntry> stream = zip.stream()) {
|
Stream<? extends ZipEntry> stream = zip.stream()) {
|
||||||
stream.forEach((x) -> {
|
stream.forEach((x) -> {
|
||||||
if (x.getName().contains("manifest.json")) {
|
String name = x.getName();
|
||||||
|
if (name.length() >= 80) {
|
||||||
|
GeyserImpl.getInstance().getLogger().warning("The resource pack " + file.getName()
|
||||||
|
+ " has a file in it that meets or exceeds 80 characters in its path (" + name
|
||||||
|
+ ", " + name.length() + " characters long). This will cause problems on some Bedrock platforms." +
|
||||||
|
" Please rename it to be shorter, or reduce the amount of folders needed to get to the file.");
|
||||||
|
}
|
||||||
|
if (name.contains("manifest.json")) {
|
||||||
try {
|
try {
|
||||||
ResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), ResourcePackManifest.class);
|
ResourcePackManifest manifest = FileUtils.loadJson(zip.getInputStream(x), ResourcePackManifest.class);
|
||||||
// Sometimes a pack_manifest file is present and not in a valid format,
|
// Sometimes a pack_manifest file is present and not in a valid format,
|
||||||
|
@ -112,6 +112,8 @@ import org.geysermc.geyser.entity.type.Entity;
|
|||||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||||
import org.geysermc.geyser.entity.type.Tickable;
|
import org.geysermc.geyser.entity.type.Tickable;
|
||||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||||
|
import org.geysermc.geyser.erosion.AbstractGeyserboundPacketHandler;
|
||||||
|
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
|
||||||
import org.geysermc.geyser.inventory.Inventory;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
import org.geysermc.geyser.inventory.PlayerInventory;
|
import org.geysermc.geyser.inventory.PlayerInventory;
|
||||||
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
import org.geysermc.geyser.inventory.recipe.GeyserRecipe;
|
||||||
@ -136,6 +138,7 @@ import org.geysermc.geyser.translator.text.MessageTranslator;
|
|||||||
import org.geysermc.geyser.util.ChunkUtils;
|
import org.geysermc.geyser.util.ChunkUtils;
|
||||||
import org.geysermc.geyser.util.DimensionUtils;
|
import org.geysermc.geyser.util.DimensionUtils;
|
||||||
import org.geysermc.geyser.util.LoginEncryptionUtils;
|
import org.geysermc.geyser.util.LoginEncryptionUtils;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
@ -168,6 +171,10 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
@Setter
|
@Setter
|
||||||
private JsonNode certChainData;
|
private JsonNode certChainData;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Setter
|
||||||
|
private AbstractGeyserboundPacketHandler erosionHandler;
|
||||||
|
|
||||||
@Accessors(fluent = true)
|
@Accessors(fluent = true)
|
||||||
@Setter
|
@Setter
|
||||||
private RemoteServer remoteServer;
|
private RemoteServer remoteServer;
|
||||||
@ -255,7 +262,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a list of all lectern locations and their block entity tags.
|
* Stores a list of all lectern locations and their block entity tags.
|
||||||
* See {@link WorldManager#getLecternDataAt(GeyserSession, int, int, int, boolean)}
|
* See {@link WorldManager#sendLecternData(GeyserSession, int, int, int)}
|
||||||
* for more information.
|
* for more information.
|
||||||
*/
|
*/
|
||||||
private final Set<Vector3i> lecternCache;
|
private final Set<Vector3i> lecternCache;
|
||||||
@ -557,6 +564,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
this.upstream = new UpstreamSession(bedrockServerSession);
|
this.upstream = new UpstreamSession(bedrockServerSession);
|
||||||
this.eventLoop = eventLoop;
|
this.eventLoop = eventLoop;
|
||||||
|
|
||||||
|
this.erosionHandler = new GeyserboundHandshakePacketHandler(this);
|
||||||
|
|
||||||
this.advancementsCache = new AdvancementsCache(this);
|
this.advancementsCache = new AdvancementsCache(this);
|
||||||
this.bookEditCache = new BookEditCache(this);
|
this.bookEditCache = new BookEditCache(this);
|
||||||
this.chunkCache = new ChunkCache(this);
|
this.chunkCache = new ChunkCache(this);
|
||||||
@ -585,7 +594,7 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
this.spawned = false;
|
this.spawned = false;
|
||||||
this.loggedIn = false;
|
this.loggedIn = false;
|
||||||
|
|
||||||
if (geyser.getWorldManager().shouldExpectLecternHandled()) {
|
if (geyser.getWorldManager().shouldExpectLecternHandled(this)) {
|
||||||
// Unneeded on these platforms
|
// Unneeded on these platforms
|
||||||
this.lecternCache = null;
|
this.lecternCache = null;
|
||||||
} else {
|
} else {
|
||||||
@ -1094,6 +1103,8 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||||||
tickThread.cancel(false);
|
tickThread.cancel(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erosionHandler.close();
|
||||||
|
|
||||||
closed = true;
|
closed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ import com.nukkitx.nbt.NbtMap;
|
|||||||
import com.nukkitx.nbt.NbtMapBuilder;
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
import com.nukkitx.nbt.NbtType;
|
import com.nukkitx.nbt.NbtType;
|
||||||
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
|
||||||
|
import org.geysermc.erosion.util.LecternUtils;
|
||||||
import org.geysermc.geyser.inventory.GeyserItemStack;
|
import org.geysermc.geyser.inventory.GeyserItemStack;
|
||||||
import org.geysermc.geyser.inventory.Inventory;
|
import org.geysermc.geyser.inventory.Inventory;
|
||||||
import org.geysermc.geyser.inventory.LecternContainer;
|
import org.geysermc.geyser.inventory.LecternContainer;
|
||||||
@ -110,13 +111,13 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
|||||||
Vector3i position = session.getLastInteractionBlockPosition();
|
Vector3i position = session.getLastInteractionBlockPosition();
|
||||||
// If shouldExpectLecternHandled returns true, this is already handled for us
|
// If shouldExpectLecternHandled returns true, this is already handled for us
|
||||||
// shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet
|
// shouldRefresh means that we should boot out the client on our side because their lectern GUI isn't updated yet
|
||||||
boolean shouldRefresh = !session.getGeyser().getWorldManager().shouldExpectLecternHandled() && !session.getLecternCache().contains(position);
|
boolean shouldRefresh = !session.getGeyser().getWorldManager().shouldExpectLecternHandled(session) && !session.getLecternCache().contains(position);
|
||||||
|
|
||||||
NbtMap blockEntityTag;
|
NbtMap blockEntityTag;
|
||||||
if (tag != null) {
|
if (tag != null) {
|
||||||
int pagesSize = ((ListTag) tag.get("pages")).size();
|
int pagesSize = ((ListTag) tag.get("pages")).size();
|
||||||
ItemData itemData = book.getItemData(session);
|
ItemData itemData = book.getItemData(session);
|
||||||
NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize);
|
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), pagesSize);
|
||||||
lecternTag.putCompound("book", NbtMap.builder()
|
lecternTag.putCompound("book", NbtMap.builder()
|
||||||
.putByte("Count", (byte) itemData.getCount())
|
.putByte("Count", (byte) itemData.getCount())
|
||||||
.putShort("Damage", (short) 0)
|
.putShort("Damage", (short) 0)
|
||||||
@ -127,7 +128,7 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
|||||||
blockEntityTag = lecternTag.build();
|
blockEntityTag = lecternTag.build();
|
||||||
} else {
|
} else {
|
||||||
// There is *a* book here, but... no NBT.
|
// There is *a* book here, but... no NBT.
|
||||||
NbtMapBuilder lecternTag = getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1);
|
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 1);
|
||||||
NbtMapBuilder bookTag = NbtMap.builder()
|
NbtMapBuilder bookTag = NbtMap.builder()
|
||||||
.putByte("Count", (byte) 1)
|
.putByte("Count", (byte) 1)
|
||||||
.putShort("Damage", (short) 0)
|
.putShort("Damage", (short) 0)
|
||||||
@ -162,20 +163,4 @@ public class LecternInventoryTranslator extends BaseInventoryTranslator {
|
|||||||
public Inventory createInventory(String name, int windowId, ContainerType containerType, PlayerInventory playerInventory) {
|
public Inventory createInventory(String name, int windowId, ContainerType containerType, PlayerInventory playerInventory) {
|
||||||
return new LecternContainer(name, windowId, this.size, containerType, playerInventory);
|
return new LecternContainer(name, windowId, this.size, containerType, playerInventory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NbtMapBuilder getBaseLecternTag(int x, int y, int z, int totalPages) {
|
|
||||||
NbtMapBuilder builder = NbtMap.builder()
|
|
||||||
.putInt("x", x)
|
|
||||||
.putInt("y", y)
|
|
||||||
.putInt("z", z)
|
|
||||||
.putString("id", "Lectern");
|
|
||||||
if (totalPages != 0) {
|
|
||||||
builder.putByte("hasBook", (byte) 1);
|
|
||||||
builder.putInt("totalPages", totalPages);
|
|
||||||
} else {
|
|
||||||
// Not usually needed, but helps with kicking out Bedrock players from reading the UI
|
|
||||||
builder.putByte("hasBook", (byte) 0);
|
|
||||||
}
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ public class PotionTranslator extends ItemTranslator {
|
|||||||
if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings);
|
if (itemStack.getNbt() == null) return super.translateToBedrock(itemStack, mapping, mappings);
|
||||||
Tag potionTag = itemStack.getNbt().get("Potion");
|
Tag potionTag = itemStack.getNbt().get("Potion");
|
||||||
if (potionTag instanceof StringTag) {
|
if (potionTag instanceof StringTag) {
|
||||||
|
int customItemId = CustomItemTranslator.getCustomItem(itemStack.getNbt(), mapping);
|
||||||
|
if (customItemId == -1) {
|
||||||
Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
|
Potion potion = Potion.getByJavaIdentifier(((StringTag) potionTag).getValue());
|
||||||
if (potion != null) {
|
if (potion != null) {
|
||||||
return ItemData.builder()
|
return ItemData.builder()
|
||||||
@ -57,6 +59,12 @@ public class PotionTranslator extends ItemTranslator {
|
|||||||
.tag(translateNbtToBedrock(itemStack.getNbt()));
|
.tag(translateNbtToBedrock(itemStack.getNbt()));
|
||||||
}
|
}
|
||||||
GeyserImpl.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue());
|
GeyserImpl.getInstance().getLogger().debug("Unknown Java potion: " + potionTag.getValue());
|
||||||
|
} else {
|
||||||
|
return ItemData.builder()
|
||||||
|
.id(customItemId)
|
||||||
|
.count(itemStack.getAmount())
|
||||||
|
.tag(translateNbtToBedrock(itemStack.getNbt()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.translateToBedrock(itemStack, mapping, mappings);
|
return super.translateToBedrock(itemStack, mapping, mappings);
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,8 @@ import javax.annotation.Nonnull;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static org.geysermc.erosion.util.BannerUtils.getJavaPatternTag;
|
||||||
|
|
||||||
@ItemRemapper
|
@ItemRemapper
|
||||||
public class BannerTranslator extends NbtItemStackTranslator {
|
public class BannerTranslator extends NbtItemStackTranslator {
|
||||||
/**
|
/**
|
||||||
@ -66,15 +68,6 @@ public class BannerTranslator extends NbtItemStackTranslator {
|
|||||||
OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("bo", 15));
|
OMINOUS_BANNER_PATTERN.add(getJavaPatternTag("bo", 15));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompoundTag getJavaPatternTag(String pattern, int color) {
|
|
||||||
StringTag patternType = new StringTag("Pattern", pattern);
|
|
||||||
IntTag colorTag = new IntTag("Color", color);
|
|
||||||
CompoundTag tag = new CompoundTag("");
|
|
||||||
tag.put(patternType);
|
|
||||||
tag.put(colorTag);
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BannerTranslator() {
|
public BannerTranslator() {
|
||||||
appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems())
|
appliedItems = Arrays.stream(Registries.ITEMS.forVersion(GameProtocol.DEFAULT_BEDROCK_CODEC.getProtocolVersion()).getItems())
|
||||||
.filter(entry -> entry.getJavaIdentifier().endsWith("banner"))
|
.filter(entry -> entry.getJavaIdentifier().endsWith("banner"))
|
||||||
@ -117,7 +110,7 @@ public class BannerTranslator extends NbtItemStackTranslator {
|
|||||||
* @return The Java edition format pattern nbt
|
* @return The Java edition format pattern nbt
|
||||||
*/
|
*/
|
||||||
public static CompoundTag getJavaBannerPattern(NbtMap pattern) {
|
public static CompoundTag getJavaBannerPattern(NbtMap pattern) {
|
||||||
return BannerTranslator.getJavaPatternTag(pattern.getString("Pattern"), 15 - pattern.getInt("Color"));
|
return getJavaPatternTag(pattern.getString("Pattern"), 15 - pattern.getInt("Color"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -224,7 +224,7 @@ public class PistonBlockEntity {
|
|||||||
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, blockInFront);
|
int blockId = session.getGeyser().getWorldManager().getBlockAt(session, blockInFront);
|
||||||
if (BlockStateValues.isPistonHead(blockId)) {
|
if (BlockStateValues.isPistonHead(blockId)) {
|
||||||
ChunkUtils.updateBlock(session, BlockStateValues.JAVA_AIR_ID, blockInFront);
|
ChunkUtils.updateBlock(session, BlockStateValues.JAVA_AIR_ID, blockInFront);
|
||||||
} else if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT && blockId == BlockStateValues.JAVA_AIR_ID) {
|
} else if ((session.getGeyser().getPlatformType() == PlatformType.SPIGOT || session.getErosionHandler().isActive()) && blockId == BlockStateValues.JAVA_AIR_ID) {
|
||||||
// Spigot removes the piston head from the cache, but we need to send the block update ourselves
|
// Spigot removes the piston head from the cache, but we need to send the block update ourselves
|
||||||
ChunkUtils.updateBlock(session, BlockStateValues.JAVA_AIR_ID, blockInFront);
|
ChunkUtils.updateBlock(session, BlockStateValues.JAVA_AIR_ID, blockInFront);
|
||||||
}
|
}
|
||||||
|
@ -71,13 +71,12 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
|
|||||||
boolean addNbtData = packet.isAddUserData() && blockMapping.isBlockEntity(); // Holding down CTRL
|
boolean addNbtData = packet.isAddUserData() && blockMapping.isBlockEntity(); // Holding down CTRL
|
||||||
if (BlockStateValues.getBannerColor(blockToPick) != -1 || addNbtData) {
|
if (BlockStateValues.getBannerColor(blockToPick) != -1 || addNbtData) {
|
||||||
session.getGeyser().getWorldManager().getPickItemNbt(session, vector.getX(), vector.getY(), vector.getZ(), addNbtData)
|
session.getGeyser().getWorldManager().getPickItemNbt(session, vector.getX(), vector.getY(), vector.getZ(), addNbtData)
|
||||||
.whenComplete((tag, ex) -> {
|
.whenComplete((tag, ex) -> session.ensureInEventLoop(() -> {
|
||||||
if (tag == null) {
|
if (tag == null) {
|
||||||
pickItem(session, blockMapping);
|
pickItem(session, blockMapping);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
session.ensureInEventLoop(() -> {
|
|
||||||
if (addNbtData) {
|
if (addNbtData) {
|
||||||
ListTag lore = new ListTag("Lore");
|
ListTag lore = new ListTag("Lore");
|
||||||
lore.add(new StringTag("", "\"(+NBT)\""));
|
lore.add(new StringTag("", "\"(+NBT)\""));
|
||||||
@ -93,8 +92,7 @@ public class BedrockBlockPickRequestTranslator extends PacketTranslator<BlockPic
|
|||||||
|
|
||||||
ItemStack itemStack = new ItemStack(mapping.getJavaId(), 1, tag);
|
ItemStack itemStack = new ItemStack(mapping.getJavaId(), 1, tag);
|
||||||
InventoryUtils.findOrCreateItem(session, itemStack);
|
InventoryUtils.findOrCreateItem(session, itemStack);
|
||||||
});
|
}));
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,10 +35,13 @@ import io.netty.buffer.Unpooled;
|
|||||||
import org.geysermc.cumulus.Forms;
|
import org.geysermc.cumulus.Forms;
|
||||||
import org.geysermc.cumulus.form.Form;
|
import org.geysermc.cumulus.form.Form;
|
||||||
import org.geysermc.cumulus.form.util.FormType;
|
import org.geysermc.cumulus.form.util.FormType;
|
||||||
|
import org.geysermc.erosion.Constants;
|
||||||
|
import org.geysermc.erosion.packet.ErosionPacket;
|
||||||
|
import org.geysermc.erosion.packet.Packets;
|
||||||
|
import org.geysermc.erosion.packet.geyserbound.GeyserboundPacket;
|
||||||
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
|
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
|
||||||
import org.geysermc.geyser.GeyserImpl;
|
import org.geysermc.geyser.GeyserImpl;
|
||||||
import org.geysermc.geyser.GeyserLogger;
|
import org.geysermc.geyser.GeyserLogger;
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||||
import org.geysermc.geyser.translator.protocol.Translator;
|
import org.geysermc.geyser.translator.protocol.Translator;
|
||||||
@ -51,14 +54,17 @@ public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCus
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, ClientboundCustomPayloadPacket packet) {
|
public void translate(GeyserSession session, ClientboundCustomPayloadPacket packet) {
|
||||||
// The only plugin messages it has to listen for are Floodgate plugin messages
|
String channel = packet.getChannel();
|
||||||
if (session.remoteServer().authType() != AuthType.FLOODGATE) {
|
|
||||||
|
if (channel.equals(Constants.PLUGIN_MESSAGE)) {
|
||||||
|
ByteBuf buf = Unpooled.wrappedBuffer(packet.getData());
|
||||||
|
ErosionPacket<?> erosionPacket = Packets.decode(buf);
|
||||||
|
((GeyserboundPacket) erosionPacket).handle(session.getErosionHandler());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String channel = packet.getChannel();
|
|
||||||
|
|
||||||
if (channel.equals(PluginMessageChannels.FORM)) {
|
if (channel.equals(PluginMessageChannels.FORM)) {
|
||||||
|
session.ensureInEventLoop(() -> {
|
||||||
byte[] data = packet.getData();
|
byte[] data = packet.getData();
|
||||||
|
|
||||||
// receive: first byte is form type, second and third are the id, remaining is the form data
|
// receive: first byte is form type, second and third are the id, remaining is the form data
|
||||||
@ -89,8 +95,10 @@ public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCus
|
|||||||
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(channel, finalData));
|
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(channel, finalData));
|
||||||
});
|
});
|
||||||
session.sendForm(form);
|
session.sendForm(form);
|
||||||
|
});
|
||||||
|
|
||||||
} else if (channel.equals(PluginMessageChannels.TRANSFER)) {
|
} else if (channel.equals(PluginMessageChannels.TRANSFER)) {
|
||||||
|
session.ensureInEventLoop(() -> {
|
||||||
byte[] data = packet.getData();
|
byte[] data = packet.getData();
|
||||||
|
|
||||||
// port (4 bytes), address (remaining data)
|
// port (4 bytes), address (remaining data)
|
||||||
@ -109,8 +117,10 @@ public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCus
|
|||||||
transferPacket.setAddress(address);
|
transferPacket.setAddress(address);
|
||||||
transferPacket.setPort(port);
|
transferPacket.setPort(port);
|
||||||
session.sendUpstreamPacket(transferPacket);
|
session.sendUpstreamPacket(transferPacket);
|
||||||
|
});
|
||||||
|
|
||||||
} else if (channel.equals(PluginMessageChannels.PACKET)) {
|
} else if (channel.equals(PluginMessageChannels.PACKET)) {
|
||||||
|
session.ensureInEventLoop(() -> {
|
||||||
logger.debug("A packet has been sent using the Floodgate api");
|
logger.debug("A packet has been sent using the Floodgate api");
|
||||||
byte[] data = packet.getData();
|
byte[] data = packet.getData();
|
||||||
|
|
||||||
@ -127,6 +137,13 @@ public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCus
|
|||||||
toSend.setPayload(packetData);
|
toSend.setPayload(packetData);
|
||||||
|
|
||||||
session.sendUpstreamPacket(toSend);
|
session.sendUpstreamPacket(toSend);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldExecuteInEventLoop() {
|
||||||
|
// For Erosion packets
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
|||||||
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
|
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
|
||||||
import org.geysermc.geyser.api.network.AuthType;
|
import org.geysermc.geyser.api.network.AuthType;
|
||||||
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
import org.geysermc.geyser.entity.type.player.SessionPlayerEntity;
|
||||||
|
import org.geysermc.geyser.erosion.GeyserboundHandshakePacketHandler;
|
||||||
import org.geysermc.geyser.level.JavaDimension;
|
import org.geysermc.geyser.level.JavaDimension;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.text.TextDecoration;
|
import org.geysermc.geyser.text.TextDecoration;
|
||||||
@ -57,6 +58,11 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
|||||||
SessionPlayerEntity entity = session.getPlayerEntity();
|
SessionPlayerEntity entity = session.getPlayerEntity();
|
||||||
entity.setEntityId(packet.getEntityId());
|
entity.setEntityId(packet.getEntityId());
|
||||||
|
|
||||||
|
if (session.getErosionHandler().isActive()) {
|
||||||
|
session.getErosionHandler().close();
|
||||||
|
session.setErosionHandler(new GeyserboundHandshakePacketHandler(session));
|
||||||
|
}
|
||||||
|
|
||||||
Map<String, JavaDimension> dimensions = session.getDimensions();
|
Map<String, JavaDimension> dimensions = session.getDimensions();
|
||||||
dimensions.clear();
|
dimensions.clear();
|
||||||
|
|
||||||
@ -129,6 +135,10 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
|||||||
|
|
||||||
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData()));
|
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:brand", PluginMessageUtils.getGeyserBrandData()));
|
||||||
|
|
||||||
|
// TODO don't send two packets
|
||||||
|
// if (true) {
|
||||||
|
// session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", Constants.PLUGIN_MESSAGE.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
// }
|
||||||
// register the plugin messaging channels used in Floodgate
|
// register the plugin messaging channels used in Floodgate
|
||||||
if (session.remoteServer().authType() == AuthType.FLOODGATE) {
|
if (session.remoteServer().authType() == AuthType.FLOODGATE) {
|
||||||
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", PluginMessageChannels.getFloodgateRegisterData()));
|
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket("minecraft:register", PluginMessageChannels.getFloodgateRegisterData()));
|
||||||
|
@ -58,15 +58,16 @@ public class JavaBlockEventTranslator extends PacketTranslator<ClientboundBlockE
|
|||||||
blockEventPacket.setEventType(1);
|
blockEventPacket.setEventType(1);
|
||||||
session.sendUpstreamPacket(blockEventPacket);
|
session.sendUpstreamPacket(blockEventPacket);
|
||||||
} else if (packet.getValue() instanceof NoteBlockValue) {
|
} else if (packet.getValue() instanceof NoteBlockValue) {
|
||||||
int blockState = session.getGeyser().getWorldManager().getBlockAt(session, position);
|
session.getGeyser().getWorldManager().getBlockAtAsync(session, position).thenAccept(blockState -> {
|
||||||
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
|
blockEventPacket.setEventData(BlockStateValues.getNoteblockPitch(blockState));
|
||||||
session.sendUpstreamPacket(blockEventPacket);
|
session.sendUpstreamPacket(blockEventPacket);
|
||||||
|
});
|
||||||
} else if (packet.getValue() instanceof PistonValue pistonValue) {
|
} else if (packet.getValue() instanceof PistonValue pistonValue) {
|
||||||
PistonValueType action = (PistonValueType) packet.getType();
|
PistonValueType action = (PistonValueType) packet.getType();
|
||||||
Direction direction = Direction.fromPistonValue(pistonValue.getDirection());
|
Direction direction = Direction.fromPistonValue(pistonValue.getDirection());
|
||||||
PistonCache pistonCache = session.getPistonCache();
|
PistonCache pistonCache = session.getPistonCache();
|
||||||
|
|
||||||
if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT) {
|
if (session.getGeyser().getPlatformType() == PlatformType.SPIGOT || session.getErosionHandler().isActive()) {
|
||||||
// Mostly handled in the GeyserPistonEvents class
|
// Mostly handled in the GeyserPistonEvents class
|
||||||
// Retracting sticky pistons is an exception, since the event is not called on Spigot from 1.13.2 - 1.17.1
|
// Retracting sticky pistons is an exception, since the event is not called on Spigot from 1.13.2 - 1.17.1
|
||||||
// See https://github.com/PaperMC/Paper/blob/6fa1983e9ce177a4a412d5b950fd978620174777/patches/server/0304-Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch
|
// See https://github.com/PaperMC/Paper/blob/6fa1983e9ce177a4a412d5b950fd978620174777/patches/server/0304-Fire-BlockPistonRetractEvent-for-all-empty-pistons.patch
|
||||||
|
@ -43,7 +43,7 @@ public class JavaBlockUpdateTranslator extends PacketTranslator<ClientboundBlock
|
|||||||
public void translate(GeyserSession session, ClientboundBlockUpdatePacket packet) {
|
public void translate(GeyserSession session, ClientboundBlockUpdatePacket packet) {
|
||||||
Vector3i pos = packet.getEntry().getPosition();
|
Vector3i pos = packet.getEntry().getPosition();
|
||||||
boolean updatePlacement = session.getGeyser().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event
|
boolean updatePlacement = session.getGeyser().getPlatformType() != PlatformType.SPIGOT && // Spigot simply listens for the block place event
|
||||||
session.getGeyser().getWorldManager().getBlockAt(session, pos) != packet.getEntry().getBlock();
|
!session.getErosionHandler().isActive() && session.getGeyser().getWorldManager().getBlockAt(session, pos) != packet.getEntry().getBlock();
|
||||||
session.getWorldCache().updateServerCorrectBlockState(pos, packet.getEntry().getBlock());
|
session.getWorldCache().updateServerCorrectBlockState(pos, packet.getEntry().getBlock());
|
||||||
if (updatePlacement) {
|
if (updatePlacement) {
|
||||||
this.checkPlace(session, packet);
|
this.checkPlace(session, packet);
|
||||||
|
@ -52,7 +52,7 @@ public class JavaForgetLevelChunkTranslator extends PacketTranslator<Clientbound
|
|||||||
}
|
}
|
||||||
removedSkulls.forEach(session.getSkullCache()::removeSkull);
|
removedSkulls.forEach(session.getSkullCache()::removeSkull);
|
||||||
|
|
||||||
if (!session.getGeyser().getWorldManager().shouldExpectLecternHandled()) {
|
if (!session.getGeyser().getWorldManager().shouldExpectLecternHandled(session)) {
|
||||||
// Do the same thing with lecterns
|
// Do the same thing with lecterns
|
||||||
Iterator<Vector3i> iterator = session.getLecternCache().iterator();
|
Iterator<Vector3i> iterator = session.getLecternCache().iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
|
@ -38,6 +38,7 @@ import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
|||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.nbt.NBTOutputStream;
|
import com.nukkitx.nbt.NBTOutputStream;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import com.nukkitx.nbt.NbtMapBuilder;
|
||||||
import com.nukkitx.nbt.NbtUtils;
|
import com.nukkitx.nbt.NbtUtils;
|
||||||
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
@ -49,6 +50,7 @@ import it.unimi.dsi.fastutil.ints.IntImmutableList;
|
|||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntLists;
|
import it.unimi.dsi.fastutil.ints.IntLists;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
||||||
|
import org.geysermc.erosion.util.LecternUtils;
|
||||||
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
import org.geysermc.geyser.entity.type.ItemFrameEntity;
|
||||||
import org.geysermc.geyser.level.BedrockDimension;
|
import org.geysermc.geyser.level.BedrockDimension;
|
||||||
import org.geysermc.geyser.level.block.BlockStateValues;
|
import org.geysermc.geyser.level.block.BlockStateValues;
|
||||||
@ -95,6 +97,7 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
|||||||
|
|
||||||
final BlockEntityInfo[] blockEntities = packet.getBlockEntities();
|
final BlockEntityInfo[] blockEntities = packet.getBlockEntities();
|
||||||
final List<NbtMap> bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length);
|
final List<NbtMap> bedrockBlockEntities = new ObjectArrayList<>(blockEntities.length);
|
||||||
|
final List<BlockEntityInfo> lecterns = new ObjectArrayList<>();
|
||||||
|
|
||||||
BitSet waterloggedPaletteIds = new BitSet();
|
BitSet waterloggedPaletteIds = new BitSet();
|
||||||
BitSet bedrockOnlyBlockEntityIds = new BitSet();
|
BitSet bedrockOnlyBlockEntityIds = new BitSet();
|
||||||
@ -328,7 +331,9 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
|||||||
extendedCollisionNextSection = thisExtendedCollisionNextSection;
|
extendedCollisionNextSection = thisExtendedCollisionNextSection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!session.getErosionHandler().isActive()) {
|
||||||
session.getChunkCache().addToCache(packet.getX(), packet.getZ(), javaChunks);
|
session.getChunkCache().addToCache(packet.getX(), packet.getZ(), javaChunks);
|
||||||
|
}
|
||||||
|
|
||||||
final int chunkBlockX = packet.getX() << 4;
|
final int chunkBlockX = packet.getX() << 4;
|
||||||
final int chunkBlockZ = packet.getZ() << 4;
|
final int chunkBlockZ = packet.getZ() << 4;
|
||||||
@ -350,8 +355,15 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
|||||||
|
|
||||||
if (type == BlockEntityType.LECTERN && BlockStateValues.getLecternBookStates().get(blockState)) {
|
if (type == BlockEntityType.LECTERN && BlockStateValues.getLecternBookStates().get(blockState)) {
|
||||||
// If getLecternBookStates is false, let's just treat it like a normal block entity
|
// If getLecternBookStates is false, let's just treat it like a normal block entity
|
||||||
bedrockBlockEntities.add(session.getGeyser().getWorldManager().getLecternDataAt(
|
// Fill in tag with a default value
|
||||||
session, x + chunkBlockX, y, z + chunkBlockZ, true));
|
NbtMapBuilder lecternTag = LecternUtils.getBaseLecternTag(x + chunkBlockX, y, z + chunkBlockZ, 1);
|
||||||
|
lecternTag.putCompound("book", NbtMap.builder()
|
||||||
|
.putByte("Count", (byte) 1)
|
||||||
|
.putShort("Damage", (short) 0)
|
||||||
|
.putString("Name", "minecraft:written_book").build());
|
||||||
|
lecternTag.putInt("page", -1);
|
||||||
|
bedrockBlockEntities.add(lecternTag.build());
|
||||||
|
lecterns.add(blockEntity);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -455,6 +467,10 @@ public class JavaLevelChunkWithLightTranslator extends PacketTranslator<Clientbo
|
|||||||
levelChunkPacket.setData(payload);
|
levelChunkPacket.setData(payload);
|
||||||
session.sendUpstreamPacket(levelChunkPacket);
|
session.sendUpstreamPacket(levelChunkPacket);
|
||||||
|
|
||||||
|
if (!lecterns.isEmpty()) {
|
||||||
|
session.getGeyser().getWorldManager().sendLecternData(session, packet.getX(), packet.getZ(), lecterns);
|
||||||
|
}
|
||||||
|
|
||||||
for (Map.Entry<Vector3i, ItemFrameEntity> entry : session.getItemFrameCache().entrySet()) {
|
for (Map.Entry<Vector3i, ItemFrameEntity> entry : session.getItemFrameCache().entrySet()) {
|
||||||
Vector3i position = entry.getKey();
|
Vector3i position = entry.getKey();
|
||||||
if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {
|
if ((position.getX() >> 4) == packet.getX() && (position.getZ() >> 4) == packet.getZ()) {
|
||||||
|
@ -27,9 +27,9 @@ package org.geysermc.geyser.util.collection;
|
|||||||
|
|
||||||
import com.nukkitx.math.vector.Vector3i;
|
import com.nukkitx.math.vector.Vector3i;
|
||||||
import com.nukkitx.nbt.NbtMap;
|
import com.nukkitx.nbt.NbtMap;
|
||||||
|
import org.geysermc.erosion.util.LecternUtils;
|
||||||
import org.geysermc.geyser.level.WorldManager;
|
import org.geysermc.geyser.level.WorldManager;
|
||||||
import org.geysermc.geyser.session.GeyserSession;
|
import org.geysermc.geyser.session.GeyserSession;
|
||||||
import org.geysermc.geyser.translator.inventory.LecternInventoryTranslator;
|
|
||||||
import org.geysermc.geyser.util.BlockEntityUtils;
|
import org.geysermc.geyser.util.BlockEntityUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,27 +47,24 @@ public class LecternHasBookMap extends FixedInt2BooleanMap {
|
|||||||
int offset = blockState - this.start;
|
int offset = blockState - this.start;
|
||||||
if (offset < 0 || offset >= this.value.length) {
|
if (offset < 0 || offset >= this.value.length) {
|
||||||
// Block state is out of bounds of this map - lectern has been destroyed, if it existed
|
// Block state is out of bounds of this map - lectern has been destroyed, if it existed
|
||||||
if (!worldManager.shouldExpectLecternHandled()) {
|
if (!worldManager.shouldExpectLecternHandled(session)) {
|
||||||
session.getLecternCache().remove(position);
|
session.getLecternCache().remove(position);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean newLecternHasBook;
|
boolean newLecternHasBook;
|
||||||
if (worldManager.shouldExpectLecternHandled()) {
|
if (worldManager.shouldExpectLecternHandled(session)) {
|
||||||
// As of right now, no tag can be added asynchronously
|
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||||
worldManager.getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false);
|
|
||||||
} else if ((newLecternHasBook = this.value[offset]) != this.get(worldManager.getBlockAt(session, position))) {
|
} else if ((newLecternHasBook = this.value[offset]) != this.get(worldManager.getBlockAt(session, position))) {
|
||||||
// If the lectern block was updated, or it previously had a book
|
|
||||||
NbtMap newLecternTag;
|
|
||||||
// newLecternHasBook = the new lectern block state's "has book" toggle.
|
// newLecternHasBook = the new lectern block state's "has book" toggle.
|
||||||
if (newLecternHasBook) {
|
if (newLecternHasBook) {
|
||||||
newLecternTag = worldManager.getLecternDataAt(session, position.getX(), position.getY(), position.getZ(), false);
|
worldManager.sendLecternData(session, position.getX(), position.getY(), position.getZ());
|
||||||
} else {
|
} else {
|
||||||
session.getLecternCache().remove(position);
|
session.getLecternCache().remove(position);
|
||||||
newLecternTag = LecternInventoryTranslator.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build();
|
NbtMap newLecternTag = LecternUtils.getBaseLecternTag(position.getX(), position.getY(), position.getZ(), 0).build();
|
||||||
}
|
|
||||||
BlockEntityUtils.updateBlockEntity(session, newLecternTag, position);
|
BlockEntityUtils.updateBlockEntity(session, newLecternTag, position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
[versions]
|
[versions]
|
||||||
base-api = "1.0.0-SNAPSHOT"
|
base-api = "1.0.0-SNAPSHOT"
|
||||||
cumulus = "1.1.1"
|
cumulus = "1.1.1"
|
||||||
|
erosion = "1.0-20230330.170602-3"
|
||||||
events = "1.0-SNAPSHOT"
|
events = "1.0-SNAPSHOT"
|
||||||
jackson = "2.14.0"
|
jackson = "2.14.0"
|
||||||
fastutil = "8.5.2"
|
fastutil = "8.5.2"
|
||||||
@ -19,9 +20,9 @@ checkerframework = "3.19.0"
|
|||||||
log4j = "2.17.1"
|
log4j = "2.17.1"
|
||||||
jline = "3.21.0"
|
jline = "3.21.0"
|
||||||
terminalconsoleappender = "1.2.0"
|
terminalconsoleappender = "1.2.0"
|
||||||
paper = "1.19-R0.1-SNAPSHOT"
|
folia = "1.19.4-R0.1-SNAPSHOT"
|
||||||
viaversion = "4.0.0"
|
viaversion = "4.0.0"
|
||||||
adapters = "1.6-SNAPSHOT"
|
adapters = "1.7-SNAPSHOT"
|
||||||
commodore = "2.2"
|
commodore = "2.2"
|
||||||
bungeecord = "a7c6ede"
|
bungeecord = "a7c6ede"
|
||||||
velocity = "3.0.0"
|
velocity = "3.0.0"
|
||||||
@ -35,6 +36,9 @@ base-api = { group = "org.geysermc.api", name = "base-api", version.ref = "base-
|
|||||||
cumulus = { group = "org.geysermc.cumulus", name = "cumulus", version.ref = "cumulus" }
|
cumulus = { group = "org.geysermc.cumulus", name = "cumulus", version.ref = "cumulus" }
|
||||||
events = { group = "org.geysermc.event", name = "events", version.ref = "events" }
|
events = { group = "org.geysermc.event", name = "events", version.ref = "events" }
|
||||||
|
|
||||||
|
erosion-bukkit-common = { group = "org.geysermc.erosion", name = "bukkit-common", version.ref = "erosion" }
|
||||||
|
erosion-common = { group = "org.geysermc.erosion", name = "common", version.ref = "erosion" }
|
||||||
|
|
||||||
jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jackson" }
|
jackson-annotations = { group = "com.fasterxml.jackson.core", name = "jackson-annotations", version.ref = "jackson" }
|
||||||
jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" }
|
jackson-core = { group = "com.fasterxml.jackson.core", name = "jackson-databind", version.ref = "jackson" }
|
||||||
jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson" }
|
jackson-dataformat-yaml = { group = "com.fasterxml.jackson.dataformat", name = "jackson-dataformat-yaml", version.ref = "jackson" }
|
||||||
@ -66,8 +70,8 @@ jline-terminal = { group = "org.jline", name = "jline-terminal", version.ref = "
|
|||||||
jline-terminal-jna = { group = "org.jline", name = "jline-terminal-jna", version.ref = "jline" }
|
jline-terminal-jna = { group = "org.jline", name = "jline-terminal-jna", version.ref = "jline" }
|
||||||
jline-reader = { group = "org.jline", name = "jline-reader", version.ref = "jline" }
|
jline-reader = { group = "org.jline", name = "jline-reader", version.ref = "jline" }
|
||||||
|
|
||||||
paper-api = { group = "io.papermc.paper", name = "paper-api", version.ref = "paper" }
|
folia-api = { group = "dev.folia", name = "folia-api", version.ref = "folia" }
|
||||||
paper-mojangapi = { group = "io.papermc.paper", name = "paper-mojangapi", version.ref = "paper" }
|
paper-mojangapi = { group = "io.papermc.paper", name = "paper-mojangapi", version.ref = "folia" }
|
||||||
|
|
||||||
# check these on https://modmuss50.me/fabric.html
|
# check these on https://modmuss50.me/fabric.html
|
||||||
fabric-minecraft = { group = "com.mojang", name = "minecraft", version.ref = "fabric-minecraft" }
|
fabric-minecraft = { group = "com.mojang", name = "minecraft", version.ref = "fabric-minecraft" }
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren