Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions forge-game/src/main/java/forge/game/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -694,8 +694,17 @@ public Card findByView(CardView view) {
if (ZoneType.Stack.equals(view.getZone())) {
visit.visitAll(getStackZone());
} else if (view.getController() != null && view.getZone() != null) {
visit.visitAll(getPlayer(view.getController()).getZone(view.getZone()));
} else { // fallback if view doesn't has controller or zone set for some reason
Player p = getPlayer(view.getController());
if (p != null) {
visit.visitAll(p.getZone(view.getZone()));
}
} else {
forEachCardInGame(visit);
}
// Zone-specific search may miss if the view has stale zone info
// (e.g. IdRef resolved from a tracker that wasn't updated after a
// zone change). Fall back to global search.
if (visit.getFound() == null) {
Comment thread
tool4ever marked this conversation as resolved.
forEachCardInGame(visit);
}
return visit.getFound();
Expand Down
2 changes: 1 addition & 1 deletion forge-game/src/main/java/forge/game/phase/Phase.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public Phase(PhaseType type) {

protected final List<GameCommand> at = Lists.newArrayList();
private final List<GameCommand> until = Lists.newArrayList();
private final Multimap<Player, GameCommand> untilMap = MultimapBuilder.hashKeys().arrayListValues().build();;
private final Multimap<Player, GameCommand> untilMap = MultimapBuilder.hashKeys().arrayListValues().build();
private final Multimap<Player, GameCommand> untilEndMap = MultimapBuilder.hashKeys().arrayListValues().build();
private final Multimap<Player, GameCommand> registerMap = MultimapBuilder.hashKeys().arrayListValues().build();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package forge.gamemodes.net;

import forge.trackable.Tracker;
import io.netty.handler.codec.serialization.ClassResolver;

import java.io.*;

public class CObjectInputStream extends ObjectInputStream {
private final ClassResolver classResolver;
private final Tracker tracker;

CObjectInputStream(InputStream in, ClassResolver classResolver) throws IOException {
CObjectInputStream(InputStream in, ClassResolver classResolver, Tracker tracker) throws IOException {
super(in);
this.classResolver = classResolver;
this.tracker = tracker;
if (tracker != null) {
enableResolveObject(true);
}
}

@Override
Expand All @@ -35,4 +41,9 @@ protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, Clas
}
return clazz;
}

@Override
protected Object resolveObject(Object obj) throws IOException {
return TrackableSerializer.resolve(obj, tracker);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@
public class CObjectOutputStream extends ObjectOutputStream {
static final int TYPE_THIN_DESCRIPTOR = 1;

CObjectOutputStream(OutputStream out) throws IOException {
CObjectOutputStream(OutputStream out, boolean replaceTrackables) throws IOException {
super(out);
if (replaceTrackables) {
enableReplaceObject(true);
}
}

@Override
Expand All @@ -18,4 +21,9 @@ protected void writeClassDescriptor(ObjectStreamClass desc) throws IOException {
write(TYPE_THIN_DESCRIPTOR);
writeUTF(desc.getName());
}

@Override
protected Object replaceObject(Object obj) throws IOException {
return TrackableSerializer.replace(obj, null);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package forge.gamemodes.net;

import forge.gui.GuiBase;
import forge.trackable.Tracker;
import forge.util.IHasForgeLog;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;
Expand All @@ -15,16 +16,17 @@
public class CompatibleObjectDecoder extends LengthFieldBasedFrameDecoder implements IHasForgeLog {

private final ClassResolver classResolver;

public CompatibleObjectDecoder(ClassResolver classResolver) {
this(1048576, classResolver);
}
private volatile Tracker tracker;

public CompatibleObjectDecoder(int maxObjectSize, ClassResolver classResolver) {
super(maxObjectSize, 0, 4, 0, 4);
this.classResolver = classResolver;
}

public void setTracker(Tracker tracker) {
this.tracker = tracker;
}

@Override
protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
int frameStart = in.readerIndex();
Expand All @@ -34,9 +36,15 @@ protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception
}
int frameSize = frame.readableBytes();
long startMs = System.currentTimeMillis();
ObjectInputStream ois = GuiBase.hasPropertyConfig() ?
new ObjectInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true))):
new CObjectInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true)),this.classResolver);

ObjectInputStream ois;
if (GuiBase.hasPropertyConfig()) {
ois = tracker != null
? new TrackableSerializer.ResolvingInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true)), tracker)
: new ObjectInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true)));
} else {
ois = new CObjectInputStream(new LZ4BlockInputStream(new ByteBufInputStream(frame, true)), this.classResolver, tracker);
}

