Compare commits

...
23 Commits
Author SHA1 Message Date
Nassim Jahnke a8acae2d5f 4.8.1 Release 2023-10-03 20:57:47 +10:00
Nassim Jahnke fdb7f47749 Properly throw the client information packet into the queue instead 2023-10-03 20:54:51 +10:00
Nassim Jahnke cabb95a3a4 Schedule send client information 2023-10-03 20:39:46 +10:00
Nassim Jahnke 0b93af88a3 Re-send client information when re-entering play state
Fixes #3442
2023-10-03 19:49:55 +10:00
Unoqwy 11cb113619 Fix disconnecting error for unknown mobs with metadata (#3443) 2023-10-03 09:46:02 +00:00
Nassim Jahnke 429e4f0bff Switch server state after sending game profile
Probably fixes #3453
2023-09-28 11:12:46 +10:00
Nassim Jahnke 3b0d1d961a Fix secondary effect reading in serverbound beacon
Fixes #3455
2023-09-28 09:34:26 +10:00
Nassim Jahnke b4720c723a Fix biome and chunk size preconditions 2023-09-27 14:35:20 +10:00
Nassim Jahnke 215cbc6310 Fix 1.20.2 custom potion effect handling
Fixes #3448
2023-09-27 11:30:35 +10:00
RK_01 f07ba9b527 Reduce delay for sequence acknowledgements (#3445) 2023-09-26 10:53:36 +10:00
Misode 5a20030ff3 [ci skip] Update Minecraft Wiki link to new domain after fork (#3446) 2023-09-26 10:52:03 +10:00
Nassim Jahnke 1b350c0c53 Try to fix ci 2023-09-26 10:40:44 +10:00
Nassim Jahnke 87db6fc75a [ci skip] Fix branch check 2023-09-26 00:17:33 +10:00
Nassim Jahnke 840770a350 Fix missing server state change 2023-09-25 20:11:29 +10:00
Nassim Jahnke 3997ea70f7 Fixup state splitting 2023-09-25 18:59:15 +10:00
Nassim Jahnke 854696abff Add separate client and server protocol state 2023-09-25 16:44:41 +10:00
Nassim Jahnke d92b9fdeaa Fix connection field search on 1.20.2 2023-09-25 09:57:55 +10:00
Nassim Jahnke 270d28f259 Don't cancel custom query answer 2023-09-25 08:56:26 +10:00
RK_01 ed1852ba40 Fix 1.20.2 -> 1.20.1 brand custom payloads properly (#3440) 2023-09-25 08:52:17 +10:00
riku6460 f91e6a83f9 Fix sign nbt on 1.20 client (#3436) 2023-09-24 13:02:31 +10:00
Nassim Jahnke 8a0e187621 Drop custom payloads with non-alphanumerical brands
For some reason the client errors only if it contains certain characters, needs further investigation. Mojang changed their string writing implementation, though the actual encoding does not seem to have changed, and this error does not occur anywhere else
2023-09-24 10:48:47 +10:00
Nassim Jahnke 0e8b5e8e98 Bump version to 4.8.1-SNAPSHOT 2023-09-24 10:47:43 +10:00
Nassim Jahnke e30dd63b3c [ci skip] Only publish releases from the main branch 2023-09-22 15:32:14 +10:00
27 changed files with 624 additions and 245 deletions

No files matched your search

+1 -1
View File
@@ -83,7 +83,7 @@ Resources
--------
- **[Via Mappings Generator](https://github.com/ViaVersion/Mappings)**
- **[Mojang mappings](https://minecraft.fandom.com/wiki/Obfuscation_map)** (Thank you, Mojang, very cool)
- **[Mojang mappings](https://minecraft.wiki/w/Obfuscation_map)** (Thank you, Mojang, very cool)
- **[wiki.vg](https://wiki.vg)** (Used for historic information regarding packet structure, we also contribute back)
- **[Burger](https://github.com/Pokechu22/Burger)** (See [PAaaS](https://github.com/Matsv/Paaas))
@@ -23,6 +23,7 @@
package com.viaversion.viaversion.api.connection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.State;
import java.util.UUID;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -33,10 +34,63 @@ public interface ProtocolInfo {
* Returns the protocol state the user is currently in.
*
* @return protocol state
* @deprecated server and client can be in different states, use {@link #getClientState()} or {@link #getServerState()}
*/
State getState();
@Deprecated/*(forRemoval = true)*/
default State getState() {
return this.getServerState();
}
void setState(State state);
/**
* Returns the protocol state the client is currently in.
*
* @return the client protocol state
*/
State getClientState();
/**
* Returns the protocol state the server is currently in.
*
* @return the server protocol state
*/
State getServerState();
/**
* Returns the protocol state for the given direction.
*
* @param direction protocol direction
* @return state for the given direction
*/
default State getState(final Direction direction) {
// Return the state the packet is coming from
return direction == Direction.CLIENTBOUND ? this.getServerState() : this.getClientState();
}
/**
* Sets both client and server state.
*
* @param state the new protocol state
* @see #setClientState(State)
* @see #setServerState(State)
*/
default void setState(final State state) {
this.setClientState(state);
this.setServerState(state);
}
/**
* Sets the client protocol state.
*
* @param clientState the new client protocol state
*/
void setClientState(State clientState);
/**
* Sets the server protocol state.
*
* @param serverState the new server protocol state
*/
void setServerState(State serverState);
/**
* Returns the user's protocol version, or -1 if not set.
@@ -96,19 +96,7 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
initialized = true;
registerPackets();
// Register handlers for protocol state switching
// TODO Only register one of those handlers, possibly somehow in the base protocol
final SU configurationAcknowledgedPacket = configurationAcknowledgedPacket();
if (configurationAcknowledgedPacket != null) {
registerServerbound(configurationAcknowledgedPacket, wrapper -> wrapper.user().getProtocolInfo().setState(State.CONFIGURATION));
}
final ServerboundPacketType finishConfigurationPacket = finishConfigurationPacket();
if (finishConfigurationPacket != null) {
final int id = finishConfigurationPacket.getId();
registerServerbound(State.CONFIGURATION, id, id, wrapper -> wrapper.user().getProtocolInfo().setState(State.PLAY));
}
registerConfigurationChangeHandlers();
// Register the rest of the ids with no handlers if necessary
if (unmappedClientboundPacketType != null && mappedClientboundPacketType != null
@@ -131,6 +119,33 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
}
}
protected void registerConfigurationChangeHandlers() {
// Register handlers for protocol state switching
// Assuming ids will change too often, it is cleaner to register them here instead of the base protocols,
// even if there will be multiple of these handlers
final SU configurationAcknowledgedPacket = configurationAcknowledgedPacket();
if (configurationAcknowledgedPacket != null) {
registerServerbound(configurationAcknowledgedPacket, setClientStateHandler(State.CONFIGURATION));
}
final CU startConfigurationPacket = startConfigurationPacket();
if (startConfigurationPacket != null) {
registerClientbound(startConfigurationPacket, setServerStateHandler(State.CONFIGURATION));
}
final ServerboundPacketType finishConfigurationPacket = serverboundFinishConfigurationPacket();
if (finishConfigurationPacket != null) {
final int id = finishConfigurationPacket.getId();
registerServerbound(State.CONFIGURATION, id, id, setClientStateHandler(State.PLAY));
}
final ClientboundPacketType clientboundFinishConfigurationPacket = clientboundFinishConfigurationPacket();
if (clientboundFinishConfigurationPacket != null) {
final int id = clientboundFinishConfigurationPacket.getId();
registerClientbound(State.CONFIGURATION, id, id, setServerStateHandler(State.PLAY));
}
}
private <U extends PacketType, M extends PacketType> void registerPacketIdChanges(
Map<State, PacketTypeMap<U>> unmappedPacketTypes,
Map<State, PacketTypeMap<M>> mappedPacketTypes,
@@ -228,7 +243,18 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
return packetTypeMap != null ? packetTypeMap.typeByName("CONFIGURATION_ACKNOWLEDGED") : null;
}
protected @Nullable ServerboundPacketType finishConfigurationPacket() {
protected @Nullable CU startConfigurationPacket() {
final Map<State, PacketTypeMap<CU>> packetTypes = packetTypesProvider.unmappedClientboundPacketTypes();
final PacketTypeMap<CU> packetTypeMap = packetTypes.get(State.PLAY);
return packetTypeMap != null ? packetTypeMap.typeByName("START_CONFIGURATION") : null;
}
protected @Nullable ServerboundPacketType serverboundFinishConfigurationPacket() {
// To be overridden
return null;
}
protected @Nullable ClientboundPacketType clientboundFinishConfigurationPacket() {
// To be overridden
return null;
}
@@ -429,6 +455,14 @@ public abstract class AbstractProtocol<CU extends ClientboundPacketType, CM exte
}
}
protected PacketHandler setClientStateHandler(final State state) {
return wrapper -> wrapper.user().getProtocolInfo().setClientState(state);
}
protected PacketHandler setServerStateHandler(final State state) {
return wrapper -> wrapper.user().getProtocolInfo().setClientState(state);
}
@Override
public PacketTypesProvider<CU, CM, SM, SU> getPacketTypesProvider() {
return packetTypesProvider;
@@ -69,10 +69,17 @@ public interface ProtocolPipeline extends SimpleProtocol {
/**
* Returns the list of protocols this pipeline contains.
*
* @return list of protocols in this pipe
* @return immutable list of protocols in this pipe
*/
List<Protocol> pipes();
/**
* Returns the list of protocols this pipeline contains in reversed order.
*
* @return immutable list of protocols in reversed direction
*/
List<Protocol> reversedPipes();
/**
* Returns whether this pipe has protocols that are not base protocols, as given by {@link Protocol#isBaseProtocol()}.
*
@@ -27,6 +27,6 @@ public enum State {
HANDSHAKE,
STATUS,
LOGIN,
PLAY,
CONFIGURATION
CONFIGURATION,
PLAY
}
@@ -28,9 +28,8 @@ import com.viaversion.viaversion.api.type.Type;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
import io.netty.buffer.ByteBufOutputStream;
import org.checkerframework.checker.nullness.qual.Nullable;
import java.io.IOException;
import org.checkerframework.checker.nullness.qual.Nullable;
public class NBTType extends Type<CompoundTag> {
@@ -42,37 +41,31 @@ public class NBTType extends Type<CompoundTag> {
}
@Override
public CompoundTag read(ByteBuf buffer) throws Exception {
public CompoundTag read(final ByteBuf buffer) throws Exception {
return read(buffer, true);
}
@Override
public void write(ByteBuf buffer, CompoundTag object) throws Exception {
public void write(final ByteBuf buffer, final CompoundTag object) throws Exception {
write(buffer, object, "");
}
public static CompoundTag read(final ByteBuf buffer, final boolean readName) throws Exception {
final int readerIndex = buffer.readerIndex();
final byte b = buffer.readByte();
if (b == 0) {
final byte id = buffer.readByte();
if (id == 0) {
return null;
}
buffer.readerIndex(readerIndex);
final ByteBufInputStream in = new ByteBufInputStream(buffer);
final int id = in.readByte();
if (id != CompoundTag.ID) {
throw new IOException(String.format("Expected root tag to be a CompoundTag, was %s", id));
}
if (readName) {
in.skipBytes(in.readUnsignedShort());
buffer.skipBytes(buffer.readUnsignedShort());
}
final TagLimiter tagLimiter = TagLimiter.create(MAX_NBT_BYTES, MAX_NESTING_LEVEL);
final CompoundTag tag = new CompoundTag();
tag.read(in, tagLimiter);
tag.read(new ByteBufInputStream(buffer), tagLimiter);
return tag;
}
+1 -1
View File
@@ -50,7 +50,7 @@ fun Project.latestCommitMessage(): String {
fun Project.branchName(): String {
val byteOut = ByteArrayOutputStream()
exec {
commandLine = listOf("git", "branch")
commandLine = listOf("git", "rev-parse", "--abbrev-ref", "HEAD")
standardOutput = byteOut
}
return byteOut.toString(Charsets.UTF_8.name()).trim()
@@ -46,8 +46,8 @@ public class JoinListener implements Listener {
Field gamePacketListenerField = null, connectionField = null, channelField = null;
try {
getHandleMethod = NMSUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle");
gamePacketListenerField = findField(getHandleMethod.getReturnType(), "PlayerConnection", "ServerGamePacketListenerImpl");
connectionField = findField(gamePacketListenerField.getType(), "NetworkManager", "Connection");
gamePacketListenerField = findField(false, getHandleMethod.getReturnType(), "PlayerConnection", "ServerGamePacketListenerImpl");
connectionField = findField(true, gamePacketListenerField.getType(), "NetworkManager", "Connection");
channelField = findField(connectionField.getType(), Class.forName("io.netty.channel.Channel"));
} catch (NoSuchMethodException | NoSuchFieldException | ClassNotFoundException e) {
Via.getPlatform().getLogger().log(
@@ -63,8 +63,8 @@ public class JoinListener implements Listener {
}
// Loosely search a field with any name, as long as it matches a type name.
private static Field findField(Class<?> clazz, String... types) throws NoSuchFieldException {
for (Field field : clazz.getDeclaredFields()) {
private static Field findField(boolean all, Class<?> clazz, String... types) throws NoSuchFieldException {
for (Field field : all ? clazz.getFields() : clazz.getDeclaredFields()) {
String fieldTypeName = field.getType().getSimpleName();
for (String type : types) {
if (!fieldTypeName.equals(type)) {
@@ -17,6 +17,7 @@
*/
package com.viaversion.viaversion.connection;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.ProtocolPipeline;
@@ -26,25 +27,42 @@ import java.util.UUID;
public class ProtocolInfoImpl implements ProtocolInfo {
private final UserConnection connection;
private State state = State.HANDSHAKE;
private State clientState = State.HANDSHAKE;
private State serverState = State.HANDSHAKE;
private int protocolVersion = -1;
private int serverProtocolVersion = -1;
private String username;
private UUID uuid;
private ProtocolPipeline pipeline;
public ProtocolInfoImpl(UserConnection connection) {
public ProtocolInfoImpl(final UserConnection connection) {
this.connection = connection;
}
@Override
public State getState() {
return state;
public State getClientState() {
return clientState;
}
@Override
public void setState(State state) {
this.state = state;
public void setClientState(final State clientState) {
if (Via.getManager().debugHandler().enabled()) {
Via.getPlatform().getLogger().info("Client state changed from " + this.clientState + " to " + clientState + " for " + connection.getProtocolInfo().getUuid());
}
this.clientState = clientState;
}
@Override
public State getServerState() {
return serverState;
}
@Override
public void setServerState(final State serverState) {
if (Via.getManager().debugHandler().enabled()) {
Via.getPlatform().getLogger().info("Server state changed from " + this.serverState + " to " + serverState + " for " + connection.getProtocolInfo().getUuid());
}
this.serverState = serverState;
}
@Override
@@ -108,7 +126,8 @@ public class ProtocolInfoImpl implements ProtocolInfo {
@Override
public String toString() {
return "ProtocolInfo{" +
"state=" + state +
"clientState=" + clientState +
", serverState=" + serverState +
", protocolVersion=" + protocolVersion +
", serverProtocolVersion=" + serverProtocolVersion +
", username='" + username + '\'' +
@@ -27,6 +27,7 @@ import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
import com.viaversion.viaversion.api.protocol.packet.PacketTracker;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.exception.CancelException;
import com.viaversion.viaversion.protocol.packet.PacketWrapperImpl;
@@ -324,8 +325,9 @@ public class UserConnectionImpl implements UserConnection {
}
PacketWrapper wrapper = new PacketWrapperImpl(id, buf, this);
State state = protocolInfo.getState(direction);
try {
protocolInfo.getPipeline().transform(direction, protocolInfo.getState(), wrapper);
protocolInfo.getPipeline().transform(direction, state, wrapper);
} catch (CancelException ex) {
throw cancelSupplier.apply(ex);
}
@@ -37,7 +37,7 @@ public class EntityTrackerBase implements EntityTracker, ClientEntityIdChangeLis
private final UserConnection connection;
private final EntityType playerType;
private int clientEntityId = -1;
private int currentWorldSectionHeight = 16;
private int currentWorldSectionHeight = -1;
private int currentMinY;
private String currentWorld;
private int biomesSent = -1;
@@ -17,7 +17,6 @@
*/
package com.viaversion.viaversion.protocol;
import com.google.common.collect.Sets;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.debug.DebugHandler;
@@ -30,9 +29,10 @@ import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.protocol.packet.State;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -42,8 +42,10 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
/**
* Protocol list ordered from client to server transforation with the base protocols at the end.
*/
private List<Protocol> protocolList;
private Set<Class<? extends Protocol>> protocolSet;
private final List<Protocol> protocolList = new CopyOnWriteArrayList<>();
private final Set<Class<? extends Protocol>> protocolSet = new HashSet<>();
private List<Protocol> reversedProtocolList = new CopyOnWriteArrayList<>();
private int baseProtocols;
public ProtocolPipelineImpl(UserConnection userConnection) {
this.userConnection = userConnection;
@@ -53,14 +55,12 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
@Override
protected void registerPackets() {
protocolList = new CopyOnWriteArrayList<>();
// Create a backing set for faster contains calls with larger pipes
protocolSet = Sets.newSetFromMap(new ConcurrentHashMap<>());
// This is a pipeline so we register basic pipes
Protocol baseProtocol = Via.getManager().getProtocolManager().getBaseProtocol();
final Protocol<?, ?, ?, ?> baseProtocol = Via.getManager().getProtocolManager().getBaseProtocol();
protocolList.add(baseProtocol);
reversedProtocolList.add(baseProtocol);
protocolSet.add(baseProtocol.getClass());
baseProtocols++;
}
@Override
@@ -69,44 +69,38 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
@Override
public void add(Protocol protocol) {
protocolList.add(protocol);
public synchronized void add(final Protocol protocol) {
if (protocol.isBaseProtocol()) {
// Add base protocol on top of previous ones
protocolList.add(baseProtocols, protocol);
reversedProtocolList.add(baseProtocols, protocol);
baseProtocols++;
} else {
protocolList.add(protocol);
reversedProtocolList.add(0, protocol);
}
protocolSet.add(protocol.getClass());
protocol.init(userConnection);
if (!protocol.isBaseProtocol()) {
moveBaseProtocolsToTail();
}
}
@Override
public void add(Collection<Protocol> protocols) {
public synchronized void add(final Collection<Protocol> protocols) {
protocolList.addAll(protocols);
for (Protocol protocol : protocols) {
for (final Protocol protocol : protocols) {
protocol.init(userConnection);
this.protocolSet.add(protocol.getClass());
protocolSet.add(protocol.getClass());
}
moveBaseProtocolsToTail();
refreshReversedList();
}
private void moveBaseProtocolsToTail() {
// Move base Protocols to the end, so the login packets can be modified by other protocols
List<Protocol> baseProtocols = null;
for (Protocol protocol : protocolList) {
if (protocol.isBaseProtocol()) {
if (baseProtocols == null) {
baseProtocols = new ArrayList<>();
}
baseProtocols.add(protocol);
}
}
if (baseProtocols != null) {
protocolList.removeAll(baseProtocols);
protocolList.addAll(baseProtocols);
}
private synchronized void refreshReversedList() {
final List<Protocol> protocols = new ArrayList<>(protocolList.subList(0, this.baseProtocols));
final List<Protocol> additionalProtocols = new ArrayList<>(protocolList.subList(this.baseProtocols, protocolList.size()));
Collections.reverse(additionalProtocols);
protocols.addAll(additionalProtocols);
reversedProtocolList = new CopyOnWriteArrayList<>(protocols);
}
@Override
@@ -119,7 +113,7 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
// Apply protocols
packetWrapper.apply(direction, state, 0, protocolList, direction == Direction.CLIENTBOUND);
packetWrapper.apply(direction, state, 0, protocolListFor(direction));
super.transform(direction, state, packetWrapper);
if (debugHandler.enabled() && debugHandler.logPostPacketTransform() && debugHandler.shouldLog(packetWrapper, direction)) {
@@ -127,6 +121,10 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
}
}
private List<Protocol> protocolListFor(final Direction direction) {
return Collections.unmodifiableList(direction == Direction.SERVERBOUND ? protocolList : reversedProtocolList);
}
private void logPacket(Direction direction, State state, PacketWrapper packetWrapper, int originalID) {
// Debug packet
int clientProtocol = userConnection.getProtocolInfo().getProtocolVersion();
@@ -166,7 +164,12 @@ public class ProtocolPipelineImpl extends AbstractSimpleProtocol implements Prot
@Override
public List<Protocol> pipes() {
return protocolList;
return Collections.unmodifiableList(protocolList);
}
@Override
public List<Protocol> reversedPipes() {
return Collections.unmodifiableList(reversedProtocolList);
}
@Override
@@ -19,6 +19,7 @@ package com.viaversion.viaversion.protocol.packet;
import com.google.common.base.Preconditions;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.Protocol;
import com.viaversion.viaversion.api.protocol.packet.Direction;
@@ -308,12 +309,18 @@ public class PacketWrapperImpl implements PacketWrapper {
* @throws Exception if it fails to write
*/
private ByteBuf constructPacket(Class<? extends Protocol> packetProtocol, boolean skipCurrentPipeline, Direction direction) throws Exception {
// Apply current pipeline - for outgoing protocol, the collection will be reversed in the apply method
Protocol[] protocols = user().getProtocolInfo().getPipeline().pipes().toArray(PROTOCOL_ARRAY);
boolean reverse = direction == Direction.CLIENTBOUND;
final ProtocolInfo protocolInfo = user().getProtocolInfo();
final List<Protocol> pipes = direction == Direction.SERVERBOUND ? protocolInfo.getPipeline().pipes() : protocolInfo.getPipeline().reversedPipes();
final List<Protocol> protocols = new ArrayList<>();
int index = -1;
for (int i = 0; i < protocols.length; i++) {
if (protocols[i].getClass() == packetProtocol) {
for (int i = 0; i < pipes.size(); i++) {
// Always add base protocols to the head
final Protocol protocol = pipes.get(i);
if (protocol.isBaseProtocol()) {
protocols.add(protocol);
}
if (protocol.getClass() == packetProtocol) {
index = i;
break;
}
@@ -325,15 +332,18 @@ public class PacketWrapperImpl implements PacketWrapper {
}
if (skipCurrentPipeline) {
index = reverse ? index - 1 : index + 1;
index = Math.min(index + 1, pipes.size());
}
// Add remaining protocols on top
protocols.addAll(pipes.subList(index, pipes.size()));
// Reset reader before we start
resetReader();
// Apply other protocols
apply(direction, user().getProtocolInfo().getState(), index, protocols, reverse);
ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
apply(direction, protocolInfo.getState(direction), 0, protocols);
final ByteBuf output = inputBuffer == null ? user().getChannel().alloc().buffer() : inputBuffer.alloc().buffer();
try {
writeToBuffer(output);
return output.retain();
@@ -402,15 +412,22 @@ public class PacketWrapperImpl implements PacketWrapper {
private PacketWrapperImpl apply(Direction direction, State state, int index, Protocol[] pipeline, boolean reverse) throws Exception {
// Reset the reader after every transformation for the packetWrapper, so it can be recycled across packets
State updatedState = state; // The state might change while transforming, so we need to check for that
if (reverse) {
for (int i = index; i >= 0; i--) {
pipeline[i].transform(direction, state, this);
pipeline[i].transform(direction, updatedState, this);
resetReader();
if (this.packetType != null) {
updatedState = this.packetType.state();
}
}
} else {
for (int i = index; i < pipeline.length; i++) {
pipeline[i].transform(direction, state, this);
pipeline[i].transform(direction, updatedState, this);
resetReader();
if (this.packetType != null) {
updatedState = this.packetType.state();
}
}
}
return this;
@@ -33,12 +33,10 @@ import com.viaversion.viaversion.api.protocol.version.VersionProvider;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocol.ProtocolManagerImpl;
import com.viaversion.viaversion.protocol.ServerProtocolVersionSingleton;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ServerboundConfigurationPackets1_20_2;
import com.viaversion.viaversion.protocols.protocol1_9to1_8.Protocol1_9To1_8;
import com.viaversion.viaversion.util.ChatColorUtil;
import com.viaversion.viaversion.util.GsonUtil;
import io.netty.channel.ChannelFuture;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
@@ -121,7 +119,7 @@ public class BaseProtocol1_7 extends AbstractProtocol {
// Login Success Packet
registerClientbound(ClientboundLoginPackets.GAME_PROFILE, wrapper -> {
ProtocolInfo info = wrapper.user().getProtocolInfo();
if (info.getProtocolVersion() < ProtocolVersion.v1_20_2.getVersion()) {
if (info.getProtocolVersion() < ProtocolVersion.v1_20_2.getVersion()) { // On 1.20.2+, wait for the login ack
info.setState(State.PLAY);
}
@@ -165,7 +163,10 @@ public class BaseProtocol1_7 extends AbstractProtocol {
}
});
registerServerbound(ServerboundLoginPackets.LOGIN_ACKNOWLEDGED, wrapper -> wrapper.user().getProtocolInfo().setState(State.CONFIGURATION));
registerServerbound(ServerboundLoginPackets.LOGIN_ACKNOWLEDGED, wrapper -> {
final ProtocolInfo info = wrapper.user().getProtocolInfo();
info.setState(State.CONFIGURATION);
});
}
@Override
@@ -44,8 +44,8 @@ public final class WorldPackets {
protocol.registerClientbound(ClientboundPackets1_18.CHUNK_DATA, wrapper -> {
final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());
Preconditions.checkArgument(tracker.biomesSent() != 0, "Biome count not set");
Preconditions.checkArgument(tracker.currentWorldSectionHeight() != 0, "Section height not set");
Preconditions.checkArgument(tracker.biomesSent() != -1, "Biome count not set");
Preconditions.checkArgument(tracker.currentWorldSectionHeight() != -1, "Section height not set");
final Chunk1_18Type chunkType = new Chunk1_18Type(tracker.currentWorldSectionHeight(),
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()),
MathUtil.ceilLog2(tracker.biomesSent()));
@@ -29,6 +29,7 @@ public class AckSequenceProvider implements Provider {
public void handleSequence(final UserConnection connection, final int sequence) throws Exception {
final PacketWrapper ackPacket = PacketWrapper.create(ClientboundPackets1_19.BLOCK_CHANGED_ACK, connection);
ackPacket.write(Type.VAR_INT, sequence);
ackPacket.scheduleSend(Protocol1_19To1_18_2.class);
ackPacket.send(Protocol1_19To1_18_2.class);
}
}
}
@@ -20,6 +20,7 @@ package com.viaversion.viaversion.protocols.protocol1_20_2to1_20;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.google.gson.JsonElement;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.connection.ProtocolInfo;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.data.MappingData;
import com.viaversion.viaversion.api.data.MappingDataBase;
@@ -70,6 +71,21 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
soundRewriter.register1_19_3Sound(ClientboundPackets1_19_4.SOUND);
soundRewriter.registerEntitySound(ClientboundPackets1_19_4.ENTITY_SOUND);
registerClientbound(ClientboundPackets1_19_4.PLUGIN_MESSAGE, wrapper -> {
final String channel = wrapper.passthrough(Type.STRING);
if (channel.equals("minecraft:brand")) {
wrapper.passthrough(Type.STRING);
wrapper.read(Type.REMAINING_BYTES);
}
});
registerServerbound(ServerboundPackets1_20_2.PLUGIN_MESSAGE, wrapper -> {
final String channel = wrapper.passthrough(Type.STRING);
if (channel.equals("minecraft:brand")) {
wrapper.passthrough(Type.STRING);
wrapper.read(Type.REMAINING_BYTES);
}
});
registerClientbound(ClientboundPackets1_19_4.RESOURCE_PACK, wrapper -> {
final String url = wrapper.passthrough(Type.STRING);
final String hash = wrapper.passthrough(Type.STRING);
@@ -98,33 +114,52 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
// during the configuration phase before finally transitioning to the play state with the client as well.
registerClientbound(State.LOGIN, ClientboundLoginPackets.GAME_PROFILE.getId(), ClientboundLoginPackets.GAME_PROFILE.getId(), wrapper -> {
wrapper.user().get(ConfigurationState.class).setBridgePhase(BridgePhase.PROFILE_SENT);
// Set the state according to what the server expects. All packets between now and when the client
// switches to PLAY as well will be discarded after being dealt with.
wrapper.user().getProtocolInfo().setState(State.PLAY);
wrapper.user().getProtocolInfo().setServerState(State.PLAY);
});
registerServerbound(State.LOGIN, ServerboundLoginPackets.LOGIN_ACKNOWLEDGED.getId(), -1, wrapper -> {
wrapper.cancel();
// Overwrite the state set in the base protocol to what the server actually keeps sending
wrapper.user().getProtocolInfo().setServerState(State.PLAY);
final ConfigurationState configurationState = wrapper.user().get(ConfigurationState.class);
configurationState.setBridgePhase(BridgePhase.CONFIGURATION);
configurationState.sendQueuedPackets(wrapper.user());
});
cancelServerbound(State.LOGIN, ServerboundLoginPackets.CUSTOM_QUERY_ANSWER.getId());
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.FINISH_CONFIGURATION.getId(), -1, wrapper -> {
wrapper.cancel();
wrapper.user().getProtocolInfo().setClientState(State.PLAY);
final ConfigurationState configurationState = wrapper.user().get(ConfigurationState.class);
configurationState.setBridgePhase(BridgePhase.NONE);
configurationState.sendQueuedPackets(wrapper.user());
configurationState.clear();
});
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.CLIENT_INFORMATION.getId(), -1, queueServerboundPacket(ServerboundPackets1_20_2.CLIENT_SETTINGS));
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.CLIENT_INFORMATION.getId(), -1, wrapper -> {
final ConfigurationState.ClientInformation clientInformation = new ConfigurationState.ClientInformation(
wrapper.read(Type.STRING), // Language
wrapper.read(Type.BYTE), // View distance
wrapper.read(Type.VAR_INT), // Chat visibility
wrapper.read(Type.BOOLEAN), // Chat colors
wrapper.read(Type.UNSIGNED_BYTE), // Model customization
wrapper.read(Type.VAR_INT), // Main hand
wrapper.read(Type.BOOLEAN), // Text filtering enabled
wrapper.read(Type.BOOLEAN) // Allow listing in server list preview
);
// Store it to re-send it when another ClientboundLoginPacket is sent, since the client will only send it
// once per connection right after the handshake
final ConfigurationState configurationState = wrapper.user().get(ConfigurationState.class);
configurationState.setClientInformation(clientInformation);
wrapper.cancel();
});
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.CUSTOM_PAYLOAD.getId(), -1, queueServerboundPacket(ServerboundPackets1_20_2.PLUGIN_MESSAGE));
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.KEEP_ALIVE.getId(), -1, queueServerboundPacket(ServerboundPackets1_20_2.KEEP_ALIVE));
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.PONG.getId(), -1, queueServerboundPacket(ServerboundPackets1_20_2.PONG));
// Cancel this, as it will always just be the response to a re-sent pack from us
registerServerbound(State.CONFIGURATION, ServerboundConfigurationPackets1_20_2.RESOURCE_PACK.getId(), -1, PacketWrapper::cancel);
@@ -138,6 +173,7 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
}
// Reenter the configuration state
wrapper.user().getProtocolInfo().setClientState(State.CONFIGURATION);
configurationState.setBridgePhase(BridgePhase.CONFIGURATION);
final LastResourcePack lastResourcePack = wrapper.user().get(LastResourcePack.class);
@@ -165,6 +201,12 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
@Override
public void transform(final Direction direction, final State state, final PacketWrapper packetWrapper) throws Exception {
if (direction == Direction.SERVERBOUND) {
// The client will have the correct state set
super.transform(direction, state, packetWrapper);
return;
}
final ConfigurationState configurationBridge = packetWrapper.user().get(ConfigurationState.class);
if (configurationBridge == null) {
// Bad state during an unexpected disconnect
@@ -177,13 +219,6 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
return;
}
if (direction == Direction.SERVERBOUND) {
// Client and server will be on different protocol states, pick the right client protocol state
super.transform(direction, phase == BridgePhase.PROFILE_SENT ? State.LOGIN
: phase == BridgePhase.REENTERING_CONFIGURATION ? State.PLAY : State.CONFIGURATION, packetWrapper);
return;
}
if (phase == BridgePhase.PROFILE_SENT || phase == BridgePhase.REENTERING_CONFIGURATION) {
// Queue packets sent by the server while we wait for the client to transition to the configuration state
configurationBridge.addPacketToQueue(packetWrapper, true);
@@ -194,7 +229,7 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
// Map some of them to their configuration state counterparts, but make sure to let join game through
final int unmappedId = packetWrapper.getId();
if (unmappedId == ClientboundPackets1_19_4.JOIN_GAME.getId()) {
super.transform(direction, state, packetWrapper);
super.transform(direction, State.PLAY, packetWrapper);
return;
}
@@ -237,6 +272,9 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
}
public static void sendConfigurationPackets(final UserConnection connection, final CompoundTag dimensionRegistry, @Nullable final LastResourcePack lastResourcePack) throws Exception {
final ProtocolInfo protocolInfo = connection.getProtocolInfo();
protocolInfo.setServerState(State.CONFIGURATION);
final PacketWrapper registryDataPacket = PacketWrapper.create(ClientboundConfigurationPackets1_20_2.REGISTRY_DATA, connection);
registryDataPacket.write(Type.NAMELESS_NBT, dimensionRegistry);
registryDataPacket.send(Protocol1_20_2To1_20.class);
@@ -260,6 +298,8 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
final PacketWrapper finishConfigurationPacket = PacketWrapper.create(ClientboundConfigurationPackets1_20_2.FINISH_CONFIGURATION, connection);
finishConfigurationPacket.send(Protocol1_20_2To1_20.class);
protocolInfo.setServerState(State.PLAY);
}
@Override
@@ -268,8 +308,8 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
}
@Override
protected @Nullable ServerboundPackets1_20_2 configurationAcknowledgedPacket() {
return null; // Don't handle it in the transitioning protocol
protected void registerConfigurationChangeHandlers() {
// Don't handle it in the transitioning protocol
}
@Override
@@ -287,4 +327,4 @@ public final class Protocol1_20_2To1_20 extends AbstractProtocol<ClientboundPack
public ItemRewriter<Protocol1_20_2To1_20> getItemRewriter() {
return itemPacketRewriter;
}
}
}
@@ -19,7 +19,10 @@ package com.viaversion.viaversion.protocols.protocol1_20_2to1_20.rewriter;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.github.steveice10.opennbt.tag.builtin.IntTag;
import com.github.steveice10.opennbt.tag.builtin.ListTag;
import com.github.steveice10.opennbt.tag.builtin.NumberTag;
import com.github.steveice10.opennbt.tag.builtin.StringTag;
import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.viaversion.viaversion.api.data.ParticleMappings;
import com.viaversion.viaversion.api.data.entity.EntityTracker;
import com.viaversion.viaversion.api.minecraft.blockentity.BlockEntity;
@@ -38,51 +41,14 @@ import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.rewriter.Recip
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.Protocol1_20_2To1_20;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.packet.ServerboundPackets1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.type.ChunkType1_20_2;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.util.PotionEffects;
import com.viaversion.viaversion.rewriter.BlockRewriter;
import com.viaversion.viaversion.rewriter.ItemRewriter;
import com.viaversion.viaversion.util.Key;
import com.viaversion.viaversion.util.MathUtil;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<ClientboundPackets1_19_4, ServerboundPackets1_20_2, Protocol1_20_2To1_20> {
public static final String[] POTION_EFFECTS = {
"", // No effect
"speed",
"slowness",
"haste",
"mining_fatigue",
"strength",
"instant_health",
"instant_damage",
"jump_boost",
"nausea",
"regeneration",
"resistance",
"fire_resistance",
"water_breathing",
"invisibility",
"blindness",
"night_vision",
"hunger",
"weakness",
"poison",
"wither",
"health_boost",
"absorption",
"saturation",
"glowing",
"levitation",
"luck",
"unluck",
"slow_falling",
"conduit_power",
"dolphins_grace",
"bad_omen",
"hero_of_the_village",
"darkness"
};
public BlockItemPacketRewriter1_20_2(final Protocol1_20_2To1_20 protocol) {
super(protocol);
}
@@ -96,8 +62,11 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
blockRewriter.registerEffect(ClientboundPackets1_19_4.EFFECT, 1010, 2001);
protocol.registerServerbound(ServerboundPackets1_20_2.SET_BEACON_EFFECT, wrapper -> {
if (wrapper.passthrough(Type.BOOLEAN)) {
// Effects start at 1 before 1.20.2
// Effects start at 1 before 1.20.2
if (wrapper.passthrough(Type.BOOLEAN)) { // Primary effect
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT) + 1);
}
if (wrapper.passthrough(Type.BOOLEAN)) { // Secondary effect
wrapper.write(Type.VAR_INT, wrapper.read(Type.VAR_INT) + 1);
}
});
@@ -151,8 +120,12 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
map(Type.UNSIGNED_BYTE); // Window id
map(Type.VAR_INT); // State id
handler(wrapper -> {
wrapper.write(Type.ITEM1_20_2_VAR_INT_ARRAY, wrapper.read(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT)); // Items
wrapper.write(Type.ITEM1_20_2, wrapper.read(Type.FLAT_VAR_INT_ITEM)); // Carried item
final Item[] items = wrapper.read(Type.FLAT_VAR_INT_ITEM_ARRAY_VAR_INT);
for (final Item item : items) {
handleItemToClient(item);
}
wrapper.write(Type.ITEM1_20_2_VAR_INT_ARRAY, items);
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Carried item
});
}
});
@@ -162,7 +135,7 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
map(Type.UNSIGNED_BYTE); // Window id
map(Type.VAR_INT); // State id
map(Type.SHORT); // Slot id
map(Type.FLAT_VAR_INT_ITEM, Type.ITEM1_20_2); // Item
handler(wrapper -> wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))));
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.ADVANCEMENTS, wrapper -> {
@@ -179,7 +152,7 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
if (wrapper.passthrough(Type.BOOLEAN)) {
wrapper.passthrough(Type.COMPONENT); // Title
wrapper.passthrough(Type.COMPONENT); // Description
wrapper.write(Type.ITEM1_20_2, wrapper.read(Type.FLAT_VAR_INT_ITEM)); // Icon
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Icon
wrapper.passthrough(Type.VAR_INT); // Frame type
final int flags = wrapper.passthrough(Type.INT); // Flags
if ((flags & 1) != 0) {
@@ -208,7 +181,7 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
byte slot;
do {
slot = wrapper.passthrough(Type.BYTE);
wrapper.write(Type.ITEM1_20_2, wrapper.read(Type.FLAT_VAR_INT_ITEM));
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM)));
} while ((slot & 0xFFFFFF80) != 0);
});
}
@@ -227,11 +200,11 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
final int length = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < length; i++) {
wrapper.passthrough(Type.SHORT); // Slot
wrapper.write(Type.FLAT_VAR_INT_ITEM, wrapper.read(Type.ITEM1_20_2));
wrapper.write(Type.FLAT_VAR_INT_ITEM, handleItemToServer(wrapper.read(Type.ITEM1_20_2)));
}
// Carried item
wrapper.write(Type.FLAT_VAR_INT_ITEM, wrapper.read(Type.ITEM1_20_2));
wrapper.write(Type.FLAT_VAR_INT_ITEM, handleItemToServer(wrapper.read(Type.ITEM1_20_2)));
});
}
});
@@ -239,9 +212,9 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
wrapper.passthrough(Type.VAR_INT); // Container id
final int size = wrapper.passthrough(Type.VAR_INT);
for (int i = 0; i < size; i++) {
wrapper.write(Type.ITEM1_20_2, wrapper.read(Type.FLAT_VAR_INT_ITEM)); // Input
wrapper.write(Type.ITEM1_20_2, wrapper.read(Type.FLAT_VAR_INT_ITEM)); // Output
wrapper.write(Type.ITEM1_20_2, wrapper.read(Type.FLAT_VAR_INT_ITEM)); // Second Item
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Input
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Output
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM))); // Second Item
wrapper.passthrough(Type.BOOLEAN); // Trade disabled
wrapper.passthrough(Type.INT); // Number of tools uses
@@ -257,7 +230,7 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
@Override
public void register() {
map(Type.SHORT); // 0 - Slot
map(Type.ITEM1_20_2, Type.FLAT_VAR_INT_ITEM); // 1 - Clicked Item
handler(wrapper -> wrapper.write(Type.FLAT_VAR_INT_ITEM, handleItemToServer(wrapper.read(Type.ITEM1_20_2)))); // 1 - Clicked Item
}
});
protocol.registerClientbound(ClientboundPackets1_19_4.SPAWN_PARTICLE, new PacketHandlers() {
@@ -280,7 +253,7 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
final int data = wrapper.read(Type.VAR_INT);
wrapper.write(Type.VAR_INT, protocol.getMappingData().getNewBlockStateId(data));
} else if (mappings.isItemParticle(id)) {
wrapper.write(Type.ITEM1_20_2, wrapper.read(Type.FLAT_VAR_INT_ITEM));
wrapper.write(Type.ITEM1_20_2, handleItemToClient(wrapper.read(Type.FLAT_VAR_INT_ITEM)));
}
});
}
@@ -370,6 +343,99 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
}.register(ClientboundPackets1_19_4.DECLARE_RECIPES);
}
@Override
public @Nullable Item handleItemToClient(@Nullable final Item item) {
if (item == null) {
return null;
}
if (item.tag() != null) {
to1_20_2Effects(item);
}
return super.handleItemToClient(item);
}
@Override
public @Nullable Item handleItemToServer(@Nullable final Item item) {
if (item == null) {
return null;
}
if (item.tag() != null) {
to1_20_1Effects(item);
}
return super.handleItemToServer(item);
}
public static void to1_20_2Effects(final Item item) {
final Tag customPotionEffectsTag = item.tag().remove("CustomPotionEffects");
if (customPotionEffectsTag instanceof ListTag) {
final ListTag effectsTag = (ListTag) customPotionEffectsTag;
item.tag().put("custom_potion_effects", customPotionEffectsTag);
for (final Tag tag : effectsTag) {
if (!(tag instanceof CompoundTag)) {
continue;
}
final CompoundTag effectTag = (CompoundTag) tag;
final Tag idTag = effectTag.remove("Id");
if (idTag instanceof NumberTag) {
final String key = PotionEffects.idToKey(((NumberTag) idTag).asInt());
if (key != null) {
effectTag.put("id", new StringTag(key));
}
}
renameTag(effectTag, "Amplifier", "amplifier");
renameTag(effectTag, "Duration", "duration");
renameTag(effectTag, "Ambient", "ambient");
renameTag(effectTag, "ShowParticles", "show_particles");
renameTag(effectTag, "ShowIcon", "show_icon");
renameTag(effectTag, "HiddenEffect", "hidden_effect");
renameTag(effectTag, "FactorCalculationData", "factor_calculation_data");
}
}
}
public static void to1_20_1Effects(final Item item) {
final Tag customPotionEffectsTag = item.tag().remove("custom_potion_effects");
if (customPotionEffectsTag instanceof ListTag) {
final ListTag effectsTag = (ListTag) customPotionEffectsTag;
item.tag().put("CustomPotionEffects", effectsTag);
for (final Tag tag : effectsTag) {
if (!(tag instanceof CompoundTag)) {
continue;
}
final CompoundTag effectTag = (CompoundTag) tag;
final Tag idTag = effectTag.remove("id");
if (idTag instanceof StringTag) {
final int id = PotionEffects.keyToId(((StringTag) idTag).getValue());
effectTag.put("Id", new IntTag(id));
}
renameTag(effectTag, "amplifier", "Amplifier");
renameTag(effectTag, "duration", "Duration");
renameTag(effectTag, "ambient", "Ambient");
renameTag(effectTag, "show_particles", "ShowParticles");
renameTag(effectTag, "show_icon", "ShowIcon");
renameTag(effectTag, "hidden_effect", "HiddenEffect");
renameTag(effectTag, "factor_calculation_data", "FactorCalculationData");
}
}
}
private static void renameTag(final CompoundTag tag, final String entryName, final String toEntryName) {
final Tag entry = tag.remove(entryName);
if (entry != null) {
tag.put(toEntryName, entry);
}
}
private @Nullable CompoundTag handleBlockEntity(@Nullable final CompoundTag tag) {
if (tag == null) {
return null;
@@ -377,17 +443,13 @@ public final class BlockItemPacketRewriter1_20_2 extends ItemRewriter<Clientboun
final IntTag primaryEffect = tag.remove("Primary");
if (primaryEffect != null && primaryEffect.asInt() != 0) {
tag.put("primary_effect", new StringTag(effect(primaryEffect.asInt())));
tag.put("primary_effect", new StringTag(PotionEffects.idToKeyOrLuck(primaryEffect.asInt())));
}
final IntTag secondaryEffect = tag.remove("Secondary");
if (secondaryEffect != null && secondaryEffect.asInt() != 0) {
tag.put("secondary_effect", new StringTag(effect(secondaryEffect.asInt())));
tag.put("secondary_effect", new StringTag(PotionEffects.idToKeyOrLuck(secondaryEffect.asInt())));
}
return tag;
}
private String effect(final int id) {
return id >= 1 && id < POTION_EFFECTS.length ? Key.namespaced(POTION_EFFECTS[id]) : "minecraft:luck";
}
}
@@ -101,6 +101,10 @@ public final class EntityPacketRewriter1_20_2 extends EntityRewriter<Clientbound
final ConfigurationState configurationBridge = wrapper.user().get(ConfigurationState.class);
if (!configurationBridge.setLastDimensionRegistry(dimensionRegistry)) {
// No change, so no need to re-enter the configuration state - just let this one through
final PacketWrapper clientInformationPacket = configurationBridge.clientInformationPacket(wrapper.user());
if (clientInformationPacket != null) {
clientInformationPacket.sendToServer(Protocol1_20_2To1_20.class);
}
return;
}
@@ -22,6 +22,8 @@ import com.viaversion.viaversion.api.connection.StorableObject;
import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.api.protocol.packet.PacketType;
import com.viaversion.viaversion.api.protocol.packet.PacketWrapper;
import com.viaversion.viaversion.api.type.Type;
import com.viaversion.viaversion.protocols.protocol1_19_4to1_19_3.ServerboundPackets1_19_4;
import com.viaversion.viaversion.protocols.protocol1_20_2to1_20.Protocol1_20_2To1_20;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
@@ -37,6 +39,7 @@ public class ConfigurationState implements StorableObject {
private QueuedPacket joinGamePacket;
private boolean queuedJoinGame;
private CompoundTag lastDimensionRegistry;
private ClientInformation clientInformation;
public BridgePhase bridgePhase() {
return bridgePhase;
@@ -62,6 +65,10 @@ public class ConfigurationState implements StorableObject {
return !equals;
}
public void setClientInformation(final ClientInformation clientInformation) {
this.clientInformation = clientInformation;
}
public void addPacketToQueue(final PacketWrapper wrapper, final boolean clientbound) throws Exception {
packetQueue.add(toQueuedPacket(wrapper, clientbound, false));
}
@@ -99,11 +106,17 @@ public class ConfigurationState implements StorableObject {
}
public void sendQueuedPackets(final UserConnection connection) throws Exception {
if (joinGamePacket != null) {
final boolean hasJoinGamePacket = joinGamePacket != null;
if (hasJoinGamePacket) {
packetQueue.add(0, joinGamePacket);
joinGamePacket = null;
}
final PacketWrapper clientInformationPacket = clientInformationPacket(connection);
if (clientInformationPacket != null) {
packetQueue.add(hasJoinGamePacket ? 1 : 0, toQueuedPacket(clientInformationPacket, false, true));
}
final ConfigurationState.QueuedPacket[] queuedPackets = packetQueue.toArray(new ConfigurationState.QueuedPacket[0]);
packetQueue.clear();
@@ -142,6 +155,24 @@ public class ConfigurationState implements StorableObject {
NONE, PROFILE_SENT, CONFIGURATION, REENTERING_CONFIGURATION
}
public @Nullable PacketWrapper clientInformationPacket(final UserConnection connection) {
if (clientInformation == null) {
// Should never be null, but we also shouldn't error
return null;
}
final PacketWrapper settingsPacket = PacketWrapper.create(ServerboundPackets1_19_4.CLIENT_SETTINGS, connection);
settingsPacket.write(Type.STRING, clientInformation.language);
settingsPacket.write(Type.BYTE, clientInformation.viewDistance);
settingsPacket.write(Type.VAR_INT, clientInformation.chatVisibility);
settingsPacket.write(Type.BOOLEAN, clientInformation.showChatColors);
settingsPacket.write(Type.UNSIGNED_BYTE, clientInformation.modelCustomization);
settingsPacket.write(Type.VAR_INT, clientInformation.mainHand);
settingsPacket.write(Type.BOOLEAN, clientInformation.textFiltering);
settingsPacket.write(Type.BOOLEAN, clientInformation.allowListing);
return settingsPacket;
}
public static final class QueuedPacket {
private final ByteBuf buf;
private final boolean clientbound;
@@ -189,4 +220,28 @@ public class ConfigurationState implements StorableObject {
'}';
}
}
public static final class ClientInformation {
private final String language;
private final byte viewDistance;
private final int chatVisibility;
private final boolean showChatColors;
private final short modelCustomization;
private final int mainHand;
private final boolean textFiltering;
private final boolean allowListing;
public ClientInformation(final String language, final byte viewDistance, final int chatVisibility,
final boolean showChatColors, final short modelCustomization, final int mainHand,
final boolean textFiltering, final boolean allowListing) {
this.language = language;
this.viewDistance = viewDistance;
this.chatVisibility = chatVisibility;
this.showChatColors = showChatColors;
this.modelCustomization = modelCustomization;
this.mainHand = mainHand;
this.textFiltering = textFiltering;
this.allowListing = allowListing;
}
}
}
@@ -0,0 +1,83 @@
/*
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
* Copyright (C) 2023 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.protocol1_20_2to1_20.util;
import com.viaversion.viaversion.util.Key;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import org.checkerframework.checker.nullness.qual.Nullable;
public final class PotionEffects {
private static final Object2IntMap<String> KEY_TO_ID = new Object2IntOpenHashMap<>();
private static final String[] POTION_EFFECTS = {
"", // No effect
"speed",
"slowness",
"haste",
"mining_fatigue",
"strength",
"instant_health",
"instant_damage",
"jump_boost",
"nausea",
"regeneration",
"resistance",
"fire_resistance",
"water_breathing",
"invisibility",
"blindness",
"night_vision",
"hunger",
"weakness",
"poison",
"wither",
"health_boost",
"absorption",
"saturation",
"glowing",
"levitation",
"luck",
"unluck",
"slow_falling",
"conduit_power",
"dolphins_grace",
"bad_omen",
"hero_of_the_village",
"darkness"
};
static {
for (int i = 1; i < POTION_EFFECTS.length; i++) {
final String effect = POTION_EFFECTS[i];
KEY_TO_ID.put(effect, i);
}
}
public static @Nullable String idToKey(final int id) {
return id >= 1 && id < POTION_EFFECTS.length ? Key.namespaced(POTION_EFFECTS[id]) : null;
}
public static String idToKeyOrLuck(final int id) {
return id >= 1 && id < POTION_EFFECTS.length ? Key.namespaced(POTION_EFFECTS[id]) : "minecraft:luck";
}
public static int keyToId(final String key) {
return KEY_TO_ID.getInt(Key.stripMinecraftNamespace(key));
}
}
@@ -172,17 +172,19 @@ public final class InventoryPackets extends ItemRewriter<ClientboundPackets1_19_
final ListTag messages = new ListTag(StringTag.class);
for (int i = 1; i < 5; i++) {
final Tag text = tag.get("Text" + i);
final Tag text = tag.remove("Text" + i);
messages.add(text != null ? text : new StringTag(ChatRewriter.emptyComponentString()));
}
frontText.put("messages", messages);
final ListTag filteredMessages = new ListTag(StringTag.class);
for (int i = 1; i < 5; i++) {
final Tag text = tag.get("FilteredText" + i);
filteredMessages.add(text != null ? text : new StringTag(ChatRewriter.emptyComponentString()));
final Tag text = tag.remove("FilteredText" + i);
filteredMessages.add(text != null ? text : messages.get(i - 1));
}
if (!filteredMessages.equals(messages)) {
frontText.put("filtered_messages", filteredMessages);
}
frontText.put("filtered_messages", filteredMessages);
final Tag color = tag.remove("Color");
if (color != null) {
@@ -32,7 +32,7 @@ import java.util.logging.Level;
public class MovementTransmitterProvider implements Provider {
public void sendPlayer(UserConnection userConnection) {
if (userConnection.getProtocolInfo().getState() != State.PLAY || userConnection.getEntityTracker(Protocol1_9To1_8.class).clientEntityId() == -1) {
if (userConnection.getProtocolInfo().getClientState() != State.PLAY || userConnection.getEntityTracker(Protocol1_9To1_8.class).clientEntityId() == -1) {
return;
}
@@ -172,8 +172,8 @@ public class BlockRewriter<C extends ClientboundPacketType> {
public PacketHandler chunkDataHandler1_19(ChunkTypeSupplier chunkTypeSupplier, @Nullable Consumer<BlockEntity> blockEntityHandler) {
return wrapper -> {
final EntityTracker tracker = protocol.getEntityRewriter().tracker(wrapper.user());
Preconditions.checkArgument(tracker.biomesSent() != 0, "Biome count not set");
Preconditions.checkArgument(tracker.currentWorldSectionHeight() != 0, "Section height not set");
Preconditions.checkArgument(tracker.biomesSent() != -1, "Biome count not set");
Preconditions.checkArgument(tracker.currentWorldSectionHeight() != -1, "Section height not set");
final Type<Chunk> chunkType = chunkTypeSupplier.supply(tracker.currentWorldSectionHeight(),
MathUtil.ceilLog2(protocol.getMappingData().getBlockStateMappings().mappedSize()),
MathUtil.ceilLog2(tracker.biomesSent()));
@@ -104,11 +104,10 @@ public abstract class EntityRewriter<C extends ClientboundPacketType, T extends
public void handleMetadata(final int entityId, final List<Metadata> metadataList, final UserConnection connection) {
final TrackedEntity entity = tracker(connection).entity(entityId);
final EntityType type = entity != null ? entity.entityType() : null;
int i = 0; // Count index for fast removal
for (final Metadata metadata : metadataList.toArray(EMPTY_ARRAY)) { // Copy the list to allow mutation
// Call handlers implementing the old handleMetadata
if (!callOldMetaHandler(entityId, type, metadata, metadataList, connection)) {
metadataList.remove(i--);
metadataList.remove(metadata);
continue;
}
@@ -126,22 +125,22 @@ public abstract class EntityRewriter<C extends ClientboundPacketType, T extends
filter.handler().handle(event, metadata);
} catch (final Exception e) {
logException(e, type, metadataList, metadata);
metadataList.remove(i--);
metadataList.remove(metadata);
break;
}
if (event.cancelled()) {
// Remove meta, decrease list index counter, and break current filter loop
metadataList.remove(i--);
// Remove meta, and break current filter loop
metadataList.remove(metadata);
break;
}
}
if (event != null && event.extraMeta() != null) {
final List<Metadata> extraMeta = event != null ? event.extraMeta() : null;
if (extraMeta != null) {
// Finally, add newly created meta
metadataList.addAll(event.extraMeta());
metadataList.addAll(extraMeta);
}
i++;
}
if (entity != null) {
+1 -1
View File
@@ -1,5 +1,5 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=4.8.0
projectVersion=4.8.1
# Smile emoji
mcVersions=1.20.2, 1.20.1, 1.20, 1.19.4, 1.19.3, 1.19.2, 1.19.1, 1.19, 1.18.2, 1.18.1, 1.18, 1.17.1, 1.17, 1.16.5, 1.16.4, 1.16.3, 1.16.2, 1.16.1, 1.16, 1.15.2, 1.15.1, 1.15, 1.14.4, 1.14.3, 1.14.2, 1.14.1, 1.14, 1.13.2, 1.13.1, 1.13, 1.12.2, 1.12.1, 1.12, 1.11.2, 1.11.1, 1.11, 1.10.2, 1.10.1, 1.10, 1.9.4, 1.9.3, 1.9.2, 1.9.1, 1.9, 1.8.9
+52 -49
View File
@@ -46,59 +46,62 @@ publishShadowJar()
val branch = rootProject.branchName()
val baseVersion = project.version as String
val isRelease = !baseVersion.contains('-')
val suffixedVersion = if (isRelease) baseVersion else baseVersion + "+" + System.getenv("GITHUB_RUN_NUMBER")
val changelogContent = if (isRelease) {
"See [GitHub](https://github.com/ViaVersion/ViaVersion) for release notes."
} else {
val commitHash = rootProject.latestCommitHash()
"[$commitHash](https://github.com/ViaVersion/ViaVersion/commit/$commitHash) ${rootProject.latestCommitMessage()}"
}
val isMainBranch = branch == "master"
modrinth {
val mcVersions: List<String> = (property("mcVersions") as String)
.split(",")
.map { it.trim() }
token.set(System.getenv("MODRINTH_TOKEN"))
projectId.set("viaversion")
versionType.set(if (isRelease) "release" else if (isMainBranch) "beta" else "alpha")
versionNumber.set(suffixedVersion)
versionName.set(suffixedVersion)
changelog.set(changelogContent)
uploadFile.set(tasks.shadowJar.flatMap { it.archiveFile })
gameVersions.set(mcVersions)
loaders.add("fabric")
loaders.add("paper")
loaders.add("folia")
loaders.add("velocity")
loaders.add("bungeecord")
loaders.add("sponge")
autoAddDependsOn.set(false)
detectLoaders.set(false)
dependencies {
optional.project("viafabric")
optional.project("viafabricplus")
if (!isRelease || isMainBranch) { // Only publish releases from the main branch
val suffixedVersion = if (isRelease) baseVersion else baseVersion + "+" + System.getenv("GITHUB_RUN_NUMBER")
val changelogContent = if (isRelease) {
"See [GitHub](https://github.com/ViaVersion/ViaVersion) for release notes."
} else {
val commitHash = rootProject.latestCommitHash()
"[$commitHash](https://github.com/ViaVersion/ViaVersion/commit/$commitHash) ${rootProject.latestCommitMessage()}"
}
}
hangarPublish {
publications.register("plugin") {
version.set(suffixedVersion)
id.set("ViaVersion")
channel.set(if (isRelease) "Release" else if (isMainBranch) "Snapshot" else "Alpha")
modrinth {
val mcVersions: List<String> = (property("mcVersions") as String)
.split(",")
.map { it.trim() }
token.set(System.getenv("MODRINTH_TOKEN"))
projectId.set("viaversion")
versionType.set(if (isRelease) "release" else if (isMainBranch) "beta" else "alpha")
versionNumber.set(suffixedVersion)
versionName.set(suffixedVersion)
changelog.set(changelogContent)
apiKey.set(System.getenv("HANGAR_TOKEN"))
platforms {
register(Platforms.PAPER) {
jar.set(tasks.shadowJar.flatMap { it.archiveFile })
platformVersions.set(listOf(property("mcVersionRange") as String))
}
register(Platforms.VELOCITY) {
jar.set(tasks.shadowJar.flatMap { it.archiveFile })
platformVersions.set(listOf(property("velocityVersion") as String))
}
register(Platforms.WATERFALL) {
jar.set(tasks.shadowJar.flatMap { it.archiveFile })
platformVersions.set(listOf(property("waterfallVersion") as String))
uploadFile.set(tasks.shadowJar.flatMap { it.archiveFile })
gameVersions.set(mcVersions)
loaders.add("fabric")
loaders.add("paper")
loaders.add("folia")
loaders.add("velocity")
loaders.add("bungeecord")
loaders.add("sponge")
autoAddDependsOn.set(false)
detectLoaders.set(false)
dependencies {
optional.project("viafabric")
optional.project("viafabricplus")
}
}
hangarPublish {
publications.register("plugin") {
version.set(suffixedVersion)
id.set("ViaVersion")
channel.set(if (isRelease) "Release" else if (isMainBranch) "Snapshot" else "Alpha")
changelog.set(changelogContent)
apiKey.set(System.getenv("HANGAR_TOKEN"))
platforms {
register(Platforms.PAPER) {
jar.set(tasks.shadowJar.flatMap { it.archiveFile })
platformVersions.set(listOf(property("mcVersionRange") as String))
}
register(Platforms.VELOCITY) {
jar.set(tasks.shadowJar.flatMap { it.archiveFile })
platformVersions.set(listOf(property("velocityVersion") as String))
}
register(Platforms.WATERFALL) {
jar.set(tasks.shadowJar.flatMap { it.archiveFile })
platformVersions.set(listOf(property("waterfallVersion") as String))
}
}
}
}