Skip to content

Commit

Permalink
Rebuild event system to add exclusive client and server events
Browse files Browse the repository at this point in the history
  • Loading branch information
CodeDoctorDE committed Aug 20, 2024
1 parent a55678d commit 9c9da98
Show file tree
Hide file tree
Showing 28 changed files with 1,672 additions and 912 deletions.
67 changes: 41 additions & 26 deletions app/lib/bloc/multiplayer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:networker/networker.dart';
import 'package:networker_socket/client.dart';
import 'package:networker_socket/server.dart';
import 'package:quokka/bloc/world_event.dart';
import 'package:quokka/bloc/world/event.dart';

part 'multiplayer.mapper.dart';

Expand Down Expand Up @@ -41,9 +41,9 @@ final class MultiplayerDisconnectedState extends MultiplayerState
final class MultiplayerConnectedState extends MultiplayerState
with MultiplayerConnectedStateMappable {
final NetworkerBase networker;
final NetworkerPipeTransformer<String, WorldEvent> transformer;
final SimpleNetworkerPipe<WorldEvent> pipe;

MultiplayerConnectedState(this.networker, this.transformer);
MultiplayerConnectedState(this.networker, this.pipe);

@override
bool get isClient => networker is NetworkerClient;
Expand All @@ -52,10 +52,16 @@ final class MultiplayerConnectedState extends MultiplayerState
}

class MultiplayerCubit extends Cubit<MultiplayerState> {
final StreamController<WorldEvent> _eventController =
final StreamController<PlayableWorldEvent> _eventController =
StreamController.broadcast();

Stream<WorldEvent> get events => _eventController.stream;
Stream<PlayableWorldEvent> get events => _eventController.stream;

final StreamController<(ClientWorldEvent, Channel)> _serverEventController =
StreamController.broadcast();

Stream<(ClientWorldEvent, Channel)> get serverEvents =>
_serverEventController.stream;

final StreamController<int> _initController = StreamController.broadcast();

Expand All @@ -72,26 +78,34 @@ class MultiplayerCubit extends Cubit<MultiplayerState> {
WorldEventMapper.fromJson,
(e) => e.toJson(),
);
base.connect(StringNetworkerPlugin()..connect(transformer));
transformer.read.listen((event) {
if (event.data is WorldMultiplayerEvent) {
_eventController.add(event.data);
}
});
if (base is NetworkerServer) {
base.connect(RawJsonNetworkerPlugin()
..connect(AdvancedNetworkerPipeTransformer<dynamic, WorldEvent>(
(data, channel) => (WorldEventMapper.fromJson(data), channel),
(data, channel) {
if (data is UserBasedEvent) {
data = data.copyWith(user: channel);
}
return (data.toJson(), channel);
})
..connect(EchoPipe(toChannel: kAnyChannel))));
base.clientConnect.listen(_initController.add);
final pipe = SimpleNetworkerPipe<WorldEvent>();
final stringPlugin = StringNetworkerPlugin();
if (base is NetworkerClient) {
base.connect(stringPlugin..connect(transformer));
transformer
..read.listen(_onServerEvent)
..connect(pipe);
} else if (base is NetworkerServer) {
transformer.connect(SimpleNetworkerPipe()
..read.listen(_onClientEvent)
..write.listen(_onClientEvent)
..connect(pipe));
}
return MultiplayerConnectedState(base, pipe);
}

void _onClientEvent(NetworkerPacket<WorldEvent> event) {
final data = event.data;
if (data is ClientWorldEvent) {
_serverEventController.add((data, event.channel));
}
}

void _onServerEvent(event) {
final data = event.data;
if (data is ServerWorldEvent) {
_eventController.add(data);
}
return MultiplayerConnectedState(base, transformer);
}

@override
Expand Down Expand Up @@ -144,9 +158,10 @@ class MultiplayerCubit extends Cubit<MultiplayerState> {
}
}