Object var5 = null;
try {
Expand All @@ -55,4 +63,5 @@ protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception

return var5;
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package forge.gamemodes.net;

import forge.gamemodes.net.event.GuiGameEvent;
import forge.gui.GuiBase;
import forge.trackable.Tracker;
import forge.util.IHasForgeLog;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
Expand All @@ -14,22 +16,34 @@
public class CompatibleObjectEncoder extends MessageToByteEncoder<Serializable> implements IHasForgeLog {

private static final byte[] LENGTH_PLACEHOLDER = new byte[4];

private final NetworkByteTracker byteTracker;
private volatile Tracker tracker;

public CompatibleObjectEncoder(NetworkByteTracker byteTracker) {
this.byteTracker = byteTracker;
}

public void setTracker(Tracker tracker) {
this.tracker = tracker;
}

@Override
protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
int startIdx = out.writerIndex();
ByteBufOutputStream bout = new ByteBufOutputStream(out);
ObjectOutputStream oout = null;

boolean replace = shouldReplaceTrackables(msg);

try {
bout.write(LENGTH_PLACEHOLDER);
oout = GuiBase.hasPropertyConfig() ? new ObjectOutputStream(new LZ4BlockOutputStream(bout)) : new CObjectOutputStream(new LZ4BlockOutputStream(bout));
if (GuiBase.hasPropertyConfig()) {
oout = replace
? new TrackableSerializer.ReplacingOutputStream(new LZ4BlockOutputStream(bout), tracker)
: new ObjectOutputStream(new LZ4BlockOutputStream(bout));
} else {
oout = new CObjectOutputStream(new LZ4BlockOutputStream(bout), replace);
}
oout.writeObject(msg);
oout.flush();
} finally {
Expand All @@ -53,4 +67,24 @@ protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out)
netLog.info("Encoded {} bytes (compressed) for {}", msgSize, msg.getClass().getSimpleName());
}
}

/**
* Determines whether TrackableObject references should be replaced with
* IdRef markers for this message. Excludes only:
* - setGameView/openView: carry full state to populate the client's Tracker
*
* applyDelta is NOT excluded: its property maps already use Integer IDs
* (via DeltaSyncManager.toNetworkValue), and bundled events are wrapped
* by TrackableSerializer.wrapEvents before entering the packet, so no
* raw TrackableObjects remain in the serialization graph.
*/
private static boolean shouldReplaceTrackables(Serializable msg) {
if (msg instanceof GuiGameEvent event) {
ProtocolMethod method = event.getMethod();
return method != ProtocolMethod.setGameView
&& method != ProtocolMethod.openView;
}
return true;
}

}
18 changes: 9 additions & 9 deletions forge-gui/src/main/java/forge/gamemodes/net/DeltaPacket.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public final class DeltaPacket implements NetEvent {
private final Map<Integer, Map<TrackableProperty, Object>> newObjects;
private final int checksum;
private final int[] checksumProperties;
private List<Object> proxiedEvents;
private List<Object> events;

public static final int TYPE_CARD_VIEW = 0;
public static final int TYPE_PLAYER_VIEW = 1;
Expand Down Expand Up @@ -114,9 +114,9 @@ public CombatData(List<List<Integer>> bandAttackerIds, List<int[]> bandDefenderR
}

/** Create an events-only DeltaPacket with no state deltas (seq=-1 means no ack needed). */
public static DeltaPacket eventsOnly(List<Object> proxiedEvents) {
public static DeltaPacket eventsOnly(List<Object> events) {
DeltaPacket packet = new DeltaPacket(-1L, null, null, 0, null);
packet.setProxiedEvents(proxiedEvents);
packet.setEvents(events);
return packet;
}

Expand Down Expand Up @@ -159,19 +159,19 @@ public boolean isEmpty() {
return objectDeltas.isEmpty() && newObjects.isEmpty() && !hasEvents() && !hasChecksum();
}

public void setProxiedEvents(List<Object> events) {
this.proxiedEvents = events;
public void setEvents(List<Object> events) {
this.events = events;
}

public List<Object> getProxiedEvents() {
return proxiedEvents;
public List<Object> getEvents() {
return events;
}

public boolean hasEvents() {
return proxiedEvents != null && !proxiedEvents.isEmpty();
return events != null && !events.isEmpty();
}

/** Return a shallow copy without proxied events, for state-only size measurement. */
/** Return a shallow copy without events, for state-only size measurement. */
public DeltaPacket withoutEvents() {
return new DeltaPacket(sequenceNumber, objectDeltas, newObjects, checksum, checksumProperties);
}
Expand Down
Loading
Loading