Skip to content

Commit f42a837

Browse files
committed
Implement progress import validation
1 parent 5c43ef2 commit f42a837

File tree

5 files changed

+59
-14
lines changed

5 files changed

+59
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ If two cards are the same and one card is different in any feature, then it is n
2424

2525
## License
2626

27-
Copyright © 2023 Matthias Elbel & Patrick Zedler. All rights reserved.
27+
Copyright © 2024 Matthias Elbel & Patrick Zedler. All rights reserved.
2828

2929
[GNU General Public License version 3](https://www.gnu.org/licenses/gpl.txt)
3030

src/main/scala/de/htwg/se/set/controller/controller/base/Command.scala

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ import de.htwg.se.set.model.GameMode.{GAME_END, IN_GAME, SETTINGS}
55
import de.htwg.se.set.model.game.base.{Deck, Player, Triplet}
66
import de.htwg.se.set.model.{ICard, ITriplet}
77
import de.htwg.se.set.util.PrintUtil
8-
import play.api.libs.json.JsValue
8+
import play.api.libs.json.{JsValue, Json}
99

10-
import scala.xml.Node
10+
import scala.xml.{Node, Utility}
1111

1212
private class Command(controller: IController) extends ICommand(controller):
1313

@@ -71,14 +71,19 @@ case class AddColumnCommand(controller: IController) extends Command(controller)
7171
val cardsAdded = controller.game.deck.tableCards(
7272
controller.game.columns, controller.game.tableCards, controller.game.playersCards
7373
)
74-
if cardsAdded.length > controller.game.tableCards.length then
74+
val cardsAvailable = cardsAdded.length > controller.game.tableCards.length
75+
val setsAvailable = controller.game.deck.findSets(controller.game.tableCards).nonEmpty
76+
if cardsAvailable && controller.game.columns <= 6 then
7577
val msg = "One column of cards added to the table."
7678
println(msg)
7779
controller.setMessage(msg)
7880
controller.setTableCards(cardsAdded)
7981
controller.changeState(SelectPlayerState(controller))
80-
else if controller.game.deck.findSets(controller.game.tableCards).nonEmpty then
81-
val msg = "No more cards left, but there still is at least one SET to be found!"
82+
else if setsAvailable then
83+
val msg = if cardsAvailable && controller.game.columns > 6 then
84+
"Maximum number of columns reached, but there still is at least one SET to be found!"
85+
else
86+
"No more cards left, but there still is at least one SET to be found!"
8287
println(PrintUtil.red(msg + "\n"))
8388
controller.setMessage(msg)
8489
controller.removeColumn()
@@ -169,8 +174,20 @@ case class ExitCommand(controller: IController) extends Command(controller):
169174

170175
case class LoadXmlCommand(controller: IController, node: Node) extends Command(controller):
171176

172-
override def execute(): Unit = controller.restoreSnapshot(Snapshot.fromXml(node, controller))
177+
override def execute(): Unit =
178+
val hash = (node \ "hash").text
179+
val xmlSnapshot = (node \ "snapshot").head
180+
if hash == Snapshot.hash(Utility.trim(xmlSnapshot).toString) then
181+
controller.restoreSnapshot(Snapshot.fromXml(xmlSnapshot, controller))
182+
else
183+
println(PrintUtil.red("Invalid XML progress file!"))
173184

174185
case class LoadJsonCommand(controller: IController, json: JsValue) extends Command(controller):
175186

176-
override def execute(): Unit = controller.restoreSnapshot(Snapshot.fromJson(json, controller))
187+
override def execute(): Unit =
188+
val hash = (json \ "hash").get.as[String]
189+
val jsonSnapshot = (json \ "snapshot").get
190+
if hash == Snapshot.hash(Json.stringify(jsonSnapshot)) then
191+
controller.restoreSnapshot(Snapshot.fromJson(jsonSnapshot, controller))
192+
else
193+
println(PrintUtil.red("Invalid JSON progress file!"))

src/main/scala/de/htwg/se/set/controller/controller/base/Controller.scala

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import play.api.libs.json.Json
77

88
import java.nio.charset.StandardCharsets
99
import java.nio.file.{Files, Paths}
10+
import java.security.MessageDigest
1011
import java.time.LocalDateTime
1112
import java.time.format.DateTimeFormatter
12-
import scala.xml.{PrettyPrinter, XML}
13+
import scala.xml.{Elem, PrettyPrinter, Utility, XML}
1314

1415
case class Controller @Inject() (var settings: ISettings, var game: IGame) extends IController:
1516

@@ -53,9 +54,28 @@ case class Controller @Inject() (var settings: ISettings, var game: IGame) exten
5354
override def save(): Unit =
5455
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm")
5556
val date = formatter.format(LocalDateTime.now)
56-
val name = s"set_$date"
57-
XML.save(name + ".xml", XML.loadString(PrettyPrinter(100, 2).format(snapshot.toXml)), "UTF-8", true, null)
58-
Files.write(Paths.get(name + ".json"), Json.prettyPrint(snapshot.toJson).getBytes(StandardCharsets.UTF_8))
57+
val name = s"progress_$date"
58+
saveXml(name)
59+
saveJson(name)
60+
61+
private def saveXml(name: String): Unit =
62+
val xmlSnapshot = snapshot.toXml
63+
val hash = Snapshot.hash(Utility.trim(XML.loadString(xmlSnapshot.toString)).toString)
64+
val xml =
65+
<progress>
66+
<hash>{hash}</hash>
67+
{xmlSnapshot}
68+
</progress>
69+
XML.save(name + ".xml", XML.loadString(PrettyPrinter(100, 2).format(xml)), "UTF-8", true, null)
70+
71+
private def saveJson(name: String): Unit =
72+
val jsonSnapshot = snapshot.toJson
73+
val hash = Snapshot.hash(Json.stringify(jsonSnapshot))
74+
val json = Json.obj(
75+
"hash" -> hash,
76+
"snapshot" -> jsonSnapshot
77+
)
78+
Files.write(Paths.get(name + ".json"), Json.prettyPrint(json).getBytes(StandardCharsets.UTF_8))
5979

6080
override def canUndo: Boolean = undoManager.canUndo
6181

src/main/scala/de/htwg/se/set/controller/controller/base/Snapshot.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import de.htwg.se.set.model.settings.base.Settings
66
import de.htwg.se.set.model.{IGame, ISettings}
77
import play.api.libs.json.{JsValue, Json}
88

9+
import java.security.MessageDigest
910
import scala.xml.{Elem, Node}
1011

1112
case class Snapshot(settings: ISettings, game: IGame, state: IState) extends ISnapshot:
@@ -47,4 +48,9 @@ object Snapshot:
4748
case "GameState" => GameState(controller)
4849
case "GameEndState" => GameEndState(controller)
4950
case _ => throw IllegalArgumentException("Invalid state")
50-
Snapshot(settings, game, state)
51+
Snapshot(settings, game, state)
52+
53+
def hash(input: String): String =
54+
val md = MessageDigest.getInstance("SHA-256")
55+
val hashBytes = md.digest(input.getBytes("UTF-8"))
56+
hashBytes.map("%02x".format(_)).mkString

src/main/scala/de/htwg/se/set/view/panel/MenuPanel.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,9 @@ case class MenuPanel(controller: IController) extends BoxPanel(Orientation.Horiz
9494
def update(): Unit =
9595
undoButton.enabled = controller.canUndo
9696
redoButton.enabled = controller.canRedo
97-
addButton.enabled = controller.game.selectedPlayer.isEmpty
97+
val setsAvailable = controller.game.deck.findSets(controller.game.tableCards).nonEmpty
98+
addButton.enabled = controller.game.selectedPlayer.isEmpty &&
99+
(controller.game.columns < 6 || (controller.game.columns >= 6 && !setsAvailable))
98100
addButton.visible = controller.settings.mode == IN_GAME && !controller.settings.singlePlayer
99101
exitButton.visible = controller.settings.mode == IN_GAME
100102

0 commit comments

Comments
 (0)