Skip to content

Commit 922cc84

Browse files
authored
Implement the Server Protocol (#819)
This implements a new improved version of the server protocol. The following changes have been made: - The protocol is based on JSON messages. This allows for example for more structured commands where it's easier to provide multiple arguments for a command and even have optional arguments. - For each command, there is a corresponding response. It is either a `success` response with possibly the value that you requested, or an `error` response with an error `code`. - On top of the responses you also get sent `event` messages that indicate changes to the timer. These can either be changes triggered via a command that you sent or by changes that happened through other sources, such as the user directly interacting with the timer or an auto splitter. The protocol is still work in progress and we will evolve it into a protocol that fully allows synchronizing timers over the network. The event sink has now been renamed to command sink, because there is now a clear distinction between incoming commands and events that are the results of these commands.
1 parent 4ebb4bb commit 922cc84

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2845
-806
lines changed

benches/balanced_pb.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,16 @@ criterion_main!(benches);
99
criterion_group!(benches, fake_splits, actual_splits);
1010

1111
fn run_with_splits(timer: &mut Timer, splits: &[f64]) {
12-
timer.start();
13-
timer.initialize_game_time();
14-
timer.pause_game_time();
12+
timer.start().unwrap();
13+
timer.initialize_game_time().unwrap();
14+
timer.pause_game_time().unwrap();
1515

1616
for &split in splits {
17-
timer.set_game_time(TimeSpan::from_seconds(split));
18-
timer.split();
17+
timer.set_game_time(TimeSpan::from_seconds(split)).unwrap();
18+
timer.split().unwrap();
1919
}
2020

21-
timer.reset(true);
21+
timer.reset(true).unwrap();
2222
}
2323

2424
fn fake_splits(c: &mut Criterion) {

benches/layout_state.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ fn artificial() -> (Timer, Layout, ImageCache) {
1818
run.push_segment(Segment::new("Foo"));
1919

2020
let mut timer = Timer::new(run).unwrap();
21-
timer.start();
21+
timer.start().unwrap();
2222

2323
(timer, Layout::default_layout(), ImageCache::new())
2424
}
@@ -28,7 +28,7 @@ fn real() -> (Timer, Layout, ImageCache) {
2828
let run = livesplit::parse(&buf).unwrap();
2929

3030
let mut timer = Timer::new(run).unwrap();
31-
timer.start();
31+
timer.start().unwrap();
3232

3333
(timer, Layout::default_layout(), ImageCache::new())
3434
}

benches/scene_management.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -146,19 +146,19 @@ cfg_if::cfg_if! {
146146

147147
fn start_run(timer: &mut Timer) {
148148
timer.set_current_timing_method(TimingMethod::GameTime);
149-
timer.start();
150-
timer.initialize_game_time();
151-
timer.pause_game_time();
152-
timer.set_game_time(TimeSpan::zero());
149+
timer.start().unwrap();
150+
timer.initialize_game_time().unwrap();
151+
timer.pause_game_time().unwrap();
152+
timer.set_game_time(TimeSpan::zero()).unwrap();
153153
}
154154

155155
fn make_progress_run_with_splits_opt(timer: &mut Timer, splits: &[Option<f64>]) {
156156
for &split in splits {
157157
if let Some(split) = split {
158-
timer.set_game_time(TimeSpan::from_seconds(split));
159-
timer.split();
158+
timer.set_game_time(TimeSpan::from_seconds(split)).unwrap();
159+
timer.split().unwrap();
160160
} else {
161-
timer.skip_split();
161+
timer.skip_split().unwrap();
162162
}
163163
}
164164
}

benches/software_rendering.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,19 @@ cfg_if::cfg_if! {
7474

7575
fn start_run(timer: &mut Timer) {
7676
timer.set_current_timing_method(TimingMethod::GameTime);
77-
timer.start();
78-
timer.initialize_game_time();
79-
timer.pause_game_time();
80-
timer.set_game_time(TimeSpan::zero());
77+
timer.start().unwrap();
78+
timer.initialize_game_time().unwrap();
79+
timer.pause_game_time().unwrap();
80+
timer.set_game_time(TimeSpan::zero()).unwrap();
8181
}
8282

8383
fn make_progress_run_with_splits_opt(timer: &mut Timer, splits: &[Option<f64>]) {
8484
for &split in splits {
8585
if let Some(split) = split {
86-
timer.set_game_time(TimeSpan::from_seconds(split));
87-
timer.split();
86+
timer.set_game_time(TimeSpan::from_seconds(split)).unwrap();
87+
timer.split().unwrap();
8888
} else {
89-
timer.skip_split();
89+
timer.skip_split().unwrap();
9090
}
9191
}
9292
}

benches/svg_rendering.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,19 +82,19 @@ cfg_if::cfg_if! {
8282

8383
fn start_run(timer: &mut Timer) {
8484
timer.set_current_timing_method(TimingMethod::GameTime);
85-
timer.start();
86-
timer.initialize_game_time();
87-
timer.pause_game_time();
88-
timer.set_game_time(TimeSpan::zero());
85+
timer.start().unwrap();
86+
timer.initialize_game_time().unwrap();
87+
timer.pause_game_time().unwrap();
88+
timer.set_game_time(TimeSpan::zero()).unwrap();
8989
}
9090

9191
fn make_progress_run_with_splits_opt(timer: &mut Timer, splits: &[Option<f64>]) {
9292
for &split in splits {
9393
if let Some(split) = split {
94-
timer.set_game_time(TimeSpan::from_seconds(split));
95-
timer.split();
94+
timer.set_game_time(TimeSpan::from_seconds(split)).unwrap();
95+
timer.split().unwrap();
9696
} else {
97-
timer.skip_split();
97+
timer.skip_split().unwrap();
9898
}
9999
}
100100
}

capi/Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "livesplit-core-capi"
33
version = "0.11.0"
44
authors = ["Christopher Serr <[email protected]>"]
5-
edition = "2018"
5+
edition = "2021"
66

77
[lib]
88
name = "livesplit_core"
@@ -15,13 +15,14 @@ time = { version = "0.3.4", default-features = false, features = ["formatting"]
1515
simdutf8 = { git = "https://github.com/CryZe/simdutf8", branch = "wasm-ub-panic", default-features = false }
1616

1717
wasm-bindgen = { version = "0.2.78", optional = true }
18+
wasm-bindgen-futures = { version = "0.4.28", optional = true }
1819
web-sys = { version = "0.3.28", optional = true }
1920

2021
[features]
2122
default = ["image-shrinking"]
2223
image-shrinking = ["livesplit-core/image-shrinking"]
2324
software-rendering = ["livesplit-core/software-rendering"]
24-
wasm-web = ["livesplit-core/wasm-web", "wasm-bindgen", "web-sys"]
25+
wasm-web = ["livesplit-core/wasm-web", "wasm-bindgen", "wasm-bindgen-futures", "web-sys"]
2526
auto-splitting = ["livesplit-core/auto-splitting"]
2627
assume-str-parameters-are-utf8 = []
2728
web-rendering = ["wasm-web", "livesplit-core/web-rendering"]

capi/bind_gen/src/typescript.ts

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,99 @@ export enum TimerPhase {
216216
Paused = 3,
217217
}
218218

219+
/** An event informs you about a change in the timer. */
220+
export enum Event {
221+
/** The timer has been started. */
222+
Started = 0,
223+
/**
224+
* A split happened. Note that the final split is signaled by `Finished`.
225+
*/
226+
Splitted = 1,
227+
/**
228+
* The final split happened, the run is now finished, but has not been reset
229+
* yet.
230+
*/
231+
Finished = 2,
232+
/** The timer has been reset. */
233+
Reset = 3,
234+
/** The previous split has been undone. */
235+
SplitUndone = 4,
236+
/** The current split has been skipped. */
237+
SplitSkipped = 5,
238+
/** The timer has been paused. */
239+
Paused = 6,
240+
/** The timer has been resumed. */
241+
Resumed = 7,
242+
/** All the pauses have been undone. */
243+
PausesUndone = 8,
244+
/** All the pauses have been undone and the timer has been resumed. */
245+
PausesUndoneAndResumed = 9,
246+
/** The comparison has been changed. */
247+
ComparisonChanged = 10,
248+
/** The timing method has been changed. */
249+
TimingMethodChanged = 11,
250+
/** The game time has been initialized. */
251+
GameTimeInitialized = 12,
252+
/** The game time has been set. */
253+
GameTimeSet = 13,
254+
/** The game time has been paused. */
255+
GameTimePaused = 14,
256+
/** The game time has been resumed. */
257+
GameTimeResumed = 15,
258+
/** The loading times have been set. */
259+
LoadingTimesSet = 16,
260+
/** A custom variable has been set. */
261+
CustomVariableSet = 17,
262+
}
263+
264+
/** An error that occurred when a command was being processed. */
265+
export enum CommandError {
266+
/** The operation is not supported. */
267+
Unsupported = -1,
268+
/** The timer can't be interacted with at the moment. */
269+
Busy = -2,
270+
/** There is already a run in progress. */
271+
RunAlreadyInProgress = -3,
272+
/** There is no run in progress. */
273+
NoRunInProgress = -4,
274+
/** The run is already finished. */
275+
RunFinished = -5,
276+
/** The time is negative, you can't split yet. */
277+
NegativeTime = -6,
278+
/** The last split can't be skipped. */
279+
CantSkipLastSplit = -7,
280+
/** There is no split to undo. */
281+
CantUndoFirstSplit = -8,
282+
/** The timer is already paused. */
283+
AlreadyPaused = -9,
284+
/** The timer is not paused. */
285+
NotPaused = -10,
286+
/** The requested comparison doesn't exist. */
287+
ComparisonDoesntExist = -11,
288+
/** The game time is already initialized. */
289+
GameTimeAlreadyInitialized = -12,
290+
/** The game time is already paused. */
291+
GameTimeAlreadyPaused = -13,
292+
/** The game time is not paused. */
293+
GameTimeNotPaused = -14,
294+
/** The time could not be parsed. */
295+
CouldNotParseTime = -15,
296+
/** The timer is currently paused. */
297+
TimerPaused = -16,
298+
/** The runner decided to not reset the run. */
299+
RunnerDecidedAgainstReset = -17,
300+
}
301+
302+
/** The result of a command that was processed. */
303+
export type CommandResult = Event | CommandError;
304+
305+
/**
306+
* Checks if the result of a command is a successful event instead of an error.
307+
*/
308+
export function isEvent(result: CommandResult): result is Event {
309+
return result >= 0;
310+
}
311+
219312
/** The state object describes the information to visualize for this component. */
220313
export interface BlankSpaceComponentStateJson {
221314
/** The background shown behind the component. */

0 commit comments

Comments
 (0)