In this step, you serialize the game events to make them sendable through the network.
Keywords: serialization, reflection, mirrors
→ Edit lib/risk.dart
, as follows.
library risk;
import 'dart:math';
import 'dart:convert';
import 'package:risk_engine/risk_engine.dart';
import 'package:observe/observe.dart';
import 'package:morph/morph.dart';
part 'src/map.dart';
part 'src/game.dart';
part 'src/event_codec.dart';
You have added 3 lines:
import 'dart:convert'
: dart:convert provides encoders and decoders for converting between different data representations, including JSON and UTF-8. It particullarly contains a standard Codec class you will implement.import 'package:morph/morph.dart'
: morph is a mirror based serializer and deserializer of Dart objects.part 'src/event_codec.dart'
: your new file containing serialization stuff.
→ Create a new file lib/src/event_codec.dart
, with the following content:
part of risk;
const EVENT = const EventCodec();
/**
* Encodes and decodes Event from/to JSON.
*/
class EventCodec extends Codec<Object, Map> {
final decoder = const EventDecoder();
final encoder = const EventEncoder();
const EventCodec();
}
/**
* Decodes Event from JSON.
*/
class EventDecoder extends Converter<Map, Object> {
const EventDecoder();
Object convert(Map input) {
// TODO implement the conversion from JSON to object
}
}
/**
* Encodes Event to JSON.
*/
class EventEncoder extends Converter<Object, Map> {
const EventEncoder();
Map convert(Object input) {
// TODO implement the conversion from object to JSON
}
}
Key information:
- The
Codec<S, T>
class contains 2 abstract getters (Converter<S, T> get encoder
andConverter<T, S> get decoder
) that need to be defined. - The
const
keyword is used for variables that you want to be compile-time constants. To allowEventCodec
to be a constant you have to define the constructor with theconst
keyword as prefix and to only use const fields.
→ Edit lib/src/event_codec.dart
and try to make test/s8_event_codec_test_single.dart
pass.
The Object to serialize in this test is:
new PlaceArmy()
..playerId = 1
..country = 'alaska'
The converted datas should be:
{
'event': 'PlaceArmy',
'data': {
'playerId': 1,
'country': 'alaska'
}
}
Tips:
- To test the type of an object you can use
if (o is PlaceArmy) {....}
. - Use the cascade operator (
..
) when you want to perform a series of operations on the members of a single object
The manual implementation can be done but is really heavy and long to do due to the large number of events.
There are several packages to perform serialization: morph, dartson or serialization. You will go with morph.
→ Edit lib/src/event_codec.dart
with the following.
part of risk;
const EVENT = const EventCodec();
final _MORPH = new Morph();
/**
* Encodes and decodes Event from/to JSON.
*/
class EventCodec extends Codec<Object, Map> {
final decoder = const EventDecoder();
final encoder = const EventEncoder();
const EventCodec();
}
/**
* Decodes Event from JSON.
*/
class EventDecoder extends Converter<Map, Object> {
final _classes = const {
"JoinGame": JoinGame,
"StartGame": StartGame,
"PlaceArmy": PlaceArmy,
"Attack": Attack,
"EndAttack": EndAttack,
"MoveArmy": MoveArmy,
"EndTurn": EndTurn,
"Welcome": Welcome,
"PlayerJoined": PlayerJoined,
"GameStarted": GameStarted,
"ArmyPlaced": ArmyPlaced,
"NextPlayer": NextPlayer,
"SetupEnded": SetupEnded,
"NextStep": NextStep,
"BattleEnded": BattleEnded,
"ArmyMoved": ArmyMoved,
"PlayerLost": PlayerLost,
"PlayerWon": PlayerWon,
};
const EventDecoder();
Object convert(Map input) {
var event = input == null ? null : input['event'];
var type = _classes[event];
return type == null ? null : _MORPH.deserialize(type, input['data']);
}
}
/**
* Encodes Event to JSON.
*/
class EventEncoder extends Converter<Object, Map> {
const EventEncoder();
Map convert(Object input) => {
'event': '${input.runtimeType}',
'data': _MORPH.serialize(input)
};
}
Key information:
- morph (like other packages mentionned above) uses reflection provided by dart:mirrors to make the conversion.
→ Run test/s8_event_codec_test.dart
to ensure the conversion works.
You now have a EventCodec
to encode/decode Object
to/from JSON data structure.
The dart:convert
library nativelly provides several codecs. One of them is the JSON codec.
To create a new codec Codec<Object,String>
you only have to compose the one you just created and the JSON
provided by dart:convert
with the fuse method:
final EVENT_JSON_CODEC = EVENT.fuse(JSON);
This actually does not work due to an incompatibility between the generic types we used but you see how powerful can be compositions.
Check your code against the files in s8_serialization (diff).