void send(WorldMultiplayerEvent event, [Channel channel = kAnyChannel]) {
void send(WorldEvent event, [Channel channel = kAnyChannel]) {
final state = this.state;
if (state is! MultiplayerConnectedState) return;
state.transformer.sendMessage(event, channel);
if (event is LocalWorldEvent) return;
state.pipe.sendMessage(event, channel);
}
}
27 changes: 10 additions & 17 deletions app/lib/bloc/multiplayer.mapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -416,22 +416,19 @@ class MultiplayerConnectedStateMapper
static NetworkerBase _$networker(MultiplayerConnectedState v) => v.networker;
static const Field<MultiplayerConnectedState, NetworkerBase> _f$networker =
Field('networker', _$networker);
static NetworkerPipeTransformer<String, WorldEvent> _$transformer(
MultiplayerConnectedState v) =>
v.transformer;
static const Field<MultiplayerConnectedState,
NetworkerPipeTransformer<String, WorldEvent>> _f$transformer =
Field('transformer', _$transformer);
static SimpleNetworkerPipe<WorldEvent> _$pipe(MultiplayerConnectedState v) =>
v.pipe;
static const Field<MultiplayerConnectedState, SimpleNetworkerPipe<WorldEvent>>
_f$pipe = Field('pipe', _$pipe);

@override
final MappableFields<MultiplayerConnectedState> fields = const {
#networker: _f$networker,
#transformer: _f$transformer,
#pipe: _f$pipe,
};

static MultiplayerConnectedState _instantiate(DecodingData data) {
return MultiplayerConnectedState(
data.dec(_f$networker), data.dec(_f$transformer));
return MultiplayerConnectedState(data.dec(_f$networker), data.dec(_f$pipe));
}

