Compare commits

...
168 Commits
Author SHA1 Message Date
Florian Reuth d8e4746053 ViaRewind 4.2.0
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-09-18 17:06:16 +02:00
BeanesandFlorian Reuth 82a3ee84e0 Replace 1.9.4 mappings with 1.9 mappings, fixes invalid sound (#689)
Signed-off-by: Florian Reuth <git@florianreuth.de>
Co-authored-by: Florian Reuth <git@florianreuth.de>
2026-09-16 18:28:43 +02:00
Beanes 9df4d11f8c Implement 1.9->1.8 command block translation (#692)
* Implement 1.9->1.8 command block translation

* Inline methods
2026-09-16 17:30:23 +02:00
Term4 c92916a265 Send the delayed potion spawn's velocity as its own packet in 1.9->1.8 (#722) 2026-09-16 17:27:33 +02:00
Term4 dc04defeb6 Pass view-relative teleport flags through in 1.9->1.8 (#719)
1.8 handles the rotation-relative position flags (0x08/0x10) natively,
and resolving them against the tracked rotation snaps the camera to a
stale look on every teleport. Keep the rotation bits and their deltas on
the wire and only resolve the position bits.

Since the client applies kept deltas to its own live look, its teleport
confirm echoes a rotation the tracker cannot predict - match those
confirms on position only and adopt the echoed rotation.
2026-09-16 17:27:04 +02:00
Beanes c7edeba340 Fix using an item in air packet not matching the 1.8 packet (#718) 2026-09-16 17:26:34 +02:00
dependabot[bot] d232bd020f [ci skip] Update GH actions (#720)
Bumps the all-dependencies group with 1 update: [actions/setup-java](https://github.com/actions/setup-java).


Updates `actions/setup-java` from 5.7.0 to 6.0.0
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/b6effb05e454b25005698d916606bdc6ffcbf961...dd06d9cba3e5552c54d9f8ea23572deb30010f7c)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: all-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 15:29:08 +02:00
dependabot[bot] 2525d42666 [ci skip] Update deps (#721)
Bumps the all-dependencies group with 1 update: [gradle-wrapper](https://github.com/gradle/gradle).


Updates `gradle-wrapper` from 9.7.0 to 9.7.1
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.7.0...v9.7.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: all-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-02 15:28:43 +02:00
Florian Reuth 4d51a62410 Fix class token replacing
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-08-19 19:56:12 +02:00
Florian Reuth b177e048dd Update BaseProject
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-08-19 19:32:39 +02:00
Florian Reuth 7577279fcb Update GH files
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-08-19 19:29:40 +02:00
dependabot[bot] e3211f99ef [ci skip] Bump gradle-wrapper from 9.6.1 to 9.7.0 (#717)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.6.1 to 9.7.0.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.6.1...v9.7.0)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-17 15:06:58 +02:00
Florian Reuth d562f9745a Update Via* API usage
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-08-13 17:48:09 +02:00
dependabot[bot] 7339df9fdb [ci skip] Bump gradle/actions/setup-gradle from 6.2.0 to 6.3.0 (#713)
Bumps [gradle/actions/setup-gradle](https://github.com/gradle/actions) from 6.2.0 to 6.3.0.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/3f131e8634966bd73d06cc69884922b02e6faf92...9c971963bec38e04b3d30dcc455b5382be2fdbfb)

---
updated-dependencies:
- dependency-name: gradle/actions/setup-gradle
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-13 17:17:59 +02:00
dependabot[bot] d98d5bed9b [ci skip] Bump actions/setup-java from 5.6.0 to 5.7.0 (#715)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.6.0 to 5.7.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/03ad4de0992f5dab5e18fcb136590ce7c4a0ac95...b6effb05e454b25005698d916606bdc6ffcbf961)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-13 17:17:44 +02:00
dependabot[bot] 786529efbe [ci skip] Bump actions/setup-java from 5.5.0 to 5.6.0 (#712)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.5.0 to 5.6.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/0f481fcb613427c0f801b606911222b5b6f3083a...03ad4de0992f5dab5e18fcb136590ce7c4a0ac95)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 18:41:34 +02:00
dependabot[bot] 71546a7699 [ci skip] Bump actions/checkout from 7.0.0 to 7.0.1 (#711)
Bumps [actions/checkout](https://github.com/actions/checkout) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0...3d3c42e5aac5ba805825da76410c181273ba90b1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-28 18:41:24 +02:00
Term4andFlorian Reuth 9e7c853a5d Fix splash potions turning into drinkable potions in 1.9->1.8 (#703)
Co-authored-by: Florian Reuth <git@florianreuth.de>
2026-07-25 21:51:08 +02:00
315a1a0fab Handle single player inventory slot updates in 1.9->1.8 (#699)
Co-authored-by: Beanes <sikyserver@gmail.com>
Co-authored-by: Florian Reuth <git@florianreuth.de>
2026-07-25 21:27:09 +02:00
BeanesandFlorian Reuth 7a2898e44a Send server titles back to the client after cooldown visualization in 1.9->1.8 (#693)
Fixes https://github.com/ViaVersion/ViaRewind/issues/160

Co-authored-by: Florian Reuth <git@florianreuth.de>
2026-07-25 21:06:50 +02:00
Beanes 5783fa98e9 Queue teleport confirmation acks to match client behaviour in 1.9->1.8 (#704)
Fixes https://github.com/ViaVersion/ViaRewind/issues/469
2026-07-25 20:12:44 +02:00
Term4 77d9d9143e Fix double->int*32 conversion rounding in 1.9->1.8 (#707) 2026-07-25 20:11:19 +02:00
Term4 ede1920686 Fix use animation cancelling upon status updates in 1.9->1.8 (#708)
Fixes https://github.com/ViaVersion/ViaRewind/issues/520
2026-07-25 19:00:29 +02:00
dependabot[bot] 189249478f [ci skip] Bump actions/setup-java from 5.4.0 to 5.5.0 (#710)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.4.0 to 5.5.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/1bcf9fb12cf4aa7d266a90ae39939e61372fe520...0f481fcb613427c0f801b606911222b5b6f3083a)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-25 18:49:53 +02:00
dependabot[bot] 6c19760af6 Bump actions/setup-java from 5.3.0 to 5.4.0 (#705)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.3.0 to 5.4.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/ad2b38190b15e4d6bdf0c97fb4fca8412226d287...1bcf9fb12cf4aa7d266a90ae39939e61372fe520)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-12 20:22:51 +02:00
dependabot[bot] 69f8213445 Bump gradle-wrapper from 9.6.0 to 9.6.1 (#706)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.6.0 to 9.6.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.6.0...v9.6.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-12 20:22:43 +02:00
Florian Reuth f95871bdc5 Start 4.1.4 cycle
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-07-12 20:18:36 +02:00
Florian Reuth 7d6c75ad31 ViaRewind 4.1.3
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-07-12 20:13:54 +02:00
Nassim Jahnke 0bb7be88ff Update mapping files 2026-07-12 11:05:47 +02:00
Nassim Jahnke 4b7264c944 Update VV usage 2026-07-09 15:22:35 +02:00
Beanes db6afa5b1b Fix potential 1.7 teleport confirmation desync (#695) 2026-07-02 16:21:32 +02:00
Beanes 02f55edb0c Fix edge cases with chat newline rewriter; make it more like vanilla (#696) 2026-07-02 16:14:48 +02:00
dependabot[bot] 5ef900a61e [ci skip] Bump actions/checkout from 6.0.3 to 7.0.0 (#701)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.3 to 7.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/df4cb1c069e1874edd31b4311f1884172cec0e10...9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 15:32:45 +02:00
dependabot[bot] 4fe7e35f6b [ci skip] Bump gradle-wrapper from 9.5.1 to 9.6.0 (#700)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.5.1 to 9.6.0.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.5.1...v9.6.0)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 15:32:30 +02:00
dependabot[bot] 1af08ced41 [ci skip] Bump actions/setup-java from 5.2.0 to 5.3.0 (#702)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/be666c2fcd27ec809703dec50e508c2fdc7f6654...ad2b38190b15e4d6bdf0c97fb4fca8412226d287)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.3.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-07-02 15:32:20 +02:00
Nassim Jahnke fd1d3ed221 Update VV use 2026-07-01 21:05:36 +02:00
dependabot[bot] 1d6ef8833f [ci skip] Bump gradle/actions from 6.1.0 to 6.2.0 (#698)
Bumps [gradle/actions](https://github.com/gradle/actions) from 6.1.0 to 6.2.0.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/50e97c2cd7a37755bbfafc9c5b7cafaece252f6e...3f131e8634966bd73d06cc69884922b02e6faf92)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-24 05:15:50 +02:00
Beanes f2e2f18511 Fix custom sound handling in 1.9->1.8 (#691) 2026-06-20 00:16:36 +02:00
Florian Reuth 2adf7757f0 Start 4.1.3 cycle
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-06-20 00:13:53 +02:00
Florian Reuth 4dd3de2ce9 ViaRewind 4.1.2
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-06-19 21:52:24 +02:00
Florian Reuth ddbdbdb1cc Cleanup code to reproduce the original tag
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-06-19 21:45:16 +02:00
Beanes 28b4998912 Use gray color for elytra & brown color for shield in 1.9->1.8 (#688) 2026-06-19 21:38:32 +02:00
Beanes f9b0fd5c5a Custom payload fixes (#687)
* Fix remaining bytes issue of 1.7.10 custom payload

* Properly rewrite 1.7 books to 1.8 books

* Small code cleanup
2026-06-19 21:35:54 +02:00
Florian Reuth a676368588 26.2 2026-06-19 21:32:14 +02:00
Beanes 966231ad1c Null check storages in global tasks (#690)
Fixes https://github.com/ViaVersion/ViaRewind/issues/564
Fixes https://github.com/ViaVersion/ViaRewind/issues/680
2026-06-19 21:21:00 +02:00
dependabot[bot] d73c2198ea [ci skip] Bump actions/checkout from 6.0.2 to 6.0.3 (#686)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.2 to 6.0.3.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-18 21:57:00 +02:00
Florian Reuth 6f319932be Start 4.1.2 cycle 2026-06-09 20:50:39 +02:00
Beanes f172de2742 Rewrite to direct chunk buffer handling in 1.8->1.7 (#670)
* Use netty directly for chunks in 1.8->1.7, additionally support native compression by velocity for 1.7 chunk compression

* Refactor the velocity code a bit
2026-06-09 20:49:09 +02:00
dependabot[bot] ca25da9445 [ci skip] Bump gradle-wrapper from 9.5.0 to 9.5.1 (#683)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.5.0 to 9.5.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.5.0...v9.5.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.5.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-07 01:41:05 +02:00
dependabot[bot] ea548dfd91 [ci skip] Bump gradle-wrapper from 9.4.1 to 9.5.0 (#681)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.4.1 to 9.5.0.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.4.1...v9.5.0)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 20:48:44 +02:00
Florian Reuth d0561f2e19 ViaRewind 4.1.1 2026-05-05 20:42:51 +02:00
Beanes 2c8189a447 Fix edge case when server sends a useless packet (#679) 2026-04-28 23:06:11 +02:00
Beanes 48c9e45bcf Also rewrite extra entity ids for other interaction modes (#678) 2026-04-26 16:56:26 +02:00
Gabik21 ecb4f4bf00 Fix nametag visibility duplicate names edge case (#675) 2026-04-24 21:34:24 +02:00
Kichura 6ea9377622 [ci skip] Fix version comments, Unsubscribe from proprietary caching tier. (#677) 2026-04-24 21:34:05 +02:00
Beanes b789fdebb7 Fix set score packet crashing (#674) 2026-04-22 15:16:00 +02:00
Beanes e7b0606e18 Rewrite "no face" in digging packet to a valid 1.8 value (#673) 2026-04-22 13:43:35 +02:00
NotAlexNoyle bf490c3805 Fix brewing stand titles for 1.8 clients on 1.9+ servers (#672) 2026-04-20 22:10:04 +02:00
Florian Reuth 7ca7078141 Start 4.1.1 cycle
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-20 22:09:12 +02:00
Florian Reuth 170af55f91 ViaRewind 4.1.0
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-20 21:15:13 +02:00
dependabot[bot] 00d82f9f2f [ci skip] Bump actions/upload-artifact from 7.0.0 to 7.0.1 (#671)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/bbbca2ddaa5d8feaa63e36b76fdaad77386f024f...043fb46d1a93c77aae656e7c1c64a875d1fc6a0a)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-20 21:13:24 +02:00
Beanes 79ef613c15 Implement invisible name tag tracking in 1.8->1.7 (#666)
Fixes https://github.com/ViaVersion/ViaRewind/issues/518
Fixes https://github.com/ViaVersion/ViaRewind/issues/364
2026-04-19 21:58:18 +02:00
Beanes e2fcbafc70 Improve hologram emulation in 1.8->1.7 (#667)
* Fix metadata flags not saving cross packets syncing

* Fix marker zombie no gravity offsets

* Improve interaction by rewriting "extra" entity ids of holograms to normal hologram id
2026-04-19 17:40:18 +02:00
dependabot[bot] 9ae46145e5 [ci skip] Bump gradle/actions from 5.0.2 to 6.1.0 (#669)
Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.2 to 6.1.0.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/0723195856401067f7a2779048b490ace7a47d7c...50e97c2cd7a37755bbfafc9c5b7cafaece252f6e)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-19 17:27:54 +02:00
dependabot[bot] 76f90b68b8 Bump de.florianreuth.baseproject from 2.0.1 to 2.0.2 (#665)
Bumps de.florianreuth.baseproject from 2.0.1 to 2.0.2.

---
updated-dependencies:
- dependency-name: de.florianreuth.baseproject
  dependency-version: 2.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-04-09 17:59:11 +02:00
Florian Reuth 32a2ee6bfa 26.1.2
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-09 17:54:37 +02:00
Florian Reuth 04878b7e34 26.1.1
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-04-02 20:50:46 +02:00
Florian Reuth adccdc4b41 26.1
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-03-27 17:39:20 +01:00
dependabot[bot] 0accea66f2 Bump gradle-wrapper from 9.4.0 to 9.4.1 (#662)
Bumps [gradle-wrapper](https://github.com/gradle/gradle) from 9.4.0 to 9.4.1.
- [Release notes](https://github.com/gradle/gradle/releases)
- [Commits](https://github.com/gradle/gradle/compare/v9.4.0...v9.4.1)

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-26 16:11:29 +01:00
dependabot[bot] 65257be3ee Bump actions/upload-artifact from 6.0.0 to 7.0.0 (#658)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6.0.0 to 7.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/b7c566a772e6b6bfb58ed0dc250532a479d7789f...bbbca2ddaa5d8feaa63e36b76fdaad77386f024f)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 7.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 21:58:54 +01:00
dependabot[bot] 880e879dce Bump gradle/actions from 5.0.1 to 5.0.2 (#659)
Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.1 to 5.0.2.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/f29f5a9d7b09a7c6b29859002d29d24e1674c884...0723195856401067f7a2779048b490ace7a47d7c)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 5.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 21:58:46 +01:00
dependabot[bot] 0ccc6a0923 Bump gradle-wrapper from 9.3.1 to 9.4.0 (#660)
Bumps gradle-wrapper from 9.3.1 to 9.4.0.

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-11 21:58:40 +01:00
dependabot[bot] 7bff361e14 Bump de.florianreuth.baseproject from 2.0.0 to 2.0.1 (#657)
Bumps de.florianreuth.baseproject from 2.0.0 to 2.0.1.

---
updated-dependencies:
- dependency-name: de.florianreuth.baseproject
  dependency-version: 2.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-03-03 20:34:07 +01:00
Florian Reuth c2c28473cc Start 4.1.0 cycle
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-03-01 18:10:12 +01:00
Florian Reuth 048d502998 ViaRewind 4.0.15
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-03-01 18:05:34 +01:00
Beanes e0c1f53115 Manually split newlines in chat messages in 1.8->1.7 (#655) 2026-02-16 17:55:48 +01:00
dependabot[bot] e5313a466b Bump gradle/actions from 5.0.0 to 5.0.1 (#654)
Bumps [gradle/actions](https://github.com/gradle/actions) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/4d9f0ba0025fe599b4ebab900eb7f3a1d93ef4c2...f29f5a9d7b09a7c6b29859002d29d24e1674c884)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: 5.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-12 15:20:14 +01:00
dependabot[bot] e30633e367 Bump gradle-wrapper from 9.3.0 to 9.3.1 (#653)
Bumps gradle-wrapper from 9.3.0 to 9.3.1.

---
updated-dependencies:
- dependency-name: gradle-wrapper
  dependency-version: 9.3.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-07 16:04:17 +01:00
Florian Reuth a443dd5d11 Update my name
Signed-off-by: Florian Reuth <git@florianreuth.de>
2026-02-07 16:00:19 +01:00
dependabot[bot] 3820426bce Bump actions/setup-java from 5.1.0 to 5.2.0 (#651)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.1.0 to 5.2.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/f2beeb24e141e01a676f977032f5a29d81c9e27e...be666c2fcd27ec809703dec50e508c2fdc7f6654)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-03 16:32:51 +01:00
Beanes 031ae32f27 Only set hologram state if name tag is visible (#652) 2026-02-03 16:32:38 +01:00
FlorianMichael e83616139e Update supported Velocity versions
Signed-off-by: FlorianMichael <git@florianmichael.de>
2026-01-31 20:16:07 +01:00
dependabot[bot] 7325b733d6 Bump actions/checkout from 6.0.1 to 6.0.2 (#650)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/8e8c483db84b4bee98b60c0593521ed34d9990e8...de0fac2e4500dabe0009e67214ff5f5447ce83dd)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-27 10:57:27 +01:00
Alessandro e64501eae8 Fix red sandstone stairs rotation in 1.8->1.7 (#649) 2026-01-25 17:09:32 +01:00
FlorianMichael 2d99607c35 Start 4.0.15 cycle
Signed-off-by: FlorianMichael <git@florianmichael.de>
2026-01-18 16:31:36 +01:00
FlorianMichael 4d159189c0 ViaRewind 4.0.14
Signed-off-by: FlorianMichael <git@florianmichael.de>
2026-01-18 16:19:42 +01:00
FlorianMichael 2076294856 Gradle 9.3.0
Signed-off-by: FlorianMichael <git@florianmichael.de>
2026-01-18 13:36:11 +01:00
FlorianMichael 49f7bd22be Add default impl class 2026-01-17 14:19:49 +01:00
FlorianMichael 298605874d Remove special sign component type 2026-01-10 00:39:23 +01:00
BeanesandFlorianMichael 35b94f1878 Use velocity native compression if available (#647)
* Optimize 1.7 compression by using velocity-native compressor

* Rewrite 1.7 chunk serializing to reduce allocations

* Don't include velocity native as shaded dependency, add fallback and only use velocity compression if available

* Change chunk compression back to default java as chunks are serialized on heap byte arrays so native compression has no use

* Move velocity only code in custom decoder/encoder overrides

* Remove debug

* Move compression into non static context

---------

Co-authored-by: FlorianMichael <git@florianmichael.de>
2026-01-06 03:25:48 +01:00
FlorianMichael 37955059c4 Don't require config impl class in ViaRewindPlatform#init 2026-01-04 13:01:44 +01:00
FlorianMichael af4306bc81 Start 4.0.14 cycle 2025-12-29 15:08:09 +01:00
FlorianMichael 98ca00f625 ViaRewind 4.0.13 2025-12-29 15:04:19 +01:00
dependabot[bot] 598804432b Bump actions/upload-artifact from 5.0.0 to 6.0.0 (#648)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5.0.0 to 6.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/330a01c490aca151604b8cf639adc76d48f6c5d4...b7c566a772e6b6bfb58ed0dc250532a479d7789f)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-29 14:30:50 +01:00
FlorianMichael 92bae0a5c4 Update VV API usage 2025-12-29 14:23:20 +01:00
FlorianMichael d50b6a2f89 Prepare for next year (2026) 2025-12-29 00:12:28 +01:00
Kichura 54cc085717 Change schedule's interval to monthly. (#633) 2025-12-20 14:30:53 +01:00
dependabot[bot] bd0d4c31b2 Bump actions/checkout from 6.0.0 to 6.0.1 (#644)
Bumps [actions/checkout](https://github.com/actions/checkout) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/1af3b93b6815bc44a9784bd300feb67ff0d1eeb3...8e8c483db84b4bee98b60c0593521ed34d9990e8)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 21:41:52 +01:00
dependabot[bot] 705c88f4b3 Bump actions/setup-java from 5.0.0 to 5.1.0 (#645)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 5.0.0 to 5.1.0.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/dded0888837ed1f317902acf8a20df0ad188d165...f2beeb24e141e01a676f977032f5a29d81c9e27e)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: 5.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-15 21:41:45 +01:00
FlorianMichael 7306a795b9 [ci skip] Include the newest ViaProxy version from 3.0.0 to 4.0.0 2025-12-13 20:00:44 +01:00
FlorianMichael dfdfad4bfc Add ViaRewindPlatform#init with config instance 2025-12-13 19:59:51 +01:00
FlorianMichael c87654848d Start 4.0.13 cycle 2025-12-12 16:08:44 +01:00
FlorianMichael 08a7077d10 ViaRewind 4.0.12 2025-12-12 16:04:46 +01:00
dependabot[bot] 73afaaf493 Bump io.papermc.hangar-publish-plugin from 0.1.3 to 0.1.4 (#636)
Bumps io.papermc.hangar-publish-plugin from 0.1.3 to 0.1.4.

---
updated-dependencies:
- dependency-name: io.papermc.hangar-publish-plugin
  dependency-version: 0.1.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-12 14:29:12 +01:00
dependabot[bot] f0f8038b18 Bump de.florianmichael.baseproject.BaseProject from 1.2.5 to 1.2.6 (#631)
Bumps de.florianmichael.baseproject.BaseProject from 1.2.5 to 1.2.6.

---
updated-dependencies:
- dependency-name: de.florianmichael.baseproject.BaseProject
  dependency-version: 1.2.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-29 23:11:31 +01:00
Kichura 540c0d0d47 Mitigate security vulnerabilities in CI. (#632) 2025-11-29 23:10:32 +01:00
dependabot[bot] d00b0c8afe Bump de.florianmichael.baseproject.BaseProject from 1.2.4 to 1.2.5 (#628)
Bumps de.florianmichael.baseproject.BaseProject from 1.2.4 to 1.2.5.

---
updated-dependencies:
- dependency-name: de.florianmichael.baseproject.BaseProject
  dependency-version: 1.2.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 21:59:31 +01:00
dependabot[bot] b34385c09e Bump actions/checkout from 5 to 6 (#630)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 21:59:24 +01:00
FlorianMichael ff47845df1 Gradle 9.2.0 2025-11-03 16:18:55 +01:00
FlorianMichael fc37b9c0ba Only send start fall flying if player has elytra on chestplate in 1.9->1.8
Closes https://github.com/ViaVersion/ViaRewind/pull/625
2025-10-29 15:28:17 +01:00
FlorianMichael e3baafa2a2 4.0.11 Release 2025-10-12 19:05:30 +02:00
Nassim Jahnke 11914da37e Use VV contained sound mappings in 1.9->1.8 2025-10-12 13:58:31 +02:00
FlorianMichael 30fa58af81 Fix boat y offset in 1.9->1.8 2025-10-09 21:40:47 +02:00
dependabot[bot] 52b6aed005 Bump de.florianmichael.baseproject.BaseProject from 1.2.2 to 1.2.3 (#620)
Bumps de.florianmichael.baseproject.BaseProject from 1.2.2 to 1.2.3.

---
updated-dependencies:
- dependency-name: de.florianmichael.baseproject.BaseProject
  dependency-version: 1.2.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-07 17:51:03 +02:00
dependabot[bot] 9d74706e84 Bump gradle/actions from 4 to 5 (#621)
Bumps [gradle/actions](https://github.com/gradle/actions) from 4 to 5.
- [Release notes](https://github.com/gradle/actions/releases)
- [Commits](https://github.com/gradle/actions/compare/v4...v5)

---
updated-dependencies:
- dependency-name: gradle/actions
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-07 17:50:55 +02:00
FlorianMichael 7d5ad45ffc Update VV api usage 2025-10-07 17:50:24 +02:00
FlorianMichael bd3593e15a 4.0.10 Release 2025-10-03 13:54:15 +02:00
FlorianMichael 835fb5de08 Update VV api usage 2025-09-30 19:57:11 +02:00
FlorianMichael 8b2769f19b Don't include subprojects as dependencies in the root project 2025-09-25 21:03:21 +02:00
FlorianMichael 4da08b5a35 Auto: Gradle 9.1.0 2025-09-22 21:46:11 +02:00
dependabot[bot] 7da8e62a39 Bump actions/setup-java from 4 to 5 (#619)
Bumps [actions/setup-java](https://github.com/actions/setup-java) from 4 to 5.
- [Release notes](https://github.com/actions/setup-java/releases)
- [Commits](https://github.com/actions/setup-java/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-java
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-30 18:26:38 +02:00
FlorianMichael 31c62f96b9 [ci skip] Fix SECURITY.md formatting 2025-08-12 06:05:37 +02:00
FlorianMichael 0f866d8b50 Fix sign update component read types in 1.7<->1.9
Closes https://github.com/ViaVersion/ViaRewind/issues/613
2025-08-12 06:02:30 +02:00
dependabot[bot] 32d265116a [ci skip] Bump actions/checkout from 4 to 5 (#617)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-12 05:50:50 +02:00
dependabot[bot] b7d1197be3 [ci skip] Bump de.florianmichael.baseproject.BaseProject from 1.1.1 to 1.2.1 (#614)
Bumps de.florianmichael.baseproject.BaseProject from 1.1.1 to 1.2.1.

---
updated-dependencies:
- dependency-name: de.florianmichael.baseproject.BaseProject
  dependency-version: 1.2.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-08 02:08:39 +02:00
FlorianMichael d4363b67b6 Gradle 9.0.0 2025-08-03 01:35:34 +02:00
FlorianMichael dcdce8ad52 4.0.9 Release 2025-07-27 14:51:16 +02:00
Ambassator af44f88d6e Remove the wither outline when using the bossbar on 1.9->1.8 (#604)
Adds metadata to the spawn packet to make the wither tiny so the player doesn't see a wither outline when the bossbar is on low health
2025-07-27 14:27:06 +02:00
Beanes 1d1d77abfd Add support for armor stands with no gravity as zombie in 1.8->1.7 (#609) 2025-07-27 14:24:06 +02:00
BeanesandFlorianMichael ae800f16be Basic chat item handling in 1.8->1.7 (#606)
This is still missing most packets and should one day be moved into a proper component rewriter, but this works for now.


Co-authored-by: FlorianMichael <florian.michael07@gmail.com>
2025-07-27 14:17:55 +02:00
BeanesandFlorianMichael f31d731f34 Cleanup player info logic 1.8->1.7 (#608)
* Cleanup player info logic

* Don't update ping if it's the same

* Cleanup

---------

Co-authored-by: FlorianMichael <florian.michael07@gmail.com>
2025-07-26 20:10:15 +02:00
Beanes 2ddd0d8b66 [ci skip] Clarify respawn metadata resending (#612) 2025-07-26 13:43:57 +02:00
BeanesandFlorianMichael f97884158b Implement play state compression and handle compression being set twice (#607)
* Implement play state compression and handle compression being set twice

* Fix 0 threshold; Code cleanup

---------

Co-authored-by: FlorianMichael <florian.michael07@gmail.com>
2025-07-25 20:57:49 +02:00
FlorianMichael 1e2fcb9c58 [ci skip] Reformat all classes 2025-07-25 11:26:09 +02:00
FlorianMichael 5110326a90 Aggressively validate sign update component 2025-07-25 11:20:40 +02:00
FlorianMichael 6887556b2c [ci skip] Add SECURITY.md 2025-07-25 08:37:43 +02:00
LabyStudio 66cf650d36 Limit legacy string trimming (#605) 2025-07-25 08:36:05 +02:00
FlorianMichael 1426631c97 [ci skip] Update ViaProxy 2025-07-24 20:15:40 +02:00
EthanKeating b6a32823cc Fix hologram height offsets in 1.8->1.7 (#601) 2025-07-23 18:27:28 +02:00
EthanKeatingandFlorianMichael aff88a6ca3 Fix respawn screen appearing twice in 1.8->1.7 (#597)
* Fixed double respawn screen displaying

* Only remove, don't re-add

---------

Co-authored-by: FlorianMichael <florian.michael07@gmail.com>
2025-07-22 16:32:49 +02:00
FlorianMichaelandNassim Jahnke 0edfd844b9 [ci skip] Split up publishing steps
Co-authored-by: Nassim Jahnke <nassim@njahnke.dev>
2025-07-22 16:26:41 +02:00
FlorianMichael 57a64740aa [ci skip] Mark 1.21.8 release 2025-07-17 21:27:26 +02:00
EthanKeating f7903050bf Update ping value in player list entry instead of sending a new one in 1.8->1.7 (#599) 2025-07-14 00:18:29 +02:00
EthanKeatingandEnZaXD 3a7ac0765b Also pass sneaking through hologram emulation; fix attachments 1.8->1.7 (#598)
* Fixed holograms flying up + Hologram sneaking

* Code cleanup

---------

Co-authored-by: EnZaXD <florian.michael07@gmail.com>
2025-07-13 21:13:47 +02:00
FlorianMichael 48f500a470 Copy over always show nametag 1:1 2025-07-13 21:00:22 +02:00
EthanKeating c3e19b769b Don't drop alwaysShowNametag entity data in hologram emulation 1.8->1.7 (#600) 2025-07-13 20:49:05 +02:00
dependabot[bot] 561829d952 Bump net.raphimc.class-token-replacer from 1.1.6 to 1.1.7 (#594)
Bumps net.raphimc.class-token-replacer from 1.1.6 to 1.1.7.

---
updated-dependencies:
- dependency-name: net.raphimc.class-token-replacer
  dependency-version: 1.1.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-30 21:04:07 +02:00
FlorianMichael 6767379526 Mark 1.21.7 release 2025-06-30 21:02:49 +02:00
mariaum a10ce1f35f Remap yaw values correctly when teleporting entities in 1.9->1.8 (#585) 2025-06-27 02:40:40 +02:00
FlorianMichael 49f0c673e6 Bump version to 4.0.9-SNAPSHOT 2025-06-27 02:39:00 +02:00
dependabot[bot] fd2c81a6fd [ci skip] Bump de.florianmichael.baseproject.BaseProject from 1.0.7 to 1.1.1 (#588)
Bumps de.florianmichael.baseproject.BaseProject from 1.0.7 to 1.1.1.

---
updated-dependencies:
- dependency-name: de.florianmichael.baseproject.BaseProject
  dependency-version: 1.1.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-27 02:10:49 +02:00
FlorianMichael 901463e095 4.0.8 Release 2025-06-20 20:11:01 +02:00
FlorianMichael 227d37b2b9 Update VV api usage 2025-06-16 18:27:46 +02:00
FlorianMichael be5bb6e07c Write flags as byte in PLAYER_INPUT 1.8->1.7 2025-06-10 20:16:14 +02:00
Nassim Jahnke 064dbee920 Update profile properties type 2025-05-26 17:32:13 +02:00
FlorianMichael 783006baa5 Auto: Gradle 8.14.1 2025-05-22 17:51:04 +02:00
Adrian ba93a40d22 Change end rod block state mapping to white stained glass in 1.9->1.8 (#584) 2025-05-22 00:18:48 +02:00
dependabot[bot] 422f81dd2e Bump org.gradle.toolchains.foojay-resolver-convention (#583)
Bumps org.gradle.toolchains.foojay-resolver-convention from 0.10.0 to 1.0.0.

---
updated-dependencies:
- dependency-name: org.gradle.toolchains.foojay-resolver-convention
  dependency-version: 1.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-22 00:12:32 +02:00
FlorianMichael aa47e3c0a5 Update BaseProject 2025-05-19 00:12:39 +02:00
FlorianMichael 094298771e Update ClassTokenReplacer 2025-05-17 17:57:07 +02:00
FlorianMichael 66133d1ccc Update VV api usage 2025-05-17 15:37:37 +02:00
FlorianMichael 11011d59aa Rewrite build files 2025-05-15 15:28:44 +02:00
FlorianMichael 691cbf1b2a Small cleanup 2025-05-15 11:18:26 +02:00
EthanKeating 8d8675a891 Fix hologram movement by wrapping them to an interpolated entity in 1.8->1.7 (#580)
* Hologram movement fix for 1.7 clients

* fix: addressed feedback from PR review
2025-05-15 08:26:12 +02:00
Beanes 4fa2f10f3a Use correct entity id data type in 1.8->1.7 entity data sending (#579) 2025-05-08 20:24:50 +02:00
FlorianMichael c2e516dcd2 Auto: Gradle 8.14 2025-04-26 16:25:07 +02:00
BeanesandFlorianMichael 153eedd5eb Optimize memory usage for 1.7 chunk rewriting (#577)
* Optimize memory usage

* Bump version; cleanup code

---------

Co-authored-by: FlorianMichael <florian.michael07@gmail.com>
2025-04-22 23:52:39 +02:00
FlorianMichael 2d404608ee [ci skip] Reformat GH issue templates 2025-04-20 14:41:56 +02:00
130 changed files with 9399 additions and 8013 deletions

No files matched your search

+2 -2
View File
@@ -1,2 +1,2 @@
github: FlorianMichael
custom: ['https://florianmichael.de/donate', 'https://creeper123123321.github.io/#donate']
github: florianreuth
custom: [ 'https://florianreuth.de/donate', 'https://creeper123123321.github.io/#donate' ]
+2 -2
View File
@@ -1,6 +1,6 @@
name: Bug report
description: Report a bug or console error
labels: [Unconfirmed]
labels: [ Unconfirmed ]
body:
- type: markdown
@@ -91,4 +91,4 @@ body:
- label: I have tried the latest build(s) from https://ci.viaversion.com/ and the issue still persists.
required: true
- label: I have looked at https://www.spigotmc.org/resources/viarewind-legacy-support.52924/ and it does not offer a fix for my problem
required: true
required: true
+18 -18
View File
@@ -5,34 +5,34 @@ labels: [ Feature Request ]
body:
- type: textarea
attributes:
label: Problem Description
description: |
Describe the issue you are facing or why you need the feature to be added.
placeholder: |
I am always frustrated with...
label: Problem Description
description: |
Describe the issue you are facing or why you need the feature to be added.
placeholder: |
I am always frustrated with...
validations:
required: true
required: true
- type: textarea
attributes:
label: Solution Description
description: |
Describe the solution you would like to see.
label: Solution Description
description: |
Describe the solution you would like to see.
validations:
required: true
required: true
- type: textarea
attributes:
label: Alternatives
description: |
Describe alternatives you have considered.
label: Alternatives
description: |
Describe alternatives you have considered.
validations:
required: false
required: false
- type: textarea
attributes:
label: Additional Info
description: |
Does the feature apply to any specific version or environment?
label: Additional Info
description: |
Does the feature apply to any specific version or environment?
validations:
required: false
required: false
+14 -2
View File
@@ -1,10 +1,22 @@
version: 2
updates:
- package-ecosystem: "gradle"
cooldown:
default-days: 7
directory: "/"
schedule:
interval: "daily"
interval: "monthly"
groups:
all-dependencies:
patterns:
- "*"
- package-ecosystem: "github-actions"
cooldown:
default-days: 7
directory: "/"
schedule:
interval: "weekly"
interval: "monthly"
groups:
all-dependencies:
patterns:
- "*"
+19 -8
View File
@@ -1,21 +1,32 @@
name: Build
on: [pull_request, push, workflow_dispatch]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-22.04
runs-on: ubuntu-26.04
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
with:
cache-disabled: true
cache-provider: basic
- name: Set up Java
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
with:
distribution: 'temurin'
java-version: 17
java-version: 25
check-latest: true
- name: Build with Gradle
run: ./gradlew build
run: ./gradlew build --stacktrace
- name: Upload Artifacts to GitHub
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: Artifacts
path: build/libs/
if-no-files-found: error
+23 -8
View File
@@ -4,26 +4,41 @@ on:
branches:
- master
- dev
workflow_dispatch:
permissions:
contents: read
jobs:
publish:
if: github.repository_owner == 'ViaVersion'
runs-on: ubuntu-22.04
runs-on: ubuntu-26.04
timeout-minutes: 30
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: gradle/actions/setup-gradle@9c971963bec38e04b3d30dcc455b5382be2fdbfb # v6.3.0
with:
cache-disabled: true
cache-provider: basic
- name: Set up Java
uses: actions/setup-java@dd06d9cba3e5552c54d9f8ea23572deb30010f7c # v6.0.0
with:
distribution: 'temurin'
java-version: 17
java-version: 25
check-latest: true
- name: Publish
- name: Build with Gradle
run: ./gradlew build --stacktrace
- name: Publish to Hangar
env:
HANGAR_TOKEN: ${{ secrets.HANGAR_TOKEN }}
run: ./gradlew publishAllPublicationsToHangar --stacktrace
continue-on-error: true
- name: Publish to Modrinth
env:
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
run: ./gradlew build modrinth publishAllPublicationsToHangar
run: ./gradlew modrinth --stacktrace
continue-on-error: true
+20 -100
View File
@@ -1,116 +1,36 @@
### Java files ###
*.class
# Gradle
# Package Files
#*.jar
*.war
*.ear
.gradle/
build/
out/
classes/
.kotlin/
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# Eclipse
*.launch
# Idea
### Intellij ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
# Ignore project files
.idea/
!.idea/copyright/*
!.idea/scopes/*
*.iml
# Ignore IDEA directory
.idea/*
# Include the project's dictionary
!.idea/dictionaries/
!.idea/dictionaries/*
# File-based project format:
*.ipr
*.iws
### Plugin-specific files: ###
# IntelliJ
/out/
# VSCode
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
### Eclipse ###
*.pydevproject
.metadata
.gradle
bin/
tmp/
*.tmp
*.bak
*.swp
*~.nib
local.properties
.settings/
.loadpath
# Eclipse Core
.vscode/
bin/
.classpath
.project
# External tool builders
.externalToolBuilders/
# macOS
# Locally stored "Eclipse launch configurations"
*.launch
DS_Store
# CDT-specific
.cproject
# Misc
# JDT-specific (Eclipse Java Development Tools)
.classpath
# PDT-specific
.buildpath
# sbteclipse plugin
.target
# TeXlipse plugin
.texlipse
### Maven ###
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
### NetBeans ###
nbproject/private/
build/
nbbuild/
dist/
nbdist/
nbactions.xml
nb-configuration.xml
.nb-gradle/
### Git ###
# Don't exclude the .gitignore itself
!/.gitignore
### Run Folder (ViaProxy) ###
common/run/
run/
+9 -4
View File
@@ -1,18 +1,23 @@
# ViaRewind
[![Latest Release](https://img.shields.io/github/v/release/ViaVersion/ViaRewind)](https://github.com/ViaVersion/ViaRewind/releases)
[![Build Status](https://github.com/ViaVersion/ViaRewind/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/ViaVersion/ViaRewind/actions)
[![Discord](https://img.shields.io/badge/chat-on%20discord-blue.svg)](https://viaversion.com/discord)
**ViaBackwards addon to allow 1.8.x and 1.7.x clients on newer server versions.** <br>
**ViaVersion addon to allow 1.8.x and 1.7.x clients on newer server versions.** <br>
Runs on 1.9-latest servers or 1.8 clients and lower.
**You can override the included version in [ViaFabric](https://modrinth.com/mod/viafabric) or [ViaProxy](https://github.com/ViaVersion/ViaProxy):**
**You can override the included version in [ViaFabric](https://modrinth.com/mod/viafabric)
or [ViaProxy](https://github.com/ViaVersion/ViaProxy):**
- in **ViaFabric**, put ViaRewind into the `mods` folder
- in **ViaProxy**, put ViaRewind into the `jars` folder
Note: when using ViaFabric <= 1.16.5 or J8 ViaProxy builds, you need [J8 ViaRewind](https://ci.viaversion.com/view/ViaRewind/job/ViaRewind-Java8/) builds.
Note: when using ViaFabric <= 1.16.5 or J8 ViaProxy builds, you
need [J8 ViaRewind](https://ci.viaversion.com/view/ViaRewind/job/ViaRewind-Java8/) builds.
**Requires [ViaVersion](https://hangar.papermc.io/ViaVersion/ViaVersion) and [ViaBackwards](https://hangar.papermc.io/ViaVersion/ViaBackwards) to be installed..**
**Requires [ViaVersion](https://hangar.papermc.io/ViaVersion/ViaVersion)
and [ViaBackwards](https://hangar.papermc.io/ViaVersion/ViaBackwards) to be installed..**
Releases/Dev Builds
-
+15
View File
@@ -0,0 +1,15 @@
# Security Policy
## Reporting a Vulnerability
If you discover a **security vulnerability**, please **do not** open a public issue. Instead, use one of the following
**confidential contact methods**:
* **Email:** [security@viaversion.com](mailto:security@viaversion.com)
* **Discord:** Join our [Discord server](https://discord.gg/viaversion) and visit
the [#exploit-report](https://discord.com/channels/316206679014244363/1388847764137316485) channel for further
instructions.
If your issue is **not security-related** (e.g., bug reports, feature requests, or usage questions), please use
the [GitHub Issues page](https://github.com/ViaVersion/ViaRewind/issues) or contact us via
the [Discord server](https://discord.gg/viaversion).
-7
View File
@@ -1,7 +0,0 @@
plugins {
id "groovy-gradle-plugin"
}
repositories {
gradlePluginPortal()
}
@@ -1,74 +0,0 @@
plugins {
id "java-library"
id "maven-publish"
id "idea"
}
repositories {
mavenCentral()
maven {
name = "ViaVersion"
url "https://repo.viaversion.com"
}
}
base {
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
compileJava.options.encoding = compileTestJava.options.encoding = javadoc.options.encoding = "UTF-8"
group = rootProject.maven_group
version = rootProject.maven_version
description = rootProject.maven_description
}
dependencies {
compileOnly "com.viaversion:viaversion:5.3.1"
compileOnly "com.viaversion:viabackwards:5.3.1"
}
publishing {
repositories {
maven {
name = "Via"
url = "https://repo.viaversion.com/"
credentials(PasswordCredentials)
authentication {
basic(BasicAuthentication)
}
}
}
publications {
main(MavenPublication) {
artifactId = project.name
groupId = project.maven_group
version = project.maven_version
from components.java
pom {
name = "ViaRewind"
description = project.description
url = "https://github.com/ViaVersion/ViaRewind"
licenses {
license {
name = "GPL-3.0 License"
url = "https://github.com/ViaVersion/ViaRewind/blob/main/LICENSE"
}
}
developers {
developer {
id = "FlorianMichael"
name = "EnZaXD"
email = "florian.michael07@gmail.com"
}
}
scm {
connection = "scm:git:git://github.com/ViaVersion/ViaRewind.git"
developerConnection = "scm:git:ssh://github.com/ViaVersion/ViaRewind.git"
url = "github.com/ViaVersion/ViaRewind"
}
}
}
}
}
@@ -1,7 +0,0 @@
plugins {
id "vr.base-conventions"
}
dependencies {
compileOnly project(":viarewind-common")
}
-142
View File
@@ -1,142 +0,0 @@
plugins {
id "vr.base-conventions"
id "io.papermc.hangar-publish-plugin" version "0.1.3"
id "com.modrinth.minotaur" version "2.+"
}
base {
archivesName = "ViaRewind"
}
configurations {
publishInclude
}
dependencies {
publishInclude project(":viarewind-common")
publishInclude project(":viarewind-bukkit")
publishInclude project(":viarewind-fabric")
publishInclude project(":viarewind-sponge")
publishInclude project(":viarewind-velocity")
}
jar {
dependsOn configurations.publishInclude
from {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
configurations.publishInclude.collect {
zipTree(it)
}
} {
exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA"
}
manifest {
attributes(
"paperweight-mappings-namespace": "mojang"
)
}
from("LICENSE") {
rename { "${it}_${project.name ?: rootProject.name}" }
}
}
idea {
module {
["run"].each {
excludeDirs << file("$it")
}
}
}
// -----------------------------------------------------
// Publishing
Provider<String> latestCommitHash() {
return providers.exec {
commandLine = ["git", "rev-parse", "--short", "HEAD"]
}.standardOutput.getAsText().map(String::trim)
}
Provider<String> latestCommitMessage() {
return providers.exec {
commandLine = ["git", "log", "-1", "--pretty=%B"]
}.standardOutput.getAsText().map(String::trim)
}
Provider<String> branchName() {
return providers.exec {
commandLine = ["git", "rev-parse", "--abbrev-ref", "HEAD"]
}.standardOutput.getAsText().map(String::trim)
}
def branch = branchName().get()
def baseVersion = project.maven_version
def isRelease = !baseVersion.contains('-')
def isMainBranch = branch == "master"
if (!isRelease || isMainBranch) { // Only publish releases from the main branch
def suffixedVersion = isRelease ? baseVersion : baseVersion + "+" + System.getenv("GITHUB_RUN_NUMBER")
def commitHash = latestCommitHash().get()
def changelogContent = "[${commitHash}](https://github.com/ViaVersion/ViaRewind/commit/${commitHash}) ${latestCommitMessage().get()}"
modrinth {
def mcVersions = project.mcVersions
.split(',')
.collect { it.trim() }
token.set(System.getenv("MODRINTH_TOKEN"))
projectId.set("viarewind")
versionType.set(isRelease ? "release" : isMainBranch ? "beta" : "alpha")
versionNumber.set(suffixedVersion)
versionName.set(suffixedVersion)
changelog.set(changelogContent)
uploadFile.set(jar.archiveFile)
gameVersions.set(mcVersions)
loaders.add("fabric")
loaders.add("paper")
loaders.add("folia")
loaders.add("velocity")
autoAddDependsOn.set(false)
detectLoaders.set(false)
dependencies {
required.project("viaversion")
required.project("viabackwards")
optional.project("viafabric")
}
}
hangarPublish {
publications.register("plugin") {
version = suffixedVersion
id = "ViaRewind"
channel = isRelease ? "Release" : isMainBranch ? "Snapshot" : "Alpha"
changelog = changelogContent
apiKey = System.getenv("HANGAR_TOKEN")
platforms {
PAPER {
jar = project.jar.archiveFile
platformVersions = [property('mcVersionRange') as String]
dependencies {
hangar("ViaVersion") {
required = true
}
hangar("ViaBackwards") {
required = true
}
}
}
VELOCITY {
jar = project.jar.archiveFile
platformVersions = [property("velocityVersion") as String]
dependencies {
hangar("ViaVersion") {
required = true
}
hangar("ViaBackwards") {
required = true
}
}
}
}
}
}
}
+155
View File
@@ -0,0 +1,155 @@
import de.florianreuth.baseproject.core.configureEmbeddedDependencies
import de.florianreuth.baseproject.integration.branchName
import de.florianreuth.baseproject.integration.latestCommitHash
import de.florianreuth.baseproject.integration.latestCommitMessage
import de.florianreuth.baseproject.setupProject
import de.florianreuth.baseproject.setupViaPublishing
import net.raphimc.classtokenreplacer.extension.ClassTokenReplacerExtension
plugins {
`java-library`
id("io.papermc.hangar-publish-plugin")
id("com.modrinth.minotaur")
id("de.florianreuth.baseproject")
id("net.raphimc.class-token-replacer") apply false
}
allprojects {
setupProject()
setupViaPublishing()
repositories {
maven("https://repo.viaversion.com")
maven("https://repo.papermc.io/repository/maven-public")
maven("https://maven.fabricmc.net")
}
}
val commitHash = latestCommitHash()
subprojects {
apply(plugin = "net.raphimc.class-token-replacer")
dependencies {
compileOnly("com.viaversion:viaversion:5.12.0")
compileOnly("com.viaversion:viabackwards:5.12.0")
}
extensions.getByType<SourceSetContainer>().configureEach {
extensions.getByType(ClassTokenReplacerExtension::class.java).apply {
property("\${version}", project.version)
property("\${impl_version}", "git-ViaRewind-${project.version}:${commitHash}")
}
}
tasks {
processResources {
val projectVersion = project.version
val projectDescription = project.description
filesMatching(listOf("plugin.yml", "fabric.mod.json", "META-INF/sponge_plugins.json")) {
expand(mapOf("version" to projectVersion, "description" to projectDescription))
}
}
}
}
val shade = configureEmbeddedDependencies()
dependencies {
subprojects.forEach {
shade(it)
}
}
tasks {
jar {
manifest {
attributes("paperweight-mappings-namespace" to "mojang")
}
}
}
val branch = branchName()
val baseVersion = version as String
val isRelease = !baseVersion.contains('-')
val isMainBranch = branch == "master"
if (!isRelease || isMainBranch) { // Only publish releases from the main branch
val suffixedVersion = if (isRelease) baseVersion else baseVersion + "+" + System.getenv("GITHUB_RUN_NUMBER")
val changelogContent = if (isRelease) {
"See [GitHub](https://github.com/ViaVersion/ViaRewind) for release notes."
} else {
val commitHash = latestCommitHash()
"[$commitHash](https://github.com/ViaVersion/ViaRewind/commit/$commitHash) ${latestCommitMessage()}"
}
modrinth {
val mcVersions: List<String> = (property("minecraft_versions") as String)
.split(",")
.map { it.trim() }
token.set(System.getenv("MODRINTH_TOKEN"))
projectId.set("viarewind")
versionType.set(if (isRelease) "release" else if (isMainBranch) "beta" else "alpha")
versionNumber.set(suffixedVersion)
versionName.set(suffixedVersion)
changelog.set(changelogContent)
uploadFile.set(tasks.jar.flatMap { it.archiveFile })
gameVersions.set(mcVersions)
loaders.add("fabric")
loaders.add("paper")
loaders.add("folia")
loaders.add("velocity")
autoAddDependsOn.set(false)
detectLoaders.set(false)
dependencies {
required.project("viaversion")
required.project("viabackwards")
optional.project("viafabric")
}
}
tasks.modrinth {
notCompatibleWithConfigurationCache("")
}
hangarPublish {
publications.register("plugin") {
version.set(suffixedVersion)
id.set("ViaRewind")
channel.set(if (isRelease) "Release" else if (isMainBranch) "Snapshot" else "Alpha")
changelog.set(changelogContent)
apiKey.set(System.getenv("HANGAR_TOKEN"))
platforms {
paper {
jar.set(tasks.jar.flatMap { it.archiveFile })
platformVersions.set(listOf(property("minecraft_version_range") as String))
dependencies {
hangar("ViaVersion") {
required = true
}
hangar("ViaBackwards") {
required = true
}
}
}
velocity {
jar.set(tasks.jar.flatMap { it.archiveFile })
platformVersions.set(listOf(property("velocity_version") as String))
dependencies {
hangar("ViaVersion") {
required = true
}
hangar("ViaBackwards") {
required = true
}
}
}
}
}
}
tasks.named("publishPluginPublicationToHangar") {
notCompatibleWithConfigurationCache("")
}
}
-24
View File
@@ -1,24 +0,0 @@
plugins {
id "vr.platform-conventions"
}
repositories {
maven {
name = "SpigotMC"
url "https://hub.spigotmc.org/nexus/content/repositories/snapshots/"
}
maven {
name = "SpongePowered"
url "https://repo.spongepowered.org/repository/maven-public/"
}
}
dependencies {
compileOnly(annotationProcessor("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT"))
}
processResources {
filesMatching("plugin.yml") {
expand("version": project.version, "description": project.description)
}
}
+4
View File
@@ -0,0 +1,4 @@
dependencies {
compileOnly(project(":viarewind-common"))
compileOnly("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT")
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,14 +18,22 @@
package com.viaversion.viarewind;
import com.viaversion.viarewind.api.ViaRewindPlatform;
import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider;
import com.viaversion.viarewind.provider.BukkitInventoryProvider;
import com.viaversion.viaversion.api.Via;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import org.bukkit.plugin.java.JavaPlugin;
public class BukkitPlugin extends JavaPlugin implements ViaRewindPlatform {
public BukkitPlugin() {
Via.getManager().addEnableListener(() -> this.init(new File(getDataFolder(), "config.yml")));
}
public BukkitPlugin() {
Via.getManager().addEnableListener(() -> this.init(new File(getDataFolder(), "config.yml")));
}
@Override
public void onEnable() {
final ViaProviders providers = Via.getManager().getProviders();
providers.use(InventoryProvider.class, new BukkitInventoryProvider());
}
}
@@ -0,0 +1,36 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.provider;
import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider;
import com.viaversion.viaversion.api.connection.UserConnection;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
public final class BukkitInventoryProvider extends InventoryProvider {
@Override
public boolean hasElytra(final UserConnection connection) {
final Player player = Bukkit.getPlayer(connection.getProtocolInfo().getUuid());
final ItemStack chestplate = player.getInventory().getChestplate();
return chestplate != null && chestplate.getType() == Material.ELYTRA;
}
}
+2 -2
View File
@@ -1,10 +1,10 @@
name: ViaRewind
main: com.viaversion.viarewind.BukkitPlugin
authors: [FlorianMichael/EnZaXD, Gerrygames, creeper123123321]
authors: [ EnZaXD, Gerrygames, creeper123123321 ]
version: ${version}
description: ${description}
website: https://viaversion.com/rewind
api-version: 1.13
folia-supported: true
load: STARTUP
depend: [ViaVersion, ViaBackwards]
depend: [ ViaVersion, ViaBackwards ]
-49
View File
@@ -1,49 +0,0 @@
plugins {
id "vr.base-conventions"
id "net.raphimc.class-token-replacer" version "1.0.0"
}
dependencies {
compileOnly "io.netty:netty-all:4.0.20.Final"
compileOnly "com.google.guava:guava:17.0"
}
sourceSets {
main {
classTokenReplacer {
property("\${version}", rootProject.maven_version)
property("\${impl_version}", "git-ViaRewind-${rootProject.maven_version}:${rootProject.latestCommitHash().get()}")
}
}
}
// Task to quickly test/debug code changes using https://github.com/ViaVersion/ViaProxy
// For further instructions see the ViaProxy repository README
tasks.register("runViaProxy", JavaExec) {
dependsOn tasks.jar
def viaProxyConfiguration = configurations.create("viaProxy")
viaProxyConfiguration.dependencies.add(dependencies.create("net.raphimc:ViaProxy:3.4.0-SNAPSHOT") {
transitive = false
})
mainClass = "net.raphimc.viaproxy.ViaProxy"
classpath = viaProxyConfiguration
workingDir = file("run")
jvmArgs = ["-DskipUpdateCheck"]
if (System.getProperty("viaproxy.gui.autoStart") != null) {
jvmArgs("-Dviaproxy.gui.autoStart")
}
doFirst {
def jarsDir = file("$workingDir/jars")
jarsDir.mkdirs()
file("$jarsDir/${project.name}.jar").bytes = tasks.jar.archiveFile.get().asFile.bytes
}
doLast {
file("$workingDir/jars/${project.name}.jar").delete()
file("$workingDir/logs").deleteDir()
}
}
+44
View File
@@ -0,0 +1,44 @@
val viaProxy: Configuration by configurations.creating
dependencies {
compileOnly("io.netty:netty-all:4.0.20.Final")
compileOnly("com.google.guava:guava:17.0")
compileOnly("com.velocitypowered:velocity-native:3.4.0-SNAPSHOT")
viaProxy("net.raphimc:ViaProxy:[3.0.0,4.0.0)") {
isTransitive = false
}
}
// Task to quickly test/debug code changes using https://github.com/ViaVersion/ViaProxy
// For further instructions see the ViaProxy repository README
val prepareViaProxyFiles by tasks.registering(Copy::class) {
dependsOn(project.tasks.jar)
from(project.tasks.jar.get().archiveFile.get().asFile)
into(layout.projectDirectory.dir("run/jars"))
val projectName = project.name
rename { "${projectName}.jar" }
}
val cleanupViaProxyFiles by tasks.registering(Delete::class) {
delete(
layout.projectDirectory.file("run/jars/${project.name}.jar"),
layout.projectDirectory.dir("run/logs")
)
}
tasks.register<JavaExec>("runViaProxy") {
dependsOn(prepareViaProxyFiles)
finalizedBy(cleanupViaProxyFiles)
mainClass.set("net.raphimc.viaproxy.ViaProxy")
classpath = viaProxy
workingDir = layout.projectDirectory.dir("run").asFile
jvmArgs = listOf("-DskipUpdateCheck")
if (System.getProperty("viaproxy.gui.autoStart") != null) {
jvmArgs("-Dviaproxy.gui.autoStart")
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,21 +23,21 @@ import com.viaversion.viarewind.api.ViaRewindPlatform;
public class ViaRewind {
private static ViaRewindPlatform platform;
private static ViaRewindConfig config;
private static ViaRewindPlatform platform;
private static ViaRewindConfig config;
public static void init(ViaRewindPlatform platform, ViaRewindConfig config) {
Preconditions.checkArgument(ViaRewind.platform == null, "ViaRewind is already initialized");
public static void init(ViaRewindPlatform platform, ViaRewindConfig config) {
Preconditions.checkArgument(ViaRewind.platform == null, "ViaRewind is already initialized");
ViaRewind.platform = platform;
ViaRewind.config = config;
}
ViaRewind.platform = platform;
ViaRewind.config = config;
}
public static ViaRewindPlatform getPlatform() {
return ViaRewind.platform;
}
public static ViaRewindPlatform getPlatform() {
return ViaRewind.platform;
}
public static ViaRewindConfig getConfig() {
return ViaRewind.config;
}
public static ViaRewindConfig getConfig() {
return ViaRewind.config;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
package com.viaversion.viarewind;
import com.viaversion.viaversion.util.Config;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
@@ -29,105 +28,105 @@ import java.util.logging.Logger;
public class ViaRewindConfig extends Config implements com.viaversion.viarewind.api.ViaRewindConfig {
private CooldownIndicator cooldownIndicator;
private boolean replaceAdventureMode;
private boolean replaceParticles;
private int maxBookPages;
private int maxBookPageSize;
private boolean emulateWorldBorder;
private boolean alwaysShowOriginalMobName;
private String worldBorderParticle;
private boolean enableOffhand;
private String offhandCommand;
private boolean emulateLevitationEffect;
private boolean handlePlayerCombatPacket;
private CooldownIndicator cooldownIndicator;
private boolean replaceAdventureMode;
private boolean replaceParticles;
private int maxBookPages;
private int maxBookPageSize;
private boolean emulateWorldBorder;
private boolean alwaysShowOriginalMobName;
private String worldBorderParticle;
private boolean enableOffhand;
private String offhandCommand;
private boolean emulateLevitationEffect;
private boolean handlePlayerCombatPacket;
public ViaRewindConfig(File configFile, Logger logger) {
super(configFile, logger);
}
@Override
public void reload() {
super.reload();
loadFields();
}
@Override
public void reload() {
super.reload();
loadFields();
}
private void loadFields() {
cooldownIndicator = CooldownIndicator.valueOf(getString("cooldown-indicator", "TITLE").toUpperCase());
replaceAdventureMode = getBoolean("replace-adventure", false);
replaceParticles = getBoolean("replace-particles", false);
maxBookPages = getInt("max-book-pages", 100);
maxBookPageSize = getInt("max-book-page-length", 5000);
emulateWorldBorder = getBoolean("emulate-world-border", true);
alwaysShowOriginalMobName = getBoolean("always-show-original-mob-name", true);
worldBorderParticle = getString("world-border-particle", "fireworksSpark");
enableOffhand = getBoolean("enable-offhand", true);
offhandCommand = getString("offhand-command", "/offhand");
emulateLevitationEffect = getBoolean("emulate-levitation-effect", true);
handlePlayerCombatPacket = getBoolean("handle-player-combat-packet", true);
}
private void loadFields() {
cooldownIndicator = CooldownIndicator.valueOf(getString("cooldown-indicator", "TITLE").toUpperCase());
replaceAdventureMode = getBoolean("replace-adventure", false);
replaceParticles = getBoolean("replace-particles", false);
maxBookPages = getInt("max-book-pages", 100);
maxBookPageSize = getInt("max-book-page-length", 5000);
emulateWorldBorder = getBoolean("emulate-world-border", true);
alwaysShowOriginalMobName = getBoolean("always-show-original-mob-name", true);
worldBorderParticle = getString("world-border-particle", "fireworksSpark");
enableOffhand = getBoolean("enable-offhand", true);
offhandCommand = getString("offhand-command", "/offhand");
emulateLevitationEffect = getBoolean("emulate-levitation-effect", true);
handlePlayerCombatPacket = getBoolean("handle-player-combat-packet", true);
}
@Override
@Override
public CooldownIndicator getCooldownIndicator() {
return cooldownIndicator;
return cooldownIndicator;
}
@Override
public boolean isReplaceAdventureMode() {
return replaceAdventureMode;
return replaceAdventureMode;
}
@Override
public boolean isReplaceParticles() {
return replaceParticles;
return replaceParticles;
}
@Override
public int getMaxBookPages() {
return maxBookPages;
return maxBookPages;
}
@Override
public int getMaxBookPageSize() {
return maxBookPageSize;
return maxBookPageSize;
}
@Override
public boolean isEmulateWorldBorder() {
return emulateWorldBorder;
}
@Override
public boolean isEmulateWorldBorder() {
return emulateWorldBorder;
}
@Override
public boolean alwaysShowOriginalMobName() {
return alwaysShowOriginalMobName;
}
@Override
public boolean alwaysShowOriginalMobName() {
return alwaysShowOriginalMobName;
}
@Override
public String getWorldBorderParticle() {
return worldBorderParticle;
}
@Override
public String getWorldBorderParticle() {
return worldBorderParticle;
}
@Override
public boolean isEnableOffhand() {
return enableOffhand;
}
@Override
public boolean isEnableOffhand() {
return enableOffhand;
}
@Override
public String getOffhandCommand() {
return offhandCommand;
}
@Override
public String getOffhandCommand() {
return offhandCommand;
}
@Override
public boolean emulateLevitationEffect() {
return emulateLevitationEffect;
}
@Override
public boolean emulateLevitationEffect() {
return emulateLevitationEffect;
}
@Override
public boolean handlePlayerCombatPacket() {
return handlePlayerCombatPacket;
}
@Override
public boolean handlePlayerCombatPacket() {
return handlePlayerCombatPacket;
}
@Override
@Override
public URL getDefaultConfigURL() {
return getClass().getClassLoader().getResource("assets/viarewind/config.yml");
}
@@ -0,0 +1,43 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind;
import com.viaversion.viarewind.api.ViaRewindPlatform;
import com.viaversion.viaversion.api.Via;
import java.io.File;
import java.util.logging.Logger;
public class ViaRewindPlatformImpl implements ViaRewindPlatform {
private final Logger logger;
public ViaRewindPlatformImpl() {
logger = Via.getPlatform().createLogger("ViaRewind");
init(new File(getDataFolder(), "viarewind.yml"));
}
@Override
public Logger getLogger() {
return logger;
}
@Override
public File getDataFolder() {
return Via.getPlatform().getDataFolder();
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,97 +24,97 @@ import com.viaversion.viaversion.api.configuration.Config;
*/
public interface ViaRewindConfig extends Config {
enum CooldownIndicator {
TITLE, ACTION_BAR, BOSS_BAR, DISABLED
}
/**
* Specifies how 1.8.x clients should see the cooldown indicator
* You can choose between TITLE, ACTION_BAR, BOSS_BAR and DISABLED
* ONLY DISABLE IF YOU HAVE 1.9 COOLDOWN DISABLED ON YOUR SERVER
* 1.8 PLAYERS MAY ASK WHY PVP IS NOT WORKING OTHERWISE
*
* @return the cooldown indicator
*/
CooldownIndicator getCooldownIndicator();
/**
* Specifies how 1.8.x clients should see the cooldown indicator
* You can choose between TITLE, ACTION_BAR, BOSS_BAR and DISABLED
* ONLY DISABLE IF YOU HAVE 1.9 COOLDOWN DISABLED ON YOUR SERVER
* 1.8 PLAYERS MAY ASK WHY PVP IS NOT WORKING OTHERWISE
*
* @return the cooldown indicator
*/
CooldownIndicator getCooldownIndicator();
/**
* Replaces Adventure mode with Survival mode for 1.7.x clients
* Enable this option if your server is using the 'CanDestroy'
* or 'CanPlaceOn' flags on items
*
* @return true if enabled
*/
boolean isReplaceAdventureMode();
/**
* Replaces Adventure mode with Survival mode for 1.7.x clients
* Enable this option if your server is using the 'CanDestroy'
* or 'CanPlaceOn' flags on items
*
* @return true if enabled
*/
boolean isReplaceAdventureMode();
/**
* Whether 1.9 particles should be replaced by similar ones in
* 1.8 and lower
*
* @return true if enabled
*/
boolean isReplaceParticles();
/**
* Whether 1.9 particles should be replaced by similar ones in
* 1.8 and lower
*
* @return true if enabled
*/
boolean isReplaceParticles();
/**
* Max amount of pages for written books before a client gets kicked
*
* @return the max amount of pages
*/
int getMaxBookPages();
/**
* Max amount of pages for written books before a client gets kicked
*
* @return the max amount of pages
*/
int getMaxBookPages();
/**
* Max amount of characters in the json (!) string of a book page before a client gets kicked
*
* @return the max amount of characters
*/
int getMaxBookPageSize();
/**
* Max amount of characters in the json (!) string of a book page before a client gets kicked
*
* @return the max amount of characters
*/
int getMaxBookPageSize();
/**
* Whether to emulate the 1.8+ world border for 1.7.x clients
*
* @return true if enabled
*/
boolean isEmulateWorldBorder();
/**
* Whether to emulate the 1.8+ world border for 1.7.x clients
*
* @return true if enabled
*/
boolean isEmulateWorldBorder();
/**
* Always shows the original mob's name instead of only when hovering over them with the cursor.
*
* @return true if enabled
*/
boolean alwaysShowOriginalMobName();
/**
* Always shows the original mob's name instead of only when hovering over them with the cursor.
*
* @return true if enabled
*/
boolean alwaysShowOriginalMobName();
/**
* The particle to show the world border for the 1.8+ world border for 1.7.x clients
*
* @return the particle name
*/
String getWorldBorderParticle();
/**
* The particle to show the world border for the 1.8+ world border for 1.7.x clients
*
* @return the particle name
*/
String getWorldBorderParticle();
/**
* If enabled, 1.8 players on 1.9+ servers can use /offhand to switch items between their main hand and offhand.
*
* @return true if enabled
*/
boolean isEnableOffhand();
/**
* If enabled, 1.8 players on 1.9+ servers can use /offhand to switch items between their main hand and offhand.
*
* @return true if enabled
*/
boolean isEnableOffhand();
/**
* Allows to define the offhand command
*
* @return the offhand command
*/
String getOffhandCommand();
/**
* Allows to define the offhand command
*
* @return the offhand command
*/
String getOffhandCommand();
/**
* If enabled, 1.8 players on 1.9+ servers will also experience the levitation effect by sending velocity packets.
*
* @return true if enabled
*/
boolean emulateLevitationEffect();
/**
* If enabled, 1.8 players on 1.9+ servers will also experience the levitation effect by sending velocity packets.
*
* @return true if enabled
*/
boolean emulateLevitationEffect();
/**
* If enabled, 1.8 players will handle the player combat packet added in 1.9 by showing the custom death message above the hotbar.
*
* @return true if enabled
*/
boolean handlePlayerCombatPacket();
/**
* If enabled, 1.8 players will handle the player combat packet added in 1.9 by showing the custom death message above the hotbar.
*
* @return true if enabled
*/
boolean handlePlayerCombatPacket();
enum CooldownIndicator {
TITLE, ACTION_BAR, BOSS_BAR, DISABLED
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,7 +25,6 @@ import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.protocol.ProtocolManager;
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
import java.io.File;
import java.util.logging.Logger;
@@ -34,59 +33,62 @@ import java.util.logging.Logger;
*/
public interface ViaRewindPlatform {
String VERSION = "${version}";
String IMPL_VERSION = "${impl_version}";
String VERSION = "${version}";
String IMPL_VERSION = "${impl_version}";
/**
* Initialize ViaRewind
*/
default void init(final File configFile) {
ViaRewindConfig config = new ViaRewindConfig(configFile, getLogger());
config.reload();
Via.getManager().getConfigurationProvider().register(config);
default void init(final File configFile) {
init(new ViaRewindConfig(configFile, getLogger()));
}
ViaRewind.init(this, config);
/**
* Initialize ViaRewind
*/
default void init(final com.viaversion.viarewind.api.ViaRewindConfig config) {
config.reload();
Via.getManager().getConfigurationProvider().register(config);
Via.getManager().getSubPlatforms().add(IMPL_VERSION);
ViaRewind.init(this, config);
getLogger().info("Registering protocols...");
final ProtocolManager protocolManager = Via.getManager().getProtocolManager();
protocolManager.registerProtocol(new Protocol1_7_6_10To1_7_2_5(), ProtocolVersion.v1_7_2, ProtocolVersion.v1_7_6);
protocolManager.registerProtocol(new Protocol1_8To1_7_6_10(), ProtocolVersion.v1_7_6, ProtocolVersion.v1_8);
Via.getManager().getSubPlatforms().add(IMPL_VERSION);
protocolManager.registerProtocol(new Protocol1_9To1_8(), ProtocolVersion.v1_8, ProtocolVersion.v1_9);
}
getLogger().info("Registering protocols...");
final ProtocolManager protocolManager = Via.getManager().getProtocolManager();
protocolManager.registerProtocol(new Protocol1_7_6_10To1_7_2_5(), ProtocolVersion.v1_7_2, ProtocolVersion.v1_7_6);
protocolManager.registerProtocol(new Protocol1_8To1_7_6_10(), ProtocolVersion.v1_7_6, ProtocolVersion.v1_8);
/**
* Get the platform logger
*
* @return the logger
*/
Logger getLogger();
protocolManager.registerProtocol(new Protocol1_9To1_8(), ProtocolVersion.v1_8, ProtocolVersion.v1_9);
}
/**
* Returns ViaRewind's data folder.
*
* @return data folder
*/
File getDataFolder();
/**
* Get the platform logger
*
* @return the logger
*/
Logger getLogger();
/**
* Returns the platform's compression handler name. Only used in the 1.8 -> 1.7 protocol.
*
* @return the compression handler name
*/
default String compressHandlerName() {
return "compress";
}
/**
* Returns ViaRewind's data folder.
*
* @return data folder
*/
File getDataFolder();
/**
* Returns the platform's decompression handler name. Only used in the 1.8 -> 1.7 protocol.
*
* @return the decompression handler name
*/
default String decompressHandlerName() {
return "decompress";
}
/**
* Returns the platform's compression handler name. Only used in the 1.8 -> 1.7 protocol.
*
* @return the compression handler name
*/
default String compressHandlerName() {
return "compress";
}
/**
* Returns the platform's decompression handler name. Only used in the 1.8 -> 1.7 protocol.
*
* @return the decompression handler name
*/
default String decompressHandlerName() {
return "decompress";
}
}
@@ -0,0 +1,157 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.api.compression;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import java.util.zip.DataFormatException;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
/**
* A ThreadLocal-based compression provider that either uses Velocity's native compression
* or falls back to Java's built-in compression (Deflater/Inflater).
*/
public final class ThreadLocalCompressionProvider {
private static final boolean VELOCITY_NATIVES_AVAILABLE;
static {
boolean velocityAvailable = false;
try {
Class.forName("com.velocitypowered.natives.compression.VelocityCompressor");
velocityAvailable = true;
} catch (final ClassNotFoundException ignored) {
}
VELOCITY_NATIVES_AVAILABLE = velocityAvailable;
}
// ThreadLocal for Java's Deflater (used when Velocity natives are not available)
private static final ThreadLocal<Deflater> JAVA_DEFLATER = ThreadLocal.withInitial(Deflater::new);
// ThreadLocal for Java's Inflater (used when Velocity natives are not available)
private static final ThreadLocal<Inflater> JAVA_INFLATER = ThreadLocal.withInitial(Inflater::new);
private ThreadLocalCompressionProvider() {
}
/**
* Compresses data from the source buffer into the destination buffer.
*
* @param source the source buffer containing uncompressed data
* @param destination the destination buffer to write compressed data to
* @throws DataFormatException if compression fails
*/
public static void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
if (VELOCITY_NATIVES_AVAILABLE) {
deflateVelocity(source, destination);
} else {
deflateJava(source, destination);
}
}
/**
* Decompresses data from the source buffer into the destination buffer.
*
* @param source the source buffer containing compressed data
* @param destination the destination buffer to write decompressed data to
* @param expectedSize the expected size of the decompressed data
* @throws DataFormatException if decompression fails
*/
public static void inflate(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
if (VELOCITY_NATIVES_AVAILABLE) {
inflateVelocity(source, destination, expectedSize);
} else {
inflateJava(source, destination, expectedSize);
}
}
// Java native implementation
private static void deflateJava(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
ByteBuf temp = source;
if (!source.hasArray()) {
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
} else {
source.retain();
}
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer();
try {
final Deflater deflater = JAVA_DEFLATER.get();
deflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
deflater.finish();
while (!deflater.finished()) {
output.ensureWritable(4096);
output.writerIndex(output.writerIndex() + deflater.deflate(output.array(), output.arrayOffset() + output.writerIndex(), output.writableBytes()));
}
destination.writeBytes(output);
} finally {
output.release();
temp.release();
JAVA_DEFLATER.get().reset();
}
}
private static void inflateJava(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
ByteBuf temp = source;
if (!source.hasArray()) {
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(source);
} else {
source.retain();
}
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(expectedSize, expectedSize);
try {
final Inflater inflater = JAVA_INFLATER.get();
inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
output.writerIndex(output.writerIndex() + inflater.inflate(output.array(), output.arrayOffset(), expectedSize));
destination.writeBytes(output);
} finally {
output.release();
temp.release();
JAVA_INFLATER.get().reset();
}
}
// Velocity native implementation
private static void deflateVelocity(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
VelocityHolder.deflate(source, destination);
}
private static void inflateVelocity(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
VelocityHolder.inflate(source, destination, expectedSize);
}
private static final class VelocityHolder {
private static final ThreadLocal<com.velocitypowered.natives.compression.VelocityCompressor> COMPRESSOR =
ThreadLocal.withInitial(() -> com.velocitypowered.natives.util.Natives.compress.get().create(-1));
static void deflate(final ByteBuf source, final ByteBuf destination) throws DataFormatException {
COMPRESSOR.get().deflate(source, destination);
}
static void inflate(final ByteBuf source, final ByteBuf destination, final int expectedSize) throws DataFormatException {
COMPRESSOR.get().inflate(source, destination, expectedSize);
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,25 +17,33 @@
*/
package com.viaversion.viarewind.api.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viabackwards.api.data.BackwardsMappingData;
import com.viaversion.viarewind.ViaRewind;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viaversion.api.data.FullMappings;
import com.viaversion.viaversion.api.data.LegacyFullStringMappingsBase;
import com.viaversion.viaversion.api.data.MappingDataLoader;
import com.viaversion.viaversion.api.data.Mappings;
import java.util.logging.Logger;
public class RewindMappingData extends BackwardsMappingData {
public RewindMappingData(String unmappedVersion, String mappedVersion) {
super(unmappedVersion, mappedVersion);
}
public RewindMappingData(String unmappedVersion, String mappedVersion) {
super(unmappedVersion, mappedVersion);
}
@Override
protected Logger getLogger() {
return ViaRewind.getPlatform().getLogger();
}
@Override
protected Logger getLogger() {
return ViaRewind.getPlatform().getLogger();
}
@Override
protected CompoundTag readMappingsFile(String name) {
return RewindMappingDataLoader.INSTANCE.loadNBTFromDir(name);
}
@Override
protected CompoundTag readMappingsFile(String name) {
return RewindMappingDataLoader.INSTANCE.loadNBTFromDir(name);
}
@Override
protected FullMappings createFullMappings(MappingDataLoader.IdentifiersPair identifiersPair, Mappings mappings) {
return new LegacyFullStringMappingsBase(identifiersPair, mappings);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,26 +19,25 @@ package com.viaversion.viarewind.api.data;
import com.viaversion.viabackwards.api.data.BackwardsMappingDataLoader;
import com.viaversion.viarewind.ViaRewind;
import java.io.File;
import java.util.logging.Logger;
public class RewindMappingDataLoader extends BackwardsMappingDataLoader {
public static final RewindMappingDataLoader INSTANCE = new RewindMappingDataLoader();
public static final RewindMappingDataLoader INSTANCE = new RewindMappingDataLoader();
public RewindMappingDataLoader() {
super(RewindMappingDataLoader.class, "assets/viarewind/data/");
}
public RewindMappingDataLoader() {
super(RewindMappingDataLoader.class, "assets/viarewind/data/");
}
@Override
public Logger getLogger() {
return ViaRewind.getPlatform().getLogger();
}
@Override
public Logger getLogger() {
return ViaRewind.getPlatform().getLogger();
}
@Override
public File getDataFolder() {
return ViaRewind.getPlatform().getDataFolder();
}
@Override
public File getDataFolder() {
return ViaRewind.getPlatform().getDataFolder();
}
}
@@ -1,77 +0,0 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.api.minecraft;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.NibbleArray;
public class ExtendedBlockStorage {
private final byte[] blockLSBArray = new byte[4096];
private final NibbleArray blockMetadataArray = new NibbleArray(this.blockLSBArray.length);
private final NibbleArray blockLightArray = new NibbleArray(this.blockLSBArray.length);
private NibbleArray blockMSBArray;
private NibbleArray skyLightArray;
public ExtendedBlockStorage(final boolean skylight) {
if (skylight) {
this.skyLightArray = new NibbleArray(this.blockLSBArray.length);
}
}
public void setBlockId(final int x, final int y, final int z, final int value) {
this.blockLSBArray[ChunkSection.index(x, y, z)] = (byte) (value & 255);
if (value > 255) {
this.getOrCreateBlockMSBArray().set(x, y, z, (value & 0xF00) >> 8);
} else if (this.blockMSBArray != null) {
this.blockMSBArray.set(x, y, z, 0);
}
}
public void setBlockMetadata(final int x, final int y, final int z, final int value) {
this.blockMetadataArray.set(x, y, z, value);
}
public boolean hasBlockMSBArray() {
return this.blockMSBArray != null;
}
public byte[] getBlockLSBArray() {
return this.blockLSBArray;
}
public NibbleArray getOrCreateBlockMSBArray() {
if (this.blockMSBArray == null) {
return this.blockMSBArray = new NibbleArray(this.blockLSBArray.length);
}
return this.blockMSBArray;
}
public NibbleArray getBlockMetadataArray() {
return this.blockMetadataArray;
}
public NibbleArray getBlockLightArray() {
return this.blockLightArray;
}
public NibbleArray getSkyLightArray() {
return this.skyLightArray;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,33 +23,33 @@ import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
public enum EntityDataTypes1_7_6_10 implements EntityDataType {
BYTE(0, Types.BYTE),
SHORT(1, Types.SHORT),
INT(2, Types.INT),
FLOAT(3, Types.FLOAT),
STRING(4, Types.STRING),
ITEM(5, RewindTypes.COMPRESSED_NBT_ITEM),
POSITION(6, Types.VECTOR);
BYTE(0, Types.BYTE),
SHORT(1, Types.SHORT),
INT(2, Types.INT),
FLOAT(3, Types.FLOAT),
STRING(4, Types.STRING),
ITEM(5, RewindTypes.COMPRESSED_NBT_ITEM),
POSITION(6, Types.VECTOR);
private final int typeID;
private final Type<?> type;
private final int typeID;
private final Type<?> type;
EntityDataTypes1_7_6_10(int typeID, Type<?> type) {
this.typeID = typeID;
this.type = type;
}
EntityDataTypes1_7_6_10(int typeID, Type<?> type) {
this.typeID = typeID;
this.type = type;
}
public static EntityDataTypes1_7_6_10 byId(int id) {
return values()[id];
}
public static EntityDataTypes1_7_6_10 byId(int id) {
return values()[id];
}
@Override
public int typeId() {
return this.typeID;
}
@Override
public int typeId() {
return this.typeID;
}
@Override
public Type<?> type() {
return this.type;
}
@Override
public Type<?> type() {
return this.type;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,19 +18,19 @@
package com.viaversion.viarewind.api.minecraft.math;
public class AABB {
Vector3d min;
Vector3d max;
Vector3d min;
Vector3d max;
public AABB(Vector3d min, Vector3d max) {
this.min = min;
this.max = max;
}
public AABB(Vector3d min, Vector3d max) {
this.min = min;
this.max = max;
}
public Vector3d getMin() {
return this.min;
}
public Vector3d getMin() {
return this.min;
}
public Vector3d getMax() {
return this.max;
}
public Vector3d getMax() {
return this.max;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,19 +18,19 @@
package com.viaversion.viarewind.api.minecraft.math;
public class Ray3d {
Vector3d start;
Vector3d dir;
Vector3d start;
Vector3d dir;
public Ray3d(Vector3d start, Vector3d dir) {
this.start = start;
this.dir = dir;
}
public Ray3d(Vector3d start, Vector3d dir) {
this.start = start;
this.dir = dir;
}
public Vector3d getStart() {
return this.start;
}
public Vector3d getStart() {
return this.start;
}
public Vector3d getDir() {
return this.dir;
}
public Vector3d getDir() {
return this.dir;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,40 +19,40 @@ package com.viaversion.viarewind.api.minecraft.math;
public class RayTracing {
public static Vector3d trace(Ray3d ray, AABB aabb, double distance) {
Vector3d invDir = new Vector3d(1f / ray.dir.x, 1f / ray.dir.y, 1f / ray.dir.z);
public static Vector3d trace(Ray3d ray, AABB aabb, double distance) {
Vector3d invDir = new Vector3d(1f / ray.dir.x, 1f / ray.dir.y, 1f / ray.dir.z);
boolean signDirX = invDir.x < 0;
boolean signDirY = invDir.y < 0;
boolean signDirZ = invDir.z < 0;
boolean signDirX = invDir.x < 0;
boolean signDirY = invDir.y < 0;
boolean signDirZ = invDir.z < 0;
Vector3d bbox = signDirX ? aabb.max : aabb.min;
double tmin = (bbox.x - ray.start.x) * invDir.x;
bbox = signDirX ? aabb.min : aabb.max;
double tmax = (bbox.x - ray.start.x) * invDir.x;
bbox = signDirY ? aabb.max : aabb.min;
double tymin = (bbox.y - ray.start.y) * invDir.y;
bbox = signDirY ? aabb.min : aabb.max;
double tymax = (bbox.y - ray.start.y) * invDir.y;
Vector3d bbox = signDirX ? aabb.max : aabb.min;
double tmin = (bbox.x - ray.start.x) * invDir.x;
bbox = signDirX ? aabb.min : aabb.max;
double tmax = (bbox.x - ray.start.x) * invDir.x;
bbox = signDirY ? aabb.max : aabb.min;
double tymin = (bbox.y - ray.start.y) * invDir.y;
bbox = signDirY ? aabb.min : aabb.max;
double tymax = (bbox.y - ray.start.y) * invDir.y;
if (tmin > tymax || tymin > tmax) return null;
if (tmin > tymax || tymin > tmax) return null;
if (tymin > tmin) tmin = tymin;
if (tymin > tmin) tmin = tymin;
if (tymax < tmax) tmax = tymax;
if (tymax < tmax) tmax = tymax;
bbox = signDirZ ? aabb.max : aabb.min;
double tzmin = (bbox.z - ray.start.z) * invDir.z;
bbox = signDirZ ? aabb.min : aabb.max;
double tzmax = (bbox.z - ray.start.z) * invDir.z;
bbox = signDirZ ? aabb.max : aabb.min;
double tzmin = (bbox.z - ray.start.z) * invDir.z;
bbox = signDirZ ? aabb.min : aabb.max;
double tzmax = (bbox.z - ray.start.z) * invDir.z;
if (tmin > tzmax || tzmin > tmax) return null;
if (tmin > tzmax || tzmin > tmax) return null;
if (tzmin > tmin) tmin = tzmin;
if (tzmin > tmin) tmin = tzmin;
if (tzmax < tmax) tmax = tzmax;
if (tzmax < tmax) tmax = tzmax;
return tmin <= distance && tmax > 0 ? ray.start.clone().add(ray.dir.clone().normalize().multiply(tmin)) : null;
}
return tmin <= distance && tmax > 0 ? ray.start.clone().add(ray.dir.clone().normalize().multiply(tmin)) : null;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,87 +17,83 @@
*/
package com.viaversion.viarewind.api.minecraft.math;
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.EntityTracker1_9;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.Vector;
@Deprecated // ?????????
public class RelativeMoveUtil {
public static Vector[] calculateRelativeMoves(UserConnection user, int entityId, int relX, int relY, int relZ) {
EntityTracker1_9 tracker = user.getEntityTracker(Protocol1_9To1_8.class);
public static Vector[] calculateRelativeMoves(EntityTracker1_9 tracker, int entityId, int relX, int relY, int relZ) {
int x;
int y;
int z;
Vector offset = tracker.getEntityOffset(entityId);
if (offset != null) {
relX += offset.blockX();
relY += offset.blockY();
relZ += offset.blockZ();
}
int x;
int y;
int z;
Vector offset = tracker.getEntityOffset(entityId);
if (offset != null) {
relX += offset.blockX();
relY += offset.blockY();
relZ += offset.blockZ();
}
if (relX > Short.MAX_VALUE) {
x = relX - Short.MAX_VALUE;
relX = Short.MAX_VALUE;
} else if (relX < Short.MIN_VALUE) {
x = relX - Short.MIN_VALUE;
relX = Short.MIN_VALUE;
} else {
x = 0;
}
if (relX > Short.MAX_VALUE) {
x = relX - Short.MAX_VALUE;
relX = Short.MAX_VALUE;
} else if (relX < Short.MIN_VALUE) {
x = relX - Short.MIN_VALUE;
relX = Short.MIN_VALUE;
} else {
x = 0;
}
if (relY > Short.MAX_VALUE) {
y = relY - Short.MAX_VALUE;
relY = Short.MAX_VALUE;
} else if (relY < Short.MIN_VALUE) {
y = relY - Short.MIN_VALUE;
relY = Short.MIN_VALUE;
} else {
y = 0;
}
if (relY > Short.MAX_VALUE) {
y = relY - Short.MAX_VALUE;
relY = Short.MAX_VALUE;
} else if (relY < Short.MIN_VALUE) {
y = relY - Short.MIN_VALUE;
relY = Short.MIN_VALUE;
} else {
y = 0;
}
if (relZ > Short.MAX_VALUE) {
z = relZ - Short.MAX_VALUE;
relZ = Short.MAX_VALUE;
} else if (relZ < Short.MIN_VALUE) {
z = relZ - Short.MIN_VALUE;
relZ = Short.MIN_VALUE;
} else {
z = 0;
}
if (relZ > Short.MAX_VALUE) {
z = relZ - Short.MAX_VALUE;
relZ = Short.MAX_VALUE;
} else if (relZ < Short.MIN_VALUE) {
z = relZ - Short.MIN_VALUE;
relZ = Short.MIN_VALUE;
} else {
z = 0;
}
int sentRelX, sentRelY, sentRelZ;
Vector[] moves;
int sentRelX, sentRelY, sentRelZ;
Vector[] moves;
if (relX > Byte.MAX_VALUE * 128 || relX < Byte.MIN_VALUE * 128 || relY > Byte.MAX_VALUE * 128 || relY < Byte.MIN_VALUE * 128 || relZ > Byte.MAX_VALUE * 128 || relZ < Byte.MIN_VALUE * 128) {
byte relX1 = (byte) (relX / 256);
byte relX2 = (byte) (Math.round((relX - relX1 * 128) / 128f));
byte relY1 = (byte) (relY / 256);
byte relY2 = (byte) (Math.round((relY - relY1 * 128) / 128f));
byte relZ1 = (byte) (relZ / 256);
byte relZ2 = (byte) (Math.round((relZ - relZ1 * 128) / 128f));
if (relX > Byte.MAX_VALUE * 128 || relX < Byte.MIN_VALUE * 128 || relY > Byte.MAX_VALUE * 128 || relY < Byte.MIN_VALUE * 128 || relZ > Byte.MAX_VALUE * 128 || relZ < Byte.MIN_VALUE * 128) {
byte relX1 = (byte) (relX / 256);
byte relX2 = (byte) (Math.round((relX - relX1 * 128) / 128f));
byte relY1 = (byte) (relY / 256);
byte relY2 = (byte) (Math.round((relY - relY1 * 128) / 128f));
byte relZ1 = (byte) (relZ / 256);
byte relZ2 = (byte) (Math.round((relZ - relZ1 * 128) / 128f));
sentRelX = relX1 + relX2;
sentRelY = relY1 + relY2;
sentRelZ = relZ1 + relZ2;
sentRelX = relX1 + relX2;
sentRelY = relY1 + relY2;
sentRelZ = relZ1 + relZ2;
moves = new Vector[]{new Vector(relX1, relY1, relZ1), new Vector(relX2, relY2, relZ2)};
} else {
sentRelX = Math.round(relX / 128f);
sentRelY = Math.round(relY / 128f);
sentRelZ = Math.round(relZ / 128f);
moves = new Vector[]{new Vector(relX1, relY1, relZ1), new Vector(relX2, relY2, relZ2)};
} else {
sentRelX = Math.round(relX / 128f);
sentRelY = Math.round(relY / 128f);
sentRelZ = Math.round(relZ / 128f);
moves = new Vector[]{new Vector(sentRelX, sentRelY, sentRelZ)};
}
moves = new Vector[]{new Vector(sentRelX, sentRelY, sentRelZ)};
}
x = x + relX - sentRelX * 128;
y = y + relY - sentRelY * 128;
z = z + relZ - sentRelZ * 128;
x = x + relX - sentRelX * 128;
y = y + relY - sentRelY * 128;
z = z + relZ - sentRelZ * 128;
tracker.setEntityOffset(entityId, new Vector(x, y, z));
tracker.setEntityOffset(entityId, new Vector(x, y, z));
return moves;
}
return moves;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,109 +20,109 @@ package com.viaversion.viarewind.api.minecraft.math;
import java.util.Objects;
public class Vector3d {
double x, y, z;
double x, y, z;
public Vector3d(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3d(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3d() {
}
public Vector3d() {
}
public void set(Vector3d vec) {
this.x = vec.x;
this.y = vec.y;
this.z = vec.z;
}
public void set(Vector3d vec) {
this.x = vec.x;
this.y = vec.y;
this.z = vec.z;
}
public Vector3d set(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
return this;
}
public Vector3d set(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
return this;
}
public Vector3d multiply(double a) {
this.x *= a;
this.y *= a;
this.z *= a;
return this;
}
public Vector3d multiply(double a) {
this.x *= a;
this.y *= a;
this.z *= a;
return this;
}
public Vector3d add(Vector3d vec) {
this.x += vec.x;
this.y += vec.y;
this.z += vec.z;
return this;
}
public Vector3d add(Vector3d vec) {
this.x += vec.x;
this.y += vec.y;
this.z += vec.z;
return this;
}
public Vector3d substract(Vector3d vec) {
this.x -= vec.x;
this.y -= vec.y;
this.z -= vec.z;
return this;
}
public Vector3d substract(Vector3d vec) {
this.x -= vec.x;
this.y -= vec.y;
this.z -= vec.z;
return this;
}
public double length() {
return Math.sqrt(lengthSquared());
}
public double length() {
return Math.sqrt(lengthSquared());
}
public double lengthSquared() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
public double lengthSquared() {
return this.x * this.x + this.y * this.y + this.z * this.z;
}
public Vector3d normalize() {
double length = length();
multiply(1.0 / length);
return this;
}
public Vector3d normalize() {
double length = length();
multiply(1.0 / length);
return this;
}
@Override
public Vector3d clone() {
return new Vector3d(this.x, this.y, this.z);
}
@Override
public Vector3d clone() {
return new Vector3d(this.x, this.y, this.z);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Vector3d vector3d = (Vector3d) o;
return Double.compare(vector3d.x, x) == 0 && Double.compare(vector3d.y, y) == 0 && Double.compare(vector3d.z, z) == 0;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Vector3d vector3d = (Vector3d) o;
return Double.compare(vector3d.x, x) == 0 && Double.compare(vector3d.y, y) == 0 && Double.compare(vector3d.z, z) == 0;
}
@Override
public int hashCode() {
return Objects.hash(x, y, z);
}
@Override
public int hashCode() {
return Objects.hash(x, y, z);
}
@Override
public String toString() {
return "Vector3d{" + "x=" + x + ", y=" + y + ", z=" + z + '}';
}
@Override
public String toString() {
return "Vector3d{" + "x=" + x + ", y=" + y + ", z=" + z + '}';
}
public double getX() {
return this.x;
}
public double getX() {
return this.x;
}
public double getY() {
return this.y;
}
public void setX(double x) {
this.x = x;
}
public double getZ() {
return this.z;
}
public double getY() {
return this.y;
}
public void setX(double x) {
this.x = x;
}
public void setY(double y) {
this.y = y;
}
public void setY(double y) {
this.y = y;
}
public double getZ() {
return this.z;
}
public void setZ(double z) {
this.z = z;
}
public void setZ(double z) {
this.z = z;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,8 +23,8 @@ import io.netty.handler.codec.MessageToByteEncoder;
public class ForwardMessageToByteEncoder extends MessageToByteEncoder<ByteBuf> {
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
out.writeBytes(msg);
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) {
out.writeBytes(msg);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,17 +26,17 @@ import com.viaversion.viaversion.libs.gson.JsonObject;
public class VRBlockItemRewriter<C extends ClientboundPacketType, S extends ServerboundPacketType, T extends BackwardsProtocol<C, ?, ?, S>> extends LegacyBlockItemRewriter<C, S, T> {
protected VRBlockItemRewriter(T protocol, String name) {
super(protocol, name);
}
protected VRBlockItemRewriter(T protocol, String name) {
super(protocol, name);
}
@Override
protected JsonObject readMappingsFile(String name) {
return RewindMappingDataLoader.INSTANCE.loadFromDataDir(name);
}
@Override
protected JsonObject readMappingsFile(String name) {
return RewindMappingDataLoader.INSTANCE.loadFromDataDir(name);
}
@Override
public String nbtTagName() {
return "VR|" + protocol.getClass().getSimpleName();
}
@Override
public String nbtTagName() {
return "VR|" + protocol.getClass().getSimpleName();
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,55 +26,41 @@ import com.viaversion.viaversion.api.minecraft.entitydata.EntityDataType;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_8;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandler;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
public abstract class VREntityRewriter<C extends ClientboundPacketType, T extends BackwardsProtocol<C, ?, ?, ?>> extends LegacyEntityRewriter<C, T> {
public VREntityRewriter(T protocol) {
super(protocol, EntityDataTypes1_8.STRING, EntityDataTypes1_8.BYTE);
}
public VREntityRewriter(T protocol) {
super(protocol, EntityDataTypes1_8.STRING, EntityDataTypes1_8.BYTE);
}
public VREntityRewriter(T protocol, EntityDataType displayType, EntityDataType displayVisibilityType) {
super(protocol, displayType, displayVisibilityType);
}
public VREntityRewriter(T protocol, EntityDataType displayType, EntityDataType displayVisibilityType) {
super(protocol, displayType, displayVisibilityType);
}
protected void registerJoinGame1_8(final C packetType) {
protocol.registerClientbound(packetType, new PacketHandlers() {
@Override
protected void register() {
map(Types.INT); // Entity id
map(Types.UNSIGNED_BYTE); // Game mode
map(Types.BYTE); // Dimension
handler(playerTrackerHandler());
handler(getDimensionHandler());
}
});
}
protected void removeEntities(final UserConnection connection, final int[] entities) {
final EntityTrackerBase tracker = tracker(connection);
for (int entityId : entities) {
tracker.removeEntity(entityId);
}
}
protected void removeEntities(final UserConnection connection, final int[] entities) {
final EntityTrackerBase tracker = tracker(connection);
for (int entityId : entities) {
tracker.removeEntity(entityId);
}
}
protected PacketHandler getDimensionHandler() {
return wrapper -> {
final int dimension = wrapper.get(Types.BYTE, 0);
final ClientWorld clientWorld = wrapper.user().getClientWorld(protocol.getClass());
clientWorld.setEnvironment(dimension);
};
}
protected PacketHandler getDimensionHandler() {
return wrapper -> {
final int dimension = wrapper.get(Types.BYTE, 0);
final ClientWorld clientWorld = wrapper.user().getClientWorld(protocol.getClass());
clientWorld.setEnvironment(dimension);
};
}
@Override
protected Object getDisplayVisibilityDataValue() {
return (byte) 1;
}
@Override
protected Object getDisplayVisibilityDataValue() {
return (byte) 1;
}
@Override
protected boolean alwaysShowOriginalMobName() {
return ViaRewind.getConfig().alwaysShowOriginalMobName();
}
@Override
protected boolean alwaysShowOriginalMobName() {
return ViaRewind.getConfig().alwaysShowOriginalMobName();
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,23 +22,23 @@ import io.netty.buffer.ByteBuf;
public class IntArrayType extends Type<int[]> {
public IntArrayType() {
super(int[].class);
}
public IntArrayType() {
super(int[].class);
}
@Override
public int[] read(ByteBuf byteBuf) {
byte size = byteBuf.readByte();
int[] array = new int[size];
for (byte i = 0; i < size; i++) {
array[i] = byteBuf.readInt();
}
return array;
}
@Override
public int[] read(ByteBuf byteBuf) {
byte size = byteBuf.readByte();
int[] array = new int[size];
for (byte i = 0; i < size; i++) {
array[i] = byteBuf.readInt();
}
return array;
}
@Override
public void write(ByteBuf byteBuf, int[] array) {
byteBuf.writeByte(array.length);
for (int i : array) byteBuf.writeInt(i);
}
@Override
public void write(ByteBuf byteBuf, int[] array) {
byteBuf.writeByte(array.length);
for (int i : array) byteBuf.writeInt(i);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,34 +20,33 @@ package com.viaversion.viarewind.api.type;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import java.util.function.IntFunction;
public class PositionVarYType<T extends Number> extends Type<BlockPosition> {
private final Type<T> yType;
private final IntFunction<T> toY;
private final Type<T> yType;
private final IntFunction<T> toY;
public PositionVarYType(final Type<T> yType, final IntFunction<T> toY) {
super(BlockPosition.class);
public PositionVarYType(final Type<T> yType, final IntFunction<T> toY) {
super(BlockPosition.class);
this.yType = yType;
this.toY = toY;
}
this.yType = yType;
this.toY = toY;
}
@Override
public BlockPosition read(ByteBuf buffer) {
final int x = buffer.readInt();
final int y = yType.read(buffer).intValue();
final int z = buffer.readInt();
@Override
public BlockPosition read(ByteBuf buffer) {
final int x = buffer.readInt();
final int y = yType.read(buffer).intValue();
final int z = buffer.readInt();
return new BlockPosition(x, y, z);
}
return new BlockPosition(x, y, z);
}
@Override
public void write(ByteBuf buffer, BlockPosition value) {
buffer.writeInt(value.x());
yType.write(buffer, this.toY.apply(value.y()));
buffer.writeInt(value.z());
}
@Override
public void write(ByteBuf buffer, BlockPosition value) {
buffer.writeInt(value.x());
yType.write(buffer, this.toY.apply(value.y()));
buffer.writeInt(value.z());
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,18 +17,17 @@
*/
package com.viaversion.viarewind.api.type;
import com.viaversion.viarewind.api.type.item.NBTType;
import com.viaversion.viarewind.api.type.item.ItemArrayType;
import com.viaversion.viarewind.api.type.item.ItemType;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viarewind.api.type.entitydata.EntityDataListType;
import com.viaversion.viarewind.api.type.entitydata.EntityDataType;
import com.viaversion.viarewind.api.type.item.ItemArrayType;
import com.viaversion.viarewind.api.type.item.ItemType;
import com.viaversion.viarewind.api.type.item.NBTType;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.nbt.tag.CompoundTag;
import java.util.List;
/**
@@ -36,15 +35,18 @@ import java.util.List;
*/
public class RewindTypes {
public static final Type<int[]> INT_ARRAY = new IntArrayType(); // Integer array with byte as length indicator
public static final Type<int[]> INT_ARRAY = new IntArrayType(); // Integer array with byte as length indicator
public static final Type<BlockPosition> SHORT_POSITION = new PositionVarYType<>(Types.SHORT, value -> (short) value);
public static final Type<BlockPosition> INT_POSITION = new PositionVarYType<>(Types.INT, value -> value);
public static final Type<BlockPosition> BYTE_POSITION = new PositionVarYType<>(Types.BYTE, value -> (byte) value);
public static final Type<BlockPosition> U_BYTE_POSITION = new PositionVarYType<>(Types.UNSIGNED_BYTE, value -> (short) value);
public static final Type<BlockPosition> SHORT_POSITION = new PositionVarYType<>(Types.SHORT, value -> (short) value);
public static final Type<BlockPosition> INT_POSITION = new PositionVarYType<>(Types.INT, value -> value);
public static final Type<BlockPosition> BYTE_POSITION = new PositionVarYType<>(Types.BYTE, value -> (byte) value);
public static final Type<BlockPosition> U_BYTE_POSITION = new PositionVarYType<>(Types.UNSIGNED_BYTE, value -> (short) value);
public static final Type<CompoundTag> COMPRESSED_NBT = new NBTType();
public static final Type<Item> COMPRESSED_NBT_ITEM = new ItemType();
public static final Type<Item[]> COMPRESSED_NBT_ITEM_ARRAY = new ItemArrayType();
public static final Type<CompoundTag> COMPRESSED_NBT = new NBTType();
public static final Type<Item> COMPRESSED_NBT_ITEM = new ItemType();
public static final Type<Item[]> COMPRESSED_NBT_ITEM_ARRAY = new ItemArrayType();
public static final Type<EntityData> ENTITY_DATA1_7 = new EntityDataType();
public static final Type<List<EntityData>> ENTITY_DATA_LIST1_7 = new EntityDataListType(ENTITY_DATA1_7);
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,86 +17,102 @@
*/
package com.viaversion.viarewind.api.type.chunk;
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
import java.io.ByteArrayOutputStream;
import java.util.zip.Deflater;
import java.util.zip.DataFormatException;
public class BulkChunkType1_7_6 extends Type<Chunk[]> {
public static final BulkChunkType1_7_6 TYPE = new BulkChunkType1_7_6();
public static final BulkChunkType1_7_6 TYPE = new BulkChunkType1_7_6();
public BulkChunkType1_7_6() {
super(Chunk[].class);
}
public BulkChunkType1_7_6() {
super(Chunk[].class);
}
@Override
public Chunk[] read(ByteBuf byteBuf) {
throw new UnsupportedOperationException(); // Not needed, see https://github.com/ViaVersion/ViaLegacy/blob/main/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/types/ChunkBulk1_7_6Type.java
}
@Override
public Chunk[] read(ByteBuf byteBuf) {
throw new UnsupportedOperationException(); // Not needed, see https://github.com/ViaVersion/ViaLegacy/blob/main/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/types/ChunkBulk1_7_6Type.java
}
@Override
public void write(ByteBuf byteBuf, Chunk[] chunks) {
final int chunkCount = chunks.length;
final ByteArrayOutputStream output = new ByteArrayOutputStream();
final int[] chunkX = new int[chunkCount];
final int[] chunkZ = new int[chunkCount];
final short[] primaryBitMask = new short[chunkCount];
final short[] additionalBitMask = new short[chunkCount];
@Override
public void write(ByteBuf buffer, Chunk[] chunks) {
final int chunkCount = chunks.length;
final int[] addBitMasks = new int[chunkCount];
int totalSize = 0;
boolean anySkyLight = false;
for (int i = 0; i < chunkCount; i++) {
final Chunk chunk = chunks[i];
Pair<byte[], Short> chunkData;
try {
chunkData = ChunkType1_7_6.serialize(chunk);
output.write(chunkData.key());
} catch (Exception e) {
throw new RuntimeException("Unable to serialize chunk", e);
}
chunkX[i] = chunk.getX();
chunkZ[i] = chunk.getZ();
primaryBitMask[i] = (short) chunk.getBitmask();
additionalBitMask[i] = chunkData.value();
}
final byte[] data = output.toByteArray();
for (Chunk chunk : chunks) {
if (ChunkType1_7_6.hasSkyLight(chunk)) {
anySkyLight = true;
break;
}
}
final Deflater deflater = new Deflater();
byte[] compressedData;
int compressedSize;
try {
deflater.setInput(data, 0, data.length);
deflater.finish();
compressedData = new byte[data.length];
compressedSize = deflater.deflate(compressedData);
} finally {
deflater.end();
}
for (int i = 0; i < chunkCount; i++) {
Chunk chunk = chunks[i];
addBitMasks[i] = ChunkType1_7_6.getAddBitMask(chunk);
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
byteBuf.writeShort(chunkCount);
byteBuf.writeInt(compressedSize);
totalSize += ChunkType1_7_6.calcSize(
chunk.getBitmask(),
addBitMasks[i],
anySkyLight,
biomes
);
}
boolean skyLight = false;
for (Chunk chunk : chunks) {
for (ChunkSection section : chunk.getSections()) {
if (section != null && section.getLight().hasSkyLight()) {
skyLight = true;
break;
}
}
}
final ByteBuf uncompressed = buffer.alloc().buffer(totalSize);
byteBuf.writeBoolean(skyLight); // hasSkyLight
byteBuf.writeBytes(compressedData, 0, compressedSize);
try {
for (int i = 0; i < chunkCount; i++) {
Chunk chunk = chunks[i];
boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
for (int i = 0; i < chunkCount; i++) {
byteBuf.writeInt(chunkX[i]);
byteBuf.writeInt(chunkZ[i]);
byteBuf.writeShort(primaryBitMask[i]);
byteBuf.writeShort(additionalBitMask[i]);
}
}
ChunkType1_7_6.serialize(
chunk,
uncompressed,
addBitMasks[i],
anySkyLight,
biomes
);
}
buffer.writeShort(chunkCount);
// Reserve 4 bytes for the compressed size
final int sizeIndex = buffer.writerIndex();
buffer.writeInt(0); // Placeholder for compressed size
buffer.writeBoolean(anySkyLight);
// Write compressed data directly to output buffer
final int compressedStart = buffer.writerIndex();
try {
ThreadLocalCompressionProvider.deflate(uncompressed, buffer);
} catch (DataFormatException e) {
throw new RuntimeException("Failed to compress bulk chunk data", e);
}
final int compressedSize = buffer.writerIndex() - compressedStart;
// Go back and write the compressed size
final int endIndex = buffer.writerIndex();
buffer.writerIndex(sizeIndex);
buffer.writeInt(compressedSize);
buffer.writerIndex(endIndex);
} finally {
uncompressed.release();
}
for (int i = 0; i < chunkCount; i++) {
Chunk chunk = chunks[i];
buffer.writeInt(chunk.getX());
buffer.writeInt(chunk.getZ());
buffer.writeShort(chunk.getBitmask());
buffer.writeShort(addBitMasks[i]);
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,123 +17,191 @@
*/
package com.viaversion.viarewind.api.type.chunk;
import com.viaversion.viarewind.api.minecraft.ExtendedBlockStorage;
import com.viaversion.viaversion.api.minecraft.chunks.*;
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.minecraft.chunks.ChunkSection;
import com.viaversion.viaversion.api.minecraft.chunks.DataPalette;
import com.viaversion.viaversion.api.minecraft.chunks.PaletteType;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.util.Pair;
import io.netty.buffer.ByteBuf;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.Deflater;
import java.util.zip.DataFormatException;
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSection.SIZE;
import static com.viaversion.viaversion.api.minecraft.chunks.ChunkSectionLight.LIGHT_LENGTH;
public class ChunkType1_7_6 extends Type<Chunk> {
public static final ChunkType1_7_6 TYPE = new ChunkType1_7_6();
public static final ChunkType1_7_6 TYPE = new ChunkType1_7_6();
public ChunkType1_7_6() {
super(Chunk.class);
}
public ChunkType1_7_6() {
super(Chunk.class);
}
@Override
public Chunk read(ByteBuf byteBuf) {
throw new UnsupportedOperationException(); // Not needed, see https://github.com/ViaVersion/ViaLegacy/blob/main/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/types/Chunk1_7_6Type.java
}
@Override
public Chunk read(ByteBuf byteBuf) {
throw new UnsupportedOperationException(); // Not needed, see https://github.com/ViaVersion/ViaLegacy/blob/main/src/main/java/net/raphimc/vialegacy/protocols/release/protocol1_8to1_7_6_10/types/Chunk1_7_6Type.java
}
@Override
public void write(ByteBuf output, Chunk chunk) {
Pair<byte[], Short> chunkData;
try {
chunkData = serialize(chunk);
} catch (IOException e) {
throw new RuntimeException("Unable to serialize chunk", e);
}
final byte[] data = chunkData.key();
final short additionalBitMask = chunkData.value();
@Override
public void write(ByteBuf buffer, Chunk chunk) {
final int bitmask = chunk.getBitmask();
final int addBitmask = getAddBitMask(chunk);
final boolean hasSkyLight = hasSkyLight(chunk);
final boolean biomes = chunk.isFullChunk() && chunk.getBiomeData() != null;
final Deflater deflater = new Deflater();
byte[] compressedData;
int compressedSize;
try {
deflater.setInput(data, 0, data.length);
deflater.finish();
compressedData = new byte[data.length];
compressedSize = deflater.deflate(compressedData);
} finally {
deflater.end();
}
final int size = calcSize(bitmask, addBitmask, hasSkyLight, biomes);
final ByteBuf uncompressed = buffer.alloc().buffer(size);
output.writeInt(chunk.getX());
output.writeInt(chunk.getZ());
output.writeBoolean(chunk.isFullChunk());
output.writeShort(chunk.getBitmask());
output.writeShort(additionalBitMask);
output.writeInt(compressedSize);
output.writeBytes(compressedData, 0, compressedSize);
}
try {
serialize(chunk, uncompressed, addBitmask, hasSkyLight, biomes);
public static Pair<byte[], Short> serialize(final Chunk chunk) throws IOException {
final ExtendedBlockStorage[] storageArrays = new ExtendedBlockStorage[16];
for (int i = 0; i < storageArrays.length; i++) {
final ChunkSection section = chunk.getSections()[i];
if (section != null) {
final ExtendedBlockStorage storage = storageArrays[i] = new ExtendedBlockStorage(section.getLight().hasSkyLight());
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
for (int y = 0; y < 16; y++) {
final int flatBlock = section.palette(PaletteType.BLOCKS).idAt(x, y, z);
storage.setBlockId(x, y, z, flatBlock >> 4);
storage.setBlockMetadata(x, y, z, flatBlock & 15);
}
}
}
storage.getBlockLightArray().setHandle(section.getLight().getBlockLight());
if (section.getLight().hasSkyLight()) {
storage.getSkyLightArray().setHandle(section.getLight().getSkyLight());
}
}
}
buffer.writeInt(chunk.getX());
buffer.writeInt(chunk.getZ());
buffer.writeBoolean(chunk.isFullChunk());
buffer.writeShort(bitmask);
buffer.writeShort(addBitmask);
final ByteArrayOutputStream output = new ByteArrayOutputStream();
// Reserve 4 bytes for the compressed size
final int sizeIndex = buffer.writerIndex();
buffer.writeInt(0); // Placeholder for compressed size
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0) {
output.write(storageArrays[i].getBlockLSBArray());
}
}
// Write compressed data directly to output buffer
final int compressedStart = buffer.writerIndex();
try {
ThreadLocalCompressionProvider.deflate(uncompressed, buffer);
} catch (DataFormatException e) {
throw new RuntimeException("Failed to compress chunk data", e);
}
final int compressedSize = buffer.writerIndex() - compressedStart;
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0) {
output.write(storageArrays[i].getBlockMetadataArray().getHandle());
}
}
// Go back and write the compressed size
final int endIndex = buffer.writerIndex();
buffer.writerIndex(sizeIndex);
buffer.writeInt(compressedSize);
buffer.writerIndex(endIndex);
} finally {
uncompressed.release();
}
}
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0) {
output.write(storageArrays[i].getBlockLightArray().getHandle());
}
}
public static void serialize(Chunk chunk, ByteBuf output, int addBitmask, boolean writeSkyLight, boolean biomes) {
final ChunkSection[] sections = chunk.getSections();
final int bitmask = chunk.getBitmask();
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0 && storageArrays[i].getSkyLightArray() != null) {
output.write(storageArrays[i].getSkyLightArray().getHandle());
}
}
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
final ChunkSection section = sections[i];
final DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int j = 0; j < SIZE; j++) {
final int block = palette.idAt(j);
output.writeByte((block >> 4) & 0xFF);
}
}
}
short additionalBitMask = 0;
for (int i = 0; i < storageArrays.length; i++) {
if ((chunk.getBitmask() & 1 << i) != 0 && storageArrays[i].hasBlockMSBArray()) {
additionalBitMask |= (short) (1 << i);
output.write(storageArrays[i].getOrCreateBlockMSBArray().getHandle());
}
}
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
final ChunkSection section = sections[i];
final DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int j = 0; j < ChunkSection.SIZE; j += 2) {
final int meta1 = palette.idAt(j) & 0xF;
final int meta2 = palette.idAt(j + 1) & 0xF;
output.writeByte(meta1 | (meta2 << 4));
}
}
}
if (chunk.isFullChunk() && chunk.getBiomeData() != null) {
for (int biome : chunk.getBiomeData()) {
output.write(biome);
}
}
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
final byte[] blockLight = sections[i].getLight().getBlockLight();
output.writeBytes(blockLight);
}
}
return new Pair<>(output.toByteArray(), additionalBitMask);
}
if (writeSkyLight) {
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0) {
if (sections[i].getLight().hasSkyLight()) {
final byte[] skyLight = sections[i].getLight().getSkyLight();
output.writeBytes(skyLight);
} else {
// Write empty skylight data
output.writeZero(LIGHT_LENGTH);
}
}
}
}
if (addBitmask != 0) {
for (int i = 0; i < 16; i++) {
if ((bitmask & (1 << i)) != 0 && (addBitmask & (1 << i)) != 0) {
final ChunkSection section = sections[i];
final DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int j = 0; j < SIZE; j += 2) {
final int add1 = (palette.idAt(j) >> 12) & 0xF;
final int add2 = (palette.idAt(j + 1) >> 12) & 0xF;
output.writeByte(add1 | (add2 << 4));
}
}
}
}
if (biomes && chunk.getBiomeData() != null) {
final int[] biomeData = chunk.getBiomeData();
for (int biome : biomeData) {
output.writeByte(biome);
}
}
}
public static int calcSize(int bitmask, int addBitmask, boolean hasSkyLight, boolean biomes) {
int size = 0;
int sections = Integer.bitCount(bitmask);
size += sections * SIZE;
size += sections * (SIZE / 2);
size += sections * LIGHT_LENGTH;
if (hasSkyLight) {
size += sections * LIGHT_LENGTH;
}
if (addBitmask != 0) {
size += Integer.bitCount(addBitmask) * (SIZE / 2);
}
if (biomes) {
size += 256;
}
return size;
}
public static int getAddBitMask(Chunk chunk) {
int addBitMask = 0;
for (int i = 0; i < 16; i++) {
if ((chunk.getBitmask() & (1 << i)) != 0) {
final ChunkSection section = chunk.getSections()[i];
final DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int j = 0; j < SIZE; j++) {
final int id = palette.idAt(j);
if ((id >> 12) != 0) {
addBitMask |= (1 << i);
break;
}
}
}
}
return addBitMask;
}
public static boolean hasSkyLight(Chunk chunk) {
for (ChunkSection section : chunk.getSections()) {
if (section != null && section.getLight().hasSkyLight()) {
return true;
}
}
return false;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,41 +22,40 @@ import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.type.types.entitydata.EntityDataListTypeTemplate;
import io.netty.buffer.ByteBuf;
import java.util.ArrayList;
import java.util.List;
public class EntityDataListType extends EntityDataListTypeTemplate {
private final Type<EntityData> type;
private final Type<EntityData> type;
public EntityDataListType(Type<EntityData> type) {
this.type = type;
}
public EntityDataListType(Type<EntityData> type) {
this.type = type;
}
@Override
public List<EntityData> read(ByteBuf buffer) {
ArrayList<EntityData> entityData = new ArrayList<>();
@Override
public List<EntityData> read(ByteBuf buffer) {
ArrayList<EntityData> entityData = new ArrayList<>();
EntityData data;
do {
data = type.read(buffer);
if (data != null) {
entityData.add(data);
}
} while (data != null);
EntityData data;
do {
data = type.read(buffer);
if (data != null) {
entityData.add(data);
}
} while (data != null);
return entityData;
}
return entityData;
}
@Override
public void write(ByteBuf buffer, List<EntityData> entityData) {
for (EntityData meta : entityData) {
type.write(buffer, meta);
}
if (entityData.isEmpty()) {
type.write(buffer, new EntityData(0, EntityDataTypes1_7_6_10.BYTE, (byte) 0));
}
buffer.writeByte(127);
}
}
@Override
public void write(ByteBuf buffer, List<EntityData> entityData) {
for (EntityData meta : entityData) {
type.write(buffer, meta);
}
if (entityData.isEmpty()) {
type.write(buffer, new EntityData(0, EntityDataTypes1_7_6_10.BYTE, (byte) 0));
}
buffer.writeByte(127);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,23 +23,23 @@ import com.viaversion.viaversion.api.type.types.entitydata.EntityDataTypeTemplat
import io.netty.buffer.ByteBuf;
public class EntityDataType extends EntityDataTypeTemplate {
@Override
public EntityData read(ByteBuf buffer) {
byte item = buffer.readByte();
if (item == 127) {
return null;
} else {
int typeID = (item & 224) >> 5;
EntityDataTypes1_7_6_10 type = EntityDataTypes1_7_6_10.byId(typeID);
int id = item & 31;
return new EntityData(id, type, type.type().read(buffer));
}
}
@Override
public EntityData read(ByteBuf buffer) {
byte item = buffer.readByte();
if (item == 127) {
return null;
} else {
int typeID = (item & 224) >> 5;
EntityDataTypes1_7_6_10 type = EntityDataTypes1_7_6_10.byId(typeID);
int id = item & 31;
return new EntityData(id, type, type.type().read(buffer));
}
}
@Override
public void write(ByteBuf buffer, EntityData meta) {
int item = (meta.dataType().typeId() << 5 | meta.id() & 31) & 255;
buffer.writeByte(item);
meta.dataType().type().write(buffer, meta.getValue());
}
@Override
public void write(ByteBuf buffer, EntityData meta) {
int item = (meta.dataType().typeId() << 5 | meta.id() & 31) & 255;
buffer.writeByte(item);
meta.dataType().type().write(buffer, meta.getValue());
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,26 +25,26 @@ import io.netty.buffer.ByteBuf;
public class ItemArrayType extends Type<Item[]> {
public ItemArrayType() {
super(Item[].class);
}
public ItemArrayType() {
super(Item[].class);
}
@Override
public Item[] read(ByteBuf buffer) {
int amount = Types.SHORT.readPrimitive(buffer);
Item[] items = new Item[amount];
@Override
public Item[] read(ByteBuf buffer) {
int amount = Types.SHORT.readPrimitive(buffer);
Item[] items = new Item[amount];
for (int i = 0; i < amount; ++i) {
items[i] = RewindTypes.COMPRESSED_NBT_ITEM.read(buffer);
}
return items;
}
for (int i = 0; i < amount; ++i) {
items[i] = RewindTypes.COMPRESSED_NBT_ITEM.read(buffer);
}
return items;
}
@Override
public void write(ByteBuf buffer, Item[] items) {
Types.SHORT.writePrimitive(buffer, (short) items.length);
for (Item item : items) {
RewindTypes.COMPRESSED_NBT_ITEM.write(buffer, item);
}
}
@Override
public void write(ByteBuf buffer, Item[] items) {
Types.SHORT.writePrimitive(buffer, (short) items.length);
for (Item item : items) {
RewindTypes.COMPRESSED_NBT_ITEM.write(buffer, item);
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,33 +25,33 @@ import io.netty.buffer.ByteBuf;
public class ItemType extends Type<Item> {
public ItemType() {
super(Item.class);
}
public ItemType() {
super(Item.class);
}
@Override
public Item read(ByteBuf buffer) {
short id = buffer.readShort();
if (id < 0) {
return null;
}
Item item = new DataItem();
item.setIdentifier(id);
item.setAmount(buffer.readByte());
item.setData(buffer.readShort());
item.setTag(RewindTypes.COMPRESSED_NBT.read(buffer));
return item;
}
@Override
public Item read(ByteBuf buffer) {
short id = buffer.readShort();
if (id < 0) {
return null;
}
Item item = new DataItem();
item.setIdentifier(id);
item.setAmount(buffer.readByte());
item.setData(buffer.readShort());
item.setTag(RewindTypes.COMPRESSED_NBT.read(buffer));
return item;
}
@Override
public void write(ByteBuf buffer, Item item) {
if (item == null) {
buffer.writeShort(-1);
} else {
buffer.writeShort(item.identifier());
buffer.writeByte(item.amount());
buffer.writeShort(item.data());
RewindTypes.COMPRESSED_NBT.write(buffer, item.tag());
}
}
@Override
public void write(ByteBuf buffer, Item item) {
if (item == null) {
buffer.writeShort(-1);
} else {
buffer.writeShort(item.identifier());
buffer.writeByte(item.amount());
buffer.writeShort(item.data());
RewindTypes.COMPRESSED_NBT.write(buffer, item.tag());
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,57 +17,56 @@
*/
package com.viaversion.viarewind.api.type.item;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.io.NBTIO;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class NBTType extends Type<CompoundTag> {
public NBTType() {
super(CompoundTag.class);
}
public NBTType() {
super(CompoundTag.class);
}
@Override
public CompoundTag read(ByteBuf buffer) {
short length = buffer.readShort();
if (length <= 0) {
return null;
}
@Override
public CompoundTag read(ByteBuf buffer) {
short length = buffer.readShort();
if (length <= 0) {
return null;
}
ByteBuf compressed = buffer.readSlice(length);
ByteBuf compressed = buffer.readSlice(length);
try (GZIPInputStream gzipStream = new GZIPInputStream(new ByteBufInputStream(compressed))) {
return NBTIO.reader(CompoundTag.class).named().read(gzipStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
try (GZIPInputStream gzipStream = new GZIPInputStream(new ByteBufInputStream(compressed))) {
return NBTIO.reader(CompoundTag.class).named().read(gzipStream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void write(ByteBuf buffer, CompoundTag nbt) {
if (nbt == null) {
buffer.writeShort(-1);
return;
}
@Override
public void write(ByteBuf buffer, CompoundTag nbt) {
if (nbt == null) {
buffer.writeShort(-1);
return;
}
ByteBuf compressedBuf = buffer.alloc().buffer();
try {
try (GZIPOutputStream gzipStream = new GZIPOutputStream(new ByteBufOutputStream(compressedBuf))) {
NBTIO.writer().named().write(gzipStream, nbt);
} catch (IOException e) {
throw new RuntimeException(e);
}
ByteBuf compressedBuf = buffer.alloc().buffer();
try {
try (GZIPOutputStream gzipStream = new GZIPOutputStream(new ByteBufOutputStream(compressedBuf))) {
NBTIO.writer().named().write(gzipStream, nbt);
} catch (IOException e) {
throw new RuntimeException(e);
}
buffer.writeShort(compressedBuf.readableBytes());
buffer.writeBytes(compressedBuf);
} finally {
compressedBuf.release();
}
}
buffer.writeShort(compressedBuf.readableBytes());
buffer.writeBytes(compressedBuf);
} finally {
compressedBuf.release();
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,7 +17,7 @@
*/
package com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5;
import com.viaversion.viarewind.api.type.version.Types1_7_6_10;
import com.viaversion.viarewind.api.type.RewindTypes;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ServerboundPackets1_7_2_5;
import com.viaversion.viaversion.api.protocol.AbstractProtocol;
@@ -27,90 +27,89 @@ import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class Protocol1_7_6_10To1_7_2_5 extends AbstractProtocol<ClientboundPackets1_7_2_5, ClientboundPackets1_7_2_5, ServerboundPackets1_7_2_5, ServerboundPackets1_7_2_5> {
public static final ValueTransformer<String, String> REMOVE_DASHES = new ValueTransformer<>(Types.STRING) {
@Override
public String transform(PacketWrapper wrapper, String s) {
return s.replace("-", "");
}
};
public static final ValueTransformer<String, String> REMOVE_DASHES = new ValueTransformer<>(Types.STRING) {
@Override
public String transform(PacketWrapper wrapper, String s) {
return s.replace("-", "");
}
};
public Protocol1_7_6_10To1_7_2_5() {
super(ClientboundPackets1_7_2_5.class, ClientboundPackets1_7_2_5.class, ServerboundPackets1_7_2_5.class, ServerboundPackets1_7_2_5.class);
}
public Protocol1_7_6_10To1_7_2_5() {
super(ClientboundPackets1_7_2_5.class, ClientboundPackets1_7_2_5.class, ServerboundPackets1_7_2_5.class, ServerboundPackets1_7_2_5.class);
}
@Override
protected void registerPackets() {
this.registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING, REMOVE_DASHES); // Uuid
map(Types.STRING); // Name
}
});
@Override
protected void registerPackets() {
this.registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_FINISHED, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING, REMOVE_DASHES); // Uuid
map(Types.STRING); // Name
}
});
this.registerClientbound(ClientboundPackets1_7_2_5.ADD_PLAYER, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT); // Entity id
map(Types.STRING, REMOVE_DASHES); // Uuid
map(Types.STRING); // Name
handler(wrapper -> {
final int size = wrapper.read(Types.VAR_INT); // Data count
for (int i = 0; i < size; i++) {
wrapper.read(Types.STRING); // Data name
wrapper.read(Types.STRING); // Data value
wrapper.read(Types.STRING); // Data signature
}
});
map(Types.INT); // X
map(Types.INT); // Y
map(Types.INT); // Z
map(Types.BYTE); // Yaw
map(Types.BYTE); // Pitch
map(Types.SHORT); // Item in hand
map(Types1_7_6_10.ENTITY_DATA_LIST); // Entity data
}
});
this.registerClientbound(ClientboundPackets1_7_2_5.ADD_PLAYER, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT); // Entity id
map(Types.STRING, REMOVE_DASHES); // Uuid
map(Types.STRING); // Name
handler(wrapper -> {
final int size = wrapper.read(Types.VAR_INT); // Data count
for (int i = 0; i < size; i++) {
wrapper.read(Types.STRING); // Data name
wrapper.read(Types.STRING); // Data value
wrapper.read(Types.STRING); // Data signature
}
});
map(Types.INT); // X
map(Types.INT); // Y
map(Types.INT); // Z
map(Types.BYTE); // Yaw
map(Types.BYTE); // Pitch
map(Types.SHORT); // Item in hand
map(RewindTypes.ENTITY_DATA_LIST1_7); // Entity data
}
});
this.registerClientbound(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING); // Team name
map(Types.BYTE); // Mode
handler(wrapper -> {
final byte mode = wrapper.get(Types.BYTE, 0);
if (mode == 0 || mode == 2) { // Team is created or information is updated
wrapper.passthrough(Types.STRING); // Team display name
wrapper.passthrough(Types.STRING); // Team prefix
wrapper.passthrough(Types.STRING); // Team suffix
wrapper.passthrough(Types.BYTE); // Friendly fire
}
if (mode == 0 || mode == 3 || mode == 4) { // Team is created, player is added or player is removed
List<String> entryList = new ArrayList<>();
final int size = wrapper.read(Types.SHORT);
for (int i = 0; i < size; i++) {
entryList.add(wrapper.read(Types.STRING));
}
this.registerClientbound(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING); // Team name
map(Types.BYTE); // Mode
handler(wrapper -> {
final byte mode = wrapper.get(Types.BYTE, 0);
if (mode == 0 || mode == 2) { // Team is created or information is updated
wrapper.passthrough(Types.STRING); // Team display name
wrapper.passthrough(Types.STRING); // Team prefix
wrapper.passthrough(Types.STRING); // Team suffix
wrapper.passthrough(Types.BYTE); // Friendly fire
}
if (mode == 0 || mode == 3 || mode == 4) { // Team is created, player is added or player is removed
List<String> entryList = new ArrayList<>();
final int size = wrapper.read(Types.SHORT);
for (int i = 0; i < size; i++) {
entryList.add(wrapper.read(Types.STRING));
}
entryList = entryList.stream()
.map(it -> it.length() > 16 ? it.substring(0, 16) : it) // trim to 16 characters
.distinct() // remove duplicates
.collect(Collectors.toList());
entryList = entryList.stream()
.map(it -> it.length() > 16 ? it.substring(0, 16) : it) // trim to 16 characters
.distinct() // remove duplicates
.collect(Collectors.toList());
wrapper.write(Types.SHORT, (short) entryList.size());
for (String entry : entryList) {
wrapper.write(Types.STRING, entry);
}
}
});
}
});
}
wrapper.write(Types.SHORT, (short) entryList.size());
for (String entry : entryList) {
wrapper.write(Types.STRING, entry);
}
}
});
}
});
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,79 +20,79 @@ package com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet;
import com.viaversion.viaversion.api.protocol.packet.ClientboundPacketType;
public enum ClientboundPackets1_7_2_5 implements ClientboundPacketType {
KEEP_ALIVE, // 0x00
LOGIN, // 0x01
CHAT, // 0x02
SET_TIME, // 0x03
SET_EQUIPPED_ITEM, // 0x04
SET_DEFAULT_SPAWN_POSITION, // 0x05
SET_HEALTH, // 0x06
RESPAWN, // 0x07
PLAYER_POSITION, // 0x08
SET_CARRIED_ITEM, // 0x09
PLAYER_SLEEP, // 0x0A
ANIMATE, // 0x0B
ADD_PLAYER, // 0x0C
TAKE_ITEM_ENTITY, // 0x0D
ADD_ENTITY, // 0x0E
ADD_MOB, // 0x0F
ADD_PAINTING, // 0x10
ADD_EXPERIENCE_ORB, // 0x11
SET_ENTITY_MOTION, // 0x12
REMOVE_ENTITIES, // 0x13
MOVE_ENTITY, // 0x14
MOVE_ENTITY_POS, // 0x15
MOVE_ENTITY_ROT, // 0x16
MOVE_ENTITY_POS_ROT, // 0x17
TELEPORT_ENTITY, // 0x18
ROTATE_HEAD, // 0x19
ENTITY_EVENT, // 0x1A
SET_ENTITY_LINK, // 0x1B
SET_ENTITY_DATA, // 0x1C
UPDATE_MOB_EFFECT, // 0x1D
REMOVE_MOB_EFFECT, // 0x1E
SET_EXPERIENCE, // 0x1F
UPDATE_ATTRIBUTES, // 0x20
LEVEL_CHUNK, // 0x21
CHUNK_BLOCKS_UPDATE, // 0x22
BLOCK_UPDATE, // 0x23
BLOCK_EVENT, // 0x24
BLOCK_DESTRUCTION, // 0x25
MAP_BULK_CHUNK, // 0x26
EXPLODE, // 0x27
LEVEL_EVENT, // 0x28
CUSTOM_SOUND, // 0x29
LEVEL_PARTICLES, // 0x2A
GAME_EVENT, // 0x2B
ADD_GLOBAL_ENTITY, // 0x2C
OPEN_SCREEN, // 0x2D
CONTAINER_CLOSE, // 0x2E
CONTAINER_SET_SLOT, // 0x2F
CONTAINER_SET_CONTENT, // 0x30
CONTAINER_SET_DATA, // 0x31
CONTAINER_ACK, // 0x32
UPDATE_SIGN, // 0x33
MAP_ITEM_DATA, // 0x34
BLOCK_ENTITY_DATA, // 0x35
OPEN_SIGN_EDITOR, // 0x36
AWARD_STATS, // 0x37
PLAYER_INFO, // 0x38
PLAYER_ABILITIES, // 0x39
COMMAND_SUGGESTIONS, // 0x3A
SET_OBJECTIVE, // 0x3B
SET_SCORE, // 0x3C
SET_DISPLAY_OBJECTIVE, // 0x3D
SET_PLAYER_TEAM, // 0x3E
CUSTOM_PAYLOAD, // 0x3F
DISCONNECT; // 0x40
KEEP_ALIVE, // 0x00
LOGIN, // 0x01
CHAT, // 0x02
SET_TIME, // 0x03
SET_EQUIPPED_ITEM, // 0x04
SET_DEFAULT_SPAWN_POSITION, // 0x05
SET_HEALTH, // 0x06
RESPAWN, // 0x07
PLAYER_POSITION, // 0x08
SET_CARRIED_ITEM, // 0x09
PLAYER_SLEEP, // 0x0A
ANIMATE, // 0x0B
ADD_PLAYER, // 0x0C
TAKE_ITEM_ENTITY, // 0x0D
ADD_ENTITY, // 0x0E
ADD_MOB, // 0x0F
ADD_PAINTING, // 0x10
ADD_EXPERIENCE_ORB, // 0x11
SET_ENTITY_MOTION, // 0x12
REMOVE_ENTITIES, // 0x13
MOVE_ENTITY, // 0x14
MOVE_ENTITY_POS, // 0x15
MOVE_ENTITY_ROT, // 0x16
MOVE_ENTITY_POS_ROT, // 0x17
TELEPORT_ENTITY, // 0x18
ROTATE_HEAD, // 0x19
ENTITY_EVENT, // 0x1A
SET_ENTITY_LINK, // 0x1B
SET_ENTITY_DATA, // 0x1C
UPDATE_MOB_EFFECT, // 0x1D
REMOVE_MOB_EFFECT, // 0x1E
SET_EXPERIENCE, // 0x1F
UPDATE_ATTRIBUTES, // 0x20
LEVEL_CHUNK, // 0x21
CHUNK_BLOCKS_UPDATE, // 0x22
BLOCK_UPDATE, // 0x23
BLOCK_EVENT, // 0x24
BLOCK_DESTRUCTION, // 0x25
MAP_BULK_CHUNK, // 0x26
EXPLODE, // 0x27
LEVEL_EVENT, // 0x28
CUSTOM_SOUND, // 0x29
LEVEL_PARTICLES, // 0x2A
GAME_EVENT, // 0x2B
ADD_GLOBAL_ENTITY, // 0x2C
OPEN_SCREEN, // 0x2D
CONTAINER_CLOSE, // 0x2E
CONTAINER_SET_SLOT, // 0x2F
CONTAINER_SET_CONTENT, // 0x30
CONTAINER_SET_DATA, // 0x31
CONTAINER_ACK, // 0x32
UPDATE_SIGN, // 0x33
MAP_ITEM_DATA, // 0x34
BLOCK_ENTITY_DATA, // 0x35
OPEN_SIGN_EDITOR, // 0x36
AWARD_STATS, // 0x37
PLAYER_INFO, // 0x38
PLAYER_ABILITIES, // 0x39
COMMAND_SUGGESTIONS, // 0x3A
SET_OBJECTIVE, // 0x3B
SET_SCORE, // 0x3C
SET_DISPLAY_OBJECTIVE, // 0x3D
SET_PLAYER_TEAM, // 0x3E
CUSTOM_PAYLOAD, // 0x3F
DISCONNECT; // 0x40
@Override
public int getId() {
return ordinal();
}
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
@Override
public String getName() {
return name();
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,38 +20,38 @@ package com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet;
import com.viaversion.viaversion.api.protocol.packet.ServerboundPacketType;
public enum ServerboundPackets1_7_2_5 implements ServerboundPacketType {
KEEP_ALIVE, // 0x00
CHAT, // 0x01
INTERACT, // 0x02
MOVE_PLAYER_STATUS_ONLY, // 0x03
MOVE_PLAYER_POS, // 0x04
MOVE_PLAYER_ROT, // 0x05
MOVE_PLAYER_POS_ROT, // 0x06
PLAYER_ACTION, // 0x07
USE_ITEM_ON, // 0x08
SET_CARRIED_ITEM, // 0x09
SWING, // 0x0A
PLAYER_COMMAND, // 0x0B
PLAYER_INPUT, // 0x0C
CONTAINER_CLOSE, // 0x0D
CONTAINER_CLICK, // 0x0E
CONTAINER_ACK, // 0x0F
SET_CREATIVE_MODE_SLOT, // 0x10
CONTAINER_BUTTON_CLICK, // 0x11
SIGN_UPDATE, // 0x12
PLAYER_ABILITIES, // 0x13
COMMAND_SUGGESTION, // 0x14
CLIENT_INFORMATION, // 0x15
CLIENT_COMMAND, // 0x16
CUSTOM_PAYLOAD; // 0x17
KEEP_ALIVE, // 0x00
CHAT, // 0x01
INTERACT, // 0x02
MOVE_PLAYER_STATUS_ONLY, // 0x03
MOVE_PLAYER_POS, // 0x04
MOVE_PLAYER_ROT, // 0x05
MOVE_PLAYER_POS_ROT, // 0x06
PLAYER_ACTION, // 0x07
USE_ITEM_ON, // 0x08
SET_CARRIED_ITEM, // 0x09
SWING, // 0x0A
PLAYER_COMMAND, // 0x0B
PLAYER_INPUT, // 0x0C
CONTAINER_CLOSE, // 0x0D
CONTAINER_CLICK, // 0x0E
CONTAINER_ACK, // 0x0F
SET_CREATIVE_MODE_SLOT, // 0x10
CONTAINER_BUTTON_CLICK, // 0x11
SIGN_UPDATE, // 0x12
PLAYER_ABILITIES, // 0x13
COMMAND_SUGGESTION, // 0x14
CLIENT_INFORMATION, // 0x15
CLIENT_COMMAND, // 0x16
CUSTOM_PAYLOAD; // 0x17
@Override
public int getId() {
return ordinal();
}
@Override
public int getId() {
return ordinal();
}
@Override
public String getName() {
return name();
}
@Override
public String getName() {
return name();
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,13 +22,23 @@ import com.viaversion.viarewind.ViaRewind;
import com.viaversion.viarewind.api.data.RewindMappingData;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ServerboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.EntityPacketRewriter1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.*;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.CompressionHandlerProvider;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression.TrackingCompressionHandlerProvider;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.*;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.BlockItemPacketRewriter1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.EntityPacketRewriter1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.PlayerPacketRewriter1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.ScoreboardPacketRewriter1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.WorldPacketRewriter1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.CompressionStatusTracker;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.GameProfileStorage;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.InventoryTracker;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.PlayerSessionStorage;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.ScoreboardTracker;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.WorldBorderEmulator;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.task.WorldBorderUpdateTask;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolStorables;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
@@ -42,118 +52,115 @@ import com.viaversion.viaversion.protocols.base.ClientboundLoginPackets;
import com.viaversion.viaversion.protocols.base.ServerboundLoginPackets;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8;
import java.util.concurrent.TimeUnit;
public class Protocol1_8To1_7_6_10 extends BackwardsProtocol<ClientboundPackets1_8, ClientboundPackets1_7_2_5, ServerboundPackets1_8, ServerboundPackets1_7_2_5> {
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.8", "1.7.10");
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.8", "1.7.10");
private final BlockItemPacketRewriter1_8 itemRewriter = new BlockItemPacketRewriter1_8(this);
private final EntityPacketRewriter1_8 entityRewriter = new EntityPacketRewriter1_8(this);
private final BlockItemPacketRewriter1_8 itemRewriter = new BlockItemPacketRewriter1_8(this);
private final EntityPacketRewriter1_8 entityRewriter = new EntityPacketRewriter1_8(this);
public Protocol1_8To1_7_6_10() {
super(ClientboundPackets1_8.class, ClientboundPackets1_7_2_5.class, ServerboundPackets1_8.class, ServerboundPackets1_7_2_5.class);
}
public Protocol1_8To1_7_6_10() {
super(ClientboundPackets1_8.class, ClientboundPackets1_7_2_5.class, ServerboundPackets1_8.class, ServerboundPackets1_7_2_5.class);
}
@Override
protected void registerPackets() {
itemRewriter.register();
entityRewriter.register();
@Override
protected void registerPackets() {
itemRewriter.register();
entityRewriter.register();
new PlayerPacketRewriter1_8(this).register();
new ScoreboardPacketRewriter1_8(this).register();
new WorldPacketRewriter1_8(this).register();
new PlayerPacketRewriter1_8(this).register();
new ScoreboardPacketRewriter1_8(this).register();
new WorldPacketRewriter1_8(this).register();
this.registerClientbound(State.LOGIN, ClientboundLoginPackets.HELLO, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING); // server hash
map(Types.BYTE_ARRAY_PRIMITIVE, Types.SHORT_BYTE_ARRAY); // public key
map(Types.BYTE_ARRAY_PRIMITIVE, Types.SHORT_BYTE_ARRAY); // verification token
}
});
this.registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_COMPRESSION, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final int threshold = wrapper.read(Types.VAR_INT);
this.registerClientbound(State.LOGIN, ClientboundLoginPackets.HELLO, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING); // server hash
map(Types.BYTE_ARRAY_PRIMITIVE, Types.SHORT_BYTE_ARRAY); // public key
map(Types.BYTE_ARRAY_PRIMITIVE, Types.SHORT_BYTE_ARRAY); // verification token
}
});
this.registerClientbound(State.LOGIN, ClientboundLoginPackets.LOGIN_COMPRESSION, this::handleCompression);
this.registerClientbound(ClientboundPackets1_8.SET_COMPRESSION, null, this::handleCompression);
this.registerClientbound(ClientboundPackets1_8.KEEP_ALIVE, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT, Types.INT); // id
}
});
Via.getManager().getProviders().get(CompressionHandlerProvider.class).onHandleLoginCompressionPacket(wrapper.user(), threshold);
wrapper.cancel();
});
}
});
this.cancelClientbound(ClientboundPackets1_8.SET_COMPRESSION); // unused
this.registerClientbound(ClientboundPackets1_8.KEEP_ALIVE, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT, Types.INT); // id
}
});
this.registerServerbound(State.LOGIN, ServerboundLoginPackets.ENCRYPTION_KEY, new PacketHandlers() {
@Override
public void register() {
map(Types.SHORT_BYTE_ARRAY, Types.BYTE_ARRAY_PRIMITIVE); // shared secret
map(Types.SHORT_BYTE_ARRAY, Types.BYTE_ARRAY_PRIMITIVE); // verification token
}
});
this.registerServerbound(State.LOGIN, ServerboundLoginPackets.ENCRYPTION_KEY, new PacketHandlers() {
@Override
public void register() {
map(Types.SHORT_BYTE_ARRAY, Types.BYTE_ARRAY_PRIMITIVE); // shared secret
map(Types.SHORT_BYTE_ARRAY, Types.BYTE_ARRAY_PRIMITIVE); // verification token
}
});
this.registerServerbound(ServerboundPackets1_7_2_5.KEEP_ALIVE, new PacketHandlers() {
@Override
public void register() {
map(Types.INT, Types.VAR_INT); // id
}
});
}
this.registerServerbound(ServerboundPackets1_7_2_5.KEEP_ALIVE, new PacketHandlers() {
@Override
public void register() {
map(Types.INT, Types.VAR_INT); // id
}
});
}
private void handleCompression(final PacketWrapper wrapper) {
wrapper.cancel();
final int threshold = wrapper.read(Types.VAR_INT);
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws CancelException {
Via.getManager().getProviders().get(CompressionHandlerProvider.class).onTransformPacket(packetWrapper.user());
Via.getManager().getProviders().get(CompressionHandlerProvider.class).setCompressionThreshold(wrapper.user(), threshold);
}
super.transform(direction, state, packetWrapper);
}
@Override
public void transform(Direction direction, State state, PacketWrapper packetWrapper) throws CancelException {
Via.getManager().getProviders().get(CompressionHandlerProvider.class).onTransformPacket(packetWrapper.user());
@Override
public void init(UserConnection connection) {
connection.addEntityTracker(this.getClass(), new EntityTracker1_8(connection));
connection.addClientWorld(this.getClass(), new ClientWorld());
super.transform(direction, state, packetWrapper);
}
connection.put(new InventoryTracker(connection));
connection.put(new PlayerSessionStorage(connection));
connection.put(new GameProfileStorage(connection));
connection.put(new ScoreboardTracker(connection));
connection.put(new CompressionStatusTracker(connection));
connection.put(new WorldBorderEmulator(connection));
}
@Override
public void init(UserConnection connection) {
final ProtocolStorables storables = connection.storables(this);
storables.setEntityTracker(new EntityTracker1_8(connection));
storables.setClientWorld(new ClientWorld());
@Override
public void register(ViaProviders providers) {
providers.register(CompressionHandlerProvider.class, new TrackingCompressionHandlerProvider());
connection.put(new InventoryTracker(connection));
connection.put(new PlayerSessionStorage(connection));
connection.put(new GameProfileStorage(connection));
connection.put(new ScoreboardTracker(connection));
connection.put(new CompressionStatusTracker(connection));
connection.put(new WorldBorderEmulator(connection));
}
if (ViaRewind.getConfig().isEmulateWorldBorder()) {
Via.getManager().getScheduler().scheduleRepeating(new WorldBorderUpdateTask(), 0L, 50L, TimeUnit.MILLISECONDS);
}
}
@Override
public void register(ViaProviders providers) {
providers.register(CompressionHandlerProvider.class, new TrackingCompressionHandlerProvider());
@Override
public RewindMappingData getMappingData() {
return MAPPINGS;
}
if (ViaRewind.getConfig().isEmulateWorldBorder()) {
Via.getManager().getScheduler().scheduleRepeating(new WorldBorderUpdateTask(), 0L, 50L, TimeUnit.MILLISECONDS);
}
}
@Override
public BlockItemPacketRewriter1_8 getItemRewriter() {
return itemRewriter;
}
@Override
public RewindMappingData getMappingData() {
return MAPPINGS;
}
@Override
public EntityPacketRewriter1_8 getEntityRewriter() {
return entityRewriter;
}
@Override
public BlockItemPacketRewriter1_8 getItemRewriter() {
return itemRewriter;
}
@Override
public boolean hasMappingDataToLoad() {
return true;
}
@Override
public EntityPacketRewriter1_8 getEntityRewriter() {
return entityRewriter;
}
@Override
public boolean hasMappingDataToLoad() {
return true;
}
}
@@ -0,0 +1,425 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.DataItem;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
import com.viaversion.viaversion.libs.gson.JsonArray;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.libs.gson.JsonObject;
import com.viaversion.viaversion.util.ComponentUtil;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.SerializerVersion;
// TODO move this into a ComponentRewriter
@Deprecated
public final class ChatItemRewriter {
private static final Object2IntMap<String> NAME_TO_ID = new Object2IntOpenHashMap<>(336);
static {
NAME_TO_ID.put("stone", 1);
NAME_TO_ID.put("grass", 2);
NAME_TO_ID.put("dirt", 3);
NAME_TO_ID.put("cobblestone", 4);
NAME_TO_ID.put("planks", 5);
NAME_TO_ID.put("sapling", 6);
NAME_TO_ID.put("bedrock", 7);
NAME_TO_ID.put("sand", 12);
NAME_TO_ID.put("gravel", 13);
NAME_TO_ID.put("gold_ore", 14);
NAME_TO_ID.put("iron_ore", 15);
NAME_TO_ID.put("coal_ore", 16);
NAME_TO_ID.put("log", 17);
NAME_TO_ID.put("leaves", 18);
NAME_TO_ID.put("sponge", 19);
NAME_TO_ID.put("glass", 20);
NAME_TO_ID.put("lapis_ore", 21);
NAME_TO_ID.put("lapis_block", 22);
NAME_TO_ID.put("dispenser", 23);
NAME_TO_ID.put("sandstone", 24);
NAME_TO_ID.put("noteblock", 25);
NAME_TO_ID.put("golden_rail", 27);
NAME_TO_ID.put("detector_rail", 28);
NAME_TO_ID.put("sticky_piston", 29);
NAME_TO_ID.put("web", 30);
NAME_TO_ID.put("tallgrass", 31);
NAME_TO_ID.put("deadbush", 32);
NAME_TO_ID.put("piston", 33);
NAME_TO_ID.put("wool", 35);
NAME_TO_ID.put("yellow_flower", 37);
NAME_TO_ID.put("red_flower", 38);
NAME_TO_ID.put("brown_mushroom", 39);
NAME_TO_ID.put("red_mushroom", 40);
NAME_TO_ID.put("gold_block", 41);
NAME_TO_ID.put("iron_block", 42);
NAME_TO_ID.put("stone_slab", 44);
NAME_TO_ID.put("brick_block", 45);
NAME_TO_ID.put("tnt", 46);
NAME_TO_ID.put("bookshelf", 47);
NAME_TO_ID.put("mossy_cobblestone", 48);
NAME_TO_ID.put("obsidian", 49);
NAME_TO_ID.put("torch", 50);
NAME_TO_ID.put("mob_spawner", 52);
NAME_TO_ID.put("oak_stairs", 53);
NAME_TO_ID.put("chest", 54);
NAME_TO_ID.put("diamond_ore", 56);
NAME_TO_ID.put("diamond_block", 57);
NAME_TO_ID.put("crafting_table", 58);
NAME_TO_ID.put("farmland", 60);
NAME_TO_ID.put("furnace", 61);
NAME_TO_ID.put("ladder", 65);
NAME_TO_ID.put("rail", 66);
NAME_TO_ID.put("stone_stairs", 67);
NAME_TO_ID.put("lever", 69);
NAME_TO_ID.put("stone_pressure_plate", 70);
NAME_TO_ID.put("wooden_pressure_plate", 72);
NAME_TO_ID.put("redstone_ore", 73);
NAME_TO_ID.put("redstone_torch", 76);
NAME_TO_ID.put("stone_button", 77);
NAME_TO_ID.put("snow_layer", 78);
NAME_TO_ID.put("ice", 79);
NAME_TO_ID.put("snow", 80);
NAME_TO_ID.put("cactus", 81);
NAME_TO_ID.put("clay", 82);
NAME_TO_ID.put("jukebox", 84);
NAME_TO_ID.put("fence", 85);
NAME_TO_ID.put("pumpkin", 86);
NAME_TO_ID.put("netherrack", 87);
NAME_TO_ID.put("soul_sand", 88);
NAME_TO_ID.put("glowstone", 89);
NAME_TO_ID.put("lit_pumpkin", 91);
NAME_TO_ID.put("stained_glass", 95);
NAME_TO_ID.put("trapdoor", 96);
NAME_TO_ID.put("monster_egg", 97);
NAME_TO_ID.put("stonebrick", 98);
NAME_TO_ID.put("brown_mushroom_block", 99);
NAME_TO_ID.put("red_mushroom_block", 100);
NAME_TO_ID.put("iron_bars", 101);
NAME_TO_ID.put("glass_pane", 102);
NAME_TO_ID.put("melon_block", 103);
NAME_TO_ID.put("vine", 106);
NAME_TO_ID.put("fence_gate", 107);
NAME_TO_ID.put("brick_stairs", 108);
NAME_TO_ID.put("stone_brick_stairs", 109);
NAME_TO_ID.put("mycelium", 110);
NAME_TO_ID.put("waterlily", 111);
NAME_TO_ID.put("nether_brick", 112);
NAME_TO_ID.put("nether_brick_fence", 113);
NAME_TO_ID.put("nether_brick_stairs", 114);
NAME_TO_ID.put("enchanting_table", 116);
NAME_TO_ID.put("end_portal_frame", 120);
NAME_TO_ID.put("end_stone", 121);
NAME_TO_ID.put("dragon_egg", 122);
NAME_TO_ID.put("redstone_lamp", 123);
NAME_TO_ID.put("wooden_slab", 126);
NAME_TO_ID.put("sandstone_stairs", 128);
NAME_TO_ID.put("emerald_ore", 129);
NAME_TO_ID.put("ender_chest", 130);
NAME_TO_ID.put("tripwire_hook", 131);
NAME_TO_ID.put("emerald_block", 133);
NAME_TO_ID.put("spruce_stairs", 134);
NAME_TO_ID.put("birch_stairs", 135);
NAME_TO_ID.put("jungle_stairs", 136);
NAME_TO_ID.put("command_block", 137);
NAME_TO_ID.put("beacon", 138);
NAME_TO_ID.put("cobblestone_wall", 139);
NAME_TO_ID.put("wooden_button", 143);
NAME_TO_ID.put("anvil", 145);
NAME_TO_ID.put("trapped_chest", 146);
NAME_TO_ID.put("light_weighted_pressure_plate", 147);
NAME_TO_ID.put("heavy_weighted_pressure_plate", 148);
NAME_TO_ID.put("daylight_detector", 151);
NAME_TO_ID.put("redstone_block", 152);
NAME_TO_ID.put("quartz_ore", 153);
NAME_TO_ID.put("hopper", 154);
NAME_TO_ID.put("quartz_block", 155);
NAME_TO_ID.put("quartz_stairs", 156);
NAME_TO_ID.put("activator_rail", 157);
NAME_TO_ID.put("dropper", 158);
NAME_TO_ID.put("stained_hardened_clay", 159);
NAME_TO_ID.put("stained_glass_pane", 160);
NAME_TO_ID.put("leaves2", 161);
NAME_TO_ID.put("log2", 162);
NAME_TO_ID.put("acacia_stairs", 163);
NAME_TO_ID.put("dark_oak_stairs", 164);
NAME_TO_ID.put("slime", 165);
NAME_TO_ID.put("barrier", 166);
NAME_TO_ID.put("iron_trapdoor", 167);
NAME_TO_ID.put("prismarine", 168);
NAME_TO_ID.put("sea_lantern", 169);
NAME_TO_ID.put("hay_block", 170);
NAME_TO_ID.put("carpet", 171);
NAME_TO_ID.put("hardened_clay", 172);
NAME_TO_ID.put("coal_block", 173);
NAME_TO_ID.put("packed_ice", 174);
NAME_TO_ID.put("double_plant", 175);
NAME_TO_ID.put("red_sandstone", 179);
NAME_TO_ID.put("red_sandstone_stairs", 180);
NAME_TO_ID.put("stone_slab2", 182);
NAME_TO_ID.put("spruce_fence_gate", 183);
NAME_TO_ID.put("birch_fence_gate", 184);
NAME_TO_ID.put("jungle_fence_gate", 185);
NAME_TO_ID.put("dark_oak_fence_gate", 186);
NAME_TO_ID.put("acacia_fence_gate", 187);
NAME_TO_ID.put("spruce_fence", 188);
NAME_TO_ID.put("birch_fence", 189);
NAME_TO_ID.put("jungle_fence", 190);
NAME_TO_ID.put("dark_oak_fence", 191);
NAME_TO_ID.put("acacia_fence", 192);
NAME_TO_ID.put("iron_shovel", 256);
NAME_TO_ID.put("iron_pickaxe", 257);
NAME_TO_ID.put("iron_axe", 258);
NAME_TO_ID.put("flint_and_steel", 259);
NAME_TO_ID.put("apple", 260);
NAME_TO_ID.put("bow", 261);
NAME_TO_ID.put("arrow", 262);
NAME_TO_ID.put("coal", 263);
NAME_TO_ID.put("diamond", 264);
NAME_TO_ID.put("iron_ingot", 265);
NAME_TO_ID.put("gold_ingot", 266);
NAME_TO_ID.put("iron_sword", 267);
NAME_TO_ID.put("wooden_sword", 268);
NAME_TO_ID.put("wooden_shovel", 269);
NAME_TO_ID.put("wooden_pickaxe", 270);
NAME_TO_ID.put("wooden_axe", 271);
NAME_TO_ID.put("stone_sword", 272);
NAME_TO_ID.put("stone_shovel", 273);
NAME_TO_ID.put("stone_pickaxe", 274);
NAME_TO_ID.put("stone_axe", 275);
NAME_TO_ID.put("diamond_sword", 276);
NAME_TO_ID.put("diamond_shovel", 277);
NAME_TO_ID.put("diamond_pickaxe", 278);
NAME_TO_ID.put("diamond_axe", 279);
NAME_TO_ID.put("stick", 280);
NAME_TO_ID.put("bowl", 281);
NAME_TO_ID.put("mushroom_stew", 282);
NAME_TO_ID.put("golden_sword", 283);
NAME_TO_ID.put("golden_shovel", 284);
NAME_TO_ID.put("golden_pickaxe", 285);
NAME_TO_ID.put("golden_axe", 286);
NAME_TO_ID.put("string", 287);
NAME_TO_ID.put("feather", 288);
NAME_TO_ID.put("gunpowder", 289);
NAME_TO_ID.put("wooden_hoe", 290);
NAME_TO_ID.put("stone_hoe", 291);
NAME_TO_ID.put("iron_hoe", 292);
NAME_TO_ID.put("diamond_hoe", 293);
NAME_TO_ID.put("golden_hoe", 294);
NAME_TO_ID.put("wheat_seeds", 295);
NAME_TO_ID.put("wheat", 296);
NAME_TO_ID.put("bread", 297);
NAME_TO_ID.put("leather_helmet", 298);
NAME_TO_ID.put("leather_chestplate", 299);
NAME_TO_ID.put("leather_leggings", 300);
NAME_TO_ID.put("leather_boots", 301);
NAME_TO_ID.put("chainmail_helmet", 302);
NAME_TO_ID.put("chainmail_chestplate", 303);
NAME_TO_ID.put("chainmail_leggings", 304);
NAME_TO_ID.put("chainmail_boots", 305);
NAME_TO_ID.put("iron_helmet", 306);
NAME_TO_ID.put("iron_chestplate", 307);
NAME_TO_ID.put("iron_leggings", 308);
NAME_TO_ID.put("iron_boots", 309);
NAME_TO_ID.put("diamond_helmet", 310);
NAME_TO_ID.put("diamond_chestplate", 311);
NAME_TO_ID.put("diamond_leggings", 312);
NAME_TO_ID.put("diamond_boots", 313);
NAME_TO_ID.put("golden_helmet", 314);
NAME_TO_ID.put("golden_chestplate", 315);
NAME_TO_ID.put("golden_leggings", 316);
NAME_TO_ID.put("golden_boots", 317);
NAME_TO_ID.put("flint", 318);
NAME_TO_ID.put("porkchop", 319);
NAME_TO_ID.put("cooked_porkchop", 320);
NAME_TO_ID.put("painting", 321);
NAME_TO_ID.put("golden_apple", 322);
NAME_TO_ID.put("sign", 323);
NAME_TO_ID.put("wooden_door", 324);
NAME_TO_ID.put("bucket", 325);
NAME_TO_ID.put("water_bucket", 326);
NAME_TO_ID.put("lava_bucket", 327);
NAME_TO_ID.put("minecart", 328);
NAME_TO_ID.put("saddle", 329);
NAME_TO_ID.put("iron_door", 330);
NAME_TO_ID.put("redstone", 331);
NAME_TO_ID.put("snowball", 332);
NAME_TO_ID.put("boat", 333);
NAME_TO_ID.put("leather", 334);
NAME_TO_ID.put("milk_bucket", 335);
NAME_TO_ID.put("brick", 336);
NAME_TO_ID.put("clay_ball", 337);
NAME_TO_ID.put("reeds", 338);
NAME_TO_ID.put("paper", 339);
NAME_TO_ID.put("book", 340);
NAME_TO_ID.put("slime_ball", 341);
NAME_TO_ID.put("chest_minecart", 342);
NAME_TO_ID.put("furnace_minecart", 343);
NAME_TO_ID.put("egg", 344);
NAME_TO_ID.put("compass", 345);
NAME_TO_ID.put("fishing_rod", 346);
NAME_TO_ID.put("clock", 347);
NAME_TO_ID.put("glowstone_dust", 348);
NAME_TO_ID.put("fish", 349);
NAME_TO_ID.put("cooked_fish", 350);
NAME_TO_ID.put("dye", 351);
NAME_TO_ID.put("bone", 352);
NAME_TO_ID.put("sugar", 353);
NAME_TO_ID.put("cake", 354);
NAME_TO_ID.put("bed", 355);
NAME_TO_ID.put("repeater", 356);
NAME_TO_ID.put("cookie", 357);
NAME_TO_ID.put("filled_map", 358);
NAME_TO_ID.put("shears", 359);
NAME_TO_ID.put("melon", 360);
NAME_TO_ID.put("pumpkin_seeds", 361);
NAME_TO_ID.put("melon_seeds", 362);
NAME_TO_ID.put("beef", 363);
NAME_TO_ID.put("cooked_beef", 364);
NAME_TO_ID.put("chicken", 365);
NAME_TO_ID.put("cooked_chicken", 366);
NAME_TO_ID.put("rotten_flesh", 367);
NAME_TO_ID.put("ender_pearl", 368);
NAME_TO_ID.put("blaze_rod", 369);
NAME_TO_ID.put("ghast_tear", 370);
NAME_TO_ID.put("gold_nugget", 371);
NAME_TO_ID.put("nether_wart", 372);
NAME_TO_ID.put("potion", 373);
NAME_TO_ID.put("glass_bottle", 374);
NAME_TO_ID.put("spider_eye", 375);
NAME_TO_ID.put("fermented_spider_eye", 376);
NAME_TO_ID.put("blaze_powder", 377);
NAME_TO_ID.put("magma_cream", 378);
NAME_TO_ID.put("brewing_stand", 379);
NAME_TO_ID.put("cauldron", 380);
NAME_TO_ID.put("ender_eye", 381);
NAME_TO_ID.put("speckled_melon", 382);
NAME_TO_ID.put("spawn_egg", 383);
NAME_TO_ID.put("experience_bottle", 384);
NAME_TO_ID.put("fire_charge", 385);
NAME_TO_ID.put("writable_book", 386);
NAME_TO_ID.put("written_book", 387);
NAME_TO_ID.put("emerald", 388);
NAME_TO_ID.put("item_frame", 389);
NAME_TO_ID.put("flower_pot", 390);
NAME_TO_ID.put("carrot", 391);
NAME_TO_ID.put("potato", 392);
NAME_TO_ID.put("baked_potato", 393);
NAME_TO_ID.put("poisonous_potato", 394);
NAME_TO_ID.put("map", 395);
NAME_TO_ID.put("golden_carrot", 396);
NAME_TO_ID.put("skull", 397);
NAME_TO_ID.put("carrot_on_a_stick", 398);
NAME_TO_ID.put("nether_star", 399);
NAME_TO_ID.put("pumpkin_pie", 400);
NAME_TO_ID.put("fireworks", 401);
NAME_TO_ID.put("firework_charge", 402);
NAME_TO_ID.put("enchanted_book", 403);
NAME_TO_ID.put("comparator", 404);
NAME_TO_ID.put("netherbrick", 405);
NAME_TO_ID.put("quartz", 406);
NAME_TO_ID.put("tnt_minecart", 407);
NAME_TO_ID.put("hopper_minecart", 408);
NAME_TO_ID.put("prismarine_shard", 409);
NAME_TO_ID.put("prismarine_crystals", 410);
NAME_TO_ID.put("rabbit", 411);
NAME_TO_ID.put("cooked_rabbit", 412);
NAME_TO_ID.put("rabbit_stew", 413);
NAME_TO_ID.put("rabbit_foot", 414);
NAME_TO_ID.put("rabbit_hide", 415);
NAME_TO_ID.put("armor_stand", 416);
NAME_TO_ID.put("iron_horse_armor", 417);
NAME_TO_ID.put("golden_horse_armor", 418);
NAME_TO_ID.put("diamond_horse_armor", 419);
NAME_TO_ID.put("lead", 420);
NAME_TO_ID.put("name_tag", 421);
NAME_TO_ID.put("command_block_minecart", 422);
NAME_TO_ID.put("mutton", 423);
NAME_TO_ID.put("cooked_mutton", 424);
NAME_TO_ID.put("banner", 425);
NAME_TO_ID.put("spruce_door", 427);
NAME_TO_ID.put("birch_door", 428);
NAME_TO_ID.put("jungle_door", 429);
NAME_TO_ID.put("acacia_door", 430);
NAME_TO_ID.put("dark_oak_door", 431);
NAME_TO_ID.put("record_13", 2256);
NAME_TO_ID.put("record_cat", 2257);
NAME_TO_ID.put("record_blocks", 2258);
NAME_TO_ID.put("record_chirp", 2259);
NAME_TO_ID.put("record_far", 2260);
NAME_TO_ID.put("record_mall", 2261);
NAME_TO_ID.put("record_mellohi", 2262);
NAME_TO_ID.put("record_stal", 2263);
NAME_TO_ID.put("record_strad", 2264);
NAME_TO_ID.put("record_ward", 2265);
NAME_TO_ID.put("record_11", 2266);
NAME_TO_ID.put("record_wait", 2267);
}
public static void toClient(final Protocol1_8To1_7_6_10 protocol, final UserConnection connection, JsonElement element) {
if (element instanceof final JsonObject obj) {
if (obj.has("hoverEvent")) {
if (!(obj.get("hoverEvent") instanceof final JsonObject hoverEvent)) {
return;
}
if (!hoverEvent.has("action") || !hoverEvent.has("value")) {
return;
}
final String type = hoverEvent.get("action").getAsString();
if (!type.equals("show_item")) {
return;
}
final CompoundTag compound = ComponentUtil.deserializeLegacyShowItem(hoverEvent.get("value"), SerializerVersion.V1_8);
final int identifier;
final String stringId = compound.getString("id");
if (stringId != null) {
identifier = NAME_TO_ID.getOrDefault(Key.stripMinecraftNamespace(stringId), compound.getShort("id"));
} else {
identifier = compound.getShort("id");
}
final CompoundTag itemTag = compound.getCompoundTag("tag");
final Item dataItem = new DataItem(identifier, (byte) 1, (short) 0, itemTag);
protocol.getItemRewriter().handleItemToClient(connection, dataItem);
compound.putInt("id", dataItem.identifier());
if (dataItem.tag() != itemTag) {
compound.put("tag", dataItem.tag());
}
hoverEvent.addProperty("value", SerializerVersion.V1_7.toSNBT(compound));
} else if (obj.has("extra")) {
toClient(protocol, connection, obj.get("extra"));
} else if (obj.has("translate") && obj.has("with")) {
toClient(protocol, connection, obj.get("with"));
}
} else if (element instanceof final JsonArray array) {
for (JsonElement value : array) {
toClient(protocol, connection, value);
}
}
}
}
@@ -0,0 +1,234 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data;
import com.viaversion.viaversion.libs.gson.JsonArray;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.viaversion.libs.gson.JsonObject;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class ChatNewlineRewriter {
private static final String[] STYLE_KEYS = {
"color",
"bold",
"italic",
"underlined",
"strikethrough",
"obfuscated",
"clickEvent",
"hoverEvent",
"insertion"
};
public static List<JsonObject> splitChatComponentByNewline(JsonElement element) {
return splitChatComponentByNewline(element, new JsonObject());
}
private static List<JsonObject> splitChatComponentByNewline(JsonElement element, JsonObject inheritedStyle) {
List<JsonObject> results = new ArrayList<>();
if (element == null || element.isJsonNull()) {
return results;
}
if (element.isJsonPrimitive()) {
String text = element.getAsString();
for (String part : text.split("\n", -1)) {
JsonObject obj = inheritedStyle.deepCopy();
obj.addProperty("text", part);
results.add(obj);
}
return results;
}
if (element.isJsonArray()) {
List<JsonObject> currentLine = new ArrayList<>();
JsonObject siblingStyle = inheritedStyle;
boolean foundRoot = false;
for (JsonElement child : element.getAsJsonArray()) {
List<JsonObject> splitParts = splitChatComponentByNewline(child, siblingStyle);
for (int i = 0; i < splitParts.size(); i++) {
currentLine.add(splitParts.get(i));
if (i < splitParts.size() - 1) {
results.add(mergeLine(currentLine));
currentLine.clear();
}
}
if (!foundRoot && child != null && !child.isJsonNull()) {
siblingStyle = rootStyle(inheritedStyle, child);
foundRoot = true;
}
}
if (!currentLine.isEmpty()) {
results.add(mergeLine(currentLine));
}
return results;
}
if (!element.isJsonObject()) {
return results;
}
JsonObject obj = element.getAsJsonObject();
// Build the style for this node: inherit first, then override with this object's style
JsonObject currentStyle = mergeStyle(inheritedStyle, obj);
// Gather components in order: base text then extra array
List<JsonElement> ordered = new ArrayList<>();
if (obj.has("text")) {
ordered.add(obj.get("text"));
}
if (obj.has("extra") && obj.get("extra").isJsonArray()) {
for (JsonElement extra : obj.getAsJsonArray("extra")) {
ordered.add(extra);
}
}
// If there is no text/extra, return a single object with the style
if (ordered.isEmpty()) {
JsonObject baseComponent = copyComponentWithoutExtra(obj, currentStyle);
results.add(baseComponent != null ? baseComponent : currentStyle);
return results;
}
// Build lines by concatenating split pieces
List<JsonObject> currentLine = new ArrayList<>();
if (!obj.has("text")) {
JsonObject baseComponent = copyComponentWithoutExtra(obj, currentStyle);
if (baseComponent != null) {
currentLine.add(baseComponent);
}
}
for (JsonElement part : ordered) {
if (part == null || part.isJsonNull()) {
continue;
}
if (part.isJsonPrimitive()) {
String text = part.getAsString();
String[] split = text.split("\n", -1);
for (int i = 0; i < split.length; i++) {
JsonObject piece = currentStyle.deepCopy();
piece.addProperty("text", split[i]);
currentLine.add(piece);
if (i < split.length - 1) {
results.add(mergeLine(currentLine));
currentLine.clear();
}
}
continue;
}
if (part.isJsonObject() || part.isJsonArray()) {
List<JsonObject> splitParts = splitChatComponentByNewline(part, currentStyle);
for (int i = 0; i < splitParts.size(); i++) {
currentLine.add(splitParts.get(i));
if (i < splitParts.size() - 1) {
results.add(mergeLine(currentLine));
currentLine.clear();
}
}
}
}
if (!currentLine.isEmpty()) {
results.add(mergeLine(currentLine));
}
return results;
}
private static JsonObject rootStyle(JsonObject inheritedStyle, JsonElement element) {
if (element == null || element.isJsonNull()) {
return inheritedStyle;
}
if (element.isJsonObject()) {
return mergeStyle(inheritedStyle, element.getAsJsonObject());
}
if (element.isJsonArray()) {
for (JsonElement child : element.getAsJsonArray()) {
if (child != null && !child.isJsonNull()) {
return rootStyle(inheritedStyle, child);
}
}
}
return inheritedStyle;
}
private static JsonObject mergeStyle(JsonObject inheritedStyle, JsonObject obj) {
JsonObject currentStyle = inheritedStyle.deepCopy();
for (String key : STYLE_KEYS) {
if (obj.has(key)) {
currentStyle.add(key, obj.get(key).deepCopy());
}
}
return currentStyle;
}
private static JsonObject copyComponentWithoutExtra(JsonObject obj, JsonObject currentStyle) {
JsonObject component = currentStyle.deepCopy();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
String key = entry.getKey();
if ("extra".equals(key) || isStyleKey(key)) {
continue;
}
component.add(key, entry.getValue().deepCopy());
}
return component.size() == currentStyle.size() ? null : component;
}
private static boolean isStyleKey(String key) {
for (String styleKey : STYLE_KEYS) {
if (styleKey.equals(key)) {
return true;
}
}
return false;
}
private static JsonObject mergeLine(List<JsonObject> lineParts) {
if (lineParts.isEmpty()) {
JsonObject empty = new JsonObject();
empty.addProperty("text", "");
return empty;
}
JsonObject first = lineParts.get(0);
if (lineParts.size() == 1) {
return first;
}
JsonObject merged = new JsonObject();
merged.addProperty("text", "");
JsonArray extra = new JsonArray();
for (JsonObject linePart : lineParts) {
extra.add(linePart);
}
merged.add("extra", extra);
return merged;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,194 +22,226 @@ import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_8;
import com.viaversion.viaversion.util.Pair;
import java.util.HashMap;
import java.util.Optional;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.*;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ABSTRACT_AGEABLE;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ABSTRACT_MINECART;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ARMOR_STAND;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ARROW;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.BAT;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.BLAZE;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.BOAT;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.CREEPER;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ENDERMAN;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.END_CRYSTAL;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ENTITY;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.FIREWORK_ROCKET;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.FURNACE_MINECART;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.GHAST;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.GUARDIAN;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.HORSE;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.IRON_GOLEM;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ITEM;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ITEM_FRAME;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.LIVING_ENTITY;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.LIVING_ENTITY_BASE;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.OCELOT;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.PIG;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.PLAYER;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.SHEEP;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.SKELETON;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.SLIME;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.SPIDER;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.TAMABLE_ANIMAL;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.VILLAGER;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.WITCH;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.WITHER;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.WOLF;
import static com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8.EntityType.ZOMBIE;
public enum EntityDataIndex1_7_6_10 {
ENTITY_FLAGS(ENTITY, 0, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ENTITY_AIR(ENTITY, 1, EntityDataTypes1_7_6_10.SHORT, EntityDataTypes1_8.SHORT),
ENTITY_NAME_TAG(ENTITY, -1, null, 2, EntityDataTypes1_8.STRING),
ENTITY_NAME_TAG_VISIBILITY(ENTITY, -1, null, 3, EntityDataTypes1_8.BYTE),
ENTITY_SILENT(ENTITY, -1, null, 4, EntityDataTypes1_8.BYTE),
ENTITY_FLAGS(ENTITY, 0, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ENTITY_AIR(ENTITY, 1, EntityDataTypes1_7_6_10.SHORT, EntityDataTypes1_8.SHORT),
ENTITY_NAME_TAG(ENTITY, -1, null, 2, EntityDataTypes1_8.STRING),
ENTITY_NAME_TAG_VISIBILITY(ENTITY, -1, null, 3, EntityDataTypes1_8.BYTE),
ENTITY_SILENT(ENTITY, -1, null, 4, EntityDataTypes1_8.BYTE),
LIVING_ENTITY_BASE_HEALTH(LIVING_ENTITY_BASE, 6, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
LIVING_ENTITY_BASE_POTION_EFFECT_COLOR(LIVING_ENTITY_BASE, 7, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
LIVING_ENTITY_BASE_IS_POTION_EFFECT_AMBIENT(LIVING_ENTITY_BASE, 8, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
LIVING_ENTITY_BASE_ARROWS(LIVING_ENTITY_BASE, 9, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
LIVING_ENTITY_BASE_NAME_TAG(LIVING_ENTITY_BASE, 10, EntityDataTypes1_7_6_10.STRING, 2, EntityDataTypes1_8.STRING),
LIVING_ENTITY_BASE_NAME_TAG_VISIBILITY(LIVING_ENTITY_BASE, 11, EntityDataTypes1_7_6_10.BYTE, 3, EntityDataTypes1_8.BYTE),
LIVING_ENTITY_BASE_HEALTH(LIVING_ENTITY_BASE, 6, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
LIVING_ENTITY_BASE_POTION_EFFECT_COLOR(LIVING_ENTITY_BASE, 7, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
LIVING_ENTITY_BASE_IS_POTION_EFFECT_AMBIENT(LIVING_ENTITY_BASE, 8, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
LIVING_ENTITY_BASE_ARROWS(LIVING_ENTITY_BASE, 9, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
LIVING_ENTITY_BASE_NAME_TAG(LIVING_ENTITY_BASE, 10, EntityDataTypes1_7_6_10.STRING, 2, EntityDataTypes1_8.STRING),
LIVING_ENTITY_BASE_NAME_TAG_VISIBILITY(LIVING_ENTITY_BASE, 11, EntityDataTypes1_7_6_10.BYTE, 3, EntityDataTypes1_8.BYTE),
LIVING_ENTITY_AI(LIVING_ENTITY, -1, null, 15, EntityDataTypes1_8.BYTE),
LIVING_ENTITY_AI(LIVING_ENTITY, -1, null, 15, EntityDataTypes1_8.BYTE),
ABSTRACT_AGEABLE_AGE(ABSTRACT_AGEABLE, 12, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.BYTE),
ABSTRACT_AGEABLE_AGE(ABSTRACT_AGEABLE, 12, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.BYTE),
ARMOR_STAND_FLAGS(ARMOR_STAND, -1, null, 10, EntityDataTypes1_8.BYTE),
ARMOR_STAND_HEAD_POSITION(ARMOR_STAND, -1, null, 11, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_BODY_POSITION(ARMOR_STAND, -1, null, 12, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_LEFT_ARM_POSITION(ARMOR_STAND, -1, null, 13, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_RIGHT_ARM_POSITION(ARMOR_STAND, -1, null, 14, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_LEFT_LEG_POSITION(ARMOR_STAND, -1, null, 15, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_RIGHT_LEG_POSITION(ARMOR_STAND, -1, null, 16, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_FLAGS(ARMOR_STAND, -1, null, 10, EntityDataTypes1_8.BYTE),
ARMOR_STAND_HEAD_POSITION(ARMOR_STAND, -1, null, 11, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_BODY_POSITION(ARMOR_STAND, -1, null, 12, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_LEFT_ARM_POSITION(ARMOR_STAND, -1, null, 13, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_RIGHT_ARM_POSITION(ARMOR_STAND, -1, null, 14, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_LEFT_LEG_POSITION(ARMOR_STAND, -1, null, 15, EntityDataTypes1_8.ROTATIONS),
ARMOR_STAND_RIGHT_LEG_POSITION(ARMOR_STAND, -1, null, 16, EntityDataTypes1_8.ROTATIONS),
PLAYER_SKIN_FLAGS(PLAYER, 16, EntityDataTypes1_7_6_10.BYTE, 10, EntityDataTypes1_8.BYTE),
PLAYER_UNUSED(PLAYER, -1, null, 16, EntityDataTypes1_8.BYTE),
PLAYER_ABSORPTION_HEATS(PLAYER, 17, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
PLAYER_SCORE(PLAYER, 18, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
PLAYER_SKIN_FLAGS(PLAYER, 16, EntityDataTypes1_7_6_10.BYTE, 10, EntityDataTypes1_8.BYTE),
PLAYER_UNUSED(PLAYER, -1, null, 16, EntityDataTypes1_8.BYTE),
PLAYER_ABSORPTION_HEATS(PLAYER, 17, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
PLAYER_SCORE(PLAYER, 18, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
HORSE_FLAGS(HORSE, 16, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
HORSE_TYPE(HORSE, 19, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
HORSE_COLOR(HORSE, 20, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
HORSE_OWNER(HORSE, 21, EntityDataTypes1_7_6_10.STRING, EntityDataTypes1_8.STRING),
HORSE_ARMOR(HORSE, 22, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
HORSE_FLAGS(HORSE, 16, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
HORSE_TYPE(HORSE, 19, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
HORSE_COLOR(HORSE, 20, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
HORSE_OWNER(HORSE, 21, EntityDataTypes1_7_6_10.STRING, EntityDataTypes1_8.STRING),
HORSE_ARMOR(HORSE, 22, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
BAT_HANGING(BAT, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
BAT_HANGING(BAT, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
TAMABLE_ANIMAL_FLAGS(TAMABLE_ANIMAL, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
TAMABLE_ANIMAL_OWNER(TAMABLE_ANIMAL, 17, EntityDataTypes1_7_6_10.STRING, EntityDataTypes1_8.STRING),
TAMABLE_ANIMAL_FLAGS(TAMABLE_ANIMAL, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
TAMABLE_ANIMAL_OWNER(TAMABLE_ANIMAL, 17, EntityDataTypes1_7_6_10.STRING, EntityDataTypes1_8.STRING),
OCELOT_TYPE(OCELOT, 18, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
OCELOT_TYPE(OCELOT, 18, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WOLF_FLAGS(WOLF, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WOLF_HEALTH(WOLF, 18, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
WOLF_BEGGING(WOLF, 19, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WOLF_COLLAR_COLOR(WOLF, 20, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WOLF_FLAGS(WOLF, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WOLF_HEALTH(WOLF, 18, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
WOLF_BEGGING(WOLF, 19, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WOLF_COLLAR_COLOR(WOLF, 20, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
PIG_SADDLE(PIG, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
PIG_SADDLE(PIG, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
SHEEP_COLOR_OR_SHEARED(SHEEP, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
SHEEP_COLOR_OR_SHEARED(SHEEP, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
VILLAGER_TYPE(VILLAGER, 16, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
VILLAGER_TYPE(VILLAGER, 16, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ENDERMAN_CARRIED_BLOCK(ENDERMAN, 16, null, EntityDataTypes1_8.SHORT),
ENDERMAN_CARRIED_BLOCK_DATA(ENDERMAN, 17, null, EntityDataTypes1_8.BYTE),
ENDERMAN_IS_SCREAMING(ENDERMAN, 18, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ENDERMAN_CARRIED_BLOCK(ENDERMAN, 16, null, EntityDataTypes1_8.SHORT),
ENDERMAN_CARRIED_BLOCK_DATA(ENDERMAN, 17, null, EntityDataTypes1_8.BYTE),
ENDERMAN_IS_SCREAMING(ENDERMAN, 18, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ZOMBIE_CHILD(ZOMBIE, 12, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ZOMBIE_VILLAGER(ZOMBIE, 13, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ZOMBIE_CONVERTING(ZOMBIE, 14, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ZOMBIE_CHILD(ZOMBIE, 12, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ZOMBIE_VILLAGER(ZOMBIE, 13, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ZOMBIE_CONVERTING(ZOMBIE, 14, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
BLAZE_ON_FIRE(BLAZE, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
BLAZE_ON_FIRE(BLAZE, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
SPIDER_CLIMBING(SPIDER, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
SPIDER_CLIMBING(SPIDER, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
CREEPER_STATE(CREEPER, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
CREEPER_POWERED(CREEPER, 17, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
CREEPER_IGNITED(CREEPER, 18, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
CREEPER_STATE(CREEPER, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
CREEPER_POWERED(CREEPER, 17, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
CREEPER_IGNITED(CREEPER, 18, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
GHAST_STATE(GHAST, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
GHAST_IS_POWERED(GHAST, 17, null, EntityDataTypes1_8.BYTE),
GHAST_STATE(GHAST, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
GHAST_IS_POWERED(GHAST, 17, null, EntityDataTypes1_8.BYTE),
SLIME_SIZE(SLIME, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
SLIME_SIZE(SLIME, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
SKELETON_TYPE(SKELETON, 13, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
SKELETON_TYPE(SKELETON, 13, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WITCH_AGGRESSIVE(WITCH, 21, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WITCH_AGGRESSIVE(WITCH, 21, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
IRON_GOLEM_IS_PLAYER_CREATED(IRON_GOLEM, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
IRON_GOLEM_IS_PLAYER_CREATED(IRON_GOLEM, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
WITHER_WATCHED_TARGET_1(WITHER, 17, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
WITHER_WATCHED_TARGET_2(WITHER, 18, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
WITHER_WATCHED_TARGET_3(WITHER, 19, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
WITHER_INVULNERABLE_TIME(WITHER, 20, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
WITHER_WATCHED_TARGET_1(WITHER, 17, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
WITHER_WATCHED_TARGET_2(WITHER, 18, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
WITHER_WATCHED_TARGET_3(WITHER, 19, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
WITHER_INVULNERABLE_TIME(WITHER, 20, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
GUARDIAN_FLAGS(GUARDIAN, 16, null, EntityDataTypes1_8.BYTE),
GUARDIAN_TARGET(GUARDIAN, 17, null, EntityDataTypes1_8.INT),
GUARDIAN_FLAGS(GUARDIAN, 16, null, EntityDataTypes1_8.BYTE),
GUARDIAN_TARGET(GUARDIAN, 17, null, EntityDataTypes1_8.INT),
BOAT_TIME_SINCE_HIT(BOAT, 17, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
BOAT_FORWARD_DIRECTION(BOAT, 18, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
BOAT_DAMAGE_TAKEN(BOAT, 19, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
BOAT_TIME_SINCE_HIT(BOAT, 17, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
BOAT_FORWARD_DIRECTION(BOAT, 18, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
BOAT_DAMAGE_TAKEN(BOAT, 19, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
ABSTRACT_MINECART_SHAKING_POWER(ABSTRACT_MINECART, 17, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ABSTRACT_MINECART_SHAKING_DIRECTION(ABSTRACT_MINECART, 18, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ABSTRACT_MINECART_DAMAGE_TAKEN(ABSTRACT_MINECART, 19, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
ABSTRACT_MINECART_BLOCK_INSIDE(ABSTRACT_MINECART, 20, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ABSTRACT_MINECART_BLOCK_Y(ABSTRACT_MINECART, 21, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ABSTRACT_MINECART_SHOW_BLOCK(ABSTRACT_MINECART, 22, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ABSTRACT_MINECART_SHAKING_POWER(ABSTRACT_MINECART, 17, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ABSTRACT_MINECART_SHAKING_DIRECTION(ABSTRACT_MINECART, 18, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ABSTRACT_MINECART_DAMAGE_TAKEN(ABSTRACT_MINECART, 19, EntityDataTypes1_7_6_10.FLOAT, EntityDataTypes1_8.FLOAT),
ABSTRACT_MINECART_BLOCK_INSIDE(ABSTRACT_MINECART, 20, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ABSTRACT_MINECART_BLOCK_Y(ABSTRACT_MINECART, 21, EntityDataTypes1_7_6_10.INT, EntityDataTypes1_8.INT),
ABSTRACT_MINECART_SHOW_BLOCK(ABSTRACT_MINECART, 22, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
FURNACE_MINECART_IS_POWERED(FURNACE_MINECART, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
FURNACE_MINECART_IS_POWERED(FURNACE_MINECART, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ITEM_ITEM(ITEM, 10, EntityDataTypes1_7_6_10.ITEM, EntityDataTypes1_8.ITEM),
ITEM_ITEM(ITEM, 10, EntityDataTypes1_7_6_10.ITEM, EntityDataTypes1_8.ITEM),
ARROW_IS_CRITICAL(ARROW, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
ARROW_IS_CRITICAL(ARROW, 16, EntityDataTypes1_7_6_10.BYTE, EntityDataTypes1_8.BYTE),
FIREWORK_ROCKET_INFO(FIREWORK_ROCKET, 8, EntityDataTypes1_7_6_10.ITEM, EntityDataTypes1_8.ITEM),
FIREWORK_ROCKET_INFO(FIREWORK_ROCKET, 8, EntityDataTypes1_7_6_10.ITEM, EntityDataTypes1_8.ITEM),
ITEM_FRAME_ITEM(ITEM_FRAME, 2, EntityDataTypes1_7_6_10.ITEM, 8, EntityDataTypes1_8.ITEM),
ITEM_FRAME_ROTATION(ITEM_FRAME, 3, EntityDataTypes1_7_6_10.BYTE, 9, EntityDataTypes1_8.BYTE),
ITEM_FRAME_ITEM(ITEM_FRAME, 2, EntityDataTypes1_7_6_10.ITEM, 8, EntityDataTypes1_8.ITEM),
ITEM_FRAME_ROTATION(ITEM_FRAME, 3, EntityDataTypes1_7_6_10.BYTE, 9, EntityDataTypes1_8.BYTE),
END_CRYSTAL_HEALTH(END_CRYSTAL, 8, EntityDataTypes1_7_6_10.INT, 9, EntityDataTypes1_8.INT);
END_CRYSTAL_HEALTH(END_CRYSTAL, 8, EntityDataTypes1_7_6_10.INT, 9, EntityDataTypes1_8.INT);
private static final HashMap<Pair<EntityTypes1_8.EntityType, Integer>, EntityDataIndex1_7_6_10> ENTITY_DATA_REWRITES = new HashMap<>();
private static final HashMap<Pair<EntityTypes1_8.EntityType, Integer>, EntityDataIndex1_7_6_10> ENTITY_DATA_REWRITES = new HashMap<>();
static {
for (EntityDataIndex1_7_6_10 index : EntityDataIndex1_7_6_10.values()) {
ENTITY_DATA_REWRITES.put(new Pair<>(index.getClazz(), index.getNewIndex()), index);
}
}
static {
for (EntityDataIndex1_7_6_10 index : EntityDataIndex1_7_6_10.values()) {
ENTITY_DATA_REWRITES.put(new Pair<>(index.getClazz(), index.getNewIndex()), index);
}
}
private final EntityTypes1_8.EntityType clazz;
private final int newIndex;
private final EntityDataTypes1_8 newType;
private final EntityDataTypes1_7_6_10 oldType;
private final int index;
private final EntityTypes1_8.EntityType clazz;
private final int newIndex;
private final EntityDataTypes1_8 newType;
private final EntityDataTypes1_7_6_10 oldType;
private final int index;
EntityDataIndex1_7_6_10(EntityTypes1_8.EntityType type, int index, EntityDataTypes1_7_6_10 oldType, EntityDataTypes1_8 newType) {
this.clazz = type;
this.index = index;
this.newIndex = index;
this.oldType = oldType;
this.newType = newType;
}
EntityDataIndex1_7_6_10(EntityTypes1_8.EntityType type, int index, EntityDataTypes1_7_6_10 oldType, EntityDataTypes1_8 newType) {
this.clazz = type;
this.index = index;
this.newIndex = index;
this.oldType = oldType;
this.newType = newType;
}
EntityDataIndex1_7_6_10(EntityTypes1_8.EntityType type, int index, EntityDataTypes1_7_6_10 oldType, int newIndex, EntityDataTypes1_8 newType) {
this.clazz = type;
this.index = index;
this.oldType = oldType;
this.newIndex = newIndex;
this.newType = newType;
}
EntityDataIndex1_7_6_10(EntityTypes1_8.EntityType type, int index, EntityDataTypes1_7_6_10 oldType, int newIndex, EntityDataTypes1_8 newType) {
this.clazz = type;
this.index = index;
this.oldType = oldType;
this.newIndex = newIndex;
this.newType = newType;
}
private static Optional<EntityDataIndex1_7_6_10> getIndex(EntityType type, int index) {
final Pair<EntityType, Integer> pair = new Pair<>(type, index);
return Optional.ofNullable(ENTITY_DATA_REWRITES.get(pair));
}
private static Optional<EntityDataIndex1_7_6_10> getIndex(EntityType type, int index) {
final Pair<EntityType, Integer> pair = new Pair<>(type, index);
return Optional.ofNullable(ENTITY_DATA_REWRITES.get(pair));
}
public EntityTypes1_8.EntityType getClazz() {
return clazz;
}
public static EntityDataIndex1_7_6_10 searchIndex(EntityType type, int index) {
EntityType currentType = type;
do {
Optional<EntityDataIndex1_7_6_10> optMeta = getIndex(currentType, index);
public int getNewIndex() {
return newIndex;
}
if (optMeta.isPresent()) {
return optMeta.get();
}
public EntityDataTypes1_8 getNewType() {
return newType;
}
currentType = currentType.getParent();
} while (currentType != null);
public EntityDataTypes1_7_6_10 getOldType() {
return oldType;
}
return null;
}
public int getIndex() {
return index;
}
public EntityTypes1_8.EntityType getClazz() {
return clazz;
}
public static EntityDataIndex1_7_6_10 searchIndex(EntityType type, int index) {
EntityType currentType = type;
do {
Optional<EntityDataIndex1_7_6_10> optMeta = getIndex(currentType, index);
public int getNewIndex() {
return newIndex;
}
if (optMeta.isPresent()) {
return optMeta.get();
}
public EntityDataTypes1_8 getNewType() {
return newType;
}
currentType = currentType.getParent();
} while (currentType != null);
public EntityDataTypes1_7_6_10 getOldType() {
return oldType;
}
return null;
}
}
public int getIndex() {
return index;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,77 +20,78 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data;
import java.util.HashMap;
public enum Particles1_8 {
EXPLOSION_NORMAL("explode"),
EXPLOSION_LARGE("largeexplode"),
EXPLOSION_HUGE("hugeexplosion"),
FIREWORKS_SPARK("fireworksSpark"),
WATER_BUBBLE("bubble"),
WATER_SPLASH("splash"),
WATER_WAKE("wake"),
SUSPENDED("suspended"),
SUSPENDED_DEPTH("depthsuspend"),
CRIT("crit"),
CRIT_MAGIC("magicCrit"),
SMOKE_NORMAL("smoke"),
SMOKE_LARGE("largesmoke"),
SPELL("spell"),
SPELL_INSTANT("instantSpell"),
SPELL_MOB("mobSpell"),
SPELL_MOB_AMBIENT("mobSpellAmbient"),
SPELL_WITCH("witchMagic"),
DRIP_WATER("dripWater"),
DRIP_LAVA("dripLava"),
VILLAGER_ANGRY("angryVillager"),
VILLAGER_HAPPY("happyVillager"),
TOWN_AURA("townaura"),
NOTE("note"),
PORTAL("portal"),
ENCHANTMENT_TABLE("enchantmenttable"),
FLAME("flame"),
LAVA("lava"),
FOOTSTEP("footstep"),
CLOUD("cloud"),
REDSTONE("reddust"),
SNOWBALL("snowballpoof"),
SNOW_SHOVEL("snowshovel"),
SLIME("slime"),
HEART("heart"),
BARRIER("barrier"),
ICON_CRACK("iconcrack", 2),
BLOCK_CRACK("blockcrack", 1),
BLOCK_DUST("blockdust", 1),
WATER_DROP("droplet"),
ITEM_TAKE("take"),
MOB_APPEARANCE("mobappearance");
EXPLOSION_NORMAL("explode"),
EXPLOSION_LARGE("largeexplode"),
EXPLOSION_HUGE("hugeexplosion"),
FIREWORKS_SPARK("fireworksSpark"),
WATER_BUBBLE("bubble"),
WATER_SPLASH("splash"),
WATER_WAKE("wake"),
SUSPENDED("suspended"),
SUSPENDED_DEPTH("depthsuspend"),
CRIT("crit"),
CRIT_MAGIC("magicCrit"),
SMOKE_NORMAL("smoke"),
SMOKE_LARGE("largesmoke"),
SPELL("spell"),
SPELL_INSTANT("instantSpell"),
SPELL_MOB("mobSpell"),
SPELL_MOB_AMBIENT("mobSpellAmbient"),
SPELL_WITCH("witchMagic"),
DRIP_WATER("dripWater"),
DRIP_LAVA("dripLava"),
VILLAGER_ANGRY("angryVillager"),
VILLAGER_HAPPY("happyVillager"),
TOWN_AURA("townaura"),
NOTE("note"),
PORTAL("portal"),
ENCHANTMENT_TABLE("enchantmenttable"),
FLAME("flame"),
LAVA("lava"),
FOOTSTEP("footstep"),
CLOUD("cloud"),
REDSTONE("reddust"),
SNOWBALL("snowballpoof"),
SNOW_SHOVEL("snowshovel"),
SLIME("slime"),
HEART("heart"),
BARRIER("barrier"),
ICON_CRACK("iconcrack", 2),
BLOCK_CRACK("blockcrack", 1),
BLOCK_DUST("blockdust", 1),
WATER_DROP("droplet"),
ITEM_TAKE("take"),
MOB_APPEARANCE("mobappearance");
public final String name;
public final int extra;
private static final HashMap<String, Particles1_8> particleMap = new HashMap<>();
private static final HashMap<String, Particles1_8> particleMap = new HashMap<>();
Particles1_8(String name) {
this(name, 0);
}
static {
Particles1_8[] particles = values();
Particles1_8(String name, int extra) {
this.name = name;
this.extra = extra;
}
for (Particles1_8 particle : particles) {
particleMap.put(particle.name, particle);
}
}
public static Particles1_8 find(String part) {
return particleMap.get(part);
}
public final String name;
public final int extra;
public static Particles1_8 find(int id) {
if (id < 0) return null;
Particles1_8[] values = Particles1_8.values();
return id >= values.length ? null : values[id];
}
Particles1_8(String name) {
this(name, 0);
}
static {
Particles1_8[] particles = values();
Particles1_8(String name, int extra) {
this.name = name;
this.extra = extra;
}
for (Particles1_8 particle : particles) {
particleMap.put(particle.name, particle);
}
}
}
public static Particles1_8 find(String part) {
return particleMap.get(part);
}
public static Particles1_8 find(int id) {
if (id < 0) return null;
Particles1_8[] values = Particles1_8.values();
return id >= values.length ? null : values[id];
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,13 +17,14 @@
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data;
import com.viaversion.viarewind.api.type.version.Types1_7_6_10;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.api.minecraft.entitydata.EntityDataTypes1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.EntityPacketRewriter1_8;
import com.viaversion.viarewind.api.minecraft.math.AABB;
import com.viaversion.viarewind.api.minecraft.math.Vector3d;
import com.viaversion.viarewind.api.type.RewindTypes;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter.EntityPacketRewriter1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
@@ -33,293 +34,335 @@ import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.data.entity.TrackedEntityImpl;
import com.viaversion.viaversion.rewriter.entitydata.EntityDataHandlerEvent;
import com.viaversion.viaversion.rewriter.entitydata.EntityDataHandlerEventImpl;
import java.util.ArrayList;
import java.util.List;
public class VirtualHologramEntity {
private final List<EntityData> entityDataTracker = new ArrayList<>();
private double locX, locY, locZ;
private final List<EntityData> entityDataTracker = new ArrayList<>();
private final UserConnection user;
private final int entityId;
private double locX, locY, locZ;
private int[] entityIds = null;
private State currentState = null;
private String name = null;
private float yaw, pitch;
private float headYaw;
private byte flags = 0;
private byte armorStandFlags = 0;
private boolean small = false;
private boolean marker = false;
private boolean sneaking = false;
private boolean gravity = false;
private byte alwaysShowNametag;
private final UserConnection user;
private final int entityId;
public VirtualHologramEntity(final UserConnection user, final int entityId) {
this.user = user;
this.entityId = entityId;
}
private int[] entityIds = null;
private State currentState = null;
private String name = null;
private float yaw, pitch;
private float headYaw;
private boolean small = false;
private boolean marker = false;
public void setPosition(final double x, final double y, final double z) {
if (x == this.locX && y == this.locY && z == this.locZ) return;
this.locX = x;
this.locY = y;
this.locZ = z;
public VirtualHologramEntity(final UserConnection user, final int entityId) {
this.user = user;
this.entityId = entityId;
}
updateLocation();
}
public void setPosition(final double x, final double y, final double z) {
if (x == this.locX && y == this.locY && z == this.locZ) return;
this.locX = x;
this.locY = y;
this.locZ = z;
public void setRelativePosition(final double x, final double y, final double z) {
if (x == 0.0 && y == 0.0 && z == 0.0) return;
this.locX += x;
this.locY += y;
this.locZ += z;
updateLocation(false);
}
updateLocation();
}
public void setRelativePosition(final double x, final double y, final double z) {
if (x == 0.0 && y == 0.0 && z == 0.0) return;
this.locX += x;
this.locY += y;
this.locZ += z;
public void setRotation(final float yaw, final float pitch) {
if (this.yaw == yaw || this.headYaw == yaw || this.pitch == pitch) return;
this.yaw = yaw;
this.headYaw = yaw;
this.pitch = pitch;
updateLocation(false);
}
updateLocation();
}
public void setRotation(final float yaw, final float pitch) {
if (this.yaw == yaw || this.headYaw == yaw || this.pitch == pitch) return;
this.yaw = yaw;
this.headYaw = yaw;
this.pitch = pitch;
public void setHeadYaw(float yaw) {
if (this.headYaw == yaw) return;
this.headYaw = yaw;
updateLocation(false);
}
updateLocation();
}
public void setHeadYaw(float yaw) {
if (this.headYaw == yaw) return;
this.headYaw = yaw;
public void syncState(final EntityPacketRewriter1_8 entityRewriter, final List<EntityData> entityDataList) {
// Merge entity data updates into current tracker
for (EntityData entityData : entityDataList) {
entityDataTracker.removeIf(m -> m.id() == entityData.id());
entityDataTracker.add(entityData);
}
updateLocation(false);
}
// Filter armor stand data to calculate emulation
for (EntityData entityData : entityDataTracker) {
if (entityData.id() == 0 && entityData.dataType() == EntityDataTypes1_8.BYTE) {
flags = ((Number) entityData.getValue()).byteValue();
} else if (entityData.id() == 2 && entityData.dataType() == EntityDataTypes1_8.STRING) {
name = entityData.getValue().toString();
if (name != null && name.isEmpty()) name = null;
} else if (entityData.id() == 3 && entityData.dataType() == EntityDataTypes1_8.BYTE) {
alwaysShowNametag = ((Number) entityData.getValue()).byteValue();
} else if (entityData.id() == 10 && entityData.dataType() == EntityDataTypes1_8.BYTE) {
armorStandFlags = ((Number) entityData.getValue()).byteValue();
}
}
final boolean invisible = (flags & 0x20) != 0;
sneaking = (flags & 0x02) != 0;
small = (armorStandFlags & 0x01) != 0;
marker = (armorStandFlags & 0x10) != 0;
gravity = (armorStandFlags & 0x02) == 0;
public void syncState(final EntityPacketRewriter1_8 entityRewriter, final List<EntityData> entityDataList) {
// Merge entity data updates into current tracker
for (EntityData entityData : entityDataList) {
entityDataTracker.removeIf(m -> m.id() == entityData.id());
entityDataTracker.add(entityData);
}
State prevState = currentState;
if (invisible && name != null && alwaysShowNametag == 1) {
currentState = State.HOLOGRAM;
} else if (gravity) {
currentState = State.ZOMBIE;
} else {
currentState = State.ZOMBIE_NO_GRAVITY;
}
// Filter armor stand data to calculate emulation
byte flags = 0;
byte armorStandFlags = 0;
for (EntityData entityData : entityDataTracker) {
if (entityData.id() == 0 && entityData.dataType() == EntityDataTypes1_8.BYTE) {
flags = ((Number) entityData.getValue()).byteValue();
} else if (entityData.id() == 2 && entityData.dataType() == EntityDataTypes1_8.STRING) {
name = entityData.getValue().toString();
if (name != null && name.isEmpty()) name = null;
} else if (entityData.id() == 10 && entityData.dataType() == EntityDataTypes1_8.BYTE) {
armorStandFlags = ((Number) entityData.getValue()).byteValue();
}
}
final boolean invisible = (flags & 0x20) != 0;
small = (armorStandFlags & 0x01) != 0;
marker = (armorStandFlags & 0x10) != 0;
if (currentState != prevState) {
deleteEntity();
sendSpawnPacket(entityRewriter);
} else {
sendEntityDataUpdate(entityRewriter);
updateLocation();
}
}
State prevState = currentState;
if (invisible && name != null) {
currentState = State.HOLOGRAM;
} else {
currentState = State.ZOMBIE;
}
private void updateLocation() {
if (entityIds == null) {
return;
}
if (currentState == State.ZOMBIE) {
teleportEntity(entityId, locX, locY, locZ, yaw, pitch);
if (currentState != prevState) {
deleteEntity();
sendSpawnPacket(entityRewriter);
} else {
sendEntityDataUpdate(entityRewriter);
updateLocation(false);
}
}
final PacketWrapper entityHeadLook = PacketWrapper.create(ClientboundPackets1_7_2_5.ROTATE_HEAD, user);
private void updateLocation(final boolean remount) {
if (entityIds == null) {
return;
}
if (currentState == State.ZOMBIE) {
teleportEntity(entityId, locX, locY, locZ, yaw, pitch);
entityHeadLook.write(Types.INT, entityId);
entityHeadLook.write(Types.BYTE, (byte) ((headYaw / 360f) * 256));
final PacketWrapper entityHeadLook = PacketWrapper.create(ClientboundPackets1_7_2_5.ROTATE_HEAD, user);
entityHeadLook.send(Protocol1_8To1_7_6_10.class);
} else if (currentState == State.HOLOGRAM || currentState == State.ZOMBIE_NO_GRAVITY) {
// Don't ask me where this offset is coming from
teleportEntity(entityIds[1], locX, (locY + getOffset()), locZ, 0, 0); // Squid
}
}
entityHeadLook.write(Types.INT, entityId);
entityHeadLook.write(Types.BYTE, (byte) ((headYaw / 360f) * 256));
private double getOffset() {
if (currentState == State.HOLOGRAM) {
double baseOffset = 54.35;
entityHeadLook.send(Protocol1_8To1_7_6_10.class);
} else if (currentState == State.HOLOGRAM) {
if (remount) {
PacketWrapper detach = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_LINK, user);
detach.write(Types.INT, entityIds[1]);
detach.write(Types.INT, -1);
detach.write(Types.BOOLEAN, false);
detach.send(Protocol1_8To1_7_6_10.class);
}
if (marker) {
return baseOffset;
} else if (small) {
return baseOffset + 0.9875;
} else {
return baseOffset + (0.9875 * 2);
}
} else if (currentState == State.ZOMBIE_NO_GRAVITY) {
// 1.7 ride stack offset sum: Squid (0.95 * 0.75) + Zombie (1.8 * 0.75) = 2.0625
// By starting at -2.0625, we zero out the 1.7 passenger height so we can apply 1.8's offsets.
double baseOffset = -2.0625;
// Don't ask me where this offset is coming from
teleportEntity(entityIds[0], locX, (locY + (marker ? 54.85 : small ? 56 : 57) - 0.16), locZ, 0, 0); // Skull
if (marker) {
// 1.8 Marker ArmorStand height is 0.0 -> mounted offset 0.0
return baseOffset;
} else {
// 1.8 Normal ArmorStand height is 1.975 -> mounted offset 1.975 * 0.75 = 1.48125
return baseOffset + 1.48125;
}
} else {
return -0.4;
}
}
if (remount) {
teleportEntity(entityIds[1], locX, locY + 56.75, locZ, 0, 0); // Horse
protected void teleportEntity(final int entityId, final double x, final double y, final double z, final float yaw, final float pitch) {
final PacketWrapper entityTeleport = PacketWrapper.create(ClientboundPackets1_7_2_5.TELEPORT_ENTITY, user);
PacketWrapper attach = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_LINK, null, user);
attach.write(Types.INT, entityIds[1]);
attach.write(Types.INT, entityIds[0]);
attach.write(Types.BOOLEAN, false);
attach.send(Protocol1_8To1_7_6_10.class);
}
}
}
entityTeleport.write(Types.INT, entityId); // entity id
entityTeleport.write(Types.INT, (int) (x * 32.0)); // x
entityTeleport.write(Types.INT, (int) (y * 32.0)); // y
entityTeleport.write(Types.INT, (int) (z * 32.0)); // z
entityTeleport.write(Types.BYTE, (byte) ((yaw / 360f) * 256)); // yaw
entityTeleport.write(Types.BYTE, (byte) ((pitch / 360f) * 256)); // pitch
protected void teleportEntity(final int entityId, final double x, final double y, final double z, final float yaw, final float pitch) {
final PacketWrapper entityTeleport = PacketWrapper.create(ClientboundPackets1_7_2_5.TELEPORT_ENTITY, user);
entityTeleport.send(Protocol1_8To1_7_6_10.class);
}
entityTeleport.write(Types.INT, entityId); // entity id
entityTeleport.write(Types.INT, (int) (x * 32.0)); // x
entityTeleport.write(Types.INT, (int) (y * 32.0)); // y
entityTeleport.write(Types.INT, (int) (z * 32.0)); // z
entityTeleport.write(Types.BYTE, (byte) ((yaw / 360f) * 256)); // yaw
entityTeleport.write(Types.BYTE, (byte) ((pitch / 360f) * 256)); // pitch
protected void spawnEntity(final int entityId, final int type, final double locX, final double locY, final double locZ, final List<EntityData> entityData) {
final PacketWrapper addMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, user);
entityTeleport.send(Protocol1_8To1_7_6_10.class);
}
addMob.write(Types.VAR_INT, entityId); // Entity id
addMob.write(Types.UNSIGNED_BYTE, (short) type); // Entity type
addMob.write(Types.INT, (int) (locX * 32.0)); // X
addMob.write(Types.INT, (int) (locY * 32.0)); // Y
addMob.write(Types.INT, (int) (locZ * 32.0)); // Z
addMob.write(Types.BYTE, (byte) 0); // Yaw
addMob.write(Types.BYTE, (byte) 0); // Pitch
addMob.write(Types.BYTE, (byte) 0); // Head pitch
addMob.write(Types.SHORT, (short) 0); // Velocity x
addMob.write(Types.SHORT, (short) 0); // Velocity y
addMob.write(Types.SHORT, (short) 0); // Velocity z
addMob.write(RewindTypes.ENTITY_DATA_LIST1_7, entityData); // Entity data
protected void spawnEntity(final int entityId, final int type, final double locX, final double locY, final double locZ) {
final PacketWrapper addMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, user);
addMob.send(Protocol1_8To1_7_6_10.class);
}
addMob.write(Types.VAR_INT, entityId); // Entity id
addMob.write(Types.UNSIGNED_BYTE, (short) type); // Entity type
addMob.write(Types.INT, (int) (locX * 32.0)); // X
addMob.write(Types.INT, (int) (locY * 32.0)); // Y
addMob.write(Types.INT, (int) (locZ * 32.0)); // Z
addMob.write(Types.BYTE, (byte) 0); // Yaw
addMob.write(Types.BYTE, (byte) 0); // Pitch
addMob.write(Types.BYTE, (byte) 0); // Head pitch
addMob.write(Types.SHORT, (short) 0); // Velocity x
addMob.write(Types.SHORT, (short) 0); // Velocity y
addMob.write(Types.SHORT, (short) 0); // Velocity z
addMob.write(Types1_7_6_10.ENTITY_DATA_LIST, new ArrayList<>()); // Entity data
public void sendEntityDataUpdate(final EntityPacketRewriter1_8 entityRewriter) {
if (entityIds == null) {
return;
}
final PacketWrapper setEntityData = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_DATA, user);
addMob.send(Protocol1_8To1_7_6_10.class);
}
if (currentState == State.ZOMBIE || currentState == State.ZOMBIE_NO_GRAVITY) {
writeZombieMeta(entityRewriter, setEntityData);
} else if (currentState == State.HOLOGRAM) {
writeHologramMeta(setEntityData);
} else {
return;
}
setEntityData.send(Protocol1_8To1_7_6_10.class);
}
public void sendEntityDataUpdate(final EntityPacketRewriter1_8 entityRewriter) {
if (entityIds == null) {
return;
}
final PacketWrapper setEntityData = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_DATA, user);
private void writeZombieMeta(final EntityPacketRewriter1_8 entityRewriter, final PacketWrapper wrapper) {
wrapper.write(Types.INT, entityIds[0]);
if (currentState == State.ZOMBIE) {
writeZombieMeta(entityRewriter, setEntityData);
} else if (currentState == State.HOLOGRAM) {
writeHologramMeta(setEntityData);
} else {
return;
}
setEntityData.send(Protocol1_8To1_7_6_10.class);
}
// Filter entity data sent by the server and convert them together with our custom entity data
final List<EntityData> entityDataList = new ArrayList<>();
for (EntityData entityData : entityDataTracker) {
// Remove non existent entityData
if (entityData.id() < 0 || entityData.id() > 9) {
continue;
}
entityDataList.add(new EntityData(entityData.id(), entityData.dataType(), entityData.getValue()));
}
if (small) {
entityDataList.add(new EntityData(12, EntityDataTypes1_8.BYTE, (byte) 1));
}
private void writeZombieMeta(final EntityPacketRewriter1_8 entityRewriter, PacketWrapper wrapper) {
wrapper.write(Types.INT, entityIds[0]);
// Push entity data from the server through entity data conversion 1.7->1.8
for (EntityData entityData : entityDataList.toArray(new EntityData[0])) {
final EntityDataHandlerEvent event = new EntityDataHandlerEventImpl(wrapper.user(), new TrackedEntityImpl(EntityTypes1_8.EntityType.ZOMBIE), -1, entityData, entityDataList);
try {
entityRewriter.handleEntityData(event, entityData);
} catch (Exception e) {
entityDataList.remove(entityData);
break;
}
if (event.cancelled()) {
entityDataList.remove(entityData);
break;
}
}
wrapper.write(RewindTypes.ENTITY_DATA_LIST1_7, entityDataList);
}
// Filter entity data sent by the server and convert them together with our custom entity data
final List<EntityData> entityDataList = new ArrayList<>();
for (EntityData entityData : entityDataTracker) {
// Remove non existent entityData
if (entityData.id() < 0 || entityData.id() > 9) {
continue;
}
entityDataList.add(new EntityData(entityData.id(), entityData.dataType(), entityData.getValue()));
}
if (small) {
entityDataList.add(new EntityData(12, EntityDataTypes1_8.BYTE, (byte) 1));
}
private void writeHologramMeta(PacketWrapper wrapper) {
wrapper.write(Types.INT, entityIds[0]);
// Push entity data from the server through entity data conversion 1.7->1.8
for (EntityData entityData : entityDataList.toArray(new EntityData[0])) {
final EntityDataHandlerEvent event = new EntityDataHandlerEventImpl(wrapper.user(), new TrackedEntityImpl(EntityTypes1_8.EntityType.ZOMBIE), -1, entityData, entityDataList);
try {
entityRewriter.handleEntityData(event, entityData);
} catch (Exception e) {
entityDataList.remove(entityData);
break;
}
if (event.cancelled()) {
entityDataList.remove(entityData);
break;
}
}
wrapper.write(Types1_7_6_10.ENTITY_DATA_LIST, entityDataList);
}
// Directly write 1.7 entity data here since we are making them up
final List<EntityData> entityDataList = new ArrayList<>();
entityDataList.add(new EntityData(EntityDataIndex1_7_6_10.ENTITY_FLAGS.getIndex(), EntityDataTypes1_7_6_10.BYTE, (byte) (sneaking ? 2 : 0)));
entityDataList.add(new EntityData(EntityDataIndex1_7_6_10.ABSTRACT_AGEABLE_AGE.getIndex(), EntityDataTypes1_7_6_10.INT, -1700000));
entityDataList.add(new EntityData(EntityDataIndex1_7_6_10.LIVING_ENTITY_BASE_NAME_TAG.getIndex(), EntityDataTypes1_7_6_10.STRING, name));
entityDataList.add(new EntityData(EntityDataIndex1_7_6_10.LIVING_ENTITY_BASE_NAME_TAG_VISIBILITY.getIndex(), EntityDataTypes1_7_6_10.BYTE, alwaysShowNametag));
private void writeHologramMeta(PacketWrapper wrapper) {
wrapper.write(Types.INT, entityIds[1]);
wrapper.write(RewindTypes.ENTITY_DATA_LIST1_7, entityDataList);
}
// Directly write 1.7 entity data here since we are making them up
final List<EntityData> entityDataList = new ArrayList<>();
entityDataList.add(new EntityData(EntityDataIndex1_7_6_10.ABSTRACT_AGEABLE_AGE.getIndex(), EntityDataTypes1_7_6_10.INT, -1700000));
entityDataList.add(new EntityData(EntityDataIndex1_7_6_10.LIVING_ENTITY_BASE_NAME_TAG.getIndex(), EntityDataTypes1_7_6_10.STRING, name));
entityDataList.add(new EntityData(EntityDataIndex1_7_6_10.LIVING_ENTITY_BASE_NAME_TAG_VISIBILITY.getIndex(), EntityDataTypes1_7_6_10.BYTE, (byte) 1));
public void sendSpawnPacket(final EntityPacketRewriter1_8 entityRewriter) {
if (entityIds != null) {
deleteEntity();
}
if (currentState == State.ZOMBIE) {
spawnEntity(entityId, EntityTypes1_8.EntityType.ZOMBIE.getId(), locX, locY, locZ, new ArrayList<>());
wrapper.write(Types1_7_6_10.ENTITY_DATA_LIST, entityDataList);
}
entityIds = new int[]{entityId};
} else if (currentState == State.ZOMBIE_NO_GRAVITY || currentState == State.HOLOGRAM) {
final int[] entityIds = {entityId, additionalEntityId()};
public void sendSpawnPacket(final EntityPacketRewriter1_8 entityRewriter) {
if (entityIds != null) {
deleteEntity();
}
if (currentState == State.ZOMBIE) {
spawnEntity(entityId, EntityTypes1_8.EntityType.ZOMBIE.getId(), locX, locY, locZ);
final List<EntityData> squidEntityData = new ArrayList<>();
squidEntityData.add(new EntityData(0, EntityDataTypes1_8.BYTE, (byte) 32));
entityIds = new int[]{entityId};
} else if (currentState == State.HOLOGRAM) {
final int[] entityIds = { entityId, additionalEntityId() };
if (currentState == State.HOLOGRAM) {
spawnEntity(entityIds[0], EntityTypes1_8.EntityType.HORSE.getId(), locX, locY + (getOffset() + 0.68), locZ, new ArrayList<>());
} else {
spawnEntity(entityIds[0], EntityTypes1_8.EntityType.ZOMBIE.getId(), locX, locY, locZ, new ArrayList<>());
}
final PacketWrapper spawnSkull = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_ENTITY, user);
spawnSkull.write(Types.VAR_INT, entityIds[0]);
spawnSkull.write(Types.BYTE, (byte) 66);
spawnSkull.write(Types.INT, (int) (locX * 32.0));
spawnSkull.write(Types.INT, (int) (locY * 32.0));
spawnSkull.write(Types.INT, (int) (locZ * 32.0));
spawnSkull.write(Types.BYTE, (byte) 0);
spawnSkull.write(Types.BYTE, (byte) 0);
spawnSkull.write(Types.INT, 0);
spawnSkull.send(Protocol1_8To1_7_6_10.class);
spawnEntity(entityIds[1], EntityTypes1_8.EntityType.SQUID.getId(), locX, locY + getOffset(), locZ, squidEntityData);
spawnEntity(entityIds[1], EntityTypes1_8.EntityType.HORSE.getId(), locX, locY, locZ); // Horse
this.entityIds = entityIds;
}
this.entityIds = entityIds;
}
for (int extraId : entityIds) {
if (extraId != entityId) {
((EntityTracker1_8) user.getEntityTracker(Protocol1_8To1_7_6_10.class)).setExtraHologramId(entityId, extraId);
}
}
sendEntityDataUpdate(entityRewriter);
updateLocation(true);
}
public AABB getBoundingBox() {
final double width = this.small ? 0.25 : 0.5;
final double height = this.small ? 0.9875 : 1.975;
sendEntityDataUpdate(entityRewriter);
if (entityIds == null) {
return;
}
final Vector3d min = new Vector3d(this.locX - width / 2, this.locY, this.locZ - width / 2);
final Vector3d max = new Vector3d(this.locX + width / 2, this.locY + height, this.locZ + width / 2);
if (currentState == State.ZOMBIE) {
updateLocation();
} else {
final PacketWrapper attach = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_LINK, user);
attach.write(Types.INT, entityIds[0]);
attach.write(Types.INT, entityIds[1]);
attach.write(Types.BOOLEAN, false);
return new AABB(min, max);
}
attach.send(Protocol1_8To1_7_6_10.class);
}
}
private int additionalEntityId() {
return Integer.MAX_VALUE - 16000 - entityId;
}
public AABB getBoundingBox() {
final double width = this.small ? 0.25 : 0.5;
final double height = this.small ? 0.9875 : 1.975;
public void deleteEntity() {
if (entityIds == null) {
return;
}
final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user);
despawn.write(Types.BYTE, (byte) entityIds.length);
for (int id : entityIds) {
despawn.write(Types.INT, id);
}
entityIds = null;
despawn.send(Protocol1_8To1_7_6_10.class);
}
final Vector3d min = new Vector3d(this.locX - width / 2, this.locY, this.locZ - width / 2);
final Vector3d max = new Vector3d(this.locX + width / 2, this.locY + height, this.locZ + width / 2);
private enum State {
HOLOGRAM, ZOMBIE
}
return new AABB(min, max);
}
private int additionalEntityId() {
return Integer.MAX_VALUE - 16000 - entityId;
}
public void deleteEntity() {
if (entityIds == null) {
return;
}
for (int extraId : entityIds) {
if (extraId != entityId) {
((EntityTracker1_8) user.getEntityTracker(Protocol1_8To1_7_6_10.class)).removeExtraHologramId(extraId);
}
}
final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user);
despawn.write(Types.BYTE, (byte) entityIds.length);
for (int id : entityIds) {
despawn.write(Types.INT, id);
}
entityIds = null;
despawn.send(Protocol1_8To1_7_6_10.class);
}
private enum State {
HOLOGRAM, ZOMBIE, ZOMBIE_NO_GRAVITY
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,21 +20,19 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.CompressionStatusTracker;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.platform.providers.Provider;
import io.netty.channel.ChannelHandler;
public abstract class CompressionHandlerProvider implements Provider {
public abstract void onHandleLoginCompressionPacket(UserConnection user, int threshold);
public abstract void onTransformPacket(UserConnection user);
public abstract void setCompressionThreshold(UserConnection user, int threshold);
public abstract ChannelHandler getEncoder(int threshold);
public abstract ChannelHandler getDecoder(int threshold);
public abstract void onTransformPacket(UserConnection user);
public boolean isCompressionEnabled(UserConnection user) {
return user.get(CompressionStatusTracker.class).removeCompression;
}
public void setCompressionEnabled(UserConnection user, boolean enabled) {
user.get(CompressionStatusTracker.class).removeCompression = enabled;
}
public boolean isRemoveCompression(UserConnection user) {
return user.get(CompressionStatusTracker.class).removeCompression;
}
public void setRemoveCompression(UserConnection user, boolean enabled) {
user.get(CompressionStatusTracker.class).removeCompression = enabled;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,7 +18,6 @@
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider;
import com.viaversion.viaversion.api.platform.providers.Provider;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
@@ -26,74 +25,74 @@ import java.util.concurrent.atomic.AtomicInteger;
// TODO I believe this should be a storage?
public abstract class TitleRenderProvider implements Provider {
protected Map<UUID, Integer> fadeIn = new HashMap<>();
protected Map<UUID, Integer> stay = new HashMap<>();
protected Map<UUID, Integer> fadeOut = new HashMap<>();
protected Map<UUID, String> titles = new HashMap<>();
protected Map<UUID, String> subTitles = new HashMap<>();
protected Map<UUID, AtomicInteger> times = new HashMap<>();
protected Map<UUID, Integer> fadeIn = new HashMap<>();
protected Map<UUID, Integer> stay = new HashMap<>();
protected Map<UUID, Integer> fadeOut = new HashMap<>();
protected Map<UUID, String> titles = new HashMap<>();
protected Map<UUID, String> subTitles = new HashMap<>();
protected Map<UUID, AtomicInteger> times = new HashMap<>();
public void setTimings(UUID uuid, int fadeIn, int stay, int fadeOut) {
setFadeIn(uuid, fadeIn);
setStay(uuid, stay);
setFadeOut(uuid, fadeOut);
public void setTimings(UUID uuid, int fadeIn, int stay, int fadeOut) {
setFadeIn(uuid, fadeIn);
setStay(uuid, stay);
setFadeOut(uuid, fadeOut);
AtomicInteger time = getTime(uuid);
if (time.get() > 0) time.set(getFadeIn(uuid) + getStay(uuid) + getFadeOut(uuid));
}
AtomicInteger time = getTime(uuid);
if (time.get() > 0) time.set(getFadeIn(uuid) + getStay(uuid) + getFadeOut(uuid));
}
public void reset(UUID uuid) {
this.titles.remove(uuid);
this.subTitles.remove(uuid);
getTime(uuid).set(0);
fadeIn.remove(uuid);
stay.remove(uuid);
fadeOut.remove(uuid);
}
public void reset(UUID uuid) {
this.titles.remove(uuid);
this.subTitles.remove(uuid);
getTime(uuid).set(0);
fadeIn.remove(uuid);
stay.remove(uuid);
fadeOut.remove(uuid);
}
public void setTitle(UUID uuid, String title) {
this.titles.put(uuid, title);
getTime(uuid).set(getFadeIn(uuid) + getStay(uuid) + getFadeOut(uuid));
}
public void setTitle(UUID uuid, String title) {
this.titles.put(uuid, title);
getTime(uuid).set(getFadeIn(uuid) + getStay(uuid) + getFadeOut(uuid));
}
public void setSubTitle(UUID uuid, String subTitle) {
this.subTitles.put(uuid, subTitle);
}
public void setSubTitle(UUID uuid, String subTitle) {
this.subTitles.put(uuid, subTitle);
}
public void clear(UUID uuid) {
this.titles.remove(uuid);
this.subTitles.remove(uuid);
getTime(uuid).set(0);
}
public void clear(UUID uuid) {
this.titles.remove(uuid);
this.subTitles.remove(uuid);
getTime(uuid).set(0);
}
public AtomicInteger getTime(UUID uuid) {
return times.computeIfAbsent(uuid, key -> new AtomicInteger(0));
}
public AtomicInteger getTime(UUID uuid) {
return times.computeIfAbsent(uuid, key -> new AtomicInteger(0));
}
public int getFadeIn(UUID uuid) {
return fadeIn.getOrDefault(uuid, 10);
}
public int getFadeIn(UUID uuid) {
return fadeIn.getOrDefault(uuid, 10);
}
public int getStay(UUID uuid) {
return stay.getOrDefault(uuid, 70);
}
public int getStay(UUID uuid) {
return stay.getOrDefault(uuid, 70);
}
public int getFadeOut(UUID uuid) {
return fadeOut.getOrDefault(uuid, 20);
}
public int getFadeOut(UUID uuid) {
return fadeOut.getOrDefault(uuid, 20);
}
public void setFadeIn(UUID uuid, int fadeIn) {
if (fadeIn >= 0) this.fadeIn.put(uuid, fadeIn);
else this.fadeIn.remove(uuid);
}
public void setFadeIn(UUID uuid, int fadeIn) {
if (fadeIn >= 0) this.fadeIn.put(uuid, fadeIn);
else this.fadeIn.remove(uuid);
}
public void setStay(UUID uuid, int stay) {
if (stay >= 0) this.stay.put(uuid, stay);
else this.stay.remove(uuid);
}
public void setStay(UUID uuid, int stay) {
if (stay >= 0) this.stay.put(uuid, stay);
else this.stay.remove(uuid);
}
public void setFadeOut(UUID uuid, int fadeOut) {
if (fadeOut >= 0) this.fadeOut.put(uuid, fadeOut);
else this.fadeOut.remove(uuid);
}
public void setFadeOut(UUID uuid, int fadeOut) {
if (fadeOut >= 0) this.fadeOut.put(uuid, fadeOut);
else this.fadeOut.remove(uuid);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,56 +17,51 @@
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.List;
import java.util.zip.Inflater;
public class CompressionDecoder extends MessageToMessageDecoder<ByteBuf> {
private final Inflater inflater = new Inflater();
private final int threshold;
private int threshold;
public CompressionDecoder(final int threshold) {
this.threshold = threshold;
}
public CompressionDecoder(final int threshold) {
this.threshold = threshold;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (!in.isReadable()) return;
public void setThreshold(final int threshold) {
this.threshold = threshold;
}
int outLength = Types.VAR_INT.readPrimitive(in);
if (outLength == 0) {
out.add(in.readBytes(in.readableBytes()));
return;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
if (!in.isReadable()) {
return;
}
if (outLength < this.threshold) {
throw new DecoderException("Badly compressed packet - size of " + outLength + " is below server threshold of " + this.threshold);
} else if (outLength > 2097152) {
throw new DecoderException("Badly compressed packet - size of " + outLength + " is larger than protocol maximum of " + 2097152);
}
int outLength = Types.VAR_INT.readPrimitive(in);
if (outLength == 0) {
out.add(in.readBytes(in.readableBytes()));
return;
}
if (outLength < this.threshold) {
throw new DecoderException("Badly compressed packet - size of " + outLength + " is below server threshold of " + this.threshold);
} else if (outLength > 2097152) {
throw new DecoderException("Badly compressed packet - size of " + outLength + " is larger than protocol maximum of " + 2097152);
}
ByteBuf output = ctx.alloc().buffer(outLength);
try {
ThreadLocalCompressionProvider.inflate(in, output, outLength);
out.add(output.retain());
} finally {
output.release();
}
}
ByteBuf temp = in;
if (!in.hasArray()) {
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(in);
} else {
in.retain();
}
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer(outLength, outLength);
try {
this.inflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
output.writerIndex(output.writerIndex() + this.inflater.inflate(output.array(), output.arrayOffset(), outLength));
out.add(output.retain());
} finally {
output.release();
temp.release();
this.inflater.reset();
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,54 +17,35 @@
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.compression;
import com.viaversion.viarewind.api.compression.ThreadLocalCompressionProvider;
import com.viaversion.viaversion.api.type.Types;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import java.util.zip.Deflater;
public class CompressionEncoder extends MessageToByteEncoder<ByteBuf> {
private final Deflater deflater = new Deflater();
private final int threshold;
private int threshold;
public CompressionEncoder(final int threshold) {
this.threshold = threshold;
}
public CompressionEncoder(final int threshold) {
this.threshold = threshold;
}
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
int frameLength = in.readableBytes();
if (frameLength < this.threshold) {
out.writeByte(0); // VarInt
out.writeBytes(in);
return;
}
public void setThreshold(final int threshold) {
this.threshold = threshold;
}
Types.VAR_INT.writePrimitive(out, frameLength);
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
int frameLength = in.readableBytes();
if (frameLength < this.threshold) {
out.writeByte(0); // VarInt
out.writeBytes(in);
return;
}
ByteBuf temp = in;
if (!in.hasArray()) {
temp = ByteBufAllocator.DEFAULT.heapBuffer().writeBytes(in);
} else {
in.retain();
}
ByteBuf output = ByteBufAllocator.DEFAULT.heapBuffer();
try {
this.deflater.setInput(temp.array(), temp.arrayOffset() + temp.readerIndex(), temp.readableBytes());
deflater.finish();
Types.VAR_INT.writePrimitive(out, frameLength);
ThreadLocalCompressionProvider.deflate(in, out);
}
while (!deflater.finished()) {
output.ensureWritable(4096);
output.writerIndex(output.writerIndex() + this.deflater.deflate(output.array(), output.arrayOffset() + output.writerIndex(), output.writableBytes()));
}
out.writeBytes(output);
} finally {
output.release();
temp.release();
this.deflater.reset();
}
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,52 +23,57 @@ import com.viaversion.viarewind.api.minecraft.netty.ForwardMessageToByteEncoder;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.CompressionHandlerProvider;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPipeline;
public class TrackingCompressionHandlerProvider extends CompressionHandlerProvider {
@Override
public void onHandleLoginCompressionPacket(UserConnection user, int threshold) {
final ChannelPipeline pipeline = user.getChannel().pipeline();
if (user.isClientSide()) {
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), ViaRewind.getPlatform().compressHandlerName(), getEncoder(threshold));
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), ViaRewind.getPlatform().decompressHandlerName(), getDecoder(threshold));
} else {
setCompressionEnabled(user, true); // We need to remove compression for 1.7 clients
}
}
@Override
public void setCompressionThreshold(UserConnection user, int threshold) {
final ChannelPipeline pipeline = user.getChannel().pipeline();
if (!user.isClientSide() || threshold < 0) {
setRemoveCompression(user, true); // We need to remove compression for 1.7 clients
return;
}
@Override
public void onTransformPacket(UserConnection user) {
if (isCompressionEnabled(user)) {
final ChannelPipeline pipeline = user.getChannel().pipeline();
final String compressHandlerName = ViaRewind.getPlatform().compressHandlerName();
final CompressionEncoder encoder = (CompressionEncoder) pipeline.get(compressHandlerName);
if (encoder != null) {
encoder.setThreshold(threshold);
} else {
pipeline.addBefore(Via.getManager().getInjector().getEncoderName(), compressHandlerName, new CompressionEncoder(threshold));
}
String compressor = null;
String decompressor = null;
if (pipeline.get(ViaRewind.getPlatform().compressHandlerName()) != null) {
compressor = ViaRewind.getPlatform().compressHandlerName();
decompressor = ViaRewind.getPlatform().decompressHandlerName();
}
final String decompressHandlerName = ViaRewind.getPlatform().decompressHandlerName();
final CompressionDecoder decoder = (CompressionDecoder) pipeline.get(decompressHandlerName);
if (decoder != null) {
decoder.setThreshold(threshold);
} else {
pipeline.addBefore(Via.getManager().getInjector().getDecoderName(), decompressHandlerName, new CompressionDecoder(threshold));
}
}
if (compressor != null) { // We can neutralize the effect of compressor to the client
pipeline.replace(decompressor, decompressor, new EmptyChannelHandler());
pipeline.replace(compressor, compressor, new ForwardMessageToByteEncoder());
} else {
throw new IllegalStateException("Couldn't remove compression for 1.7!");
}
@Override
public void onTransformPacket(UserConnection user) {
if (isRemoveCompression(user)) {
final ChannelPipeline pipeline = user.getChannel().pipeline();
setCompressionEnabled(user, false);
}
}
String compressor = null;
String decompressor = null;
if (pipeline.get(ViaRewind.getPlatform().compressHandlerName()) != null) {
compressor = ViaRewind.getPlatform().compressHandlerName();
decompressor = ViaRewind.getPlatform().decompressHandlerName();
}
if (compressor != null) { // We can neutralize the effect of compressor to the client
pipeline.replace(decompressor, decompressor, new EmptyChannelHandler());
pipeline.replace(compressor, compressor, new ForwardMessageToByteEncoder());
} else {
throw new IllegalStateException("Couldn't remove compression for 1.7!");
}
setRemoveCompression(user, false);
}
}
@Override
public ChannelHandler getEncoder(int threshold) {
return new CompressionEncoder(threshold);
}
@Override
public ChannelHandler getDecoder(int threshold) {
return new CompressionDecoder(threshold);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,304 +17,305 @@
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viabackwards.api.rewriters.LegacyEnchantmentRewriter;
import com.viaversion.viarewind.api.rewriter.VRBlockItemRewriter;
import com.viaversion.viarewind.api.type.RewindTypes;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ServerboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.GameProfileStorage;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.InventoryTracker;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.PlayerSessionStorage;
import com.viaversion.viarewind.api.type.RewindTypes;
import com.viaversion.viarewind.utils.ChatUtil;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.nbt.tag.*;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import java.util.UUID;
public class BlockItemPacketRewriter1_8 extends VRBlockItemRewriter<ClientboundPackets1_8, ServerboundPackets1_7_2_5, Protocol1_8To1_7_6_10> {
private LegacyEnchantmentRewriter enchantmentRewriter;
private LegacyEnchantmentRewriter enchantmentRewriter;
public BlockItemPacketRewriter1_8(Protocol1_8To1_7_6_10 protocol) {
super(protocol, "1.8");
}
public BlockItemPacketRewriter1_8(Protocol1_8To1_7_6_10 protocol) {
super(protocol, "1.8");
}
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_8.OPEN_SCREEN, wrapper -> {
final InventoryTracker windowTracker = wrapper.user().get(InventoryTracker.class);
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_8.OPEN_SCREEN, wrapper -> {
final InventoryTracker windowTracker = wrapper.user().get(InventoryTracker.class);
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE); // Window id
final short windowTypeId = InventoryTracker.getInventoryType(wrapper.read(Types.STRING)); // Window type
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE); // Window id
final short windowTypeId = InventoryTracker.getInventoryType(wrapper.read(Types.STRING)); // Window type
windowTracker.getWindowTypeMap().put(windowId, windowTypeId);
wrapper.write(Types.UNSIGNED_BYTE, windowTypeId); // Window type id
windowTracker.getWindowTypeMap().put(windowId, windowTypeId);
wrapper.write(Types.UNSIGNED_BYTE, windowTypeId); // Window type id
final JsonElement titleComponent = wrapper.read(Types.COMPONENT); // Title
String title = ChatUtil.jsonToLegacy(titleComponent);
title = ChatUtil.removeUnusedColor(title, '8');
if (title.length() > 32) {
title = title.substring(0, 32);
}
wrapper.write(Types.STRING, title); // Window title
final JsonElement titleComponent = wrapper.read(Types.COMPONENT); // Title
String title = ChatUtil.jsonToLegacy(titleComponent);
title = ChatUtil.removeUnusedColor(title, '8');
if (title.length() > 32) {
title = title.substring(0, 32);
}
wrapper.write(Types.STRING, title); // Window title
wrapper.passthrough(Types.UNSIGNED_BYTE); // Slots
wrapper.write(Types.BOOLEAN, true); // Use provided window title
wrapper.passthrough(Types.UNSIGNED_BYTE); // Slots
wrapper.write(Types.BOOLEAN, true); // Use provided window title
if (windowTypeId == 11) { // Horse
wrapper.passthrough(Types.INT); // Entity id
}
});
if (windowTypeId == 11) { // Horse
wrapper.passthrough(Types.INT); // Entity id
}
});
protocol.registerClientbound(ClientboundPackets1_8.CONTAINER_CLOSE, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE); // Window id
protocol.registerClientbound(ClientboundPackets1_8.CONTAINER_CLOSE, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE); // Window id
wrapper.user().get(InventoryTracker.class).remove(windowId);
});
wrapper.user().get(InventoryTracker.class).remove(windowId);
});
protocol.registerClientbound(ClientboundPackets1_8.CONTAINER_SET_SLOT, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Window id
map(Types.SHORT); // Slot
protocol.registerClientbound(ClientboundPackets1_8.CONTAINER_SET_SLOT, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Window id
map(Types.SHORT); // Slot
handler(wrapper -> {
final short windowType = wrapper.user().get(InventoryTracker.class).get(wrapper.get(Types.BYTE, 0));
final short slot = wrapper.get(Types.SHORT, 0);
if (windowType == 4) { // Enchantment Table
if (slot == 1) {
wrapper.cancel();
} else if (slot >= 2) {
wrapper.set(Types.SHORT, 0, (short) (slot - 1));
}
}
});
handler(wrapper -> {
final short windowType = wrapper.user().get(InventoryTracker.class).get(wrapper.get(Types.BYTE, 0));
final short slot = wrapper.get(Types.SHORT, 0);
if (windowType == 4) { // Enchantment Table
if (slot == 1) {
wrapper.cancel();
} else if (slot >= 2) {
wrapper.set(Types.SHORT, 0, (short) (slot - 1));
}
}
});
map(Types.ITEM1_8, RewindTypes.COMPRESSED_NBT_ITEM); // item
handler(wrapper -> handleItemToClient(wrapper.user(), wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM, 0)));
map(Types.ITEM1_8, RewindTypes.COMPRESSED_NBT_ITEM); // item
handler(wrapper -> handleItemToClient(wrapper.user(), wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM, 0)));
handler(wrapper -> {
final byte windowId = wrapper.get(Types.BYTE, 0);
if (windowId != 0) {
return;
}
final short slot = wrapper.get(Types.SHORT, 0);
if (slot < 5 || slot > 8) {
return;
}
final PlayerSessionStorage playerSession = wrapper.user().get(PlayerSessionStorage.class);
final Item item = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM, 0);
playerSession.setPlayerEquipment(wrapper.user().getProtocolInfo().getUuid(), item, 8 - slot);
handler(wrapper -> {
final byte windowId = wrapper.get(Types.BYTE, 0);
if (windowId != 0) {
return;
}
final short slot = wrapper.get(Types.SHORT, 0);
if (slot < 5 || slot > 8) {
return;
}
final PlayerSessionStorage playerSession = wrapper.user().get(PlayerSessionStorage.class);
final Item item = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM, 0);
playerSession.setPlayerEquipment(wrapper.user().getProtocolInfo().getUuid(), item, 8 - slot);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
if (tracker.isSpectator()) { // Spectator mode didn't exist in 1.7.10
wrapper.cancel();
}
});
}
});
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
if (tracker.isSpectator()) { // Spectator mode didn't exist in 1.7.10
wrapper.cancel();
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.CONTAINER_SET_CONTENT, new PacketHandlers() {
@Override
public void register() {
map(Types.UNSIGNED_BYTE); // Window id
protocol.registerClientbound(ClientboundPackets1_8.CONTAINER_SET_CONTENT, new PacketHandlers() {
@Override
public void register() {
map(Types.UNSIGNED_BYTE); // Window id
// remap enchantment table items
handler(wrapper -> {
final short windowType = wrapper.user().get(InventoryTracker.class).get(wrapper.get(Types.UNSIGNED_BYTE, 0));
// remap enchantment table items
handler(wrapper -> {
final short windowType = wrapper.user().get(InventoryTracker.class).get(wrapper.get(Types.UNSIGNED_BYTE, 0));
Item[] items = wrapper.read(Types.ITEM1_8_SHORT_ARRAY);
if (windowType == 4) { // Enchantment Table
Item[] old = items;
items = new Item[old.length - 1];
items[0] = old[0];
System.arraycopy(old, 2, items, 1, old.length - 2);
}
for (int i = 0; i < items.length; i++) {
items[i] = handleItemToClient(wrapper.user(), items[i]);
}
wrapper.write(RewindTypes.COMPRESSED_NBT_ITEM_ARRAY, items); // Items
});
Item[] items = wrapper.read(Types.ITEM1_8_SHORT_ARRAY);
if (windowType == 4) { // Enchantment Table
Item[] old = items;
items = new Item[old.length - 1];
items[0] = old[0];
System.arraycopy(old, 2, items, 1, old.length - 2);
}
for (int i = 0; i < items.length; i++) {
items[i] = handleItemToClient(wrapper.user(), items[i]);
}
wrapper.write(RewindTypes.COMPRESSED_NBT_ITEM_ARRAY, items); // Items
});
handler(wrapper -> {
final short windowId = wrapper.get(Types.UNSIGNED_BYTE, 0);
if (windowId != 0) {
return;
}
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(Protocol1_8To1_7_6_10.class);
final UUID userId = wrapper.user().getProtocolInfo().getUuid();
handler(wrapper -> {
final short windowId = wrapper.get(Types.UNSIGNED_BYTE, 0);
if (windowId != 0) {
return;
}
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final UUID userId = wrapper.user().getProtocolInfo().getUuid();
final Item[] items = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM_ARRAY, 0);
for (int i = 5; i < 9; i++) {
wrapper.user().get(PlayerSessionStorage.class).setPlayerEquipment(userId, items[i], 8 - i);
if (tracker.isSpectator()) {
items[i] = null;
}
}
if (tracker.isSpectator()) {
final GameProfileStorage.GameProfile profile = wrapper.user().get(GameProfileStorage.class).get(userId);
if (profile != null) {
items[5] = profile.getSkull();
}
}
});
}
});
final Item[] items = wrapper.get(RewindTypes.COMPRESSED_NBT_ITEM_ARRAY, 0);
for (int i = 5; i < 9; i++) {
wrapper.user().get(PlayerSessionStorage.class).setPlayerEquipment(userId, items[i], 8 - i);
if (tracker.isSpectator()) {
items[i] = null;
}
}
if (tracker.isSpectator()) {
final GameProfileStorage.GameProfile profile = wrapper.user().get(GameProfileStorage.class).get(userId);
if (profile != null) {
items[5] = profile.getSkull();
}
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.CONTAINER_SET_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.UNSIGNED_BYTE); // Window id
map(Types.SHORT); // Progress bar id
map(Types.SHORT); // Progress bar value
protocol.registerClientbound(ClientboundPackets1_8.CONTAINER_SET_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.UNSIGNED_BYTE); // Window id
map(Types.SHORT); // Progress bar id
map(Types.SHORT); // Progress bar value
handler(wrapper -> {
final InventoryTracker windowTracker = wrapper.user().get(InventoryTracker.class);
handler(wrapper -> {
final InventoryTracker windowTracker = wrapper.user().get(InventoryTracker.class);
final short windowId = wrapper.get(Types.UNSIGNED_BYTE, 0);
final short windowType = windowTracker.get(windowId);
final short windowId = wrapper.get(Types.UNSIGNED_BYTE, 0);
final short windowType = windowTracker.get(windowId);
final short progressBarId = wrapper.get(Types.SHORT, 0);
short progress = wrapper.get(Types.SHORT, 1);
final short progressBarId = wrapper.get(Types.SHORT, 0);
short progress = wrapper.get(Types.SHORT, 1);
if (windowType == 2) { // Furnace
// TODO | Proper methods (getOrCreate)=
final InventoryTracker.FurnaceData furnace = windowTracker.getFurnaceData().computeIfAbsent(windowId, x -> new InventoryTracker.FurnaceData());
if (progressBarId == 0 || progressBarId == 1) {
if (progressBarId == 0) {
furnace.fuelLeft = progress;
} else {
furnace.maxFuel = progress;
}
if (furnace.maxFuel == 0) {
wrapper.cancel();
return;
}
progress = (short) (200 * furnace.fuelLeft / furnace.maxFuel);
wrapper.set(Types.SHORT, 0, (short) 1);
wrapper.set(Types.SHORT, 1, progress);
} else if (progressBarId == 2 || progressBarId == 3) {
if (progressBarId == 2) {
furnace.progress = progress;
} else {
furnace.maxProgress = progress;
}
if (furnace.maxProgress == 0) {
wrapper.cancel();
return;
}
progress = (short) (200 * furnace.progress / furnace.maxProgress);
wrapper.set(Types.SHORT, 0, (short) 0);
wrapper.set(Types.SHORT, 1, progress);
}
} else if (windowType == 4 && progressBarId > 2) { // Enchanting Table
wrapper.cancel();
} else if (windowType == 8) { // Anvil
windowTracker.levelCost = progress;
windowTracker.anvilId = windowId;
}
});
}
});
if (windowType == 2) { // Furnace
// TODO | Proper methods (getOrCreate)=
final InventoryTracker.FurnaceData furnace = windowTracker.getFurnaceData().computeIfAbsent(windowId, x -> new InventoryTracker.FurnaceData());
if (progressBarId == 0 || progressBarId == 1) {
if (progressBarId == 0) {
furnace.fuelLeft = progress;
} else {
furnace.maxFuel = progress;
}
if (furnace.maxFuel == 0) {
wrapper.cancel();
return;
}
progress = (short) (200 * furnace.fuelLeft / furnace.maxFuel);
wrapper.set(Types.SHORT, 0, (short) 1);
wrapper.set(Types.SHORT, 1, progress);
} else if (progressBarId == 2 || progressBarId == 3) {
if (progressBarId == 2) {
furnace.progress = progress;
} else {
furnace.maxProgress = progress;
}
if (furnace.maxProgress == 0) {
wrapper.cancel();
return;
}
progress = (short) (200 * furnace.progress / furnace.maxProgress);
wrapper.set(Types.SHORT, 0, (short) 0);
wrapper.set(Types.SHORT, 1, progress);
}
} else if (windowType == 4 && progressBarId > 2) { // Enchanting Table
wrapper.cancel();
} else if (windowType == 8) { // Anvil
windowTracker.levelCost = progress;
windowTracker.anvilId = windowId;
}
});
}
});
protocol.registerServerbound(ServerboundPackets1_7_2_5.CONTAINER_CLOSE, wrapper -> {
final byte windowId = wrapper.passthrough(Types.BYTE);
protocol.registerServerbound(ServerboundPackets1_7_2_5.CONTAINER_CLOSE, wrapper -> {
final byte windowId = wrapper.passthrough(Types.BYTE);
wrapper.user().get(InventoryTracker.class).remove(windowId);
});
wrapper.user().get(InventoryTracker.class).remove(windowId);
});
protocol.registerServerbound(ServerboundPackets1_7_2_5.CONTAINER_CLICK, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Window id
map(Types.SHORT); // Slot
handler(wrapper -> {
final byte windowId = wrapper.get(Types.BYTE, 0); // Window id
final short slot = wrapper.get(Types.SHORT, 0);
protocol.registerServerbound(ServerboundPackets1_7_2_5.CONTAINER_CLICK, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Window id
map(Types.SHORT); // Slot
handler(wrapper -> {
final byte windowId = wrapper.get(Types.BYTE, 0); // Window id
final short slot = wrapper.get(Types.SHORT, 0);
final short windowType = wrapper.user().get(InventoryTracker.class).get(windowId);
if (windowType == 4) { // Enchantment Table
if (slot > 0) {
wrapper.set(Types.SHORT, 0, (short) (slot + 1));
}
}
});
map(Types.BYTE); // Button
map(Types.SHORT); // Action number
map(Types.BYTE); // Mode
map(RewindTypes.COMPRESSED_NBT_ITEM, Types.ITEM1_8); // Clicked item
final short windowType = wrapper.user().get(InventoryTracker.class).get(windowId);
if (windowType == 4) { // Enchantment Table
if (slot > 0) {
wrapper.set(Types.SHORT, 0, (short) (slot + 1));
}
}
});
map(Types.BYTE); // Button
map(Types.SHORT); // Action number
map(Types.BYTE); // Mode
map(RewindTypes.COMPRESSED_NBT_ITEM, Types.ITEM1_8); // Clicked item
handler(wrapper -> handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));
}
});
handler(wrapper -> handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));
}
});
protocol.registerServerbound(ServerboundPackets1_7_2_5.SET_CREATIVE_MODE_SLOT, new PacketHandlers() {
@Override
public void register() {
map(Types.SHORT); // Slot
map(RewindTypes.COMPRESSED_NBT_ITEM, Types.ITEM1_8); // Item
protocol.registerServerbound(ServerboundPackets1_7_2_5.SET_CREATIVE_MODE_SLOT, new PacketHandlers() {
@Override
public void register() {
map(Types.SHORT); // Slot
map(RewindTypes.COMPRESSED_NBT_ITEM, Types.ITEM1_8); // Item
handler(wrapper -> handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));
}
});
}
handler(wrapper -> handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));
}
});
}
@Override
protected void registerRewrites() {
enchantmentRewriter = new LegacyEnchantmentRewriter(nbtTagName(), false);
enchantmentRewriter.registerEnchantment(8, "§7Depth Strider");
}
@Override
protected void registerRewrites() {
enchantmentRewriter = new LegacyEnchantmentRewriter(nbtTagName(), false);
enchantmentRewriter.registerEnchantment(8, "§7Depth Strider");
}
@Override
public Item handleItemToClient(UserConnection connection, Item item) {
if (item == null) return null;
super.handleItemToClient(connection, item);
@Override
public Item handleItemToClient(UserConnection connection, Item item) {
if (item == null) return null;
super.handleItemToClient(connection, item);
CompoundTag tag = item.tag();
if (tag == null) return item;
CompoundTag tag = item.tag();
if (tag == null) return item;
enchantmentRewriter.handleToClient(item);
enchantmentRewriter.handleToClient(item);
if (item.identifier() == 387) {
final ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
if (pages == null) return item;
if (item.identifier() == 387) {
final ListTag<StringTag> pages = tag.getListTag("pages", StringTag.class);
if (pages == null) return item;
final ListTag<StringTag> oldPages = new ListTag<>(StringTag.class);
tag.put(nbtTagName() + "|pages", oldPages);
final ListTag<StringTag> oldPages = new ListTag<>(StringTag.class);
tag.put(nbtTagName() + "|pages", oldPages);
for (StringTag page : pages) {
final String value = page.getValue();
oldPages.add(new StringTag(value));
page.setValue(ChatUtil.jsonToLegacy(value));
}
}
return item;
}
for (StringTag page : pages) {
final String value = page.getValue();
oldPages.add(new StringTag(value));
page.setValue(ChatUtil.jsonToLegacy(value));
}
}
return item;
}
@Override
public Item handleItemToServer(UserConnection connection, Item item) {
if (item == null) return null;
super.handleItemToServer(connection, item);
@Override
public Item handleItemToServer(UserConnection connection, Item item) {
if (item == null) return null;
super.handleItemToServer(connection, item);
final CompoundTag tag = item.tag();
if (tag == null) return item;
final CompoundTag tag = item.tag();
if (tag == null) return item;
enchantmentRewriter.handleToServer(item);
enchantmentRewriter.handleToServer(item);
if (item.identifier() == 387) {
final ListTag<StringTag> oldPages = tag.getListTag(nbtTagName() + "|pages", StringTag.class);
if (oldPages != null) {
tag.remove("pages");
tag.put("pages", oldPages);
}
}
if (item.identifier() == 387) {
final ListTag<StringTag> oldPages = tag.getListTag(nbtTagName() + "|pages", StringTag.class);
if (oldPages != null) {
tag.remove("pages");
tag.put("pages", oldPages);
}
}
return item;
}
return item;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.EntityTracker1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.ScoreboardTracker;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
@@ -26,227 +27,244 @@ import com.viaversion.viaversion.api.rewriter.RewriterBase;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import com.viaversion.viaversion.util.ChatColorUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class ScoreboardPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10> {
public ScoreboardPacketRewriter1_8(Protocol1_8To1_7_6_10 protocol) {
super(protocol);
}
public ScoreboardPacketRewriter1_8(Protocol1_8To1_7_6_10 protocol) {
super(protocol);
}
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_8.SET_OBJECTIVE, wrapper -> {
String name = wrapper.passthrough(Types.STRING);
if (name.length() > 16) {
wrapper.set(Types.STRING, 0, name = name.substring(0, 16));
}
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_8.SET_OBJECTIVE, wrapper -> {
String name = wrapper.passthrough(Types.STRING);
if (name.length() > 16) {
wrapper.set(Types.STRING, 0, name = name.substring(0, 16));
}
final byte mode = wrapper.read(Types.BYTE);
ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
if (mode == 0) {
if (scoreboard.objectiveExists(name)) {
wrapper.cancel();
return;
}
scoreboard.addObjective(name);
} else if (mode == 1) {
if (!scoreboard.objectiveExists(name)) {
wrapper.cancel();
return;
}
if (scoreboard.getColorIndependentSidebar() != null) {
String username = wrapper.user().getProtocolInfo().getUsername();
Optional<Byte> color = scoreboard.getPlayerTeamColor(username);
if (color.isPresent()) {
String sidebar = scoreboard.getColorDependentSidebar().get(color.get());
if (name.equals(sidebar)) {
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
sidebarPacket.write(Types.BYTE, (byte) 1);
sidebarPacket.write(Types.STRING, scoreboard.getColorIndependentSidebar());
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
}
}
}
scoreboard.removeObjective(name);
} else if (mode == 2) {
if (!scoreboard.objectiveExists(name)) {
wrapper.cancel();
return;
}
}
final byte mode = wrapper.read(Types.BYTE);
ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
if (mode == 0) {
if (scoreboard.objectiveExists(name)) {
wrapper.cancel();
return;
}
scoreboard.addObjective(name);
} else if (mode == 1) {
if (!scoreboard.objectiveExists(name)) {
wrapper.cancel();
return;
}
if (scoreboard.getColorIndependentSidebar() != null) {
String username = wrapper.user().getProtocolInfo().getUsername();
Optional<Byte> color = scoreboard.getPlayerTeamColor(username);
if (color.isPresent()) {
String sidebar = scoreboard.getColorDependentSidebar().get(color.get());
if (name.equals(sidebar)) {
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
sidebarPacket.write(Types.BYTE, (byte) 1);
sidebarPacket.write(Types.STRING, scoreboard.getColorIndependentSidebar());
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
}
}
}
scoreboard.removeObjective(name);
} else if (mode == 2) {
if (!scoreboard.objectiveExists(name)) {
wrapper.cancel();
return;
}
}
if (mode == 0 || mode == 2) {
String displayName = wrapper.passthrough(Types.STRING);
if (displayName.length() > 32) {
wrapper.set(Types.STRING, 1, displayName.substring(0, 32));
}
wrapper.read(Types.STRING);
} else {
wrapper.write(Types.STRING, "");
}
wrapper.write(Types.BYTE, mode);
});
if (mode == 0 || mode == 2) {
String displayName = wrapper.passthrough(Types.STRING);
if (displayName.length() > 32) {
wrapper.set(Types.STRING, 1, displayName.substring(0, 32));
}
wrapper.read(Types.STRING);
} else {
wrapper.write(Types.STRING, "");
}
wrapper.write(Types.BYTE, mode);
});
protocol.registerClientbound(ClientboundPackets1_8.SET_SCORE, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING); // Name
map(Types.VAR_INT, Types.BYTE); // Mode
handler(wrapper -> {
ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
String name = wrapper.get(Types.STRING, 0);
byte mode = wrapper.get(Types.BYTE, 0);
protocol.registerClientbound(ClientboundPackets1_8.SET_SCORE, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING); // Name
map(Types.VAR_INT, Types.BYTE); // Mode
handler(wrapper -> {
ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
String name = wrapper.get(Types.STRING, 0);
byte mode = wrapper.get(Types.BYTE, 0);
if (mode == 1) {
name = scoreboard.removeTeamForScore(name);
} else {
name = scoreboard.sendTeamForScore(name);
}
if (mode == 1) {
name = scoreboard.removeTeamForScore(name);
} else {
name = scoreboard.sendTeamForScore(name);
}
if (name.length() > 16) {
name = ChatColorUtil.stripColor(name);
if (name.length() > 16) {
name = name.substring(0, 16);
}
}
wrapper.set(Types.STRING, 0, name);
if (name.length() > 16) {
name = ChatColorUtil.stripColor(name);
if (name.length() > 16) {
name = name.substring(0, 16);
}
}
wrapper.set(Types.STRING, 0, name);
String objective = wrapper.read(Types.STRING);
if (objective.length() > 16) {
objective = objective.substring(0, 16);
}
String objective = wrapper.read(Types.STRING);
if (objective.length() > 16) {
objective = objective.substring(0, 16);
}
if (mode != 1) {
int score = wrapper.read(Types.VAR_INT);
wrapper.write(Types.STRING, objective);
wrapper.write(Types.INT, score);
}
});
}
});
if (mode != 1) {
int score = wrapper.read(Types.VAR_INT);
wrapper.write(Types.STRING, objective);
wrapper.write(Types.INT, score);
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.SET_DISPLAY_OBJECTIVE, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Position
map(Types.STRING); // Score name
handler(wrapper -> {
byte position = wrapper.get(Types.BYTE, 0);
String name = wrapper.get(Types.STRING, 0);
ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
if (position > 2) { // team specific sidebar
byte receiverTeamColor = (byte) (position - 3);
scoreboard.getColorDependentSidebar().put(receiverTeamColor, name);
protocol.registerClientbound(ClientboundPackets1_8.SET_DISPLAY_OBJECTIVE, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Position
map(Types.STRING); // Score name
handler(wrapper -> {
byte position = wrapper.get(Types.BYTE, 0);
String name = wrapper.get(Types.STRING, 0);
ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
if (position > 2) { // team specific sidebar
byte receiverTeamColor = (byte) (position - 3);
scoreboard.getColorDependentSidebar().put(receiverTeamColor, name);
String username = wrapper.user().getProtocolInfo().getUsername();
Optional<Byte> color = scoreboard.getPlayerTeamColor(username);
if (color.isPresent() && color.get() == receiverTeamColor) {
position = 1;
} else {
position = -1;
}
} else if (position == 1) { // team independent sidebar
scoreboard.setColorIndependentSidebar(name);
String username = wrapper.user().getProtocolInfo().getUsername();
Optional<Byte> color = scoreboard.getPlayerTeamColor(username);
if (color.isPresent() && scoreboard.getColorDependentSidebar().containsKey(color.get())) {
position = -1;
}
}
if (position == -1) {
wrapper.cancel();
return;
}
wrapper.set(Types.BYTE, 0, position);
});
}
});
String username = wrapper.user().getProtocolInfo().getUsername();
Optional<Byte> color = scoreboard.getPlayerTeamColor(username);
if (color.isPresent() && color.get() == receiverTeamColor) {
position = 1;
} else {
position = -1;
}
} else if (position == 1) { // team independent sidebar
scoreboard.setColorIndependentSidebar(name);
String username = wrapper.user().getProtocolInfo().getUsername();
Optional<Byte> color = scoreboard.getPlayerTeamColor(username);
if (color.isPresent() && scoreboard.getColorDependentSidebar().containsKey(color.get())) {
position = -1;
}
}
if (position == -1) {
wrapper.cancel();
return;
}
wrapper.set(Types.BYTE, 0, position);
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.SET_PLAYER_TEAM, wrapper -> {
final String team = wrapper.passthrough(Types.STRING);
if (team == null) {
wrapper.cancel();
return;
}
protocol.registerClientbound(ClientboundPackets1_8.SET_PLAYER_TEAM, wrapper -> {
final String team = wrapper.passthrough(Types.STRING);
if (team == null) {
wrapper.cancel();
return;
}
final ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
final ScoreboardTracker scoreboard = wrapper.user().get(ScoreboardTracker.class);
final EntityTracker1_8 tracker = wrapper.user().getEntityTracker(protocol);
final byte mode = wrapper.passthrough(Types.BYTE);
if (mode != 0 && !scoreboard.teamExists(team)) {
wrapper.cancel();
return;
} else if (mode == 0 && scoreboard.teamExists(team)) {
scoreboard.removeTeam(team);
final byte mode = wrapper.passthrough(Types.BYTE);
if (mode != 0 && !scoreboard.teamExists(team)) {
wrapper.cancel();
return;
} else if (mode == 0 && scoreboard.teamExists(team)) {
scoreboard.removeTeam(team);
final PacketWrapper removeTeam = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, wrapper.user());
removeTeam.write(Types.STRING, team);
removeTeam.write(Types.BYTE, (byte) 1);
removeTeam.send(Protocol1_8To1_7_6_10.class);
}
final PacketWrapper removeTeam = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, wrapper.user());
removeTeam.write(Types.STRING, team);
removeTeam.write(Types.BYTE, (byte) 1);
removeTeam.send(Protocol1_8To1_7_6_10.class);
}
if (mode == 0) {
scoreboard.addTeam(team);
} else if (mode == 1) {
scoreboard.removeTeam(team);
}
if (mode == 0) {
scoreboard.addTeam(team);
} else if (mode == 1) {
// Team removed, nametag visibility might have changed
for (final String member : scoreboard.getTeamMembers(team)) {
tracker.checkNametagVisbility(member);
}
scoreboard.removeTeam(team);
}
if (mode == 0 || mode == 2) {
wrapper.passthrough(Types.STRING); // Display name
wrapper.passthrough(Types.STRING); // Prefix
wrapper.passthrough(Types.STRING); // Suffix
wrapper.passthrough(Types.BYTE); // Friendly fire
wrapper.read(Types.STRING); // Name tag visibility
byte color = wrapper.read(Types.BYTE);
if (mode == 2 && scoreboard.getTeamColor(team).get() != color) {
final String sidebar = scoreboard.getColorDependentSidebar().get(color);
if (mode == 0 || mode == 2) {
wrapper.passthrough(Types.STRING); // Display name
wrapper.passthrough(Types.STRING); // Prefix
wrapper.passthrough(Types.STRING); // Suffix
wrapper.passthrough(Types.BYTE); // Friendly fire
final String nameTagVisibility = wrapper.read(Types.STRING);
final String previousVisibility = scoreboard.getTeamNameTagVisibility(team);
scoreboard.setTeamNameTagVisibility(team, nameTagVisibility);
byte color = wrapper.read(Types.BYTE);
if (mode == 2 && scoreboard.getTeamColor(team).get() != color) {
final String sidebar = scoreboard.getColorDependentSidebar().get(color);
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
sidebarPacket.write(Types.BYTE, (byte) 1);
sidebarPacket.write(Types.STRING, sidebar == null ? "" : sidebar);
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
}
scoreboard.setTeamColor(team, color);
}
if (mode == 0 || mode == 3 || mode == 4) {
byte color = scoreboard.getTeamColor(team).get();
String[] entries = wrapper.read(Types.STRING_ARRAY);
List<String> entryList = new ArrayList<>();
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
sidebarPacket.write(Types.BYTE, (byte) 1);
sidebarPacket.write(Types.STRING, sidebar == null ? "" : sidebar);
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
}
scoreboard.setTeamColor(team, color);
for (String entry : entries) {
String username = wrapper.user().getProtocolInfo().getUsername();
// Re-evaluate nametag visibility for all team members when visibility changes
if (mode == 2 && !nameTagVisibility.equals(previousVisibility)) {
for (final String member : scoreboard.getTeamMembers(team)) {
tracker.checkNametagVisbility(member);
}
}
}
if (mode == 0 || mode == 3 || mode == 4) {
byte color = scoreboard.getTeamColor(team).get();
String[] entries = wrapper.read(Types.STRING_ARRAY);
List<String> entryList = new ArrayList<>();
if (mode == 4) {
if (!scoreboard.isPlayerInTeam(entry, team)) {
continue;
}
scoreboard.removePlayerFromTeam(entry, team);
if (entry.equals(username)) {
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
sidebarPacket.write(Types.BYTE, (byte) 1);
sidebarPacket.write(Types.STRING, scoreboard.getColorIndependentSidebar() == null ? "" : scoreboard.getColorIndependentSidebar());
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
}
} else {
scoreboard.addPlayerToTeam(entry, team);
if (entry.equals(username) && scoreboard.getColorDependentSidebar().containsKey(color)) {
final PacketWrapper displayObjective = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
displayObjective.write(Types.BYTE, (byte) 1);
displayObjective.write(Types.STRING, scoreboard.getColorDependentSidebar().get(color));
displayObjective.scheduleSend(Protocol1_8To1_7_6_10.class);
}
}
entryList.add(entry);
}
for (String entry : entries) {
String username = wrapper.user().getProtocolInfo().getUsername();
wrapper.write(Types.SHORT, (short) entryList.size());
for (String entry : entryList) {
wrapper.write(Types.STRING, entry);
}
}
});
}
if (mode == 4) {
if (!scoreboard.isPlayerInTeam(entry, team)) {
continue;
}
scoreboard.removePlayerFromTeam(entry, team);
// Player left team, nametag visibility may change
tracker.checkNametagVisbility(entry);
if (entry.equals(username)) {
final PacketWrapper sidebarPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
sidebarPacket.write(Types.BYTE, (byte) 1);
sidebarPacket.write(Types.STRING, scoreboard.getColorIndependentSidebar() == null ? "" : scoreboard.getColorIndependentSidebar());
sidebarPacket.scheduleSend(Protocol1_8To1_7_6_10.class);
}
} else {
scoreboard.addPlayerToTeam(entry, team);
// Player joined team, nametag visibility may change
tracker.checkNametagVisbility(entry);
if (entry.equals(username) && scoreboard.getColorDependentSidebar().containsKey(color)) {
final PacketWrapper displayObjective = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_DISPLAY_OBJECTIVE, wrapper.user());
displayObjective.write(Types.BYTE, (byte) 1);
displayObjective.write(Types.STRING, scoreboard.getColorDependentSidebar().get(color));
displayObjective.scheduleSend(Protocol1_8To1_7_6_10.class);
}
}
entryList.add(entry);
}
wrapper.write(Types.SHORT, (short) entryList.size());
for (String entry : entryList) {
wrapper.write(Types.STRING, entry);
}
}
});
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,14 +17,15 @@
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.rewriter;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.WorldBorderEmulator;
import com.viaversion.viarewind.api.type.chunk.ChunkType1_7_6;
import com.viaversion.viarewind.api.type.chunk.BulkChunkType1_7_6;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.Particles1_8;
import com.viaversion.viarewind.api.type.RewindTypes;
import com.viaversion.viarewind.api.type.chunk.BulkChunkType1_7_6;
import com.viaversion.viarewind.api.type.chunk.ChunkType1_7_6;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.Particles1_8;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage.WorldBorderEmulator;
import com.viaversion.viarewind.utils.ChatUtil;
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
@@ -33,261 +34,260 @@ import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.FixedByteArrayType;
import com.viaversion.viaversion.api.type.types.chunk.BulkChunkType1_8;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_8;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import com.viaversion.viaversion.util.ChatColorUtil;
import com.viaversion.viaversion.util.IdAndData;
public class WorldPacketRewriter1_8 extends RewriterBase<Protocol1_8To1_7_6_10> {
public WorldPacketRewriter1_8(Protocol1_8To1_7_6_10 protocol) {
super(protocol);
}
public WorldPacketRewriter1_8(Protocol1_8To1_7_6_10 protocol) {
super(protocol);
}
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_8.LEVEL_CHUNK, wrapper -> {
final ClientWorld world = wrapper.user().getClientWorld(Protocol1_8To1_7_6_10.class);
final Chunk chunk = wrapper.read(ChunkType1_8.forEnvironment(world.getEnvironment()));
protocol.getItemRewriter().handleChunk(chunk);
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_8.LEVEL_CHUNK, wrapper -> {
final ClientWorld world = wrapper.user().storables(protocol).clientWorld();
final Chunk chunk = wrapper.read(ChunkType1_8.forEnvironment(world.getEnvironment()));
protocol.getItemRewriter().handleChunk(chunk);
wrapper.write(ChunkType1_7_6.TYPE, chunk);
});
wrapper.write(ChunkType1_7_6.TYPE, chunk);
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_UPDATE, new PacketHandlers() {
@Override
protected void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.U_BYTE_POSITION); // Position
handler(wrapper -> {
int data = wrapper.read(Types.VAR_INT); // Block data
data = protocol.getItemRewriter().handleBlockId(data);
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_UPDATE, new PacketHandlers() {
@Override
protected void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.U_BYTE_POSITION); // Position
handler(wrapper -> {
int data = wrapper.read(Types.VAR_INT); // Block data
data = protocol.getItemRewriter().handleBlockId(data);
wrapper.write(Types.VAR_INT, IdAndData.getId(data)); // Block id
wrapper.write(Types.UNSIGNED_BYTE, (short) IdAndData.getData(data)); // Block data
});
}
});
wrapper.write(Types.VAR_INT, IdAndData.getId(data)); // Block id
wrapper.write(Types.UNSIGNED_BYTE, (short) IdAndData.getData(data)); // Block data
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.CHUNK_BLOCKS_UPDATE, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Chunk x
map(Types.INT); // Chunk z
protocol.registerClientbound(ClientboundPackets1_8.CHUNK_BLOCKS_UPDATE, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Chunk x
map(Types.INT); // Chunk z
handler(wrapper -> {
final BlockChangeRecord[] records = wrapper.read(Types.BLOCK_CHANGE_ARRAY);
handler(wrapper -> {
final BlockChangeRecord[] records = wrapper.read(Types.BLOCK_CHANGE_ARRAY);
wrapper.write(Types.SHORT, (short) records.length); // Record count
wrapper.write(Types.INT, records.length * 4); // Data array length (position + block id)
wrapper.write(Types.SHORT, (short) records.length); // Record count
wrapper.write(Types.INT, records.length * 4); // Data array length (position + block id)
for (BlockChangeRecord record : records) {
wrapper.write(Types.SHORT, (short) (record.getSectionX() << 12 | record.getSectionZ() << 8 | record.getY())); // Position
wrapper.write(Types.SHORT, (short) protocol.getItemRewriter().handleBlockId(record.getBlockId())); // Block id
}
});
}
});
for (BlockChangeRecord record : records) {
wrapper.write(Types.SHORT, (short) (record.getSectionX() << 12 | record.getSectionZ() << 8 | record.getY())); // Position
wrapper.write(Types.SHORT, (short) protocol.getItemRewriter().handleBlockId(record.getBlockId())); // Block id
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_EVENT, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.SHORT_POSITION); // Position
map(Types.UNSIGNED_BYTE); // Type
map(Types.UNSIGNED_BYTE); // Data
map(Types.VAR_INT); // Block id
}
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_EVENT, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.SHORT_POSITION); // Position
map(Types.UNSIGNED_BYTE); // Type
map(Types.UNSIGNED_BYTE); // Data
map(Types.VAR_INT); // Block id
}
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_DESTRUCTION, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT); // Entity id
map(Types.BLOCK_POSITION1_8, RewindTypes.INT_POSITION); // Position
map(Types.BYTE); // Progress
}
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_DESTRUCTION, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT); // Entity id
map(Types.BLOCK_POSITION1_8, RewindTypes.INT_POSITION); // Position
map(Types.BYTE); // Progress
}
});
protocol.registerClientbound(ClientboundPackets1_8.MAP_BULK_CHUNK, wrapper -> {
final Chunk[] chunks = wrapper.read(BulkChunkType1_8.TYPE);
for (Chunk chunk : chunks) {
protocol.getItemRewriter().handleChunk(chunk);
}
protocol.registerClientbound(ClientboundPackets1_8.MAP_BULK_CHUNK, wrapper -> {
final Chunk[] chunks = wrapper.read(BulkChunkType1_8.TYPE);
for (Chunk chunk : chunks) {
protocol.getItemRewriter().handleChunk(chunk);
}
wrapper.write(BulkChunkType1_7_6.TYPE, chunks);
});
wrapper.write(BulkChunkType1_7_6.TYPE, chunks);
});
protocol.registerClientbound(ClientboundPackets1_8.LEVEL_EVENT, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Effect id
map(Types.BLOCK_POSITION1_8, RewindTypes.BYTE_POSITION); // Position
map(Types.INT); // Data
map(Types.BOOLEAN); // Disable relative volume
}
});
protocol.registerClientbound(ClientboundPackets1_8.LEVEL_EVENT, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Effect id
map(Types.BLOCK_POSITION1_8, RewindTypes.BYTE_POSITION); // Position
map(Types.INT); // Data
map(Types.BOOLEAN); // Disable relative volume
}
});
protocol.registerClientbound(ClientboundPackets1_8.LEVEL_PARTICLES, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final int particleId = wrapper.read(Types.INT); // Particle id
Particles1_8 particle = Particles1_8.find(particleId);
if (particle == null) {
particle = Particles1_8.CRIT;
}
wrapper.write(Types.STRING, particle.name); // Particle name
protocol.registerClientbound(ClientboundPackets1_8.LEVEL_PARTICLES, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final int particleId = wrapper.read(Types.INT); // Particle id
Particles1_8 particle = Particles1_8.find(particleId);
if (particle == null) {
particle = Particles1_8.CRIT;
}
wrapper.write(Types.STRING, particle.name); // Particle name
});
read(Types.BOOLEAN); // Long distance
map(Types.FLOAT); // X
map(Types.FLOAT); // Y
map(Types.FLOAT); // Z
map(Types.FLOAT); // Offset x
map(Types.FLOAT); // Offset y
map(Types.FLOAT); // Offset z
map(Types.FLOAT); // Particle data
map(Types.INT); // Particle count
handler(wrapper -> {
String name = wrapper.get(Types.STRING, 0);
Particles1_8 particle = Particles1_8.find(name);
});
read(Types.BOOLEAN); // Long distance
map(Types.FLOAT); // X
map(Types.FLOAT); // Y
map(Types.FLOAT); // Z
map(Types.FLOAT); // Offset x
map(Types.FLOAT); // Offset y
map(Types.FLOAT); // Offset z
map(Types.FLOAT); // Particle data
map(Types.INT); // Particle count
handler(wrapper -> {
String name = wrapper.get(Types.STRING, 0);
Particles1_8 particle = Particles1_8.find(name);
if (particle == Particles1_8.ICON_CRACK || particle == Particles1_8.BLOCK_CRACK || particle == Particles1_8.BLOCK_DUST) {
int id = wrapper.read(Types.VAR_INT);
int data = particle == Particles1_8.ICON_CRACK ? wrapper.read(Types.VAR_INT) : id / 4096;
id %= 4096;
if (id >= 256 && id <= 422 || id >= 2256 && id <= 2267) { //item
particle = Particles1_8.ICON_CRACK;
} else if (id >= 0 && id <= 164 || id >= 170 && id <= 175) {
if (particle == Particles1_8.ICON_CRACK)
particle = Particles1_8.BLOCK_CRACK;
} else {
wrapper.cancel();
return;
}
name = particle.name + "_" + id + "_" + data;
}
if (particle == Particles1_8.ICON_CRACK || particle == Particles1_8.BLOCK_CRACK || particle == Particles1_8.BLOCK_DUST) {
int id = wrapper.read(Types.VAR_INT);
int data = particle == Particles1_8.ICON_CRACK ? wrapper.read(Types.VAR_INT) : id / 4096;
id %= 4096;
if (id >= 256 && id <= 422 || id >= 2256 && id <= 2267) { //item
particle = Particles1_8.ICON_CRACK;
} else if (id >= 0 && id <= 164 || id >= 170 && id <= 175) {
if (particle == Particles1_8.ICON_CRACK)
particle = Particles1_8.BLOCK_CRACK;
} else {
wrapper.cancel();
return;
}
name = particle.name + "_" + id + "_" + data;
}
wrapper.set(Types.STRING, 0, name);
});
}
});
wrapper.set(Types.STRING, 0, name);
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.UPDATE_SIGN, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.SHORT_POSITION); // position
handler(wrapper -> {
for (int i = 0; i < 4; i++) {
String line = wrapper.read(Types.STRING);
line = ChatUtil.jsonToLegacy(line);
line = ChatUtil.removeUnusedColor(line, '0');
protocol.registerClientbound(ClientboundPackets1_8.UPDATE_SIGN, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.SHORT_POSITION); // position
handler(wrapper -> {
for (int i = 0; i < 4; i++) {
String line = wrapper.read(Types.STRING);
line = ChatUtil.jsonToLegacy(line);
line = ChatUtil.removeUnusedColor(line, '0');
if (line.length() > 15) {
line = ChatColorUtil.stripColor(line);
if (line.length() > 15) line = line.substring(0, 15);
}
wrapper.write(Types.STRING, line);
}
});
}
});
if (line.length() > 15) {
line = ChatColorUtil.stripColor(line);
if (line.length() > 15) line = line.substring(0, 15);
}
wrapper.write(Types.STRING, line);
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper -> {
wrapper.cancel();
final int id = wrapper.read(Types.VAR_INT);
final byte scale = wrapper.read(Types.BYTE);
protocol.registerClientbound(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper -> {
wrapper.cancel();
final int id = wrapper.read(Types.VAR_INT);
final byte scale = wrapper.read(Types.BYTE);
final int iconCount = wrapper.read(Types.VAR_INT);
byte[] icons = new byte[iconCount * 4];
for (int i = 0; i < iconCount; i++) {
final int directionAndType = wrapper.read(Types.BYTE);
final int iconCount = wrapper.read(Types.VAR_INT);
byte[] icons = new byte[iconCount * 4];
for (int i = 0; i < iconCount; i++) {
final int directionAndType = wrapper.read(Types.BYTE);
icons[i * 4] = (byte) (directionAndType >> 4 & 0xF);
icons[i * 4 + 1] = wrapper.read(Types.BYTE); // x
icons[i * 4 + 2] = wrapper.read(Types.BYTE); // z
icons[i * 4 + 3] = (byte) (directionAndType & 0xF);
}
final short columns = wrapper.read(Types.UNSIGNED_BYTE);
if (columns > 0) {
final short rows = wrapper.read(Types.UNSIGNED_BYTE);
final short x = wrapper.read(Types.UNSIGNED_BYTE);
final short z = wrapper.read(Types.UNSIGNED_BYTE);
icons[i * 4] = (byte) (directionAndType >> 4 & 0xF);
icons[i * 4 + 1] = wrapper.read(Types.BYTE); // x
icons[i * 4 + 2] = wrapper.read(Types.BYTE); // z
icons[i * 4 + 3] = (byte) (directionAndType & 0xF);
}
final short columns = wrapper.read(Types.UNSIGNED_BYTE);
if (columns > 0) {
final short rows = wrapper.read(Types.UNSIGNED_BYTE);
final short x = wrapper.read(Types.UNSIGNED_BYTE);
final short z = wrapper.read(Types.UNSIGNED_BYTE);
final byte[] data = wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);
for (int column = 0; column < columns; column++) {
byte[] columnData = new byte[rows + 3];
columnData[0] = 0;
columnData[1] = (byte) (x + column);
columnData[2] = (byte) z;
final byte[] data = wrapper.read(Types.BYTE_ARRAY_PRIMITIVE);
for (int column = 0; column < columns; column++) {
byte[] columnData = new byte[rows + 3];
columnData[0] = 0;
columnData[1] = (byte) (x + column);
columnData[2] = (byte) z;
for (int i = 0; i < rows; i++) {
columnData[i + 3] = data[column + i * columns];
}
for (int i = 0; i < rows; i++) {
columnData[i + 3] = data[column + i * columns];
}
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper.user());
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) columnData.length); // data length
mapData.write(new FixedByteArrayType(columnData.length), columnData); // data
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper.user());
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) columnData.length); // data length
mapData.write(new FixedByteArrayType(columnData.length), columnData); // data
mapData.send(Protocol1_8To1_7_6_10.class);
}
}
mapData.send(Protocol1_8To1_7_6_10.class);
}
}
if (iconCount > 0) {
final byte[] iconData = new byte[iconCount * 3 + 1];
iconData[0] = 1;
for (int i = 0; i < iconCount; i++) {
iconData[i * 3 + 1] = (byte) (icons[i * 4] << 4 | icons[i * 4 + 3] & 0xF);
iconData[i * 3 + 2] = icons[i * 4 + 1];
iconData[i * 3 + 3] = icons[i * 4 + 2];
}
if (iconCount > 0) {
final byte[] iconData = new byte[iconCount * 3 + 1];
iconData[0] = 1;
for (int i = 0; i < iconCount; i++) {
iconData[i * 3 + 1] = (byte) (icons[i * 4] << 4 | icons[i * 4 + 3] & 0xF);
iconData[i * 3 + 2] = icons[i * 4 + 1];
iconData[i * 3 + 3] = icons[i * 4 + 2];
}
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper.user());
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) iconData.length); // data length
mapData.write(new FixedByteArrayType(iconData.length), iconData); // data
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper.user());
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) iconData.length); // data length
mapData.write(new FixedByteArrayType(iconData.length), iconData); // data
mapData.send(Protocol1_8To1_7_6_10.class);
}
mapData.send(Protocol1_8To1_7_6_10.class);
}
// Update scale
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper.user());
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) 2); // data length
mapData.write(new FixedByteArrayType(2), new byte[]{2, scale}); // data
// Update scale
final PacketWrapper mapData = PacketWrapper.create(ClientboundPackets1_8.MAP_ITEM_DATA, wrapper.user());
mapData.write(Types.VAR_INT, id); // map id
mapData.write(Types.SHORT, (short) 2); // data length
mapData.write(new FixedByteArrayType(2), new byte[]{2, scale}); // data
mapData.send(Protocol1_8To1_7_6_10.class);
});
mapData.send(Protocol1_8To1_7_6_10.class);
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_ENTITY_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.SHORT_POSITION); // position
map(Types.UNSIGNED_BYTE); // action
map(Types.NAMED_COMPOUND_TAG, RewindTypes.COMPRESSED_NBT); // nbt
}
});
protocol.registerClientbound(ClientboundPackets1_8.BLOCK_ENTITY_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8, RewindTypes.SHORT_POSITION); // position
map(Types.UNSIGNED_BYTE); // action
map(Types.NAMED_COMPOUND_TAG, RewindTypes.COMPRESSED_NBT); // nbt
}
});
protocol.cancelClientbound(ClientboundPackets1_8.CHANGE_DIFFICULTY);
protocol.cancelClientbound(ClientboundPackets1_8.CHANGE_DIFFICULTY);
protocol.registerClientbound(ClientboundPackets1_8.SET_BORDER, null, wrapper -> {
final WorldBorderEmulator emulator = wrapper.user().get(WorldBorderEmulator.class);
wrapper.cancel();
protocol.registerClientbound(ClientboundPackets1_8.SET_BORDER, null, wrapper -> {
final WorldBorderEmulator emulator = wrapper.user().get(WorldBorderEmulator.class);
wrapper.cancel();
final int action = wrapper.read(Types.VAR_INT);
if (action == 0) { // set size
emulator.setSize(wrapper.read(Types.DOUBLE)); // radius
} else if (action == 1) { // lerp size
emulator.lerpSize(wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), wrapper.read(Types.VAR_LONG)); // old radius, new radius, speed
} else if (action == 2) { // set center
emulator.setCenter(wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE)); // x, z
} else if (action == 3) { // initialize
emulator.init(
wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), // x, z
wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), // old radius, new radius
wrapper.read(Types.VAR_LONG) // speed
);
}
});
}
final int action = wrapper.read(Types.VAR_INT);
if (action == 0) { // set size
emulator.setSize(wrapper.read(Types.DOUBLE)); // radius
} else if (action == 1) { // lerp size
emulator.lerpSize(wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), wrapper.read(Types.VAR_LONG)); // old radius, new radius, speed
} else if (action == 2) { // set center
emulator.setCenter(wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE)); // x, z
} else if (action == 3) { // initialize
emulator.init(
wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), // x, z
wrapper.read(Types.DOUBLE), wrapper.read(Types.DOUBLE), // old radius, new radius
wrapper.read(Types.VAR_LONG) // speed
);
}
});
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,9 +24,9 @@ import com.viaversion.viaversion.api.connection.UserConnection;
* @see com.viaversion.viarewind.protocol.v1_8to1_7_6_10.provider.CompressionHandlerProvider
*/
public class CompressionStatusTracker extends StoredObject {
public boolean removeCompression = false;
public boolean removeCompression = false;
public CompressionStatusTracker(UserConnection user) {
super(user);
}
public CompressionStatusTracker(UserConnection user) {
super(user);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@ import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ServerboundPa
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.Protocol1_8To1_7_6_10;
import com.viaversion.viarewind.protocol.v1_8to1_7_6_10.data.VirtualHologramEntity;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_8;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
@@ -36,172 +37,301 @@ import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntMap;
import com.viaversion.viaversion.libs.fastutil.objects.Object2IntOpenHashMap;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import com.viaversion.viarewind.api.minecraft.entitydata.EntityDataTypes1_7_6_10;
import com.viaversion.viarewind.api.type.RewindTypes;
import com.viaversion.viarewind.protocol.v1_7_6_10to1_7_2_5.packet.ClientboundPackets1_7_2_5;
public class EntityTracker1_8 extends EntityTrackerBase {
private final Int2ObjectMap<VirtualHologramEntity> holograms = new Int2ObjectArrayMap<>();
private final Int2IntMap vehicles = new Int2IntArrayMap();
private final Int2ObjectMap<UUID> entityIdToUUID = new Int2ObjectArrayMap<>();
private final Object2IntMap<UUID> entityUUIDToId = new Object2IntOpenHashMap<>();
private final Int2ObjectMap<VirtualHologramEntity> holograms = new Int2ObjectArrayMap<>();
private final Int2IntMap extraHologramIds = new Int2IntArrayMap();
private final Int2IntMap vehicles = new Int2IntArrayMap();
private final Int2ObjectMap<UUID> entityIdToUUID = new Int2ObjectArrayMap<>();
private final Object2IntMap<UUID> entityUUIDToId = new Object2IntOpenHashMap<>();
private final Int2IntMap playerNametagHiderEntities = new Int2IntArrayMap();
private final List<EntityData> entityData = new ArrayList<>();
private final List<EntityData> entityData = new ArrayList<>();
public Integer spectatingClientEntityId;
private int clientEntityGameMode;
public Integer spectatingClientEntityId;
private int clientEntityGameMode;
public EntityTracker1_8(UserConnection connection) {
super(connection, EntityTypes1_8.EntityType.PLAYER);
}
public EntityTracker1_8(UserConnection connection) {
super(connection, EntityTypes1_8.EntityType.PLAYER);
}
@Override
public void addEntity(int id, EntityType type) {
super.addEntity(id, type);
if (type == EntityTypes1_8.EntityType.ARMOR_STAND) {
holograms.put(id, new VirtualHologramEntity(user(), id));
}
}
@Override
public TrackedEntity addEntity(int id, EntityType type) {
if (type == EntityTypes1_8.EntityType.ARMOR_STAND) {
holograms.put(id, new VirtualHologramEntity(user(), id));
}
return super.addEntity(id, type);
}
@Override
public void removeEntity(int entityId) {
super.removeEntity(entityId);
@Override
public TrackedEntity removeEntity(int entityId) {
final VirtualHologramEntity hologram = holograms.get(entityId);
if (hologram != null) {
hologram.deleteEntity();
holograms.remove(entityId);
}
if (entityIdToUUID.containsKey(entityId)) {
final UUID playerId = entityIdToUUID.remove(entityId);
if (playerNametagHiderEntities.containsKey(entityId)) {
despawnNametagHiderEntity(entityId);
}
entityUUIDToId.removeInt(playerId);
user().get(PlayerSessionStorage.class).getPlayerEquipment().remove(playerId);
}
}
if (entityIdToUUID.containsKey(entityId)) {
final UUID playerId = entityIdToUUID.remove(entityId);
@Override
public void clearEntities() {
super.clearEntities();
vehicles.clear();
}
entityUUIDToId.removeInt(playerId);
user().get(PlayerSessionStorage.class).getPlayerEquipment().remove(playerId);
}
return super.removeEntity(entityId);
}
@Override
public void setClientEntityId(int entityId) {
if (Objects.equals(this.spectatingClientEntityId, clientEntityIdOrNull())) {
this.spectatingClientEntityId = entityId;
}
super.setClientEntityId(entityId);
}
@Override
public void clearEntities() {
super.clearEntities();
holograms.clear();
extraHologramIds.clear();
vehicles.clear();
playerNametagHiderEntities.clear();
}
public Integer clientEntityIdOrNull() {
return this.hasClientEntityId() ? this.clientEntityId() : null;
}
@Override
public void setClientEntityId(int entityId) {
if (Objects.equals(this.spectatingClientEntityId, clientEntityIdOrNull())) {
this.spectatingClientEntityId = entityId;
}
super.setClientEntityId(entityId);
}
public void addPlayer(final int entityId, final UUID uuid) {
entityUUIDToId.put(uuid, entityId);
entityIdToUUID.put(entityId, uuid);
}
public Integer clientEntityIdOrNull() {
return this.hasClientEntityId() ? this.clientEntityId() : null;
}
public UUID getPlayerUUID(final int entityId) {
return entityIdToUUID.get(entityId);
}
public void addPlayer(final int entityId, final UUID uuid) {
entityUUIDToId.put(uuid, entityId);
entityIdToUUID.put(entityId, uuid);
}
public int getPlayerEntityId(final UUID uuid) {
return entityUUIDToId.getOrDefault(uuid, -1);
}
public UUID getPlayerUUID(final int entityId) {
return entityIdToUUID.get(entityId);
}
public int getVehicle(final int passengerId) {
for (Map.Entry<Integer, Integer> vehicle : vehicles.entrySet()) {
if (vehicle.getValue() == passengerId) {
return vehicle.getValue();
}
}
return -1;
}
public int getPlayerEntityId(final UUID uuid) {
return entityUUIDToId.getOrDefault(uuid, -1);
}
public int getPassenger(int vehicleId) {
return vehicles.getOrDefault(vehicleId, -1);
}
public int getVehicle(final int passengerId) {
for (Map.Entry<Integer, Integer> vehicle : vehicles.int2IntEntrySet()) {
if (vehicle.getValue() == passengerId) {
return vehicle.getValue();
}
}
return -1;
}
protected void startSneaking() {
try {
final PacketWrapper entityAction = PacketWrapper.create(ServerboundPackets1_7_2_5.PLAYER_COMMAND, user());
entityAction.write(Types.VAR_INT, this.clientEntityId()); // Entity id
entityAction.write(Types.VAR_INT, 0); // Action id
entityAction.write(Types.VAR_INT, 0); // Jump boost
public int getPassenger(int vehicleId) {
return vehicles.getOrDefault(vehicleId, -1);
}
entityAction.sendToServer(Protocol1_8To1_7_6_10.class);
} catch (Exception e) {
ViaRewind.getPlatform().getLogger().log(Level.SEVERE, "Failed to send sneak packet", e);
}
}
protected void startSneaking() {
try {
final PacketWrapper entityAction = PacketWrapper.create(ServerboundPackets1_7_2_5.PLAYER_COMMAND, user());
entityAction.write(Types.VAR_INT, this.clientEntityId()); // Entity id
entityAction.write(Types.VAR_INT, 0); // Action id
entityAction.write(Types.VAR_INT, 0); // Jump boost
public void setPassenger(final int vehicleId, final int passengerId) {
if (vehicleId == this.spectatingClientEntityId && this.spectatingClientEntityId != this.clientEntityId()) {
startSneaking();
setSpectating(this.clientEntityId());
}
entityAction.sendToServer(Protocol1_8To1_7_6_10.class);
} catch (Exception e) {
ViaRewind.getPlatform().getLogger().log(Level.SEVERE, "Failed to send sneak packet", e);
}
}
if (vehicleId == -1) {
vehicles.remove(getVehicle(passengerId));
} else if (passengerId == -1) {
vehicles.remove(vehicleId);
} else {
vehicles.put(vehicleId, passengerId);
}
}
public void setPassenger(final int vehicleId, final int passengerId) {
if (vehicleId == this.spectatingClientEntityId && this.spectatingClientEntityId != this.clientEntityId()) {
startSneaking();
setSpectating(this.clientEntityId());
}
protected void attachEntity(final int target) {
try {
final PacketWrapper attachEntity = PacketWrapper.create(ClientboundPackets1_8.SET_ENTITY_LINK, user());
attachEntity.write(Types.INT, this.clientEntityId()); // vehicle id
attachEntity.write(Types.INT, target); // passenger id
attachEntity.write(Types.BOOLEAN, false); // leash
if (vehicleId == -1) {
vehicles.remove(getVehicle(passengerId));
} else if (passengerId == -1) {
vehicles.remove(vehicleId);
} else {
vehicles.put(vehicleId, passengerId);
}
attachEntity.scheduleSend(Protocol1_8To1_7_6_10.class);
} catch (Exception e) {
ViaRewind.getPlatform().getLogger().log(Level.SEVERE, "Failed to send attach packet", e);
}
}
// Re-evaluate nametag visibility when a player entity's passenger changes
if (vehicleId != -1 && entityIdToUUID.containsKey(vehicleId)) {
checkNametagVisibility(vehicleId);
}
}
public void setSpectating(int spectating) {
if (spectating != this.clientEntityId() && getPassenger(spectating) != -1) {
startSneaking();
setSpectating(this.clientEntityId());
return;
}
if (this.spectatingClientEntityId != spectating && this.spectatingClientEntityId != this.clientEntityId()) {
attachEntity(-1);
}
this.spectatingClientEntityId = spectating;
if (spectating != this.clientEntityId()) {
attachEntity(this.spectatingClientEntityId);
}
}
protected void attachEntity(final int target) {
try {
final PacketWrapper attachEntity = PacketWrapper.create(ClientboundPackets1_8.SET_ENTITY_LINK, user());
attachEntity.write(Types.INT, this.clientEntityId()); // vehicle id
attachEntity.write(Types.INT, target); // passenger id
attachEntity.write(Types.BOOLEAN, false); // leash
public Int2ObjectMap<VirtualHologramEntity> getHolograms() {
return holograms;
}
attachEntity.scheduleSend(Protocol1_8To1_7_6_10.class);
} catch (Exception e) {
ViaRewind.getPlatform().getLogger().log(Level.SEVERE, "Failed to send attach packet", e);
}
}
public boolean isSpectator() {
return clientEntityGameMode == 3;
}
public void setSpectating(int spectating) {
if (spectating != this.clientEntityId() && getPassenger(spectating) != -1) {
startSneaking();
setSpectating(this.clientEntityId());
return;
}
if (this.spectatingClientEntityId != spectating && this.spectatingClientEntityId != this.clientEntityId()) {
attachEntity(-1);
}
this.spectatingClientEntityId = spectating;
if (spectating != this.clientEntityId()) {
attachEntity(this.spectatingClientEntityId);
}
}
public void setClientEntityGameMode(int clientEntityGameMode) {
this.clientEntityGameMode = clientEntityGameMode;
}
public void checkNametagVisibility(final int entityId) {
if (!entityIdToUUID.containsKey(entityId)) {
return;
}
final boolean shouldHide = isPlayerNametagHidden(entityId);
final boolean hasServerPassenger = getPassenger(entityId) != -1;
final boolean hasSkull = playerNametagHiderEntities.containsKey(entityId);
public void updateEntityData(List<EntityData> entityData) {
this.entityData.removeIf(first -> entityData.stream().anyMatch(second -> first.id() == second.id()));
for (final EntityData data : entityData) {
final Object value = data.value();
if (value instanceof Item item) {
this.entityData.add(new EntityData(data.id(), data.dataType(), item.copy()));
} else {
this.entityData.add(new EntityData(data.id(), data.dataType(), value));
}
}
}
if (shouldHide && !hasServerPassenger && !hasSkull) {
spawnNametagHiderEntity(entityId);
} else if ((!shouldHide || hasServerPassenger) && hasSkull) {
despawnNametagHiderEntity(entityId);
}
}
public List<EntityData> getEntityData() {
return entityData;
}
public void checkNametagVisbility(final String username) {
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
final List<GameProfileStorage.GameProfile> profiles = profileStorage.get(username, false);
for (GameProfileStorage.GameProfile profile : profiles) {
final int entityId = getPlayerEntityId(profile.uuid);
if (entityId == -1) {
return;
}
checkNametagVisibility(entityId);
}
}
private boolean isPlayerNametagHidden(final int entityId) {
final UUID uuid = entityIdToUUID.get(entityId);
if (uuid == null) {
return false;
}
final GameProfileStorage profileStorage = user().get(GameProfileStorage.class);
final GameProfileStorage.GameProfile profile = profileStorage.get(uuid);
if (profile == null) {
return false;
}
return user().get(ScoreboardTracker.class).isNametagHidden(profile.name);
}
private int getNametagHiderEntityId(final int playerEntityId) {
return Integer.MAX_VALUE - 32000 - playerEntityId;
}
private void spawnNametagHiderEntity(final int playerEntityId) {
final int entityId = getNametagHiderEntityId(playerEntityId);
playerNametagHiderEntities.put(playerEntityId, entityId);
final List<EntityData> mobData = new ArrayList<>();
mobData.add(new EntityData(0, EntityDataTypes1_7_6_10.BYTE, (byte) 0x20));
mobData.add(new EntityData(16, EntityDataTypes1_7_6_10.BYTE, (byte) 0));
final PacketWrapper spawnMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, user());
spawnMob.write(Types.VAR_INT, entityId);
spawnMob.write(Types.UNSIGNED_BYTE, (short) EntityTypes1_8.EntityType.MAGMA_CUBE.getId());
spawnMob.write(Types.INT, 0); // X
spawnMob.write(Types.INT, 0); // Y
spawnMob.write(Types.INT, 0); // Z
spawnMob.write(Types.BYTE, (byte) 0); // Yaw
spawnMob.write(Types.BYTE, (byte) 0); // Pitch
spawnMob.write(Types.BYTE, (byte) 0); // Head yaw
spawnMob.write(Types.SHORT, (short) 0); // Velocity x
spawnMob.write(Types.SHORT, (short) 0); // Velocity y
spawnMob.write(Types.SHORT, (short) 0); // Velocity z
spawnMob.write(RewindTypes.ENTITY_DATA_LIST1_7, mobData);
spawnMob.scheduleSend(Protocol1_8To1_7_6_10.class);
final PacketWrapper attach = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_LINK, user());
attach.write(Types.INT, entityId);
attach.write(Types.INT, playerEntityId);
attach.write(Types.BOOLEAN, false);
attach.scheduleSend(Protocol1_8To1_7_6_10.class);
}
private void despawnNametagHiderEntity(final int playerEntityId) {
if (!playerNametagHiderEntities.containsKey(playerEntityId)) return;
final int mobId = playerNametagHiderEntities.remove(playerEntityId);
final PacketWrapper despawn = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, user());
despawn.write(Types.BYTE, (byte) 1);
despawn.write(Types.INT, mobId);
despawn.scheduleSend(Protocol1_8To1_7_6_10.class);
}
public Int2ObjectMap<VirtualHologramEntity> getHolograms() {
return holograms;
}
public void setExtraHologramId(final int entityId, final int extraId) {
extraHologramIds.put(extraId, entityId);
}
public void removeExtraHologramId(int extraId) {
extraHologramIds.remove(extraId);
}
public int getHologramIdWithExtra(final int id) {
if (holograms.containsKey(id)) {
return id;
}
return extraHologramIds.getOrDefault(id, -1);
}
public boolean isSpectator() {
return clientEntityGameMode == 3;
}
public void setClientEntityGameMode(int clientEntityGameMode) {
this.clientEntityGameMode = clientEntityGameMode;
}
public void updateEntityData(List<EntityData> entityData) {
this.entityData.removeIf(first -> entityData.stream().anyMatch(second -> first.id() == second.id()));
for (final EntityData data : entityData) {
final Object value = data.value();
if (value instanceof Item item) {
this.entityData.add(new EntityData(data.id(), data.dataType(), item.copy()));
} else {
this.entityData.add(new EntityData(data.id(), data.dataType(), value));
}
}
}
public List<EntityData> getEntityData() {
return entityData;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,129 +17,135 @@
*/
package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Multimap;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viarewind.utils.ChatUtil;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.DataItem;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.viaversion.util.ChatColorUtil;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class GameProfileStorage extends StoredObject {
private final Map<UUID, GameProfile> properties = new HashMap<>();
private final Map<UUID, GameProfile> properties = new HashMap<>();
private final ListMultimap<String, GameProfile> nameToProfile = ArrayListMultimap.create();
public GameProfileStorage(UserConnection user) {
super(user);
}
public GameProfileStorage(UserConnection user) {
super(user);
}
public GameProfile put(UUID uuid, String name) {
GameProfile gameProfile = new GameProfile(uuid, name);
properties.put(uuid, gameProfile);
return gameProfile;
}
public GameProfile put(UUID uuid, String name, String displayName, int ping, int gamemode) {
GameProfile gameProfile = new GameProfile(uuid, name, displayName, ping, gamemode);
properties.put(uuid, gameProfile);
nameToProfile.put(name, gameProfile);
return gameProfile;
}
public GameProfile get(UUID uuid) {
return properties.get(uuid);
}
public GameProfile get(UUID uuid) {
return properties.get(uuid);
}
public GameProfile get(String name, boolean ignoreCase) {
if (ignoreCase) name = name.toLowerCase();
public List<GameProfile> get(String name, boolean ignoreCase) {
if (ignoreCase) name = name.toLowerCase();
return nameToProfile.get(name);
}
for (GameProfile profile : properties.values()) {
if (profile.name == null) continue;
public List<GameProfile> getAllWithPrefix(String prefix, boolean ignoreCase) {
if (ignoreCase) prefix = prefix.toLowerCase();
String n = ignoreCase ? profile.name.toLowerCase() : profile.name;
ArrayList<GameProfile> profiles = new ArrayList<>();
if (n.equals(name)) {
return profile;
}
}
return null;
}
for (GameProfile profile : properties.values()) {
if (profile.name == null) continue;
public List<GameProfile> getAllWithPrefix(String prefix, boolean ignoreCase) {
if (ignoreCase) prefix = prefix.toLowerCase();
String n = ignoreCase ? profile.name.toLowerCase() : profile.name;
ArrayList<GameProfile> profiles = new ArrayList<>();
if (n.startsWith(prefix)) profiles.add(profile);
}
for (GameProfile profile : properties.values()) {
if (profile.name == null) continue;
return profiles;
}
String n = ignoreCase ? profile.name.toLowerCase() : profile.name;
if (n.startsWith(prefix)) profiles.add(profile);
}
return profiles;
}
public GameProfile remove(UUID uuid) {
return properties.remove(uuid);
}
public GameProfile remove(UUID uuid) {
GameProfile removedProfile = properties.remove(uuid);
if (removedProfile != null) {
nameToProfile.get(removedProfile.name).remove(removedProfile);
}
return removedProfile;
}
public static class GameProfile {
public final String name;
public final UUID uuid;
public String displayName;
public int ping;
public List<Property> properties = new ArrayList<>();
public int gamemode = 0;
public static class GameProfile {
public final UUID uuid;
public final String name;
public String displayName;
public List<Property> properties = new ArrayList<>();
public int ping;
public int gamemode = 0;
public GameProfile(UUID uuid, String name) {
this.name = name;
this.uuid = uuid;
}
public GameProfile(UUID uuid, String name, String displayName, int ping, int gamemode) {
this.uuid = uuid;
this.name = name;
this.displayName = displayName;
this.ping = ping;
this.gamemode = gamemode;
}
public Item getSkull() {
CompoundTag tag = new CompoundTag();
CompoundTag ownerTag = new CompoundTag();
tag.put("SkullOwner", ownerTag);
ownerTag.put("Id", new StringTag(uuid.toString()));
CompoundTag properties = new CompoundTag();
ownerTag.put("Properties", properties);
ListTag textures = new ListTag(CompoundTag.class);
properties.put("textures", textures);
for (Property property : this.properties) {
if (property.name.equals("textures")) {
CompoundTag textureTag = new CompoundTag();
textureTag.put("Value", new StringTag(property.value));
if (property.signature != null) {
textureTag.put("Signature", new StringTag(property.signature));
}
textures.add(textureTag);
}
}
public Item getSkull() {
CompoundTag tag = new CompoundTag();
CompoundTag ownerTag = new CompoundTag();
tag.put("SkullOwner", ownerTag);
ownerTag.put("Id", new StringTag(uuid.toString()));
CompoundTag properties = new CompoundTag();
ownerTag.put("Properties", properties);
ListTag textures = new ListTag(CompoundTag.class);
properties.put("textures", textures);
for (Property property : this.properties) {
if (property.name.equals("textures")) {
CompoundTag textureTag = new CompoundTag();
textureTag.put("Value", new StringTag(property.value));
if (property.signature != null) {
textureTag.put("Signature", new StringTag(property.signature));
}
textures.add(textureTag);
}
}
return new DataItem(397, (byte) 1, (short) 3, tag);
}
return new DataItem(397, (byte) 1, (short) 3, tag);
}
public String getDisplayName() {
String displayName = this.displayName == null ? name : this.displayName;
if (displayName.length() > 16) displayName = ChatUtil.removeUnusedColor(displayName, 'f');
if (displayName.length() > 16) displayName = ChatColorUtil.stripColor(displayName);
if (displayName.length() > 16) displayName = displayName.substring(0, 16);
return displayName;
}
public String getLegacyDisplayName() {
String displayName = this.displayName == null ? name : this.displayName;
if (displayName.length() > 16) displayName = ChatUtil.removeUnusedColor(displayName, 'f');
if (displayName.length() > 16) displayName = ChatColorUtil.stripColor(displayName);
if (displayName.length() > 16) displayName = displayName.substring(0, 16);
return displayName;
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
}
public void setDisplayName(String displayName) {
this.displayName = displayName;
}
}
public static class Property {
public final String name;
public final String value;
public final String signature;
public static class Property {
public final String name;
public final String value;
public final String signature;
public Property(String name, String value, String signature) {
this.name = name;
this.value = value;
this.signature = signature;
}
}
public Property(String name, String value, String signature) {
this.name = name;
this.value = value;
this.signature = signature;
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,66 +19,65 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import java.util.HashMap;
import java.util.Map;
public class InventoryTracker extends StoredObject {
public static final Map<String, Integer> WINDOW_TYPE_REGISTRY = new HashMap<>();
public static final Map<String, Integer> WINDOW_TYPE_REGISTRY = new HashMap<>();
static {
WINDOW_TYPE_REGISTRY.put("minecraft:container", 0);
WINDOW_TYPE_REGISTRY.put("minecraft:chest", 0);
WINDOW_TYPE_REGISTRY.put("minecraft:crafting_table", 1);
WINDOW_TYPE_REGISTRY.put("minecraft:furnace", 2);
WINDOW_TYPE_REGISTRY.put("minecraft:dispenser", 3);
WINDOW_TYPE_REGISTRY.put("minecraft:enchanting_table", 4);
WINDOW_TYPE_REGISTRY.put("minecraft:brewing_stand", 5);
WINDOW_TYPE_REGISTRY.put("minecraft:villager", 6);
WINDOW_TYPE_REGISTRY.put("minecraft:beacon", 7);
WINDOW_TYPE_REGISTRY.put("minecraft:anvil", 8);
WINDOW_TYPE_REGISTRY.put("minecraft:hopper", 9);
WINDOW_TYPE_REGISTRY.put("minecraft:dropper", 10);
WINDOW_TYPE_REGISTRY.put("EntityHorse", 11);
}
static {
WINDOW_TYPE_REGISTRY.put("minecraft:container", 0);
WINDOW_TYPE_REGISTRY.put("minecraft:chest", 0);
WINDOW_TYPE_REGISTRY.put("minecraft:crafting_table", 1);
WINDOW_TYPE_REGISTRY.put("minecraft:furnace", 2);
WINDOW_TYPE_REGISTRY.put("minecraft:dispenser", 3);
WINDOW_TYPE_REGISTRY.put("minecraft:enchanting_table", 4);
WINDOW_TYPE_REGISTRY.put("minecraft:brewing_stand", 5);
WINDOW_TYPE_REGISTRY.put("minecraft:villager", 6);
WINDOW_TYPE_REGISTRY.put("minecraft:beacon", 7);
WINDOW_TYPE_REGISTRY.put("minecraft:anvil", 8);
WINDOW_TYPE_REGISTRY.put("minecraft:hopper", 9);
WINDOW_TYPE_REGISTRY.put("minecraft:dropper", 10);
WINDOW_TYPE_REGISTRY.put("EntityHorse", 11);
}
private final HashMap<Short, Short> windowTypeMap = new HashMap<>();
private final HashMap<Short, FurnaceData> furnaceData = new HashMap<>();
private final HashMap<Short, Short> windowTypeMap = new HashMap<>();
private final HashMap<Short, FurnaceData> furnaceData = new HashMap<>();
public short levelCost = 0;
public short anvilId = -1;
public short levelCost = 0;
public short anvilId = -1;
public InventoryTracker(UserConnection user) {
super(user);
}
public InventoryTracker(UserConnection user) {
super(user);
}
public short get(short windowId) {
return windowTypeMap.getOrDefault(windowId, (short) -1);
}
public static short getInventoryType(String name) {
return WINDOW_TYPE_REGISTRY.getOrDefault(name, -1).shortValue();
}
public void remove(short windowId) {
windowTypeMap.remove(windowId);
furnaceData.remove(windowId);
}
public short get(short windowId) {
return windowTypeMap.getOrDefault(windowId, (short) -1);
}
public static short getInventoryType(String name) {
return WINDOW_TYPE_REGISTRY.getOrDefault(name, -1).shortValue();
}
public void remove(short windowId) {
windowTypeMap.remove(windowId);
furnaceData.remove(windowId);
}
public HashMap<Short, Short> getWindowTypeMap() {
return windowTypeMap;
}
public HashMap<Short, Short> getWindowTypeMap() {
return windowTypeMap;
}
public HashMap<Short, FurnaceData> getFurnaceData() {
return furnaceData;
}
public HashMap<Short, FurnaceData> getFurnaceData() {
return furnaceData;
}
public static class FurnaceData {
public static class FurnaceData {
public short fuelLeft = 0;
public short maxFuel = 0;
public short progress = 0;
public short maxProgress = 200;
}
public short fuelLeft = 0;
public short maxFuel = 0;
public short progress = 0;
public short maxProgress = 200;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,78 +20,77 @@ package com.viaversion.viarewind.protocol.v1_8to1_7_6_10.storage;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.item.Item;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class PlayerSessionStorage extends StoredObject {
// Player abilities
public boolean sprinting, allowFly, flying, invincible, creative;
public float flySpeed, walkSpeed;
// Player inventory
private final Map<UUID, Item[]> playerEquipment = new HashMap<>();
// Player position, rotation and data
private double posX, posY, posZ;
public double receivedPosY;
public float yaw, pitch;
public boolean onGround;
// Player abilities
public boolean sprinting, allowFly, flying, invincible, creative;
public float flySpeed, walkSpeed;
public double receivedPosY;
public float yaw, pitch;
public boolean onGround;
// Player inventory
private final Map<UUID, Item[]> playerEquipment = new HashMap<>();
// Player position, rotation and data
private double posX, posY, posZ;
public PlayerSessionStorage(UserConnection user) {
super(user);
}
public PlayerSessionStorage(UserConnection user) {
super(user);
}
public byte combineAbilities() {
byte flags = 0;
public byte combineAbilities() {
byte flags = 0;
if (invincible) flags |= 8;
if (allowFly) flags |= 4;
if (flying) flags |= 2;
if (creative) flags |= 1;
if (invincible) flags |= 8;
if (allowFly) flags |= 4;
if (flying) flags |= 2;
if (creative) flags |= 1;
return flags;
}
return flags;
}
public double getPosX() {
return posX;
}
public double getPosX() {
return posX;
}
public double getPosY() {
return posY;
}
public double getPosY() {
return posY;
}
public double getPosZ() {
return posZ;
}
public double getPosZ() {
return posZ;
}
public void setPos(double x, double y, double z) {
posX = x;
posY = y;
posZ = z;
}
public void setPos(double x, double y, double z) {
posX = x;
posY = y;
posZ = z;
}
public Item getPlayerEquipment(final UUID uuid, final int slot) {
final Item[] items = playerEquipment.get(uuid);
if (items == null || slot < 0 || slot >= items.length) {
return null;
}
public Item getPlayerEquipment(final UUID uuid, final int slot) {
final Item[] items = playerEquipment.get(uuid);
if (items == null || slot < 0 || slot >= items.length) {
return null;
}
return items[slot];
}
return items[slot];
}
public void setPlayerEquipment(final UUID uuid, final Item equipment, final int slot) {
final Item[] items = playerEquipment.computeIfAbsent(uuid, it -> new Item[5]);
if (slot < 0 || slot >= items.length) {
return;
}
public void setPlayerEquipment(final UUID uuid, final Item equipment, final int slot) {
final Item[] items = playerEquipment.computeIfAbsent(uuid, it -> new Item[5]);
if (slot < 0 || slot >= items.length) {
return;
}
items[slot] = equipment;
}
items[slot] = equipment;
}
public Map<UUID, Item[]> getPlayerEquipment() {
return playerEquipment;
}
public Map<UUID, Item[]> getPlayerEquipment() {
return playerEquipment;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,163 +23,196 @@ import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import java.util.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class ScoreboardTracker extends StoredObject {
private final HashMap<String, List<String>> teams = new HashMap<>();
private final HashSet<String> objectives = new HashSet<>();
private final HashMap<String, ScoreTeam> scoreTeams = new HashMap<>();
private final HashMap<String, Byte> teamColors = new HashMap<>();
private final HashSet<String> scoreTeamNames = new HashSet<>();
private String colorIndependentSidebar;
private final HashMap<Byte, String> colorDependentSidebar = new HashMap<>();
private final HashMap<String, List<String>> teams = new HashMap<>();
private final HashSet<String> objectives = new HashSet<>();
private final HashMap<String, ScoreTeam> scoreTeams = new HashMap<>();
private final HashMap<String, Byte> teamColors = new HashMap<>();
private final HashSet<String> scoreTeamNames = new HashSet<>();
private final HashMap<Byte, String> colorDependentSidebar = new HashMap<>();
private final HashMap<String, String> teamNameTagVisibilities = new HashMap<>();
private String colorIndependentSidebar;
public ScoreboardTracker(UserConnection user) {
super(user);
}
public ScoreboardTracker(UserConnection user) {
super(user);
}
public void addPlayerToTeam(String player, String team) {
teams.computeIfAbsent(team, key -> new ArrayList<>()).add(player);
}
public void addPlayerToTeam(String player, String team) {
teams.computeIfAbsent(team, key -> new ArrayList<>()).add(player);
}
public void setTeamColor(String team, Byte color) {
teamColors.put(team, color);
}
public void setTeamColor(String team, Byte color) {
teamColors.put(team, color);
}
public Optional<Byte> getTeamColor(String team) {
return Optional.ofNullable(teamColors.get(team));
}
public Optional<Byte> getTeamColor(String team) {
return Optional.ofNullable(teamColors.get(team));
}
public void addTeam(String team) {
teams.computeIfAbsent(team, key -> new ArrayList<>());
}
public void addTeam(String team) {
teams.computeIfAbsent(team, key -> new ArrayList<>());
}
public void removeTeam(String team) {
teams.remove(team);
scoreTeams.remove(team);
teamColors.remove(team);
}
public void removeTeam(String team) {
teams.remove(team);
scoreTeams.remove(team);
teamColors.remove(team);
teamNameTagVisibilities.remove(team);
}
public boolean teamExists(String team) {
return teams.containsKey(team);
}
public boolean teamExists(String team) {
return teams.containsKey(team);
}
public void removePlayerFromTeam(String player, String team) {
List<String> teamPlayers = teams.get(team);
if (teamPlayers != null) teamPlayers.remove(player);
}
public void removePlayerFromTeam(String player, String team) {
List<String> teamPlayers = teams.get(team);
if (teamPlayers != null) teamPlayers.remove(player);
}
public boolean isPlayerInTeam(String player, String team) {
List<String> teamPlayers = teams.get(team);
return teamPlayers != null && teamPlayers.contains(player);
}
public boolean isPlayerInTeam(String player, String team) {
List<String> teamPlayers = teams.get(team);
return teamPlayers != null && teamPlayers.contains(player);
}
public boolean isPlayerInTeam(String player) {
for (List<String> teamPlayers : teams.values()) {
if (teamPlayers.contains(player)) return true;
}
return false;
}
public boolean isPlayerInTeam(String player) {
for (List<String> teamPlayers : teams.values()) {
if (teamPlayers.contains(player)) return true;
}
return false;
}
public Optional<Byte> getPlayerTeamColor(String player) {
Optional<String> team = getTeam(player);
return team.isPresent() ? getTeamColor(team.get()) : Optional.empty();
}
public Optional<Byte> getPlayerTeamColor(String player) {
Optional<String> team = getTeam(player);
return team.isPresent() ? getTeamColor(team.get()) : Optional.empty();
}
public Optional<String> getTeam(String player) {
for (Map.Entry<String, List<String>> entry : teams.entrySet())
if (entry.getValue().contains(player))
return Optional.of(entry.getKey());
return Optional.empty();
}
public Optional<String> getTeam(String player) {
for (Map.Entry<String, List<String>> entry : teams.entrySet())
if (entry.getValue().contains(player))
return Optional.of(entry.getKey());
return Optional.empty();
}
public void addObjective(String name) {
objectives.add(name);
}
public void setTeamNameTagVisibility(String team, String visibility) {
teamNameTagVisibilities.put(team, visibility);
}
public void removeObjective(String name) {
objectives.remove(name);
colorDependentSidebar.values().remove(name);
if (name.equals(colorIndependentSidebar)) {
colorIndependentSidebar = null;
}
}
public String getTeamNameTagVisibility(String team) {
return teamNameTagVisibilities.getOrDefault(team, "always");
}
public boolean objectiveExists(String name) {
return objectives.contains(name);
}
public boolean isNametagHidden(String username) {
for (Map.Entry<String, List<String>> entry : teams.entrySet()) {
if (entry.getValue().contains(username)) {
return "never".equalsIgnoreCase(teamNameTagVisibilities.getOrDefault(entry.getKey(), "always"));
}
}
return false;
}
public String sendTeamForScore(String score) {
if (score.length() <= 16) return score;
if (scoreTeams.containsKey(score)) return scoreTeams.get(score).name;
int l = 16;
int i = Math.min(16, score.length() - 16);
String name = score.substring(i, i + l);
while (scoreTeamNames.contains(name) || teams.containsKey(name)) {
i--;
while (score.length() - l - i > 16) {
l--;
if (l < 1) return score;
i = Math.min(16, score.length() - l);
}
name = score.substring(i, i + l);
}
String prefix = score.substring(0, i);
String suffix = i + l >= score.length() ? "" : score.substring(i + l);
public List<String> getTeamMembers(String team) {
return teams.getOrDefault(team, new ArrayList<>());
}
ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
scoreTeams.put(score, scoreTeam);
scoreTeamNames.add(name);
public void addObjective(String name) {
objectives.add(name);
}
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
teamPacket.write(Types.STRING, name);
teamPacket.write(Types.BYTE, (byte) 0);
teamPacket.write(Types.STRING, "ViaRewind");
teamPacket.write(Types.STRING, prefix);
teamPacket.write(Types.STRING, suffix);
teamPacket.write(Types.BYTE, (byte) 0);
teamPacket.write(Types.SHORT, (short) 1);
teamPacket.write(Types.STRING, name);
teamPacket.send(Protocol1_8To1_7_6_10.class);
public void removeObjective(String name) {
objectives.remove(name);
colorDependentSidebar.values().remove(name);
if (name.equals(colorIndependentSidebar)) {
colorIndependentSidebar = null;
}
}
return name;
}
public boolean objectiveExists(String name) {
return objectives.contains(name);
}
public String removeTeamForScore(String score) {
ScoreTeam scoreTeam = scoreTeams.remove(score);
if (scoreTeam == null) return score;
scoreTeamNames.remove(scoreTeam.name);
public String sendTeamForScore(String score) {
if (score.length() <= 16) return score;
if (scoreTeams.containsKey(score)) return scoreTeams.get(score).name;
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
teamPacket.write(Types.STRING, scoreTeam.name);
teamPacket.write(Types.BYTE, (byte) 1);
teamPacket.send(Protocol1_8To1_7_6_10.class);
// Find the longest window score[i, i+l) that:
// - is not already claimed as a team name
// - keeps prefix (score[0,i)) at most 16 chars i <= 16
// - keeps suffix (score[i+l, end)) at most 16 chars score.length()-l-i <= 16
for (int l = 16; l >= 1; l--) {
final int iMax = Math.min(16, score.length() - l);
final int iMin = Math.max(0, score.length() - l - 16);
for (int i = iMax; i >= iMin; i--) {
final String name = score.substring(i, i + l);
if (scoreTeamNames.contains(name) || teams.containsKey(name)) {
continue;
}
return scoreTeam.name;
}
final String prefix = score.substring(0, i);
final String suffix = i + l >= score.length() ? "" : score.substring(i + l);
public String getColorIndependentSidebar() {
return this.colorIndependentSidebar;
}
final ScoreTeam scoreTeam = new ScoreTeam(name, prefix, suffix);
scoreTeams.put(score, scoreTeam);
scoreTeamNames.add(name);
public HashMap<Byte, String> getColorDependentSidebar() {
return this.colorDependentSidebar;
}
final PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
teamPacket.write(Types.STRING, name);
teamPacket.write(Types.BYTE, (byte) 0);
teamPacket.write(Types.STRING, "ViaRewind");
teamPacket.write(Types.STRING, prefix);
teamPacket.write(Types.STRING, suffix);
teamPacket.write(Types.BYTE, (byte) 0);
teamPacket.write(Types.SHORT, (short) 1);
teamPacket.write(Types.STRING, name);
teamPacket.send(Protocol1_8To1_7_6_10.class);
public void setColorIndependentSidebar(String colorIndependentSidebar) {
this.colorIndependentSidebar = colorIndependentSidebar;
}
return name;
}
}
private static class ScoreTeam {
private final String prefix;
private final String suffix;
private final String name;
return score;
}
public ScoreTeam(String name, String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
this.name = name;
}
}
public String removeTeamForScore(String score) {
ScoreTeam scoreTeam = scoreTeams.remove(score);
if (scoreTeam == null) return score;
scoreTeamNames.remove(scoreTeam.name);
PacketWrapper teamPacket = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_PLAYER_TEAM, getUser());
teamPacket.write(Types.STRING, scoreTeam.name);
teamPacket.write(Types.BYTE, (byte) 1);
teamPacket.send(Protocol1_8To1_7_6_10.class);
return scoreTeam.name;
}
public String getColorIndependentSidebar() {
return this.colorIndependentSidebar;
}
public void setColorIndependentSidebar(String colorIndependentSidebar) {
this.colorIndependentSidebar = colorIndependentSidebar;
}
public HashMap<Byte, String> getColorDependentSidebar() {
return this.colorDependentSidebar;
}
private static class ScoreTeam {
private final String prefix;
private final String suffix;
private final String name;
public ScoreTeam(String name, String prefix, String suffix) {
this.prefix = prefix;
this.suffix = suffix;
this.name = name;
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,87 +21,87 @@ import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
public class WorldBorderEmulator extends StoredObject {
private double x, z;
private double oldDiameter, newDiameter;
private double x, z;
private double oldDiameter, newDiameter;
private long lerpTime;
private long lerpStartTime;
private long lerpTime;
private long lerpStartTime;
private boolean init = false;
private boolean init = false;
public WorldBorderEmulator(UserConnection user) {
super(user);
}
public WorldBorderEmulator(UserConnection user) {
super(user);
}
public void init(double x, double z, double oldDiameter, double newDiameter, long lerpTime) {
this.x = x;
this.z = z;
public void init(double x, double z, double oldDiameter, double newDiameter, long lerpTime) {
this.x = x;
this.z = z;
this.oldDiameter = oldDiameter;
this.newDiameter = newDiameter;
this.oldDiameter = oldDiameter;
this.newDiameter = newDiameter;
this.lerpTime = lerpTime;
this.lerpTime = lerpTime;
init = true;
}
init = true;
}
public void setCenter(double x, double z) {
this.x = x;
this.z = z;
}
public void setCenter(double x, double z) {
this.x = x;
this.z = z;
}
public void lerpSize(double oldDiameter, double newDiameter, long lerpTime) {
this.oldDiameter = oldDiameter;
this.newDiameter = newDiameter;
this.lerpTime = lerpTime;
this.lerpStartTime = System.currentTimeMillis();
}
public void lerpSize(double oldDiameter, double newDiameter, long lerpTime) {
this.oldDiameter = oldDiameter;
this.newDiameter = newDiameter;
this.lerpTime = lerpTime;
this.lerpStartTime = System.currentTimeMillis();
}
public void setSize(double size) {
this.oldDiameter = size;
this.newDiameter = size;
public double getSize() {
if (lerpTime == 0) {
return newDiameter;
}
this.lerpTime = 0;
}
double percent = ((double) (System.currentTimeMillis() - lerpStartTime) / (double) (lerpTime));
public double getSize() {
if (lerpTime == 0) {
return newDiameter;
}
// Clamp value
if (percent > 1.0D) percent = 1.0d;
else if (percent < 0.0D) percent = 0.0d;
double percent = ((double) (System.currentTimeMillis() - lerpStartTime) / (double) (lerpTime));
return oldDiameter + (newDiameter - oldDiameter) * percent;
}
// Clamp value
if (percent > 1.0D) percent = 1.0d;
else if (percent < 0.0D) percent = 0.0d;
public void setSize(double size) {
this.oldDiameter = size;
this.newDiameter = size;
return oldDiameter + (newDiameter - oldDiameter) * percent;
}
this.lerpTime = 0;
}
public double getX() {
return x;
}
public double getX() {
return x;
}
public double getZ() {
return z;
}
public double getZ() {
return z;
}
public boolean isInit() {
return init;
}
public boolean isInit() {
return init;
}
public enum Side {
NORTH(0, -1),
EAST(1, 0),
SOUTH(0, 1),
WEST(-1, 0);
public enum Side {
NORTH(0, -1),
EAST(1, 0),
SOUTH(0, 1),
WEST(-1, 0);
public final int modX;
public final int modZ;
public final int modX;
public final int modZ;
Side(int modX, int modZ) {
this.modX = modX;
this.modZ = modZ;
}
}
Side(int modX, int modZ) {
this.modX = modX;
this.modZ = modZ;
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -26,70 +26,72 @@ import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import java.util.logging.Level;
public class WorldBorderUpdateTask implements Runnable {
public static final int VIEW_DISTANCE = 16;
public static final int VIEW_DISTANCE = 16;
@Override
public void run() {
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
final WorldBorderEmulator worldBorderEmulatorTracker = connection.get(WorldBorderEmulator.class);
if (!worldBorderEmulatorTracker.isInit()) continue;
@Override
public void run() {
for (UserConnection connection : Via.getManager().getConnectionManager().getConnections()) {
final WorldBorderEmulator worldBorderEmulatorTracker = connection.get(WorldBorderEmulator.class);
if (worldBorderEmulatorTracker == null) {
continue;
}
if (!worldBorderEmulatorTracker.isInit()) continue;
final PlayerSessionStorage playerSession = connection.get(PlayerSessionStorage.class);
final PlayerSessionStorage playerSession = connection.get(PlayerSessionStorage.class);
double radius = worldBorderEmulatorTracker.getSize() / 2.0;
double radius = worldBorderEmulatorTracker.getSize() / 2.0;
for (WorldBorderEmulator.Side side : WorldBorderEmulator.Side.values()) {
double d;
double pos;
double center;
if (side.modX != 0) {
pos = playerSession.getPosZ();
center = worldBorderEmulatorTracker.getZ();
d = Math.abs(worldBorderEmulatorTracker.getX() + radius * side.modX - playerSession.getPosX());
} else {
center = worldBorderEmulatorTracker.getX();
pos = playerSession.getPosX();
d = Math.abs(worldBorderEmulatorTracker.getZ() + radius * side.modZ - playerSession.getPosZ());
}
if (d >= VIEW_DISTANCE) continue;
for (WorldBorderEmulator.Side side : WorldBorderEmulator.Side.values()) {
double d;
double pos;
double center;
if (side.modX != 0) {
pos = playerSession.getPosZ();
center = worldBorderEmulatorTracker.getZ();
d = Math.abs(worldBorderEmulatorTracker.getX() + radius * side.modX - playerSession.getPosX());
} else {
center = worldBorderEmulatorTracker.getX();
pos = playerSession.getPosX();
d = Math.abs(worldBorderEmulatorTracker.getZ() + radius * side.modZ - playerSession.getPosZ());
}
if (d >= VIEW_DISTANCE) continue;
double r = Math.sqrt(VIEW_DISTANCE * VIEW_DISTANCE - d * d);
double r = Math.sqrt(VIEW_DISTANCE * VIEW_DISTANCE - d * d);
double minH = Math.ceil(pos - r);
double maxH = Math.floor(pos + r);
double minV = Math.ceil(playerSession.getPosY() - r);
double maxV = Math.floor(playerSession.getPosY() + r);
double minH = Math.ceil(pos - r);
double maxH = Math.floor(pos + r);
double minV = Math.ceil(playerSession.getPosY() - r);
double maxV = Math.floor(playerSession.getPosY() + r);
if (minH < center - radius) minH = Math.ceil(center - radius);
if (maxH > center + radius) maxH = Math.floor(center + radius);
if (minV < 0.0) minV = 0.0;
if (minH < center - radius) minH = Math.ceil(center - radius);
if (maxH > center + radius) maxH = Math.floor(center + radius);
if (minV < 0.0) minV = 0.0;
double centerH = (minH + maxH) / 2.0;
double centerV = (minV + maxV) / 2.0;
double centerH = (minH + maxH) / 2.0;
double centerV = (minV + maxV) / 2.0;
double particleOffset = 2.5;
double particleOffset = 2.5;
final PacketWrapper spawnParticle = PacketWrapper.create(ClientboundPackets1_8.LEVEL_PARTICLES, connection);
spawnParticle.write(Types.STRING, ViaRewind.getConfig().getWorldBorderParticle()); // particle name
spawnParticle.write(Types.FLOAT, (float) (side.modX != 0 ? worldBorderEmulatorTracker.getX() + (radius * side.modX) : centerH)); // x
spawnParticle.write(Types.FLOAT, (float) centerV); // y
spawnParticle.write(Types.FLOAT, (float) (side.modX == 0 ? worldBorderEmulatorTracker.getZ() + (radius * side.modZ) : centerH)); // z
spawnParticle.write(Types.FLOAT, (float) (side.modX != 0 ? 0f : (maxH - minH) / particleOffset)); // offset x
spawnParticle.write(Types.FLOAT, (float) ((maxV - minV) / particleOffset)); // offset y
spawnParticle.write(Types.FLOAT, (float) (side.modX == 0 ? 0f : (maxH - minH) / particleOffset)); // offset z
spawnParticle.write(Types.FLOAT, 0F); // particle data
spawnParticle.write(Types.INT, (int) Math.floor((maxH - minH) * (maxV - minV) * 0.5));
final PacketWrapper spawnParticle = PacketWrapper.create(ClientboundPackets1_8.LEVEL_PARTICLES, connection);
spawnParticle.write(Types.STRING, ViaRewind.getConfig().getWorldBorderParticle()); // particle name
spawnParticle.write(Types.FLOAT, (float) (side.modX != 0 ? worldBorderEmulatorTracker.getX() + (radius * side.modX) : centerH)); // x
spawnParticle.write(Types.FLOAT, (float) centerV); // y
spawnParticle.write(Types.FLOAT, (float) (side.modX == 0 ? worldBorderEmulatorTracker.getZ() + (radius * side.modZ) : centerH)); // z
spawnParticle.write(Types.FLOAT, (float) (side.modX != 0 ? 0f : (maxH - minH) / particleOffset)); // offset x
spawnParticle.write(Types.FLOAT, (float) ((maxV - minV) / particleOffset)); // offset y
spawnParticle.write(Types.FLOAT, (float) (side.modX == 0 ? 0f : (maxH - minH) / particleOffset)); // offset z
spawnParticle.write(Types.FLOAT, 0F); // particle data
spawnParticle.write(Types.INT, (int) Math.floor((maxH - minH) * (maxV - minV) * 0.5));
try {
spawnParticle.send(Protocol1_8To1_7_6_10.class);
} catch (Exception e) {
ViaRewind.getPlatform().getLogger().log(Level.SEVERE, "Failed to send world border particle", e);
}
}
}
}
try {
spawnParticle.send(Protocol1_8To1_7_6_10.class);
} catch (Exception e) {
ViaRewind.getPlatform().getLogger().log(Level.SEVERE, "Failed to send world border particle", e);
}
}
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,94 +18,110 @@
package com.viaversion.viarewind.protocol.v1_9to1_8;
import com.viaversion.viabackwards.api.BackwardsProtocol;
import com.viaversion.viarewind.protocol.v1_9to1_8.data.RewindMappingData1_8;
import com.viaversion.viarewind.api.data.RewindMappingData;
import com.viaversion.viarewind.protocol.v1_9to1_8.provider.InventoryProvider;
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.BlockItemPacketRewriter1_9;
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.EntityPacketRewriter1_9;
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.*;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.*;
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.PlayerPacketRewriter1_9;
import com.viaversion.viarewind.protocol.v1_9to1_8.rewriter.WorldPacketRewriter1_9;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BlockPlaceDestroyTracker;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.BossBarStorage;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CommandBlockStateStorage;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CooldownStorage;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.EntityTracker1_9;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LevitationStorage;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.PlayerPositionTracker;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.WindowTracker;
import com.viaversion.viarewind.protocol.v1_9to1_8.task.CooldownIndicatorTask;
import com.viaversion.viarewind.protocol.v1_9to1_8.task.LevitationUpdateTask;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolStorables;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.api.platform.providers.ViaProviders;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.remapper.ValueTransformer;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.minecraft.ClientWorld;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_9;
import java.util.concurrent.TimeUnit;
public class Protocol1_9To1_8 extends BackwardsProtocol<ClientboundPackets1_9, ClientboundPackets1_8, ServerboundPackets1_9, ServerboundPackets1_8> {
public static final ValueTransformer<Double, Integer> DOUBLE_TO_INT_TIMES_32 = new ValueTransformer<>(Types.INT) {
@Override
public Integer transform(PacketWrapper wrapper, Double inputValue) {
return (int) (inputValue * 32.0D);
}
};
public static final ValueTransformer<Float, Byte> DEGREES_TO_ANGLE = new ValueTransformer<>(Types.BYTE) {
@Override
public Byte transform(PacketWrapper packetWrapper, Float degrees) {
return (byte) ((degrees / 360F) * 256);
}
};
public static final ValueTransformer<Double, Integer> DOUBLE_TO_INT_TIMES_32 = new ValueTransformer<>(Types.INT) {
@Override
public Integer transform(PacketWrapper wrapper, Double inputValue) {
return (int) Math.round(inputValue * 32.0D);
}
};
public static final ValueTransformer<Float, Byte> DEGREES_TO_ANGLE = new ValueTransformer<>(Types.BYTE) {
@Override
public Byte transform(PacketWrapper packetWrapper, Float degrees) {
return (byte) ((degrees / 360F) * 256);
}
};
public static final RewindMappingData1_8 MAPPINGS = new RewindMappingData1_8();
public static final RewindMappingData MAPPINGS = new RewindMappingData("1.9", "1.8");
private final BlockItemPacketRewriter1_9 itemRewriter = new BlockItemPacketRewriter1_9(this);
private final EntityPacketRewriter1_9 entityRewriter = new EntityPacketRewriter1_9(this);
private final BlockItemPacketRewriter1_9 itemRewriter = new BlockItemPacketRewriter1_9(this);
private final EntityPacketRewriter1_9 entityRewriter = new EntityPacketRewriter1_9(this);
public Protocol1_9To1_8() {
super(ClientboundPackets1_9.class, ClientboundPackets1_8.class, ServerboundPackets1_9.class, ServerboundPackets1_8.class);
}
public Protocol1_9To1_8() {
super(ClientboundPackets1_9.class, ClientboundPackets1_8.class, ServerboundPackets1_9.class, ServerboundPackets1_8.class);
}
@Override
protected void registerPackets() {
entityRewriter.register();
itemRewriter.register();
new PlayerPacketRewriter1_9(this).register();
new WorldPacketRewriter1_9(this).register();
}
@Override
protected void registerPackets() {
entityRewriter.register();
itemRewriter.register();
new PlayerPacketRewriter1_9(this).register();
new WorldPacketRewriter1_9(this).register();
}
@Override
public void init(UserConnection connection) {
connection.addEntityTracker(this.getClass(), new EntityTracker1_9(connection));
connection.addClientWorld(this.getClass(), new ClientWorld());
@Override
public void init(UserConnection connection) {
final ProtocolStorables storables = connection.storables(this);
storables.setEntityTracker(new EntityTracker1_9(connection));
storables.setClientWorld(new ClientWorld());
connection.put(new WindowTracker(connection));
connection.put(new LevitationStorage());
connection.put(new PlayerPositionTracker());
connection.put(new CooldownStorage());
connection.put(new BlockPlaceDestroyTracker());
connection.put(new BossBarStorage(connection));
}
connection.put(new WindowTracker(connection));
connection.put(new LevitationStorage());
connection.put(new PlayerPositionTracker());
connection.put(new CooldownStorage());
connection.put(new LastTitle());
connection.put(new BlockPlaceDestroyTracker());
connection.put(new BossBarStorage(connection));
connection.put(new CommandBlockStateStorage());
}
@Override
public void register(ViaProviders providers) {
Via.getManager().getScheduler().scheduleRepeating(new LevitationUpdateTask(), 0L, 50L, TimeUnit.MILLISECONDS);
Via.getManager().getScheduler().scheduleRepeating(new CooldownIndicatorTask(), 0L, 50L, TimeUnit.MILLISECONDS);
}
@Override
public void register(ViaProviders providers) {
providers.register(InventoryProvider.class, new InventoryProvider());
@Override
public RewindMappingData1_8 getMappingData() {
return MAPPINGS;
}
Via.getManager().getScheduler().scheduleRepeating(new LevitationUpdateTask(), 0L, 50L, TimeUnit.MILLISECONDS);
Via.getManager().getScheduler().scheduleRepeating(new CooldownIndicatorTask(), 0L, 50L, TimeUnit.MILLISECONDS);
}
@Override
public BlockItemPacketRewriter1_9 getItemRewriter() {
return itemRewriter;
}
@Override
public RewindMappingData getMappingData() {
return MAPPINGS;
}
@Override
public EntityPacketRewriter1_9 getEntityRewriter() {
return entityRewriter;
}
@Override
public BlockItemPacketRewriter1_9 getItemRewriter() {
return itemRewriter;
}
@Override
public boolean hasMappingDataToLoad() {
return true;
}
@Override
public EntityPacketRewriter1_9 getEntityRewriter() {
return entityRewriter;
}
@Override
public boolean hasMappingDataToLoad() {
return true;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,26 +25,26 @@ import com.viaversion.viaversion.libs.gson.JsonPrimitive;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
public class ActionBarVisualization implements CooldownVisualization {
private final UserConnection user;
private final UserConnection user;
public ActionBarVisualization(UserConnection user) {
this.user = user;
}
public ActionBarVisualization(UserConnection user) {
this.user = user;
}
@Override
public void show(double progress) throws Exception {
sendActionBar(CooldownVisualization.buildProgressText("■", progress));
}
@Override
public void show(double progress) throws Exception {
sendActionBar(CooldownVisualization.buildProgressText("■", progress));
}
@Override
public void hide() throws Exception {
sendActionBar("§r");
}
@Override
public void hide() throws Exception {
sendActionBar("§r");
}
private void sendActionBar(final String bar) {
PacketWrapper actionBarPacket = PacketWrapper.create(ClientboundPackets1_8.CHAT, user);
actionBarPacket.write(Types.COMPONENT, new JsonPrimitive(bar));
actionBarPacket.write(Types.BYTE, (byte) 2); // Position - above hotbar
actionBarPacket.scheduleSend(Protocol1_9To1_8.class);
}
private void sendActionBar(final String bar) {
PacketWrapper actionBarPacket = PacketWrapper.create(ClientboundPackets1_8.CHAT, user);
actionBarPacket.write(Types.COMPONENT, new JsonPrimitive(bar));
actionBarPacket.write(Types.BYTE, (byte) 2); // Position - above hotbar
actionBarPacket.scheduleSend(Protocol1_9To1_8.class);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -23,46 +23,45 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;
import java.util.UUID;
public class BossBarVisualization implements CooldownVisualization {
private final UserConnection user;
private UUID bossUUID;
private final UserConnection user;
private UUID bossUUID;
public BossBarVisualization(UserConnection user) {
this.user = user;
}
public BossBarVisualization(UserConnection user) {
this.user = user;
}
@Override
public void show(double progress) throws Exception {
final PacketWrapper setBossbar = PacketWrapper.create(ClientboundPackets1_9.BOSS_EVENT, user);
if (bossUUID == null) {
bossUUID = UUID.randomUUID();
setBossbar.write(Types.UUID, bossUUID);
setBossbar.write(Types.VAR_INT, 0); // Action - add
setBossbar.write(Types.COMPONENT, new JsonPrimitive(" ")); // Title
setBossbar.write(Types.FLOAT, (float) progress); // Health
setBossbar.write(Types.VAR_INT, 0); // Color
setBossbar.write(Types.VAR_INT, 0); // Division
setBossbar.write(Types.UNSIGNED_BYTE, (short) 0); // Flags
} else {
setBossbar.write(Types.UUID, bossUUID);
setBossbar.write(Types.VAR_INT, 2); // Action - update health
setBossbar.write(Types.FLOAT, (float) progress); // Health
}
setBossbar.send(Protocol1_9To1_8.class, false);
}
@Override
public void show(double progress) throws Exception {
final PacketWrapper setBossbar = PacketWrapper.create(ClientboundPackets1_9.BOSS_EVENT, user);
if (bossUUID == null) {
bossUUID = UUID.randomUUID();
setBossbar.write(Types.UUID, bossUUID);
setBossbar.write(Types.VAR_INT, 0); // Action - add
setBossbar.write(Types.COMPONENT, new JsonPrimitive(" ")); // Title
setBossbar.write(Types.FLOAT, (float) progress); // Health
setBossbar.write(Types.VAR_INT, 0); // Color
setBossbar.write(Types.VAR_INT, 0); // Division
setBossbar.write(Types.UNSIGNED_BYTE, (short) 0); // Flags
} else {
setBossbar.write(Types.UUID, bossUUID);
setBossbar.write(Types.VAR_INT, 2); // Action - update health
setBossbar.write(Types.FLOAT, (float) progress); // Health
}
setBossbar.send(Protocol1_9To1_8.class, false);
}
@Override
public void hide() throws Exception {
if (bossUUID == null) {
return;
}
final PacketWrapper removeBossbar = PacketWrapper.create(ClientboundPackets1_9.BOSS_EVENT, user);
removeBossbar.write(Types.UUID, bossUUID);
removeBossbar.write(Types.VAR_INT, 1); // Action - remove
removeBossbar.send(Protocol1_9To1_8.class, false);
bossUUID = null;
}
@Override
public void hide() throws Exception {
if (bossUUID == null) {
return;
}
final PacketWrapper removeBossbar = PacketWrapper.create(ClientboundPackets1_9.BOSS_EVENT, user);
removeBossbar.write(Types.UUID, bossUUID);
removeBossbar.write(Types.VAR_INT, 1); // Action - remove
removeBossbar.send(Protocol1_9To1_8.class, false);
bossUUID = null;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,44 +22,43 @@ import com.viaversion.viarewind.api.ViaRewindConfig.CooldownIndicator;
import com.viaversion.viaversion.api.connection.UserConnection;
public interface CooldownVisualization {
void show(double progress) throws Exception;
int MAX_PROGRESS_TEXT_LENGTH = 10;
void hide() throws Exception;
static String buildProgressText(String symbol, double cooldown) {
int green = (int) Math.floor(((double) MAX_PROGRESS_TEXT_LENGTH) * cooldown);
int grey = MAX_PROGRESS_TEXT_LENGTH - green;
StringBuilder builder = new StringBuilder("§8");
while (green-- > 0) builder.append(symbol);
builder.append("§7");
while (grey-- > 0) builder.append(symbol);
return builder.toString();
}
void show(double progress) throws Exception;
int MAX_PROGRESS_TEXT_LENGTH = 10;
void hide() throws Exception;
static String buildProgressText(String symbol, double cooldown) {
int green = (int) Math.floor(((double) MAX_PROGRESS_TEXT_LENGTH) * cooldown);
int grey = MAX_PROGRESS_TEXT_LENGTH - green;
StringBuilder builder = new StringBuilder("§8");
while (green-- > 0) builder.append(symbol);
builder.append("§7");
while (grey-- > 0) builder.append(symbol);
return builder.toString();
}
interface Factory {
Factory DISABLED = user -> new DisabledCooldownVisualization();
interface Factory {
CooldownVisualization create(UserConnection user);
static Factory fromConfiguration() {
try {
return fromIndicator(ViaRewind.getConfig().getCooldownIndicator());
} catch (IllegalArgumentException e) {
ViaRewind.getPlatform().getLogger().warning("Invalid cooldown-indicator setting");
return DISABLED;
}
}
static Factory fromConfiguration() {
try {
return fromIndicator(ViaRewind.getConfig().getCooldownIndicator());
} catch (IllegalArgumentException e) {
ViaRewind.getPlatform().getLogger().warning("Invalid cooldown-indicator setting");
return DISABLED;
}
}
static Factory fromIndicator(CooldownIndicator indicator) {
return switch (indicator) {
case TITLE -> TitleCooldownVisualization::new;
case BOSS_BAR -> BossBarVisualization::new;
case ACTION_BAR -> ActionBarVisualization::new;
case DISABLED -> DISABLED;
};
}
static Factory fromIndicator(CooldownIndicator indicator) {
return switch (indicator) {
case TITLE -> TitleCooldownVisualization::new;
case BOSS_BAR -> BossBarVisualization::new;
case ACTION_BAR -> ActionBarVisualization::new;
case DISABLED -> DISABLED;
};
}
Factory DISABLED = user -> new DisabledCooldownVisualization();
}
CooldownVisualization create(UserConnection user);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,12 +19,12 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.cooldown;
public class DisabledCooldownVisualization implements CooldownVisualization {
@Override
public void show(double progress) {
}
@Override
public void show(double progress) {
}
@Override
public void hide() {
}
@Override
public void hide() {
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,50 +18,95 @@
package com.viaversion.viarewind.protocol.v1_9to1_8.cooldown;
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.LastTitle;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.libs.gson.JsonPrimitive;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import java.util.function.Consumer;
public class TitleCooldownVisualization implements CooldownVisualization {
private static final int ACTION_SET_TITLE = 0;
private static final int ACTION_SET_SUBTITLE = 1;
private static final int ACTION_SET_TIMES_AND_DISPLAY = 2;
private static final int ACTION_HIDE = 3;
public static final int ACTION_SET_TITLE = 0;
public static final int ACTION_SET_SUBTITLE = 1;
public static final int ACTION_SET_TIMES_AND_DISPLAY = 2;
public static final int ACTION_HIDE = 3;
public static final int ACTION_RESET = 4;
private final UserConnection user;
private final UserConnection user;
private boolean visible;
public TitleCooldownVisualization(UserConnection user) {
this.user = user;
}
public TitleCooldownVisualization(UserConnection user) {
this.user = user;
}
@Override
public void show(double progress) throws Exception {
final String text = CooldownVisualization.buildProgressText("˙", progress);
@Override
public void show(double progress) throws Exception {
final LastTitle title = this.user.get(LastTitle.class);
if (this.visible && title.isVisible()) {
this.restoreTitle(title);
return;
}
sendTitlePacket(ACTION_SET_TITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive("")));
sendTitlePacket(ACTION_SET_SUBTITLE, wrapper -> wrapper.write(Types.COMPONENT, new JsonPrimitive(text)));
sendTitlePacket(ACTION_SET_TIMES_AND_DISPLAY, wrapper -> {
wrapper.write(Types.INT, 0);
wrapper.write(Types.INT, 2);
wrapper.write(Types.INT, 5);
});
}
final PacketWrapper setTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
setTitle.write(Types.VAR_INT, ACTION_SET_TITLE);
setTitle.write(Types.COMPONENT, new JsonPrimitive(""));
setTitle.send(Protocol1_9To1_8.class);
@Override
public void hide() throws Exception {
sendTitlePacket(ACTION_HIDE, wrapper -> {});
}
final PacketWrapper setSubtitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
setSubtitle.write(Types.VAR_INT, ACTION_SET_SUBTITLE);
setSubtitle.write(Types.COMPONENT, new JsonPrimitive(CooldownVisualization.buildProgressText("˙", progress)));
setSubtitle.send(Protocol1_9To1_8.class);
private void sendTitlePacket(int action, Consumer<PacketWrapper> writer) {
final PacketWrapper title = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
title.write(Types.VAR_INT, action);
writer.accept(title);
title.scheduleSend(Protocol1_9To1_8.class);
}
final PacketWrapper setTitleTimes = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
setTitleTimes.write(Types.VAR_INT, ACTION_SET_TIMES_AND_DISPLAY);
setTitleTimes.write(Types.INT, 0);
setTitleTimes.write(Types.INT, 2);
setTitleTimes.write(Types.INT, 5);
setTitleTimes.send(Protocol1_9To1_8.class);
this.visible = true;
}
@Override
public void hide() throws Exception {
if (!this.visible) {
return;
}
final LastTitle title = this.user.get(LastTitle.class);
if (title.isVisible()) {
this.restoreTitle(title);
return;
}
final PacketWrapper hideTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
hideTitle.write(Types.VAR_INT, ACTION_HIDE);
hideTitle.send(Protocol1_9To1_8.class);
this.visible = false;
}
private void restoreTitle(final LastTitle title) {
final PacketWrapper setTitleTimes = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
setTitleTimes.write(Types.VAR_INT, ACTION_SET_TIMES_AND_DISPLAY);
final int[] times = title.remainingTimes();
setTitleTimes.write(Types.INT, times[0]);
setTitleTimes.write(Types.INT, times[1]);
setTitleTimes.write(Types.INT, times[2]);
setTitleTimes.send(Protocol1_9To1_8.class);
if (title.subtitle() != null) {
final PacketWrapper setSubtitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
setSubtitle.write(Types.VAR_INT, ACTION_SET_SUBTITLE);
setSubtitle.write(Types.COMPONENT, title.subtitle());
setSubtitle.send(Protocol1_9To1_8.class);
}
final PacketWrapper setTitle = PacketWrapper.create(ClientboundPackets1_8.SET_TITLES, user);
setTitle.write(Types.VAR_INT, ACTION_SET_TITLE);
setTitle.write(Types.COMPONENT, title.title());
setTitle.send(Protocol1_9To1_8.class);
this.visible = false;
}
}
@@ -0,0 +1,165 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.protocol.v1_9to1_8.data;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.NumberTag;
import com.viaversion.nbt.tag.StringTag;
public final class CommandBlockState {
public static final int COMMAND_BLOCK_COMMAND_LIMIT = 32500;
private static final String IMPULSE = "IMPULSE";
private static final String REPEAT = "REPEAT";
private static final String CHAIN = "CHAIN";
private static final String CONDITIONAL = "CONDITIONAL";
private static final String UNCONDITIONAL = "UNCONDITIONAL";
private static final String ALWAYS_ACTIVE = "ALWAYSACTIVE";
private static final String REDSTONE = "REDSTONE";
private CommandBlockState() {
}
public static boolean isCommandBlock(final int blockState) {
return isImpulse(blockState) || isRepeat(blockState) || isChain(blockState);
}
public static void decorateCommand(final CompoundTag tag, final int blockState) {
if (tag == null || !isCommandBlock(blockState)) {
return;
}
final StringTag commandTag = tag.getStringTag("Command");
if (commandTag == null) {
return;
}
final Mode mode = mode(blockState);
final boolean conditional = (blockState & 8) != 0;
final boolean automatic = booleanTag(tag, "auto");
if (mode == Mode.REDSTONE && !conditional && !automatic) {
return;
}
final String prefix = mode.prefix + ' ' + (conditional ? CONDITIONAL : UNCONDITIONAL) + ' ' + (automatic ? ALWAYS_ACTIVE : REDSTONE) + ' ';
final String command = commandTag.getValue();
if (!parseDecoratedCommand(command).prefixed() && prefix.length() + command.length() <= COMMAND_BLOCK_COMMAND_LIMIT) {
commandTag.setValue(prefix + command);
}
}
public static ParsedCommand parseDecoratedCommand(final String command) {
final int first = command.indexOf(' ');
if (first == -1) {
return ParsedCommand.defaultState(command);
}
final int second = command.indexOf(' ', first + 1);
if (second == -1) {
return ParsedCommand.defaultState(command);
}
final int third = command.indexOf(' ', second + 1);
if (third == -1) {
return ParsedCommand.defaultState(command);
}
final Mode mode = Mode.fromPrefix(command.substring(0, first));
if (mode == null) {
return ParsedCommand.defaultState(command);
}
final String conditionalToken = command.substring(first + 1, second);
final boolean conditional;
if (CONDITIONAL.equalsIgnoreCase(conditionalToken)) {
conditional = true;
} else if (UNCONDITIONAL.equalsIgnoreCase(conditionalToken)) {
conditional = false;
} else {
return ParsedCommand.defaultState(command);
}
final String automaticToken = command.substring(second + 1, third);
final boolean automatic;
if (ALWAYS_ACTIVE.equalsIgnoreCase(automaticToken)) {
automatic = true;
} else if (REDSTONE.equalsIgnoreCase(automaticToken)) {
automatic = false;
} else {
return ParsedCommand.defaultState(command);
}
return new ParsedCommand(command.substring(third + 1), mode.serverName, conditional, automatic, true);
}
private static boolean booleanTag(final CompoundTag tag, final String key) {
final NumberTag numberTag = tag.getNumberTag(key);
return numberTag != null && numberTag.asByte() != 0;
}
private static Mode mode(final int blockState) {
if (isRepeat(blockState)) {
return Mode.AUTO;
}
if (isChain(blockState)) {
return Mode.SEQUENCE;
}
return Mode.REDSTONE;
}
private static boolean isImpulse(final int blockState) {
return blockState >= 2192 && blockState <= 2207;
}
private static boolean isRepeat(final int blockState) {
return blockState >= 3360 && blockState <= 3375;
}
private static boolean isChain(final int blockState) {
return blockState >= 3376 && blockState <= 3391;
}
public record ParsedCommand(String command, String mode, boolean conditional, boolean automatic, boolean prefixed) {
private static ParsedCommand defaultState(final String command) {
return new ParsedCommand(command, Mode.REDSTONE.serverName, false, false, false);
}
}
private enum Mode {
REDSTONE(IMPULSE, "REDSTONE"),
AUTO(REPEAT, "AUTO"),
SEQUENCE(CHAIN, "SEQUENCE");
private final String prefix;
private final String serverName;
Mode(final String prefix, final String serverName) {
this.prefix = prefix;
this.serverName = serverName;
}
private static Mode fromPrefix(final String prefix) {
for (final Mode mode : values()) {
if (mode.prefix.equalsIgnoreCase(prefix)) {
return mode;
}
}
return null;
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,49 +21,49 @@ import java.util.HashMap;
public class EffectIdMappings1_8 {
private static final HashMap<Integer, Integer> effects = new HashMap<>();
private static final HashMap<Integer, Integer> effects = new HashMap<>();
static {
effects.put(1003, 1002);
effects.put(1005, 1003);
effects.put(1006, 1003);
effects.put(1007, 1003);
effects.put(1008, 1003);
effects.put(1009, 1004);
effects.put(1010, 1005);
effects.put(1011, 1006);
effects.put(1012, 1006);
effects.put(1013, 1006);
effects.put(1014, 1006);
effects.put(1015, 1007);
effects.put(1016, 1008);
effects.put(1017, 1008);
effects.put(1018, 1009);
effects.put(1019, 1010);
effects.put(1020, 1011);
effects.put(1021, 1012);
effects.put(1022, 1012);
effects.put(1023, 1013);
effects.put(1024, 1014);
effects.put(1025, 1015);
effects.put(1026, 1016);
effects.put(1027, 1017);
effects.put(1028, 1018);
effects.put(1029, 1020);
effects.put(1030, 1021);
effects.put(1031, 1022);
effects.put(1032, -1);
effects.put(1033, -1);
effects.put(1034, -1);
effects.put(1035, -1);
effects.put(1036, 1003);
effects.put(1037, 1006);
static {
effects.put(1003, 1002);
effects.put(1005, 1003);
effects.put(1006, 1003);
effects.put(1007, 1003);
effects.put(1008, 1003);
effects.put(1009, 1004);
effects.put(1010, 1005);
effects.put(1011, 1006);
effects.put(1012, 1006);
effects.put(1013, 1006);
effects.put(1014, 1006);
effects.put(1015, 1007);
effects.put(1016, 1008);
effects.put(1017, 1008);
effects.put(1018, 1009);
effects.put(1019, 1010);
effects.put(1020, 1011);
effects.put(1021, 1012);
effects.put(1022, 1012);
effects.put(1023, 1013);
effects.put(1024, 1014);
effects.put(1025, 1015);
effects.put(1026, 1016);
effects.put(1027, 1017);
effects.put(1028, 1018);
effects.put(1029, 1020);
effects.put(1030, 1021);
effects.put(1031, 1022);
effects.put(1032, -1);
effects.put(1033, -1);
effects.put(1034, -1);
effects.put(1035, -1);
effects.put(1036, 1003);
effects.put(1037, 1006);
effects.put(3000, -1);
effects.put(3001, -1);
}
effects.put(3000, -1);
effects.put(3001, -1);
}
public static int getOldId(int id) {
return effects.getOrDefault(id, id);
}
public static int getOldId(int id) {
return effects.getOrDefault(id, id);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,35 +21,34 @@ import com.viaversion.viaversion.api.minecraft.entities.EntityType;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_9;
import com.viaversion.viaversion.protocols.v1_8to1_9.data.EntityDataIndex1_9;
import com.viaversion.viaversion.util.Pair;
import java.util.HashMap;
import java.util.Optional;
public class EntityDataIndex1_8 {
private static final HashMap<Pair<EntityTypes1_9.EntityType, Integer>, EntityDataIndex1_9> ENTITY_DATA_REWRITES = new HashMap<>();
private static final HashMap<Pair<EntityTypes1_9.EntityType, Integer>, EntityDataIndex1_9> ENTITY_DATA_REWRITES = new HashMap<>();
static {
for (EntityDataIndex1_9 index : EntityDataIndex1_9.values()) {
ENTITY_DATA_REWRITES.put(new Pair<>(index.getClazz(), index.getNewIndex()), index);
}
}
static {
for (EntityDataIndex1_9 index : EntityDataIndex1_9.values()) {
ENTITY_DATA_REWRITES.put(new Pair<>(index.getClazz(), index.getNewIndex()), index);
}
}
private static Optional<EntityDataIndex1_9> getIndex(final EntityType type, final int index) {
final Pair<EntityType, Integer> pair = new Pair<>(type, index);
return Optional.ofNullable(ENTITY_DATA_REWRITES.get(pair));
}
private static Optional<EntityDataIndex1_9> getIndex(final EntityType type, final int index) {
final Pair<EntityType, Integer> pair = new Pair<>(type, index);
return Optional.ofNullable(ENTITY_DATA_REWRITES.get(pair));
}
public static EntityDataIndex1_9 searchIndex(final EntityType type, final int index) {
EntityType currentType = type;
do {
final Optional<EntityDataIndex1_9> optMeta = getIndex(currentType, index);
if (optMeta.isPresent()) {
return optMeta.get();
}
currentType = currentType.getParent();
} while (currentType != null);
return null;
}
public static EntityDataIndex1_9 searchIndex(final EntityType type, final int index) {
EntityType currentType = type;
do {
final Optional<EntityDataIndex1_9> optMeta = getIndex(currentType, index);
if (optMeta.isPresent()) {
return optMeta.get();
}
currentType = currentType.getParent();
} while (currentType != null);
return null;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,63 +18,62 @@
package com.viaversion.viarewind.protocol.v1_9to1_8.data;
import com.viaversion.viaversion.protocols.v1_8to1_9.data.PotionIdMappings1_9;
import java.util.HashMap;
import java.util.Map;
public class PotionIdMappings1_8 {
public static final Map<String, String> POTION_NAME_INDEX = new HashMap<>();
public static final Map<String, Integer> POTION_NAME_TO_ID = new HashMap<>();
public static final Map<String, String> POTION_NAME_INDEX = new HashMap<>();
public static final Map<String, Integer> POTION_NAME_TO_ID = new HashMap<>();
static {
POTION_NAME_TO_ID.putAll(PotionIdMappings1_9.POTION_NAME_TO_ID);
POTION_NAME_TO_ID.put("luck", 8203); // Map new potion to similar one (luck->leaping)
static {
POTION_NAME_TO_ID.putAll(PotionIdMappings1_9.POTION_NAME_TO_ID);
POTION_NAME_TO_ID.put("luck", 8203); // Map new potion to similar one (luck->leaping)
POTION_NAME_INDEX.put("water", "§rWater Bottle");
POTION_NAME_INDEX.put("mundane", "§rMundane Potion");
POTION_NAME_INDEX.put("thick", "§rThick Potion");
POTION_NAME_INDEX.put("awkward", "§rAwkward Potion");
POTION_NAME_INDEX.put("water_splash", "§rSplash Water Bottle");
POTION_NAME_INDEX.put("mundane_splash", "§rMundane Splash Potion");
POTION_NAME_INDEX.put("thick_splash", "§rThick Splash Potion");
POTION_NAME_INDEX.put("awkward_splash", "§rAwkward Splash Potion");
POTION_NAME_INDEX.put("water_lingering", "§rLingering Water Bottle");
POTION_NAME_INDEX.put("mundane_lingering", "§rMundane Lingering Potion");
POTION_NAME_INDEX.put("thick_lingering", "§rThick Lingering Potion");
POTION_NAME_INDEX.put("awkward_lingering", "§rAwkward Lingering Potion");
POTION_NAME_INDEX.put("night_vision_lingering", "§rLingering Potion of Night Vision");
POTION_NAME_INDEX.put("long_night_vision_lingering", "§rLingering Potion of Night Vision");
POTION_NAME_INDEX.put("invisibility_lingering", "§rLingering Potion of Invisibility");
POTION_NAME_INDEX.put("long_invisibility_lingering", "§rLingering Potion of Invisibility");
POTION_NAME_INDEX.put("leaping_lingering", "§rLingering Potion of Leaping");
POTION_NAME_INDEX.put("long_leaping_lingering", "§rLingering Potion of Leaping");
POTION_NAME_INDEX.put("strong_leaping_lingering", "§rLingering Potion of Leaping");
POTION_NAME_INDEX.put("fire_resistance_lingering", "§rLingering Potion of Fire Resistance");
POTION_NAME_INDEX.put("long_fire_resistance_lingering", "§rLingering Potion of Fire Resistance");
POTION_NAME_INDEX.put("swiftness_lingering", "§rLingering Potion of Swiftness");
POTION_NAME_INDEX.put("long_swiftness_lingering", "§rLingering Potion of Swiftness");
POTION_NAME_INDEX.put("strong_swiftness_lingering", "§rLingering Potion of Swiftness");
POTION_NAME_INDEX.put("slowness_lingering", "§rLingering Potion of Slowness");
POTION_NAME_INDEX.put("long_slowness_lingering", "§rLingering Potion of Slowness");
POTION_NAME_INDEX.put("water_breathing_lingering", "§rLingering Potion of Water Breathing");
POTION_NAME_INDEX.put("long_water_breathing_lingering", "§rLingering Potion of Water Breathing");
POTION_NAME_INDEX.put("healing_lingering", "§rLingering Potion of Healing");
POTION_NAME_INDEX.put("strong_healing_lingering", "§rLingering Potion of Healing");
POTION_NAME_INDEX.put("harming_lingering", "§rLingering Potion of Harming");
POTION_NAME_INDEX.put("strong_harming_lingering", "§rLingering Potion of Harming");
POTION_NAME_INDEX.put("poison_lingering", "§rLingering Potion of Poisen");
POTION_NAME_INDEX.put("long_poison_lingering", "§rLingering Potion of Poisen");
POTION_NAME_INDEX.put("strong_poison_lingering", "§rLingering Potion of Poisen");
POTION_NAME_INDEX.put("regeneration_lingering", "§rLingering Potion of Regeneration");
POTION_NAME_INDEX.put("long_regeneration_lingering", "§rLingering Potion of Regeneration");
POTION_NAME_INDEX.put("strong_regeneration_lingering", "§rLingering Potion of Regeneration");
POTION_NAME_INDEX.put("strength_lingering", "§rLingering Potion of Strength");
POTION_NAME_INDEX.put("long_strength_lingering", "§rLingering Potion of Strength");
POTION_NAME_INDEX.put("strong_strength_lingering", "§rLingering Potion of Strength");
POTION_NAME_INDEX.put("weakness_lingering", "§rLingering Potion of Weakness");
POTION_NAME_INDEX.put("long_weakness_lingering", "§rLingering Potion of Weakness");
POTION_NAME_INDEX.put("luck_lingering", "§rLingering Potion of Luck");
POTION_NAME_INDEX.put("luck", "§rPotion of Luck");
POTION_NAME_INDEX.put("luck_splash", "§rSplash Potion of Luck");
}
POTION_NAME_INDEX.put("water", "§rWater Bottle");
POTION_NAME_INDEX.put("mundane", "§rMundane Potion");
POTION_NAME_INDEX.put("thick", "§rThick Potion");
POTION_NAME_INDEX.put("awkward", "§rAwkward Potion");
POTION_NAME_INDEX.put("water_splash", "§rSplash Water Bottle");
POTION_NAME_INDEX.put("mundane_splash", "§rMundane Splash Potion");
POTION_NAME_INDEX.put("thick_splash", "§rThick Splash Potion");
POTION_NAME_INDEX.put("awkward_splash", "§rAwkward Splash Potion");
POTION_NAME_INDEX.put("water_lingering", "§rLingering Water Bottle");
POTION_NAME_INDEX.put("mundane_lingering", "§rMundane Lingering Potion");
POTION_NAME_INDEX.put("thick_lingering", "§rThick Lingering Potion");
POTION_NAME_INDEX.put("awkward_lingering", "§rAwkward Lingering Potion");
POTION_NAME_INDEX.put("night_vision_lingering", "§rLingering Potion of Night Vision");
POTION_NAME_INDEX.put("long_night_vision_lingering", "§rLingering Potion of Night Vision");
POTION_NAME_INDEX.put("invisibility_lingering", "§rLingering Potion of Invisibility");
POTION_NAME_INDEX.put("long_invisibility_lingering", "§rLingering Potion of Invisibility");
POTION_NAME_INDEX.put("leaping_lingering", "§rLingering Potion of Leaping");
POTION_NAME_INDEX.put("long_leaping_lingering", "§rLingering Potion of Leaping");
POTION_NAME_INDEX.put("strong_leaping_lingering", "§rLingering Potion of Leaping");
POTION_NAME_INDEX.put("fire_resistance_lingering", "§rLingering Potion of Fire Resistance");
POTION_NAME_INDEX.put("long_fire_resistance_lingering", "§rLingering Potion of Fire Resistance");
POTION_NAME_INDEX.put("swiftness_lingering", "§rLingering Potion of Swiftness");
POTION_NAME_INDEX.put("long_swiftness_lingering", "§rLingering Potion of Swiftness");
POTION_NAME_INDEX.put("strong_swiftness_lingering", "§rLingering Potion of Swiftness");
POTION_NAME_INDEX.put("slowness_lingering", "§rLingering Potion of Slowness");
POTION_NAME_INDEX.put("long_slowness_lingering", "§rLingering Potion of Slowness");
POTION_NAME_INDEX.put("water_breathing_lingering", "§rLingering Potion of Water Breathing");
POTION_NAME_INDEX.put("long_water_breathing_lingering", "§rLingering Potion of Water Breathing");
POTION_NAME_INDEX.put("healing_lingering", "§rLingering Potion of Healing");
POTION_NAME_INDEX.put("strong_healing_lingering", "§rLingering Potion of Healing");
POTION_NAME_INDEX.put("harming_lingering", "§rLingering Potion of Harming");
POTION_NAME_INDEX.put("strong_harming_lingering", "§rLingering Potion of Harming");
POTION_NAME_INDEX.put("poison_lingering", "§rLingering Potion of Poisen");
POTION_NAME_INDEX.put("long_poison_lingering", "§rLingering Potion of Poisen");
POTION_NAME_INDEX.put("strong_poison_lingering", "§rLingering Potion of Poisen");
POTION_NAME_INDEX.put("regeneration_lingering", "§rLingering Potion of Regeneration");
POTION_NAME_INDEX.put("long_regeneration_lingering", "§rLingering Potion of Regeneration");
POTION_NAME_INDEX.put("strong_regeneration_lingering", "§rLingering Potion of Regeneration");
POTION_NAME_INDEX.put("strength_lingering", "§rLingering Potion of Strength");
POTION_NAME_INDEX.put("long_strength_lingering", "§rLingering Potion of Strength");
POTION_NAME_INDEX.put("strong_strength_lingering", "§rLingering Potion of Strength");
POTION_NAME_INDEX.put("weakness_lingering", "§rLingering Potion of Weakness");
POTION_NAME_INDEX.put("long_weakness_lingering", "§rLingering Potion of Weakness");
POTION_NAME_INDEX.put("luck_lingering", "§rLingering Potion of Luck");
POTION_NAME_INDEX.put("luck", "§rPotion of Luck");
POTION_NAME_INDEX.put("luck_splash", "§rSplash Potion of Luck");
}
}
@@ -1,48 +0,0 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.protocol.v1_9to1_8.data;
import com.viaversion.viarewind.api.data.RewindMappingDataLoader;
import com.viaversion.viaversion.libs.fastutil.objects.ObjectArrayList;
import com.viaversion.viaversion.libs.fastutil.objects.ObjectList;
import com.viaversion.viaversion.libs.gson.JsonArray;
import com.viaversion.viaversion.libs.gson.JsonElement;
import com.viaversion.nbt.tag.CompoundTag;
public final class RewindMappingData1_8 extends com.viaversion.viarewind.api.data.RewindMappingData {
private final ObjectList<String> sounds = new ObjectArrayList<>();
public RewindMappingData1_8() {
super("1.9.4", "1.8");
}
@Override
protected void loadExtras(CompoundTag data) {
super.loadExtras(data);
final JsonArray sounds = RewindMappingDataLoader.INSTANCE.loadData("sounds-1.9.json").getAsJsonArray("sounds");
for (final JsonElement sound : sounds) {
this.sounds.add(sound.getAsString());
}
}
public String soundName(final int soundId) {
return sounds.get(soundId);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,225 +28,226 @@ import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_8;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.viaversion.api.type.types.version.Types1_8;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public class WitherBossBar implements BossBar {
private static int highestId = Integer.MAX_VALUE - 10000;
private static int highestId = Integer.MAX_VALUE - 10000;
private final UUID uuid;
private String title;
private float health;
private boolean visible = false;
private final UUID uuid;
private final UserConnection connection;
private final int entityId = highestId++;
private String title;
private float health;
private boolean visible = false;
private double locX, locY, locZ;
private final UserConnection connection;
public WitherBossBar(UserConnection connection, UUID uuid, String title, float health) {
this.connection = connection;
this.uuid = uuid;
this.title = title;
this.health = health;
}
private final int entityId = highestId++;
private double locX, locY, locZ;
@Override
public String getTitle() {
return title;
}
public WitherBossBar(UserConnection connection, UUID uuid, String title, float health) {
this.connection = connection;
this.uuid = uuid;
this.title = title;
this.health = health;
}
@Override
public BossBar setTitle(String title) {
this.title = title;
if (this.visible) {
updateEntityData();
}
return this;
}
@Override
public String getTitle() {
return title;
}
@Override
public float getHealth() {
return health;
}
@Override
public BossBar setTitle(String title) {
this.title = title;
if (this.visible) {
updateEntityData();
}
return this;
}
@Override
public BossBar setHealth(float health) {
this.health = health;
if (this.health <= 0) {
this.health = 0.0001f;
}
if (this.visible) {
updateEntityData();
}
return this;
}
@Override
public float getHealth() {
return health;
}
@Override
public BossColor getColor() {
return null;
}
@Override
public BossBar setHealth(float health) {
this.health = health;
if (this.health <= 0) {
this.health = 0.0001f;
}
if (this.visible) {
updateEntityData();
}
return this;
}
@Override
public BossBar setColor(BossColor bossColor) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support color");
}
@Override
public BossColor getColor() {
return null;
}
@Override
public BossStyle getStyle() {
return null;
}
@Override
public BossBar setColor(BossColor bossColor) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support color");
}
@Override
public BossBar setStyle(BossStyle bossStyle) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support styles");
}
@Override
public BossStyle getStyle() {
return null;
}
@Override
public BossBar addPlayer(UUID uuid) {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar setStyle(BossStyle bossStyle) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support styles");
}
@Override
public BossBar addConnection(UserConnection userConnection) {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar addPlayer(UUID uuid) {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar removePlayer(UUID uuid) {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar addConnection(UserConnection userConnection) {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar removeConnection(UserConnection userConnection) {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar removePlayer(UUID uuid) {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar addFlag(BossFlag bossFlag) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support flags");
}
@Override
public BossBar removeConnection(UserConnection userConnection) {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar removeFlag(BossFlag bossFlag) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support flags");
}
@Override
public BossBar addFlag(BossFlag bossFlag) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support flags");
}
@Override
public boolean hasFlag(BossFlag bossFlag) {
return false;
}
@Override
public BossBar removeFlag(BossFlag bossFlag) {
throw new UnsupportedOperationException(this.getClass().getName() + " does not support flags");
}
@Override
public Set<UUID> getPlayers() {
return Collections.singleton(connection.getProtocolInfo().getUuid());
}
@Override
public boolean hasFlag(BossFlag bossFlag) {
return false;
}
@Override
public Set<UserConnection> getConnections() {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public Set<UUID> getPlayers() {
return Collections.singleton(connection.getProtocolInfo().getUuid());
}
@Override
public BossBar show() {
if (!this.visible) {
addWither();
this.visible = true;
}
return this;
}
@Override
public Set<UserConnection> getConnections() {
throw new UnsupportedOperationException(this.getClass().getName() + " is only for one UserConnection!");
}
@Override
public BossBar hide() {
if (this.visible) {
removeWither();
this.visible = false;
}
return this;
}
@Override
public BossBar show() {
if (!this.visible) {
addWither();
this.visible = true;
}
return this;
}
@Override
public boolean isVisible() {
return visible;
}
@Override
public BossBar hide() {
if (this.visible) {
removeWither();
this.visible = false;
}
return this;
}
@Override
public UUID getId() {
return this.uuid;
}
@Override
public boolean isVisible() {
return visible;
}
public void setLocation(double x, double y, double z) {
locX = x;
locY = y;
locZ = z;
updateLocation();
}
@Override
public UUID getId() {
return this.uuid;
}
private void addWither() {
final PacketWrapper addMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, this.connection);
addMob.write(Types.VAR_INT, entityId);
addMob.write(Types.UNSIGNED_BYTE, (short) 64);
addMob.write(Types.INT, (int) (locX * 32D));
addMob.write(Types.INT, (int) (locY * 32D));
addMob.write(Types.INT, (int) (locZ * 32D));
addMob.write(Types.BYTE, (byte) 0);
addMob.write(Types.BYTE, (byte) 0);
addMob.write(Types.BYTE, (byte) 0);
addMob.write(Types.SHORT, (short) 0);
addMob.write(Types.SHORT, (short) 0);
addMob.write(Types.SHORT, (short) 0);
public void setLocation(double x, double y, double z) {
locX = x;
locY = y;
locZ = z;
updateLocation();
}
final List<EntityData> entityData = new ArrayList<>();
entityData.add(new EntityData(0, EntityDataTypes1_8.BYTE, (byte) 0x20));
entityData.add(new EntityData(2, EntityDataTypes1_8.STRING, title));
entityData.add(new EntityData(3, EntityDataTypes1_8.BYTE, (byte) 1));
entityData.add(new EntityData(6, EntityDataTypes1_8.FLOAT, health * 300f));
entityData.add(new EntityData(20, EntityDataTypes1_8.INT, 880));
private void addWither() {
final PacketWrapper addMob = PacketWrapper.create(ClientboundPackets1_7_2_5.ADD_MOB, this.connection);
addMob.write(Types.VAR_INT, entityId);
addMob.write(Types.UNSIGNED_BYTE, (short) 64);
addMob.write(Types.INT, (int) (locX * 32D));
addMob.write(Types.INT, (int) (locY * 32D));
addMob.write(Types.INT, (int) (locZ * 32D));
addMob.write(Types.BYTE, (byte) 0);
addMob.write(Types.BYTE, (byte) 0);
addMob.write(Types.BYTE, (byte) 0);
addMob.write(Types.SHORT, (short) 0);
addMob.write(Types.SHORT, (short) 0);
addMob.write(Types.SHORT, (short) 0);
addMob.write(Types.ENTITY_DATA_LIST1_8, entityData);
addMob.scheduleSend(Protocol1_9To1_8.class);
}
final List<EntityData> entityData = new ArrayList<>();
entityData.add(new EntityData(0, EntityDataTypes1_8.BYTE, (byte) 0x20));
entityData.add(new EntityData(2, EntityDataTypes1_8.STRING, title));
entityData.add(new EntityData(3, EntityDataTypes1_8.BYTE, (byte) 1));
entityData.add(new EntityData(6, EntityDataTypes1_8.FLOAT, health * 300f));
private void updateLocation() {
final PacketWrapper teleportEntity = PacketWrapper.create(ClientboundPackets1_7_2_5.TELEPORT_ENTITY, this.connection);
teleportEntity.write(Types.VAR_INT, entityId);
teleportEntity.write(Types.INT, (int) (locX * 32D));
teleportEntity.write(Types.INT, (int) (locY * 32D));
teleportEntity.write(Types.INT, (int) (locZ * 32D));
teleportEntity.write(Types.BYTE, (byte) 0);
teleportEntity.write(Types.BYTE, (byte) 0);
teleportEntity.write(Types.BOOLEAN, false);
teleportEntity.scheduleSend(Protocol1_9To1_8.class);
}
addMob.write(Types1_8.ENTITY_DATA_LIST, entityData);
addMob.scheduleSend(Protocol1_9To1_8.class);
}
private void updateEntityData() {
final PacketWrapper setEntityData = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_DATA, this.connection);
setEntityData.write(Types.VAR_INT, entityId);
private void updateLocation() {
final PacketWrapper teleportEntity = PacketWrapper.create(ClientboundPackets1_7_2_5.TELEPORT_ENTITY, this.connection);
teleportEntity.write(Types.VAR_INT, entityId);
teleportEntity.write(Types.INT, (int) (locX * 32D));
teleportEntity.write(Types.INT, (int) (locY * 32D));
teleportEntity.write(Types.INT, (int) (locZ * 32D));
teleportEntity.write(Types.BYTE, (byte) 0);
teleportEntity.write(Types.BYTE, (byte) 0);
teleportEntity.write(Types.BOOLEAN, false);
teleportEntity.scheduleSend(Protocol1_9To1_8.class);
}
final List<EntityData> entityData = new ArrayList<>();
entityData.add(new EntityData(2, EntityDataTypes1_8.STRING, title));
entityData.add(new EntityData(6, EntityDataTypes1_8.FLOAT, health * 300f));
private void updateEntityData() {
final PacketWrapper setEntityData = PacketWrapper.create(ClientboundPackets1_7_2_5.SET_ENTITY_DATA, this.connection);
setEntityData.write(Types.VAR_INT, entityId);
setEntityData.write(Types.ENTITY_DATA_LIST1_8, entityData);
setEntityData.scheduleSend(Protocol1_9To1_8.class);
}
final List<EntityData> entityData = new ArrayList<>();
entityData.add(new EntityData(2, EntityDataTypes1_8.STRING, title));
entityData.add(new EntityData(6, EntityDataTypes1_8.FLOAT, health * 300f));
private void removeWither() {
final PacketWrapper removeEntity = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, this.connection);
removeEntity.write(Types.VAR_INT_ARRAY_PRIMITIVE, new int[]{entityId});
removeEntity.scheduleSend(Protocol1_9To1_8.class);
}
setEntityData.write(Types1_8.ENTITY_DATA_LIST, entityData);
setEntityData.scheduleSend(Protocol1_9To1_8.class);
}
public void setPlayerLocation(double posX, double posY, double posZ, float yaw, float pitch) {
double yawR = Math.toRadians(yaw);
double pitchR = Math.toRadians(pitch);
private void removeWither() {
final PacketWrapper removeEntity = PacketWrapper.create(ClientboundPackets1_7_2_5.REMOVE_ENTITIES, this.connection);
removeEntity.write(Types.VAR_INT_ARRAY_PRIMITIVE, new int[]{entityId});
removeEntity.scheduleSend(Protocol1_9To1_8.class);
}
posX -= Math.cos(pitchR) * Math.sin(yawR) * 48;
posY -= Math.sin(pitchR) * 48;
posZ += Math.cos(pitchR) * Math.cos(yawR) * 48;
public void setPlayerLocation(double posX, double posY, double posZ, float yaw, float pitch) {
double yawR = Math.toRadians(yaw);
double pitchR = Math.toRadians(pitch);
posX -= Math.cos(pitchR) * Math.sin(yawR) * 48;
posY -= Math.sin(pitchR) * 48;
posZ += Math.cos(pitchR) * Math.cos(yawR) * 48;
setLocation(posX, posY, posZ);
}
setLocation(posX, posY, posZ);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -15,18 +15,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.api.type.version;
package com.viaversion.viarewind.protocol.v1_9to1_8.provider;
import com.viaversion.viarewind.api.type.entitydata.EntityDataListType;
import com.viaversion.viarewind.api.type.entitydata.EntityDataType;
import com.viaversion.viaversion.api.minecraft.entitydata.EntityData;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.platform.providers.Provider;
import java.util.List;
public class InventoryProvider implements Provider {
public class Types1_7_6_10 {
public static final Type<EntityData> ENTITY_DATA = new EntityDataType();
public static final Type<List<EntityData>> ENTITY_DATA_LIST = new EntityDataListType(ENTITY_DATA);
public boolean hasElytra(final UserConnection connection) {
return false;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,324 +17,423 @@
*/
package com.viaversion.viarewind.protocol.v1_9to1_8.rewriter;
import com.viaversion.nbt.tag.ByteTag;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.IntTag;
import com.viaversion.nbt.tag.ListTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viabackwards.api.rewriters.LegacyEnchantmentRewriter;
import com.viaversion.viarewind.api.rewriter.VRBlockItemRewriter;
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viarewind.protocol.v1_9to1_8.data.PotionIdMappings1_8;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CommandBlockStateStorage;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.WindowTracker;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.minecraft.BlockChangeRecord;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.minecraft.item.Item;
import com.viaversion.viaversion.api.protocol.remapper.PacketHandlers;
import com.viaversion.viaversion.api.type.Types;
import com.viaversion.nbt.tag.*;
import com.viaversion.viaversion.protocols.v1_8to1_9.data.EntityIds1_8;
import com.viaversion.viaversion.protocols.v1_8to1_9.data.PotionIdMappings1_9;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ServerboundPackets1_8;
import com.viaversion.viaversion.util.Key;
import java.util.HashSet;
import java.util.Set;
public class BlockItemPacketRewriter1_9 extends VRBlockItemRewriter<ClientboundPackets1_9, ServerboundPackets1_8, Protocol1_9To1_8> {
public final Set<String> VALID_ATTRIBUTES = new HashSet<>();
public final Set<String> VALID_ATTRIBUTES = new HashSet<>();
private LegacyEnchantmentRewriter enchantmentRewriter;
private LegacyEnchantmentRewriter enchantmentRewriter;
public BlockItemPacketRewriter1_9(Protocol1_9To1_8 protocol) {
super(protocol, "1.9");
public BlockItemPacketRewriter1_9(Protocol1_9To1_8 protocol) {
super(protocol, "1.9");
VALID_ATTRIBUTES.add("generic.maxHealth");
VALID_ATTRIBUTES.add("generic.followRange");
VALID_ATTRIBUTES.add("generic.knockbackResistance");
VALID_ATTRIBUTES.add("generic.movementSpeed");
VALID_ATTRIBUTES.add("generic.attackDamage");
VALID_ATTRIBUTES.add("horse.jumpStrength");
VALID_ATTRIBUTES.add("zombie.spawnReinforcements");
}
VALID_ATTRIBUTES.add("generic.maxHealth");
VALID_ATTRIBUTES.add("generic.followRange");
VALID_ATTRIBUTES.add("generic.knockbackResistance");
VALID_ATTRIBUTES.add("generic.movementSpeed");
VALID_ATTRIBUTES.add("generic.attackDamage");
VALID_ATTRIBUTES.add("horse.jumpStrength");
VALID_ATTRIBUTES.add("zombie.spawnReinforcements");
}
@Override
protected void registerPackets() {
registerBlockChange(ClientboundPackets1_9.BLOCK_UPDATE);
registerMultiBlockChange(ClientboundPackets1_9.CHUNK_BLOCKS_UPDATE);
registerSetCreativeModeSlot(ServerboundPackets1_8.SET_CREATIVE_MODE_SLOT);
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_9.BLOCK_UPDATE, wrapper -> {
final BlockPosition position = wrapper.passthrough(Types.BLOCK_POSITION1_8);
final int blockState = wrapper.passthrough(Types.VAR_INT);
wrapper.user().get(CommandBlockStateStorage.class).storeOrRemove(position, blockState);
wrapper.set(Types.VAR_INT, 0, handleBlockId(blockState));
});
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_CLOSE, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
protocol.registerClientbound(ClientboundPackets1_9.CHUNK_BLOCKS_UPDATE, wrapper -> {
final int chunkX = wrapper.passthrough(Types.INT);
final int chunkZ = wrapper.passthrough(Types.INT);
final BlockChangeRecord[] records = wrapper.passthrough(Types.BLOCK_CHANGE_ARRAY);
final CommandBlockStateStorage storage = wrapper.user().get(CommandBlockStateStorage.class);
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
for (BlockChangeRecord record : records) {
final int blockState = record.getBlockId();
final BlockPosition position = new BlockPosition((chunkX << 4) + record.getSectionX(), record.getY(), (chunkZ << 4) + record.getSectionZ());
storage.storeOrRemove(position, blockState);
record.setBlockId(handleBlockId(blockState));
}
});
registerSetCreativeModeSlot(ServerboundPackets1_8.SET_CREATIVE_MODE_SLOT);
final String windowType = tracker.get(windowId);
if (windowType != null && windowType.equalsIgnoreCase("minecraft:enchanting_table")) {
tracker.clearEnchantmentProperties();
}
tracker.remove(windowId);
});
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_CLOSE, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
protocol.registerClientbound(ClientboundPackets1_9.OPEN_SCREEN, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
final String windowType = wrapper.passthrough(Types.STRING);
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
wrapper.user().get(WindowTracker.class).put(windowId, windowType);
});
final String windowType = tracker.get(windowId);
if (windowType != null && windowType.equalsIgnoreCase("minecraft:enchanting_table")) {
tracker.clearEnchantmentProperties();
}
tracker.remove(windowId);
});
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_CONTENT, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
protocol.registerClientbound(ClientboundPackets1_9.OPEN_SCREEN, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
final String windowType = wrapper.passthrough(Types.STRING);
wrapper.passthrough(Types.COMPONENT); // title
final short slotCount = wrapper.passthrough(Types.UNSIGNED_BYTE);
Item[] items = wrapper.read(Types.ITEM1_8_SHORT_ARRAY);
for (int i = 0; i < items.length; i++) {
items[i] = handleItemToClient(wrapper.user(), items[i]);
}
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
tracker.put(windowId, windowType);
tracker.openWindow(windowId, windowType, slotCount);
});
if (windowId == 0 && items.length == 46) {
Item[] old = items;
items = new Item[45];
System.arraycopy(old, 0, items, 0, 45);
} else {
final String type = wrapper.user().get(WindowTracker.class).get(windowId);
if (type != null && type.equalsIgnoreCase("minecraft:brewing_stand")) {
System.arraycopy(items, 0, wrapper.user().get(WindowTracker.class).getBrewingItems(windowId), 0, 4);
WindowTracker.updateBrewingStand(wrapper.user(), items[4], windowId);
Item[] old = items;
items = new Item[old.length - 1];
System.arraycopy(old, 0, items, 0, 4);
System.arraycopy(old, 5, items, 4, old.length - 5);
}
}
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_CONTENT, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
wrapper.write(Types.ITEM1_8_SHORT_ARRAY, items);
});
Item[] items = wrapper.read(Types.ITEM1_8_SHORT_ARRAY);
for (int i = 0; i < items.length; i++) {
items[i] = handleItemToClient(wrapper.user(), items[i]);
}
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_SLOT, wrapper -> {
final byte windowId = wrapper.passthrough(Types.BYTE);
final short slot = wrapper.passthrough(Types.SHORT);
final Item item = wrapper.passthrough(Types.ITEM1_8);
if (windowId == 0 && items.length == 46) {
Item[] old = items;
items = new Item[45];
System.arraycopy(old, 0, items, 0, 45);
} else {
final String type = wrapper.user().get(WindowTracker.class).get(windowId);
if (type != null && type.equalsIgnoreCase("minecraft:brewing_stand")) {
System.arraycopy(items, 0, wrapper.user().get(WindowTracker.class).getBrewingItems(windowId), 0, 4);
WindowTracker.updateBrewingStand(wrapper.user(), items[4], windowId);
Item[] old = items;
items = new Item[old.length - 1];
System.arraycopy(old, 0, items, 0, 4);
System.arraycopy(old, 5, items, 4, old.length - 5);
}
}
handleItemToClient(wrapper.user(), item);
if (windowId == 0 && slot == 45) {
wrapper.cancel();
return;
}
final WindowTracker windowTracker = wrapper.user().get(WindowTracker.class);
final String windowType = windowTracker.get(windowId);
if (windowType != null && windowType.equalsIgnoreCase("minecraft:brewing_stand")) {
if (slot > 4) {
wrapper.set(Types.SHORT, 0, (short) (slot - 1));
} else if (slot == 4) {
wrapper.cancel();
WindowTracker.updateBrewingStand(wrapper.user(), wrapper.get(Types.ITEM1_8, 0), windowId);
} else {
windowTracker.getBrewingItems(windowId)[slot] = wrapper.get(Types.ITEM1_8, 0);
}
}
});
wrapper.write(Types.ITEM1_8_SHORT_ARRAY, items);
});
protocol.registerServerbound(ServerboundPackets1_8.CONTAINER_CLOSE, wrapper -> {
final byte windowId = wrapper.passthrough(Types.BYTE);
wrapper.user().get(WindowTracker.class).remove(windowId);
});
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_SLOT, wrapper -> {
final byte windowId = wrapper.passthrough(Types.BYTE);
final short slot = wrapper.passthrough(Types.SHORT);
final Item item = wrapper.passthrough(Types.ITEM1_8);
protocol.registerServerbound(ServerboundPackets1_8.CONTAINER_CLICK, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Window id
map(Types.SHORT); // Slot
map(Types.BYTE); // Button
map(Types.SHORT); // Action number
map(Types.BYTE, Types.VAR_INT); // Mode
map(Types.ITEM1_8); // Clicked item
handler(wrapper -> handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));
handler(wrapper -> {
final byte windowId = wrapper.get(Types.BYTE, 0);
final String windowType = wrapper.user().get(WindowTracker.class).get(windowId);
handleItemToClient(wrapper.user(), item);
if (windowId == -2) {
// 1.8 has no window -2 (set a player-inv slot by raw index, ignoring the open container) — retarget it.
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
final short openWindow = tracker.openWindowId();
if (openWindow != 0) {
// Foreign container open: 1.8 only applies player-inv changes through it (main=size.., hotbar=size+27..).
final int size = tracker.openWindowSize();
final int containerSlot;
if (slot >= 0 && slot <= 8) {
containerSlot = size + 27 + slot;
} else if (slot >= 9 && slot <= 35) {
containerSlot = size + slot - 9;
} else { // armor/offhand: no slot in a foreign container
wrapper.cancel();
return;
}
wrapper.set(Types.BYTE, 0, (byte) openWindow);
wrapper.set(Types.SHORT, 0, (short) containerSlot);
return;
}
if (windowType != null && windowType.equalsIgnoreCase("minecraft:brewing_stand")) {
final short slot = wrapper.get(Types.SHORT, 0);
if (slot > 3) {
wrapper.set(Types.SHORT, 0, (short) (slot + 1));
}
}
});
}
});
// Otherwise window 0 with the menu slot.
final int windowSlot;
if (slot >= 0 && slot <= 8) {
windowSlot = slot + 36;
} else if (slot >= 9 && slot <= 35) {
windowSlot = slot;
} else if (slot >= 36 && slot <= 39) {
windowSlot = 44 - slot;
} else { // offhand/body/saddle: no 1.8 equivalent
wrapper.cancel();
return;
}
wrapper.set(Types.BYTE, 0, (byte) 0);
wrapper.set(Types.SHORT, 0, (short) windowSlot);
return;
}
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_DATA, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
short key = wrapper.read(Types.SHORT);
short value = wrapper.read(Types.SHORT);
if (windowId == 0 && slot == 45) {
wrapper.cancel();
return;
}
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
final String windowType = tracker.get(windowId);
if (windowType != null && windowType.equalsIgnoreCase("minecraft:enchanting_table")) {
if (key >= 4 && key <= 6) { // Store enchantment ids
tracker.putEnchantmentProperty(key, value);
wrapper.cancel();
} else if (key >= 7 && key <= 9) { // Mix levels with tracked ids
key -= 3;
final short property = tracker.getEnchantmentValue(key);
value = (short) (property | (value << 8));
}
}
wrapper.write(Types.SHORT, key);
wrapper.write(Types.SHORT, value);
});
}
final WindowTracker windowTracker = wrapper.user().get(WindowTracker.class);
final String windowType = windowTracker.get(windowId);
if (windowType != null && windowType.equalsIgnoreCase("minecraft:brewing_stand")) {
if (slot > 4) {
wrapper.set(Types.SHORT, 0, (short) (slot - 1));
} else if (slot == 4) {
wrapper.cancel();
WindowTracker.updateBrewingStand(wrapper.user(), wrapper.get(Types.ITEM1_8, 0), windowId);
} else {
windowTracker.getBrewingItems(windowId)[slot] = wrapper.get(Types.ITEM1_8, 0);
}
}
});
@Override
protected void registerRewrites() {
enchantmentRewriter = new LegacyEnchantmentRewriter(nbtTagName());
enchantmentRewriter.registerEnchantment(9, "§7Frost Walker");
enchantmentRewriter.registerEnchantment(70, "§7Mending");
}
protocol.registerServerbound(ServerboundPackets1_8.CONTAINER_CLOSE, wrapper -> {
final byte windowId = wrapper.passthrough(Types.BYTE);
wrapper.user().get(WindowTracker.class).remove(windowId);
});
@Override
public Item handleItemToClient(UserConnection connection, Item item) {
if (item == null) return null;
super.handleItemToClient(connection, item);
protocol.registerServerbound(ServerboundPackets1_8.CONTAINER_CLICK, new PacketHandlers() {
@Override
public void register() {
map(Types.BYTE); // Window id
map(Types.SHORT); // Slot
map(Types.BYTE); // Button
map(Types.SHORT); // Action number
map(Types.BYTE, Types.VAR_INT); // Mode
map(Types.ITEM1_8); // Clicked item
handler(wrapper -> handleItemToServer(wrapper.user(), wrapper.get(Types.ITEM1_8, 0)));
handler(wrapper -> {
final byte windowId = wrapper.get(Types.BYTE, 0);
final String windowType = wrapper.user().get(WindowTracker.class).get(windowId);
CompoundTag tag = item.tag();
enchantmentRewriter.handleToClient(item);
if (windowType != null && windowType.equalsIgnoreCase("minecraft:brewing_stand")) {
final short slot = wrapper.get(Types.SHORT, 0);
if (slot > 3) {
wrapper.set(Types.SHORT, 0, (short) (slot + 1));
}
}
});
}
});
CompoundTag displayTag = tag == null ? null : tag.getCompoundTag("display");
if (item.data() != 0) {
final ByteTag unbreakableTag = tag == null ? null : tag.getByteTag("Unbreakable");
if (unbreakableTag != null && unbreakableTag.asByte() != 0) {
tag.put(nbtTagName() + "|Unbreakable", new ByteTag(unbreakableTag.asByte()));
tag.remove("Unbreakable");
protocol.registerClientbound(ClientboundPackets1_9.CONTAINER_SET_DATA, wrapper -> {
final short windowId = wrapper.passthrough(Types.UNSIGNED_BYTE);
short key = wrapper.read(Types.SHORT);
short value = wrapper.read(Types.SHORT);
if (displayTag == null) { // Remove the display tag again if it was only added to emulate unbreakable
tag.put("display", displayTag = new CompoundTag());
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
}
final WindowTracker tracker = wrapper.user().get(WindowTracker.class);
final String windowType = tracker.get(windowId);
if (windowType != null && windowType.equalsIgnoreCase("minecraft:enchanting_table")) {
if (key >= 4 && key <= 6) { // Store enchantment ids
tracker.putEnchantmentProperty(key, value);
wrapper.cancel();
} else if (key >= 7 && key <= 9) { // Mix levels with tracked ids
key -= 3;
final short property = tracker.getEnchantmentValue(key);
value = (short) (property | (value << 8));
}
}
wrapper.write(Types.SHORT, key);
wrapper.write(Types.SHORT, value);
});
}
ListTag<StringTag> loreTag = displayTag.getListTag("Lore", StringTag.class);
if (loreTag == null) {
displayTag.put("Lore", loreTag = new ListTag<>(StringTag.class));
}
loreTag.add(new StringTag("§9Unbreakable"));
}
}
@Override
protected void registerRewrites() {
enchantmentRewriter = new LegacyEnchantmentRewriter(nbtTagName());
enchantmentRewriter.registerEnchantment(9, "§7Frost Walker");
enchantmentRewriter.registerEnchantment(70, "§7Mending");
}
if (item.identifier() == 383 && item.data() == 0) { // Spawn eggs
int data = 0;
final CompoundTag entityTag = tag == null ? null : tag.getCompoundTag("EntityTag");
if (entityTag != null) {
final StringTag idTag = entityTag.getStringTag("id");
if (idTag != null) {
final String id = idTag.getValue();
if (EntityIds1_8.ENTITY_NAME_TO_ID.containsKey(id)) {
data = EntityIds1_8.ENTITY_NAME_TO_ID.get(id);
} else if (displayTag == null) {
tag.put("display", displayTag = new CompoundTag());
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
displayTag.put("Name", new StringTag("§rSpawn " + id));
}
}
}
item.setData((short) data);
}
@Override
public Item handleItemToClient(UserConnection connection, Item item) {
if (item == null) return null;
final int originalId = item.identifier();
super.handleItemToClient(connection, item);
// Potion, splash potion, lingering potion
final boolean potion = item.identifier() == 373;
final boolean splashPotion = item.identifier() == 438;
final boolean lingeringPotion = item.identifier() == 441;
CompoundTag tag = item.tag();
enchantmentRewriter.handleToClient(item);
if (potion || splashPotion || lingeringPotion) {
int data = 0;
final StringTag potionTag = tag == null ? null : tag.getStringTag("Potion");
if (potionTag != null) {
String potionName = Key.stripMinecraftNamespace(potionTag.getValue());
if (PotionIdMappings1_8.POTION_NAME_TO_ID.containsKey(potionName)) {
data = PotionIdMappings1_8.POTION_NAME_TO_ID.get(potionName);
}
if (splashPotion) {
potionName += "_splash";
} else if (lingeringPotion) {
potionName += "_lingering";
}
// Don't override custom name
if ((displayTag == null || !displayTag.contains("Name")) && PotionIdMappings1_8.POTION_NAME_INDEX.containsKey(potionName)) {
tag.put("display", displayTag = new CompoundTag());
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
displayTag.put("Name", new StringTag(PotionIdMappings1_8.POTION_NAME_INDEX.get(potionName)));
}
}
if (splashPotion || lingeringPotion) { // Convert splash and lingering potions to normal potions
item.setIdentifier(373);
data += 8192;
}
CompoundTag displayTag = tag == null ? null : tag.getCompoundTag("display");
if (item.data() != 0) {
final ByteTag unbreakableTag = tag == null ? null : tag.getByteTag("Unbreakable");
if (unbreakableTag != null && unbreakableTag.asByte() != 0) {
tag.put(nbtTagName() + "|Unbreakable", new ByteTag(unbreakableTag.asByte()));
tag.remove("Unbreakable");
item.setData((short) data);
}
if (displayTag == null) { // Remove the display tag again if it was only added to emulate unbreakable
tag.put("display", displayTag = new CompoundTag());
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
}
final ListTag<CompoundTag> attributeModifiers = tag == null ? null : tag.getListTag("AttributeModifiers", CompoundTag.class);
if (attributeModifiers != null) {
tag.put(nbtTagName() + "|AttributeModifiers", attributeModifiers.copy());
attributeModifiers.getValue().removeIf(entries -> {
final StringTag nameTag = entries.getStringTag("AttributeName");
return nameTag != null && !VALID_ATTRIBUTES.contains(nameTag.getValue());
});
}
ListTag<StringTag> loreTag = displayTag.getListTag("Lore", StringTag.class);
if (loreTag == null) {
displayTag.put("Lore", loreTag = new ListTag<>(StringTag.class));
}
loreTag.add(new StringTag("§9Unbreakable"));
}
}
return item;
}
if (item.identifier() == 383 && item.data() == 0) { // Spawn eggs
int data = 0;
final CompoundTag entityTag = tag == null ? null : tag.getCompoundTag("EntityTag");
if (entityTag != null) {
final StringTag idTag = entityTag.getStringTag("id");
if (idTag != null) {
final String id = idTag.getValue();
if (EntityIds1_8.ENTITY_NAME_TO_ID.containsKey(id)) {
data = EntityIds1_8.ENTITY_NAME_TO_ID.get(id);
} else if (displayTag == null) {
tag.put("display", displayTag = new CompoundTag());
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
displayTag.put("Name", new StringTag("§rSpawn " + id));
}
}
}
item.setData((short) data);
}
@Override
public Item handleItemToServer(UserConnection connection, Item item) {
if (item == null) return null;
super.handleItemToServer(connection, item);
// Potion, splash potion, lingering potion
final boolean potion = item.identifier() == 373;
final boolean splashPotion = item.identifier() == 438;
final boolean lingeringPotion = item.identifier() == 441;
CompoundTag tag = item.tag();
if (potion || splashPotion || lingeringPotion) {
int data = 0;
final StringTag potionTag = tag == null ? null : tag.getStringTag("Potion");
if (potionTag != null) {
String potionName = Key.stripMinecraftNamespace(potionTag.getValue());
if (PotionIdMappings1_8.POTION_NAME_TO_ID.containsKey(potionName)) {
data = PotionIdMappings1_8.POTION_NAME_TO_ID.get(potionName);
}
if (splashPotion) {
potionName += "_splash";
} else if (lingeringPotion) {
potionName += "_lingering";
}
// Don't override custom name
if ((displayTag == null || !displayTag.contains("Name")) && PotionIdMappings1_8.POTION_NAME_INDEX.containsKey(potionName)) {
tag.put("display", displayTag = new CompoundTag());
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
displayTag.put("Name", new StringTag(PotionIdMappings1_8.POTION_NAME_INDEX.get(potionName)));
}
}
if (splashPotion || lingeringPotion) { // Convert splash and lingering potions to normal potions
item.setIdentifier(373);
data += 8192;
}
enchantmentRewriter.handleToServer(item);
item.setData((short) data);
}
if (item.identifier() == 383 && item.data() != 0) { // Spawn eggs
if ((tag == null || !tag.contains("EntityTag")) && EntityIds1_8.ENTITY_ID_TO_NAME.containsKey((int) item.data())) {
if (tag == null) {
item.setTag(tag = new CompoundTag());
}
final CompoundTag entityTag = new CompoundTag();
entityTag.put("id", new StringTag(EntityIds1_8.ENTITY_ID_TO_NAME.get((int) item.data())));
tag.put("EntityTag", entityTag);
}
item.setData((short) 0);
}
final ListTag<CompoundTag> attributeModifiers = tag == null ? null : tag.getListTag("AttributeModifiers", CompoundTag.class);
if (attributeModifiers != null) {
tag.put(nbtTagName() + "|AttributeModifiers", attributeModifiers.copy());
attributeModifiers.getValue().removeIf(entries -> {
final StringTag nameTag = entries.getStringTag("AttributeName");
return nameTag != null && !VALID_ATTRIBUTES.contains(nameTag.getValue());
});
}
if (item.identifier() == 373 && (tag == null || !tag.contains("Potion"))) { // Potions
// Colors the fake leather armor for an elytra gray
if (originalId == 443) {
if (tag == null) {
item.setTag(tag = new CompoundTag());
}
CompoundTag display = tag.getCompoundTag("display");
if (display == null) {
tag.put("display", display = new CompoundTag());
}
display.put("color", new IntTag(0x737373)); // Gray
tag.put(nbtTagName() + "|noDisplay", new ByteTag(true));
}
// Makes the fake banner for a shield brown if it has no banner patterns
if (originalId == 442) {
final CompoundTag blockEntityTag = tag == null ? null : tag.getCompoundTag("BlockEntityTag");
final ListTag<CompoundTag> patterns = blockEntityTag == null ? null : blockEntityTag.getListTag("Patterns", CompoundTag.class);
if (patterns == null || patterns.isEmpty()) {
item.setData((short) 3); // Brown
if (tag == null) {
item.setTag(tag = new CompoundTag());
}
tag.put(nbtTagName() + "|noData", new ByteTag(true));
}
}
return item;
}
@Override
public Item handleItemToServer(UserConnection connection, Item item) {
if (item == null) return null;
super.handleItemToServer(connection, item);
CompoundTag tag = item.tag();
enchantmentRewriter.handleToServer(item);
if (item.identifier() == 383 && item.data() != 0) { // Spawn eggs
if ((tag == null || !tag.contains("EntityTag")) && EntityIds1_8.ENTITY_ID_TO_NAME.containsKey((int) item.data())) {
if (tag == null) {
item.setTag(tag = new CompoundTag());
}
final CompoundTag entityTag = new CompoundTag();
entityTag.put("id", new StringTag(EntityIds1_8.ENTITY_ID_TO_NAME.get((int) item.data())));
tag.put("EntityTag", entityTag);
}
item.setData((short) 0);
}
if (item.identifier() == 373) { // Potions
// Restore the splash id even when a Potion tag is present (e.g. items re-sent by a creative mode client)
if (item.data() >= 16384) {
item.setIdentifier(438);
item.setData((short) (item.data() - 8192));
}
item.setIdentifier(438);
item.setData((short) (item.data() - 8192));
}
if (tag == null) {
item.setTag(tag = new CompoundTag());
if (tag == null) {
item.setTag(tag = new CompoundTag());
final String name = item.data() == 8192 ? "water" : PotionIdMappings1_9.potionNameFromDamage(item.data());
tag.put("Potion", new StringTag("minecraft:" + name));
}
item.setData((short) 0);
}
final String name = item.data() == 8192 ? "water" : PotionIdMappings1_9.potionNameFromDamage(item.data());
tag.put("Potion", new StringTag("minecraft:" + name));
}
item.setData((short) 0);
}
if (tag == null) {
return item;
}
final Tag noDisplayTag = tag.remove(nbtTagName() + "|noDisplay");
if (noDisplayTag != null) {
tag.remove("display");
if (tag.isEmpty()) {
item.setTag(null);
}
}
final Tag unbreakableTag = tag.remove(nbtTagName() + "|Unbreakable");
if (unbreakableTag != null) {
tag.put("Unbreakable", unbreakableTag);
}
final Tag attributeModifiersTag = tag.remove(nbtTagName() + "|AttributeModifiers");
if (attributeModifiersTag != null) {
tag.put("AttributeModifiers", attributeModifiersTag);
}
if (tag == null) {
return item;
}
if (tag.remove(nbtTagName() + "|noData") != null) {
item.setData((short) 0);
}
final Tag noDisplayTag = tag.remove(nbtTagName() + "|noDisplay");
if (noDisplayTag != null) {
tag.remove("display");
if (tag.isEmpty()) {
item.setTag(null);
}
}
final Tag unbreakableTag = tag.remove(nbtTagName() + "|Unbreakable");
if (unbreakableTag != null) {
tag.put("Unbreakable", unbreakableTag);
}
final Tag attributeModifiersTag = tag.remove(nbtTagName() + "|AttributeModifiers");
if (attributeModifiersTag != null) {
tag.put("AttributeModifiers", attributeModifiersTag);
}
return item;
}
return item;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,10 +17,14 @@
*/
package com.viaversion.viarewind.protocol.v1_9to1_8.rewriter;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.StringTag;
import com.viaversion.nbt.tag.Tag;
import com.viaversion.viarewind.ViaRewind;
import com.viaversion.viarewind.protocol.v1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viarewind.protocol.v1_9to1_8.data.CommandBlockState;
import com.viaversion.viarewind.protocol.v1_9to1_8.data.EffectIdMappings1_8;
import com.viaversion.viarewind.protocol.v1_9to1_8.storage.CommandBlockStateStorage;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import com.viaversion.viaversion.api.minecraft.Environment;
import com.viaversion.viaversion.api.minecraft.chunks.BaseChunk;
@@ -37,235 +41,272 @@ import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_8;
import com.viaversion.viaversion.api.type.types.chunk.ChunkType1_9_1;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_8;
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;
import com.viaversion.nbt.tag.CompoundTag;
import com.viaversion.nbt.tag.StringTag;
import java.util.ArrayList;
public class WorldPacketRewriter1_9 extends RewriterBase<Protocol1_9To1_8> {
public WorldPacketRewriter1_9(Protocol1_9To1_8 protocol) {
super(protocol);
}
public WorldPacketRewriter1_9(Protocol1_9To1_8 protocol) {
super(protocol);
}
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_9.BLOCK_ENTITY_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8); // Position
map(Types.UNSIGNED_BYTE); // Action
map(Types.NAMED_COMPOUND_TAG); // Tag
handler(wrapper -> {
final CompoundTag tag = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);
@Override
protected void registerPackets() {
protocol.registerClientbound(ClientboundPackets1_9.BLOCK_ENTITY_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8); // Position
map(Types.UNSIGNED_BYTE); // Action
map(Types.NAMED_COMPOUND_TAG); // Tag
handler(wrapper -> {
final CompoundTag tag = wrapper.get(Types.NAMED_COMPOUND_TAG, 0);
final short action = wrapper.get(Types.UNSIGNED_BYTE, 0);
if (tag.remove("SpawnData") instanceof CompoundTag spawnData) {
final Tag id = spawnData.remove("id");
if (id instanceof StringTag) {
tag.put("EntityId", id);
}
}
});
}
});
if (action == 2) {
final BlockPosition position = wrapper.get(Types.BLOCK_POSITION1_8, 0);
CommandBlockState.decorateCommand(tag, wrapper.user().get(CommandBlockStateStorage.class).state(position));
}
protocol.registerClientbound(ClientboundPackets1_9.BLOCK_EVENT, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8); // Position
map(Types.UNSIGNED_BYTE); // Byte 1 (depending on block type)
map(Types.UNSIGNED_BYTE); // Byte 2 (depending on block type)
map(Types.VAR_INT); // Block
handler(wrapper -> {
final int block = wrapper.get(Types.VAR_INT, 0);
if (block >= 219 && block <= 234) {
wrapper.set(Types.VAR_INT, 0, 130);
}
});
}
});
if (tag.remove("SpawnData") instanceof CompoundTag spawnData) {
final Tag id = spawnData.remove("id");
if (id instanceof StringTag) {
tag.put("EntityId", id);
}
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_9.CUSTOM_SOUND, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING); // Sound name
handler(wrapper -> {
String name = wrapper.get(Types.STRING, 0);
name = protocol.getMappingData().getMappedNamedSound(name);
if (name == null) {
wrapper.cancel();
} else {
wrapper.set(Types.STRING, 0, name);
}
});
read(Types.VAR_INT); // Sound category
map(Types.INT); // Effect position x
map(Types.INT); // Effect position y
map(Types.INT); // Effect position z
map(Types.FLOAT); // Volume
map(Types.UNSIGNED_BYTE); // Pitch
}
});
protocol.registerClientbound(ClientboundPackets1_9.BLOCK_EVENT, new PacketHandlers() {
@Override
public void register() {
map(Types.BLOCK_POSITION1_8); // Position
map(Types.UNSIGNED_BYTE); // Byte 1 (depending on block type)
map(Types.UNSIGNED_BYTE); // Byte 2 (depending on block type)
map(Types.VAR_INT); // Block
handler(wrapper -> {
final int block = wrapper.get(Types.VAR_INT, 0);
if (block >= 219 && block <= 234) {
wrapper.set(Types.VAR_INT, 0, 130);
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_9.FORGET_LEVEL_CHUNK, ClientboundPackets1_8.LEVEL_CHUNK, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final Environment environment = wrapper.user().getClientWorld(Protocol1_9To1_8.class).getEnvironment();
protocol.registerClientbound(ClientboundPackets1_9.CUSTOM_SOUND, new PacketHandlers() {
@Override
public void register() {
map(Types.STRING); // Sound name
handler(wrapper -> {
String name = wrapper.get(Types.STRING, 0);
name = protocol.getMappingData().getMappedNamedSound(name);
if (name == null) {
return;
}
if (!name.isEmpty()) {
wrapper.set(Types.STRING, 0, name);
} else {
wrapper.cancel();
}
});
read(Types.VAR_INT); // Sound category
map(Types.INT); // Effect position x
map(Types.INT); // Effect position y
map(Types.INT); // Effect position z
map(Types.FLOAT); // Volume
map(Types.UNSIGNED_BYTE); // Pitch
}
});
final int chunkX = wrapper.read(Types.INT);
final int chunkZ = wrapper.read(Types.INT);
protocol.registerClientbound(ClientboundPackets1_9.FORGET_LEVEL_CHUNK, ClientboundPackets1_8.LEVEL_CHUNK, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final Environment environment = wrapper.user().storables(protocol).clientWorld().getEnvironment();
final CommandBlockStateStorage commandBlockStates = wrapper.user().get(CommandBlockStateStorage.class);
wrapper.write(ChunkType1_8.forEnvironment(environment), new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], null, new ArrayList<>()));
});
}
});
final int chunkX = wrapper.read(Types.INT);
final int chunkZ = wrapper.read(Types.INT);
commandBlockStates.unloadChunk(chunkX, chunkZ);
protocol.registerClientbound(ClientboundPackets1_9.LEVEL_CHUNK, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final Environment environment = wrapper.user().getClientWorld(Protocol1_9To1_8.class).getEnvironment();
wrapper.write(ChunkType1_8.forEnvironment(environment), new BaseChunk(chunkX, chunkZ, true, false, 0, new ChunkSection[16], null, new ArrayList<>()));
});
}
});
Chunk chunk = wrapper.read(ChunkType1_9_1.forEnvironment(environment));
protocol.registerClientbound(ClientboundPackets1_9.LEVEL_CHUNK, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final Environment environment = wrapper.user().storables(protocol).clientWorld().getEnvironment();
final CommandBlockStateStorage commandBlockStates = wrapper.user().get(CommandBlockStateStorage.class);
for (ChunkSection section : chunk.getSections()) {
if (section == null) continue;
DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int i = 0; i < palette.size(); i++) {
int block = palette.idByIndex(i);
int replacedBlock = protocol.getItemRewriter().handleBlockId(block);
palette.setIdByIndex(i, replacedBlock);
}
}
Chunk chunk = wrapper.read(ChunkType1_9_1.forEnvironment(environment));
final Chunk originalChunk = chunk;
if (chunk.isFullChunk() && chunk.getBitmask() == 0) { // This would be an unload packet for 1.8 clients. Just set one air section
boolean skylight = environment == Environment.NORMAL;
ChunkSection[] sections = new ChunkSection[16];
ChunkSection section = new ChunkSectionImpl(true);
sections[0] = section;
section.palette(PaletteType.BLOCKS).addId(0);
if (skylight) section.getLight().setSkyLight(new byte[2048]);
chunk = new BaseChunk(chunk.getX(), chunk.getZ(), true, false, 1, sections, chunk.getBiomeData(), chunk.getBlockEntities());
}
chunk.getBlockEntities().forEach(nbt -> {
if (!nbt.contains("x") || !nbt.contains("y") || !nbt.contains("z")) {
return;
}
wrapper.write(ChunkType1_8.forEnvironment(environment), chunk);
final BlockPosition position = new BlockPosition((int) nbt.get("x").getValue(), (int) nbt.get("y").getValue(), (int) nbt.get("z").getValue());
final int blockState = blockStateAt(originalChunk, position);
if (CommandBlockState.isCommandBlock(blockState)) {
commandBlockStates.storeOrRemove(position, blockState);
CommandBlockState.decorateCommand(nbt, blockState);
}
});
chunk.getBlockEntities().forEach(nbt -> {
if (!nbt.contains("x") || !nbt.contains("y") || !nbt.contains("z") || !nbt.contains("id")) {
return;
}
BlockPosition position = new BlockPosition((int) nbt.get("x").getValue(), (int) nbt.get("y").getValue(), (int) nbt.get("z").getValue());
String id = (String) nbt.get("id").getValue();
for (ChunkSection section : chunk.getSections()) {
if (section == null) continue;
DataPalette palette = section.palette(PaletteType.BLOCKS);
for (int i = 0; i < palette.size(); i++) {
int block = palette.idByIndex(i);
int replacedBlock = protocol.getItemRewriter().handleBlockId(block);
palette.setIdByIndex(i, replacedBlock);
}
}
short action;
switch (id) {
case "minecraft:mob_spawner":
action = 1;
break;
case "minecraft:command_block":
action = 2;
break;
case "minecraft:beacon":
action = 3;
break;
case "minecraft:skull":
action = 4;
break;
case "minecraft:flower_pot":
action = 5;
break;
case "minecraft:banner":
action = 6;
break;
default:
return;
}
if (chunk.isFullChunk() && chunk.getBitmask() == 0) { // This would be an unload packet for 1.8 clients. Just set one air section
boolean skylight = environment == Environment.NORMAL;
ChunkSection[] sections = new ChunkSection[16];
ChunkSection section = new ChunkSectionImpl(true);
sections[0] = section;
section.palette(PaletteType.BLOCKS).addId(0);
if (skylight) section.getLight().setSkyLight(new byte[2048]);
chunk = new BaseChunk(chunk.getX(), chunk.getZ(), true, false, 1, sections, chunk.getBiomeData(), chunk.getBlockEntities());
}
final PacketWrapper blockEntityData = PacketWrapper.create(ClientboundPackets1_9.BLOCK_ENTITY_DATA, wrapper.user());
blockEntityData.write(Types.BLOCK_POSITION1_8, position);
blockEntityData.write(Types.UNSIGNED_BYTE, action);
blockEntityData.write(Types.NAMED_COMPOUND_TAG, nbt);
blockEntityData.scheduleSend(Protocol1_9To1_8.class, false);
});
});
}
});
wrapper.write(ChunkType1_8.forEnvironment(environment), chunk);
protocol.registerClientbound(ClientboundPackets1_9.LEVEL_EVENT, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Effect id
map(Types.BLOCK_POSITION1_8); // Position
map(Types.INT); // data
map(Types.BOOLEAN); // disable relative volume
handler(wrapper -> {
int id = wrapper.get(Types.INT, 0);
id = EffectIdMappings1_8.getOldId(id);
if (id == -1) {
wrapper.cancel();
return;
}
wrapper.set(Types.INT, 0, id);
if (id == 2001) {
int replacedBlock = protocol.getItemRewriter().handleBlockId(wrapper.get(Types.INT, 1));
wrapper.set(Types.INT, 1, replacedBlock);
}
});
}
});
chunk.getBlockEntities().forEach(nbt -> {
if (!nbt.contains("x") || !nbt.contains("y") || !nbt.contains("z") || !nbt.contains("id")) {
return;
}
BlockPosition position = new BlockPosition((int) nbt.get("x").getValue(), (int) nbt.get("y").getValue(), (int) nbt.get("z").getValue());
String id = (String) nbt.get("id").getValue();
protocol.registerClientbound(ClientboundPackets1_9.LEVEL_PARTICLES, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Particle id
handler(wrapper -> {
int id = wrapper.get(Types.INT, 0);
if (id > 41 && !ViaRewind.getConfig().isReplaceParticles()) {
wrapper.cancel();
return;
}
if (id == 42) { // Dragon Breath
wrapper.set(Types.INT, 0, 24); // Portal
} else if (id == 43) { // End Rod
wrapper.set(Types.INT, 0, 3); // Firework Spark
} else if (id == 44) { // Damage Indicator
wrapper.set(Types.INT, 0, 34); // Heart
} else if (id == 45) { // Sweep Attack
wrapper.set(Types.INT, 0, 1); // Large Explosion
}
});
}
});
short action;
switch (id) {
case "minecraft:mob_spawner":
action = 1;
break;
case "minecraft:command_block":
action = 2;
break;
case "minecraft:beacon":
action = 3;
break;
case "minecraft:skull":
action = 4;
break;
case "minecraft:flower_pot":
action = 5;
break;
case "minecraft:banner":
action = 6;
break;
default:
return;
}
protocol.registerClientbound(ClientboundPackets1_9.MAP_ITEM_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT); // Item damage
map(Types.BYTE); // Scale
read(Types.BOOLEAN); // Tracking position
}
});
final PacketWrapper blockEntityData = PacketWrapper.create(ClientboundPackets1_9.BLOCK_ENTITY_DATA, wrapper.user());
blockEntityData.write(Types.BLOCK_POSITION1_8, position);
blockEntityData.write(Types.UNSIGNED_BYTE, action);
blockEntityData.write(Types.NAMED_COMPOUND_TAG, nbt);
blockEntityData.scheduleSend(Protocol1_9To1_8.class, false);
});
});
}
});
protocol.registerClientbound(ClientboundPackets1_9.SOUND, ClientboundPackets1_8.CUSTOM_SOUND, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final int soundId = wrapper.read(Types.VAR_INT);
final String soundName = protocol.getMappingData().soundName(soundId);
if (soundName == null) {
wrapper.cancel();
} else {
wrapper.write(Types.STRING, protocol.getMappingData().getMappedNamedSound(soundName));
}
});
read(Types.VAR_INT); // Sound category
map(Types.INT); // Effect position x
map(Types.INT); // Effect position y
map(Types.INT); // Effect position z
map(Types.FLOAT); // Volume
map(Types.UNSIGNED_BYTE); // Pitch
}
});
}
protocol.registerClientbound(ClientboundPackets1_9.LEVEL_EVENT, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Effect id
map(Types.BLOCK_POSITION1_8); // Position
map(Types.INT); // data
map(Types.BOOLEAN); // disable relative volume
handler(wrapper -> {
int id = wrapper.get(Types.INT, 0);
id = EffectIdMappings1_8.getOldId(id);
if (id == -1) {
wrapper.cancel();
return;
}
wrapper.set(Types.INT, 0, id);
if (id == 2001) {
int replacedBlock = protocol.getItemRewriter().handleBlockId(wrapper.get(Types.INT, 1));
wrapper.set(Types.INT, 1, replacedBlock);
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_9.LEVEL_PARTICLES, new PacketHandlers() {
@Override
public void register() {
map(Types.INT); // Particle id
handler(wrapper -> {
int id = wrapper.get(Types.INT, 0);
if (id > 41 && !ViaRewind.getConfig().isReplaceParticles()) {
wrapper.cancel();
return;
}
if (id == 42) { // Dragon Breath
wrapper.set(Types.INT, 0, 24); // Portal
} else if (id == 43) { // End Rod
wrapper.set(Types.INT, 0, 3); // Firework Spark
} else if (id == 44) { // Damage Indicator
wrapper.set(Types.INT, 0, 34); // Heart
} else if (id == 45) { // Sweep Attack
wrapper.set(Types.INT, 0, 1); // Large Explosion
}
});
}
});
protocol.registerClientbound(ClientboundPackets1_9.MAP_ITEM_DATA, new PacketHandlers() {
@Override
public void register() {
map(Types.VAR_INT); // Item damage
map(Types.BYTE); // Scale
read(Types.BOOLEAN); // Tracking position
}
});
protocol.registerClientbound(ClientboundPackets1_9.SOUND, ClientboundPackets1_8.CUSTOM_SOUND, new PacketHandlers() {
@Override
public void register() {
handler(wrapper -> {
final int soundId = wrapper.read(Types.VAR_INT);
final int mappedSoundId = protocol.getMappingData().getFullSoundMappings().getNewId(soundId);
if (mappedSoundId == -1) {
wrapper.cancel();
} else {
wrapper.write(Types.STRING, protocol.getMappingData().getFullSoundMappings().mappedIdentifier(mappedSoundId));
}
});
read(Types.VAR_INT); // Sound category
map(Types.INT); // Effect position x
map(Types.INT); // Effect position y
map(Types.INT); // Effect position z
map(Types.FLOAT); // Volume
map(Types.UNSIGNED_BYTE); // Pitch
}
});
}
private static int blockStateAt(final Chunk chunk, final BlockPosition position) {
final int sectionY = position.y() >> 4;
if (sectionY < 0 || sectionY >= chunk.getSections().length) {
return -1;
}
final ChunkSection section = chunk.getSections()[sectionY];
if (section == null) {
return -1;
}
return section.palette(PaletteType.BLOCKS).idAt(position.x() & 0xF, position.y() & 0xF, position.z() & 0xF);
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,24 +20,24 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
public class BlockPlaceDestroyTracker implements StorableObject {
private long lastMining;
private long lastMining;
public boolean isMining() {
long time = System.currentTimeMillis() - lastMining;
return time < 75;
}
public boolean isMining() {
long time = System.currentTimeMillis() - lastMining;
return time < 75;
}
public void setMining() {
lastMining = System.currentTimeMillis();
}
public void setMining() {
lastMining = System.currentTimeMillis();
}
public void updateMining() {
if (this.isMining()) {
lastMining = System.currentTimeMillis();
}
}
public void updateMining() {
if (this.isMining()) {
lastMining = System.currentTimeMillis();
}
}
public void setLastMining(long lastMining) {
this.lastMining = lastMining;
}
public void setLastMining(long lastMining) {
this.lastMining = lastMining;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -20,65 +20,64 @@ package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
import com.viaversion.viarewind.protocol.v1_9to1_8.data.WitherBossBar;
import com.viaversion.viaversion.api.connection.StoredObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class BossBarStorage extends StoredObject {
private final Map<UUID, WitherBossBar> bossBars = new HashMap<>();
private final Map<UUID, WitherBossBar> bossBars = new HashMap<>();
public BossBarStorage(UserConnection user) {
super(user);
}
public BossBarStorage(UserConnection user) {
super(user);
}
public void reset() {
updateLocation();
changeWorld();
}
public void reset() {
updateLocation();
changeWorld();
}
public void add(UUID uuid, String title, float health) {
WitherBossBar bossBar = new WitherBossBar(this.getUser(), uuid, title, health);
PlayerPositionTracker playerPositionTracker = this.getUser().get(PlayerPositionTracker.class);
bossBar.setPlayerLocation(playerPositionTracker.getPosX(), playerPositionTracker.getPosY(), playerPositionTracker.getPosZ(), playerPositionTracker.getYaw(), playerPositionTracker.getPitch());
bossBar.show();
bossBars.put(uuid, bossBar);
}
public void add(UUID uuid, String title, float health) {
WitherBossBar bossBar = new WitherBossBar(this.getUser(), uuid, title, health);
PlayerPositionTracker playerPositionTracker = this.getUser().get(PlayerPositionTracker.class);
bossBar.setPlayerLocation(playerPositionTracker.getPosX(), playerPositionTracker.getPosY(), playerPositionTracker.getPosZ(), playerPositionTracker.getYaw(), playerPositionTracker.getPitch());
bossBar.show();
bossBars.put(uuid, bossBar);
}
public void remove(UUID uuid) {
WitherBossBar bossBar = bossBars.remove(uuid);
if (bossBar == null) return;
bossBar.hide();
}
public void remove(UUID uuid) {
WitherBossBar bossBar = bossBars.remove(uuid);
if (bossBar == null) return;
bossBar.hide();
}
public void updateLocation() {
PlayerPositionTracker playerPositionTracker = this.getUser().get(PlayerPositionTracker.class);
bossBars.values().forEach(bossBar -> {
try {
bossBar.setPlayerLocation(playerPositionTracker.getPosX(), playerPositionTracker.getPosY(), playerPositionTracker.getPosZ(), playerPositionTracker.getYaw(), playerPositionTracker.getPitch());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
public void updateLocation() {
PlayerPositionTracker playerPositionTracker = this.getUser().get(PlayerPositionTracker.class);
bossBars.values().forEach(bossBar -> {
try {
bossBar.setPlayerLocation(playerPositionTracker.getPosX(), playerPositionTracker.getPosY(), playerPositionTracker.getPosZ(), playerPositionTracker.getYaw(), playerPositionTracker.getPitch());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
public void changeWorld() {
bossBars.values().forEach(bossBar -> {
bossBar.hide();
bossBar.show();
});
}
public void changeWorld() {
bossBars.values().forEach(bossBar -> {
bossBar.hide();
bossBar.show();
});
}
public void updateHealth(UUID uuid, float health) {
WitherBossBar bossBar = bossBars.get(uuid);
if (bossBar == null) return;
bossBar.setHealth(health);
}
public void updateHealth(UUID uuid, float health) {
WitherBossBar bossBar = bossBars.get(uuid);
if (bossBar == null) return;
bossBar.setHealth(health);
}
public void updateTitle(UUID uuid, String title) {
WitherBossBar bossBar = bossBars.get(uuid);
if (bossBar == null) return;
bossBar.setTitle(title);
}
public void updateTitle(UUID uuid, String title) {
WitherBossBar bossBar = bossBars.get(uuid);
if (bossBar == null) return;
bossBar.setTitle(title);
}
}
@@ -0,0 +1,52 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
import com.viaversion.viarewind.protocol.v1_9to1_8.data.CommandBlockState;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.minecraft.BlockPosition;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public final class CommandBlockStateStorage implements StorableObject {
private final Map<BlockPosition, Integer> commandBlockStates = new HashMap<>();
public void storeOrRemove(final BlockPosition position, final int blockState) {
if (CommandBlockState.isCommandBlock(blockState)) {
commandBlockStates.put(position, blockState);
} else {
commandBlockStates.remove(position);
}
}
public int state(final BlockPosition position) {
return commandBlockStates.getOrDefault(position, -1);
}
public void unloadChunk(final int chunkX, final int chunkZ) {
final Iterator<BlockPosition> iterator = commandBlockStates.keySet().iterator();
while (iterator.hasNext()) {
final BlockPosition position = iterator.next();
if (position.x() >> 4 == chunkX && position.z() >> 4 == chunkZ) {
iterator.remove();
}
}
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,101 +22,100 @@ import com.viaversion.viarewind.protocol.v1_9to1_8.cooldown.CooldownVisualizatio
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.util.Pair;
import java.util.List;
import java.util.logging.Level;
public class CooldownStorage implements StorableObject {
private CooldownVisualization.Factory visualizationFactory = CooldownVisualization.Factory.fromConfiguration();
private CooldownVisualization current;
private CooldownVisualization.Factory visualizationFactory = CooldownVisualization.Factory.fromConfiguration();
private CooldownVisualization current;
private double attackSpeed = 4.0;
private long lastHit = 0;
private double attackSpeed = 4.0;
private long lastHit = 0;
public void tick(final UserConnection connection) {
if (!hasCooldown()) {
endCurrentVisualization();
return;
}
BlockPlaceDestroyTracker tracker = connection.get(BlockPlaceDestroyTracker.class);
if (tracker.isMining()) {
lastHit = 0;
endCurrentVisualization();
return;
}
if (current == null) {
current = visualizationFactory.create(connection);
}
try {
current.show(getCooldown());
} catch (Exception exception) {
ViaRewind.getPlatform().getLogger().log(Level.WARNING, "Unable to show cooldown visualization", exception);
}
}
public void tick(final UserConnection connection) {
if (!hasCooldown()) {
endCurrentVisualization();
return;
}
BlockPlaceDestroyTracker tracker = connection.get(BlockPlaceDestroyTracker.class);
if (tracker.isMining()) {
lastHit = 0;
endCurrentVisualization();
return;
}
if (current == null) {
current = visualizationFactory.create(connection);
}
try {
current.show(getCooldown());
} catch (Exception exception) {
ViaRewind.getPlatform().getLogger().log(Level.WARNING, "Unable to show cooldown visualization", exception);
}
}
private void endCurrentVisualization() {
if (current != null) {
try {
current.hide();
} catch (Exception exception) {
ViaRewind.getPlatform().getLogger().log(Level.WARNING, "Unable to hide cooldown visualization", exception);
}
current = null;
}
}
private void endCurrentVisualization() {
if (current != null) {
try {
current.hide();
} catch (Exception exception) {
ViaRewind.getPlatform().getLogger().log(Level.WARNING, "Unable to hide cooldown visualization", exception);
}
current = null;
}
}
public boolean hasCooldown() {
long time = System.currentTimeMillis() - lastHit;
double cooldown = restrain(((double) time) * attackSpeed / 1000d, 1.5);
return cooldown > 0.1 && cooldown < 1.1;
}
public boolean hasCooldown() {
long time = System.currentTimeMillis() - lastHit;
double cooldown = restrain(((double) time) * attackSpeed / 1000d, 1.5);
return cooldown > 0.1 && cooldown < 1.1;
}
public double getCooldown() {
long time = System.currentTimeMillis() - lastHit;
return restrain(((double) time) * attackSpeed / 1000d, 1);
}
public double getCooldown() {
long time = System.currentTimeMillis() - lastHit;
return restrain(((double) time) * attackSpeed / 1000d, 1);
}
private double restrain(double x, double b) {
if (x < 0) return 0;
return Math.min(x, b);
}
private double restrain(double x, double b) {
if (x < 0) return 0;
return Math.min(x, b);
}
public void setAttackSpeed(double base, List<Pair<Byte, Double>> modifiers) {
attackSpeed = base;
for (int j = 0; j < modifiers.size(); j++) {
if (modifiers.get(j).key() == 0) {
attackSpeed += modifiers.get(j).value();
modifiers.remove(j--);
}
}
for (int j = 0; j < modifiers.size(); j++) {
if (modifiers.get(j).key() == 1) {
attackSpeed += base * modifiers.get(j).value();
modifiers.remove(j--);
}
}
for (int j = 0; j < modifiers.size(); j++) {
if (modifiers.get(j).key() == 2) {
attackSpeed *= (1.0 + modifiers.get(j).value());
modifiers.remove(j--);
}
}
}
public void setAttackSpeed(double base, List<Pair<Byte, Double>> modifiers) {
attackSpeed = base;
for (int j = 0; j < modifiers.size(); j++) {
if (modifiers.get(j).key() == 0) {
attackSpeed += modifiers.get(j).value();
modifiers.remove(j--);
}
}
for (int j = 0; j < modifiers.size(); j++) {
if (modifiers.get(j).key() == 1) {
attackSpeed += base * modifiers.get(j).value();
modifiers.remove(j--);
}
}
for (int j = 0; j < modifiers.size(); j++) {
if (modifiers.get(j).key() == 2) {
attackSpeed *= (1.0 + modifiers.get(j).value());
modifiers.remove(j--);
}
}
}
public void hit() {
lastHit = System.currentTimeMillis();
}
public void hit() {
lastHit = System.currentTimeMillis();
}
public void setLastHit(long lastHit) {
this.lastHit = lastHit;
}
public void setLastHit(long lastHit) {
this.lastHit = lastHit;
}
public CooldownVisualization.Factory getVisualizationFactory() {
return visualizationFactory;
}
public CooldownVisualization.Factory getVisualizationFactory() {
return visualizationFactory;
}
public void setVisualizationFactory(CooldownVisualization.Factory visualizationFactory) {
this.visualizationFactory = visualizationFactory;
}
public void setVisualizationFactory(CooldownVisualization.Factory visualizationFactory) {
this.visualizationFactory = visualizationFactory;
}
}
@@ -1,6 +1,6 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2025 ViaVersion and contributors
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -18,70 +18,110 @@
package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
import com.viaversion.viaversion.api.minecraft.Vector;
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_9;
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
import com.viaversion.viaversion.libs.fastutil.ints.*;
import com.viaversion.viaversion.libs.fastutil.ints.Int2IntMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2IntOpenHashMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectMap;
import com.viaversion.viaversion.libs.fastutil.ints.Int2ObjectOpenHashMap;
import com.viaversion.viaversion.libs.fastutil.ints.IntArrayList;
import com.viaversion.viaversion.libs.fastutil.ints.IntList;
import com.viaversion.viaversion.libs.fastutil.ints.IntOpenHashSet;
import com.viaversion.viaversion.libs.fastutil.ints.IntSet;
import java.util.List;
import java.util.Map;
public class EntityTracker1_9 extends EntityTrackerBase {
private final Int2ObjectMap<IntList> vehicles = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<Vector> offsets = new Int2ObjectOpenHashMap<>();
private final Int2IntMap status = new Int2IntOpenHashMap();
private final Int2ObjectMap<IntList> vehicles = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<Vector> offsets = new Int2ObjectOpenHashMap<>();
private final Int2IntMap status = new Int2IntOpenHashMap();
private final IntSet handActive = new IntOpenHashSet();
private final Int2ObjectMap<PendingPotionEntity> pendingPotions = new Int2ObjectOpenHashMap<>();
public EntityTracker1_9(UserConnection connection) {
super(connection, EntityTypes1_9.EntityType.PLAYER);
}
public EntityTracker1_9(UserConnection connection) {
super(connection, EntityTypes1_9.EntityType.PLAYER);
}
@Override
public void removeEntity(int id) {
vehicles.remove(id);
offsets.remove(id);
status.remove(id);
@Override
public TrackedEntity removeEntity(int id) {
vehicles.remove(id);
offsets.remove(id);
status.remove(id);
handActive.remove(id);
pendingPotions.remove(id);
vehicles.forEach((vehicle, passengers) -> passengers.rem(id));
vehicles.int2ObjectEntrySet().removeIf(entry -> entry.getValue().isEmpty());
super.removeEntity(id);
}
vehicles.forEach((vehicle, passengers) -> passengers.rem(id));
vehicles.int2ObjectEntrySet().removeIf(entry -> entry.getValue().isEmpty());
return super.removeEntity(id);
}
public void resetEntityOffset(final int id) {
offsets.remove(id);
}
public void resetEntityOffset(final int id) {
offsets.remove(id);
}
public Vector getEntityOffset(final int id) {
return offsets.get(id);
}
public Vector getEntityOffset(final int id) {
return offsets.get(id);
}
public void setEntityOffset(final int id, Vector offset) {
offsets.put(id, offset);
}
public void setEntityOffset(final int id, Vector offset) {
offsets.put(id, offset);
}
public IntList getPassengers(final int id) {
return vehicles.getOrDefault(id, new IntArrayList());
}
public IntList getPassengers(final int id) {
return vehicles.getOrDefault(id, new IntArrayList());
}
public void setPassengers(final int id, final IntList passengers) {
vehicles.put(id, passengers);
}
public void setPassengers(final int id, final IntList passengers) {
vehicles.put(id, passengers);
}
public boolean isInsideVehicle(final int id) {
for (List<Integer> vehicle : vehicles.values()) {
if (vehicle.contains(id)) return true;
}
return false;
}
public boolean isInsideVehicle(final int id) {
for (List<Integer> vehicle : vehicles.values()) {
if (vehicle.contains(id)) return true;
}
return false;
}
public Integer getVehicle(final int passenger) {
for (Map.Entry<Integer, IntList> vehicle : vehicles.int2ObjectEntrySet()) {
if (vehicle.getValue().contains(passenger)) return vehicle.getKey();
}
return null;
}
public Integer getVehicle(final int passenger) {
for (Map.Entry<Integer, IntList> vehicle : vehicles.int2ObjectEntrySet()) {
if (vehicle.getValue().contains(passenger)) return vehicle.getKey();
}
return null;
}
public Int2IntMap getStatus() {
return status;
}
public boolean isHandActive(final int id) {
return handActive.contains(id);
}
public void setHandActive(final int id, final boolean active) {
if (active) {
handActive.add(id);
} else {
handActive.remove(id);
}
}
public Int2ObjectMap<PendingPotionEntity> getPendingPotions() {
return pendingPotions;
}
public record PendingPotionEntity(int x, int y, int z, byte pitch, byte yaw, short velocityX, short velocityY, short velocityZ) {
public PendingPotionEntity withPosition(final int x, final int y, final int z, final byte pitch, final byte yaw) {
return new PendingPotionEntity(x, y, z, pitch, yaw, velocityX, velocityY, velocityZ);
}
public PendingPotionEntity withVelocity(final short velocityX, final short velocityY, final short velocityZ) {
return new PendingPotionEntity(x, y, z, pitch, yaw, velocityX, velocityY, velocityZ);
}
}
public Int2IntMap getStatus() {
return status;
}
}
@@ -0,0 +1,107 @@
/*
* This file is part of ViaRewind - https://github.com/ViaVersion/ViaRewind
* Copyright (C) 2018-2026 ViaVersion and contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.viaversion.viarewind.protocol.v1_9to1_8.storage;
import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.libs.gson.JsonElement;
public final class LastTitle implements StorableObject {
private JsonElement title;
private JsonElement subtitle;
private long visibleSince;
private long visibleUntil;
private int fadeIn;
private int stay;
private int fadeOut;
public LastTitle() {
this.reset();
}
public void setTitle(final JsonElement title) {
this.title = title;
final long now = System.currentTimeMillis();
this.visibleSince = now;
this.visibleUntil = now + titleLength();
}
public void setSubtitle(final JsonElement subtitle) {
this.subtitle = subtitle;
}
public void setTimes(final int fadeIn, final int stay, final int fadeOut) {
this.fadeIn = fadeIn;
this.stay = stay;
this.fadeOut = fadeOut;
if (this.visibleUntil > System.currentTimeMillis()) {
final long now = System.currentTimeMillis();
this.visibleSince = now;
this.visibleUntil = now + titleLength();
}
}
public void hide() {
this.visibleSince = 0;
this.visibleUntil = 0;
}
public void reset() {
this.hide();
this.fadeIn = 10;
this.stay = 70;
this.fadeOut = 20;
}
public boolean isVisible() {
return this.title != null && this.visibleUntil > System.currentTimeMillis();
}
public JsonElement title() {
return this.title;
}
public JsonElement subtitle() {
return this.subtitle;
}
public int[] remainingTimes() {
final int fullLength = Math.max(0, this.fadeIn + this.stay + this.fadeOut);
final int elapsed = (int) Math.min(fullLength, Math.max(0, (System.currentTimeMillis() - this.visibleSince) / 50L));
if (elapsed < this.fadeIn) {
return new int[]{this.fadeIn - elapsed, this.stay, this.fadeOut};
}
final int titleEnd = this.fadeIn + this.stay;
if (elapsed < titleEnd) {
return new int[]{0, titleEnd - elapsed, this.fadeOut};
}
return new int[]{0, 0, fullLength - elapsed};
}
private long titleLength() {
return Math.max(0, this.fadeIn + this.stay + this.fadeOut) * 50L;
}
}
Loaded 100 of 130 files, more files were not shown because too many files have changed in this diff. Show more