Skip to content

Keyword: add View Records for Serializing#10651

Draft
Hanmac wants to merge 8 commits into
masterfrom
keywordView
Draft

Keyword: add View Records for Serializing#10651
Hanmac wants to merge 8 commits into
masterfrom
keywordView

Conversation

@Hanmac
Copy link
Copy Markdown
Contributor

@Hanmac Hanmac commented May 10, 2026

Closes #9918

@MostCromulent can you help me to make this KeywordCollectionView into a Trackable Property replacing
TrackableProperty.Keywords and TrackableProperty.KeywordKey?

we also could remove many of the other Trackable Properties like HasDeathtouch

i made KeywordView into an Interface and not the record directly so it can be extended with Cost and Amount stuff later if needed

@MostCromulent
Copy link
Copy Markdown
Contributor

MostCromulent commented May 10, 2026

@Hanmac @tool4ever here's AI review of how to get this into serializable form: keyword-view-architecture.md

let me know how you want to proceed once happy with an architecture - I could have AI implement and then open a PR against this PR before this gets merged?

@MostCromulent
Copy link
Copy Markdown
Contributor

Looking at that architecture doc again: I'm pretty sure AI is wrong suggesting any future change to support hover tooltips (if components are not included now) would require a new record type rather than just extending DefaultKeywordView - the record is only serialized in the context of a single online game, so as long as both players use same version of Forge (which we assume anyway) it should be fine to expand the existing record in a future PR.

TLDR: I don't think it's necessary to include the forward-compat stuff now, can be added later.

@Hanmac
Copy link
Copy Markdown
Contributor Author

Hanmac commented May 11, 2026

KeywordCollectionView might not need to be serializable itself, but could use some TrackablePropertyType of some kind.

i need to experiment with it

i made KeywordView into an Interface because i don't know if we need the other variants (Cost/Amount) for GUI when the Title already calculated and rendered them.

@Hanmac
Copy link
Copy Markdown
Contributor Author

Hanmac commented May 12, 2026

@MostCromulent with #9642 removing the serialize/deserialize methods from TrackableTypes,
how and where exactly are the objects serialized/deserialized for network?

I don't really want to index the multimap like this:

    private final List<KeywordView> list;     // canonical, serializes natively
    private transient Multimap<Keyword, KeywordView> indexCache;

@MostCromulent
Copy link
Copy Markdown
Contributor

KeywordCollectionView should be able to implement Serializable directly without the list+transient cache.

The Multimap that MultimapBuilder.hashKeys().linkedHashSetValues().build() returns is a serializable implementation (a CustomSetMultimap under the hood, with serializable map and value-set suppliers), and since KeywordView is an interface its impls would just need to be Serializable too — DefaultKeywordView already is, since records implement Serializable automatically when declared to. Then a TrackableType<KeywordCollectionView> with a getDefaultValue() and a new TrackableProperty.Keywords pointing at it should be enough.

Background on how serialization works now: it goes through Java's native ObjectOutputStream/ObjectInputStream, compressed with LZ4, via CompatibleObjectEncoderCompatibleObjectDecoder. TrackableObject is Serializable and its props EnumMap gets serialized natively, so whatever value sits in props for a given TrackableProperty just needs to be Serializable too. The serialize/deserialize methods that were on TrackableType were never wired into this path — they wrote to a delimited text file via the old forge.trackable.TrackableSerializer, and most were //TODO stubs.

The one hook in the encoder pipeline is forge.gamemodes.net.TrackableSerializer.ReplacingOutputStream, which swaps top-level CardView/PlayerView references for IdRef(typeTag, id) markers that the receiver resolves via its Tracker. That's specifically to avoid re-sending whole tracked objects when only an ID is needed, so KeywordCollectionView wouldn't go through it (it's a value type, not a TrackableObject).

@Hanmac
Copy link
Copy Markdown
Contributor Author

Hanmac commented May 13, 2026

Hm, I need to debug what I did wrong

src/main/java/forge/game/card/CounterKeywordType.java:[68,36] cannot find symbol
Error:    symbol:   variable type
Error:    location: class forge.game.card.CounterKeywordType

There is no "type" anymore?

@Hanmac
Copy link
Copy Markdown
Contributor Author

Hanmac commented May 13, 2026

@claude try to fix "cannot find symbol variable type" in "forge.game.card.CounterKeywordType"

(no help from the AI for me)

@Hanmac

This comment was marked as resolved.

@MostCromulent
Copy link
Copy Markdown
Contributor

MostCromulent commented May 13, 2026

Sorry @Hanmac, I was just about to comment on the type fix but you pushed your commit before I clicked send! Claude identified exactly the same fix you've just implemented.

Something else Claude spotted along the way:

forge-game/src/main/java/forge/game/player/PlayerView.java:541 is a UI regression:

final String keywords = Lang.joinHomogenous(getKeywords());

getKeywords() used to return List<String> — joining produced "flying, trample, and lifelink". After this PR it returns KeywordCollectionView (Iterable<KeywordView>), and Lang.joinHomogenous calls obj.toString() on each element. KeywordView is an interface with no toString contract, and DefaultKeywordView is a record, so the auto-generated record toString takes over — the player-details tooltip ends up displaying DefaultKeywordView[original=Flying, keyword=FLYING, title=Flying, reminderText=...] and DefaultKeywordView[...] instead of the keyword names.

Smallest fix is at the call site — pass an explicit accessor so the join uses the title:

final String keywords = Lang.joinHomogenous(getKeywords(), KeywordView::title);

(Overriding toString() on DefaultKeywordView to return title would also work, but each future KeywordView impl — the Cost/Amount variants the interface was designed to support — would have to remember to do the same. The call-site fix carries over automatically.)

@Hanmac
Copy link
Copy Markdown
Contributor Author

Hanmac commented May 13, 2026

I was looking for type() or .type and didn't see the single (type)

i will look into the PlayerView thing and what the best case would be

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

KeywordView Records

2 participants