Skip to content

Commit

Permalink
Add basic server, fix client event sending
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDoctorDE committed Aug 25, 2024
1 parent 1c3e90f commit 14925b8
Show file tree
Hide file tree
Showing 8 changed files with 116 additions and 27 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/dart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
run: |
flutter analyze --fatal-infos
- name: Run build_runner
if: matrix.projects != 'tools'
if: matrix.projects == 'api' || matrix.projects == 'app'
run: dart run build_runner build --delete-conflicting-outputs
- name: Test for git changes
run: git diff --exit-code
Expand Down
31 changes: 26 additions & 5 deletions app/lib/bloc/multiplayer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,15 @@ final class MultiplayerConnectingState extends MultiplayerState
@MappableClass()
final class MultiplayerDisconnectedState extends MultiplayerState
with MultiplayerDisconnectedStateMappable {
final MultiplayerConnectedState? oldState;
final Object? error;

MultiplayerDisconnectedState([this.error]);
MultiplayerDisconnectedState({
this.error,
this.oldState,
});

bool get canReconnect => oldState != null;
}

@MappableClass()
Expand Down Expand Up @@ -144,12 +150,13 @@ class MultiplayerCubit extends Cubit<MultiplayerState> {
port: add.length <= 1 ? kDefaultPort : int.parse(add[1]),
));
final state = await _addNetworker(client);
client.onClosed.listen((_) => emit(MultiplayerDisconnectedState()),
onError: (e) => emit(MultiplayerDisconnectedState(e)));
client.onClosed.listen((_) {
if (!isClosed) emit(MultiplayerDisconnectedState(oldState: state));
}, onError: (e) => emit(MultiplayerDisconnectedState(error: e)));
await client.init();
emit(state);
} catch (e) {
emit(MultiplayerDisconnectedState(e));
emit(MultiplayerDisconnectedState(error: e));
}
}

Expand All @@ -161,7 +168,7 @@ class MultiplayerCubit extends Cubit<MultiplayerState> {
await server.init();
emit(state);
} catch (e) {
emit(MultiplayerDisconnectedState(e));
emit(MultiplayerDisconnectedState(error: e));
}
}

Expand All @@ -178,4 +185,18 @@ class MultiplayerCubit extends Cubit<MultiplayerState> {
if (state is! MultiplayerConnectedState) return;
state.pipe.sendMessage(event, channel);
}

Future<void> reconnect() async {
try {
final state = this.state;
if (state is! MultiplayerDisconnectedState) return;
final oldState = state.oldState;
if (oldState == null) return;
emit(MultiplayerConnectingState());
await oldState.networker.init();
emit(oldState);
} catch (e) {
emit(MultiplayerDisconnectedState(error: e));
}
}
}
30 changes: 25 additions & 5 deletions app/lib/bloc/multiplayer.mapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ class MultiplayerDisconnectedStateMapper
MapperContainer.globals
.use(_instance = MultiplayerDisconnectedStateMapper._());
MultiplayerStateMapper.ensureInitialized();
MultiplayerConnectedStateMapper.ensureInitialized();
}
return _instance!;
}
Expand All @@ -296,14 +297,21 @@ class MultiplayerDisconnectedStateMapper
static Object? _$error(MultiplayerDisconnectedState v) => v.error;
static const Field<MultiplayerDisconnectedState, Object> _f$error =
Field('error', _$error, opt: true);
static MultiplayerConnectedState? _$oldState(
MultiplayerDisconnectedState v) =>
v.oldState;
static const Field<MultiplayerDisconnectedState, MultiplayerConnectedState>
_f$oldState = Field('oldState', _$oldState, opt: true);

@override
final MappableFields<MultiplayerDisconnectedState> fields = const {
#error: _f$error,
#oldState: _f$oldState,
};

static MultiplayerDisconnectedState _instantiate(DecodingData data) {
return MultiplayerDisconnectedState(data.dec(_f$error));
return MultiplayerDisconnectedState(
error: data.dec(_f$error), oldState: data.dec(_f$oldState));
}