@override
Expand Down Expand Up @@ -494,9 +491,7 @@ abstract class MultiplayerConnectedStateCopyWith<
$In extends MultiplayerConnectedState,
$Out> implements MultiplayerStateCopyWith<$R, $In, $Out> {
@override
$R call(
{NetworkerBase? networker,
NetworkerPipeTransformer<String, WorldEvent>? transformer});
$R call({NetworkerBase? networker, SimpleNetworkerPipe<WorldEvent>? pipe});
MultiplayerConnectedStateCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(
Then<$Out2, $R2> t);
}
Expand All @@ -511,17 +506,15 @@ class _MultiplayerConnectedStateCopyWithImpl<$R, $Out>
late final ClassMapperBase<MultiplayerConnectedState> $mapper =
MultiplayerConnectedStateMapper.ensureInitialized();
@override
$R call(
{NetworkerBase? networker,
NetworkerPipeTransformer<String, WorldEvent>? transformer}) =>
$R call({NetworkerBase? networker, SimpleNetworkerPipe<WorldEvent>? pipe}) =>
$apply(FieldCopyWithData({
if (networker != null) #networker: networker,
if (transformer != null) #transformer: transformer
if (pipe != null) #pipe: pipe
}));
@override
MultiplayerConnectedState $make(CopyWithData data) =>
MultiplayerConnectedState(data.get(#networker, or: $value.networker),
data.get(#transformer, or: $value.transformer));
data.get(#pipe, or: $value.pipe));

@override
MultiplayerConnectedStateCopyWith<$R2, MultiplayerConnectedState, $Out2>
Expand Down
65 changes: 53 additions & 12 deletions app/lib/bloc/world.dart → app/lib/bloc/world/bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,17 @@ import 'package:collection/collection.dart';
import 'package:flutter/material.dart' show ColorScheme;
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:material_leap/helpers.dart';
import 'package:quokka/bloc/world_event.dart';
import 'package:quokka/bloc/world_state.dart';
import 'package:networker/networker.dart';
import 'package:quokka/bloc/world/event.dart';
import 'package:quokka/bloc/world/state.dart';
import 'package:quokka/helpers/asset.dart';
import 'package:quokka/models/data.dart';
import 'package:quokka/models/table.dart';
import 'package:quokka/services/file_system.dart';
import 'package:quokka/bloc/multiplayer.dart';

class WorldBloc extends Bloc<WorldEvent, WorldState> {
class WorldBloc extends Bloc<PlayableWorldEvent, WorldState> {
late final AssetManager assetManager;
bool _remoteEvent = false;
WorldBloc({
required MultiplayerCubit multiplayer,
Expand All @@ -28,6 +31,9 @@ class WorldBloc extends Bloc<WorldEvent, WorldState> {
colorScheme: colorScheme,
table: table ?? data?.getTable() ?? const GameTable(),
)) {
assetManager = AssetManager(
bloc: this,
);
state.multiplayer
..events.listen((event) {
_remoteEvent = true;
Expand All @@ -43,7 +49,8 @@ class WorldBloc extends Bloc<WorldEvent, WorldState> {
id: e,
),
e);
});
})
..serverEvents.listen(_processEvent);

on<WorldInitialized>((event, emit) {
emit(state.copyWith(
Expand Down Expand Up @@ -107,10 +114,20 @@ class WorldBloc extends Bloc<WorldEvent, WorldState> {
}));
return save();
});
/*on<ObjectVariationChanged>((event, emit) {
final cell = state.table.cells[event.cell] ?? TableCell();
if (cell.objects.isEmpty) return null;
if (!event.object.inRange(0, cell.objects.length - 1)) return null;
final newCell = cell.copyWith.objects
.at(event.object)
.$update((e) => e.copyWith(variation: event.variation));
});*/
on<CellHideChanged>((event, emit) {
final cell = state.table.cells[event.cell] ?? TableCell();
final objectIndex = event.object;
if (objectIndex != null) {
if (cell.objects.isEmpty) return null;
if (!objectIndex.inRange(0, cell.objects.length - 1)) return null;
final object = cell.objects[objectIndex];
emit(state.copyWith.table.cells.replace(
Expand Down Expand Up @@ -139,6 +156,15 @@ class WorldBloc extends Bloc<WorldEvent, WorldState> {
)));
return save();
});
on<VariationChanged>((event, emit) {
final cell = state.table.cells[event.cell] ?? TableCell();
if (!event.object.inRange(0, cell.objects.length - 1)) return null;
final object = cell.objects[event.object];
emit(state.copyWith.table.cells.replace(
event.cell,
cell.copyWith.objects.replace(
event.object, object.copyWith(variation: event.variation))));
});
on<ObjectIndexChanged>((event, emit) {
final cell = state.table.cells[event.cell] ?? TableCell();
if (!event.index.inRange(0, cell.objects.length - 1)) return null;
Expand Down Expand Up @@ -195,20 +221,35 @@ class WorldBloc extends Bloc<WorldEvent, WorldState> {
return state.fileSystem.worldSystem.updateFile(name, data);
}

void _processEvent((WorldEvent, Channel) data) {
final value = processEvent(this, data.$1, data.$2);
if (value == null) return;
state.multiplayer.send(value.$1, value.$2);
}

@override
void onEvent(WorldEvent event) {
void onEvent(PlayableWorldEvent event) {
super.onEvent(event);
if (event is WorldMultiplayerEvent && !_remoteEvent) {
if (event is ClientWorldEvent && !_remoteEvent) {
state.multiplayer.send(event);
}
}

void send(WorldEvent event) {
if (event is WorldMultiplayerEvent && state.multiplayer.isConnected) {
if (state.multiplayer.isServer) add(event);
state.multiplayer.send(event);
} else {
add(event);
void process(WorldEvent event) {
switch (event) {
case LocalWorldEvent e:
add(e);
case ServerWorldEvent e:
add(e);
case ClientWorldEvent e:
final multiplayer = state.multiplayer;
if (multiplayer.isConnected) {
multiplayer.send(e);
} else {
final event =
processEvent(this, e, kAuthorityChannel, allowServerEvents: true);
if (event != null) add(event.$1);
}
}
}
}
28 changes: 28 additions & 0 deletions app/lib/bloc/world/event.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'dart:math';

import 'package:dart_mappable/dart_mappable.dart';
import 'package:flutter/material.dart';
import 'package:material_leap/material_leap.dart';
import 'package:networker/networker.dart';
import 'package:quokka/bloc/world/bloc.dart';
import 'package:quokka/models/table.dart';
import 'package:quokka/models/vector.dart';

part 'event.mapper.dart';

part 'event/server.dart';
part 'event/client.dart';
part 'event/hybrid.dart';
part 'event/local.dart';
part 'event/process.dart';

@MappableClass(discriminatorKey: 'type')
sealed class WorldEvent with WorldEventMappable {
WorldEvent();
}

/// Events that can be processed by the event management system
/// This can be a ServerWorldEvent or a LocalWorldEvent
@MappableClass(discriminatorKey: 'type')
sealed class PlayableWorldEvent extends WorldEvent
with PlayableWorldEventMappable {}
Loading

0 comments on commit 9c9da98

Please sign in to comment.