forked from upstream/ViaVersion
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9a3a4493d | ||
|
|
0fe1b6d6db | ||
|
|
fbb92c0fac | ||
|
|
384cf91125 | ||
|
|
c6736b3532 | ||
|
|
8c4bbd1a2f | ||
|
|
386c607917 | ||
|
|
c30c787c63 | ||
|
|
6951dda36e | ||
|
|
c9d8610c05 | ||
|
|
68ad7c115b | ||
|
|
3c50cdf639 | ||
|
|
e211692ca1 | ||
|
|
8937bdd986 | ||
|
|
b49f995ef1 | ||
|
|
7ab825cc77 | ||
|
|
a00226f452 | ||
|
|
9370534b23 | ||
|
|
5fe54eb1e5 | ||
|
|
a090a909ee | ||
|
|
72084b2599 | ||
|
|
a3dcaa6f56 | ||
|
|
88f9c05be0 | ||
|
|
7b167b0eed | ||
|
|
21ff1758d5 | ||
|
|
9793ca2291 | ||
|
|
5768a5c0da | ||
|
|
decab8fe7f | ||
|
|
354a736e0c | ||
|
|
56d23661b0 | ||
|
|
ca616c74fe | ||
|
|
168d13e143 | ||
|
|
229d4fc6e1 | ||
|
|
33e426fddc | ||
|
|
0c6023e489 | ||
|
|
8ebe3a523c | ||
|
|
8239577c23 | ||
|
|
670fde5d0f | ||
|
|
d2a715862a | ||
|
|
6438f99600 | ||
|
|
1e31aafd83 | ||
|
|
736ba3c53c | ||
|
|
6d47c5af61 | ||
|
|
2b42e38300 | ||
|
|
4260095390 | ||
|
|
769498661d | ||
|
|
1c25e5011a | ||
|
|
bb3de8ef8d | ||
|
|
770dd485ec | ||
|
|
964a1b2581 | ||
|
|
bb1a5e745a | ||
|
|
a0a31641e3 | ||
|
|
4e493f61cd | ||
|
|
ae7f1f07d5 | ||
|
|
be89cb52b3 | ||
|
|
b63765a970 | ||
|
|
2047b42d78 | ||
|
|
a1179887cd | ||
|
|
b2a3512bbd | ||
|
|
2fe81b5b70 | ||
|
|
1da2fb89c4 | ||
|
|
53e18c1f8d | ||
|
|
9460a65f02 | ||
|
|
150a516006 |
No files matched your search
@@ -6,9 +6,11 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
- package-ecosystem: "gradle"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
@@ -11,16 +11,16 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
|
||||
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
|
||||
with:
|
||||
cache-disabled: false
|
||||
cache-provider: basic
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
uses: actions/setup-java@v5.5.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
|
||||
@@ -16,16 +16,16 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Set up Gradle
|
||||
uses: gradle/actions/setup-gradle@50e97c2cd7a37755bbfafc9c5b7cafaece252f6e # v6.1.0
|
||||
uses: gradle/actions/setup-gradle@3f131e8634966bd73d06cc69884922b02e6faf92 # v6.2.0
|
||||
with:
|
||||
cache-disabled: false
|
||||
cache-provider: basic
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
|
||||
uses: actions/setup-java@v5.5.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: 17
|
||||
|
||||
@@ -66,7 +66,7 @@ public interface ViaAPI<T> {
|
||||
* @return API version incremented with meaningful API changes
|
||||
*/
|
||||
default int apiVersion() {
|
||||
return 26;
|
||||
return 27;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -261,6 +261,8 @@ public interface ViaVersionConfig extends Config {
|
||||
*/
|
||||
boolean isChunkBorderFix();
|
||||
|
||||
boolean isMultiReddustColorFix();
|
||||
|
||||
/**
|
||||
* Should we make team colours based on the last colour in team prefix
|
||||
*
|
||||
|
||||
@@ -365,10 +365,10 @@ public interface UserConnection {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a newly generated uuid that will let a packet be passed through without
|
||||
* Returns a newly generated token that will let a packet be passed through without
|
||||
* transforming its contents if used together with {@link PacketWrapper#PASSTHROUGH_ID}.
|
||||
*
|
||||
* @return generated passthrough token
|
||||
*/
|
||||
UUID generatePassthroughToken();
|
||||
long generatePassthroughToken();
|
||||
}
|
||||
@@ -22,88 +22,29 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.data;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader.IdentifiersPair;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class FullIdentityMappings implements FullMappings {
|
||||
private static final String[] EMPTY_ARRAY = new String[0];
|
||||
private final Object2IntMap<String> stringToId;
|
||||
private final String[] idToString;
|
||||
private final Mappings mappings;
|
||||
public class FullIdentityMappings extends FullMappingsBase {
|
||||
|
||||
public FullIdentityMappings(final IdentifiersPair identifiersPair, final Mappings mappings) {
|
||||
Preconditions.checkNotNull(mappings, "Mappings cannot be null");
|
||||
this.mappings = mappings;
|
||||
this.stringToId = FullMappingsBase.toInverseMap(identifiersPair.unmapped());
|
||||
this.idToString = identifiersPair.unmapped().toArray(EMPTY_ARRAY);
|
||||
this(toInverseMap(identifiersPair.unmapped()), identifiersPair.unmapped().stream().map(Key::of).toArray(Key[]::new), mappings);
|
||||
}
|
||||
|
||||
private FullIdentityMappings(final Object2IntMap<String> stringToId, final String[] idToString, final Mappings mappings) {
|
||||
this.stringToId = stringToId;
|
||||
this.idToString = idToString;
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int id(final String identifier) {
|
||||
return stringToId.getInt(Key.stripMinecraftNamespace(identifier));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mappedId(final String mappedIdentifier) {
|
||||
return id(mappedIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String identifier(final int id) {
|
||||
if (id < 0 || id >= idToString.length) {
|
||||
return null;
|
||||
}
|
||||
final String identifier = idToString[id];
|
||||
return Key.namespaced(identifier);
|
||||
private FullIdentityMappings(final Object2IntMap<String> stringToId, final Key[] idToKey, final Mappings mappings) {
|
||||
super(stringToId, stringToId, idToKey, idToKey, mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String identifier(final String mappedIdentifier) {
|
||||
final int mappedId = mappedId(mappedIdentifier);
|
||||
if (mappedId == -1) {
|
||||
return null;
|
||||
}
|
||||
final int id = mappings.inverse().getNewId(mappedId);
|
||||
return id != -1 ? identifier(id) : null;
|
||||
return mappedIdentifier(mappedIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String mappedIdentifier(final int mappedId) {
|
||||
return identifier(mappedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String mappedIdentifier(final String identifier) {
|
||||
return identifier(identifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNewId(int id) {
|
||||
return mappings.getNewId(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNewId(int id, int mappedId) {
|
||||
mappings.setNewId(id, mappedId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return mappings.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int mappedSize() {
|
||||
return mappings.mappedSize();
|
||||
public @Nullable Key keyFromMappedKey(final String mappedIdentifier) {
|
||||
return mappedKeyFromKey(mappedIdentifier);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -117,7 +58,7 @@ public class FullIdentityMappings implements FullMappings {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FullMappings inverse() {
|
||||
return new FullIdentityMappings(stringToId, idToString, mappings.inverse());
|
||||
public FullIdentityMappings inverse() {
|
||||
return new FullIdentityMappings(stringToId, idToKey, mappings.inverse());
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.data;
|
||||
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
@@ -51,7 +52,10 @@ public interface FullMappings extends BiMappings {
|
||||
* @param id unmapped id
|
||||
* @return unmapped string identifier, or null if out of bounds
|
||||
*/
|
||||
@Nullable String identifier(int id);
|
||||
default @Nullable String identifier(int id) {
|
||||
final Key key = keyFromId(id);
|
||||
return key != null ? key.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unmapped string identifier for the given mapped identifier.
|
||||
@@ -59,7 +63,10 @@ public interface FullMappings extends BiMappings {
|
||||
* @param mappedIdentifier mapped identifier
|
||||
* @return unmapped string identifier, or null if not found
|
||||
*/
|
||||
@Nullable String identifier(String mappedIdentifier);
|
||||
default @Nullable String identifier(String mappedIdentifier) {
|
||||
final Key key = keyFromMappedKey(mappedIdentifier);
|
||||
return key != null ? key.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapped string identifier for the given mapped id.
|
||||
@@ -67,7 +74,10 @@ public interface FullMappings extends BiMappings {
|
||||
* @param mappedId mapped id
|
||||
* @return mapped string identifier, or null if out of bounds
|
||||
*/
|
||||
@Nullable String mappedIdentifier(int mappedId);
|
||||
default @Nullable String mappedIdentifier(int mappedId) {
|
||||
final Key key = mappedKeyFromMappedId(mappedId);
|
||||
return key != null ? key.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the mapped string identifier for the given unmapped identifier.
|
||||
@@ -75,7 +85,42 @@ public interface FullMappings extends BiMappings {
|
||||
* @param identifier unmapped identifier
|
||||
* @return mapped string identifier, or null if not found
|
||||
*/
|
||||
@Nullable String mappedIdentifier(String identifier);
|
||||
default @Nullable String mappedIdentifier(String identifier) {
|
||||
final Key key = mappedKeyFromKey(identifier);
|
||||
return key != null ? key.toString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unmapped key for the given mapped id.
|
||||
*
|
||||
* @param id unmapped id
|
||||
* @return unmapped key, or null if out of bounds
|
||||
*/
|
||||
@Nullable Key keyFromId(int id);
|
||||
|
||||
/**
|
||||
* Returns the unmapped key for the given mapped identifier.
|
||||
*
|
||||
* @param mappedIdentifier mapped identifier
|
||||
* @return unmapped key, or null if not found
|
||||
*/
|
||||
@Nullable Key keyFromMappedKey(String mappedIdentifier);
|
||||
|
||||
/**
|
||||
* Returns the mapped key for the given mapped id.
|
||||
*
|
||||
* @param mappedId mapped id
|
||||
* @return mapped key, or null if out of bounds
|
||||
*/
|
||||
@Nullable Key mappedKeyFromMappedId(int mappedId);
|
||||
|
||||
/**
|
||||
* Returns the mapped key for the given unmapped identifier.
|
||||
*
|
||||
* @param identifier unmapped identifier
|
||||
* @return mapped key, or null if not found
|
||||
*/
|
||||
@Nullable Key mappedKeyFromKey(String identifier);
|
||||
|
||||
/**
|
||||
* Returns whether the int id mappings are identity mappings.
|
||||
|
||||
@@ -31,27 +31,26 @@ import java.util.List;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class FullMappingsBase implements FullMappings {
|
||||
private static final String[] EMPTY_ARRAY = new String[0];
|
||||
private final Object2IntMap<String> stringToId;
|
||||
private final Object2IntMap<String> mappedStringToId;
|
||||
private final String[] idToString;
|
||||
private final String[] mappedIdToString;
|
||||
private final Mappings mappings;
|
||||
protected final Object2IntMap<String> stringToId;
|
||||
protected final Object2IntMap<String> mappedStringToId;
|
||||
protected final Key[] idToKey;
|
||||
protected final Key[] mappedIdToKey;
|
||||
protected final Mappings mappings;
|
||||
|
||||
public FullMappingsBase(final IdentifiersPair identifiersPair, final Mappings mappings) {
|
||||
Preconditions.checkNotNull(mappings, "Mappings cannot be null");
|
||||
this.mappings = mappings;
|
||||
this.stringToId = toInverseMap(identifiersPair.unmapped());
|
||||
this.idToString = identifiersPair.unmapped().toArray(EMPTY_ARRAY);
|
||||
this.idToKey = identifiersPair.unmapped().stream().map(Key::of).toArray(Key[]::new);
|
||||
this.mappedStringToId = toInverseMap(identifiersPair.mapped());
|
||||
this.mappedIdToString = identifiersPair.mapped().toArray(EMPTY_ARRAY);
|
||||
this.mappedIdToKey = identifiersPair.mapped().stream().map(Key::of).toArray(Key[]::new);
|
||||
}
|
||||
|
||||
private FullMappingsBase(final Object2IntMap<String> stringToId, final Object2IntMap<String> mappedStringToId, final String[] idToString, final String[] mappedIdToString, final Mappings mappings) {
|
||||
protected FullMappingsBase(final Object2IntMap<String> stringToId, final Object2IntMap<String> mappedStringToId, final Key[] idToKey, final Key[] mappedIdToKey, final Mappings mappings) {
|
||||
this.stringToId = stringToId;
|
||||
this.mappedStringToId = mappedStringToId;
|
||||
this.idToString = idToString;
|
||||
this.mappedIdToString = mappedIdToString;
|
||||
this.idToKey = idToKey;
|
||||
this.mappedIdToKey = mappedIdToKey;
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
@@ -79,45 +78,43 @@ public class FullMappingsBase implements FullMappings {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String identifier(final int id) {
|
||||
if (id < 0 || id >= idToString.length) {
|
||||
public @Nullable Key keyFromId(final int id) {
|
||||
if (id < 0 || id >= idToKey.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String identifier = idToString[id];
|
||||
return Key.namespaced(identifier);
|
||||
return idToKey[id];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String identifier(final String mappedIdentifier) {
|
||||
public @Nullable Key keyFromMappedKey(final String mappedIdentifier) {
|
||||
final int mappedId = mappedId(mappedIdentifier);
|
||||
if (mappedId == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int id = mappings.inverse().getNewId(mappedId);
|
||||
return id != -1 ? identifier(id) : null;
|
||||
return id != -1 ? keyFromId(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String mappedIdentifier(final int mappedId) {
|
||||
if (mappedId < 0 || mappedId >= mappedIdToString.length) {
|
||||
public @Nullable Key mappedKeyFromMappedId(final int mappedId) {
|
||||
if (mappedId < 0 || mappedId >= mappedIdToKey.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final String identifier = mappedIdToString[mappedId];
|
||||
return Key.namespaced(identifier);
|
||||
return mappedIdToKey[mappedId];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String mappedIdentifier(final String identifier) {
|
||||
public @Nullable Key mappedKeyFromKey(final String identifier) {
|
||||
final int id = id(identifier);
|
||||
if (id == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int mappedId = mappings.getNewId(id);
|
||||
return mappedId != -1 ? mappedIdentifier(mappedId) : null;
|
||||
return mappedId != -1 ? mappedKeyFromMappedId(mappedId) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -147,6 +144,6 @@ public class FullMappingsBase implements FullMappings {
|
||||
|
||||
@Override
|
||||
public FullMappings inverse() {
|
||||
return new FullMappingsBase(mappedStringToId, stringToId, mappedIdToString, idToString, mappings.inverse());
|
||||
return new FullMappingsBase(mappedStringToId, stringToId, mappedIdToKey, idToKey, mappings.inverse());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.api.data;
|
||||
|
||||
import com.viaversion.nbt.tag.ByteArrayTag;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
/**
|
||||
* Reads sets of non-negative ints stored as ranges, packed into a byte array of varints.
|
||||
* Each range is stored as an offset and length pair. The offset is the start of the first range and the
|
||||
* difference to the previous range's exclusive end; the length is the inclusive end minus the start.
|
||||
*/
|
||||
public final class IdRanges {
|
||||
|
||||
/**
|
||||
* Returns the encoded id set as an int set.
|
||||
*
|
||||
* @param rangesTag tag with the encoded ranges
|
||||
* @return decoded ids
|
||||
*/
|
||||
public static IntSet decode(final ByteArrayTag rangesTag) {
|
||||
final IntSet ids = new IntOpenHashSet();
|
||||
forEachId(rangesTag, ids::add);
|
||||
return ids;
|
||||
}
|
||||
|
||||
public static void forEachId(final ByteArrayTag rangesTag, final IntConsumer consumer) {
|
||||
final ByteBuf buf = Unpooled.wrappedBuffer(rangesTag.getValue());
|
||||
int prevEnd = 0;
|
||||
while (buf.isReadable()) {
|
||||
final int start = prevEnd + Types.VAR_INT.readPrimitive(buf);
|
||||
final int end = start + Types.VAR_INT.readPrimitive(buf);
|
||||
for (int id = start; id <= end; id++) {
|
||||
consumer.accept(id);
|
||||
}
|
||||
prevEnd = end + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.api.data;
|
||||
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader.IdentifiersPair;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Full mappings for versions where required identifiers hadn't yet followed the resource location format (i.e., pre-1.9.4 sounds).
|
||||
*/
|
||||
public class LegacyFullStringMappingsBase extends FullMappingsBase {
|
||||
private final String[] idToString;
|
||||
private final String[] mappedIdToString;
|
||||
|
||||
public LegacyFullStringMappingsBase(final IdentifiersPair identifiersPair, final Mappings mappings) {
|
||||
super(toInverseMap(identifiersPair.unmapped()), toInverseMap(identifiersPair.mapped()), new Key[0], new Key[0], mappings);
|
||||
this.idToString = identifiersPair.unmapped().toArray(new String[0]);
|
||||
this.mappedIdToString = identifiersPair.mapped().toArray(new String[0]);
|
||||
}
|
||||
|
||||
private LegacyFullStringMappingsBase(final Object2IntMap<String> stringToId, final Object2IntMap<String> mappedStringToId, final String[] idToKey, final String[] mappedIdToString, final Mappings mappings) {
|
||||
super(stringToId, mappedStringToId, new Key[0], new Key[0], mappings);
|
||||
this.idToString = idToKey;
|
||||
this.mappedIdToString = mappedIdToString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String identifier(final int id) {
|
||||
if (id < 0 || id >= idToString.length) {
|
||||
return null;
|
||||
}
|
||||
return Key.namespaced(idToString[id]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String identifier(final String mappedIdentifier) {
|
||||
final int mappedId = mappedId(mappedIdentifier);
|
||||
if (mappedId == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int id = mappings.inverse().getNewId(mappedId);
|
||||
return id != -1 ? identifier(id) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String mappedIdentifier(final int mappedId) {
|
||||
if (mappedId < 0 || mappedId >= mappedIdToString.length) {
|
||||
return null;
|
||||
}
|
||||
return Key.namespaced(mappedIdToString[mappedId]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String mappedIdentifier(final String identifier) {
|
||||
final int id = id(identifier);
|
||||
if (id == -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int mappedId = mappings.getNewId(id);
|
||||
return mappedId != -1 ? mappedIdentifier(mappedId) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Key keyFromId(final int id) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Key keyFromMappedKey(final String mappedIdentifier) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Key mappedKeyFromMappedId(final int mappedId) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Key mappedKeyFromKey(final String identifier) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FullMappings inverse() {
|
||||
return new LegacyFullStringMappingsBase(mappedStringToId, stringToId, mappedIdToString, idToString, mappings.inverse());
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import com.viaversion.viaversion.util.Key;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public interface MappingData {
|
||||
@@ -190,15 +191,23 @@ public interface MappingData {
|
||||
*/
|
||||
@Nullable IntSet changedBlocks();
|
||||
|
||||
@Nullable Set<String> changedEnvironmentAttributes();
|
||||
|
||||
/**
|
||||
* Type of mappings. Currently only relevant for ops writing of generic holder classes and expanded when needed.
|
||||
*/
|
||||
enum MappingType implements RegistryKey {
|
||||
ITEM, BLOCK, SOUND, ENTITY_TYPE;
|
||||
|
||||
private final Key key;
|
||||
|
||||
MappingType() {
|
||||
this.key = Key.of(name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key key() {
|
||||
return Key.of(name().toLowerCase(Locale.ROOT));
|
||||
public Key registry() {
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,8 @@ package com.viaversion.viaversion.api.data;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntArrayTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader.IdentifiersPair;
|
||||
@@ -35,7 +37,9 @@ import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class MappingDataBase implements MappingData {
|
||||
@@ -63,6 +67,7 @@ public class MappingDataBase implements MappingData {
|
||||
* Set of block ids that have had type or property changes.
|
||||
*/
|
||||
protected IntSet changedBlocks;
|
||||
protected Set<String> changedEnvironmentAttributes;
|
||||
|
||||
public MappingDataBase(final String unmappedVersion, final String mappedVersion) {
|
||||
this.unmappedVersion = unmappedVersion;
|
||||
@@ -118,6 +123,7 @@ public class MappingDataBase implements MappingData {
|
||||
loadTags(RegistryType.ITEM, tagsTag);
|
||||
loadTags(RegistryType.BLOCK, tagsTag);
|
||||
loadTags(RegistryType.ENTITY, tagsTag);
|
||||
loadTags(RegistryType.FLUID, tagsTag);
|
||||
}
|
||||
|
||||
final IntArrayTag changedBlocks = data.getIntArrayTag("changed_blocks");
|
||||
@@ -125,6 +131,11 @@ public class MappingDataBase implements MappingData {
|
||||
this.changedBlocks = new IntOpenHashSet(changedBlocks.getValue());
|
||||
}
|
||||
|
||||
final ListTag<StringTag> changedEnvironmentAttributes = data.getListTag("changed_environment_attributes", StringTag.class);
|
||||
if (changedEnvironmentAttributes != null) {
|
||||
this.changedEnvironmentAttributes = changedEnvironmentAttributes.stream().map(StringTag::getValue).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
loadExtras(data);
|
||||
}
|
||||
|
||||
@@ -167,6 +178,10 @@ public class MappingDataBase implements MappingData {
|
||||
if (mappings == null) {
|
||||
mappings = new IdentityMappings(identifiersPair.unmapped().size(), identifiersPair.mapped().size());
|
||||
}
|
||||
return createFullMappings(identifiersPair, mappings);
|
||||
}
|
||||
|
||||
protected FullMappings createFullMappings(final IdentifiersPair identifiersPair, final Mappings mappings) {
|
||||
return FullMappingsBase.of(identifiersPair, mappings);
|
||||
}
|
||||
|
||||
@@ -349,6 +364,11 @@ public class MappingDataBase implements MappingData {
|
||||
return changedBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Set<String> changedEnvironmentAttributes() {
|
||||
return changedEnvironmentAttributes;
|
||||
}
|
||||
|
||||
protected Logger getLogger() {
|
||||
return Via.getPlatform().getLogger();
|
||||
}
|
||||
|
||||
@@ -31,13 +31,17 @@ import com.google.gson.JsonSyntaxException;
|
||||
import com.viaversion.nbt.io.NBTIO;
|
||||
import com.viaversion.nbt.io.TagReader;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntArrayTag;
|
||||
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.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.util.GsonUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import it.unimi.dsi.fastutil.ints.IntList;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -51,6 +55,7 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.logging.Logger;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
@@ -59,15 +64,16 @@ public class MappingDataLoader {
|
||||
public static final MappingDataLoader INSTANCE = new MappingDataLoader(MappingDataLoader.class, "assets/viaversion/data/");
|
||||
public static final TagReader<CompoundTag> MAPPINGS_READER = NBTIO.reader(CompoundTag.class).named();
|
||||
private static final Map<String, String[]> GLOBAL_IDENTIFIER_INDEXES = new HashMap<>();
|
||||
private static final int VERSION = 2;
|
||||
private static final byte DIRECT_ID = 0;
|
||||
private static final byte SHIFTS_ID = 1;
|
||||
private static final byte CHANGES_ID = 2;
|
||||
private static final byte IDENTITY_ID = 3;
|
||||
|
||||
private final Map<String, CompoundTag> mappingsCache = new HashMap<>();
|
||||
private final Map<String, CompoundTag> mappingsCache = new ConcurrentHashMap<>();
|
||||
private final Class<?> dataLoaderClass;
|
||||
private final String dataPath;
|
||||
private boolean cacheValid = true;
|
||||
private volatile boolean cacheValid = true;
|
||||
|
||||
public MappingDataLoader(final Class<?> dataLoaderClass, final String dataPath) {
|
||||
this.dataLoaderClass = dataLoaderClass;
|
||||
@@ -104,14 +110,14 @@ public class MappingDataLoader {
|
||||
throw new IllegalArgumentException("Unknown global identifier key: " + registry);
|
||||
}
|
||||
if (globalId < 0 || globalId >= array.length) {
|
||||
throw new IllegalArgumentException("Unknown global identifier index: " + globalId);
|
||||
throw new IllegalArgumentException("Unknown global identifier index: " + registry + "/" + globalId);
|
||||
}
|
||||
return array[globalId];
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
mappingsCache.clear();
|
||||
cacheValid = false;
|
||||
mappingsCache.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -160,17 +166,14 @@ public class MappingDataLoader {
|
||||
return loadNBTFromFile(name);
|
||||
}
|
||||
|
||||
CompoundTag data = mappingsCache.get(name);
|
||||
if (data != null) {
|
||||
return data;
|
||||
if (cache) {
|
||||
// Also blocks concurrent loaders of the same file until the first one has finished
|
||||
return mappingsCache.computeIfAbsent(name, this::loadNBTFromFile);
|
||||
}
|
||||
|
||||
data = loadNBTFromFile(name);
|
||||
|
||||
if (cache && data != null) {
|
||||
mappingsCache.put(name, data);
|
||||
}
|
||||
return data;
|
||||
// Still do a lookup, but don't store it
|
||||
final CompoundTag data = mappingsCache.get(name);
|
||||
return data != null ? data : loadNBTFromFile(name);
|
||||
}
|
||||
|
||||
public @Nullable CompoundTag loadNBT(final String name) {
|
||||
@@ -211,15 +214,29 @@ public class MappingDataLoader {
|
||||
return null;
|
||||
}
|
||||
|
||||
final int version = mappingsTag.getInt("version", -1);
|
||||
if (version != VERSION) {
|
||||
throw new IllegalArgumentException("Invalid version: " + version);
|
||||
}
|
||||
|
||||
final int mappedSize = tag.getInt("mappedSize", -1);
|
||||
final byte strategy = tag.getByteTag("id").asByte();
|
||||
final V mappings;
|
||||
if (strategy == DIRECT_ID) {
|
||||
final IntArrayTag valuesTag = tag.getIntArrayTag("val");
|
||||
return IntArrayMappings.of(valuesTag.getValue(), mappedSize);
|
||||
// Zigzag varints of the difference to the previous mapped id
|
||||
final int size = tag.getIntTag("size").asInt();
|
||||
final ByteBuf buf = Unpooled.wrappedBuffer(tag.getByteArrayTag("val").getValue());
|
||||
final int[] values = new int[size];
|
||||
int prev = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
prev += readZigZagVarInt(buf);
|
||||
values[i] = prev;
|
||||
}
|
||||
return IntArrayMappings.of(values, mappedSize);
|
||||
} else if (strategy == SHIFTS_ID) {
|
||||
final int[] shiftsAt = tag.getIntArrayTag("at").getValue();
|
||||
final int[] shiftsTo = tag.getIntArrayTag("to").getValue();
|
||||
final ValuePairs pairs = readAtValuePairs(tag);
|
||||
final int[] shiftsAt = pairs.first;
|
||||
final int[] shiftsTo = pairs.second;
|
||||
final int size = tag.getIntTag("size").asInt();
|
||||
mappings = holderSupplier.get(size);
|
||||
|
||||
@@ -242,8 +259,9 @@ public class MappingDataLoader {
|
||||
}
|
||||
}
|
||||
} else if (strategy == CHANGES_ID) {
|
||||
final int[] changesAt = tag.getIntArrayTag("at").getValue();
|
||||
final int[] values = tag.getIntArrayTag("val").getValue();
|
||||
final ValuePairs pairs = readAtValuePairs(tag);
|
||||
final int[] changesAt = pairs.first;
|
||||
final int[] values = pairs.second;
|
||||
final int size = tag.getIntTag("size").asInt();
|
||||
final boolean fillBetween = tag.get("nofill") == null;
|
||||
mappings = holderSupplier.get(size);
|
||||
@@ -262,6 +280,13 @@ public class MappingDataLoader {
|
||||
// Assign the changed value
|
||||
addConsumer.addTo(mappings, changedId, values[i]);
|
||||
}
|
||||
|
||||
// Fill the remainder
|
||||
if (fillBetween && nextUnhandledId != size) {
|
||||
for (int id = nextUnhandledId; id < size; id++) {
|
||||
addConsumer.addTo(mappings, id, id);
|
||||
}
|
||||
}
|
||||
} else if (strategy == IDENTITY_ID) {
|
||||
final IntTag sizeTag = tag.getIntTag("size");
|
||||
return new IdentityMappings(sizeTag.asInt(), mappedSize);
|
||||
@@ -271,6 +296,37 @@ public class MappingDataLoader {
|
||||
return mappingsSupplier.create(mappings, mappedSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads alternating id and value pairs from varints. The id as the difference to the previous id,
|
||||
* the value as the zigzag difference to the previous value.
|
||||
*
|
||||
* @param tag tag to read the packed values from
|
||||
* @return array of ids and array of values
|
||||
*/
|
||||
private static ValuePairs readAtValuePairs(final CompoundTag tag) {
|
||||
final ByteBuf buf = Unpooled.wrappedBuffer(tag.getByteArrayTag("val").getValue());
|
||||
final IntList at = new IntArrayList();
|
||||
final IntList values = new IntArrayList();
|
||||
int prevAt = -1;
|
||||
int prevValue = 0;
|
||||
while (buf.isReadable()) {
|
||||
prevAt = prevAt + 1 + Types.VAR_INT.readPrimitive(buf);
|
||||
prevValue += readZigZagVarInt(buf);
|
||||
at.add(prevAt);
|
||||
values.add(prevValue);
|
||||
}
|
||||
return new ValuePairs(at.toIntArray(), values.toIntArray());
|
||||
}
|
||||
|
||||
private static int readZigZagVarInt(final ByteBuf buf) {
|
||||
// https://lemire.me/blog/2022/11/25/making-all-your-integers-positive-with-zigzag-encoding/
|
||||
// In short, using deltas to previous ids makes the numbers small, and using zigzag encoding means they're all positive.
|
||||
// Being all positive means we can use varints to make most of the data much shorter than 8 bytes.
|
||||
// All of that combined ends up having a decent effect when compressed in the jar file as well.
|
||||
final int value = Types.VAR_INT.readPrimitive(buf);
|
||||
return (value >>> 1) ^ -(value & 1);
|
||||
}
|
||||
|
||||
public @Nullable List<String> identifiersFromGlobalIds(final CompoundTag mappingsTag, final String key) {
|
||||
final Mappings mappings = loadMappings(mappingsTag, key);
|
||||
return mappings != null ? identifiers(mappings, key) : null;
|
||||
@@ -315,6 +371,10 @@ public class MappingDataLoader {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mappedGlobalId == -1) {
|
||||
throw new IllegalArgumentException("Mapped global id -1 for unmapped global id: " + key + "/" + unmappedIdentifier + " (" + unmappedGlobalId + ")");
|
||||
}
|
||||
|
||||
identity = false;
|
||||
unmappedIdentifiers.add(unmappedIdentifier);
|
||||
mappedIdentifiers.add(identifierFromGlobalId(key, mappedGlobalId));
|
||||
@@ -323,8 +383,9 @@ public class MappingDataLoader {
|
||||
}
|
||||
|
||||
private List<String> identifiers(final Mappings mappings, final String key) {
|
||||
final List<String> identifiers = new ArrayList<>(mappings.size());
|
||||
for (int i = 0; i < mappings.size(); i++) {
|
||||
final int size = mappings.size();
|
||||
final List<String> identifiers = new ArrayList<>(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
final int globalId = mappings.getNewId(i);
|
||||
identifiers.add(identifierFromGlobalId(key, globalId));
|
||||
}
|
||||
@@ -401,4 +462,7 @@ public class MappingDataLoader {
|
||||
this(identifiers, identifiers, true);
|
||||
}
|
||||
}
|
||||
|
||||
private record ValuePairs(int[] first, int[] second) {
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,9 @@
|
||||
package com.viaversion.viaversion.api.data.entity;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.util.KeyMappings;
|
||||
import java.util.Map;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
@@ -43,8 +45,9 @@ public interface EntityTracker {
|
||||
*
|
||||
* @param id entity id
|
||||
* @param type entity type
|
||||
* @return tracked entity
|
||||
*/
|
||||
void addEntity(int id, EntityType type);
|
||||
TrackedEntity addEntity(int id, EntityType type);
|
||||
|
||||
/**
|
||||
* Returns whether the entity is currently tracked.
|
||||
@@ -75,8 +78,9 @@ public interface EntityTracker {
|
||||
* Untracks an entity.
|
||||
*
|
||||
* @param id entity id
|
||||
* @return previously tracked entity
|
||||
*/
|
||||
void removeEntity(int id);
|
||||
@Nullable TrackedEntity removeEntity(int id);
|
||||
|
||||
/**
|
||||
* Clears stored entity types and data, only leaving behind the client entity.
|
||||
@@ -88,25 +92,6 @@ public interface EntityTracker {
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Returns the stored entity data if an entity with the id is tracked, else null.
|
||||
* If no data has been initialized yet, it will be done and returned by this method.
|
||||
*
|
||||
* @param id entity id
|
||||
* @return stored entity data if an entity with the id is tracked, else null
|
||||
* @throws IllegalArgumentException if entity data storage has not been enabled via the implementation
|
||||
*/
|
||||
@Nullable StoredEntityData entityData(int id);
|
||||
|
||||
/**
|
||||
* Returns stored entity data if it has previously been initialized by {@link #entityData(int)}, else null.
|
||||
*
|
||||
* @param id entity id
|
||||
* @return stored entity data if it has previously been initialized by {@link #entityData(int)}
|
||||
* @throws IllegalArgumentException if entity data storage has not been enabled via the implementation
|
||||
*/
|
||||
@Nullable StoredEntityData entityDataIfPresent(int id);
|
||||
|
||||
/**
|
||||
* Returns whether the client entity id has been set.
|
||||
*
|
||||
@@ -204,6 +189,24 @@ public interface EntityTracker {
|
||||
|
||||
void setBiomesSent(int biomesSent);
|
||||
|
||||
/**
|
||||
* Returns the cached chunk type for the current dimension if set.
|
||||
*
|
||||
* @param mapped whether to return the mapped chunk type
|
||||
* @return cached chunk type
|
||||
*/
|
||||
@Nullable Type<Chunk> chunkType(boolean mapped);
|
||||
|
||||
/**
|
||||
* Caches the chunk type for the current dimension.
|
||||
* <p>
|
||||
* Invalidates when the world section height or the number of biomes change.
|
||||
*
|
||||
* @param mapped whether to set the mapped chunk type
|
||||
* @param chunkType chunk type
|
||||
*/
|
||||
void setChunkType(boolean mapped, @Nullable Type<Chunk> chunkType);
|
||||
|
||||
EntityType playerType();
|
||||
|
||||
@Nullable DimensionData dimensionData(String dimension);
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.api.data.entity;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public interface StoredEntityData {
|
||||
|
||||
/**
|
||||
* Returns the entity type of the stored entity.
|
||||
*
|
||||
* @return entity type
|
||||
*/
|
||||
EntityType type();
|
||||
|
||||
/**
|
||||
* Checks if the storage contains an object of the given type.
|
||||
*
|
||||
* @param objectClass object class to check
|
||||
* @return whether an object of the given type is in the storage
|
||||
*/
|
||||
boolean has(Class<?> objectClass);
|
||||
|
||||
/**
|
||||
* Returns an object from the storage if present.
|
||||
*
|
||||
* @param objectClass class of the object to get
|
||||
* @param <T> object type
|
||||
* @return object if present
|
||||
*/
|
||||
@Nullable <T> T get(Class<T> objectClass);
|
||||
|
||||
/**
|
||||
* Removes and returns an object from the storage if present.
|
||||
*
|
||||
* @param objectClass class of the object to remove
|
||||
* @param <T> object type
|
||||
* @return removed object if present
|
||||
*/
|
||||
@Nullable <T> T remove(Class<T> objectClass);
|
||||
|
||||
/**
|
||||
* Stores an object based on its class.
|
||||
*
|
||||
* @param object object to store
|
||||
*/
|
||||
void put(Object object);
|
||||
}
|
||||
@@ -22,7 +22,9 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.data.entity;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public interface TrackedEntity {
|
||||
|
||||
@@ -34,18 +36,37 @@ public interface TrackedEntity {
|
||||
EntityType entityType();
|
||||
|
||||
/**
|
||||
* Object to hold arbitrary additional data.
|
||||
* Checks if the storage contains an object of the given type.
|
||||
*
|
||||
* @return entity data
|
||||
* @param objectClass object class to check
|
||||
* @return whether an object of the given type is in the storage
|
||||
*/
|
||||
StoredEntityData data();
|
||||
boolean has(Class<? extends StorableObject> objectClass);
|
||||
|
||||
/**
|
||||
* Returns whether the stored entity currently has any additional data.
|
||||
* Returns an object from the storage if present.
|
||||
*
|
||||
* @return whether the stored entity currently has additional data
|
||||
* @param objectClass class of the object to get
|
||||
* @param <T> object type
|
||||
* @return object if present
|
||||
*/
|
||||
boolean hasData();
|
||||
@Nullable <T extends StorableObject> T get(Class<T> objectClass);
|
||||
|
||||
/**
|
||||
* Removes and returns an object from the storage if present.
|
||||
*
|
||||
* @param objectClass class of the object to remove
|
||||
* @param <T> object type
|
||||
* @return removed object if present
|
||||
*/
|
||||
@Nullable <T extends StorableObject> T remove(Class<T> objectClass);
|
||||
|
||||
/**
|
||||
* Stores an object based on its class.
|
||||
*
|
||||
* @param object object to store
|
||||
*/
|
||||
void put(StorableObject object);
|
||||
|
||||
/**
|
||||
* Returns whether entity data has already been sent at least once for this entity.
|
||||
|
||||
@@ -34,7 +34,7 @@ import com.viaversion.viaversion.util.Key;
|
||||
*/
|
||||
public interface RegistryKey {
|
||||
|
||||
Key key();
|
||||
Key registry();
|
||||
|
||||
static RegistryKey of(final String key) {
|
||||
return new SimpleRegistryKey(Key.of(key));
|
||||
|
||||
@@ -43,7 +43,7 @@ public enum RegistryType implements RegistryKey {
|
||||
|
||||
static {
|
||||
for (RegistryType type : getValues()) {
|
||||
MAP.put(type.identifier, type);
|
||||
MAP.put(type.identifier(), type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,18 +55,18 @@ public enum RegistryType implements RegistryKey {
|
||||
return MAP.get(identifier);
|
||||
}
|
||||
|
||||
private final String identifier;
|
||||
private final Key key;
|
||||
|
||||
RegistryType(final String identifier) {
|
||||
this.identifier = identifier;
|
||||
this.key = Key.of(identifier);
|
||||
}
|
||||
|
||||
public String identifier() {
|
||||
return identifier;
|
||||
return key.path();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key key() {
|
||||
return Key.of(identifier);
|
||||
public Key registry() {
|
||||
return this.key;
|
||||
}
|
||||
}
|
||||
@@ -24,10 +24,10 @@ package com.viaversion.viaversion.api.minecraft;
|
||||
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
|
||||
record SimpleRegistryKey(Key key) implements RegistryKey {
|
||||
record SimpleRegistryKey(Key registry) implements RegistryKey {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return key.toString();
|
||||
return registry.toString();
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,9 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.chunks;
|
||||
|
||||
import com.viaversion.viaversion.util.CompactArrayUtil;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
public interface DataPalette {
|
||||
@@ -146,6 +149,27 @@ public interface DataPalette {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs over all section coordinate indices that match the given predicate.
|
||||
*
|
||||
* @param idPredicate predicate to test ids
|
||||
* @param coordinateConsumer consumer to run over applicable coordinate indices (not ids!)
|
||||
* @see #idAt(int)
|
||||
*/
|
||||
void forEachMatchingCoordinate(IntPredicate idPredicate, IntConsumer coordinateConsumer);
|
||||
|
||||
/**
|
||||
* Packs the section values into a compact long array as used by the protocol.
|
||||
*
|
||||
* @param bitsPerValue bits used per value
|
||||
* @param entries number of values
|
||||
* @param rawIds whether to pack raw ids instead of palette indexes (direct palette)
|
||||
* @return compact long array of the values
|
||||
*/
|
||||
default long[] createPackedValues(final int bitsPerValue, final int entries, final boolean rawIds) {
|
||||
return CompactArrayUtil.createCompactArrayWithPadding(bitsPerValue, entries, rawIds ? this::idAt : this::paletteIndexAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the palette.
|
||||
*
|
||||
|
||||
+179
-11
@@ -22,9 +22,13 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.chunks;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.viaversion.viaversion.util.CompactArrayUtil;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.IntPredicate;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
|
||||
public final class DataPaletteImpl implements DataPalette {
|
||||
@@ -32,8 +36,8 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
private static final int DEFAULT_INITIAL_SIZE = 16;
|
||||
|
||||
private final IntArrayList palette;
|
||||
private final Int2IntMap inversePalette;
|
||||
private final int sizeBits;
|
||||
private Int2IntMap inversePalette;
|
||||
private ChunkData values;
|
||||
|
||||
public DataPaletteImpl(final int valuesLength) {
|
||||
@@ -43,11 +47,30 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
public DataPaletteImpl(final int valuesLength, final int initialSize) {
|
||||
values = new SingleChunkData(valuesLength);
|
||||
sizeBits = Integer.numberOfTrailingZeros(valuesLength) / 3;
|
||||
// Pre-size the palette array/map
|
||||
// Pre-size the palette array
|
||||
palette = new IntArrayList(initialSize);
|
||||
// To get an initial table size of initialSize, need to scale it down by load factor.
|
||||
inversePalette = new Int2IntOpenHashMap((int) (initialSize * Int2IntOpenHashMap.DEFAULT_LOAD_FACTOR));
|
||||
inversePalette.defaultReturnValue(-1);
|
||||
}
|
||||
|
||||
private Int2IntMap inversePalette() {
|
||||
// Lazily build the inverse palette. Most palettes never need it
|
||||
if (inversePalette == null) {
|
||||
inversePalette = new Int2IntOpenHashMap(palette.size());
|
||||
inversePalette.defaultReturnValue(-1);
|
||||
for (int i = 0; i < palette.size(); i++) {
|
||||
inversePalette.put(palette.getInt(i), i);
|
||||
}
|
||||
}
|
||||
return inversePalette;
|
||||
}
|
||||
|
||||
private Int2IntMap emptyInversePalette() {
|
||||
if (inversePalette == null) {
|
||||
inversePalette = new Int2IntOpenHashMap(palette.size());
|
||||
inversePalette.defaultReturnValue(-1);
|
||||
} else {
|
||||
inversePalette.clear();
|
||||
}
|
||||
return inversePalette;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,7 +86,7 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
|
||||
@Override
|
||||
public void setIdAt(final int sectionCoordinate, final int id) {
|
||||
int index = inversePalette.get(id);
|
||||
int index = inversePalette().get(id);
|
||||
if (index == -1) {
|
||||
index = palette.size();
|
||||
palette.add(id);
|
||||
@@ -83,6 +106,38 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
values.set(sectionCoordinate, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores palette indexes from a compact long array, replacing all current values.
|
||||
* The data is kept packed unless written to; reads extract bits the slow way, since almost all work will use replaceIds or will only have a few reads.
|
||||
* <p>
|
||||
* Only valid for local palettes (bitsPerValue <= 8).
|
||||
*
|
||||
* @param data compact long array
|
||||
* @param bitsPerValue bits per value
|
||||
* @param valuesPerLong packked values per long
|
||||
*/
|
||||
public void setPaletteIndexes(final long[] data, final int bitsPerValue, final int valuesPerLong) {
|
||||
Preconditions.checkArgument(bitsPerValue <= 8, "Cannot pack global palette");
|
||||
values = new PackedChunkData(data, bitsPerValue, valuesPerLong, values.size());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long[] createPackedValues(final int bitsPerValue, final int entries, final boolean rawIds) {
|
||||
if (rawIds) {
|
||||
return CompactArrayUtil.createCompactArrayWithPadding(bitsPerValue, entries, this::idAt);
|
||||
}
|
||||
|
||||
if (values instanceof final PackedChunkData packed && packed.bitsPerValue == bitsPerValue) {
|
||||
// Values were never modified and the width still matches - write them back as-is
|
||||
return packed.data;
|
||||
}
|
||||
|
||||
final int valuesPerLong = (char) (64 / bitsPerValue);
|
||||
final long[] data = new long[(entries + valuesPerLong - 1) / valuesPerLong];
|
||||
values.packInto(data, bitsPerValue);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return palette.size();
|
||||
@@ -96,7 +151,7 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
@Override
|
||||
public void setIdByIndex(final int index, final int id) {
|
||||
final int oldId = palette.set(index, id);
|
||||
if (oldId == id) return;
|
||||
if (oldId == id || inversePalette == null) return;
|
||||
|
||||
inversePalette.put(id, index);
|
||||
if (inversePalette.get(oldId) == index) {
|
||||
@@ -112,7 +167,7 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
|
||||
@Override
|
||||
public void replaceId(final int oldId, final int newId) {
|
||||
final int index = inversePalette.remove(oldId);
|
||||
final int index = inversePalette().remove(oldId);
|
||||
if (index == -1) return;
|
||||
|
||||
inversePalette.put(newId, index);
|
||||
@@ -130,12 +185,12 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
return;
|
||||
}
|
||||
|
||||
inversePalette.clear();
|
||||
int writeIndex = 0;
|
||||
int[] indexRemap = null;
|
||||
int firstCollision = 0;
|
||||
|
||||
final int[] palette = this.palette.elements();
|
||||
final Int2IntMap inversePalette = emptyInversePalette();
|
||||
for (int readIndex = 0; readIndex < oldSize; readIndex++) {
|
||||
final int mappedId = mapper.applyAsInt(palette[readIndex]);
|
||||
final int existingIndex = inversePalette.putIfAbsent(mappedId, writeIndex);
|
||||
@@ -172,14 +227,51 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
|
||||
@Override
|
||||
public void addId(final int id) {
|
||||
inversePalette.put(id, palette.size());
|
||||
if (inversePalette != null) {
|
||||
inversePalette.put(id, palette.size());
|
||||
}
|
||||
palette.add(id);
|
||||
}
|
||||
|
||||
private static final ThreadLocal<boolean[]> MATCHING_INDEXES = ThreadLocal.withInitial(() -> new boolean[ChunkSection.SIZE]);
|
||||
|
||||
@Override
|
||||
public void forEachMatchingCoordinate(final IntPredicate idPredicate, final IntConsumer coordinateConsumer) {
|
||||
final int size = size();
|
||||
if (size > ChunkSection.SIZE) { // Shouldn't ever happen normally, but is technically possible...
|
||||
for (int idx = 0, len = this.values.size(); idx < len; idx++) {
|
||||
if (idPredicate.test(idAt(idx))) {
|
||||
coordinateConsumer.accept(idx);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Instead of going through every single coordinate value (4096 for block sections), check the palette ids first.
|
||||
// Reuse a thread-local array for quick checking against palette indexes.
|
||||
final boolean[] matching = MATCHING_INDEXES.get();
|
||||
boolean any = false;
|
||||
for (int i = 0; i < size; i++) {
|
||||
final boolean match = idPredicate.test(idByIndex(i));
|
||||
matching[i] = match;
|
||||
any |= match;
|
||||
}
|
||||
|
||||
if (!any) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int idx = 0, len = this.values.size(); idx < len; idx++) {
|
||||
if (matching[paletteIndexAt(idx)]) {
|
||||
coordinateConsumer.accept(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
palette.clear();
|
||||
inversePalette.clear();
|
||||
inversePalette = null;
|
||||
}
|
||||
|
||||
interface ChunkData {
|
||||
@@ -188,6 +280,11 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
void set(int idx, int val);
|
||||
|
||||
int size();
|
||||
|
||||
/**
|
||||
* Packs all values into the given compact long array.
|
||||
*/
|
||||
void packInto(long[] data, int bitsPerValue);
|
||||
}
|
||||
|
||||
private final class SingleChunkData implements ChunkData {
|
||||
@@ -215,6 +312,11 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packInto(final long[] data, final int bitsPerValue) {
|
||||
// All values are 0, nothing to write
|
||||
}
|
||||
}
|
||||
|
||||
private final class ByteChunkData implements ChunkData {
|
||||
@@ -224,6 +326,10 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
this.data = new byte[size];
|
||||
}
|
||||
|
||||
public ByteChunkData(byte[] data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int get(int idx) {
|
||||
return data[idx] & 0xFF;
|
||||
@@ -244,6 +350,11 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
public int size() {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packInto(final long[] out, final int bitsPerValue) {
|
||||
CompactArrayUtil.packWithPadding(data, out, bitsPerValue);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ShortChunkData implements ChunkData {
|
||||
@@ -270,6 +381,63 @@ public final class DataPaletteImpl implements DataPalette {
|
||||
public int size() {
|
||||
return data.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packInto(final long[] out, final int bitsPerValue) {
|
||||
CompactArrayUtil.packWithPadding(data, out, bitsPerValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Raw chunk data as read from the buffer.
|
||||
*/
|
||||
private final class PackedChunkData implements ChunkData {
|
||||
private final long[] data;
|
||||
private final int bitsPerValue;
|
||||
private final int valuesPerLong;
|
||||
private final long maxEntryValue;
|
||||
private final int size;
|
||||
|
||||
private PackedChunkData(final long[] data, final int bitsPerValue, final int valuesPerLong, final int size) {
|
||||
this.data = data;
|
||||
this.bitsPerValue = bitsPerValue;
|
||||
this.valuesPerLong = valuesPerLong;
|
||||
this.size = size;
|
||||
this.maxEntryValue = (1L << bitsPerValue) - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int get(final int idx) {
|
||||
// Called so rarely that the cheap storage outweighs the more complex getter
|
||||
final int cellIndex = idx / valuesPerLong;
|
||||
final int bitIndex = (idx - cellIndex * valuesPerLong) * bitsPerValue;
|
||||
return (int) (data[cellIndex] >>> bitIndex & maxEntryValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(final int idx, final int val) {
|
||||
// First write access, actually read out for fast value access
|
||||
final ChunkData byteChunkData = extractData();
|
||||
values = byteChunkData;
|
||||
byteChunkData.set(idx, val);
|
||||
}
|
||||
|
||||
private ChunkData extractData() {
|
||||
final byte[] unpackedData = new byte[size];
|
||||
CompactArrayUtil.unpackWithPadding(this.data, unpackedData, bitsPerValue);
|
||||
return new ByteChunkData(unpackedData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void packInto(final long[] out, final int newBitsPerValue) {
|
||||
// Always called with bitsPerValue != newBitsPerValue, which should only really happen if the server sends an unncessarily high bit width,
|
||||
// or if the number of ids shrunk in replaceIds. Rare enough to not need its own special method
|
||||
extractData().packInto(out, newBitsPerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-8
@@ -39,8 +39,8 @@ final class RegistryAccessImpl implements CodecContext.RegistryAccess {
|
||||
private final boolean mapped;
|
||||
|
||||
RegistryAccessImpl(final Protocol<?, ?, ?, ?> protocol, final UserConnection connection) {
|
||||
this.mappingData = Preconditions.checkNotNull(protocol.getMappingData());
|
||||
this.entityTracker = Preconditions.checkNotNull(protocol.getEntityRewriter().tracker(connection));
|
||||
this.mappingData = Preconditions.checkNotNull(protocol.getMappingData(), "No mapping data present for protocol");
|
||||
this.entityTracker = Preconditions.checkNotNull(protocol.getEntityRewriter().tracker(connection), "Entity tracker has to be added first");
|
||||
this.connection = connection;
|
||||
this.mapped = false;
|
||||
}
|
||||
@@ -102,17 +102,16 @@ final class RegistryAccessImpl implements CodecContext.RegistryAccess {
|
||||
public Key registryKey(final String registry, final int id) {
|
||||
final KeyMappings mappings = entityTracker.registryKeys(registry);
|
||||
Preconditions.checkNotNull(mappings, "No registry mappings for registry: %s", registry);
|
||||
final String identifier = id >= 0 && id < mappings.size() ? mappings.idToKey(id) : null;
|
||||
return key(identifier, id);
|
||||
return keyOrUnknown(mappings.key(id), id);
|
||||
}
|
||||
|
||||
private Key key(final FullMappings mappings, final int id) {
|
||||
final String identifier = mapped ? mappings.mappedIdentifier(id) : mappings.identifier(id);
|
||||
return key(identifier, id);
|
||||
final Key key = mapped ? mappings.mappedKeyFromMappedId(id) : mappings.keyFromId(id);
|
||||
return keyOrUnknown(key, id);
|
||||
}
|
||||
|
||||
private Key key(@Nullable final String identifier, final int id) {
|
||||
return identifier != null ? Key.of(identifier) : Key.of("viaversion", "unknown/" + id);
|
||||
private Key keyOrUnknown(@Nullable final Key key, final int id) {
|
||||
return key != null ? key : Key.of("viaversion", "unknown/" + id);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -270,7 +270,7 @@ public final class StructuredDataContainer implements Copyable {
|
||||
|
||||
@Override
|
||||
public StructuredDataContainer copy() {
|
||||
final Reference2ObjectOpenHashMap<StructuredDataKey<?>, StructuredData<?>> map = new Reference2ObjectOpenHashMap<>();
|
||||
final Reference2ObjectOpenHashMap<StructuredDataKey<?>, StructuredData<?>> map = new Reference2ObjectOpenHashMap<>(data.size());
|
||||
for (final StructuredData<?> value : data.values()) {
|
||||
map.put(value.key(), value.copy());
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_11;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_2;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_5;
|
||||
import com.viaversion.viaversion.api.minecraft.item.Item;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.AdventureModePredicate;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrim;
|
||||
import com.viaversion.viaversion.api.minecraft.item.data.ArmorTrimMaterial;
|
||||
|
||||
+2
-2
@@ -34,7 +34,7 @@ public class StructuredDataKeys1_20_5 extends VersionedStructuredDataKeys {
|
||||
|
||||
public StructuredDataKeys1_20_5(final VersionedTypesHolder types) {
|
||||
this.container = add("container", types.itemArray());
|
||||
this.chargedProjectiles = add("charged_projectiles", types.itemArray());
|
||||
this.bundleContents = add("bundle_contents", types.itemArray());
|
||||
this.chargedProjectiles = add("charged_projectiles", types.itemArray().withMaxLength(1024));
|
||||
this.bundleContents = add("bundle_contents", types.itemArray().withMaxLength(1024));
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -36,8 +36,8 @@ public class StructuredDataKeys1_21_2 extends VersionedStructuredDataKeys {
|
||||
|
||||
public StructuredDataKeys1_21_2(final VersionedTypesHolder types) {
|
||||
this.container = add("container", new ContainerContentsType(types.optionalItemTemplate()));
|
||||
this.chargedProjectiles = add("charged_projectiles", types.itemTemplateArray());
|
||||
this.bundleContents = add("bundle_contents", types.itemTemplateArray());
|
||||
this.chargedProjectiles = add("charged_projectiles", types.itemTemplateArray().withMaxLength(1024));
|
||||
this.bundleContents = add("bundle_contents", types.itemTemplateArray().withMaxLength(1024));
|
||||
this.useRemainder = add("use_remainder", types.itemTemplate());
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.data.version;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.data.StructuredDataKey;
|
||||
import com.viaversion.viaversion.api.minecraft.item.Item;
|
||||
import com.viaversion.viaversion.api.type.types.version.VersionedTypesHolder;
|
||||
|
||||
public class StructuredDataKeys26_2 extends StructuredDataKeys1_21_11 {
|
||||
|
||||
public final StructuredDataKey<Item> sulfurCubeContent;
|
||||
|
||||
public StructuredDataKeys26_2(final VersionedTypesHolder types) {
|
||||
super(types);
|
||||
this.sulfurCubeContent = add("sulfur_cube_content", types.item());
|
||||
}
|
||||
}
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.api.minecraft.entities;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.viaversion.viaversion.api.protocol.Protocol;
|
||||
import com.viaversion.viaversion.util.EntityTypeUtil;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import java.util.Locale;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public enum EntityTypes26_2 implements EntityType {
|
||||
|
||||
ENTITY(null, null),
|
||||
|
||||
AREA_EFFECT_CLOUD(ENTITY),
|
||||
END_CRYSTAL(ENTITY),
|
||||
EVOKER_FANGS(ENTITY),
|
||||
EXPERIENCE_ORB(ENTITY),
|
||||
EYE_OF_ENDER(ENTITY),
|
||||
FALLING_BLOCK(ENTITY),
|
||||
ITEM(ENTITY),
|
||||
TNT(ENTITY),
|
||||
OMINOUS_ITEM_SPAWNER(ENTITY),
|
||||
MARKER(ENTITY),
|
||||
LIGHTNING_BOLT(ENTITY),
|
||||
INTERACTION(ENTITY),
|
||||
|
||||
DISPLAY(ENTITY, null),
|
||||
BLOCK_DISPLAY(DISPLAY),
|
||||
ITEM_DISPLAY(DISPLAY),
|
||||
TEXT_DISPLAY(DISPLAY),
|
||||
|
||||
// Hanging entities
|
||||
HANGING_ENTITY(ENTITY, null),
|
||||
LEASH_KNOT(HANGING_ENTITY),
|
||||
PAINTING(HANGING_ENTITY),
|
||||
ITEM_FRAME(HANGING_ENTITY),
|
||||
GLOW_ITEM_FRAME(ITEM_FRAME),
|
||||
|
||||
// Projectiles
|
||||
PROJECTILE(ENTITY, null),
|
||||
ITEM_PROJECTILE(PROJECTILE, null),
|
||||
SNOWBALL(ITEM_PROJECTILE),
|
||||
ENDER_PEARL(ITEM_PROJECTILE),
|
||||
EGG(ITEM_PROJECTILE),
|
||||
EXPERIENCE_BOTTLE(ITEM_PROJECTILE),
|
||||
FIREWORK_ROCKET(PROJECTILE),
|
||||
LLAMA_SPIT(PROJECTILE),
|
||||
SHULKER_BULLET(PROJECTILE),
|
||||
FISHING_BOBBER(PROJECTILE),
|
||||
WITHER_SKULL(PROJECTILE),
|
||||
DRAGON_FIREBALL(PROJECTILE), // Doesn't actually inherit fireball
|
||||
|
||||
ABSTRACT_THROWN_POTION(ITEM_PROJECTILE, null),
|
||||
SPLASH_POTION(ABSTRACT_THROWN_POTION),
|
||||
LINGERING_POTION(ABSTRACT_THROWN_POTION),
|
||||
|
||||
ABSTRACT_ARROW(PROJECTILE, null),
|
||||
ARROW(ABSTRACT_ARROW),
|
||||
SPECTRAL_ARROW(ABSTRACT_ARROW),
|
||||
TRIDENT(ABSTRACT_ARROW),
|
||||
|
||||
ABSTRACT_FIREBALL(ENTITY, null),
|
||||
FIREBALL(ABSTRACT_FIREBALL),
|
||||
SMALL_FIREBALL(ABSTRACT_FIREBALL),
|
||||
|
||||
ABSTRACT_WIND_CHARGE(PROJECTILE, null),
|
||||
WIND_CHARGE(ABSTRACT_WIND_CHARGE),
|
||||
BREEZE_WIND_CHARGE(ABSTRACT_WIND_CHARGE),
|
||||
|
||||
// Vehicles
|
||||
VEHICLE(ENTITY, null),
|
||||
|
||||
ABSTRACT_MINECART(VEHICLE, null),
|
||||
MINECART(ABSTRACT_MINECART),
|
||||
FURNACE_MINECART(ABSTRACT_MINECART),
|
||||
COMMAND_BLOCK_MINECART(ABSTRACT_MINECART),
|
||||
TNT_MINECART(ABSTRACT_MINECART),
|
||||
SPAWNER_MINECART(ABSTRACT_MINECART),
|
||||
|
||||
ABSTRACT_MINECART_CONTAINER(ABSTRACT_MINECART, null),
|
||||
CHEST_MINECART(ABSTRACT_MINECART_CONTAINER),
|
||||
HOPPER_MINECART(ABSTRACT_MINECART_CONTAINER),
|
||||
|
||||
ABSTRACT_BOAT(VEHICLE, null),
|
||||
ABSTRACT_CHEST_BOAT(ABSTRACT_BOAT, null),
|
||||
ACACIA_BOAT(ABSTRACT_BOAT),
|
||||
ACACIA_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
BAMBOO_CHEST_RAFT(ABSTRACT_CHEST_BOAT),
|
||||
BAMBOO_RAFT(ABSTRACT_BOAT),
|
||||
BIRCH_BOAT(ABSTRACT_BOAT),
|
||||
BIRCH_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
CHERRY_BOAT(ABSTRACT_BOAT),
|
||||
CHERRY_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
DARK_OAK_BOAT(ABSTRACT_BOAT),
|
||||
DARK_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
JUNGLE_BOAT(ABSTRACT_BOAT),
|
||||
JUNGLE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
MANGROVE_BOAT(ABSTRACT_BOAT),
|
||||
MANGROVE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
OAK_BOAT(ABSTRACT_BOAT),
|
||||
OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
PALE_OAK_BOAT(ABSTRACT_BOAT),
|
||||
PALE_OAK_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
SPRUCE_BOAT(ABSTRACT_BOAT),
|
||||
SPRUCE_CHEST_BOAT(ABSTRACT_CHEST_BOAT),
|
||||
|
||||
// Living entities as a larger subclass
|
||||
LIVING_ENTITY(ENTITY, null),
|
||||
ARMOR_STAND(LIVING_ENTITY),
|
||||
|
||||
// Players
|
||||
AVATAR(LIVING_ENTITY, null),
|
||||
MANNEQUIN(AVATAR),
|
||||
PLAYER(AVATAR),
|
||||
|
||||
// Mobs as a larger subclass
|
||||
MOB(LIVING_ENTITY, null),
|
||||
ENDER_DRAGON(MOB),
|
||||
|
||||
// Ambient mobs
|
||||
AMBIENT_CREATURE(MOB, null),
|
||||
BAT(AMBIENT_CREATURE),
|
||||
|
||||
// Flying mobs
|
||||
FLYING_MOB(MOB, null),
|
||||
GHAST(FLYING_MOB),
|
||||
PHANTOM(FLYING_MOB),
|
||||
|
||||
// Pathfinder mobs and its subclasses
|
||||
PATHFINDER_MOB(MOB, null),
|
||||
ALLAY(PATHFINDER_MOB),
|
||||
|
||||
ABSTRACT_GOLEM(PATHFINDER_MOB, null),
|
||||
SNOW_GOLEM(ABSTRACT_GOLEM),
|
||||
IRON_GOLEM(ABSTRACT_GOLEM),
|
||||
SHULKER(ABSTRACT_GOLEM),
|
||||
COPPER_GOLEM(ABSTRACT_GOLEM),
|
||||
|
||||
// Ageable mobs and (tamable) animals
|
||||
ABSTRACT_AGEABLE(PATHFINDER_MOB, null),
|
||||
|
||||
ABSTRACT_VILLAGER(ABSTRACT_AGEABLE, null),
|
||||
VILLAGER(ABSTRACT_VILLAGER),
|
||||
WANDERING_TRADER(ABSTRACT_VILLAGER),
|
||||
|
||||
ABSTRACT_CUBE_MOB(ABSTRACT_AGEABLE, null),
|
||||
SLIME(ABSTRACT_CUBE_MOB),
|
||||
MAGMA_CUBE(ABSTRACT_CUBE_MOB),
|
||||
SULFUR_CUBE(ABSTRACT_CUBE_MOB),
|
||||
|
||||
ABSTRACT_ANIMAL(ABSTRACT_AGEABLE, null),
|
||||
AXOLOTL(ABSTRACT_ANIMAL),
|
||||
CHICKEN(ABSTRACT_ANIMAL),
|
||||
PANDA(ABSTRACT_ANIMAL),
|
||||
PIG(ABSTRACT_ANIMAL),
|
||||
POLAR_BEAR(ABSTRACT_ANIMAL),
|
||||
RABBIT(ABSTRACT_ANIMAL),
|
||||
SHEEP(ABSTRACT_ANIMAL),
|
||||
BEE(ABSTRACT_ANIMAL),
|
||||
TURTLE(ABSTRACT_ANIMAL),
|
||||
FOX(ABSTRACT_ANIMAL),
|
||||
FROG(ABSTRACT_ANIMAL),
|
||||
GOAT(ABSTRACT_ANIMAL),
|
||||
HOGLIN(ABSTRACT_ANIMAL),
|
||||
STRIDER(ABSTRACT_ANIMAL),
|
||||
SNIFFER(ABSTRACT_ANIMAL),
|
||||
ARMADILLO(ABSTRACT_ANIMAL),
|
||||
HAPPY_GHAST(ABSTRACT_ANIMAL),
|
||||
OCELOT(ABSTRACT_ANIMAL),
|
||||
|
||||
ABSTRACT_COW(ABSTRACT_ANIMAL, null),
|
||||
COW(ABSTRACT_COW),
|
||||
MOOSHROOM(ABSTRACT_COW),
|
||||
|
||||
TAMABLE_ANIMAL(ABSTRACT_ANIMAL, null),
|
||||
CAT(TAMABLE_ANIMAL),
|
||||
WOLF(TAMABLE_ANIMAL),
|
||||
PARROT(TAMABLE_ANIMAL),
|
||||
|
||||
ABSTRACT_NAUTILUS(TAMABLE_ANIMAL, null),
|
||||
NAUTILUS(ABSTRACT_NAUTILUS),
|
||||
ZOMBIE_NAUTILUS(ABSTRACT_NAUTILUS),
|
||||
|
||||
ABSTRACT_HORSE(ABSTRACT_ANIMAL, null),
|
||||
HORSE(ABSTRACT_HORSE),
|
||||
SKELETON_HORSE(ABSTRACT_HORSE),
|
||||
ZOMBIE_HORSE(ABSTRACT_HORSE),
|
||||
CAMEL(ABSTRACT_HORSE),
|
||||
CAMEL_HUSK(CAMEL),
|
||||
|
||||
ABSTRACT_CHESTED_HORSE(ABSTRACT_HORSE, null),
|
||||
DONKEY(ABSTRACT_CHESTED_HORSE),
|
||||
MULE(ABSTRACT_CHESTED_HORSE),
|
||||
LLAMA(ABSTRACT_CHESTED_HORSE),
|
||||
TRADER_LLAMA(LLAMA),
|
||||
|
||||
// Monsters
|
||||
ABSTRACT_MONSTER(PATHFINDER_MOB, null),
|
||||
BLAZE(ABSTRACT_MONSTER),
|
||||
CREEPER(ABSTRACT_MONSTER),
|
||||
ENDERMITE(ABSTRACT_MONSTER),
|
||||
ENDERMAN(ABSTRACT_MONSTER),
|
||||
GIANT(ABSTRACT_MONSTER),
|
||||
SILVERFISH(ABSTRACT_MONSTER),
|
||||
VEX(ABSTRACT_MONSTER),
|
||||
WITHER(ABSTRACT_MONSTER),
|
||||
BREEZE(ABSTRACT_MONSTER),
|
||||
ZOGLIN(ABSTRACT_MONSTER),
|
||||
WARDEN(ABSTRACT_MONSTER),
|
||||
CREAKING(ABSTRACT_MONSTER),
|
||||
|
||||
ABSTRACT_SKELETON(ABSTRACT_MONSTER, null),
|
||||
SKELETON(ABSTRACT_SKELETON),
|
||||
STRAY(ABSTRACT_SKELETON),
|
||||
WITHER_SKELETON(ABSTRACT_SKELETON),
|
||||
BOGGED(ABSTRACT_SKELETON),
|
||||
PARCHED(ABSTRACT_SKELETON),
|
||||
|
||||
ZOMBIE(ABSTRACT_MONSTER),
|
||||
DROWNED(ZOMBIE),
|
||||
HUSK(ZOMBIE),
|
||||
ZOMBIFIED_PIGLIN(ZOMBIE),
|
||||
ZOMBIE_VILLAGER(ZOMBIE),
|
||||
|
||||
GUARDIAN(ABSTRACT_MONSTER),
|
||||
ELDER_GUARDIAN(GUARDIAN),
|
||||
|
||||
SPIDER(ABSTRACT_MONSTER),
|
||||
CAVE_SPIDER(SPIDER),
|
||||
|
||||
ABSTRACT_PIGLIN(ABSTRACT_MONSTER, null),
|
||||
PIGLIN(ABSTRACT_PIGLIN),
|
||||
PIGLIN_BRUTE(ABSTRACT_PIGLIN),
|
||||
|
||||
// Water mobs
|
||||
AGEABLE_WATER_CREATURE(ABSTRACT_AGEABLE, null),
|
||||
DOLPHIN(AGEABLE_WATER_CREATURE),
|
||||
|
||||
SQUID(AGEABLE_WATER_CREATURE),
|
||||
GLOW_SQUID(SQUID),
|
||||
|
||||
WATER_ANIMAL(PATHFINDER_MOB, null),
|
||||
ABSTRACT_FISH(WATER_ANIMAL, null),
|
||||
PUFFERFISH(ABSTRACT_FISH),
|
||||
TADPOLE(ABSTRACT_FISH),
|
||||
|
||||
ABSTRACT_SCHOOLING_FISH(ABSTRACT_FISH, null),
|
||||
COD(ABSTRACT_SCHOOLING_FISH),
|
||||
SALMON(ABSTRACT_SCHOOLING_FISH),
|
||||
TROPICAL_FISH(ABSTRACT_SCHOOLING_FISH),
|
||||
|
||||
// Raiders
|
||||
ABSTRACT_RAIDER(ABSTRACT_MONSTER, null),
|
||||
WITCH(ABSTRACT_RAIDER),
|
||||
RAVAGER(ABSTRACT_RAIDER),
|
||||
|
||||
ABSTRACT_ILLAGER(ABSTRACT_RAIDER, null),
|
||||
SPELLCASTER_ILLAGER(ABSTRACT_ILLAGER, null),
|
||||
VINDICATOR(ABSTRACT_ILLAGER),
|
||||
PILLAGER(ABSTRACT_ILLAGER),
|
||||
EVOKER(SPELLCASTER_ILLAGER),
|
||||
ILLUSIONER(SPELLCASTER_ILLAGER);
|
||||
|
||||
private static final EntityType[] TYPES = EntityTypeUtil.createSizedArray(values());
|
||||
private final EntityType parent;
|
||||
private final String identifier;
|
||||
private int id = -1;
|
||||
|
||||
EntityTypes26_2(final EntityType parent) {
|
||||
this.parent = parent;
|
||||
this.identifier = Key.namespaced(name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
|
||||
EntityTypes26_2(final EntityType parent, @Nullable final String identifier) {
|
||||
this.parent = parent;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
if (id == -1) {
|
||||
throw new IllegalStateException("Ids have not been initialized yet (type " + name() + ")");
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String identifier() {
|
||||
Preconditions.checkArgument(identifier != null, "Called identifier method on abstract type");
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable EntityType getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAbstractType() {
|
||||
return identifier == null;
|
||||
}
|
||||
|
||||
public static EntityType getTypeFromId(final int typeId) {
|
||||
return EntityTypeUtil.getTypeFromId(TYPES, typeId, PIG);
|
||||
}
|
||||
|
||||
public static void initialize(final Protocol<?, ?, ?, ?> protocol) {
|
||||
EntityTypeUtil.initialize(values(), TYPES, protocol, (type, id) -> type.id = id);
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -23,10 +23,12 @@
|
||||
package com.viaversion.viaversion.api.minecraft.item.data;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.HolderSet;
|
||||
import com.viaversion.viaversion.api.minecraft.RegistryKey;
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.type.TransformingType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.api.type.types.misc.HolderSetType;
|
||||
|
||||
public record DamageResistant26_1(HolderSet types) {
|
||||
|
||||
@@ -34,7 +36,7 @@ public record DamageResistant26_1(HolderSet types) {
|
||||
|
||||
@Override
|
||||
public void write(final Ops ops, final DamageResistant26_1 value) {
|
||||
ops.writeMap(map -> map.write("types", Types.HOLDER_SET, value.types));
|
||||
ops.writeMap(map -> map.write("types", new HolderSetType(RegistryKey.of("damage_type")), value.types));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -94,6 +94,7 @@ public class ProtocolVersion implements Comparable<ProtocolVersion> {
|
||||
public static final ProtocolVersion v1_21_9 = register(773, "1.21.9-1.21.10", new SubVersionRange("1.21", 9, 10));
|
||||
public static final ProtocolVersion v1_21_11 = register(774, "1.21.11");
|
||||
public static final ProtocolVersion v26_1 = register(775, "26.1-26.1.2", new SubVersionRange("26.1", 0, 2));
|
||||
public static final ProtocolVersion v26_2 = register(776, "26.2");
|
||||
public static final ProtocolVersion unknown = new ProtocolVersion(VersionType.SPECIAL, -1, -1, "UNKNOWN", null);
|
||||
|
||||
static {
|
||||
|
||||
@@ -179,7 +179,8 @@ public final class Types {
|
||||
public static final Type<UUID[]> UUID_ARRAY = new ArrayType<>(Types.UUID);
|
||||
|
||||
public static final VarIntType VAR_INT = new VarIntType();
|
||||
public static final OptionalVarIntType OPTIONAL_VAR_INT = new OptionalVarIntType();
|
||||
public static final OptionalVarIntType OPTIONAL_VAR_INT = new OptionalVarIntType(); // compact
|
||||
public static final OptionalType<Integer> BOOL_OPTIONAL_VAR_INT = new VarIntType.OptionalVarIntType(); // regular boolean prefixed
|
||||
public static final Type<int[]> VAR_INT_ARRAY_PRIMITIVE = new VarIntArrayType();
|
||||
public static final VarLongType VAR_LONG = new VarLongType();
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ public class ArrayType<T> extends Type<T[]> {
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
private ArrayType(Class<? super T[]> outputClass, Type<T> type, int maxLength) {
|
||||
super(type.getTypeName() + " Array", outputClass);
|
||||
this.elementType = type;
|
||||
this.maxLength = maxLength;
|
||||
}
|
||||
|
||||
public static Class<?> getArrayClass(Class<?> componentType) {
|
||||
// Should only happen once per class init.
|
||||
return Array.newInstance(componentType, 0).getClass();
|
||||
@@ -101,4 +107,8 @@ public class ArrayType<T> extends Type<T[]> {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public ArrayType<T> withMaxLength(final int maxLength) {
|
||||
return new ArrayType<>(this.getOutputClass(), this.elementType, maxLength);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.api.type.types;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.type.OptionalType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
@@ -35,6 +34,7 @@ public class StringType extends Type<String> {
|
||||
private static final int MAX_CHAR_UTF_8_LENGTH = Character.toString(Character.MAX_VALUE)
|
||||
.getBytes(StandardCharsets.UTF_8).length;
|
||||
private final int maxLength;
|
||||
private final int maxByteLength;
|
||||
|
||||
public StringType() {
|
||||
this(Short.MAX_VALUE);
|
||||
@@ -43,21 +43,22 @@ public class StringType extends Type<String> {
|
||||
public StringType(int maxLength) {
|
||||
super(String.class);
|
||||
this.maxLength = maxLength;
|
||||
this.maxByteLength = maxLength * MAX_CHAR_UTF_8_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String read(ByteBuf buffer) {
|
||||
int len = Types.VAR_INT.readPrimitive(buffer);
|
||||
|
||||
Preconditions.checkArgument(len <= maxLength * MAX_CHAR_UTF_8_LENGTH,
|
||||
"Cannot receive string longer than " + maxLength + " * " + MAX_CHAR_UTF_8_LENGTH + " bytes (got %s bytes)", len);
|
||||
if (len > maxByteLength) {
|
||||
throw new IllegalArgumentException("Cannot receive string longer than " + maxLength + " * " + MAX_CHAR_UTF_8_LENGTH + " bytes (got " + len + " bytes)");
|
||||
}
|
||||
|
||||
String string = buffer.toString(buffer.readerIndex(), len, StandardCharsets.UTF_8);
|
||||
buffer.skipBytes(len);
|
||||
|
||||
Preconditions.checkArgument(string.length() <= maxLength,
|
||||
"Cannot receive string longer than " + maxLength + " characters (got %s bytes)", string.length());
|
||||
|
||||
if (string.length() > maxLength) {
|
||||
throw new IllegalArgumentException("Cannot receive string longer than " + maxLength + " characters (got " + string.length() + ")");
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
package com.viaversion.viaversion.api.type.types;
|
||||
|
||||
import com.viaversion.viaversion.api.minecraft.codec.Ops;
|
||||
import com.viaversion.viaversion.api.type.OptionalType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.api.type.TypeConverter;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
@@ -108,4 +109,11 @@ public class VarIntType extends Type<Integer> implements TypeConverter<Integer>
|
||||
public static int varIntLength(final int value) {
|
||||
return VAR_INT_LENGTHS[Integer.numberOfLeadingZeros(value)];
|
||||
}
|
||||
|
||||
public static final class OptionalVarIntType extends OptionalType<Integer> {
|
||||
|
||||
public OptionalVarIntType() {
|
||||
super(Types.VAR_INT);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-7
@@ -83,8 +83,11 @@ public class PaletteType1_18 extends PaletteTypeBase {
|
||||
final int valuesPerLong = (char) (64 / bitsPerValue);
|
||||
final int expectedLength = (type.size() + valuesPerLong - 1) / valuesPerLong;
|
||||
if (values.length == expectedLength) {
|
||||
CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerValue, type.size(), values,
|
||||
bitsPerValue == globalPaletteBits ? palette::setIdAt : palette::setPaletteIndexAt);
|
||||
if (bitsPerValue == globalPaletteBits) {
|
||||
CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerValue, type.size(), values, palette::setIdAt);
|
||||
} else {
|
||||
palette.setPaletteIndexes(values, bitsPerValue, valuesPerLong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,11 +123,7 @@ public class PaletteType1_18 extends PaletteTypeBase {
|
||||
return;
|
||||
}
|
||||
|
||||
final long[] values = CompactArrayUtil.createCompactArrayWithPadding(
|
||||
bitsPerValue,
|
||||
type.size(),
|
||||
bitsPerValue == globalPaletteBits ? palette::idAt : palette::paletteIndexAt
|
||||
);
|
||||
final long[] values = palette.createPackedValues(bitsPerValue, type.size(), bitsPerValue == globalPaletteBits);
|
||||
Types.LONG_ARRAY_PRIMITIVE.write(buffer, values);
|
||||
}
|
||||
|
||||
|
||||
+6
-11
@@ -46,12 +46,11 @@ public final class PaletteType1_21_5 extends PaletteType1_18 {
|
||||
final int expectedLength = (type.size() + valuesPerLong - 1) / valuesPerLong;
|
||||
final long[] values = LongArrayType.readFixedLength(buffer, expectedLength);
|
||||
if (values.length != 0) {
|
||||
CompactArrayUtil.iterateCompactArrayWithPadding(
|
||||
bitsPerValue,
|
||||
type.size(),
|
||||
values,
|
||||
bitsPerValue == globalPaletteBits ? palette::setIdAt : palette::setPaletteIndexAt
|
||||
);
|
||||
if (bitsPerValue == globalPaletteBits) {
|
||||
CompactArrayUtil.iterateCompactArrayWithPadding(bitsPerValue, type.size(), values, palette::setIdAt);
|
||||
} else {
|
||||
palette.setPaletteIndexes(values, bitsPerValue, valuesPerLong);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,11 +60,7 @@ public final class PaletteType1_21_5 extends PaletteType1_18 {
|
||||
return;
|
||||
}
|
||||
|
||||
final long[] values = CompactArrayUtil.createCompactArrayWithPadding(
|
||||
bitsPerValue,
|
||||
type.size(),
|
||||
bitsPerValue == globalPaletteBits ? palette::idAt : palette::paletteIndexAt
|
||||
);
|
||||
final long[] values = palette.createPackedValues(bitsPerValue, type.size(), bitsPerValue == globalPaletteBits);
|
||||
LongArrayType.writeFixedLength(buffer, values);
|
||||
}
|
||||
|
||||
|
||||
@@ -133,8 +133,7 @@ public class ItemType1_20_5 extends Type<Item> {
|
||||
}
|
||||
|
||||
map.writeMap("components", components -> {
|
||||
for (final Map.Entry<StructuredDataKey<?>, StructuredData<?>> entry : item.dataContainer().data().entrySet()) {
|
||||
final StructuredData<?> data = entry.getValue();
|
||||
for (final StructuredData<?> data : item.dataContainer().data().values()) {
|
||||
String key = Key.namespaced(data.key().identifier());
|
||||
if (data.isEmpty()) {
|
||||
key = "!" + key;
|
||||
|
||||
@@ -100,9 +100,9 @@ public class HolderSetType extends Type<HolderSet> {
|
||||
if (registryKey instanceof final MappingData.MappingType mappingType) {
|
||||
return ops.context().registryAccess().key(mappingType, id);
|
||||
} else if (registryKey instanceof final RegistryValueType registryValueType) {
|
||||
return Key.of(registryValueType.byId(id));
|
||||
return registryValueType.byId(id);
|
||||
} else {
|
||||
return ops.context().registryAccess().registryKey(registryKey.key().toString(), id);
|
||||
return ops.context().registryAccess().registryKey(registryKey.registry().toString(), id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,6 +163,13 @@ public class ParticleType extends DynamicType<Particle> {
|
||||
particle.add(Types.INT, Types.INT.readPrimitive(buf)); // Color
|
||||
particle.add(Types.FLOAT, Types.FLOAT.readPrimitive(buf)); // Power
|
||||
};
|
||||
public static final DataReader<Particle> GEYSER_BASE = (buf, particle) -> {
|
||||
particle.add(Types.INT, Types.INT.readPrimitive(buf)); // Water blocks
|
||||
particle.add(Types.FLOAT, Types.FLOAT.readPrimitive(buf)); // Burst impulse base
|
||||
};
|
||||
public static final DataReader<Particle> GEYSER = (buf, particle) -> {
|
||||
particle.add(Types.INT, Types.INT.readPrimitive(buf)); // Water blocks
|
||||
};
|
||||
|
||||
public static DataReader<Particle> item(Type<Item> item) {
|
||||
return (buf, particle) -> particle.add(item, item.read(buf));
|
||||
@@ -259,12 +266,20 @@ public class ParticleType extends DynamicType<Particle> {
|
||||
return fill1_21_4(protocol).reader("tinted_leaves", Readers.COLOR);
|
||||
}
|
||||
|
||||
public static void fill1_21_9(final Protocol<?, ?, ?, ?> protocol) {
|
||||
fill1_21_5(protocol)
|
||||
public static DataFiller fill1_21_9(final Protocol<?, ?, ?, ?> protocol) {
|
||||
return fill1_21_5(protocol)
|
||||
.reader("dragon_breath", Readers.POWER)
|
||||
.reader("effect", Readers.SPELL)
|
||||
.reader("instant_effect", Readers.SPELL)
|
||||
.reader("flash", Readers.COLOR);
|
||||
}
|
||||
|
||||
public static void fill26_2(final Protocol<?, ?, ?, ?> protocol) {
|
||||
fill1_21_9(protocol)
|
||||
.reader("geyser", Readers.GEYSER)
|
||||
.reader("geyser_base", Readers.GEYSER_BASE)
|
||||
.reader("geyser_poof", Readers.GEYSER_BASE)
|
||||
.reader("geyser_plume", Readers.GEYSER);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-14
@@ -30,32 +30,32 @@ import com.viaversion.viaversion.util.Key;
|
||||
|
||||
public final class RegistryValueType extends VarIntType implements RegistryKey {
|
||||
|
||||
private final Key key;
|
||||
private final String[] names;
|
||||
private final Key registry;
|
||||
private final Key[] keys;
|
||||
|
||||
public RegistryValueType(final Key key, final String... names) {
|
||||
this.key = key;
|
||||
this.names = names;
|
||||
for (int i = 0; i < names.length; i++) {
|
||||
names[i] = Key.namespaced(names[i]);
|
||||
public RegistryValueType(final Key registry, final String... keys) {
|
||||
this.registry = registry;
|
||||
this.keys = new Key[keys.length];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
this.keys[i] = Key.of(keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(final Ops ops, final Integer value) {
|
||||
Types.IDENTIFIER.write(ops, Key.of(names[value]));
|
||||
Types.IDENTIFIER.write(ops, keys[value]);
|
||||
}
|
||||
|
||||
public String[] names() {
|
||||
return names;
|
||||
public Key[] keys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
public String byId(final int id) {
|
||||
return names[id];
|
||||
public Key byId(final int id) {
|
||||
return keys[id];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key key() {
|
||||
return key;
|
||||
public Key registry() {
|
||||
return registry;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -87,4 +87,4 @@ public final class ResolvableProfileType extends Type<ResolvableProfile> {
|
||||
.writeOptional("model", Types.OPTIONAL_STRING, modelType)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -42,7 +42,7 @@ public final class SynchronizedRegistryEitherType extends EitherType<Integer, St
|
||||
@Override
|
||||
public void write(final Ops ops, final Either<Integer, String> value) {
|
||||
if (value.isLeft()) {
|
||||
final Key key = ops.context().registryAccess().registryKey(this.registryKey.key().toString(), value.left());
|
||||
final Key key = ops.context().registryAccess().registryKey(this.registryKey.registry().toString(), value.left());
|
||||
Types.IDENTIFIER.write(ops, key);
|
||||
} else {
|
||||
Types.IDENTIFIER.write(ops, Key.of(value.right()));
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ public final class SynchronizedRegistryValueType extends VarIntType {
|
||||
|
||||
@Override
|
||||
public void write(final Ops ops, final Integer value) {
|
||||
final Key key = ops.context().registryAccess().registryKey(this.registryKey.key().toString(), value);
|
||||
final Key key = ops.context().registryAccess().registryKey(this.registryKey.registry().toString(), value);
|
||||
Types.IDENTIFIER.write(ops, key);
|
||||
}
|
||||
}
|
||||
@@ -47,7 +47,7 @@ public class Types1_20_5<K extends VersionedStructuredDataKeys, E extends Abstra
|
||||
public final Type<StructuredData<?>[]> structuredDataArray = new ArrayType<>(structuredData);
|
||||
public final Type<Item> item = new ItemType1_20_5(structuredData);
|
||||
public final Type<Item> lengthPrefixedItem = new ItemType1_20_5(lengthPrefixedStructuredData);
|
||||
public final Type<Item[]> itemArray = new ArrayType<>(item);
|
||||
public final ArrayType<Item> itemArray = new ArrayType<>(item);
|
||||
public final Type<Item> itemCost = new ItemCostType1_20_5(structuredDataArray);
|
||||
public final Type<Item> optionalItemCost = new ItemCostType1_20_5.OptionalItemCostType(itemCost);
|
||||
private K structuredDataKeys;
|
||||
@@ -77,7 +77,7 @@ public class Types1_20_5<K extends VersionedStructuredDataKeys, E extends Abstra
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type<Item[]> itemArray() {
|
||||
public ArrayType<Item> itemArray() {
|
||||
return itemArray;
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ public class Types1_20_5<K extends VersionedStructuredDataKeys, E extends Abstra
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type<Item[]> itemTemplateArray() {
|
||||
public ArrayType<Item> itemTemplateArray() {
|
||||
return itemArray;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public class Types26_1<K extends VersionedStructuredDataKeys, E extends Abstract
|
||||
|
||||
public final Type<Item> itemTemplate = new ItemTemplateType26_1(structuredData);
|
||||
public final Type<Item> optionalItemTemplate = new ItemTemplateType26_1.OptionalItemTemplateType(itemTemplate);
|
||||
public final Type<Item[]> itemTemplateArray = new ArrayType<>(itemTemplate);
|
||||
public final ArrayType<Item> itemTemplateArray = new ArrayType<>(itemTemplate);
|
||||
|
||||
public Types26_1(final Function<VersionedTypesHolder, K> keysSupplier, final Function<VersionedTypesHolder, E> entityDataTypesSupplier) {
|
||||
super(null, entityDataTypesSupplier);
|
||||
@@ -52,7 +52,7 @@ public class Types26_1<K extends VersionedStructuredDataKeys, E extends Abstract
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type<Item[]> itemTemplateArray() {
|
||||
public ArrayType<Item> itemTemplateArray() {
|
||||
return itemTemplateArray;
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_11;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_2;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys1_21_5;
|
||||
import com.viaversion.viaversion.api.minecraft.data.version.StructuredDataKeys26_2;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_20_5;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21;
|
||||
import com.viaversion.viaversion.api.minecraft.entitydata.types.EntityDataTypes1_21_11;
|
||||
@@ -51,4 +52,5 @@ public final class VersionedTypes {
|
||||
public static final Types1_20_5<StructuredDataKeys1_21_5, EntityDataTypes1_21_9> V1_21_9 = new Types1_20_5<>(StructuredDataKeys1_21_5::new, EntityDataTypes1_21_9::new);
|
||||
public static final Types1_20_5<StructuredDataKeys1_21_11, EntityDataTypes1_21_11> V1_21_11 = new Types1_20_5<>(StructuredDataKeys1_21_11::new, EntityDataTypes1_21_11::new);
|
||||
public static final Types26_1<StructuredDataKeys1_21_11, EntityDataTypes26_1> V26_1 = new Types26_1<>(StructuredDataKeys1_21_11::new, EntityDataTypes26_1::new);
|
||||
public static final Types26_1<StructuredDataKeys26_2, EntityDataTypes26_1> V26_2 = new Types26_1<>(StructuredDataKeys26_2::new, EntityDataTypes26_1::new);
|
||||
}
|
||||
+2
-2
@@ -46,7 +46,7 @@ public interface VersionedTypesHolder {
|
||||
*/
|
||||
Type<Item> item();
|
||||
|
||||
Type<Item[]> itemArray();
|
||||
ArrayType<Item> itemArray();
|
||||
|
||||
/**
|
||||
* Returns the unmapped item stack template type, used for certain recipes/templates in 26.1+.
|
||||
@@ -57,7 +57,7 @@ public interface VersionedTypesHolder {
|
||||
|
||||
Type<Item> optionalItemTemplate();
|
||||
|
||||
Type<Item[]> itemTemplateArray();
|
||||
ArrayType<Item> itemTemplateArray();
|
||||
|
||||
/**
|
||||
* Returns the unmapped item cost type, used in merchant offers.
|
||||
|
||||
@@ -88,6 +88,67 @@ public final class CompactArrayUtil {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
// Utility methods for packing/unpacking raw data at once and without indirection.
|
||||
// They go value-first instead of cell-first
|
||||
|
||||
public static void packWithPadding(final byte[] values, final long[] out, final int bitsPerEntry) {
|
||||
final long maxEntryValue = (1L << bitsPerEntry) - 1;
|
||||
long cell = 0L;
|
||||
int bitIndex = 0;
|
||||
int outIndex = 0;
|
||||
for (final byte value : values) {
|
||||
cell |= (value & maxEntryValue) << bitIndex;
|
||||
bitIndex += bitsPerEntry;
|
||||
if (bitIndex + bitsPerEntry > 64) {
|
||||
out[outIndex++] = cell;
|
||||
cell = 0L;
|
||||
bitIndex = 0;
|
||||
}
|
||||
}
|
||||
if (bitIndex != 0) {
|
||||
out[outIndex] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
public static void packWithPadding(final short[] values, final long[] out, final int bitsPerEntry) {
|
||||
final long maxEntryValue = (1L << bitsPerEntry) - 1;
|
||||
long cell = 0L;
|
||||
int bitIndex = 0;
|
||||
int outIndex = 0;
|
||||
for (final short value : values) {
|
||||
cell |= (value & maxEntryValue) << bitIndex;
|
||||
bitIndex += bitsPerEntry;
|
||||
if (bitIndex + bitsPerEntry > 64) {
|
||||
out[outIndex++] = cell;
|
||||
cell = 0L;
|
||||
bitIndex = 0;
|
||||
}
|
||||
}
|
||||
if (bitIndex != 0) {
|
||||
out[outIndex] = cell;
|
||||
}
|
||||
}
|
||||
|
||||
public static void unpackWithPadding(final long[] data, final byte[] out, final int bitsPerEntry) {
|
||||
final long maxEntryValue = (1L << bitsPerEntry) - 1;
|
||||
final int valuesPerLong = 64 / bitsPerEntry;
|
||||
long cell = 0L;
|
||||
int cellIndex = 0;
|
||||
int remaining = 0;
|
||||
for (int i = 0; i < out.length; i++) {
|
||||
if (remaining == 0) {
|
||||
cell = data[cellIndex++];
|
||||
remaining = valuesPerLong;
|
||||
}
|
||||
out[i] = (byte) (cell & maxEntryValue);
|
||||
cell >>>= bitsPerEntry;
|
||||
remaining--;
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------
|
||||
|
||||
public static long[] createCompactArray(int bitsPerEntry, int entries, IntToLongFunction valueGetter) {
|
||||
long maxEntryValue = (1L << bitsPerEntry) - 1;
|
||||
long[] data = new long[(int) Math.ceil(entries * bitsPerEntry / 64.0)];
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.util;
|
||||
|
||||
final class CustomKey extends Key {
|
||||
|
||||
private final String original;
|
||||
private final String namespace;
|
||||
private final String path;
|
||||
|
||||
/**
|
||||
* Non-Minecraft key.
|
||||
*
|
||||
* @param original full key including the ':' separator
|
||||
* @param namespace non-Minecraft namespace
|
||||
* @param path path
|
||||
*/
|
||||
CustomKey(final String original, final String namespace, final String path) {
|
||||
if (!MinecraftKey.isValidNamespace(namespace)) {
|
||||
throw new IllegalArgumentException("Invalid namespace: " + namespace);
|
||||
}
|
||||
if (!MinecraftKey.isValidPath(path)) {
|
||||
throw new IllegalArgumentException("Invalid path: " + path);
|
||||
}
|
||||
this.original = original;
|
||||
this.namespace = namespace;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String namespace() {
|
||||
return namespace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String path() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String original() {
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String minimized() {
|
||||
return this.original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMinecraftNamespace() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof final CustomKey key)) return false;
|
||||
return this.original.equals(key.original);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return this.original.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.original;
|
||||
}
|
||||
}
|
||||
@@ -24,25 +24,17 @@ package com.viaversion.viaversion.util;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class Key {
|
||||
import static com.viaversion.viaversion.util.MinecraftKey.MINECRAFT_NAMESPACE;
|
||||
import static com.viaversion.viaversion.util.MinecraftKey.MINECRAFT_NAMESPACE_LENGTH;
|
||||
import static com.viaversion.viaversion.util.MinecraftKey.MINECRAFT_NAMESPACE_WITH_COLON;
|
||||
|
||||
private static final String MINECRAFT_NAMESPACE = "minecraft";
|
||||
private static final int MINECRAFT_NAMESPACE_LENGTH = MINECRAFT_NAMESPACE.length();
|
||||
private final String original;
|
||||
private final String namespace;
|
||||
private final String path;
|
||||
|
||||
private Key(final String original, final String namespace, final String path) {
|
||||
if (!isValidNamespace(namespace)) {
|
||||
throw new IllegalArgumentException("Invalid namespace: " + namespace);
|
||||
}
|
||||
if (!isValidPath(path)) {
|
||||
throw new IllegalArgumentException("Invalid path: " + path);
|
||||
}
|
||||
this.original = original; // assume this is correct; also saves whether the namespace was explitly set
|
||||
this.namespace = namespace;
|
||||
this.path = path;
|
||||
}
|
||||
/**
|
||||
* Represents a Minecraft Identifier/ResourceLocation.
|
||||
* <p>
|
||||
* Internally split into {@link CustomKey} and {@link MinecraftKey} to reduce data held
|
||||
* and keep the runtime/allocation cost of {@link #original()} and {@link #toString()} minimal.
|
||||
*/
|
||||
public abstract sealed class Key permits MinecraftKey, CustomKey {
|
||||
|
||||
/**
|
||||
* Creates a new key with the given namespace and path.
|
||||
@@ -52,7 +44,10 @@ public final class Key {
|
||||
* @return a new key with the given namespace and path
|
||||
*/
|
||||
public static Key of(final String namespace, final String path) {
|
||||
return new Key(namespace + ':' + path, namespace, path);
|
||||
if (namespace.equals(MINECRAFT_NAMESPACE) || namespace.isEmpty()) {
|
||||
return ofPath(path); // ends up compacting it
|
||||
}
|
||||
return new CustomKey(namespace + ':' + path, namespace, path);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,7 +57,7 @@ public final class Key {
|
||||
* @return a new key with the given path and the default namespace
|
||||
*/
|
||||
public static Key ofPath(final String path) {
|
||||
return new Key(path, MINECRAFT_NAMESPACE, path);
|
||||
return new MinecraftKey.CompactMinecraftKey(path);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,9 +72,17 @@ public final class Key {
|
||||
return ofPath(identifier);
|
||||
}
|
||||
|
||||
final String namespace = separatorIndex == 0 ? MINECRAFT_NAMESPACE : identifier.substring(0, separatorIndex);
|
||||
final String path = identifier.substring(separatorIndex + 1);
|
||||
return new Key(identifier, namespace, path);
|
||||
if (separatorIndex == 0) {
|
||||
return new MinecraftKey.ColonPrefixedMinecraftKey(identifier, path);
|
||||
}
|
||||
|
||||
final String namespace = identifier.substring(0, separatorIndex);
|
||||
if (namespace.equals(MINECRAFT_NAMESPACE)) {
|
||||
return new MinecraftKey.FullMinecraftKey(identifier, path);
|
||||
}
|
||||
|
||||
return new CustomKey(identifier, namespace, path);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -96,6 +99,38 @@ public final class Key {
|
||||
}
|
||||
}
|
||||
|
||||
public abstract String namespace();
|
||||
|
||||
public abstract String path();
|
||||
|
||||
/**
|
||||
* Returns the unmodified original identifier, possbily without an explicit namespace.
|
||||
*
|
||||
* @return the original identifier, possibly without an explicit namespace
|
||||
*/
|
||||
public abstract String original();
|
||||
|
||||
/**
|
||||
* Returns the identifier in a minimized form. If the namespace is "minecraft", it will return just the path.
|
||||
*
|
||||
* @return the identifier in a minimized form
|
||||
*/
|
||||
public abstract String minimized();
|
||||
|
||||
public abstract boolean hasMinecraftNamespace();
|
||||
|
||||
public Key withNamespace(final String namespace) {
|
||||
return of(namespace, this.path());
|
||||
}
|
||||
|
||||
public Key withPath(final String path) {
|
||||
return of(this.namespace(), path);
|
||||
}
|
||||
|
||||
public final boolean equals(final String identifier) {
|
||||
return this.equals(Key.of(identifier));
|
||||
}
|
||||
|
||||
public static String stripNamespace(final String identifier) {
|
||||
int index = identifier.indexOf(':');
|
||||
if (index == -1) {
|
||||
@@ -106,16 +141,14 @@ public final class Key {
|
||||
|
||||
public static String namespace(final String identifier) {
|
||||
final int index = identifier.indexOf(':');
|
||||
if (index == -1) {
|
||||
return MINECRAFT_NAMESPACE;
|
||||
} else if (index == 0) {
|
||||
if (index == -1 || index == 0) {
|
||||
return MINECRAFT_NAMESPACE;
|
||||
}
|
||||
return identifier.substring(0, index);
|
||||
}
|
||||
|
||||
public static String stripMinecraftNamespace(final String identifier) {
|
||||
if (identifier.startsWith("minecraft:")) {
|
||||
if (identifier.startsWith(MINECRAFT_NAMESPACE_WITH_COLON)) {
|
||||
return identifier.substring(MINECRAFT_NAMESPACE_LENGTH + 1);
|
||||
} else if (!identifier.isEmpty() && identifier.charAt(0) == ':') {
|
||||
return identifier.substring(1);
|
||||
@@ -130,7 +163,7 @@ public final class Key {
|
||||
public static String namespaced(final String identifier) {
|
||||
final int index = identifier.indexOf(':');
|
||||
if (index == -1) {
|
||||
return "minecraft:" + identifier;
|
||||
return MINECRAFT_NAMESPACE_WITH_COLON + identifier;
|
||||
} else if (index == 0) {
|
||||
return MINECRAFT_NAMESPACE + identifier;
|
||||
}
|
||||
@@ -140,98 +173,13 @@ public final class Key {
|
||||
public static boolean isValid(final String identifier) {
|
||||
final int separatorIndex = identifier.indexOf(':');
|
||||
if (separatorIndex == -1) {
|
||||
return isValidPath(identifier);
|
||||
return MinecraftKey.isValidPath(identifier);
|
||||
} else if (separatorIndex == 0) {
|
||||
return isValidPath(identifier.substring(1));
|
||||
return MinecraftKey.isValidPath(identifier.substring(1));
|
||||
}
|
||||
|
||||
final String namespace = identifier.substring(0, separatorIndex);
|
||||
final String path = identifier.substring(separatorIndex + 1);
|
||||
return isValidNamespace(namespace) && isValidPath(path);
|
||||
}
|
||||
|
||||
public String namespace() {
|
||||
return this.namespace;
|
||||
}
|
||||
|
||||
public String path() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the unmodified original identifier, possbily without an explicit namespace.
|
||||
*
|
||||
* @return the original identifier, possibly without an explicit namespace
|
||||
*/
|
||||
public String original() {
|
||||
return this.original;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the identifier in a minimized form. If the namespace is "minecraft", it will return just the path.
|
||||
*
|
||||
* @return the identifier in a minimized form
|
||||
*/
|
||||
public String minimized() {
|
||||
return this.hasMinecraftNamespace() ? this.path : this.toString();
|
||||
}
|
||||
|
||||
public boolean hasMinecraftNamespace() {
|
||||
return this.namespace.equals(MINECRAFT_NAMESPACE);
|
||||
}
|
||||
|
||||
public Key withNamespace(final String namespace) {
|
||||
return of(namespace, this.path);
|
||||
}
|
||||
|
||||
public Key withPath(final String path) {
|
||||
return of(this.namespace, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof final Key key)) return false;
|
||||
return this.namespace.equals(key.namespace) && this.path.equals(key.path);
|
||||
}
|
||||
|
||||
public final boolean equals(final String identifier) {
|
||||
return this.equals(Key.of(identifier));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = this.namespace.hashCode();
|
||||
result = 31 * result + this.path.hashCode();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.namespace + ':' + this.path;
|
||||
}
|
||||
|
||||
private static boolean isValidNamespace(final String namespace) {
|
||||
//noinspection StringEquality - a quick way out
|
||||
if (namespace == MINECRAFT_NAMESPACE) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0, length = namespace.length(); i < length; i++) {
|
||||
final char c = namespace.charAt(i);
|
||||
if (!(c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_' || c == '-' || c == '.')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isValidPath(final String path) {
|
||||
for (int i = 0, length = path.length(); i < length; i++) {
|
||||
final char c = path.charAt(i);
|
||||
if (!(c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_' || c == '-' || c == '.' || c == '/')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return MinecraftKey.isValidNamespace(namespace) && MinecraftKey.isValidPath(path);
|
||||
}
|
||||
}
|
||||
@@ -26,25 +26,25 @@ import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Arrays;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class KeyMappings {
|
||||
|
||||
private final Object2IntMap<String> keyToId;
|
||||
private final String[] keys;
|
||||
private final Key[] keys;
|
||||
|
||||
public KeyMappings(final String... keys) {
|
||||
public KeyMappings(final Key... keys) {
|
||||
this.keys = keys;
|
||||
keyToId = new Object2IntOpenHashMap<>(keys.length);
|
||||
keyToId.defaultReturnValue(-1);
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
keyToId.put(keys[i], i);
|
||||
keyToId.put(keys[i].minimized(), i);
|
||||
}
|
||||
}
|
||||
|
||||
public KeyMappings(final Collection<String> keys) {
|
||||
this(keys.toArray(new String[0]));
|
||||
public KeyMappings(final String... keys) {
|
||||
this(Arrays.stream(keys).map(Key::of).toArray(Key[]::new));
|
||||
}
|
||||
|
||||
public KeyMappings(final ListTag<StringTag> keys) {
|
||||
@@ -52,6 +52,13 @@ public final class KeyMappings {
|
||||
}
|
||||
|
||||
public @Nullable String idToKey(final int id) {
|
||||
if (id < 0 || id >= keys.length) {
|
||||
return null;
|
||||
}
|
||||
return keys[id].minimized();
|
||||
}
|
||||
|
||||
public @Nullable Key key(final int id) {
|
||||
if (id < 0 || id >= keys.length) {
|
||||
return null;
|
||||
}
|
||||
@@ -62,7 +69,7 @@ public final class KeyMappings {
|
||||
return keyToId.getInt(Key.stripMinecraftNamespace(identifier));
|
||||
}
|
||||
|
||||
public String[] keys() {
|
||||
public Key[] keys() {
|
||||
return keys;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-2026 ViaVersion and contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
package com.viaversion.viaversion.util;
|
||||
|
||||
|
||||
abstract sealed class MinecraftKey extends Key {
|
||||
|
||||
static final String MINECRAFT_NAMESPACE = "minecraft";
|
||||
static final String MINECRAFT_NAMESPACE_WITH_COLON = "minecraft:";
|
||||
static final int MINECRAFT_NAMESPACE_LENGTH = MINECRAFT_NAMESPACE.length();
|
||||
private final String path;
|
||||
|
||||
/**
|
||||
* Minecraft key with a custom path.
|
||||
*
|
||||
* @param path path
|
||||
*/
|
||||
protected MinecraftKey(final String path) {
|
||||
if (!isValidPath(path)) {
|
||||
throw new IllegalArgumentException("Invalid path: " + path);
|
||||
}
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String namespace() {
|
||||
return MINECRAFT_NAMESPACE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String path() {
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String minimized() {
|
||||
return this.path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMinecraftNamespace() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof final MinecraftKey key)) return false;
|
||||
return this.path.equals(key.path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return path.hashCode();
|
||||
}
|
||||
|
||||
static boolean isValidNamespace(final String namespace) {
|
||||
for (int i = 0, length = namespace.length(); i < length; i++) {
|
||||
final char c = namespace.charAt(i);
|
||||
if (!(c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_' || c == '-' || c == '.')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static boolean isValidPath(final String path) {
|
||||
// Effectively the same as !(c >= 'a' && c <= 'z' || c >= '0' && c <= '9' || c == '_' || c == '-' || c == '.' || c == '/')
|
||||
// but with less branches, making use of char being unsigned and the fact the separation chars are right after the numbers
|
||||
for (int i = 0, length = path.length(); i < length; i++) {
|
||||
final char c = path.charAt(i);
|
||||
if ((char) (c - 'a') > 25 // not a-z
|
||||
&& (char) (c - '-') > 12 // not - . / 0-9
|
||||
&& c != '_') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact Minecraft key, e.g. {@code "dirt"}
|
||||
*/
|
||||
static final class CompactMinecraftKey extends MinecraftKey {
|
||||
|
||||
private String identifier;
|
||||
|
||||
CompactMinecraftKey(final String path) {
|
||||
super(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String original() {
|
||||
return path();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (identifier == null) {
|
||||
identifier = MINECRAFT_NAMESPACE_WITH_COLON + this.path();
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Colon-prefixed Minecraft keys, e.g. {@code ":dirt"}
|
||||
*/
|
||||
static final class ColonPrefixedMinecraftKey extends MinecraftKey {
|
||||
|
||||
private final String original;
|
||||
private String identifier;
|
||||
|
||||
ColonPrefixedMinecraftKey(final String original, final String path) {
|
||||
super(path);
|
||||
this.original = original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String original() {
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (identifier == null) {
|
||||
identifier = MINECRAFT_NAMESPACE_WITH_COLON + this.path();
|
||||
}
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Full Minecraft keys, e.g. {@code "minecraft:dirt"}
|
||||
*/
|
||||
static final class FullMinecraftKey extends MinecraftKey {
|
||||
|
||||
private final String identifier;
|
||||
|
||||
FullMinecraftKey(final String identifier, final String path) {
|
||||
super(path);
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String original() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
package com.viaversion.viaversion.util;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class UUIDUtil {
|
||||
@@ -49,4 +50,20 @@ public final class UUIDUtil {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a random UUID, similar to {@link UUID#randomUUID()}, but without using {@link java.security.SecureRandom} and its synchronization.
|
||||
* <p>
|
||||
* Should not be used where a UUID should be unpredictable.
|
||||
*
|
||||
* @return random UUID
|
||||
*/
|
||||
public static UUID randomUUID() {
|
||||
final ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
long most = random.nextLong();
|
||||
long least = random.nextLong();
|
||||
most = (most & 0xffffffffffff0fffL) | 0x0000000000004000L; // version 4
|
||||
least = (least & 0x3fffffffffffffffL) | 0x8000000000000000L; // IETF variant
|
||||
return new UUID(most, least);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ repositories {
|
||||
|
||||
dependencies {
|
||||
// version must be manually kept in sync with the one in root project settings.gradle.kts
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.4.1")
|
||||
implementation("com.gradleup.shadow:shadow-gradle-plugin:9.4.2")
|
||||
|
||||
// A nice no-conflict comment for patching in downgrading
|
||||
}
|
||||
@@ -245,7 +245,8 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
|
||||
final String className = Bukkit.getServer().getClass().getName();
|
||||
final String craftBukkitPackage = className.substring(0, className.lastIndexOf('.'));
|
||||
Class.forName(craftBukkitPackage + ".entity.CraftPlayer").getMethod("addChannel", String.class).invoke(player, channel);
|
||||
} catch (final IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
|
||||
} catch (final IllegalAccessException | InvocationTargetException | NoSuchMethodException |
|
||||
ClassNotFoundException e) {
|
||||
getLogger().log(Level.SEVERE, "Failed to register custom payload channel " + channel + " for player " + player.getName(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -51,8 +51,6 @@ import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ViaManagerImpl implements ViaManager {
|
||||
private final ProtocolManagerImpl protocolManager = new ProtocolManagerImpl();
|
||||
@@ -245,28 +243,13 @@ public class ViaManagerImpl implements ViaManager {
|
||||
platform.getLogger().info("ViaVersion has been disabled; uninjected the platform and shut down the scheduler.");
|
||||
}
|
||||
|
||||
private void checkJavaVersion() { // Stolen from Paper
|
||||
String javaVersion = System.getProperty("java.version");
|
||||
Matcher matcher = Pattern.compile("(?:1\\.)?(\\d+)").matcher(javaVersion);
|
||||
if (!matcher.find()) {
|
||||
platform.getLogger().warning("Failed to determine Java version; could not parse: " + javaVersion);
|
||||
return;
|
||||
}
|
||||
|
||||
String versionString = matcher.group(1);
|
||||
int version;
|
||||
try {
|
||||
version = Integer.parseInt(versionString);
|
||||
} catch (NumberFormatException e) {
|
||||
platform.getLogger().log(Level.WARNING, "Failed to determine Java version; could not parse: " + versionString, e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (version < 17) {
|
||||
private void checkJavaVersion() {
|
||||
int javaVersion = Runtime.version().feature();
|
||||
if (javaVersion < 17) {
|
||||
platform.getLogger().warning("You are running an outdated Java version, please update it to at least Java 21 (your version is " + javaVersion + ").");
|
||||
platform.getLogger().warning("ViaVersion no longer officially supports this version of Java, only offering unsupported compatibility builds.");
|
||||
platform.getLogger().warning("See https://github.com/ViaVersion/ViaVersion/releases/tag/5.0.0 for more information.");
|
||||
} else if (version < 21) {
|
||||
} else if (javaVersion < 21) {
|
||||
platform.getLogger().warning("Please update your Java runtime to at least Java 21 (your version is " + javaVersion + ").");
|
||||
platform.getLogger().warning("At some point in the future, ViaVersion will no longer be compatible with this version of Java.");
|
||||
}
|
||||
|
||||
+3
-1
@@ -17,7 +17,9 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.codec.hash;
|
||||
|
||||
final class CRC32C implements HashFunction {
|
||||
// Required so long as Java 8 downgraded VV jars are offered
|
||||
@Deprecated
|
||||
final class FallbackCRC32C implements HashFunction {
|
||||
|
||||
private static final int[] CRC32C_TABLE = new int[256];
|
||||
|
||||
@@ -20,7 +20,9 @@ package com.viaversion.viaversion.codec.hash;
|
||||
@FunctionalInterface
|
||||
public interface HashFunction {
|
||||
|
||||
HashFunction CRC32C = new CRC32C();
|
||||
static HashFunction crc32c() {
|
||||
return Runtime.version().feature() < 9 ? new FallbackCRC32C() : new JavaCRC32C();
|
||||
}
|
||||
|
||||
int hashBytes(byte[] data, int length);
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ public class HashOps extends OpsBase implements Hasher {
|
||||
|
||||
@Override
|
||||
public void writeString(final CharSequence sequence) {
|
||||
hashBuilder.preSize(sequence.length() + Byte.BYTES + Integer.BYTES)
|
||||
hashBuilder.preSize((sequence.length() * Character.BYTES) + Byte.BYTES + Integer.BYTES)
|
||||
.writeByte(TAG_STRING)
|
||||
.writeInt(sequence.length())
|
||||
.writeString(sequence);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-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.viaversion.codec.hash;
|
||||
|
||||
import java.util.zip.CRC32C;
|
||||
|
||||
final class JavaCRC32C implements HashFunction {
|
||||
|
||||
private final CRC32C crc = new CRC32C();
|
||||
|
||||
@Override
|
||||
public int hashBytes(final byte[] data, final int length) {
|
||||
crc.reset();
|
||||
crc.update(data, 0, length);
|
||||
return (int) crc.getValue();
|
||||
}
|
||||
}
|
||||
@@ -72,6 +72,7 @@ public class AbstractViaConfig extends Config implements ViaVersionConfig {
|
||||
private String reloadDisconnectMessage;
|
||||
private boolean logOtherConversionErrors;
|
||||
private boolean logTextComponentConversionErrors;
|
||||
private boolean multiReddustColorFix;
|
||||
private boolean disable1_13TabComplete;
|
||||
private boolean teamColourFix;
|
||||
private boolean serversideBlockConnections;
|
||||
@@ -146,6 +147,7 @@ public class AbstractViaConfig extends Config implements ViaVersionConfig {
|
||||
blockedDisconnectMessage = getString("block-disconnect-msg", "You are using an unsupported Minecraft version!");
|
||||
reloadDisconnectMessage = getString("reload-disconnect-msg", "Server reload, please rejoin!");
|
||||
teamColourFix = getBoolean("team-colour-fix", true);
|
||||
multiReddustColorFix = getBoolean("multi-reddust-color-fix", false);
|
||||
disable1_13TabComplete = getBoolean("disable-1_13-auto-complete", false);
|
||||
serversideBlockConnections = getBoolean("serverside-blockconnections", true);
|
||||
reduceBlockStorageMemory = getBoolean("reduce-blockstorage-memory", false);
|
||||
@@ -475,6 +477,11 @@ public class AbstractViaConfig extends Config implements ViaVersionConfig {
|
||||
return reloadDisconnectMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMultiReddustColorFix() {
|
||||
return multiReddustColorFix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean is1_13TeamColourFix() {
|
||||
return teamColourFix;
|
||||
|
||||
+2
-1
@@ -35,6 +35,7 @@ public class ConnectionManagerImpl implements ConnectionManager {
|
||||
protected final Map<UUID, UserConnection> serverConnections = new ConcurrentHashMap<>();
|
||||
protected final Map<UUID, UserConnection> clientConnections = new ConcurrentHashMap<>();
|
||||
protected final Set<UserConnection> connections = ConcurrentHashMap.newKeySet();
|
||||
private final Set<UserConnection> connectionsView = Collections.unmodifiableSet(connections);
|
||||
|
||||
@Override
|
||||
public void onLoginSuccess(UserConnection connection) {
|
||||
@@ -120,7 +121,7 @@ public class ConnectionManagerImpl implements ConnectionManager {
|
||||
|
||||
@Override
|
||||
public Set<UserConnection> getConnections() {
|
||||
return Collections.unmodifiableSet(connections);
|
||||
return connectionsView;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.connection;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.ProtocolInfo;
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
@@ -43,30 +42,28 @@ import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPipeline;
|
||||
import io.netty.handler.codec.CodecException;
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.longs.LongSet;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.SplittableRandom;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Function;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class UserConnectionImpl implements UserConnection {
|
||||
private static final int PASSTHROUGH_DATA_BYTES = Long.BYTES * 2 + 2;
|
||||
private static final int PASSTHROUGH_DATA_BYTES = Long.BYTES + 2;
|
||||
private static final AtomicLong IDS = new AtomicLong();
|
||||
private final long id = IDS.incrementAndGet();
|
||||
private final SplittableRandom random = new SplittableRandom();
|
||||
private final Map<Class<?>, StorableObject> storedObjects = new ConcurrentHashMap<>();
|
||||
private final Map<Class<? extends Protocol>, EntityTracker> entityTrackers = new Reference2ObjectOpenHashMap<>();
|
||||
private final Map<Class<? extends Protocol>, ItemHasher> itemHashers = new Reference2ObjectOpenHashMap<>();
|
||||
private final Map<Class<? extends Protocol>, ClientWorld> clientWorlds = new Reference2ObjectOpenHashMap<>();
|
||||
private final PacketTracker packetTracker = new PacketTracker(this);
|
||||
private final Set<UUID> passthroughTokens = Collections.newSetFromMap(CacheBuilder.newBuilder()
|
||||
.expireAfterWrite(10, TimeUnit.SECONDS)
|
||||
.<UUID, Boolean>build().asMap());
|
||||
private final LongSet passthroughTokens = new LongOpenHashSet();
|
||||
private final ProtocolInfo protocolInfo = new ProtocolInfoImpl();
|
||||
private final Channel channel;
|
||||
private final boolean clientSide;
|
||||
@@ -261,7 +258,7 @@ public class UserConnectionImpl implements UserConnection {
|
||||
if (shouldTransformPacket()) {
|
||||
// Bypass serverbound packet decoder transforming
|
||||
Types.VAR_INT.writePrimitive(buf, PacketWrapper.PASSTHROUGH_ID);
|
||||
Types.UUID.write(buf, generatePassthroughToken());
|
||||
Types.LONG.writePrimitive(buf, generatePassthroughToken());
|
||||
}
|
||||
|
||||
buf.writeBytes(packet);
|
||||
@@ -346,11 +343,13 @@ public class UserConnectionImpl implements UserConnection {
|
||||
}
|
||||
|
||||
final int id = Types.VAR_INT.readPrimitive(buf);
|
||||
if (id == PacketWrapper.PASSTHROUGH_ID) {
|
||||
if (!passthroughTokens.remove(Types.UUID.read(buf))) {
|
||||
throw new IllegalArgumentException("Invalid token");
|
||||
if (id == PacketWrapper.PASSTHROUGH_ID && buf.readableBytes() >= Long.BYTES) {
|
||||
if (removePassthroughToken(buf.readLong())) {
|
||||
return;
|
||||
}
|
||||
return;
|
||||
|
||||
// Move reader back and treat it like a regular packet
|
||||
buf.readerIndex(buf.readerIndex() - Long.BYTES);
|
||||
}
|
||||
|
||||
final int valuesReaderIndex = buf.readerIndex();
|
||||
@@ -443,10 +442,22 @@ public class UserConnectionImpl implements UserConnection {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID generatePassthroughToken() {
|
||||
UUID token = UUID.randomUUID();
|
||||
passthroughTokens.add(token);
|
||||
return token;
|
||||
public long generatePassthroughToken() {
|
||||
// Don't use TLR so it's not entirely predictable; use SplittableRandom over Random since it actually has a 64 bit seed
|
||||
while (true) {
|
||||
synchronized (passthroughTokens) {
|
||||
final long token = random.nextLong();
|
||||
if (passthroughTokens.add(token)) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean removePassthroughToken(final long token) {
|
||||
synchronized (passthroughTokens) {
|
||||
return passthroughTokens.remove(token);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -23,9 +23,10 @@ import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.entity.ClientEntityIdChangeListener;
|
||||
import com.viaversion.viaversion.api.data.entity.DimensionData;
|
||||
import com.viaversion.viaversion.api.data.entity.EntityTracker;
|
||||
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
|
||||
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
|
||||
import com.viaversion.viaversion.api.minecraft.chunks.Chunk;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import com.viaversion.viaversion.api.type.Type;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.KeyMappings;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
@@ -48,6 +49,8 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
|
||||
private int biomesSent = -1;
|
||||
private Map<String, DimensionData> dimensions = Collections.emptyMap();
|
||||
private boolean instaBuild;
|
||||
private Type<Chunk> chunkType;
|
||||
private Type<Chunk> mappedChunkType;
|
||||
|
||||
public EntityTrackerBase(UserConnection connection, @Nullable EntityType playerType) {
|
||||
this.connection = connection;
|
||||
@@ -60,8 +63,10 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEntity(int id, EntityType type) {
|
||||
entities.put(id, new TrackedEntityImpl(type));
|
||||
public TrackedEntity addEntity(int id, EntityType type) {
|
||||
final TrackedEntityImpl entity = new TrackedEntityImpl(type);
|
||||
entities.put(id, entity);
|
||||
return entity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,20 +86,8 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable StoredEntityData entityData(int id) {
|
||||
final TrackedEntity entity = entities.get(id);
|
||||
return entity != null ? entity.data() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable StoredEntityData entityDataIfPresent(int id) {
|
||||
final TrackedEntity entity = entities.get(id);
|
||||
return entity != null && entity.hasData() ? entity.data() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(int id) {
|
||||
entities.remove(id);
|
||||
public TrackedEntity removeEntity(int id) {
|
||||
return entities.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -164,6 +157,10 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
|
||||
|
||||
@Override
|
||||
public void setCurrentWorldSectionHeight(int currentWorldSectionHeight) {
|
||||
if (this.currentWorldSectionHeight != currentWorldSectionHeight) {
|
||||
this.chunkType = null;
|
||||
this.mappedChunkType = null;
|
||||
}
|
||||
this.currentWorldSectionHeight = currentWorldSectionHeight;
|
||||
}
|
||||
|
||||
@@ -204,9 +201,27 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
|
||||
|
||||
@Override
|
||||
public void setBiomesSent(int biomesSent) {
|
||||
if (this.biomesSent != biomesSent) {
|
||||
this.chunkType = null;
|
||||
this.mappedChunkType = null;
|
||||
}
|
||||
this.biomesSent = biomesSent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Type<Chunk> chunkType(final boolean mapped) {
|
||||
return mapped ? mappedChunkType : chunkType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChunkType(final boolean mapped, @Nullable final Type<Chunk> chunkType) {
|
||||
if (mapped) {
|
||||
this.mappedChunkType = chunkType;
|
||||
} else {
|
||||
this.chunkType = chunkType;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType playerType() {
|
||||
return playerType;
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-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.viaversion.data.entity;
|
||||
|
||||
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class StoredEntityDataImpl implements StoredEntityData {
|
||||
private final Map<Class<?>, Object> storedObjects = new HashMap<>();
|
||||
private final EntityType type;
|
||||
|
||||
public StoredEntityDataImpl(EntityType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable <T> T get(Class<T> objectClass) {
|
||||
//noinspection unchecked
|
||||
return (T) storedObjects.get(objectClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> @Nullable T remove(Class<T> objectClass) {
|
||||
//noinspection unchecked
|
||||
return (T) storedObjects.remove(objectClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(Class<?> objectClass) {
|
||||
return storedObjects.containsKey(objectClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(Object object) {
|
||||
storedObjects.put(object.getClass(), object);
|
||||
}
|
||||
}
|
||||
@@ -17,13 +17,16 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.data.entity;
|
||||
|
||||
import com.viaversion.viaversion.api.data.entity.StoredEntityData;
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityType;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectArrayMap;
|
||||
import java.util.Map;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public final class TrackedEntityImpl implements TrackedEntity {
|
||||
private final EntityType entityType;
|
||||
private StoredEntityData data;
|
||||
private Map<Class<?>, StorableObject> storedObjects;
|
||||
private boolean sentEntityData;
|
||||
|
||||
public TrackedEntityImpl(final EntityType entityType) {
|
||||
@@ -36,16 +39,32 @@ public final class TrackedEntityImpl implements TrackedEntity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoredEntityData data() {
|
||||
if (data == null) {
|
||||
data = new StoredEntityDataImpl(entityType);
|
||||
}
|
||||
return data;
|
||||
public boolean has(final Class<? extends StorableObject> objectClass) {
|
||||
return storedObjects != null && storedObjects.containsKey(objectClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasData() {
|
||||
return data != null;
|
||||
public @Nullable <T extends StorableObject> T get(final Class<T> objectClass) {
|
||||
//noinspection unchecked
|
||||
return storedObjects != null ? (T) storedObjects.get(objectClass) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(final StorableObject object) {
|
||||
storedObjects().put(object.getClass(), object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends StorableObject> @Nullable T remove(final Class<T> objectClass) {
|
||||
//noinspection unchecked
|
||||
return storedObjects != null ? (T) storedObjects.remove(objectClass) : null;
|
||||
}
|
||||
|
||||
private Map<Class<?>, StorableObject> storedObjects() {
|
||||
if (storedObjects == null) {
|
||||
storedObjects = new Reference2ObjectArrayMap<>(); // Only ever contains 1-2 entries
|
||||
}
|
||||
return storedObjects;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,7 +81,7 @@ public final class TrackedEntityImpl implements TrackedEntity {
|
||||
public String toString() {
|
||||
return "TrackedEntityImpl{" +
|
||||
"entityType=" + entityType +
|
||||
", data=" + data +
|
||||
", storedObjects=" + storedObjects +
|
||||
", sentEntityData=" + sentEntityData +
|
||||
'}';
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public class ItemHasherBase implements ItemHasher {
|
||||
* @return the hashed item
|
||||
*/
|
||||
public HashedItem toHashedItem(final Item item, final boolean mapped) {
|
||||
return toHashedItem(new HashOps(mapped ? mappedContext : context, HashFunction.CRC32C), item);
|
||||
return toHashedItem(new HashOps(mapped ? mappedContext : context, HashFunction.crc32c()), item);
|
||||
}
|
||||
|
||||
public static HashedItem toHashedItem(final Hasher hasher, final Item item) {
|
||||
@@ -137,7 +137,7 @@ public class ItemHasherBase implements ItemHasher {
|
||||
}
|
||||
|
||||
private int hashTag(final CompoundTag tag) {
|
||||
final HashOps hasher = new HashOps(context, HashFunction.CRC32C);
|
||||
final HashOps hasher = new HashOps(context, HashFunction.crc32c());
|
||||
Types.TAG.write(hasher, tag);
|
||||
return hasher.hash();
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.Protocol1_8To1_9;
|
||||
import com.viaversion.viaversion.protocols.v1_8to1_9.packet.ClientboundPackets1_9;
|
||||
import com.viaversion.viaversion.util.ComponentUtil;
|
||||
import com.viaversion.viaversion.util.UUIDUtil;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
@@ -54,7 +55,7 @@ public class CommonBoss implements BossBar {
|
||||
Preconditions.checkNotNull(title, "Title cannot be null");
|
||||
Preconditions.checkArgument((health >= 0 && health <= 1), "Health must be between 0 and 1. Input: " + health);
|
||||
|
||||
this.uuid = UUID.randomUUID();
|
||||
this.uuid = UUIDUtil.randomUUID();
|
||||
this.title = title;
|
||||
this.health = health;
|
||||
this.color = color == null ? BossColor.PURPLE : color;
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.viaversion.viaversion.api.platform.ViaChannelHandler;
|
||||
import com.viaversion.viaversion.exception.CancelCodecException;
|
||||
import com.viaversion.viaversion.exception.CancelDecoderException;
|
||||
import com.viaversion.viaversion.exception.CancelEncoderException;
|
||||
import com.viaversion.viaversion.util.ByteBufUtil;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
@@ -57,7 +58,7 @@ public class ViaCodecHandler extends ByteToMessageCodec<ByteBuf> implements ViaC
|
||||
throw CancelDecoderException.generate(null);
|
||||
}
|
||||
|
||||
final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(in);
|
||||
final ByteBuf transformedBuf = ByteBufUtil.copy(ctx.alloc(), in);
|
||||
try {
|
||||
if (this.connection.shouldTransformPacket()) {
|
||||
this.connection.transformIncoming(transformedBuf, CancelDecoderException::generate);
|
||||
|
||||
@@ -90,18 +90,19 @@ import com.viaversion.viaversion.protocols.v1_8to1_9.Protocol1_8To1_9;
|
||||
import com.viaversion.viaversion.protocols.v1_9_1to1_9_3.Protocol1_9_1To1_9_3;
|
||||
import com.viaversion.viaversion.protocols.v1_9_3to1_10.Protocol1_9_3To1_10;
|
||||
import com.viaversion.viaversion.protocols.v1_9to1_9_1.Protocol1_9To1_9_1;
|
||||
import com.viaversion.viaversion.protocols.v26_1to26_2.Protocol26_1To26_2;
|
||||
import com.viaversion.viaversion.util.MathUtil;
|
||||
import com.viaversion.viaversion.util.Pair;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectSortedMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -219,6 +220,7 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
registerProtocol(new Protocol1_21_9To1_21_11(), ProtocolVersion.v1_21_11, ProtocolVersion.v1_21_9);
|
||||
|
||||
registerProtocol(new Protocol1_21_11To26_1(), ProtocolVersion.v26_1, ProtocolVersion.v1_21_11);
|
||||
registerProtocol(new Protocol26_1To26_2(), ProtocolVersion.v26_2, ProtocolVersion.v26_1);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -321,17 +323,13 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
}
|
||||
|
||||
// Calculate path
|
||||
Object2ObjectSortedMap<ProtocolVersion, Protocol> outputPath = getProtocolPath(new Object2ObjectLinkedOpenHashMap<>(), clientVersion, serverVersion);
|
||||
if (outputPath == null) {
|
||||
List<ProtocolPathEntry> path = calculateProtocolPath(clientVersion, serverVersion);
|
||||
if (path == null) {
|
||||
// Also cache that there is no path
|
||||
pathCache.put(protocolKey, List.of());
|
||||
return null;
|
||||
}
|
||||
|
||||
List<ProtocolPathEntry> path = new ArrayList<>(outputPath.size());
|
||||
for (Map.Entry<ProtocolVersion, Protocol> entry : outputPath.entrySet()) {
|
||||
path.add(new ProtocolPathEntryImpl(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
pathCache.put(protocolKey, path);
|
||||
return path;
|
||||
}
|
||||
@@ -347,56 +345,70 @@ public class ProtocolManagerImpl implements ProtocolManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a path to get from an input protocol to the server's protocol.
|
||||
* Calculates the shortest path to get from an input protocol to the server's protocol using a breadth-first search.
|
||||
*
|
||||
* @param current current items in the path
|
||||
* @param clientVersion current input version
|
||||
* @param clientVersion input version
|
||||
* @param serverVersion desired output version
|
||||
* @return path that has been generated, null if failed
|
||||
* @return shortest path to the server version, or null if none exists
|
||||
*/
|
||||
private @Nullable Object2ObjectSortedMap<ProtocolVersion, Protocol> getProtocolPath(Object2ObjectSortedMap<ProtocolVersion, Protocol> current, ProtocolVersion clientVersion, ProtocolVersion serverVersion) {
|
||||
if (current.size() > maxProtocolPathSize) return null; // Fail-safe, protocol too complicated.
|
||||
private @Nullable List<ProtocolPathEntry> calculateProtocolPath(ProtocolVersion clientVersion, ProtocolVersion serverVersion) {
|
||||
Set<ProtocolVersion> visited = new HashSet<>();
|
||||
visited.add(clientVersion);
|
||||
|
||||
// First, check if there is any protocols for this
|
||||
Object2ObjectMap<ProtocolVersion, Protocol> toServerProtocolMap = registryMap.get(clientVersion);
|
||||
if (toServerProtocolMap == null) {
|
||||
return null; // Not supported
|
||||
}
|
||||
Deque<PathNode> queue = new ArrayDeque<>();
|
||||
queue.add(new PathNode(null, clientVersion, null, 0));
|
||||
|
||||
// Next, check if there is a direct, single Protocol path
|
||||
Protocol protocol = toServerProtocolMap.get(serverVersion);
|
||||
if (protocol != null) {
|
||||
current.put(serverVersion, protocol);
|
||||
return current; // Easy solution
|
||||
}
|
||||
PathNode node;
|
||||
while ((node = queue.poll()) != null) {
|
||||
if (node.depth() > maxProtocolPathSize){
|
||||
continue; // Fail-safe, protocol too complicated
|
||||
}
|
||||
|
||||
// There might be a more advanced solution... So we'll see if any of the others can get us there
|
||||
Object2ObjectSortedMap<ProtocolVersion, Protocol> shortest = null;
|
||||
for (Map.Entry<ProtocolVersion, Protocol> entry : toServerProtocolMap.entrySet()) {
|
||||
// Ensure we don't go back to already contained versions
|
||||
ProtocolVersion translatedToVersion = entry.getKey();
|
||||
if (current.containsKey(translatedToVersion)) continue;
|
||||
Object2ObjectMap<ProtocolVersion, Protocol> toServerProtocolMap = registryMap.get(node.version());
|
||||
if (toServerProtocolMap == null) {
|
||||
continue; // Not supported
|
||||
}
|
||||
|
||||
// Check if the new version is farther away than the current client version
|
||||
if (maxPathDeltaIncrease != -1 && translatedToVersion.getVersionType() == clientVersion.getVersionType()) {
|
||||
final int delta = Math.abs(serverVersion.getVersion() - translatedToVersion.getVersion()) - Math.abs(serverVersion.getVersion() - clientVersion.getVersion());
|
||||
if (delta > maxPathDeltaIncrease) {
|
||||
// Check if there is a direct protocol path; always preferred over paths through other versions,
|
||||
// even if it first moves farther away from the server version
|
||||
Protocol protocol = toServerProtocolMap.get(serverVersion);
|
||||
if (protocol != null) {
|
||||
return reconstructPath(new PathNode(node, serverVersion, protocol, node.depth() + 1));
|
||||
}
|
||||
|
||||
for (Map.Entry<ProtocolVersion, Protocol> entry : toServerProtocolMap.entrySet()) {
|
||||
// Ensure we don't go back to already visited versions
|
||||
ProtocolVersion translatedToVersion = entry.getKey();
|
||||
if (visited.contains(translatedToVersion)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a copy
|
||||
Object2ObjectSortedMap<ProtocolVersion, Protocol> newCurrent = new Object2ObjectLinkedOpenHashMap<>(current);
|
||||
newCurrent.put(translatedToVersion, entry.getValue());
|
||||
// Check if the new version is farther away than the current client version
|
||||
if (maxPathDeltaIncrease != -1 && translatedToVersion.getVersionType() == node.version().getVersionType()) {
|
||||
final int delta = Math.abs(serverVersion.getVersion() - translatedToVersion.getVersion()) - Math.abs(serverVersion.getVersion() - node.version().getVersion());
|
||||
if (delta > maxPathDeltaIncrease) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate the rest of the protocol starting from translatedToVersion and take the shortest
|
||||
newCurrent = getProtocolPath(newCurrent, translatedToVersion, serverVersion);
|
||||
if (newCurrent != null && (shortest == null || newCurrent.size() < shortest.size())) {
|
||||
shortest = newCurrent;
|
||||
// Go on to the next node
|
||||
visited.add(translatedToVersion);
|
||||
queue.add(new PathNode(node, translatedToVersion, entry.getValue(), node.depth() + 1));
|
||||
}
|
||||
}
|
||||
return null; // No path found
|
||||
}
|
||||
|
||||
return shortest; // null if none found
|
||||
private List<ProtocolPathEntry> reconstructPath(PathNode node) {
|
||||
// Go back the full path again to build the final list
|
||||
final ProtocolPathEntry[] path = new ProtocolPathEntry[node.depth()];
|
||||
for (PathNode current = node; current.protocol() != null; current = current.parent()) {
|
||||
path[current.depth() - 1] = new ProtocolPathEntryImpl(current.version(), current.protocol());
|
||||
}
|
||||
return List.of(path);
|
||||
}
|
||||
|
||||
private record PathNode(@Nullable PathNode parent, ProtocolVersion version, @Nullable Protocol protocol, int depth) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
@@ -122,6 +122,11 @@ public final class RegistrationBuilder {
|
||||
this.adapter = adapter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param max max version, exclusive
|
||||
* @param action registration
|
||||
* @return self
|
||||
*/
|
||||
public TypedRangesBuilder<CU, SU, R> to(final ProtocolVersion max, final TypedRegistrationAction<CU, SU, R> action) {
|
||||
to(max, wrapAction(action));
|
||||
return this;
|
||||
|
||||
+1
-1
@@ -78,7 +78,7 @@ public final class SharedRegistrations {
|
||||
}
|
||||
|
||||
for (final VersionedTemplateGroup group : groups) {
|
||||
if (group.min().newerThan(version)) { // TODO binary search?
|
||||
if (group.min().newerThan(version)) {
|
||||
// Sorted by the minimum, so we can stop early if we're out
|
||||
break;
|
||||
}
|
||||
|
||||
+2
-2
@@ -38,7 +38,7 @@ final class BlockRegistrations {
|
||||
ctx.clientbound(ClientboundPackets1_13.CHUNK_BLOCKS_UPDATE, br::registerChunkBlocksUpdate);
|
||||
}
|
||||
|
||||
private static <CU extends ClientboundPacketType> void common1_13(RegistrationContext<CU, ?> ctx, BlockRewriter<CU> br) {
|
||||
private static <CU extends ClientboundPacketType> void common1_13(final RegistrationContext<CU, ?> ctx, final BlockRewriter<CU> br) {
|
||||
ctx.clientbound(ClientboundPackets1_13.BLOCK_EVENT, br::registerBlockEvent);
|
||||
ctx.clientbound(ClientboundPackets1_13.BLOCK_UPDATE, br::registerBlockUpdate);
|
||||
ctx.clientbound(ClientboundPackets1_13.LEVEL_EVENT, br::registerLevelEvent1_13);
|
||||
@@ -70,7 +70,7 @@ final class BlockRegistrations {
|
||||
ctx.clientbound(ClientboundPackets1_19.SECTION_BLOCKS_UPDATE, br::registerSectionBlocksUpdate);
|
||||
}
|
||||
|
||||
private static <CU extends ClientboundPacketType> void common1_19(RegistrationContext<CU, ?> ctx, BlockRewriter<CU> br) {
|
||||
private static <CU extends ClientboundPacketType> void common1_19(final RegistrationContext<CU, ?> ctx, final BlockRewriter<CU> br) {
|
||||
ctx.clientbound(ClientboundPackets1_19.BLOCK_EVENT, br::registerBlockEvent);
|
||||
ctx.clientbound(ClientboundPackets1_19.BLOCK_UPDATE, br::registerBlockUpdate);
|
||||
ctx.clientbound(ClientboundPackets1_19.LEVEL_CHUNK_WITH_LIGHT, br::registerLevelChunk1_18);
|
||||
|
||||
+2
-1
@@ -86,7 +86,8 @@ public final class DefaultRegistrations {
|
||||
.to(ProtocolVersion.v1_21_5, TextComponentRegistrations::registerComponents1_21_4)
|
||||
)
|
||||
.ranges(TextComponentRegistrations::nbtText, ProtocolVersion.v1_21_5, b -> b
|
||||
.since(TextComponentRegistrations::registerComponents1_21_5)
|
||||
.to(ProtocolVersion.v26_2, TextComponentRegistrations::registerComponents1_21_5)
|
||||
.since(TextComponentRegistrations::registerComponents26_2)
|
||||
)
|
||||
|
||||
.ranges(ProtocolVersion.v1_17_1, sbeps -> sbeps
|
||||
|
||||
+8
@@ -183,6 +183,14 @@ final class TextComponentRegistrations {
|
||||
ctx.clientbound(ClientboundPackets1_21_5.PLAYER_CHAT, cr::registerPlayerChat1_21_5);
|
||||
}
|
||||
|
||||
static <CU extends ClientboundPacketType> void registerComponents26_2(final RegistrationContext<CU, ?> ctx, final NBTComponentRewriter<CU> cr) {
|
||||
common1_20_5(ctx, cr);
|
||||
ctx.clientbound(ClientboundPackets1_21_5.SET_PLAYER_TEAM, cr::registerSetPlayerTeam26_2);
|
||||
ctx.clientbound(ClientboundPackets1_21_5.DISGUISED_CHAT, cr::registerDisguisedChat);
|
||||
ctx.clientbound(ClientboundPackets1_21_5.PLAYER_INFO_UPDATE, cr::registerPlayerInfoUpdate1_21_4);
|
||||
ctx.clientbound(ClientboundPackets1_21_5.PLAYER_CHAT, cr::registerPlayerChat1_21_5);
|
||||
}
|
||||
|
||||
static @Nullable <CU extends ClientboundPacketType, SU extends ServerboundPacketType> ComponentRewriterBase<CU> text(final RegistrationContext<CU, SU> ctx) {
|
||||
return (ComponentRewriterBase<CU>) ctx.protocol().getComponentRewriter();
|
||||
}
|
||||
|
||||
+8
@@ -30,12 +30,14 @@ import com.viaversion.viaversion.api.type.types.version.Types1_20_5;
|
||||
import com.viaversion.viaversion.api.type.types.version.VersionedTypes;
|
||||
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPacket26_1;
|
||||
import com.viaversion.viaversion.protocols.v1_21_11to26_1.packet.ClientboundPackets26_1;
|
||||
import com.viaversion.viaversion.protocols.v1_21_4to1_21_5.rewriter.RecipeDisplayRewriter1_21_5;
|
||||
import com.viaversion.viaversion.protocols.v1_21_5to1_21_6.packet.ServerboundPackets1_21_6;
|
||||
import com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ClientboundConfigurationPackets1_21_9;
|
||||
import com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundConfigurationPackets1_21_9;
|
||||
import com.viaversion.viaversion.protocols.v1_21_7to1_21_9.packet.ServerboundPacket1_21_9;
|
||||
import com.viaversion.viaversion.rewriter.BlockRewriter;
|
||||
import com.viaversion.viaversion.rewriter.ParticleRewriter;
|
||||
import com.viaversion.viaversion.rewriter.RecipeDisplayRewriter;
|
||||
import com.viaversion.viaversion.rewriter.RegistryDataRewriter;
|
||||
import com.viaversion.viaversion.rewriter.TagRewriter;
|
||||
import com.viaversion.viaversion.rewriter.block.BlockRewriter1_21_5;
|
||||
@@ -58,6 +60,7 @@ final class Protocol98_1To99_1 extends AbstractProtocol<ClientboundPacket26_1, C
|
||||
private final ParticleRewriter<ClientboundPacket26_1> particleRewriter = new ParticleRewriter<>(this);
|
||||
private final TagRewriter<ClientboundPacket26_1> tagRewriter = new TagRewriter<>(this);
|
||||
private final NBTComponentRewriter<ClientboundPacket26_1> componentRewriter = new ComponentRewriter99_1(this);
|
||||
private final RecipeDisplayRewriter<ClientboundPacket26_1> recipeewriter = new RecipeDisplayRewriter1_21_5<>(this);
|
||||
private final RegistryDataRewriter registryDataRewriter = new RegistryDataRewriter(this);
|
||||
|
||||
public Protocol98_1To99_1() {
|
||||
@@ -132,6 +135,11 @@ final class Protocol98_1To99_1 extends AbstractProtocol<ClientboundPacket26_1, C
|
||||
return registryDataRewriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecipeDisplayRewriter<ClientboundPacket26_1> getRecipeRewriter() {
|
||||
return recipeewriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleRewriter<ClientboundPacket26_1> getParticleRewriter() {
|
||||
return particleRewriter;
|
||||
|
||||
+4
-3
@@ -18,10 +18,12 @@
|
||||
package com.viaversion.viaversion.protocols.v1_10to1_11.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_11.EntityType;
|
||||
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class EntityTracker1_11 extends EntityTrackerBase {
|
||||
private final IntSet holograms = new IntOpenHashSet();
|
||||
@@ -31,10 +33,9 @@ public class EntityTracker1_11 extends EntityTrackerBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(int entityId) {
|
||||
super.removeEntity(entityId);
|
||||
|
||||
public @Nullable TrackedEntity removeEntity(int entityId) {
|
||||
removeHologram(entityId);
|
||||
return super.removeEntity(entityId);
|
||||
}
|
||||
|
||||
public boolean addHologram(int entId) {
|
||||
|
||||
+15
-31
@@ -17,12 +17,8 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections;
|
||||
|
||||
import com.viaversion.nbt.tag.ByteArrayTag;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntArrayTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.NumberTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import com.viaversion.nbt.tag.Tag;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
@@ -35,12 +31,15 @@ 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.protocol.packet.PacketWrapper;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import com.viaversion.viaversion.api.type.Types;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.providers.BlockConnectionProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.providers.PacketBlockConnectionProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.blockconnections.providers.UserBlockData;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.data.BlockStates1_13;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.packet.ClientboundPackets1_13;
|
||||
import com.viaversion.viaversion.api.data.IdRanges;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
@@ -162,16 +161,14 @@ public final class ConnectionData {
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
if (!Via.getConfig().isServersideBlockConnections()) {
|
||||
final ProtocolVersion version = Via.getAPI().getServerVersion().lowestSupportedProtocolVersion();
|
||||
if (!Via.getConfig().isServersideBlockConnections() || (version.isKnown() && version.newerThan(ProtocolVersion.v1_12_2))) {
|
||||
return;
|
||||
}
|
||||
|
||||
Via.getPlatform().getLogger().info("Loading block connection mappings ...");
|
||||
ListTag<StringTag> blockStates = MappingDataLoader.INSTANCE.loadNBT("blockstates-1.13.nbt").getListTag("blockstates", StringTag.class);
|
||||
for (int id = 0; id < blockStates.size(); id++) {
|
||||
String key = blockStates.get(id).getValue();
|
||||
KEY_TO_ID.put(key, id);
|
||||
}
|
||||
CompoundTag blockStatesData = MappingDataLoader.INSTANCE.loadNBT("blockstates-1.13.nbt");
|
||||
BlockStates1_13.forEach(blockStatesData, KEY_TO_ID::put);
|
||||
|
||||
connectionHandlerMap = new Int2ObjectOpenHashMap<>(3650);
|
||||
|
||||
@@ -179,41 +176,28 @@ public final class ConnectionData {
|
||||
blockConnectionData = new Int2ObjectOpenHashMap<>(2048);
|
||||
|
||||
CompoundTag data = MappingDataLoader.INSTANCE.loadNBT("blockConnections.nbt");
|
||||
ListTag<CompoundTag> blockConnectionMappings = data.getListTag("data", CompoundTag.class);
|
||||
for (CompoundTag blockTag : blockConnectionMappings) {
|
||||
for (CompoundTag profileTag : data.getListTag("profiles", CompoundTag.class)) {
|
||||
BlockData blockData = new BlockData();
|
||||
for (Entry<String, Tag> entry : blockTag.entrySet()) {
|
||||
for (Entry<String, Tag> entry : profileTag.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (key.equals("id") || key.equals("ids")) {
|
||||
if (key.equals("ids")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
byte connections = ((NumberTag) entry.getValue()).asByte();
|
||||
boolean[] attachingFaces = new boolean[4];
|
||||
ByteArrayTag connections = (ByteArrayTag) entry.getValue();
|
||||
for (byte blockFaceId : connections.getValue()) {
|
||||
attachingFaces[blockFaceId] = true;
|
||||
for (int i = 0; i < attachingFaces.length; i++) {
|
||||
attachingFaces[i] = (connections & (1 << i)) != 0;
|
||||
}
|
||||
|
||||
int connectionTypeId = Integer.parseInt(key);
|
||||
blockData.put(connectionTypeId, attachingFaces);
|
||||
}
|
||||
|
||||
NumberTag idTag = blockTag.getNumberTag("id");
|
||||
if (idTag != null) {
|
||||
blockConnectionData.put(idTag.asInt(), blockData);
|
||||
} else {
|
||||
IntArrayTag idsTag = blockTag.getIntArrayTag("ids");
|
||||
for (int id : idsTag.getValue()) {
|
||||
blockConnectionData.put(id, blockData);
|
||||
}
|
||||
}
|
||||
IdRanges.forEachId(profileTag.getByteArrayTag("ids"), id -> blockConnectionData.put(id, blockData));
|
||||
}
|
||||
|
||||
IntArrayTag occludingStatesArray = data.getIntArrayTag("occluding-states");
|
||||
for (final int blockStateId : occludingStatesArray.getValue()) {
|
||||
OCCLUDING_STATES.add(blockStateId);
|
||||
}
|
||||
IdRanges.forEachId(data.getByteArrayTag("occluding-states"), OCCLUDING_STATES::add);
|
||||
}
|
||||
|
||||
List<ConnectorInitAction> initActions = new ArrayList<>();
|
||||
|
||||
+6
-5
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v1_12_2to1_13.data;
|
||||
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.viaversion.viaversion.protocols.v1_12_2to1_13.Protocol1_12_2To1_13;
|
||||
import com.viaversion.viaversion.util.GsonUtil;
|
||||
@@ -26,12 +25,13 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class BlockIdData {
|
||||
public static final String[] PREVIOUS = new String[0];
|
||||
public static final Map<String, String[]> blockIdMapping = new HashMap<>();
|
||||
public static final Map<String, String[]> fallbackReverseMapping = new HashMap<>();
|
||||
public static final Int2ObjectMap<String> numberIdToString = new Int2ObjectOpenHashMap<>();
|
||||
@@ -46,13 +46,14 @@ public class BlockIdData {
|
||||
new TypeToken<Map<String, String[]>>() {
|
||||
}.getType());
|
||||
blockIdMapping.putAll(map);
|
||||
|
||||
Map<String, List<String>> reverse = new HashMap<>();
|
||||
for (Map.Entry<String, String[]> entry : blockIdMapping.entrySet()) {
|
||||
for (String val : entry.getValue()) {
|
||||
String[] previous = fallbackReverseMapping.get(val);
|
||||
if (previous == null) previous = PREVIOUS;
|
||||
fallbackReverseMapping.put(val, ObjectArrays.concat(previous, entry.getKey()));
|
||||
reverse.computeIfAbsent(val, k -> new ArrayList<>()).add(entry.getKey());
|
||||
}
|
||||
}
|
||||
reverse.forEach((val, keys) -> fallbackReverseMapping.put(val, keys.toArray(new String[0])));
|
||||
} catch (IOException e) {
|
||||
Protocol1_12_2To1_13.LOGGER.log(Level.SEVERE, "Failed to load block id mappings", e);
|
||||
}
|
||||
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-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.viaversion.protocols.v1_12_2to1_13.data;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntArrayTag;
|
||||
import com.viaversion.nbt.tag.ListTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.ObjIntConsumer;
|
||||
|
||||
public final class BlockStates1_13 {
|
||||
|
||||
/**
|
||||
* Visits every block state key (e.g. {@code chest[facing=north,type=single,waterlogged=true]})
|
||||
* together with its block state id, decoded from the given blockstates-1.13.nbt data.
|
||||
*
|
||||
* @param data root tag of blockstates-1.13.nbt
|
||||
* @param consumer consumer called with each block state key and its id, in id order
|
||||
*/
|
||||
public static void forEach(CompoundTag data, ObjIntConsumer<String> consumer) {
|
||||
ListTag<CompoundTag> propertyTable = data.getListTag("properties", CompoundTag.class);
|
||||
int blockStateId = 0;
|
||||
for (CompoundTag blockTag : data.getListTag("blockstates", CompoundTag.class)) {
|
||||
String name = blockTag.getString("name");
|
||||
IntArrayTag propertiesTag = blockTag.getIntArrayTag("properties");
|
||||
if (propertiesTag == null) {
|
||||
consumer.accept(name, blockStateId++);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Construct the full string from the properties
|
||||
List<String> states = List.of("");
|
||||
for (int propertyIndex : propertiesTag.getValue()) {
|
||||
CompoundTag property = propertyTable.get(propertyIndex);
|
||||
String propertyName = property.getString("name");
|
||||
List<String> combined = new ArrayList<>();
|
||||
for (String state : states) {
|
||||
for (StringTag value : property.getListTag("values", StringTag.class)) {
|
||||
String extendedState = state.isEmpty()
|
||||
? propertyName + "=" + value.getValue()
|
||||
: state + "," + propertyName + "=" + value.getValue();
|
||||
combined.add(extendedState);
|
||||
}
|
||||
}
|
||||
states = combined;
|
||||
}
|
||||
for (String state : states) {
|
||||
consumer.accept(name + "[" + state + "]", blockStateId++);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-21
@@ -22,7 +22,6 @@ import com.google.common.collect.HashBiMap;
|
||||
import com.google.common.io.CharStreams;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntArrayTag;
|
||||
import com.viaversion.nbt.tag.StringTag;
|
||||
@@ -33,7 +32,6 @@ import com.viaversion.viaversion.api.data.Int2IntMapBiMappings;
|
||||
import com.viaversion.viaversion.api.data.MappingDataBase;
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader;
|
||||
import com.viaversion.viaversion.api.data.Mappings;
|
||||
import com.viaversion.viaversion.util.GsonUtil;
|
||||
import com.viaversion.viaversion.util.Int2IntBiHashMap;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import java.io.IOException;
|
||||
@@ -94,37 +92,46 @@ public class MappingData1_13 extends MappingDataBase {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, String> translationMappingData = GsonUtil.getGson().fromJson(
|
||||
new InputStreamReader(MappingData1_13.class.getClassLoader().getResourceAsStream("assets/viaversion/data/mapping-lang-1.12-1.13.json")),
|
||||
new TypeToken<Map<String, String>>() {
|
||||
}.getType());
|
||||
|
||||
String[] unmappedTranslationLines;
|
||||
String[] translationLines;
|
||||
try (Reader reader = new InputStreamReader(MappingData1_13.class.getClassLoader()
|
||||
.getResourceAsStream("assets/viaversion/data/en_US.properties"), StandardCharsets.UTF_8)) {
|
||||
unmappedTranslationLines = CharStreams.toString(reader).split("\n");
|
||||
.getResourceAsStream("assets/viaversion/data/mapping-lang-1.12-1.13.txt"), StandardCharsets.UTF_8)) {
|
||||
translationLines = CharStreams.toString(reader).split("\n");
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
for (String line : unmappedTranslationLines) {
|
||||
if (line.isEmpty()) {
|
||||
// 1.12 translations with number placeholders already replaced and their mapped 1.13 keys inlined:
|
||||
// key=translation for keys unchanged in 1.13,
|
||||
// old>new=translation for renamed keys with placeholder changes,
|
||||
// -key=translation for keys removed in 1.13 (= translations are inlined into components instead)
|
||||
for (String line : translationLines) {
|
||||
if (line.endsWith("\r")) {
|
||||
line = line.substring(0, line.length() - 1);
|
||||
}
|
||||
if (line.isEmpty() || line.charAt(0) == '#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] keyAndTranslation = line.split("=", 2);
|
||||
if (keyAndTranslation.length != 2) {
|
||||
int keySeparator = line.indexOf('=');
|
||||
if (keySeparator == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = keyAndTranslation[0];
|
||||
String translation = keyAndTranslation[1].replaceAll("%(\\d\\$)?d", "%$1s").trim();
|
||||
mojangTranslation.put(key, translation);
|
||||
String translation = line.substring(keySeparator + 1);
|
||||
if (line.charAt(0) == '-') {
|
||||
mojangTranslation.put(line.substring(1, keySeparator), translation);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Null values in the file mean the key did not change AND the translation has the same amount of placeholders still
|
||||
if (translationMappingData.containsKey(key)) {
|
||||
String mappedKey = translationMappingData.get(key);
|
||||
translateMapping.put(key, mappedKey != null ? mappedKey : key);
|
||||
int renameSeparator = line.indexOf('>');
|
||||
if (renameSeparator == -1 || renameSeparator > keySeparator) {
|
||||
String key = line.substring(0, keySeparator);
|
||||
mojangTranslation.put(key, translation);
|
||||
translateMapping.put(key, key);
|
||||
} else {
|
||||
String key = line.substring(0, renameSeparator);
|
||||
mojangTranslation.put(key, translation);
|
||||
translateMapping.put(key, line.substring(renameSeparator + 1, keySeparator));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+77
-7
@@ -17,30 +17,100 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v1_12_2to1_13.data;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.viaversion.viaversion.api.minecraft.item.DataItem;
|
||||
import com.viaversion.viaversion.util.GsonUtil;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class RecipeData {
|
||||
public static Map<String, Recipe> recipes;
|
||||
|
||||
public static void init() {
|
||||
JsonObject object;
|
||||
InputStream stream = MappingData1_13.class.getClassLoader()
|
||||
.getResourceAsStream("assets/viaversion/data/itemrecipes1_12_2to1_13.json");
|
||||
try (InputStreamReader reader = new InputStreamReader(stream)) {
|
||||
recipes = GsonUtil.getGson().fromJson(
|
||||
reader,
|
||||
new TypeToken<Map<String, Recipe>>() {
|
||||
}.getType()
|
||||
);
|
||||
object = GsonUtil.getGson().fromJson(reader, JsonObject.class);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
// Ignored
|
||||
|
||||
recipes = new LinkedHashMap<>();
|
||||
|
||||
// Shaped and shapeless sections are keyed by their legacy id
|
||||
for (Map.Entry<String, JsonElement> entry : object.getAsJsonObject("shapeless").entrySet()) {
|
||||
JsonObject recipe = entry.getValue().getAsJsonObject();
|
||||
DataItem[][] ingredients = ingredients(recipe.getAsJsonArray("ingredients"));
|
||||
recipes.put("viaversion:legacy/" + entry.getKey(), new Recipe("crafting_shapeless", group(recipe), 0, 0, 0, 0, null, ingredients, result(recipe)));
|
||||
}
|
||||
|
||||
// Ingredients are stored as grid rows, so the width and height are implicit
|
||||
for (Map.Entry<String, JsonElement> entry : object.getAsJsonObject("shaped").entrySet()) {
|
||||
JsonObject recipe = entry.getValue().getAsJsonObject();
|
||||
JsonArray grid = recipe.getAsJsonArray("grid");
|
||||
int height = grid.size();
|
||||
int width = grid.get(0).getAsJsonArray().size();
|
||||
DataItem[][] ingredients = new DataItem[width * height][];
|
||||
int slot = 0;
|
||||
for (JsonElement row : grid) {
|
||||
for (JsonElement element : row.getAsJsonArray()) {
|
||||
ingredients[slot++] = ingredient(element);
|
||||
}
|
||||
}
|
||||
recipes.put("viaversion:legacy/" + entry.getKey(), new Recipe("crafting_shaped", group(recipe), width, height, 0, 0, null, ingredients, result(recipe)));
|
||||
}
|
||||
|
||||
for (Map.Entry<String, JsonElement> entry : object.getAsJsonObject("smelting").entrySet()) {
|
||||
JsonObject recipe = entry.getValue().getAsJsonObject();
|
||||
float experience = recipe.get("experience").getAsFloat();
|
||||
recipes.put(entry.getKey(), new Recipe("smelting", group(recipe), 0, 0, experience, 200, ingredient(recipe.get("ingredient")), null, result(recipe)));
|
||||
}
|
||||
|
||||
// Name to type suffix
|
||||
for (Map.Entry<String, JsonElement> entry : object.getAsJsonObject("special").entrySet()) {
|
||||
recipes.put(entry.getKey(), new Recipe("crafting_special_" + entry.getValue().getAsString(), "", 0, 0, 0, 0, null, null, null));
|
||||
}
|
||||
}
|
||||
|
||||
private static String group(JsonObject recipe) {
|
||||
JsonElement group = recipe.get("group");
|
||||
return group != null ? group.getAsString() : "";
|
||||
}
|
||||
|
||||
private static DataItem result(JsonObject recipe) {
|
||||
JsonElement count = recipe.get("count");
|
||||
return new DataItem(recipe.get("result").getAsInt(), count != null ? count.getAsByte() : (byte) 1, null);
|
||||
}
|
||||
|
||||
// null for an empty slot, a single item id, or an array of item id choices
|
||||
private static DataItem[] ingredient(JsonElement element) {
|
||||
if (element.isJsonNull()) {
|
||||
return new DataItem[0];
|
||||
}
|
||||
if (element.isJsonArray()) {
|
||||
JsonArray choices = element.getAsJsonArray();
|
||||
DataItem[] items = new DataItem[choices.size()];
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
items[i] = new DataItem(choices.get(i).getAsInt(), (byte) 1, null);
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
return new DataItem[]{new DataItem(element.getAsInt(), (byte) 1, null)};
|
||||
}
|
||||
|
||||
private static DataItem[][] ingredients(JsonArray slots) {
|
||||
DataItem[][] items = new DataItem[slots.size()][];
|
||||
for (int i = 0; i < items.length; i++) {
|
||||
items[i] = ingredient(slots.get(i));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public record Recipe(String type, String group, int width, int height, float experience, int cookingTime,
|
||||
|
||||
+58
-6
@@ -51,6 +51,7 @@ import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
|
||||
public class WorldPacketRewriter1_13 {
|
||||
private static final IntSet VALID_BIOMES = new IntOpenHashSet(70);
|
||||
@@ -512,19 +513,42 @@ public class WorldPacketRewriter1_13 {
|
||||
float speed = wrapper.get(Types.FLOAT, 6);
|
||||
// Only handle for count = 0
|
||||
if (count == 0) {
|
||||
// Change count and speed
|
||||
wrapper.set(Types.INT, 1, 1);
|
||||
wrapper.set(Types.FLOAT, 6, 0f);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
//RGB values are represented by the X/Y/Z offset
|
||||
float colorValue = wrapper.get(Types.FLOAT, i + 3) * speed;
|
||||
if (colorValue == 0 && i == 0) {
|
||||
// https://minecraft.gamepedia.com/User:Alphappy/reddust
|
||||
colorValue = 1;
|
||||
}
|
||||
// RGB values are represented by the X/Y/Z offset
|
||||
float offset = wrapper.get(Types.FLOAT, i + 3);
|
||||
float colorValue = redstoneDustColor(speed, offset, i == 0);
|
||||
particle.<Float>getArgument(i).setValue(colorValue);
|
||||
wrapper.set(Types.FLOAT, i + 3, 0f);
|
||||
}
|
||||
} else if (Via.getConfig().isMultiReddustColorFix()) {
|
||||
wrapper.cancel();
|
||||
|
||||
final boolean longDistance = wrapper.get(Types.BOOLEAN, 0);
|
||||
final float x = wrapper.get(Types.FLOAT, 0);
|
||||
final float y = wrapper.get(Types.FLOAT, 1);
|
||||
final float z = wrapper.get(Types.FLOAT, 2);
|
||||
final float offsetX = wrapper.get(Types.FLOAT, 3);
|
||||
final float offsetY = wrapper.get(Types.FLOAT, 4);
|
||||
final float offsetZ = wrapper.get(Types.FLOAT, 5);
|
||||
final ThreadLocalRandom random = ThreadLocalRandom.current();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final float particleX = (float) (x + random.nextGaussian() * offsetX);
|
||||
final float particleY = (float) (y + random.nextGaussian() * offsetY);
|
||||
final float particleZ = (float) (z + random.nextGaussian() * offsetZ);
|
||||
sendRedstoneParticle(wrapper,
|
||||
longDistance,
|
||||
particleX,
|
||||
particleY,
|
||||
particleZ,
|
||||
redstoneDustColor((float) random.nextGaussian(), speed, true),
|
||||
redstoneDustColor((float) random.nextGaussian(), speed, false),
|
||||
redstoneDustColor((float) random.nextGaussian(), speed, false));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -563,6 +587,34 @@ public class WorldPacketRewriter1_13 {
|
||||
|
||||
}
|
||||
|
||||
private static float redstoneDustColor(float speed, float offset, boolean red) {
|
||||
// Vanilla 1.12 substitutes red when the raw red value is zero
|
||||
float color = speed * offset;
|
||||
if (red && color == 0F) {
|
||||
return 1F;
|
||||
}
|
||||
return ((int) (color * 255F) & 255) / 255F;
|
||||
}
|
||||
|
||||
private static void sendRedstoneParticle(PacketWrapper wrapper, boolean longDistance, float x, float y, float z, float red, float green, float blue) {
|
||||
final PacketWrapper particlePacket = wrapper.create(ClientboundPackets1_13.LEVEL_PARTICLES);
|
||||
particlePacket.write(Types.INT, 11);
|
||||
particlePacket.write(Types.BOOLEAN, longDistance);
|
||||
particlePacket.write(Types.FLOAT, x);
|
||||
particlePacket.write(Types.FLOAT, y);
|
||||
particlePacket.write(Types.FLOAT, z);
|
||||
particlePacket.write(Types.FLOAT, 0F);
|
||||
particlePacket.write(Types.FLOAT, 0F);
|
||||
particlePacket.write(Types.FLOAT, 0F);
|
||||
particlePacket.write(Types.FLOAT, 0F);
|
||||
particlePacket.write(Types.INT, 0);
|
||||
particlePacket.write(Types.FLOAT, red);
|
||||
particlePacket.write(Types.FLOAT, green);
|
||||
particlePacket.write(Types.FLOAT, blue);
|
||||
particlePacket.write(Types.FLOAT, 1F);
|
||||
particlePacket.send(Protocol1_12_2To1_13.class);
|
||||
}
|
||||
|
||||
public static int toNewId(int oldId) {
|
||||
if (oldId < 0) {
|
||||
oldId = 0; // Some plugins use negative numbers to clear blocks, remap them to air.
|
||||
|
||||
+3
-6
@@ -18,11 +18,10 @@
|
||||
package com.viaversion.viaversion.protocols.v1_13_2to1_14.data;
|
||||
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.nbt.tag.IntArrayTag;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.data.MappingDataBase;
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import com.viaversion.viaversion.api.data.IdRanges;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
public class MappingData1_14 extends MappingDataBase {
|
||||
@@ -36,12 +35,10 @@ public class MappingData1_14 extends MappingDataBase {
|
||||
@Override
|
||||
public void loadExtras(final CompoundTag data) {
|
||||
final CompoundTag heightmap = MappingDataLoader.INSTANCE.loadNBT("heightmap-1.14.nbt");
|
||||
final IntArrayTag motionBlocking = heightmap.getIntArrayTag("motionBlocking");
|
||||
this.motionBlocking = new IntOpenHashSet(motionBlocking.getValue());
|
||||
this.motionBlocking = IdRanges.decode(heightmap.getByteArrayTag("motionBlocking"));
|
||||
|
||||
if (Via.getConfig().isNonFullBlockLightFix()) {
|
||||
final IntArrayTag nonFullBlocks = heightmap.getIntArrayTag("nonFullBlocks");
|
||||
this.nonFullBlocks = new IntOpenHashSet(nonFullBlocks.getValue());
|
||||
this.nonFullBlocks = IdRanges.decode(heightmap.getByteArrayTag("nonFullBlocks"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -18,10 +18,12 @@
|
||||
package com.viaversion.viaversion.protocols.v1_13_2to1_14.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.entity.TrackedEntity;
|
||||
import com.viaversion.viaversion.api.minecraft.entities.EntityTypes1_14;
|
||||
import com.viaversion.viaversion.data.entity.EntityTrackerBase;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
public class EntityTracker1_14 extends EntityTrackerBase {
|
||||
private final Int2ObjectMap<Byte> insentientData = new Int2ObjectOpenHashMap<>();
|
||||
@@ -38,12 +40,11 @@ public class EntityTracker1_14 extends EntityTrackerBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeEntity(int entityId) {
|
||||
super.removeEntity(entityId);
|
||||
|
||||
public @Nullable TrackedEntity removeEntity(int entityId) {
|
||||
insentientData.remove(entityId);
|
||||
sleepingAndRiptideData.remove(entityId);
|
||||
playerEntityFlags.remove(entityId);
|
||||
return super.removeEntity(entityId);
|
||||
}
|
||||
|
||||
public byte getInsentientData(int entity) {
|
||||
|
||||
+2
-1
@@ -37,6 +37,7 @@ import com.viaversion.viaversion.protocols.v1_15_2to1_16.packet.ServerboundPacke
|
||||
import com.viaversion.viaversion.protocols.v1_15_2to1_16.storage.InventoryTracker1_16;
|
||||
import com.viaversion.viaversion.rewriter.EntityRewriter;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.UUIDUtil;
|
||||
import java.util.UUID;
|
||||
|
||||
public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1_15, Protocol1_15_2To1_16> {
|
||||
@@ -93,7 +94,7 @@ public class EntityPacketRewriter1_16 extends EntityRewriter<ClientboundPackets1
|
||||
|
||||
wrapper.user().getEntityTracker(Protocol1_15_2To1_16.class).addEntity(entityId, EntityTypes1_16.LIGHTNING_BOLT);
|
||||
|
||||
wrapper.write(Types.UUID, UUID.randomUUID()); // uuid
|
||||
wrapper.write(Types.UUID, UUIDUtil.randomUUID()); // uuid
|
||||
wrapper.write(Types.VAR_INT, EntityTypes1_16.LIGHTNING_BOLT.getId()); // entity type
|
||||
|
||||
wrapper.passthrough(Types.DOUBLE); // x
|
||||
|
||||
+8
-2
@@ -36,6 +36,7 @@ import com.viaversion.viaversion.protocols.v1_17_1to1_18.rewriter.ItemPacketRewr
|
||||
import com.viaversion.viaversion.protocols.v1_17_1to1_18.rewriter.WorldPacketRewriter1_18;
|
||||
import com.viaversion.viaversion.protocols.v1_17_1to1_18.storage.ChunkLightStorage;
|
||||
import com.viaversion.viaversion.protocols.v1_17to1_17_1.packet.ClientboundPackets1_17_1;
|
||||
import com.viaversion.viaversion.rewriter.BlockRewriter;
|
||||
import com.viaversion.viaversion.rewriter.ParticleRewriter;
|
||||
import com.viaversion.viaversion.rewriter.TagRewriter;
|
||||
import com.viaversion.viaversion.util.ProtocolLogger;
|
||||
@@ -48,6 +49,7 @@ public final class Protocol1_17_1To1_18 extends AbstractProtocol<ClientboundPack
|
||||
private final ItemPacketRewriter1_18 itemRewriter = new ItemPacketRewriter1_18(this);
|
||||
private final ParticleRewriter<ClientboundPackets1_17_1> particleRewriter = new ParticleRewriter<>(this);
|
||||
private final ComponentRewriter1_18 componentRewriter = new ComponentRewriter1_18(this);
|
||||
private final BlockRewriter<ClientboundPackets1_17_1> blockRewriter = BlockRewriter.for1_14(this);
|
||||
private final TagRewriter<ClientboundPackets1_17_1> tagRewriter = new TagRewriter<>(this);
|
||||
|
||||
public Protocol1_17_1To1_18() {
|
||||
@@ -56,8 +58,7 @@ public final class Protocol1_17_1To1_18 extends AbstractProtocol<ClientboundPack
|
||||
|
||||
@Override
|
||||
protected void registerPackets() {
|
||||
entityRewriter.register();
|
||||
itemRewriter.register();
|
||||
super.registerPackets();
|
||||
WorldPacketRewriter1_18.register(this);
|
||||
|
||||
registerServerbound(ServerboundPackets1_17.CLIENT_INFORMATION, new PacketHandlers() {
|
||||
@@ -110,6 +111,11 @@ public final class Protocol1_17_1To1_18 extends AbstractProtocol<ClientboundPack
|
||||
return itemRewriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockRewriter<ClientboundPackets1_17_1> getBlockRewriter() {
|
||||
return blockRewriter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParticleRewriter<ClientboundPackets1_17_1> getParticleRewriter() {
|
||||
return particleRewriter;
|
||||
|
||||
+15
@@ -45,6 +45,7 @@ import com.viaversion.viaversion.protocols.v1_18_2to1_19.rewriter.EntityPacketRe
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.rewriter.ItemPacketRewriter1_19;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.rewriter.WorldPacketRewriter1_19;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.storage.DimensionRegistryStorage;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.storage.LoomStorage;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.storage.NonceStorage1_19;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.storage.SequenceStorage;
|
||||
import com.viaversion.viaversion.rewriter.BlockRewriter;
|
||||
@@ -341,6 +342,19 @@ public final class Protocol1_18_2To1_19 extends AbstractProtocol<ClientboundPack
|
||||
tagRewriter.addEmptyTag(RegistryType.ENTITY, "minecraft:frog_food");
|
||||
tagRewriter.addEmptyTags(RegistryType.GAME_EVENT, "minecraft:allay_can_listen", "minecraft:shrieker_can_listen", "minecraft:warden_can_listen");
|
||||
|
||||
// Selectable loom patterns. 0 is the base pattern and the last 6 have pattern items
|
||||
final int[] noItemRequiredPatterns = new int[34];
|
||||
for (int i = 0; i < noItemRequiredPatterns.length; i++) {
|
||||
noItemRequiredPatterns[i] = i + 1;
|
||||
}
|
||||
tagRewriter.addTagRaw(RegistryType.BANNER_PATTERN, "minecraft:no_item_required", noItemRequiredPatterns);
|
||||
tagRewriter.addTagRaw(RegistryType.BANNER_PATTERN, "minecraft:pattern_item/globe", 35);
|
||||
tagRewriter.addTagRaw(RegistryType.BANNER_PATTERN, "minecraft:pattern_item/creeper", 36);
|
||||
tagRewriter.addTagRaw(RegistryType.BANNER_PATTERN, "minecraft:pattern_item/skull", 37);
|
||||
tagRewriter.addTagRaw(RegistryType.BANNER_PATTERN, "minecraft:pattern_item/flower", 38);
|
||||
tagRewriter.addTagRaw(RegistryType.BANNER_PATTERN, "minecraft:pattern_item/mojang", 39);
|
||||
tagRewriter.addTagRaw(RegistryType.BANNER_PATTERN, "minecraft:pattern_item/piglin", 40);
|
||||
|
||||
super.onMappingDataLoaded();
|
||||
}
|
||||
|
||||
@@ -355,6 +369,7 @@ public final class Protocol1_18_2To1_19 extends AbstractProtocol<ClientboundPack
|
||||
user.put(new DimensionRegistryStorage());
|
||||
}
|
||||
user.put(new SequenceStorage());
|
||||
user.put(new LoomStorage());
|
||||
addEntityTracker(user, new EntityTrackerBase(user, EntityTypes1_19.PLAYER));
|
||||
}
|
||||
|
||||
|
||||
+41
@@ -26,12 +26,16 @@ import com.viaversion.viaversion.protocols.v1_17_1to1_18.packet.ClientboundPacke
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.Protocol1_18_2To1_19;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.packet.ServerboundPackets1_19;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.provider.AckSequenceProvider;
|
||||
import com.viaversion.viaversion.protocols.v1_18_2to1_19.storage.LoomStorage;
|
||||
import com.viaversion.viaversion.rewriter.ItemRewriter;
|
||||
import com.viaversion.viaversion.rewriter.RecipeRewriter;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
|
||||
public final class ItemPacketRewriter1_19 extends ItemRewriter<ClientboundPackets1_18, ServerboundPackets1_19, Protocol1_18_2To1_19> {
|
||||
|
||||
private static final int LOOM_MENU_TYPE_ID = 17;
|
||||
private static final int LOOM_SELECTABLE_PATTERNS = 34;
|
||||
|
||||
public ItemPacketRewriter1_19(Protocol1_18_2To1_19 protocol) {
|
||||
super(protocol, Types.ITEM1_13_2, Types.ITEM1_13_2_ARRAY);
|
||||
}
|
||||
@@ -127,6 +131,43 @@ public final class ItemPacketRewriter1_19 extends ItemRewriter<ClientboundPacket
|
||||
}
|
||||
});
|
||||
|
||||
// The loom's selected pattern changed from the raw pattern id to an index of selectable pattern tags
|
||||
protocol.registerClientbound(ClientboundPackets1_18.CONTAINER_CLOSE, wrapper -> wrapper.user().get(LoomStorage.class).reset());
|
||||
protocol.registerServerbound(ServerboundPackets1_19.CONTAINER_CLOSE, wrapper -> wrapper.user().get(LoomStorage.class).reset());
|
||||
protocol.registerServerbound(ServerboundPackets1_19.CONTAINER_BUTTON_CLICK, wrapper -> {
|
||||
final byte containerId = wrapper.passthrough(Types.BYTE);
|
||||
if (containerId != -1 && containerId == wrapper.user().get(LoomStorage.class).containerId()) {
|
||||
final byte patternIndex = wrapper.read(Types.BYTE);
|
||||
wrapper.write(Types.BYTE, (byte) (patternIndex + 1));
|
||||
}
|
||||
});
|
||||
protocol.registerClientbound(ClientboundPackets1_18.OPEN_SCREEN, wrapper -> {
|
||||
final int containerId = wrapper.passthrough(Types.VAR_INT);
|
||||
final int menuTypeId = wrapper.passthrough(Types.VAR_INT);
|
||||
wrapper.user().get(LoomStorage.class).setContainerId(menuTypeId == LOOM_MENU_TYPE_ID ? containerId : -1);
|
||||
});
|
||||
protocol.appendClientbound(ClientboundPackets1_18.CONTAINER_SET_DATA, wrapper -> {
|
||||
wrapper.resetReader();
|
||||
final short containerId = wrapper.passthrough(Types.UNSIGNED_BYTE);
|
||||
if (containerId == -1 || containerId != wrapper.user().get(LoomStorage.class).containerId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
final short property = wrapper.passthrough(Types.SHORT);
|
||||
if (property == 0) { // Selected pattern
|
||||
final short patternId = wrapper.read(Types.SHORT);
|
||||
final short patternIndex;
|
||||
if (patternId == 0) {
|
||||
patternIndex = -1; // No pattern selected
|
||||
} else if (patternId <= LOOM_SELECTABLE_PATTERNS) {
|
||||
patternIndex = (short) (patternId - 1); // Index into the no_item_required tag
|
||||
} else {
|
||||
patternIndex = 0; // Only entry of its pattern_item tag
|
||||
}
|
||||
wrapper.write(Types.SHORT, patternIndex);
|
||||
}
|
||||
});
|
||||
|
||||
new RecipeRewriter<>(protocol).register(ClientboundPackets1_18.UPDATE_RECIPES);
|
||||
}
|
||||
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
||||
* Copyright (C) 2016-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.viaversion.protocols.v1_18_2to1_19.storage;
|
||||
|
||||
import com.viaversion.viaversion.api.connection.StorableObject;
|
||||
|
||||
public final class LoomStorage implements StorableObject {
|
||||
|
||||
private int containerId = -1;
|
||||
|
||||
public int containerId() {
|
||||
return containerId;
|
||||
}
|
||||
|
||||
public void setContainerId(final int containerId) {
|
||||
this.containerId = containerId;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.containerId = -1;
|
||||
}
|
||||
}
|
||||
+53
@@ -23,6 +23,7 @@ import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||
import com.viaversion.viaversion.api.data.MappingData;
|
||||
import com.viaversion.viaversion.api.data.MappingDataBase;
|
||||
import com.viaversion.viaversion.api.minecraft.BlockPosition;
|
||||
import com.viaversion.viaversion.api.minecraft.Holder;
|
||||
import com.viaversion.viaversion.api.minecraft.PlayerMessageSignature;
|
||||
import com.viaversion.viaversion.api.minecraft.RegistryType;
|
||||
@@ -69,6 +70,17 @@ public final class Protocol1_19_1To1_19_3 extends AbstractProtocol<ClientboundPa
|
||||
public static final MappingData MAPPINGS = new MappingDataBase("1.19", "1.19.3");
|
||||
private static final UUID ZERO_UUID = new UUID(0, 0);
|
||||
private static final byte[] EMPTY_BYTES = new byte[0];
|
||||
private static final int SOUND_SOURCE_BLOCKS = 4;
|
||||
private static final int SOUND_IRON_DOOR_CLOSE = 614;
|
||||
private static final int SOUND_IRON_DOOR_OPEN = 615;
|
||||
private static final int SOUND_IRON_TRAPDOOR_CLOSE = 622;
|
||||
private static final int SOUND_IRON_TRAPDOOR_OPEN = 623;
|
||||
private static final int SOUND_FENCE_GATE_CLOSE = 110;
|
||||
private static final int SOUND_FENCE_GATE_OPEN = 111;
|
||||
private static final int SOUND_WOODEN_DOOR_CLOSE = 1345;
|
||||
private static final int SOUND_WOODEN_DOOR_OPEN = 1346;
|
||||
private static final int SOUND_WOODEN_TRAPDOOR_CLOSE = 1347;
|
||||
private static final int SOUND_WOODEN_TRAPDOOR_OPEN = 1348;
|
||||
private final EntityPacketRewriter1_19_3 entityRewriter = new EntityPacketRewriter1_19_3(this);
|
||||
private final ItemPacketRewriter1_19_3 itemRewriter = new ItemPacketRewriter1_19_3(this);
|
||||
private final ParticleRewriter<ClientboundPackets1_19_1> particleRewriter = new ParticleRewriter<>(this);
|
||||
@@ -101,6 +113,31 @@ public final class Protocol1_19_1To1_19_3 extends AbstractProtocol<ClientboundPa
|
||||
final String soundIdentifier = wrapper.read(Types.STRING);
|
||||
wrapper.write(Types.SOUND_EVENT, Holder.of(new SoundEvent(soundIdentifier, null)));
|
||||
});
|
||||
appendClientbound(ClientboundPackets1_19_1.LEVEL_EVENT, wrapper -> {
|
||||
wrapper.resetReader();
|
||||
|
||||
final int eventId = wrapper.read(Types.INT);
|
||||
final int soundId = levelEventSoundId(eventId);
|
||||
if (soundId == -1) {
|
||||
wrapper.write(Types.INT, eventId);
|
||||
return;
|
||||
}
|
||||
|
||||
final BlockPosition position = wrapper.read(Types.BLOCK_POSITION1_14);
|
||||
wrapper.read(Types.INT); // Data
|
||||
wrapper.read(Types.BOOLEAN); // Global event
|
||||
|
||||
// These level events were removed, instead send them as sounds directly
|
||||
wrapper.setPacketType(ClientboundPackets1_19_3.SOUND);
|
||||
wrapper.write(Types.SOUND_EVENT, Holder.of(soundId));
|
||||
wrapper.write(Types.VAR_INT, SOUND_SOURCE_BLOCKS);
|
||||
wrapper.write(Types.INT, position.x() * 8 + 4);
|
||||
wrapper.write(Types.INT, position.y() * 8 + 4);
|
||||
wrapper.write(Types.INT, position.z() * 8 + 4);
|
||||
wrapper.write(Types.FLOAT, 1F); // Volume
|
||||
wrapper.write(Types.FLOAT, ThreadLocalRandom.current().nextFloat() * 0.1F + 0.9F); // Pitch
|
||||
wrapper.write(Types.LONG, ThreadLocalRandom.current().nextLong()); // Seed
|
||||
});
|
||||
|
||||
final CommandRewriter<ClientboundPackets1_19_1> commandRewriter = new CommandRewriter<>(this) {
|
||||
@Override
|
||||
@@ -380,4 +417,20 @@ public final class Protocol1_19_1To1_19_3 extends AbstractProtocol<ClientboundPa
|
||||
public TagRewriter<ClientboundPackets1_19_1> getTagRewriter() {
|
||||
return tagRewriter;
|
||||
}
|
||||
|
||||
private static int levelEventSoundId(final int eventId) {
|
||||
return switch (eventId) {
|
||||
case 1005 -> SOUND_IRON_DOOR_OPEN;
|
||||
case 1006 -> SOUND_WOODEN_DOOR_OPEN;
|
||||
case 1007 -> SOUND_WOODEN_TRAPDOOR_OPEN;
|
||||
case 1008 -> SOUND_FENCE_GATE_OPEN;
|
||||
case 1011 -> SOUND_IRON_DOOR_CLOSE;
|
||||
case 1012 -> SOUND_WOODEN_DOOR_CLOSE;
|
||||
case 1013 -> SOUND_WOODEN_TRAPDOOR_CLOSE;
|
||||
case 1014 -> SOUND_FENCE_GATE_CLOSE;
|
||||
case 1036 -> SOUND_IRON_TRAPDOOR_CLOSE;
|
||||
case 1037 -> SOUND_IRON_TRAPDOOR_OPEN;
|
||||
default -> -1;
|
||||
};
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -196,8 +196,8 @@ public final class EntityPacketRewriter1_19_4 extends EntityRewriter<Clientbound
|
||||
wrapper.setPacketType(ClientboundPackets1_19_4.DAMAGE_EVENT);
|
||||
wrapper.write(Types.VAR_INT, entityId);
|
||||
wrapper.write(Types.VAR_INT, damageType);
|
||||
wrapper.write(Types.VAR_INT, 0); // No source entity
|
||||
wrapper.write(Types.VAR_INT, 0); // No direct source entity
|
||||
wrapper.write(Types.OPTIONAL_VAR_INT, null); // No source entity
|
||||
wrapper.write(Types.OPTIONAL_VAR_INT, null); // No direct source entity
|
||||
wrapper.write(Types.BOOLEAN, false); // No source position
|
||||
return;
|
||||
}
|
||||
|
||||
+2
-1
@@ -17,6 +17,7 @@
|
||||
*/
|
||||
package com.viaversion.viaversion.protocols.v1_20_3to1_20_5.data;
|
||||
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.KeyMappings;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@@ -136,7 +137,7 @@ public final class BannerPatterns1_20_5 {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String[] keys() {
|
||||
public static Key[] keys() {
|
||||
return PATTERNS.keys();
|
||||
}
|
||||
}
|
||||
+3
-2
@@ -1550,8 +1550,9 @@ public class ComponentRewriter1_20_5<C extends ClientboundPacketType> extends Js
|
||||
}
|
||||
|
||||
protected Item[] itemArrayFromTag(final UserConnection connection, final ListTag<CompoundTag> tag) {
|
||||
final Item[] items = new Item[tag.size()];
|
||||
for (int i = 0; i < tag.size(); i++) {
|
||||
final int size = Math.min(tag.size(), 1024);
|
||||
final Item[] items = new Item[size];
|
||||
for (int i = 0; i < size; i++) {
|
||||
items[i] = itemFromTag(connection, tag.get(i));
|
||||
}
|
||||
return items;
|
||||
|
||||
+8
-8
@@ -54,6 +54,7 @@ import com.viaversion.viaversion.rewriter.EntityRewriter;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.KeyMappings;
|
||||
import com.viaversion.viaversion.util.TagUtil;
|
||||
import com.viaversion.viaversion.util.UUIDUtil;
|
||||
import it.unimi.dsi.fastutil.ints.IntArraySet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
import java.util.Arrays;
|
||||
@@ -224,15 +225,14 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
|
||||
|
||||
final PacketWrapper bannerPatternsPacket = wrapper.create(ClientboundConfigurationPackets1_20_5.REGISTRY_DATA);
|
||||
bannerPatternsPacket.write(Types.STRING, "minecraft:banner_pattern");
|
||||
final RegistryEntry[] patternEntries = new RegistryEntry[BannerPatterns1_20_5.keys().length];
|
||||
final String[] keys = BannerPatterns1_20_5.keys();
|
||||
final Key[] keys = BannerPatterns1_20_5.keys();
|
||||
final RegistryEntry[] patternEntries = new RegistryEntry[keys.length];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
final CompoundTag pattern = new CompoundTag();
|
||||
final String key = keys[i];
|
||||
final String resourceLocation = "minecraft:" + key;
|
||||
pattern.putString("asset_id", key);
|
||||
pattern.putString("translation_key", "block.minecraft.banner." + key);
|
||||
patternEntries[i] = new RegistryEntry(resourceLocation, pattern);
|
||||
final Key key = keys[i];
|
||||
pattern.putString("asset_id", key.path());
|
||||
pattern.putString("translation_key", "block.minecraft.banner." + key.path());
|
||||
patternEntries[i] = new RegistryEntry(key.toString(), pattern);
|
||||
}
|
||||
bannerPatternsPacket.write(Types.REGISTRY_ENTRY_ARRAY, patternEntries);
|
||||
bannerPatternsPacket.send(Protocol1_20_3To1_20_5.class);
|
||||
@@ -407,7 +407,7 @@ public final class EntityPacketRewriter1_20_5 extends EntityRewriter<Clientbound
|
||||
|
||||
for (int i = 0; i < entries.length; i++) {
|
||||
if (entries[i] == null) {
|
||||
entries[i] = first.withKey(UUID.randomUUID().toString());
|
||||
entries[i] = first.withKey(UUIDUtil.randomUUID().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -159,7 +159,7 @@ public final class Protocol1_20_5To1_21 extends AbstractProtocol<ClientboundPack
|
||||
}
|
||||
|
||||
uuid = UUIDUtil.parseUUID(Key.stripNamespace(id).toUpperCase(Locale.ROOT));
|
||||
return uuid != null ? uuid : UUID.randomUUID();
|
||||
return uuid != null ? uuid : UUIDUtil.randomUUID();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+31
-17
@@ -66,6 +66,7 @@ import com.viaversion.viaversion.rewriter.TagRewriter;
|
||||
import com.viaversion.viaversion.rewriter.block.BlockRewriter1_21_5;
|
||||
import com.viaversion.viaversion.rewriter.text.NBTComponentRewriter;
|
||||
import com.viaversion.viaversion.util.Key;
|
||||
import com.viaversion.viaversion.util.MathUtil;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.viaversion.viaversion.util.ProtocolUtil.packetTypeMap;
|
||||
@@ -144,16 +145,19 @@ public final class Protocol1_21_11To26_1 extends AbstractProtocol<ClientboundPac
|
||||
registryDataRewriter.addHandler("dimension_type", (key, tag) -> {
|
||||
tag.putBoolean("has_ender_dragon_fight", Key.equals(key, "the_end"));
|
||||
|
||||
final CompoundTag attributes = tag.getCompoundTag("attributes");
|
||||
if (attributes != null) {
|
||||
final int ambientLightColor = switch (Key.stripMinecraftNamespace(key)) {
|
||||
case "the_end" -> -12630209;
|
||||
case "the_nether" -> -13621215;
|
||||
case "overworld" -> -16119286;
|
||||
default -> -16777216;
|
||||
};
|
||||
attributes.putInt("visual/ambient_light_color", ambientLightColor);
|
||||
CompoundTag attributes = tag.getCompoundTag("attributes");
|
||||
if (attributes == null) {
|
||||
attributes = new CompoundTag();
|
||||
tag.put("attributes", attributes);
|
||||
}
|
||||
|
||||
final int ambientLightColor = switch (Key.stripMinecraftNamespace(key)) {
|
||||
case "the_end" -> -12630209;
|
||||
case "the_nether" -> -13621215;
|
||||
case "overworld" -> -16119286;
|
||||
default -> ambientLightColor(tag.getFloat("ambient_light", 0F));
|
||||
};
|
||||
attributes.putInt("visual/ambient_light_color", ambientLightColor);
|
||||
});
|
||||
registerClientbound(ClientboundPackets1_21_11.SET_TIME, wrapper -> {
|
||||
wrapper.passthrough(Types.LONG); // Game time
|
||||
@@ -239,17 +243,21 @@ public final class Protocol1_21_11To26_1 extends AbstractProtocol<ClientboundPac
|
||||
|
||||
private void addJukeboxPlayables(final String... songs) {
|
||||
for (final String song : songs) {
|
||||
final CompoundTag songTag = new CompoundTag();
|
||||
final CompoundTag songDescription = new CompoundTag();
|
||||
songDescription.putString("translate", "jukebox_song.minecraft." + song);
|
||||
songTag.put("description", songDescription);
|
||||
songTag.putString("sound_event", "music_disc." + song);
|
||||
songTag.putFloat("length_in_seconds", 175F);
|
||||
songTag.putInt("comparator_output", 10);
|
||||
registryDataRewriter.addEntries("jukebox_song", new RegistryEntry(song, songTag));
|
||||
registryDataRewriter.addEntries("jukebox_song", createJukeboxPlayableEntry(song));
|
||||
}
|
||||
}
|
||||
|
||||
public static RegistryEntry createJukeboxPlayableEntry(final String song) {
|
||||
final CompoundTag songTag = new CompoundTag();
|
||||
final CompoundTag songDescription = new CompoundTag();
|
||||
songDescription.putString("translate", "jukebox_song.minecraft." + song);
|
||||
songTag.put("description", songDescription);
|
||||
songTag.putString("sound_event", "music_disc." + song);
|
||||
songTag.putFloat("length_in_seconds", 175F);
|
||||
songTag.putInt("comparator_output", 10);
|
||||
return new RegistryEntry(song, songTag);
|
||||
}
|
||||
|
||||
private void swapAffixAndAddAssetId(final String registryKey, final String affix) {
|
||||
registryDataRewriter.addHandler(registryKey, (key, tag) -> {
|
||||
swapEntityNameAffix(affix, tag);
|
||||
@@ -269,6 +277,12 @@ public final class Protocol1_21_11To26_1 extends AbstractProtocol<ClientboundPac
|
||||
assetIdTag.setValue(assetId.replace(key + "/", key + "/" + key + "_"));
|
||||
}
|
||||
|
||||
private int ambientLightColor(final float ambientLight) {
|
||||
// Interpolate between the overworld (ambient_light=0) and full white
|
||||
final int value = MathUtil.clamp(Math.round(10 + ambientLight * 245), 10, 255);
|
||||
return 0xFF000000 | value << 16 | value << 8 | value;
|
||||
}
|
||||
|
||||
private void swapEntityNameAffix(final String key, final CompoundTag tag) {
|
||||
final StringTag assetIdTag = tag.getStringTag("asset_id");
|
||||
final String assetId = assetIdTag.getValue();
|
||||
|
||||
+2
-2
@@ -20,7 +20,7 @@ package com.viaversion.viaversion.protocols.v1_21_11to26_1.data;
|
||||
import com.viaversion.nbt.tag.CompoundTag;
|
||||
import com.viaversion.viaversion.api.data.MappingDataBase;
|
||||
import com.viaversion.viaversion.api.data.MappingDataLoader;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import com.viaversion.viaversion.api.data.IdRanges;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
|
||||
public final class MappingData26_1 extends MappingDataBase {
|
||||
@@ -43,7 +43,7 @@ public final class MappingData26_1 extends MappingDataBase {
|
||||
pigSoundVariants = tag.getCompoundTag("pig_sound_variant");
|
||||
chickenSoundVariants = tag.getCompoundTag("chicken_sound_variant");
|
||||
|
||||
fluidBlockStates = new IntOpenHashSet(MappingDataLoader.INSTANCE.loadNBTFromFile("fluids-26.1.nbt").getIntArrayTag("fluids").getValue());
|
||||
fluidBlockStates = IdRanges.decode(MappingDataLoader.INSTANCE.loadNBTFromFile("fluids-26.1.nbt").getByteArrayTag("fluids"));
|
||||
}
|
||||
|
||||
public CompoundTag catSoundVariants() {
|
||||
|
||||
+1
-1
@@ -81,7 +81,7 @@ public enum ServerboundPackets26_1 implements ServerboundPacket26_1 {
|
||||
SET_STRUCTURE_BLOCK, // 0x3B
|
||||
SET_TEST_BLOCK, // 0x3C
|
||||
SIGN_UPDATE, // 0x3D
|
||||
SPECTATE_ENTITY, // 0x3E
|
||||
SPECTATE_ENTITY, // 0x3E // TODO rename to SPECTATOR_ACTION once a new enum is needed
|
||||
SWING, // 0x3F
|
||||
TELEPORT_TO_ENTITY, // 0x40
|
||||
TEST_INSTANCE_BLOCK_ACTION, // 0x41
|
||||
|
||||
Loaded 100 of 194 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user