@override
Expand Down Expand Up @@ -365,8 +373,10 @@ abstract class MultiplayerDisconnectedStateCopyWith<
$R,
$In extends MultiplayerDisconnectedState,
$Out> implements MultiplayerStateCopyWith<$R, $In, $Out> {
MultiplayerConnectedStateCopyWith<$R, MultiplayerConnectedState,
MultiplayerConnectedState>? get oldState;
@override
$R call({Object? error});
$R call({Object? error, MultiplayerConnectedState? oldState});
MultiplayerDisconnectedStateCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(
Then<$Out2, $R2> t);
}
Expand All @@ -383,11 +393,21 @@ class _MultiplayerDisconnectedStateCopyWithImpl<$R, $Out>
late final ClassMapperBase<MultiplayerDisconnectedState> $mapper =
MultiplayerDisconnectedStateMapper.ensureInitialized();
@override
$R call({Object? error = $none}) =>
$apply(FieldCopyWithData({if (error != $none) #error: error}));
MultiplayerConnectedStateCopyWith<$R, MultiplayerConnectedState,
MultiplayerConnectedState>?
get oldState =>
$value.oldState?.copyWith.$chain((v) => call(oldState: v));
@override
$R call({Object? error = $none, Object? oldState = $none}) =>
$apply(FieldCopyWithData({
if (error != $none) #error: error,
if (oldState != $none) #oldState: oldState
}));
@override
MultiplayerDisconnectedState $make(CopyWithData data) =>
MultiplayerDisconnectedState(data.get(#error, or: $value.error));
MultiplayerDisconnectedState(
error: data.get(#error, or: $value.error),
oldState: data.get(#oldState, or: $value.oldState));

@override
MultiplayerDisconnectedStateCopyWith<$R2, MultiplayerDisconnectedState, $Out2>
Expand Down
4 changes: 2 additions & 2 deletions app/lib/bloc/world/bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,8 +103,6 @@ class WorldBloc extends Bloc<PlayableWorldEvent, ClientWorldState> {
switch (event) {
case LocalWorldEvent e:
add(e);
case ServerWorldEvent e:
add(e);
case ClientWorldEvent e:
final multiplayer = state.multiplayer;
if (multiplayer.isConnected) {
Expand All @@ -116,6 +114,8 @@ class WorldBloc extends Bloc<PlayableWorldEvent, ClientWorldState> {
allowServerEvents: true);
if (event != null) add(event.$1);
}
case ServerWorldEvent e:
add(e);
}
}
}
18 changes: 11 additions & 7 deletions app/lib/pages/game/page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,17 @@ class GamePage extends StatefulWidget {
typedef Blocs = (MultiplayerCubit, WorldBloc);

class _GamePageState extends State<GamePage> {
late final Future<Blocs> _bloc;
Future<Blocs>? _bloc;
final ContextMenuController _contextMenuController = ContextMenuController();

@override
void initState() {
super.initState();
_bloc = _loadTable();
WidgetsBinding.instance.addPostFrameCallback((_) {
setState(() {
_bloc = _loadTable();
});
});
}

Blocs _initBloc([QuokkaData? data]) {
Expand Down Expand Up @@ -81,15 +85,15 @@ class _GamePageState extends State<GamePage> {

final nextColorScheme = Theme.of(context).colorScheme;
final bloc = await _bloc;
if (nextColorScheme != bloc.$2.state.colorScheme) {
bloc.$2.process(ColorSchemeChanged(nextColorScheme));
if (nextColorScheme != bloc?.$2.state.colorScheme) {
bloc?.$2.process(ColorSchemeChanged(nextColorScheme));
}
}

@override
void dispose() {
super.dispose();
_bloc.then((bloc) {
_bloc?.then((bloc) {
bloc.$1.close();
bloc.$2.close();
});
Expand Down Expand Up @@ -156,8 +160,8 @@ class _GamePageState extends State<GamePage> {
mainAxisSize: MainAxisSize.min,
children: [
FilledButton(
onPressed: () =>
GoRouter.of(context).go('/'),
onPressed: () async =>
(await _bloc)?.$1.reconnect(),
child: Text(
AppLocalizations.of(context)
.reconnect),
Expand Down
44 changes: 40 additions & 4 deletions server/lib/main.dart
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
import 'dart:io';

import 'package:bloc/bloc.dart';
import 'package:networker/networker.dart';
import 'package:networker_socket/server.dart';
import 'package:quokka_api/quokka_api.dart';
import 'package:quokka_server/asset.dart';

final class QuokkaServer {
final class QuokkaServer extends Bloc<ServerWorldEvent, WorldState> {
final ServerAssetManager assetManager;
bool _verbose = false;
GameTable _table = const GameTable();

NetworkerSocketServer? _server;
NetworkerPipe<dynamic, WorldEvent>? _pipe;

QuokkaServer() : assetManager = ServerAssetManager();
QuokkaServer()
: assetManager = ServerAssetManager(),
super(WorldState(data: QuokkaData.empty())) {
on<ServerWorldEvent>((event, emit) {
final newState = processServerEvent(event, state);
if (newState == null) return null;
emit(newState);
});
}

Future<void> init({int port = kDefaultPort, bool verbose = false}) async {
await assetManager.init(verbose: verbose);
Expand All @@ -23,13 +32,19 @@ final class QuokkaServer {
final transformer = _pipe = NetworkerPipeTransformer<String, WorldEvent>(
WorldEventMapper.fromJson, (e) => e.toJson());
transformer.read.listen(_onClientEvent);
server.connect(StringNetworkerPlugin()..connect(transformer));
server
..clientConnect.listen(_onJoin)
..clientDisconnect.listen(_onLeave)
..connect(StringNetworkerPlugin()..connect(transformer));
_table = const GameTable();
await _server?.init();
}

Future<void> run() async {
print('Server running on ${_server?.address}');
if (_verbose) {
print('Verbose logging activated');
}
await _server?.onClosed.first;
}

Expand All @@ -43,8 +58,29 @@ final class QuokkaServer {
);
if (process == null) return;
if (_verbose) {
print('Processing event: $process');
print('Processing event by ${event.channel}: $process');
}
_pipe?.sendMessage(process.$1, process.$2);
if (process.$2 == kAnyChannel || process.$2 == kAuthorityChannel) {
add(process.$1);
}
}

void _onJoin(Channel user) {
final info = _server?.getConnectionInfo(user);
if (info == null) return;
print('${info.address} ($user) joined the game');
_pipe?.sendMessage(
WorldInitialized(
table: state.table,
id: state.id,
teamMembers: state.teamMembers,
),
user);
}

void _onLeave(Channel user) {
final info = _server?.getConnectionInfo(user);
print('${info?.address} ($user) left the game');
}
}
10 changes: 9 additions & 1 deletion server/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.11.0"
bloc:
dependency: "direct main"
description:
name: bloc
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
url: "https://pub.dev"
source: hosted
version: "8.1.4"
boolean_selector:
dependency: transitive
description:
Expand Down Expand Up @@ -168,7 +176,7 @@ packages:
source: hosted
version: "4.0.0"
logging:
dependency: transitive
dependency: "direct main"
description:
name: logging
sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
Expand Down
4 changes: 2 additions & 2 deletions server/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ name: quokka_server
description: The Linwood Quokka game server
version: 0.1.0
publish_to: none
# repository: https://github.com/my_org/my_repo

environment:
sdk: ^3.5.1

# Add regular dependencies here.
dependencies:
args: ^2.4.2
bloc: ^8.1.4
logging: ^1.2.0
networker:
git:
url: https://github.com/LinwoodDev/dart_pkgs
Expand Down

0 comments on commit 14925b8

Please sign in to comment.