software-challenge / backend

Server, Client und Spiel-Plugins der Software-Challenge Germany
https://www.software-challenge.de
11 stars 10 forks source link

NullPointerException: GameState.deployedPieces ist null #313

Closed 0xhtml closed 3 years ago

0xhtml commented 3 years ago

Bei mir tritt folgender Fehler auf:

java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.collections.MapsKt__MapsKt.getValue, parameter $this$getValue
    at kotlin.collections.MapsKt__MapsKt.getValue(Maps.kt)
    at sc.plugin2021.util.GameRuleLogic.performSetMove(GameRuleLogic.kt:114)
    at sc.plugin2021.util.GameRuleLogic.performMove(GameRuleLogic.kt:51)
    at sc.player2021.logic.TestLogic.onRequestAction(Logic.java:25)
    at sc.plugin2021.AbstractClient.onRoomMessage(AbstractClient.kt:73)
    at sc.networking.clients.LobbyClient.onRoomMessage(LobbyClient.java:216)
    at sc.networking.clients.LobbyClient.onObject(LobbyClient.java:87)
    at sc.networking.clients.XStreamClient.receiveThread(XStreamClient.java:110)
    at sc.networking.clients.XStreamClient$1.run(XStreamClient.java:74)
    at java.base/java.lang.Thread.run(Thread.java:834)

Ich habe den Fehler etwas debuged und konnte feststellen, dass der Fehler entsteht, weil gameState.deployedPieces null ist. Ich habe eine minimale Logic erstellt, mit der der Fehler reproduziert werden kann. Dazu einfach die SimpleClient-Source nehmen und die Logic ersetzen.

package sc.player2021.logic;

import sc.framework.plugins.Player;
import sc.player2021.Starter;
import sc.plugin2021.GameState;
import sc.plugin2021.IGameHandler;
import sc.plugin2021.Move;
import sc.plugin2021.Team;
import sc.plugin2021.util.GameRuleLogic;
import sc.shared.GameResult;

public class Logic implements IGameHandler {
    private final Starter client;
    private GameState gameState;

    public Logic(Starter client) {
        this.client = client;
    }

    public void gameEnded(GameResult data, Team color, String errorMessage) {}

    public void onRequestAction() 
        GameRuleLogic.performMove(gameState, GameRuleLogic.getPossibleMoves(gameState).iterator().next());
    }

    public void onUpdate(Player player, Player otherPlayer) {}

    public void onUpdate(GameState gameState) {
        this.gameState = gameState;
    }

    public void sendAction(Move move) {}
}
anarchuser commented 3 years ago

Danke für die ausführlichen Informationen. Das Problem ist recht simple: Um Bandweite zu sparen, wird die Liste deployedPieces nicht serialisiert. Demnach ist diese Liste bei GameStates von Clients immer null.

anarchuser commented 3 years ago

In der Dokumentation ist das nicht ersichtlich; ich werde zusehen, dass das entsprechend kenntlich wird.

Alle wichtigen Informationen sollten aber aus undeployedPieceShapes, board und lastMoveMono des GameStates ersichtlich werden. Wenn dem nicht so ist, bitte ich um Rückmeldung

0xhtml commented 3 years ago

Ok. Es funktioniert nur die GameRuleLogic.performMove-Methode nicht ohne deployedPieces. Die Methode könnte auch ohne deployedPieces funktionieren, aber die Methode greift auf deployedPieces zu, ohne zu kontrollieren, ob deployedPieces null ist.

anarchuser commented 3 years ago

Oh, ja, in der Tat - sie versucht, den Stein nach erfolgreichem Zug zu dieser Liste hinzuzufügen, ohne zu prüfen, ob die Liste existiert.

Danke für den Hinweis!

anarchuser commented 3 years ago

Bisher wurde performMove immer nur vom Server ausgeführt, der über diese Liste verfügt. Natürlich macht es aber auch für Clients Sinn, das entsprechend zu Simulieren. Ich werde baldmöglichst für eine Lösung sorgen