From 51a627fb8ec8aa9e82374fef20f4b07722e275f1 Mon Sep 17 00:00:00 2001 From: mt-gitlocalize Date: Sat, 8 Jun 2024 17:01:56 +0000 Subject: [PATCH 1/7] Translate ko.yml via GitLocalize --- src/main/resources/locales/ko.yml | 375 ++++++++++++++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 src/main/resources/locales/ko.yml diff --git a/src/main/resources/locales/ko.yml b/src/main/resources/locales/ko.yml new file mode 100644 index 0000000..4fb7282 --- /dev/null +++ b/src/main/resources/locales/ko.yml @@ -0,0 +1,375 @@ +--- +boxed: + completed: "&a [이름] 완료!" + size-changed: "&a 상자 크기가 [숫자]만큼 증가했습니다!" + size-decreased: "&c 상자 크기가 [숫자]만큼 감소했습니다!" + user-completed: "&a [이름]이 [설명]을 완료했습니다!" + adv-disallowed: "&c 자신의 상자에서만 발전 과제를 완료할 수 있습니다. [설명]을 취소합니다." + general: + errors: + no-island: "&c 상자가 없습니다!" + player-has-island: "&c 플레이어가 이미 상자를 가지고 있습니다!" + player-has-no-island: "&c 그 플레이어는 상자가 없습니다!" + already-have-island: "&c 이미 상자가 있습니다!" + no-safe-location: "&c 상자에서 안전한 위치를 찾을 수 없습니다!" + not-owner: "&c 당신은 팀의 주인이 아닙니다!" + no-teleport-outside: "&c 상자 밖으로 순간이동할 수 없습니다." + commands: + boxed: + help: + description: Box형 게임을 시작하거나 Box로 순간이동하세요. + go: + parameters: "[집 전화 번호]" + sethome: + parameters: "[집 전화 번호]" + boxadmin: + place: + description: 영역 구조 배치 + parameters: " <회전> <미러> <몹 없음>" + use-integers: "&c 조정은 정수여야 합니다." + wrong-world: "&c 이 명령은 박스형 세계에서만 사용할 수 있습니다." + unknown-structure: "&c 배치할 수 없음: 알 수 없는 구조" + unknown-rotation: "&c 배치할 수 없음: 알 수 없는 회전 유형" + unknown-mirror: "&c 배치할 수 없음: 알 수 없는 미러 유형" + saved: "&a Structures.yml에 배치 및 저장됨" + failed: "&c를 Structures.yml에 저장할 수 없습니다. 콘솔에서 오류를 확인하세요." + unknown: "&c 알 수 없는 매개변수: [label]" + island: + go: + parameters: "[집 전화 번호]" + description: 당신을 당신의 상자로 순간이동 시키세요 + teleport: "&a 당신을 당신의 상자로 순간이동시킵니다." + teleported: "&a 당신을 집으로 순간이동시켰습니다 &e #[number]." + help: + description: 메인 박스 명령 + create: + description: 선택적 청사진을 사용하여 상자 만들기(권한 필요) + parameters: "<설계도>" + too-many-islands: "&c 이 세상에는 상자가 너무 많습니다. 여러분의 상자를 만들 공간이 충분하지 않습니다." + cannot-create-island: "&c 시간 내에 자리를 찾을 수 없습니다. 다시 시도해 주세요..." + unable-create-island: "&c 상자를 생성할 수 없습니다. 관리자에게 문의하십시오." + creating-island: "&a 상자를 놓을 자리를 찾는 중..." + pasting: + estimated-time: "&a 예상 시간: &b [숫자] &a 초." + blocks: "&a 블록별로 구성: &b [숫자] &a 모두 블록..." + entities: "&a 엔터티로 채우기: &b [숫자] &a 모든 엔터티..." + done: "&a 완료! 귀하의 상자가 준비되어 귀하를 기다리고 있습니다!" + pick: "&2 상자 선택" + unknown-blueprint: "&c 해당 청사진은 아직 로드되지 않았습니다." + on-first-login: "&a 환영합니다! 몇 초 후에 상자 준비를 시작하겠습니다." + you-can-teleport-to-your-island: "&a 원할 때 상자로 순간이동할 수 있습니다." + info: + description: 귀하의 상자 또는 플레이어의 상자에 대한 정보 표시 + parameters: "<플레이어>" + near: + description: 주변에 있는 이웃 상자의 이름을 표시합니다. + the-following-islands: "&a 다음 상자가 근처에 있습니다." + syntax: "&6 [방향]: &a [이름]" + north: 북쪽 + south: 남쪽 + east: 동쪽 + west: 서쪽 + no-neighbors: "&c 바로 옆에 있는 상자가 없습니다!" + reset: + description: 상자를 다시 시작하고 이전 상자를 제거하십시오 + parameters: "<설계도>" + none-left: "&c 더 이상 재설정이 남지 않았습니다!" + resets-left: "&c &b [number] &c 재설정이 남았습니다." + confirmation: |- + &c 정말로 이 작업을 수행하시겠습니까? + &c 모든 박스 회원은 박스에서 추방되며 나중에 다시 초대해야 합니다. + &c 되돌릴 수 없습니다. 현재 상자가 삭제되면 나중에 해당 상자를 검색할 수 있는 방법이 &l 없으며 &r &c 없습니다. + kicked-from-island: "&c 소유자가 상자를 재설정했기 때문에 [게임 모드]에서 상자에서 쫓겨났습니다." + sethome: + description: 집 텔레포트 지점을 설정하세요 + must-be-on-your-island: "&c 집으로 돌아가려면 상자 위에 있어야 합니다!" + num-homes: "&c 주택은 1부터 [숫자]까지 가능합니다." + home-set: "&6 박스홈이 현재 위치로 설정되었습니다." + nether: + not-allowed: "&c 네더에 집을 짓는 것은 허용되지 않습니다." + confirmation: "&c 정말로 네더에 집을 짓고 싶나요?" + the-end: + not-allowed: "&c 당신은 당신의 집을 종말에 놓을 수 없습니다." + confirmation: "&c 당신의 집을 종말에 놓이게 하시겠습니까?" + parameters: "[집 전화 번호]" + setname: + description: 상자 이름을 정하세요 + name-too-short: "&c 너무 짧습니다. 최소 크기는 [숫자]자입니다." + name-too-long: "&c 너무 깁니다. 최대 크기는 [숫자]자입니다." + name-already-exists: "&c 해당 이름의 상자가 이미 있습니다!" + parameters: "<이름>" + success: "&a 상자 이름을 &b [이름]&a 로 설정했습니다." + resetname: + description: 상자 이름을 재설정하세요 + success: "&a 상자 이름을 성공적으로 재설정했습니다." + team: + description: 팀을 관리하다 + coop: + description: 상자에 플레이어 협동 순위를 설정하세요 + parameters: "<플레이어>" + cannot-coop-yourself: "&c 당신은 스스로 협동할 수 없습니다!" + already-has-rank: "&c 플레이어는 이미 순위를 가지고 있습니다!" + you-are-a-coop-member: "&2 &b[name]&a님이 당신을 도와주었습니다." + success: "&a 당신은 &b [이름]&a에 협력했습니다." + name-has-invited-you: "&a [name] 님이 귀하를 자신의 박스의 Coop 회원으로 초대했습니다." + uncoop: + description: 플레이어의 협동 순위를 제거합니다. + parameters: "<플레이어>" + cannot-uncoop-yourself: "&c 당신은 스스로를 풀 수 없습니다!" + cannot-uncoop-member: "&c 팀원의 협력을 해제할 수 없습니다!" + player-not-cooped: "&c 플레이어가 갇히지 않았습니다!" + you-are-no-longer-a-coop-member: "&c 귀하는 더 이상 [이름] 상자의 협동 조합원이 아닙니다." + all-members-logged-off: "&c 모든 박스 회원이 로그오프되었으므로 귀하는 더 이상 [이름] 박스의 협동조합 회원이 + 아닙니다." + success: "&b [이름] &a은(는) 더 이상 귀하 상자의 협동 조합원이 아닙니다." + is-full: "&c 당신은 다른 사람과 협력할 수 없습니다." + trust: + description: 당신의 박스에서 플레이어에게 신뢰할 수 있는 순위를 부여하세요 + parameters: "<플레이어>" + trust-in-yourself: "&c 자신을 믿으세요!" + name-has-invited-you: "&a [name]님이 귀하를 자신의 박스의 신뢰할 수 있는 회원으로 초대했습니다." + player-already-trusted: "&c 플레이어는 이미 신뢰받고 있습니다!" + you-are-trusted: "&2 귀하는 &b [이름]&a의 신뢰를 받고 있습니다!" + success: "&a 당신은 &b [이름]&a 을(를) 신뢰했습니다." + is-full: "&c 당신은 다른 사람을 믿을 수 없습니다." + invite: + description: 플레이어를 당신의 박스에 초대하세요 + invitation-sent: "&a &b[이름]&a에게 초대장이 전송되었습니다." + removing-invite: "&c 초대를 제거합니다." + name-has-invited-you: "&a [name] 님이 귀하를 자신의 박스에 초대했습니다." + to-accept-or-reject: "&a /[label] 팀이 수락하려면 수락하거나 /[label] 팀이 거부하려면 거부하세요." + you-will-lose-your-island: "&c 경고! 수락하시면 상자를 잃게 됩니다!" + errors: + cannot-invite-self: "&c 자신을 초대할 수 없습니다!" + cooldown: "&c 앞으로 [숫자]초 동안은 그 사람을 초대할 수 없습니다." + island-is-full: "&c 상자가 가득 차서 다른 사람을 초대할 수 없습니다." + none-invited-you: "&c 아무도 당신을 초대하지 않았습니다 :c." + you-already-are-in-team: "&c 당신은 이미 팀에 속해 있습니다!" + already-on-team: "&c 그 플레이어는 이미 팀에 속해 있습니다!" + invalid-invite: "&c 해당 초대는 더 이상 유효하지 않습니다. 죄송합니다." + you-have-already-invited: "&c 당신은 이미 그 플레이어를 초대했습니다!" + parameters: "<플레이어>" + you-can-invite: "&a [숫자]명의 플레이어를 더 초대할 수 있습니다." + accept: + description: 초대를 수락하다 + you-joined-island: "&a 상자에 가입하셨습니다! 다른 구성원을 보려면 &b/[label] 팀 &a를 사용하세요." + name-joined-your-island: "&a [이름]이(가) 귀하의 상자에 합류했습니다!" + confirmation: |- + &c 이 초대를 수락하시겠습니까? + &c&l 현재 상자를 &n &r&c&l 잃게 됩니다! + reject: + description: 초대를 거절하다 + you-rejected-invite: "&a 귀하는 박스 가입 초대를 거부했습니다." + name-rejected-your-invite: "&c [name]님이 귀하의 박스 초대를 거부했습니다!" + cancel: + description: 귀하의 박스에 가입하기 위해 대기 중인 초대를 취소하세요 + leave: + cannot-leave: "&c 소유자는 떠날 수 없습니다! 먼저 회원가입을 하세요. 아니면 모든 회원을 추방하세요." + description: 상자를 두고 가세요 + left-your-island: "&c [이름] &c 상자를 떠났어요" + success: "&a 이 상자를 떠났습니다." + kick: + description: 귀하의 박스에서 회원을 제거하십시오 + parameters: "<플레이어>" + owner-kicked: "&c [게임 모드]에서 주인이 당신을 상자에서 쫓아냈습니다!" + cannot-kick: "&c 당신은 자신을 걷어찰 수 없습니다!" + success: "&b [이름] &a이(가) 귀하의 상자에서 추방되었습니다." + demote: + description: 상자에 있는 플레이어를 한 단계 아래로 강등시킵니다. + parameters: "<플레이어>" + errors: + cant-demote-yourself: "&c 당신은 자신을 강등시킬 수 없습니다!" + failure: "&c 플레이어는 더 이상 강등될 수 없습니다!" + success: "&a [이름]을(를) [순위](으)로 강등했습니다." + promote: + description: 당신의 박스에 있는 선수를 순위 위로 승격시키세요 + parameters: "<플레이어>" + failure: "&c 플레이어는 더 이상 승격될 수 없습니다!" + success: "&a [이름]을(를) [순위](으)로 승진시켰습니다." + setowner: + description: 박스 소유권을 회원에게 양도하세요 + errors: + cant-transfer-to-yourself: "&c 소유권을 자신에게 양도할 수 없습니다! &7 (&o 사실, 그럴 수도 + 있습니다... 하지만 우리는 당신이 그렇게 하는 것을 원하지 않습니다. 왜냐면 그것은 쓸모없기 때문입니다.&r &7 )" + target-is-not-member: "&c 그 선수는 당신의 박스 팀의 일원이 아닙니다!" + name-is-the-owner: "&a [이름]이(가) 이제 상자 소유자가 되었습니다!" + parameters: "<플레이어>" + you-are-the-owner: "&a 이제 귀하는 상자 소유자입니다!" + ban: + description: 당신의 상자에서 플레이어를 금지 + parameters: "<플레이어>" + cannot-ban-yourself: "&c 자신을 금지할 수 없습니다!" + cannot-ban: "&c 해당 플레이어는 금지될 수 없습니다." + cannot-ban-member: "&c 팀원을 먼저 추방한 후 금지합니다." + cannot-ban-more-players: "&c 금지 제한에 도달했습니다. 상자에서 더 이상 플레이어를 금지할 수 없습니다." + player-already-banned: "&c 플레이어는 이미 금지되었습니다." + player-banned: "&b [이름]&c는 이제 귀하의 상자에서 금지되었습니다." + owner-banned-you: "&b [이름]&c 당신을 그들의 상자에서 차단했습니다!" + you-are-banned: "&b 당신은 이 상자에 들어갈 수 없습니다!" + unban: + description: 상자에서 플레이어 금지 해제 + parameters: "<플레이어>" + cannot-unban-yourself: "&c 자신은 금지를 해제할 수 없습니다!" + player-not-banned: "&c 플레이어가 금지되지 않았습니다." + player-unbanned: "&b [이름]&a가 이제 귀하의 상자에서 금지 해제되었습니다." + you-are-unbanned: "&b [name]&a이(가) 당신을 그들의 상자에서 금지 해제했습니다!" + banlist: + description: 금지된 플레이어 목록 + noone: "&a 이 상자에는 누구도 금지되지 않습니다." + the-following: "&b 다음 플레이어는 금지됩니다:" + names: "&c [줄]" + you-can-ban: "&b 최대 &e [숫자] &b개 이상의 플레이어를 금지할 수 있습니다." + settings: + description: 디스플레이 상자 설정 + language: + description: 언어 선택 + parameters: "[언어]" + not-available: "&c 이 언어는 사용할 수 없습니다." + already-selected: "&c 이미 이 언어를 사용하고 있습니다." + expel: + description: 당신의 박스에서 플레이어를 추방 + parameters: "<플레이어>" + cannot-expel-yourself: "&c 당신은 스스로를 추방할 수 없습니다!" + cannot-expel: "&c 해당 플레이어는 추방될 수 없습니다." + cannot-expel-member: "&c 팀원을 추방할 수 없습니다!" + not-on-island: "&c 그 플레이어는 당신의 상자에 없습니다!" + player-expelled-you: "&b [이름]&c이 당신을 상자에서 추방했습니다!" + success: "&a &b [이름] &a를 상자에서 추방하셨습니다." + admin: + team: + add: + name-has-island: "&c [이름]에 상자가 있습니다. 먼저 등록을 취소하거나 삭제하세요!" + setowner: + description: 상자 소유권을 플레이어에게 이전합니다. + already-owner: "&c 플레이어는 이미 이 상자의 소유자입니다!" + range: + description: 관리 상자 범위 명령 + display: + description: 상자 범위 표시기 표시/숨기기 + hint: |- + &c 빨간색 장벽 아이콘 &f는 현재 보호 범위 제한을 표시합니다. + &7 회색 입자 &f는 최대 한계를 표시합니다. + &a 녹색 입자 &f는 보호 범위가 다른 경우 기본 보호 범위를 표시합니다. + set: + description: 상자 보호 범위를 설정합니다. + reset: + description: 보호된 범위를 세계 기본값으로 재설정합니다. + register: + parameters: "<플레이어>" + description: 당신이 속한 소유되지 않은 상자에 플레이어를 등록하십시오 + registered-island: "&a [xyz]의 박스에 플레이어를 등록했습니다." + already-owned: "&c 영역은 이미 다른 플레이어가 소유하고 있습니다!" + no-island-here: "&c 여기에는 플레이어 상자가 없습니다. 확인하여 만드세요." + in-deletion: "&c 이 공간은 현재 재생성 중입니다. 나중에 시도해 보세요." + unregister: + description: 상자에서 소유자 등록을 취소하지만 상자 블록은 그대로 유지합니다. + unregistered-island: "&a [xyz]에 있는 상자에서 등록되지 않은 플레이어입니다." + info: + description: 현재 위치나 플레이어에 대한 정보 얻기 + no-island: "&c 현재 등록된 박스에 없습니다..." + island-location: '지역 위치: [xyz]' + island-coords: '영역 좌표: [xz1] ~ [xz2]' + is-spawn: 지역은 스폰 섬입니다. + setrange: + description: 플레이어 박스의 범위 설정 + range-updated: 지역 범위가 [번호](으)로 업데이트되었습니다. + tp: + description: 플레이어의 상자로 순간이동 + getrank: + description: 상자에서 플레이어의 순위를 확인하세요 + rank-is: "&a 순위는 상자에서 [순위]입니다." + setrank: + description: 상자에 플레이어의 순위를 설정합니다. + setspawn: + description: 상자를 이 세계의 생성으로 설정 + already-spawn: "&c 이 상자는 이미 스폰되었습니다!" + no-island-here: "&c 등록된 박스가 없습니다." + confirmation: "&c 이 상자를 이 세계의 생성으로 설정하시겠습니까?" + delete: + description: 플레이어를 삭제하고 상자를 재생성합니다. + deleted-island: "&a &e [xyz] &a의 영역이 성공적으로 재생성되었습니다." + protection: + flags: + ALLOW_MOVE_BOX: + name: 상자 이동 + description: |- + &a 플레이어의 이동 허용 + &A 상자를 던져서 + &엔더펄 + ELYTRA: + description: 사용 전환 + ENDERMAN_GRIEFING: + description: |- + &a 엔더맨은 제거할 수 있습니다 + &a 블록 + ENTER_EXIT_MESSAGES: + description: 시작 및 종료 메시지 표시 + island: "[이름]의 보호 상자" + name: 메시지 입력/종료 + now-entering: "&a 이제 &b [이름]&a를 입력합니다." + now-entering-your-island: "&a 이제 상자에 들어가세요." + now-leaving: "&a 이제 &b [이름]&a을(를) 떠납니다." + now-leaving-your-island: "&a 이제 상자를 떠나세요." + GEO_LIMIT_MOBS: + description: |- + &a 이동하는 몹 제거 + &A 외부 보호됨 + &플레이어 공간 + name: "&e 몹을 플레이어 상자로 제한" + ISLAND_RESPAWN: + description: |- + &a 플레이어 리스폰 + &a 상자에 + name: 지역 재생성 + LOCK: + name: 플레이어 박스 잠금 + OFFLINE_REDSTONE: + description: |- + &a 비활성화되면 레드스톤 + &a는 상자에서 작동하지 않습니다. + &a 모든 구성원이 오프라인 상태입니다. + &a 지연을 줄이는 데 도움이 될 수 있습니다. + PISTON_PUSH: + description: |- + &a 피스톤이 밀도록 허용 + &a 플레이어 박스 밖의 블록 + PVP_OVERWORLD: + description: |- + &c PVP 활성화/비활성화 + &c는 보호된 상자에 있습니다. + REMOVE_MOBS: + description: |- + &a 다음의 경우 몬스터를 제거합니다. + &a 상자로 순간이동 + PREVENT_TELEPORT_WHEN_FALLING: + description: |- + &a 플레이어의 순간이동 방지 + &a 떨어지는 경우. + hint: "&c 추락하는 동안에는 순간이동을 할 수 없습니다!" + locked: "&c 이 상자는 잠겨있습니다!" + protected: "&c 보호된 영역: [설명]" + panel: + PROTECTION: + title: "&6 보호" + description: |- + &a 보호 설정 + 이 상자에 대해 &a + SETTING: + description: |- + &a 일반 설정 + 이 상자에 대해 &a + advancements: + minecraft:adventure/root: 모험, 탐험, 전투 + minecraft:end/root: 끝, 아니면 시작? + minecraft:husbandry/root: 새와 벌 + minecraft:nether/root: 여름옷을 챙겨오세요 + minecraft:recipes/root: 조리법 + minecraft:story/root: 게임의 핵심과 스토리 +protection: + flags: + MOVE_BOX: + name: 상자 이동 + description: | + &b 누가 할 수 있는지 전환 + &b 상자를 다음으로 이동하세요. + &b 엔더펄스 From cd5936d746a44aa6261ce4690ff9b64456a8a3ac Mon Sep 17 00:00:00 2001 From: tastybento Date: Sat, 22 Jun 2024 22:48:01 -0700 Subject: [PATCH 2/7] Adjust how tp is prevented. #70 --- .../boxed/listeners/EnderPearlListener.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/world/bentobox/boxed/listeners/EnderPearlListener.java b/src/main/java/world/bentobox/boxed/listeners/EnderPearlListener.java index 6574e6c..0a835e9 100644 --- a/src/main/java/world/bentobox/boxed/listeners/EnderPearlListener.java +++ b/src/main/java/world/bentobox/boxed/listeners/EnderPearlListener.java @@ -45,15 +45,16 @@ public void onPlayerTeleport(PlayerTeleportEvent e) { ) { return; } - + User u = User.getInstance(e.getPlayer()); // If the to-location is outside the box, cancel it if (e.getTo() != null) { - Island i = addon.getIslands().getIsland(e.getFrom().getWorld(), u); - if (i == null || !i.onIsland(e.getTo())) { - u.sendMessage("boxed.general.errors.no-teleport-outside"); - e.setCancelled(true); - } + addon.getIslands().getIslandAt(e.getTo()).ifPresent(i -> { + if (!i.onIsland(e.getTo())) { + u.sendMessage("boxed.general.errors.no-teleport-outside"); + e.setCancelled(true); + } + }); } } @@ -77,7 +78,7 @@ public void onEnderPearlLand(ProjectileHitEvent e) { if (is == null) { return; // Nothing to do } - + // Get the box that the player is in addon.getIslands().getIslandAt(u.getLocation()).ifPresent(fromIsland -> { // Check that it is their box From 4e548ce26b207e9cbf4c6c4f02a50fecd70ca9b1 Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 27 Jun 2024 19:47:15 -0700 Subject: [PATCH 3/7] Add nms support for multiple servers --- pom.xml | 31 +- .../boxed/listeners/NewAreaListener.java | 676 +++++++++--------- .../bentobox/boxed/nms/AbstractMetaData.java | 12 + .../boxed/nms/fallback/GetMetaData.java | 17 + .../v1_20_4_R0_1_SNAPSHOT/GetMetaData.java | 37 + .../v1_20_6_R0_1_SNAPSHOT/GetMetaData.java | 37 + .../nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java | 37 + 7 files changed, 506 insertions(+), 341 deletions(-) create mode 100644 src/main/java/world/bentobox/boxed/nms/AbstractMetaData.java create mode 100644 src/main/java/world/bentobox/boxed/nms/fallback/GetMetaData.java create mode 100644 src/main/java/world/bentobox/boxed/nms/v1_20_4_R0_1_SNAPSHOT/GetMetaData.java create mode 100644 src/main/java/world/bentobox/boxed/nms/v1_20_6_R0_1_SNAPSHOT/GetMetaData.java create mode 100644 src/main/java/world/bentobox/boxed/nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java diff --git a/pom.xml b/pom.xml index ea38ff5..19d65d7 100644 --- a/pom.xml +++ b/pom.xml @@ -178,7 +178,36 @@ ${spigot.version} provided - + + org.spigotmc..... + spigot + 1.21-R0.1-SNAPSHOT + provided + + + org.spigotmc.... + spigot + 1.20.6-R0.1-SNAPSHOT + provided + + + org.spigotmc. + spigot + 1.20.3-R0.1-SNAPSHOT + provided + + + org.spigotmc.. + spigot + 1.20.2-R0.1-SNAPSHOT + provided + + + org.spigotmc... + spigot + 1.20.1-R0.1-SNAPSHOT + provided + diff --git a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java index 64787cc..579f01c 100644 --- a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java +++ b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java @@ -2,7 +2,6 @@ import java.io.File; import java.io.IOException; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; @@ -27,7 +26,6 @@ import org.bukkit.block.structure.StructureRotation; import org.bukkit.configuration.ConfigurationSection; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Player; @@ -43,10 +41,6 @@ import com.google.common.base.Enums; import com.google.gson.Gson; -import net.minecraft.core.BlockPosition; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; -import net.minecraft.world.level.block.entity.TileEntity; import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.events.BentoBoxReadyEvent; import world.bentobox.bentobox.api.events.island.IslandCreatedEvent; @@ -56,6 +50,7 @@ import world.bentobox.bentobox.util.Pair; import world.bentobox.bentobox.util.Util; import world.bentobox.boxed.Boxed; +import world.bentobox.boxed.nms.AbstractMetaData; import world.bentobox.boxed.objects.BoxedJigsawBlock; import world.bentobox.boxed.objects.BoxedStructureBlock; import world.bentobox.boxed.objects.IslandStructures; @@ -78,22 +73,22 @@ public class NewAreaListener implements Listener { * @param noMobs - if false, mobs not pasted */ public record StructureRecord(String name, Structure structure, Location location, StructureRotation rot, - Mirror mirror, Boolean noMobs) { + Mirror mirror, Boolean noMobs) { } private static final Map BUTCHER_ANIMALS = Map.of(0, EntityType.COW, 1, EntityType.SHEEP, 2, - EntityType.PIG); + EntityType.PIG); private static final List CARDINALS = List.of(BlockFace.NORTH, BlockFace.SOUTH, BlockFace.EAST, - BlockFace.WEST); + BlockFace.WEST); private static final List JAR_STRUCTURES = List.of("bee", "pillager", "polar_bear", "axolotl", "allay", - "parrot", "frog"); + "parrot", "frog"); private static final List STRUCTURES = List.of("ancient_city", "bastion_remnant", "bastion", - "buried_treasure", "desert_pyramid", "end_city", "fortress", "igloo", "jungle_pyramid", "mansion", - "mineshaft", "mineshaft_mesa", "monument", "nether_fossil", "ocean_ruin_cold", "ocean_ruin_warm", - "pillager_outpost", "ruined_portal_desert", "ruined_portal_jungle", "ruined_portal_mountain", - "ruined_portal_nether", "ruined_portal_ocean", "ruined_portal_swamp", "ruined_portal", "shipwreck_beached", - "shipwreck", "stronghold", "swamp_hut", "village_desert", "village_plains", "village_savanna", - "village_snowy", "village_taiga"); + "buried_treasure", "desert_pyramid", "end_city", "fortress", "igloo", "jungle_pyramid", "mansion", + "mineshaft", "mineshaft_mesa", "monument", "nether_fossil", "ocean_ruin_cold", "ocean_ruin_warm", + "pillager_outpost", "ruined_portal_desert", "ruined_portal_jungle", "ruined_portal_mountain", + "ruined_portal_nether", "ruined_portal_ocean", "ruined_portal_swamp", "ruined_portal", "shipwreck_beached", + "shipwreck", "stronghold", "swamp_hut", "village_desert", "village_plains", "village_savanna", + "village_snowy", "village_taiga"); private final Boxed addon; private final File structureFile; private final Queue itemsToBuild = new LinkedList<>(); @@ -110,31 +105,31 @@ public record StructureRecord(String name, Structure structure, Location locatio * @param addon addon */ public NewAreaListener(Boxed addon) { - this.addon = addon; - // Save the default structures file from the jar - addon.saveResource("structures.yml", false); - // Load the config - structureFile = new File(addon.getDataFolder(), "structures.yml"); - // Get database ready - handler = new Database<>(addon, IslandStructures.class); - // Try to build something every second - runStructurePrinter(addon); + this.addon = addon; + // Save the default structures file from the jar + addon.saveResource("structures.yml", false); + // Load the config + structureFile = new File(addon.getDataFolder(), "structures.yml"); + // Get database ready + handler = new Database<>(addon, IslandStructures.class); + // Try to build something every second + runStructurePrinter(addon); } private void runStructurePrinter(Boxed addon2) { - Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), this::buildStructure, 20, 20); - for (String js : JAR_STRUCTURES) { - addon.saveResource("structures/" + js + ".nbt", false); - File structureFile = new File(addon.getDataFolder(), "structures/" + js + ".nbt"); - try { - Structure s = Bukkit.getStructureManager().loadStructure(structureFile); - Bukkit.getStructureManager().registerStructure(NamespacedKey.fromString("minecraft:boxed/" + js), s); - addon.log("Loaded " + js + ".nbt"); - } catch (IOException e) { - addon.logError("Error trying to load " + structureFile.getAbsolutePath()); - addon.getPlugin().logStacktrace(e); - } - } + Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), this::buildStructure, 20, 20); + for (String js : JAR_STRUCTURES) { + addon.saveResource("structures/" + js + ".nbt", false); + File structureFile = new File(addon.getDataFolder(), "structures/" + js + ".nbt"); + try { + Structure s = Bukkit.getStructureManager().loadStructure(structureFile); + Bukkit.getStructureManager().registerStructure(NamespacedKey.fromString("minecraft:boxed/" + js), s); + addon.log("Loaded " + js + ".nbt"); + } catch (IOException e) { + addon.logError("Error trying to load " + structureFile.getAbsolutePath()); + addon.getPlugin().logStacktrace(e); + } + } } @@ -142,13 +137,13 @@ private void runStructurePrinter(Boxed addon2) { * Build something in the queue */ private void buildStructure() { - // Only kick off a build if there is something to build and something isn't - // already being built - if (!pasting && !itemsToBuild.isEmpty()) { - // Build item - StructureRecord item = itemsToBuild.poll(); - placeStructure(item); - } + // Only kick off a build if there is something to build and something isn't + // already being built + if (!pasting && !itemsToBuild.isEmpty()) { + // Build item + StructureRecord item = itemsToBuild.poll(); + placeStructure(item); + } } /** @@ -160,17 +155,17 @@ private void buildStructure() { */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBentoBoxReady(BentoBoxReadyEvent event) { - addon.saveResource("templates.yml", false); - File templateFile = new File(addon.getDataFolder(), "templates.yml"); - if (templateFile.exists()) { - YamlConfiguration loader = YamlConfiguration.loadConfiguration(templateFile); - List list = loader.getStringList("templates"); - for (String struct : list) { - if (!struct.endsWith("/")) { - Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(struct)); - } - } - } + addon.saveResource("templates.yml", false); + File templateFile = new File(addon.getDataFolder(), "templates.yml"); + if (templateFile.exists()) { + YamlConfiguration loader = YamlConfiguration.loadConfiguration(templateFile); + List list = loader.getStringList("templates"); + for (String struct : list) { + if (!struct.endsWith("/")) { + Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(struct)); + } + } + } } @@ -181,31 +176,31 @@ public void onBentoBoxReady(BentoBoxReadyEvent event) { */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onPlayerMove(PlayerMoveEvent e) { - // Ignore head movements - if (!addon.inWorld(e.getFrom().getWorld()) || e.getFrom().toVector().equals(e.getTo().toVector())) { - return; - } - // Check where the player is - addon.getIslands().getIslandAt(e.getTo()).ifPresent(island -> { - // See if island is in cache - final String islandId = island.getUniqueId(); - IslandStructures is = getIslandStructData(islandId); - // Check if player is in any of the structures - Map structures = e.getTo().getWorld().getEnvironment().equals(Environment.NETHER) - ? is.getNetherStructureBoundingBoxMap() - : is.getStructureBoundingBoxMap(); - for (Map.Entry en : structures.entrySet()) { - if (en.getKey().contains(e.getTo().toVector())) { - for (String s : STRUCTURES) { - if (en.getValue().startsWith(s)) { - giveAdvFromCriteria(e.getPlayer(), s); - } - } - // STRUCTURES.stream().filter(en.getValue()::startsWith).forEach(s -> - // giveAdvFromCriteria(e.getPlayer(), s)); - } - } - }); + // Ignore head movements + if (!addon.inWorld(e.getFrom().getWorld()) || e.getFrom().toVector().equals(e.getTo().toVector())) { + return; + } + // Check where the player is + addon.getIslands().getIslandAt(e.getTo()).ifPresent(island -> { + // See if island is in cache + final String islandId = island.getUniqueId(); + IslandStructures is = getIslandStructData(islandId); + // Check if player is in any of the structures + Map structures = e.getTo().getWorld().getEnvironment().equals(Environment.NETHER) + ? is.getNetherStructureBoundingBoxMap() + : is.getStructureBoundingBoxMap(); + for (Map.Entry en : structures.entrySet()) { + if (en.getKey().contains(e.getTo().toVector())) { + for (String s : STRUCTURES) { + if (en.getValue().startsWith(s)) { + giveAdvFromCriteria(e.getPlayer(), s); + } + } + // STRUCTURES.stream().filter(en.getValue()::startsWith).forEach(s -> + // giveAdvFromCriteria(e.getPlayer(), s)); + } + } + }); } /** @@ -215,18 +210,18 @@ public void onPlayerMove(PlayerMoveEvent e) { * @param string - criteria */ private void giveAdvFromCriteria(Player player, String string) { - // Give every advancement that requires a bastion - Bukkit.advancementIterator().forEachRemaining(ad -> { - if (!player.getAdvancementProgress(ad).isDone()) { - for (String crit : ad.getCriteria()) { - if (crit.equals(string)) { - // Set the criteria (it may not complete the advancement completely - player.getAdvancementProgress(ad).awardCriteria(crit); - break; - } - } - } - }); + // Give every advancement that requires a bastion + Bukkit.advancementIterator().forEachRemaining(ad -> { + if (!player.getAdvancementProgress(ad).isDone()) { + for (String crit : ad.getCriteria()) { + if (crit.equals(string)) { + // Set the criteria (it may not complete the advancement completely + player.getAdvancementProgress(ad).awardCriteria(crit); + break; + } + } + } + }); } @@ -237,114 +232,114 @@ private void giveAdvFromCriteria(Player player, String string) { * @return IslandStructures */ private IslandStructures getIslandStructData(String islandId) { - // Return from cache if it exists - if (islandStructureCache.containsKey(islandId)) { - return islandStructureCache.get(islandId); - } - // Get from database - IslandStructures struct = handler.objectExists(islandId) ? handler.loadObject(islandId) - : new IslandStructures(islandId); - this.islandStructureCache.put(islandId, struct); - return struct; + // Return from cache if it exists + if (islandStructureCache.containsKey(islandId)) { + return islandStructureCache.get(islandId); + } + // Get from database + IslandStructures struct = handler.objectExists(islandId) ? handler.loadObject(islandId) + : new IslandStructures(islandId); + this.islandStructureCache.put(islandId, struct); + return struct; } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onIslandCreated(IslandCreatedEvent event) { - setUpIsland(event.getIsland()); + setUpIsland(event.getIsland()); } @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onIslandReset(IslandResettedEvent event) { - setUpIsland(event.getIsland()); + setUpIsland(event.getIsland()); } private void setUpIsland(Island island) { - // Check if this island is in this game - if (!(addon.inWorld(island.getWorld()))) { - return; - } - // Load the latest config so that admins can change it on the fly without - // reloading - YamlConfiguration config = YamlConfiguration.loadConfiguration(structureFile); - Location center = island.getProtectionCenter(); - for (String env : config.getKeys(false)) { - Environment e = Enums.getIfPresent(Environment.class, env.toUpperCase(Locale.ENGLISH)).orNull(); - if (e == null) { - addon.logError("Error in structures.yml - unknown environment " + env); - } else { - place(config.getConfigurationSection(env), center, e); - } - } + // Check if this island is in this game + if (!(addon.inWorld(island.getWorld()))) { + return; + } + // Load the latest config so that admins can change it on the fly without + // reloading + YamlConfiguration config = YamlConfiguration.loadConfiguration(structureFile); + Location center = island.getProtectionCenter(); + for (String env : config.getKeys(false)) { + Environment e = Enums.getIfPresent(Environment.class, env.toUpperCase(Locale.ENGLISH)).orNull(); + if (e == null) { + addon.logError("Error in structures.yml - unknown environment " + env); + } else { + place(config.getConfigurationSection(env), center, e); + } + } } private void place(ConfigurationSection section, Location center, Environment env) { - World world = env.equals(Environment.NORMAL) ? addon.getOverWorld() : addon.getNetherWorld(); - if (world == null) { - return; - } - // Loop through the structures in the file - there could be more than one - for (String vector : section.getKeys(false)) { - StructureRotation rot = StructureRotation.NONE; - Mirror mirror = Mirror.NONE; - boolean noMobs = false; - String name = section.getString(vector); - // Check for rotation - String[] split = name.split(","); - if (split.length > 1) { - // Rotation - rot = Enums.getIfPresent(StructureRotation.class, split[1].strip().toUpperCase(Locale.ENGLISH)) - .or(StructureRotation.NONE); - name = split[0]; - } - if (split.length == 3) { - // Mirror - mirror = Enums.getIfPresent(Mirror.class, split[2].strip().toUpperCase(Locale.ENGLISH)).or(Mirror.NONE); - } - if (split.length == 4) { - noMobs = split[3].strip().toUpperCase(Locale.ENGLISH).equals("NO_MOBS"); - } - // Load Structure - Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString("minecraft:" + name)); - if (s == null) { - BentoBox.getInstance().logError("Could not load " + name); - return; - } - // Extract coords - String[] value = vector.split(","); - if (value.length > 2) { - int x = Integer.parseInt(value[0].strip()) + center.getBlockX(); - int y = Integer.parseInt(value[1].strip()); - int z = Integer.parseInt(value[2].strip()) + center.getBlockZ(); - Location l = new Location(world, x, y, z); - itemsToBuild.add(new StructureRecord(name, s, l, rot, mirror, noMobs)); - } else { - addon.logError("Structure file syntax error: " + vector + ": " + Arrays.toString(value)); - } - } + World world = env.equals(Environment.NORMAL) ? addon.getOverWorld() : addon.getNetherWorld(); + if (world == null) { + return; + } + // Loop through the structures in the file - there could be more than one + for (String vector : section.getKeys(false)) { + StructureRotation rot = StructureRotation.NONE; + Mirror mirror = Mirror.NONE; + boolean noMobs = false; + String name = section.getString(vector); + // Check for rotation + String[] split = name.split(","); + if (split.length > 1) { + // Rotation + rot = Enums.getIfPresent(StructureRotation.class, split[1].strip().toUpperCase(Locale.ENGLISH)) + .or(StructureRotation.NONE); + name = split[0]; + } + if (split.length == 3) { + // Mirror + mirror = Enums.getIfPresent(Mirror.class, split[2].strip().toUpperCase(Locale.ENGLISH)).or(Mirror.NONE); + } + if (split.length == 4) { + noMobs = split[3].strip().toUpperCase(Locale.ENGLISH).equals("NO_MOBS"); + } + // Load Structure + Structure s = Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString("minecraft:" + name)); + if (s == null) { + BentoBox.getInstance().logError("Could not load " + name); + return; + } + // Extract coords + String[] value = vector.split(","); + if (value.length > 2) { + int x = Integer.parseInt(value[0].strip()) + center.getBlockX(); + int y = Integer.parseInt(value[1].strip()); + int z = Integer.parseInt(value[2].strip()) + center.getBlockZ(); + Location l = new Location(world, x, y, z); + itemsToBuild.add(new StructureRecord(name, s, l, rot, mirror, noMobs)); + } else { + addon.logError("Structure file syntax error: " + vector + ": " + Arrays.toString(value)); + } + } } private void placeStructure(StructureRecord item) { - // Set the semaphore - only paste one at a time - pasting = true; - // Place the structure - item.structure().place(item.location(), true, item.rot(), item.mirror(), -1, 1, rand); - addon.log(item.name() + " placed at " + item.location().getWorld().getName() + " " - + Util.xyz(item.location().toVector())); - // Remove any jigsaw artifacts - BoundingBox bb = removeJigsaw(item); - // Store it for future reference - addon.getIslands().getIslandAt(item.location()).map(Island::getUniqueId).ifPresent(id -> { - addon.log("Saved " + item.name()); - if (item.location().getWorld().getEnvironment().equals(Environment.NETHER)) { - getIslandStructData(id).addNetherStructure(bb, item.name()); - } else { - getIslandStructData(id).addStructure(bb, item.name()); - } - handler.saveObjectAsync(getIslandStructData(id)); - }); - // Clear the semaphore - pasting = false; + // Set the semaphore - only paste one at a time + pasting = true; + // Place the structure + item.structure().place(item.location(), true, item.rot(), item.mirror(), -1, 1, rand); + addon.log(item.name() + " placed at " + item.location().getWorld().getName() + " " + + Util.xyz(item.location().toVector())); + // Remove any jigsaw artifacts + BoundingBox bb = removeJigsaw(item); + // Store it for future reference + addon.getIslands().getIslandAt(item.location()).map(Island::getUniqueId).ifPresent(id -> { + addon.log("Saved " + item.name()); + if (item.location().getWorld().getEnvironment().equals(Environment.NETHER)) { + getIslandStructData(id).addNetherStructure(bb, item.name()); + } else { + getIslandStructData(id).addStructure(bb, item.name()); + } + handler.saveObjectAsync(getIslandStructData(id)); + }); + // Clear the semaphore + pasting = false; } /** @@ -355,46 +350,46 @@ private void placeStructure(StructureRecord item) { * @return the resulting bounding box of the structure */ public static BoundingBox removeJigsaw(StructureRecord item) { - Location loc = item.location(); - Structure structure = item.structure(); - StructureRotation structureRotation = item.rot(); - String key = item.name(); - - Location otherCorner = switch (structureRotation) { - - case CLOCKWISE_180 -> loc.clone() - .add(new Vector(-structure.getSize().getX(), structure.getSize().getY(), -structure.getSize().getZ())); - - case CLOCKWISE_90 -> loc.clone() - .add(new Vector(-structure.getSize().getZ(), structure.getSize().getY(), structure.getSize().getX())); - - case COUNTERCLOCKWISE_90 -> loc.clone() - .add(new Vector(structure.getSize().getZ(), structure.getSize().getY(), -structure.getSize().getX())); - - case NONE -> loc.clone() - .add(new Vector(structure.getSize().getX(), structure.getSize().getY(), structure.getSize().getZ())); - - }; - - BoundingBox bb = BoundingBox.of(loc, otherCorner); - for (int x = (int) bb.getMinX(); x <= bb.getMaxX(); x++) { - for (int y = (int) bb.getMinY(); y <= bb.getMaxY(); y++) { - for (int z = (int) bb.getMinZ(); z <= bb.getMaxZ(); z++) { - Block b = loc.getWorld().getBlockAt(x, y, z); - if (b.getType().equals(Material.JIGSAW)) { - // I would like to read the data from the block and do something with it! - processJigsaw(b, structureRotation, !item.noMobs()); - } else if (b.getType().equals(Material.STRUCTURE_BLOCK)) { - processStructureBlock(b); - } - // Set water blocks for underwater ruins - if (key.contains("underwater_ruin") && b.getType().equals(Material.AIR)) { - b.setType(Material.WATER); - } - } - } - } - return bb; + Location loc = item.location(); + Structure structure = item.structure(); + StructureRotation structureRotation = item.rot(); + String key = item.name(); + + Location otherCorner = switch (structureRotation) { + + case CLOCKWISE_180 -> loc.clone() + .add(new Vector(-structure.getSize().getX(), structure.getSize().getY(), -structure.getSize().getZ())); + + case CLOCKWISE_90 -> loc.clone() + .add(new Vector(-structure.getSize().getZ(), structure.getSize().getY(), structure.getSize().getX())); + + case COUNTERCLOCKWISE_90 -> loc.clone() + .add(new Vector(structure.getSize().getZ(), structure.getSize().getY(), -structure.getSize().getX())); + + case NONE -> loc.clone() + .add(new Vector(structure.getSize().getX(), structure.getSize().getY(), structure.getSize().getZ())); + + }; + + BoundingBox bb = BoundingBox.of(loc, otherCorner); + for (int x = (int) bb.getMinX(); x <= bb.getMaxX(); x++) { + for (int y = (int) bb.getMinY(); y <= bb.getMaxY(); y++) { + for (int z = (int) bb.getMinZ(); z <= bb.getMaxZ(); z++) { + Block b = loc.getWorld().getBlockAt(x, y, z); + if (b.getType().equals(Material.JIGSAW)) { + // I would like to read the data from the block and do something with it! + processJigsaw(b, structureRotation, !item.noMobs()); + } else if (b.getType().equals(Material.STRUCTURE_BLOCK)) { + processStructureBlock(b); + } + // Set water blocks for underwater ruins + if (key.contains("underwater_ruin") && b.getType().equals(Material.AIR)) { + b.setType(Material.WATER); + } + } + } + } + return bb; } /** @@ -406,79 +401,79 @@ public static BoundingBox removeJigsaw(StructureRecord item) { * @param b structure block */ private static void processStructureBlock(Block b) { - // I would like to read the data from the block and do something with it! - String data = nmsData(b); - BoxedStructureBlock bsb = gson.fromJson(data, BoxedStructureBlock.class); - b.setType(Material.STRUCTURE_VOID); - Enums.getIfPresent(EntityType.class, bsb.getMetadata().toUpperCase(Locale.ENGLISH)).toJavaUtil() - .ifPresent(type -> b.getWorld().spawnEntity(b.getRelative(BlockFace.UP).getLocation(), type)); - if (bsb.getMetadata().contains("chest")) { - Block downBlock = b.getRelative(BlockFace.DOWN); - if (downBlock.getType().equals(Material.CHEST)) { - Chest chest = (Chest) downBlock.getState(); - // TODO: for now just give treasure - chest.setLootTable(LootTables.BURIED_TREASURE.getLootTable()); - chest.update(); - if (chest.getBlockData() instanceof Waterlogged wl) { - if (wl.isWaterlogged()) { - b.setType(Material.WATER); - } - } - } - } + // I would like to read the data from the block and do something with it! + String data = nmsData(b); + BoxedStructureBlock bsb = gson.fromJson(data, BoxedStructureBlock.class); + b.setType(Material.STRUCTURE_VOID); + Enums.getIfPresent(EntityType.class, bsb.getMetadata().toUpperCase(Locale.ENGLISH)).toJavaUtil() + .ifPresent(type -> b.getWorld().spawnEntity(b.getRelative(BlockFace.UP).getLocation(), type)); + if (bsb.getMetadata().contains("chest")) { + Block downBlock = b.getRelative(BlockFace.DOWN); + if (downBlock.getType().equals(Material.CHEST)) { + Chest chest = (Chest) downBlock.getState(); + // TODO: for now just give treasure + chest.setLootTable(LootTables.BURIED_TREASURE.getLootTable()); + chest.update(); + if (chest.getBlockData() instanceof Waterlogged wl) { + if (wl.isWaterlogged()) { + b.setType(Material.WATER); + } + } + } + } } private static void processJigsaw(Block b, StructureRotation structureRotation, boolean pasteMobs) { - String data = nmsData(b); - BoxedJigsawBlock bjb = gson.fromJson(data, BoxedJigsawBlock.class); - String finalState = correctDirection(bjb.getFinal_state(), structureRotation); - BlockData bd = Bukkit.createBlockData(finalState); - b.setBlockData(bd); - if (!bjb.getPool().equalsIgnoreCase("minecraft:empty") && pasteMobs) { - spawnMob(b, bjb); - } + String data = nmsData(b); + BoxedJigsawBlock bjb = gson.fromJson(data, BoxedJigsawBlock.class); + String finalState = correctDirection(bjb.getFinal_state(), structureRotation); + BlockData bd = Bukkit.createBlockData(finalState); + b.setBlockData(bd); + if (!bjb.getPool().equalsIgnoreCase("minecraft:empty") && pasteMobs) { + spawnMob(b, bjb); + } } private static void spawnMob(Block b, BoxedJigsawBlock bjb) { - // bjb.getPool contains a lot more than just mobs, so we have to filter it to - // see if any mobs are in there. This list may need to grow in the future - EntityType type = switch (bjb.getPool()) { - case "minecraft:bastion/mobs/piglin" -> EntityType.PIGLIN; - case "minecraft:bastion/mobs/hoglin" -> EntityType.HOGLIN; - case "minecraft:bastion/mobs/piglin_melee" -> EntityType.PIGLIN_BRUTE; - case "minecraft:village/common/cats" -> EntityType.CAT; - case "minecraft:village/common/horses" -> EntityType.HORSE; - case "minecraft:village/common/sheep" -> EntityType.SHEEP; - case "minecraft:village/common/pigs" -> EntityType.PIG; - case "minecraft:village/common/cows" -> EntityType.COW; - case "minecraft:village/common/iron_golem" -> EntityType.IRON_GOLEM; - case "minecraft:village/common/butcher_animals", "minecraft:village/common/animals" -> - BUTCHER_ANIMALS.get(rand.nextInt(3)); - default -> null; - }; - // Boxed - if (type == null && bjb.getPool().startsWith("minecraft:boxed/")) { - String entString = bjb.getPool().toUpperCase(Locale.ENGLISH).substring(16, bjb.getPool().length()); - type = Enums.getIfPresent(EntityType.class, entString).orNull(); - } - // Villagers - if (bjb.getPool().contains("zombie/villagers")) { - type = EntityType.ZOMBIE_VILLAGER; - } else if (bjb.getPool().contains("villagers")) { - type = EntityType.VILLAGER; - } - // if (type == null) { - // BentoBox.getInstance().logDebug(bjb.getPool()); - // } - // Spawn it - if (type != null) { - Entity e = b.getWorld().spawnEntity(b.getRelative(BlockFace.UP).getLocation(), type); - if (e != null) { - e.setPersistent(true); - } - // BentoBox.getInstance().logDebug("Spawned a " + type + " at " + - // b.getRelative(BlockFace.UP).getLocation()); - } + // bjb.getPool contains a lot more than just mobs, so we have to filter it to + // see if any mobs are in there. This list may need to grow in the future + EntityType type = switch (bjb.getPool()) { + case "minecraft:bastion/mobs/piglin" -> EntityType.PIGLIN; + case "minecraft:bastion/mobs/hoglin" -> EntityType.HOGLIN; + case "minecraft:bastion/mobs/piglin_melee" -> EntityType.PIGLIN_BRUTE; + case "minecraft:village/common/cats" -> EntityType.CAT; + case "minecraft:village/common/horses" -> EntityType.HORSE; + case "minecraft:village/common/sheep" -> EntityType.SHEEP; + case "minecraft:village/common/pigs" -> EntityType.PIG; + case "minecraft:village/common/cows" -> EntityType.COW; + case "minecraft:village/common/iron_golem" -> EntityType.IRON_GOLEM; + case "minecraft:village/common/butcher_animals", "minecraft:village/common/animals" -> + BUTCHER_ANIMALS.get(rand.nextInt(3)); + default -> null; + }; + // Boxed + if (type == null && bjb.getPool().startsWith("minecraft:boxed/")) { + String entString = bjb.getPool().toUpperCase(Locale.ENGLISH).substring(16, bjb.getPool().length()); + type = Enums.getIfPresent(EntityType.class, entString).orNull(); + } + // Villagers + if (bjb.getPool().contains("zombie/villagers")) { + type = EntityType.ZOMBIE_VILLAGER; + } else if (bjb.getPool().contains("villagers")) { + type = EntityType.VILLAGER; + } + // if (type == null) { + // BentoBox.getInstance().logDebug(bjb.getPool()); + // } + // Spawn it + if (type != null) { + Entity e = b.getWorld().spawnEntity(b.getRelative(BlockFace.UP).getLocation(), type); + if (e != null) { + e.setPersistent(true); + } + // BentoBox.getInstance().logDebug("Spawned a " + type + " at " + + // b.getRelative(BlockFace.UP).getLocation()); + } } /** @@ -490,18 +485,18 @@ private static void spawnMob(Block b, BoxedJigsawBlock bjb) { * @return a rewritten blockstate with the updated direction, if required */ private static String correctDirection(String finalState, StructureRotation sr) { - if (sr.equals(StructureRotation.NONE)) { - // No change - return finalState; - } - BlockFace oldDirection = getDirection(finalState); - BlockFace newDirection = getNewDirection(oldDirection, sr); - if (newDirection.equals(BlockFace.SELF)) { - // No change - shouldn't happen, but just in case - return finalState; - } - return finalState.replace(oldDirection.name().toLowerCase(Locale.ENGLISH), - newDirection.name().toLowerCase(Locale.ENGLISH)); + if (sr.equals(StructureRotation.NONE)) { + // No change + return finalState; + } + BlockFace oldDirection = getDirection(finalState); + BlockFace newDirection = getNewDirection(oldDirection, sr); + if (newDirection.equals(BlockFace.SELF)) { + // No change - shouldn't happen, but just in case + return finalState; + } + return finalState.replace(oldDirection.name().toLowerCase(Locale.ENGLISH), + newDirection.name().toLowerCase(Locale.ENGLISH)); } @@ -513,26 +508,26 @@ private static String correctDirection(String finalState, StructureRotation sr) * @return the new direction, or SELF if something weird happens */ private static BlockFace getNewDirection(BlockFace oldDirection, StructureRotation sr) { - if (sr.equals(StructureRotation.CLOCKWISE_180)) { - return oldDirection.getOppositeFace(); - } else if (sr.equals(StructureRotation.CLOCKWISE_90)) { - return switch (oldDirection) { - case EAST -> BlockFace.SOUTH; - case NORTH -> BlockFace.EAST; - case SOUTH -> BlockFace.WEST; - case WEST -> BlockFace.NORTH; - default -> BlockFace.SELF; - }; - } else if (sr.equals(StructureRotation.COUNTERCLOCKWISE_90)) { - return switch (oldDirection) { - case EAST -> BlockFace.NORTH; - case NORTH -> BlockFace.WEST; - case SOUTH -> BlockFace.EAST; - case WEST -> BlockFace.SOUTH; - default -> BlockFace.SELF; - }; - } - return BlockFace.SELF; + if (sr.equals(StructureRotation.CLOCKWISE_180)) { + return oldDirection.getOppositeFace(); + } else if (sr.equals(StructureRotation.CLOCKWISE_90)) { + return switch (oldDirection) { + case EAST -> BlockFace.SOUTH; + case NORTH -> BlockFace.EAST; + case SOUTH -> BlockFace.WEST; + case WEST -> BlockFace.NORTH; + default -> BlockFace.SELF; + }; + } else if (sr.equals(StructureRotation.COUNTERCLOCKWISE_90)) { + return switch (oldDirection) { + case EAST -> BlockFace.NORTH; + case NORTH -> BlockFace.WEST; + case SOUTH -> BlockFace.EAST; + case WEST -> BlockFace.SOUTH; + default -> BlockFace.SELF; + }; + } + return BlockFace.SELF; } /** @@ -542,27 +537,28 @@ private static BlockFace getNewDirection(BlockFace oldDirection, StructureRotati * @return direction, if found, otherwise SELF */ private static BlockFace getDirection(String finalState) { - return CARDINALS.stream().filter(bf -> finalState.contains(bf.name().toLowerCase(Locale.ENGLISH))).findFirst() - .orElse(BlockFace.SELF); + return CARDINALS.stream().filter(bf -> finalState.contains(bf.name().toLowerCase(Locale.ENGLISH))).findFirst() + .orElse(BlockFace.SELF); } private static String nmsData(Block block) { - Location w = block.getLocation(); - CraftWorld cw = (CraftWorld) w.getWorld(); // CraftWorld is NMS one - // for 1.13+ (we have use WorldServer) - TileEntity te = cw.getHandle().c_(new BlockPosition(w.getBlockX(), w.getBlockY(), w.getBlockZ())); - try { - PacketPlayOutTileEntityData packet = ((PacketPlayOutTileEntityData) te.j()); // get update packet from NMS - // object - // here we should use reflection because "c" field isn't accessible - Field f = packet.getClass().getDeclaredField("c"); // get field - f.setAccessible(true); // make it available - NBTTagCompound nbtTag = (NBTTagCompound) f.get(packet); - return nbtTag.toString(); // this will show what you want - } catch (Exception exc) { - exc.printStackTrace(); - } - return "Nothing"; + // Bukkit method that was added in 2011 + // Example value: 1.20.4-R0.1-SNAPSHOT + String bukkitVersion = "v" + Bukkit.getBukkitVersion().replace('.', '_').replace('-', '_'); + String pluginPackageName = BentoBox.getInstance().getClass().getPackage().getName(); + AbstractMetaData handler; + try { + Class clazz = Class.forName(pluginPackageName + ".nms." + bukkitVersion + ".GetMetaData"); + if (AbstractMetaData.class.isAssignableFrom(clazz)) { + handler = (AbstractMetaData) clazz.getConstructor().newInstance(); + } else { + throw new IllegalStateException("Class " + clazz.getName() + " does not implement AbstractGetMetaData"); + } + } catch (Exception e) { + BentoBox.getInstance().logWarning("No metadata handler found for " + bukkitVersion + " in Boxed."); + handler = new world.bentobox.boxed.nms.fallback.GetMetaData(); + } + return handler.nmsData(block); } } diff --git a/src/main/java/world/bentobox/boxed/nms/AbstractMetaData.java b/src/main/java/world/bentobox/boxed/nms/AbstractMetaData.java new file mode 100644 index 0000000..51196bc --- /dev/null +++ b/src/main/java/world/bentobox/boxed/nms/AbstractMetaData.java @@ -0,0 +1,12 @@ +package world.bentobox.boxed.nms; + +import org.bukkit.block.Block; + +/** + * + */ +public abstract class AbstractMetaData { + + public abstract String nmsData(Block block); + +} diff --git a/src/main/java/world/bentobox/boxed/nms/fallback/GetMetaData.java b/src/main/java/world/bentobox/boxed/nms/fallback/GetMetaData.java new file mode 100644 index 0000000..423e6ee --- /dev/null +++ b/src/main/java/world/bentobox/boxed/nms/fallback/GetMetaData.java @@ -0,0 +1,17 @@ +package world.bentobox.boxed.nms.fallback; + +import org.bukkit.block.Block; + +import world.bentobox.boxed.nms.AbstractMetaData; + +/** + * Fallback + */ +public class GetMetaData extends AbstractMetaData { + + @Override + public String nmsData(Block block) { + return "Nothing"; // We cannot read it if we have no NMS + } + +} diff --git a/src/main/java/world/bentobox/boxed/nms/v1_20_4_R0_1_SNAPSHOT/GetMetaData.java b/src/main/java/world/bentobox/boxed/nms/v1_20_4_R0_1_SNAPSHOT/GetMetaData.java new file mode 100644 index 0000000..57ed417 --- /dev/null +++ b/src/main/java/world/bentobox/boxed/nms/v1_20_4_R0_1_SNAPSHOT/GetMetaData.java @@ -0,0 +1,37 @@ +package world.bentobox.boxed.nms.v1_20_4_R0_1_SNAPSHOT; + +import java.lang.reflect.Field; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; + +import net.minecraft.core.BlockPosition; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; +import net.minecraft.world.level.block.entity.TileEntity; +import world.bentobox.boxed.nms.AbstractMetaData; + +public class GetMetaData extends AbstractMetaData { + + @Override + public String nmsData(Block block) { + Location w = block.getLocation(); + CraftWorld cw = (CraftWorld) w.getWorld(); // CraftWorld is NMS one + // for 1.13+ (we have use WorldServer) + TileEntity te = cw.getHandle().c_(new BlockPosition(w.getBlockX(), w.getBlockY(), w.getBlockZ())); + try { + PacketPlayOutTileEntityData packet = ((PacketPlayOutTileEntityData) te.j()); // get update packet from NMS + // object + // here we should use reflection because "c" field isn't accessible + Field f = packet.getClass().getDeclaredField("c"); // get field + f.setAccessible(true); // make it available + NBTTagCompound nbtTag = (NBTTagCompound) f.get(packet); + return nbtTag.toString(); // this will show what you want + } catch (Exception exc) { + exc.printStackTrace(); + } + return "Nothing"; + } + +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/boxed/nms/v1_20_6_R0_1_SNAPSHOT/GetMetaData.java b/src/main/java/world/bentobox/boxed/nms/v1_20_6_R0_1_SNAPSHOT/GetMetaData.java new file mode 100644 index 0000000..be6b438 --- /dev/null +++ b/src/main/java/world/bentobox/boxed/nms/v1_20_6_R0_1_SNAPSHOT/GetMetaData.java @@ -0,0 +1,37 @@ +package world.bentobox.boxed.nms.v1_20_6_R0_1_SNAPSHOT; + +import java.lang.reflect.Field; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; + +import net.minecraft.core.BlockPosition; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; +import net.minecraft.world.level.block.entity.TileEntity; +import world.bentobox.boxed.nms.AbstractMetaData; + +public class GetMetaData extends AbstractMetaData { + + @Override + public String nmsData(Block block) { + Location w = block.getLocation(); + CraftWorld cw = (CraftWorld) w.getWorld(); // CraftWorld is NMS one + // for 1.13+ (we have use WorldServer) + TileEntity te = cw.getHandle().c_(new BlockPosition(w.getBlockX(), w.getBlockY(), w.getBlockZ())); + try { + PacketPlayOutTileEntityData packet = ((PacketPlayOutTileEntityData) te.j()); // get update packet from NMS + // object + // here we should use reflection because "c" field isn't accessible + Field f = packet.getClass().getDeclaredField("c"); // get field + f.setAccessible(true); // make it available + NBTTagCompound nbtTag = (NBTTagCompound) f.get(packet); + return nbtTag.toString(); // this will show what you want + } catch (Exception exc) { + exc.printStackTrace(); + } + return "Nothing"; + } + +} \ No newline at end of file diff --git a/src/main/java/world/bentobox/boxed/nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java b/src/main/java/world/bentobox/boxed/nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java new file mode 100644 index 0000000..d0634a0 --- /dev/null +++ b/src/main/java/world/bentobox/boxed/nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java @@ -0,0 +1,37 @@ +package world.bentobox.boxed.nms.v1_21_R0_1_SNAPSHOT; + +import java.lang.reflect.Field; + +import org.bukkit.Location; +import org.bukkit.block.Block; +import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; + +import net.minecraft.core.BlockPosition; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; +import net.minecraft.world.level.block.entity.TileEntity; +import world.bentobox.boxed.nms.AbstractMetaData; + +public class GetMetaData extends AbstractMetaData { + + @Override + public String nmsData(Block block) { + Location w = block.getLocation(); + CraftWorld cw = (CraftWorld) w.getWorld(); // CraftWorld is NMS one + // for 1.13+ (we have use WorldServer) + TileEntity te = cw.getHandle().c_(new BlockPosition(w.getBlockX(), w.getBlockY(), w.getBlockZ())); + try { + PacketPlayOutTileEntityData packet = ((PacketPlayOutTileEntityData) te.j()); // get update packet from NMS + // object + // here we should use reflection because "c" field isn't accessible + Field f = packet.getClass().getDeclaredField("c"); // get field + f.setAccessible(true); // make it available + NBTTagCompound nbtTag = (NBTTagCompound) f.get(packet); + return nbtTag.toString(); // this will show what you want + } catch (Exception exc) { + exc.printStackTrace(); + } + return "Nothing"; + } + +} \ No newline at end of file From c1400f25da61f7aff54bf87d6dca0d849d198b8b Mon Sep 17 00:00:00 2001 From: tastybento Date: Thu, 27 Jun 2024 19:51:21 -0700 Subject: [PATCH 4/7] Change to Java 21 for GitHub --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 825b18d..0bc21e0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -14,11 +14,11 @@ jobs: - uses: actions/checkout@v3 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: distribution: 'adopt' - java-version: 17 + java-version: 21 - name: Cache SonarCloud packages uses: actions/cache@v3 with: From 5c17aa1e9ca7f6795fc78a27de1266c20ea79c19 Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 1 Jul 2024 15:34:06 -0700 Subject: [PATCH 5/7] Places structures when chunks are loaded and places them slowly --- .github/.gitignore | 1 + pom.xml | 2 +- .../java/world/bentobox/boxed/Settings.java | 7 + .../boxed/listeners/NewAreaListener.java | 123 ++++++++++++------ .../bentobox/boxed/nms/AbstractMetaData.java | 31 +++++ .../boxed/nms/fallback/GetMetaData.java | 2 +- .../v1_20_4_R0_1_SNAPSHOT/GetMetaData.java | 20 +-- .../v1_20_6_R0_1_SNAPSHOT/GetMetaData.java | 20 +-- .../nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java | 18 +-- .../world/bentobox/boxed/SettingsTest.java | 17 ++- 10 files changed, 144 insertions(+), 97 deletions(-) create mode 100644 .github/.gitignore diff --git a/.github/.gitignore b/.github/.gitignore new file mode 100644 index 0000000..9bb88d3 --- /dev/null +++ b/.github/.gitignore @@ -0,0 +1 @@ +/.DS_Store diff --git a/pom.xml b/pom.xml index 19d65d7..9878113 100644 --- a/pom.xml +++ b/pom.xml @@ -58,7 +58,7 @@ 2.0.9 - 1.20.4-R0.1-SNAPSHOT + 1.21-R0.1-SNAPSHOT 2.0.0-SNAPSHOT ${build.version}-SNAPSHOT diff --git a/src/main/java/world/bentobox/boxed/Settings.java b/src/main/java/world/bentobox/boxed/Settings.java index d665c00..7a9da40 100644 --- a/src/main/java/world/bentobox/boxed/Settings.java +++ b/src/main/java/world/bentobox/boxed/Settings.java @@ -11,6 +11,7 @@ import org.bukkit.GameMode; import org.bukkit.entity.EntityType; +import world.bentobox.bentobox.BentoBox; import world.bentobox.bentobox.api.configuration.ConfigComment; import world.bentobox.bentobox.api.configuration.ConfigEntry; import world.bentobox.bentobox.api.configuration.StoreAt; @@ -106,6 +107,7 @@ public class Settings implements WorldSettings { private int ticksPerMonsterSpawns = -1; @ConfigComment("Radius of player area. (So distance between player starting spots is twice this)") + @ConfigComment("MUST BE A FACTOR OF 16. If not, it will be rounded to be one.") @ConfigComment("It is the same for every dimension : Overworld, Nether and End.") @ConfigEntry(path = "world.area-radius", needsReset = true) private int islandDistance = 320; @@ -494,6 +496,11 @@ public Difficulty getDifficulty() { */ @Override public int getIslandDistance() { + if (islandDistance % 16 != 0) { + islandDistance = islandDistance - (islandDistance % 16); + BentoBox.getInstance() + .logWarning("Boxed: Area radius is not a factor of 16. Rounding to " + islandDistance); + } return islandDistance; } diff --git a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java index 579f01c..e58cc91 100644 --- a/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java +++ b/src/main/java/world/bentobox/boxed/listeners/NewAreaListener.java @@ -2,8 +2,10 @@ import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; @@ -12,6 +14,7 @@ import java.util.Random; import org.bukkit.Bukkit; +import org.bukkit.Chunk; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.NamespacedKey; @@ -33,6 +36,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.loot.LootTables; import org.bukkit.structure.Structure; import org.bukkit.util.BoundingBox; @@ -93,19 +97,23 @@ public record StructureRecord(String name, Structure structure, Location locatio private final File structureFile; private final Queue itemsToBuild = new LinkedList<>(); private static final Random rand = new Random(); - private boolean pasting; + private boolean pasting = true; private static final Gson gson = new Gson(); Pair min = new Pair<>(0, 0); Pair max = new Pair<>(0, 0); // Database handler for structure data private final Database handler; private final Map islandStructureCache = new HashMap<>(); + private Map, List> readyToBuild = new HashMap<>(); + private static String bukkitVersion = "v" + Bukkit.getBukkitVersion().replace('.', '_').replace('-', '_'); + private static String pluginPackageName; /** * @param addon addon */ public NewAreaListener(Boxed addon) { this.addon = addon; + pluginPackageName = addon.getClass().getPackage().getName(); // Save the default structures file from the jar addon.saveResource("structures.yml", false); // Load the config @@ -113,11 +121,11 @@ public NewAreaListener(Boxed addon) { // Get database ready handler = new Database<>(addon, IslandStructures.class); // Try to build something every second - runStructurePrinter(addon); + runStructurePrinter(); } - private void runStructurePrinter(Boxed addon2) { - Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), this::buildStructure, 20, 20); + private void runStructurePrinter() { + Bukkit.getScheduler().runTaskTimer(addon.getPlugin(), this::buildStructure, 100, 60); for (String js : JAR_STRUCTURES) { addon.saveResource("structures/" + js + ".nbt", false); File structureFile = new File(addon.getDataFolder(), "structures/" + js + ".nbt"); @@ -146,6 +154,29 @@ private void buildStructure() { } } + private void placeStructure(StructureRecord item) { + // Set the semaphore - only paste one at a time + pasting = true; + // Place the structure - this cannot be done async + item.structure().place(item.location(), true, item.rot(), item.mirror(), -1, 1, rand); + addon.log(item.name() + " placed at " + item.location().getWorld().getName() + " " + + Util.xyz(item.location().toVector())); + // Remove any jigsaw artifacts + BoundingBox bb = removeJigsaw(item); + // Store it for future reference + addon.getIslands().getIslandAt(item.location()).map(Island::getUniqueId).ifPresent(id -> { + //.log("Saved " + item.name()); + if (item.location().getWorld().getEnvironment().equals(Environment.NETHER)) { + getIslandStructData(id).addNetherStructure(bb, item.name()); + } else { + getIslandStructData(id).addStructure(bb, item.name()); + } + handler.saveObjectAsync(getIslandStructData(id)); + }); + // Clear the semaphore + pasting = false; + } + /** * Load known structures from the templates file. This makes them available for * admins to use in the boxed place file. If this is not done, then no @@ -155,22 +186,49 @@ private void buildStructure() { */ @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) public void onBentoBoxReady(BentoBoxReadyEvent event) { - addon.saveResource("templates.yml", false); - File templateFile = new File(addon.getDataFolder(), "templates.yml"); - if (templateFile.exists()) { - YamlConfiguration loader = YamlConfiguration.loadConfiguration(templateFile); - List list = loader.getStringList("templates"); - for (String struct : list) { - if (!struct.endsWith("/")) { - Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(struct)); + Bukkit.getScheduler().runTaskAsynchronously(addon.getPlugin(), () -> { + addon.saveResource("templates.yml", false); + File templateFile = new File(addon.getDataFolder(), "templates.yml"); + if (templateFile.exists()) { + YamlConfiguration loader = YamlConfiguration.loadConfiguration(templateFile); + List list = loader.getStringList("templates"); + for (String struct : list) { + if (!struct.endsWith("/")) { + Bukkit.getStructureManager().loadStructure(NamespacedKey.fromString(struct)); + } } } - } + pasting = false; // Allow pasting + }); + } + /** + * Add items to the queue when they are needed due to chunk loading + * @param e ChunkLoadEvent + */ + @EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true) + public void onChunkLoad(ChunkLoadEvent e) { + Chunk chunk = e.getChunk(); + // Check if this island is in this game + if (!(addon.inWorld(chunk.getWorld()))) { + return; + } + Pair chunkCoords = new Pair(chunk.getX(), chunk.getZ()); + if (this.readyToBuild.containsKey(chunkCoords)) { + Iterator it = this.readyToBuild.get(chunkCoords).iterator(); + while (it.hasNext()) { + StructureRecord item = it.next(); + if (item.location().getWorld().equals(e.getWorld())) { + this.itemsToBuild.add(item); + it.remove(); + } + } + } } + /** - * Track if a place has entered a structure. + * Track if a player has entered a structure. * * @param e PlayerMoveEvent */ @@ -312,36 +370,14 @@ private void place(ConfigurationSection section, Location center, Environment en int y = Integer.parseInt(value[1].strip()); int z = Integer.parseInt(value[2].strip()) + center.getBlockZ(); Location l = new Location(world, x, y, z); - itemsToBuild.add(new StructureRecord(name, s, l, rot, mirror, noMobs)); + readyToBuild.computeIfAbsent(new Pair(x >> 4, z >> 4), k -> new ArrayList<>()) + .add(new StructureRecord(name, s, l, rot, mirror, noMobs)); } else { addon.logError("Structure file syntax error: " + vector + ": " + Arrays.toString(value)); } } } - private void placeStructure(StructureRecord item) { - // Set the semaphore - only paste one at a time - pasting = true; - // Place the structure - item.structure().place(item.location(), true, item.rot(), item.mirror(), -1, 1, rand); - addon.log(item.name() + " placed at " + item.location().getWorld().getName() + " " - + Util.xyz(item.location().toVector())); - // Remove any jigsaw artifacts - BoundingBox bb = removeJigsaw(item); - // Store it for future reference - addon.getIslands().getIslandAt(item.location()).map(Island::getUniqueId).ifPresent(id -> { - addon.log("Saved " + item.name()); - if (item.location().getWorld().getEnvironment().equals(Environment.NETHER)) { - getIslandStructData(id).addNetherStructure(bb, item.name()); - } else { - getIslandStructData(id).addStructure(bb, item.name()); - } - handler.saveObjectAsync(getIslandStructData(id)); - }); - // Clear the semaphore - pasting = false; - } - /** * Removes Jigsaw blocks from a placed structure. Fills underwater ruins with * water. @@ -403,6 +439,9 @@ public static BoundingBox removeJigsaw(StructureRecord item) { private static void processStructureBlock(Block b) { // I would like to read the data from the block and do something with it! String data = nmsData(b); + if (data.isEmpty()) { + return; + } BoxedStructureBlock bsb = gson.fromJson(data, BoxedStructureBlock.class); b.setType(Material.STRUCTURE_VOID); Enums.getIfPresent(EntityType.class, bsb.getMetadata().toUpperCase(Locale.ENGLISH)).toJavaUtil() @@ -425,6 +464,9 @@ private static void processStructureBlock(Block b) { private static void processJigsaw(Block b, StructureRotation structureRotation, boolean pasteMobs) { String data = nmsData(b); + if (data.isEmpty()) { + return; + } BoxedJigsawBlock bjb = gson.fromJson(data, BoxedJigsawBlock.class); String finalState = correctDirection(bjb.getFinal_state(), structureRotation); BlockData bd = Bukkit.createBlockData(finalState); @@ -542,10 +584,6 @@ private static BlockFace getDirection(String finalState) { } private static String nmsData(Block block) { - // Bukkit method that was added in 2011 - // Example value: 1.20.4-R0.1-SNAPSHOT - String bukkitVersion = "v" + Bukkit.getBukkitVersion().replace('.', '_').replace('-', '_'); - String pluginPackageName = BentoBox.getInstance().getClass().getPackage().getName(); AbstractMetaData handler; try { Class clazz = Class.forName(pluginPackageName + ".nms." + bukkitVersion + ".GetMetaData"); @@ -555,6 +593,7 @@ private static String nmsData(Block block) { throw new IllegalStateException("Class " + clazz.getName() + " does not implement AbstractGetMetaData"); } } catch (Exception e) { + e.printStackTrace(); BentoBox.getInstance().logWarning("No metadata handler found for " + bukkitVersion + " in Boxed."); handler = new world.bentobox.boxed.nms.fallback.GetMetaData(); } diff --git a/src/main/java/world/bentobox/boxed/nms/AbstractMetaData.java b/src/main/java/world/bentobox/boxed/nms/AbstractMetaData.java index 51196bc..ccab4a5 100644 --- a/src/main/java/world/bentobox/boxed/nms/AbstractMetaData.java +++ b/src/main/java/world/bentobox/boxed/nms/AbstractMetaData.java @@ -1,7 +1,14 @@ package world.bentobox.boxed.nms; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + import org.bukkit.block.Block; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; +import net.minecraft.world.level.block.entity.TileEntity; + /** * */ @@ -9,4 +16,28 @@ public abstract class AbstractMetaData { public abstract String nmsData(Block block); + protected String getData(TileEntity te, String method, String field) { + try { + // Check if the method 'j' exists + Method updatePacketMethod = te.getClass().getDeclaredMethod(method); + if (updatePacketMethod != null) { + // Invoke the method to get the PacketPlayOutTileEntityData object + updatePacketMethod.setAccessible(true); + PacketPlayOutTileEntityData packet = (PacketPlayOutTileEntityData) updatePacketMethod.invoke(te); + + // Access the private field for the NBTTagCompound getter in PacketPlayOutTileEntityData + Field fieldC = packet.getClass().getDeclaredField(field); + fieldC.setAccessible(true); + NBTTagCompound nbtTag = (NBTTagCompound) fieldC.get(packet); + + return nbtTag.toString(); // This will show what you want + } + } catch (NoSuchMethodException e) { + System.out.println("The method '" + method + "' does not exist in the TileEntity class."); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + + } } diff --git a/src/main/java/world/bentobox/boxed/nms/fallback/GetMetaData.java b/src/main/java/world/bentobox/boxed/nms/fallback/GetMetaData.java index 423e6ee..ba63d91 100644 --- a/src/main/java/world/bentobox/boxed/nms/fallback/GetMetaData.java +++ b/src/main/java/world/bentobox/boxed/nms/fallback/GetMetaData.java @@ -11,7 +11,7 @@ public class GetMetaData extends AbstractMetaData { @Override public String nmsData(Block block) { - return "Nothing"; // We cannot read it if we have no NMS + return ""; // We cannot read it if we have no NMS } } diff --git a/src/main/java/world/bentobox/boxed/nms/v1_20_4_R0_1_SNAPSHOT/GetMetaData.java b/src/main/java/world/bentobox/boxed/nms/v1_20_4_R0_1_SNAPSHOT/GetMetaData.java index 57ed417..639598a 100644 --- a/src/main/java/world/bentobox/boxed/nms/v1_20_4_R0_1_SNAPSHOT/GetMetaData.java +++ b/src/main/java/world/bentobox/boxed/nms/v1_20_4_R0_1_SNAPSHOT/GetMetaData.java @@ -1,14 +1,10 @@ package world.bentobox.boxed.nms.v1_20_4_R0_1_SNAPSHOT; -import java.lang.reflect.Field; - import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.craftbukkit.v1_20_R3.CraftWorld; import net.minecraft.core.BlockPosition; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; import net.minecraft.world.level.block.entity.TileEntity; import world.bentobox.boxed.nms.AbstractMetaData; @@ -18,20 +14,8 @@ public class GetMetaData extends AbstractMetaData { public String nmsData(Block block) { Location w = block.getLocation(); CraftWorld cw = (CraftWorld) w.getWorld(); // CraftWorld is NMS one - // for 1.13+ (we have use WorldServer) TileEntity te = cw.getHandle().c_(new BlockPosition(w.getBlockX(), w.getBlockY(), w.getBlockZ())); - try { - PacketPlayOutTileEntityData packet = ((PacketPlayOutTileEntityData) te.j()); // get update packet from NMS - // object - // here we should use reflection because "c" field isn't accessible - Field f = packet.getClass().getDeclaredField("c"); // get field - f.setAccessible(true); // make it available - NBTTagCompound nbtTag = (NBTTagCompound) f.get(packet); - return nbtTag.toString(); // this will show what you want - } catch (Exception exc) { - exc.printStackTrace(); - } - return "Nothing"; - } + return getData(te, "j", "c"); + } } \ No newline at end of file diff --git a/src/main/java/world/bentobox/boxed/nms/v1_20_6_R0_1_SNAPSHOT/GetMetaData.java b/src/main/java/world/bentobox/boxed/nms/v1_20_6_R0_1_SNAPSHOT/GetMetaData.java index be6b438..98c4a99 100644 --- a/src/main/java/world/bentobox/boxed/nms/v1_20_6_R0_1_SNAPSHOT/GetMetaData.java +++ b/src/main/java/world/bentobox/boxed/nms/v1_20_6_R0_1_SNAPSHOT/GetMetaData.java @@ -1,14 +1,10 @@ package world.bentobox.boxed.nms.v1_20_6_R0_1_SNAPSHOT; -import java.lang.reflect.Field; - import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.craftbukkit.v1_20_R4.CraftWorld; import net.minecraft.core.BlockPosition; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; import net.minecraft.world.level.block.entity.TileEntity; import world.bentobox.boxed.nms.AbstractMetaData; @@ -18,20 +14,8 @@ public class GetMetaData extends AbstractMetaData { public String nmsData(Block block) { Location w = block.getLocation(); CraftWorld cw = (CraftWorld) w.getWorld(); // CraftWorld is NMS one - // for 1.13+ (we have use WorldServer) TileEntity te = cw.getHandle().c_(new BlockPosition(w.getBlockX(), w.getBlockY(), w.getBlockZ())); - try { - PacketPlayOutTileEntityData packet = ((PacketPlayOutTileEntityData) te.j()); // get update packet from NMS - // object - // here we should use reflection because "c" field isn't accessible - Field f = packet.getClass().getDeclaredField("c"); // get field - f.setAccessible(true); // make it available - NBTTagCompound nbtTag = (NBTTagCompound) f.get(packet); - return nbtTag.toString(); // this will show what you want - } catch (Exception exc) { - exc.printStackTrace(); - } - return "Nothing"; - } + return getData(te, "j", "c"); + } } \ No newline at end of file diff --git a/src/main/java/world/bentobox/boxed/nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java b/src/main/java/world/bentobox/boxed/nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java index d0634a0..36be817 100644 --- a/src/main/java/world/bentobox/boxed/nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java +++ b/src/main/java/world/bentobox/boxed/nms/v1_21_R0_1_SNAPSHOT/GetMetaData.java @@ -1,14 +1,10 @@ package world.bentobox.boxed.nms.v1_21_R0_1_SNAPSHOT; -import java.lang.reflect.Field; - import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.craftbukkit.v1_21_R1.CraftWorld; import net.minecraft.core.BlockPosition; -import net.minecraft.nbt.NBTTagCompound; -import net.minecraft.network.protocol.game.PacketPlayOutTileEntityData; import net.minecraft.world.level.block.entity.TileEntity; import world.bentobox.boxed.nms.AbstractMetaData; @@ -20,18 +16,8 @@ public String nmsData(Block block) { CraftWorld cw = (CraftWorld) w.getWorld(); // CraftWorld is NMS one // for 1.13+ (we have use WorldServer) TileEntity te = cw.getHandle().c_(new BlockPosition(w.getBlockX(), w.getBlockY(), w.getBlockZ())); - try { - PacketPlayOutTileEntityData packet = ((PacketPlayOutTileEntityData) te.j()); // get update packet from NMS - // object - // here we should use reflection because "c" field isn't accessible - Field f = packet.getClass().getDeclaredField("c"); // get field - f.setAccessible(true); // make it available - NBTTagCompound nbtTag = (NBTTagCompound) f.get(packet); - return nbtTag.toString(); // this will show what you want - } catch (Exception exc) { - exc.printStackTrace(); - } - return "Nothing"; + + return getData(te, "az_", "tag"); } } \ No newline at end of file diff --git a/src/test/java/world/bentobox/boxed/SettingsTest.java b/src/test/java/world/bentobox/boxed/SettingsTest.java index 8b07ac2..bfc2473 100644 --- a/src/test/java/world/bentobox/boxed/SettingsTest.java +++ b/src/test/java/world/bentobox/boxed/SettingsTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.verify; import java.util.Collections; import java.util.List; @@ -13,17 +14,30 @@ import org.bukkit.entity.EntityType; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import org.powermock.reflect.Whitebox; + +import world.bentobox.bentobox.BentoBox; /** * @author tastybento * */ +@RunWith(PowerMockRunner.class) +@PrepareForTest({ BentoBox.class }) public class SettingsTest { + @Mock + private BentoBox plugin; Settings s; @Before public void setUp() { + // Set up plugin + Whitebox.setInternalState(BentoBox.class, "instance", plugin); s = new Settings(); } /** @@ -59,7 +73,8 @@ public void testSetDifficulty() { @Test public void testSetIslandDistance() { s.setIslandDistance(123); - assertEquals(123, s.getIslandDistance()); + assertEquals(112, s.getIslandDistance()); + verify(plugin).logWarning("Boxed: Area radius is not a factor of 16. Rounding to 112"); } /** From 6678b4917f111410692610b1dfa6e68525e5d65d Mon Sep 17 00:00:00 2001 From: tastybento Date: Mon, 1 Jul 2024 17:57:57 -0700 Subject: [PATCH 6/7] Move to latest BentoBox API --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9878113..1e5271e 100644 --- a/pom.xml +++ b/pom.xml @@ -59,7 +59,7 @@ 2.0.9 1.21-R0.1-SNAPSHOT - 2.0.0-SNAPSHOT + 2.4.0-SNAPSHOT ${build.version}-SNAPSHOT From c51c1b5f96edcb945f186ab4fc9b70b48ed717ed Mon Sep 17 00:00:00 2001 From: BONNe Date: Tue, 16 Jul 2024 17:36:15 +0300 Subject: [PATCH 7/7] 2.5 was released a while ago --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 1e5271e..de50fbb 100644 --- a/pom.xml +++ b/pom.xml @@ -65,7 +65,7 @@ -LOCAL - 2.5.0 + 2.6.0 BentoBoxWorld_Boxed bentobox-world