Add playing replays from file
This commit is contained in:
parent
5687adbf8a
commit
8c0d5b99ff
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,3 +12,4 @@ rust/target
|
||||
target
|
||||
|
||||
dist
|
||||
*.replay
|
||||
@ -23,6 +23,7 @@ GameManagerGlobal="*uid://bii6018k3ip3t"
|
||||
CliParserGlobal="*uid://c52o7xa0fnr53"
|
||||
GameSettingsGlobal="*uid://bjr15hjiojecw"
|
||||
AnnouncerGlobal="*uid://5gey4i80mmsa"
|
||||
StandaloneReplayGlobal="*uid://djmb2jqcpkuep"
|
||||
|
||||
[display]
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
[ext_resource type="PackedScene" uid="uid://dsxu0y8pjbw3k" path="res://scenes/ui/credits.tscn" id="1_sugp2"]
|
||||
[ext_resource type="PackedScene" uid="uid://n3u0jr4wiib1" path="res://scenes/ui/settings.tscn" id="2_0wfyh"]
|
||||
|
||||
[node name="menu" type="MainMenu" unique_id=2036763876 node_paths=PackedStringArray("camera", "main_position", "host_position", "main_panel", "host_panel", "join_panel", "host_port", "join_addr", "recent_servers_button", "popup_panel", "popup_title", "popup_text", "popup_button")]
|
||||
[node name="menu" type="MainMenu" unique_id=2036763876 node_paths=PackedStringArray("camera", "main_position", "host_position", "main_panel", "host_panel", "join_panel", "host_port", "join_addr", "recent_servers_button", "popup_panel", "popup_title", "popup_text", "popup_button", "open_replay_dialog")]
|
||||
camera = NodePath("camera_3d")
|
||||
main_position = NodePath("main_pos")
|
||||
host_position = NodePath("host_pos")
|
||||
@ -19,6 +19,7 @@ popup_title = NodePath("popup_panel/v_box_container/title")
|
||||
popup_text = NodePath("popup_panel/v_box_container/text")
|
||||
popup_button = NodePath("popup_panel/v_box_container/ok")
|
||||
credits = ExtResource("1_sugp2")
|
||||
open_replay_dialog = NodePath("open_replay_dialog")
|
||||
|
||||
[node name="camera_3d" type="Camera3D" parent="." unique_id=1513321931]
|
||||
transform = Transform3D(0.98629165, -0.031186381, 0.16203775, -1.4054481e-08, 0.981978, 0.1889952, -0.16501158, -0.18640438, 0.9685167, 1.0974052, 1.7166122, 1.4909106)
|
||||
@ -74,6 +75,11 @@ layout_mode = 2
|
||||
text = "Join Game"
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="play_replay" type="Button" parent="main_panel/v_box_container" unique_id=275552647]
|
||||
layout_mode = 2
|
||||
text = "Play Replay"
|
||||
metadata/_edit_lock_ = true
|
||||
|
||||
[node name="credits" type="Button" parent="main_panel/v_box_container" unique_id=953921422]
|
||||
layout_mode = 2
|
||||
text = "Credits"
|
||||
@ -245,8 +251,14 @@ autowrap_mode = 3
|
||||
layout_mode = 2
|
||||
text = "Ok"
|
||||
|
||||
[node name="open_replay_dialog" type="FileDialog" parent="." unique_id=925465558]
|
||||
title = "Open a File"
|
||||
file_mode = 0
|
||||
filters = PackedStringArray("*.replay")
|
||||
|
||||
[connection signal="pressed" from="main_panel/v_box_container/host" to="." method="go_to_host_game"]
|
||||
[connection signal="pressed" from="main_panel/v_box_container/join" to="." method="go_to_join_game"]
|
||||
[connection signal="pressed" from="main_panel/v_box_container/play_replay" to="." method="open_replay_dialog"]
|
||||
[connection signal="pressed" from="main_panel/v_box_container/credits" to="." method="open_credits"]
|
||||
[connection signal="pressed" from="main_panel/v_box_container/settings" to="settings" method="open"]
|
||||
[connection signal="pressed" from="main_panel/v_box_container/quit" to="." method="quit_game"]
|
||||
|
||||
3
godot/scenes/misc/standalone_replay_global.tscn
Normal file
3
godot/scenes/misc/standalone_replay_global.tscn
Normal file
@ -0,0 +1,3 @@
|
||||
[gd_scene format=3 uid="uid://djmb2jqcpkuep"]
|
||||
|
||||
[node name="standalone_replay_global" type="StandaloneReplayManager" unique_id=730657724]
|
||||
@ -6,7 +6,7 @@ use crate::{
|
||||
player::{
|
||||
IPlayer, NetTransform, ball::Ball, local_player::LocalPlayer, remote_player::RemotePlayer,
|
||||
},
|
||||
replay::{ReplayPlayer, ReplayPlayerState},
|
||||
replay::{ReplayPlayer, ReplayPlayerState, StandaloneReplayManager},
|
||||
};
|
||||
|
||||
#[derive(GodotClass)]
|
||||
@ -25,6 +25,10 @@ impl INode3D for Map {
|
||||
fn ready(&mut self) {
|
||||
if let Some(game) = &mut Game::singleton() {
|
||||
game.bind_mut().on_map_loaded(self.to_gd());
|
||||
} else {
|
||||
StandaloneReplayManager::singleton()
|
||||
.bind_mut()
|
||||
.on_map_loaded(self.to_gd());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,11 +3,12 @@ use std::{collections::HashMap, io::Cursor};
|
||||
use godot::{
|
||||
classes::{FileAccess, file_access::ModeFlags},
|
||||
prelude::*,
|
||||
tools::get_autoload_by_name,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
game_manager::{Game, Player},
|
||||
game_manager::{Game, GameManager, Player},
|
||||
map::Map,
|
||||
net::{network_manager::BallSync, util::NetVector3},
|
||||
player::{
|
||||
@ -16,6 +17,64 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
pub const STANDALONE_REPLAY_GLOBAL: &str = "StandaloneReplayGlobal";
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Node, init)]
|
||||
pub struct StandaloneReplayManager {
|
||||
loading_replay: bool,
|
||||
map: Option<Gd<Map>>,
|
||||
playback: Option<Gd<ReplayPlayback>>,
|
||||
replay: Option<Replay>,
|
||||
|
||||
base: Base<Node>,
|
||||
}
|
||||
|
||||
impl StandaloneReplayManager {
|
||||
pub fn singleton() -> Gd<StandaloneReplayManager> {
|
||||
get_autoload_by_name::<StandaloneReplayManager>(STANDALONE_REPLAY_GLOBAL)
|
||||
}
|
||||
|
||||
pub fn open_replay(&mut self, path: &GString) {
|
||||
if let Some(mut file) = FileAccess::open(path, ModeFlags::READ) {
|
||||
let mut bytes = Vec::new();
|
||||
while !file.eof_reached() {
|
||||
bytes.push(file.get_8());
|
||||
}
|
||||
|
||||
let bytes = Cursor::new(&mut bytes);
|
||||
if let Ok(res) = ciborium::from_reader::<Replay, _>(bytes) {
|
||||
self.loading_replay = true;
|
||||
let map_idx = res.map_idx;
|
||||
self.replay = Some(res);
|
||||
if let Some(map) = GameManager::singleton().bind().maps.get(map_idx as usize) {
|
||||
if let Some(scene) = &map.bind().scene {
|
||||
self.base().get_tree().change_scene_to_packed(scene);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_map_loaded(&mut self, map: Gd<Map>) {
|
||||
if self.loading_replay {
|
||||
self.loading_replay = false;
|
||||
self.map = Some(map.clone());
|
||||
|
||||
if let Some(replay) = self.replay.take() {
|
||||
let mut playback = ReplayPlayback::new_alloc();
|
||||
playback.bind_mut().replay = replay;
|
||||
playback.bind_mut().current_map = Some(map);
|
||||
playback.bind_mut().current_time = 0.;
|
||||
playback.bind_mut().fast_forwarding = false;
|
||||
playback.bind_mut().spectated_player_id = 0;
|
||||
self.playback = Some(playback.clone());
|
||||
self.base_mut().add_child(&playback);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(GodotClass)]
|
||||
#[class(base=Node, init)]
|
||||
pub struct ReplayPlayback {
|
||||
@ -292,7 +351,7 @@ impl ReplayRecorder {
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Replay {
|
||||
map_idx: u8,
|
||||
pub map_idx: u8,
|
||||
players: HashMap<u16, ReplayPlayer>,
|
||||
frames: Vec<ReplayFrame>,
|
||||
}
|
||||
|
||||
@ -1,13 +1,20 @@
|
||||
use std::io::Cursor;
|
||||
|
||||
use godot::{
|
||||
classes::{Button, Camera3D, Label, LineEdit, OptionButton, Panel, SpinBox},
|
||||
classes::{
|
||||
Button, Camera3D, FileAccess, FileDialog, Label, LineEdit, OptionButton, Panel, SpinBox,
|
||||
file_access::ModeFlags,
|
||||
},
|
||||
prelude::*,
|
||||
register::property::SimpleVar,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
game_manager::GameManager,
|
||||
game_settings::{GameSettingsManager, RecentServer},
|
||||
net::network_manager::NetworkManager,
|
||||
popup_queue::{Popup, PopupQueue},
|
||||
replay::{Replay, StandaloneReplayManager},
|
||||
ui::credits::CreditsMenu,
|
||||
};
|
||||
|
||||
@ -61,6 +68,9 @@ pub struct MainMenu {
|
||||
#[export]
|
||||
credits: Option<Gd<PackedScene>>,
|
||||
|
||||
#[export]
|
||||
open_replay_dialog: Option<Gd<FileDialog>>,
|
||||
|
||||
prev_panel: Option<Gd<Panel>>,
|
||||
|
||||
recent_servers: Vec<RecentServer>,
|
||||
@ -87,6 +97,12 @@ impl INode3D for MainMenu {
|
||||
.item_selected()
|
||||
.connect_other(self, |s, idx| s.on_recent_server_selected(idx));
|
||||
}
|
||||
if let Some(dialog) = &self.open_replay_dialog {
|
||||
dialog
|
||||
.signals()
|
||||
.file_selected()
|
||||
.connect_other(self, Self::on_open_replay);
|
||||
}
|
||||
self.hide_popup();
|
||||
}
|
||||
|
||||
@ -162,6 +178,19 @@ impl INode3D for MainMenu {
|
||||
|
||||
#[godot_api]
|
||||
impl MainMenu {
|
||||
#[func]
|
||||
pub fn open_replay_dialog(&mut self) {
|
||||
if let Some(dialog) = &mut self.open_replay_dialog {
|
||||
dialog.popup_file_dialog();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_open_replay(&mut self, path: GString) {
|
||||
StandaloneReplayManager::singleton()
|
||||
.bind_mut()
|
||||
.open_replay(&path);
|
||||
}
|
||||
|
||||
#[func]
|
||||
pub fn host_game(&mut self) {
|
||||
if let Some(host_port) = &self.host_port {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user