Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 38 additions & 13 deletions onboard/backend/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@ mod network {
use log::{log, Level};
use serde::Deserialize;
use std::ops::Deref;
use std::time::Duration;

// Construct a static client to be used for all requests. Prevents opening a new connection for
// every request.
lazy_static! {
static ref CLIENT: reqwest::Client = reqwest::Client::new();
//Added timeouts to prevent network requests from hanging indefinitely when offline. TBH TIMEOUTS MAY NOT BE THE RIGHT SOLUTUON BUT IT WORKS
static ref CLIENT: reqwest::Client = reqwest::Client::builder()
.timeout(Duration::from_secs(30))
.connect_timeout(Duration::from_secs(10))
.build()
.unwrap();
}

/**
Expand Down Expand Up @@ -147,13 +153,22 @@ mod route {
* # Errors
* This function will return an error if the request fails, or if the JSON cannot be deserialized
*/
//Now falls back to game_list_from_fs() if the API is unreachable.
pub async fn game_list() -> Result<Vec<DevcadeGame>, Error> {
let games: Vec<DevcadeGame> =
network::request_json(format!("{}/{}", api_url(), route::game_list()).as_str()).await?;
Ok(games
.into_iter()
.filter(|game| game.hash.is_some())
.collect::<Vec<DevcadeGame>>())
match network::request_json::<Vec<DevcadeGame>>(
format!("{}/{}", api_url(), route::game_list()).as_str(),
)
.await
{
Ok(games) => Ok(games
.into_iter()
.filter(|game| game.hash.is_some())
.collect()),
Err(err) => {
log::warn!("Couldn't fetch game list from API, falling back to filesystem!: {err:?}");
game_list_from_fs()
}
}
}

/**
Expand Down Expand Up @@ -395,15 +410,23 @@ pub async fn download_game(game_id: String) -> Result<DevcadeGame, Error> {
}
Err(err) => {
log::warn!("Couldn't request live info on game! Falling back to local file! {err:?}");
local_game
.as_ref()
.expect("Game not downloaded and we're offline!")
.clone()
match local_game.as_ref() {
Ok(g) => {
if g.flatpak_app_id.is_some() {
log::info!("Offline and game is already installed, skipping download.");
return Ok(g.clone());
}
g.clone()
}
Err(_) => {
return Err(anyhow!("Game not downloaded and we're offline!"));
}
}
}
};
// Is the current hash == the remote hash?
if let Ok(local_game) = local_game {
if local_game.hash == game.hash {
if local_game.hash == game.hash && local_game.flatpak_app_id.is_some() { // just to be sure sure
return Ok(local_game);
}
}
Expand Down Expand Up @@ -526,7 +549,9 @@ pub async fn launch_game(game_id: String) -> Result<(), Error> {

tokio::time::sleep(Duration::from_millis(200)).await;

kill_game(game).await?;
if let Err(e) = kill_game(game).await {
log::warn!("kill_game failed (game may have already exited cleanly): {e}"); // not necessary but this fixed exit error for me
}

Ok(())
}
Expand Down
10 changes: 4 additions & 6 deletions onboard/frontend/ui/Devcade.cs
Original file line number Diff line number Diff line change
Expand Up @@ -381,13 +381,11 @@ protected override void Update(GameTime gameTime) {
Client.launchGame(
menu.gameSelected().id
).ContinueWith(res => {
if (res.IsCompletedSuccessfully) {
state = MenuState.Input;
}
else {
logger.Error("Failed to launch game: " + res.Exception);
state = MenuState.Input;
_loading = false;
if (!res.IsCompletedSuccessfully || res.Result.type == Response.ResponseType.Err) {
logger.Error("Failed to launch game: " + res.Exception?.Message);
}
state = MenuState.Input;
});

fadeColor = 0f;
Expand Down
47 changes: 24 additions & 23 deletions onboard/frontend/ui/Menu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,25 +143,31 @@ public void clearGames() {
cards?.Clear();
tagLists.Clear();
itemSelected = 0;
currentTag = allTag.name; // reset to default
// Re-add the allTag entry immediately
tagLists.Add(allTag.name, new List<MenuCard>());
}

public bool reloadGames(GraphicsDevice device, bool clear = true) {
if (clear)
clearGames();
// Reload the .env file every time the games are reloaded to make sure that the demo mode is up to date
if (clear) clearGames();
Env.load("../.env");
itemSelected = 0;

var errorList = new List<DevcadeGame> { defaultGame };

setTags();

// Public access to state is definitely a good idea (this whole thing needs a refactor)
Devcade.instance.state = Devcade.MenuState.Loading;
Devcade.instance._loading = true;

// gameTask is 'never used' but tasks in C# are eager, so it doesn't need to be awaited to run.
Task gameTask = Client.getGameList()
Task gameTask = Client.getTags()
.ContinueWith(t => {
logger.Info("Getting tags from API");
tags = t.Result.into_result<List<devcade.Tag>>().unwrap_or(new List<devcade.Tag>());
tags.Insert(0, allTag);
if (tagLists.Keys.Count == 0) {
foreach (Tag tag in tags) {
tagLists.Add(tag.name, new List<MenuCard>());
}
}
})
.ContinueWith(_ => Client.getGameList()).Unwrap()
.ContinueWith(t => {
if (!t.IsCompletedSuccessfully) {
logger.Error($"Failed to fetch game list: {t.Exception}");
Expand Down Expand Up @@ -200,20 +206,12 @@ public bool reloadGames(GraphicsDevice device, bool clear = true) {
}

public void setTags() {
if (tags == null || tags.Count == 0) {
logger.Info("Getting tags from API (this should be only once, but maybe every reload of the game list?)");
tags = Client.getTags().Result.into_result<List<devcade.Tag>>().unwrap_or(new List<devcade.Tag>());
tags.Insert(0, allTag); // Make all tag appear at the top of the list
}

if (tags == null)tags =new List<devcade.Tag>{ allTag};
if (tagLists.Keys.Count != 0) return;

// tagLists gets cleared every time the games are reloaded?!
foreach (Tag tag in tags) {
tagLists.Add(tag.name, new List<MenuCard>());
}
}

public void setCards(GraphicsDevice graphics) {
for (int i = 0; i < gameTitles.Count; i++) {
devcade.DevcadeGame game = gameTitles[i];
Expand Down Expand Up @@ -246,9 +244,10 @@ public void setCards(GraphicsDevice graphics) {

// Add the reference to the card to the proper lists within the tag dictionary
foreach(devcade.Tag tag in game.tags) {
tagLists[tag.name].Add(newCard);
}

if (tagLists.ContainsKey(tag.name)) {
tagLists[tag.name].Add(newCard);
}
}
tagLists[allTag.name].Add(newCard);
}

Expand Down Expand Up @@ -279,6 +278,7 @@ public devcade.DevcadeGame gameSelected() {

// MAKE FONTS, TEXTURES, AND DIMS FIELDS WITHIN TAGS MENU
public void initializeTagsMenu(Texture2D cardTexture, SpriteFont font) {
if (tags == null) tags = new List<devcade.Tag> { allTag }; // just to be sure sure, yk
tagsMenu = new TagsMenu(tags.ToArray(), cardTexture, font, new Vector2(_sWidth, _sHeight), scalingAmount);
}

Expand Down Expand Up @@ -561,7 +561,8 @@ public void writeString(SpriteBatch _spriteBatch, SpriteFont font, string str, V
);
}

public void drawCards(SpriteBatch _spriteBatch, Texture2D cardTexture, SpriteFont font) {
public void drawCards(SpriteBatch _spriteBatch, Texture2D cardTexture, SpriteFont font) {
if (!tagLists.ContainsKey(currentTag)) return; //tryna make refresh work
// I still have no idea why the layerDepth does not work\
foreach (MenuCard card in tagLists[currentTag].Where(card => Math.Abs(card.listPos) == 4))
{
Expand Down
24 changes: 14 additions & 10 deletions onboard/frontend/ui/MenuCard.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Microsoft.Xna.Framework;
using System;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace onboard.ui
Expand All @@ -13,7 +15,7 @@ public class MenuCard
private Texture2D texture;

public int listPos; // Tracks the card's current position on the screen

// Same as rotation variables, but for scale, color
private float scale = 1f;
private const float scale_amt = 0.05f;
Expand All @@ -35,7 +37,7 @@ public MenuCard(int initialPos, Texture2D cardTexture, devcade.DevcadeGame game)
this.texture = cardTexture;
this.game = game;

while(initialPos > 0)
while (initialPos > 0)
{
rotation -= rotation_amt;
scale -= scale_amt;
Expand All @@ -52,12 +54,13 @@ public MenuCard(int initialPos, Texture2D cardTexture, devcade.DevcadeGame game)

}

public void setListPos(int pos) {
this.listPos = pos;
public void setListPos(int pos)
{
this.listPos = pos;
this.rotation = 0f;
this.scale = 1f;

while(pos > 0)
while (pos > 0)
{
rotation -= rotation_amt;
scale -= scale_amt;
Expand Down Expand Up @@ -108,18 +111,19 @@ public void DrawSelf(SpriteBatch _spriteBatch, Texture2D cardTexture, int _sHeig
{
_spriteBatch.Draw(
texture ?? cardTexture,
new Vector2(cardX, (int)(_sHeight / 2.0 + (cardTexture.Height * scalingAmount) /2)),
new Vector2(cardX, (int)(_sHeight / 2.0 + (cardTexture.Height * scalingAmount) / 2)),
null,
new Color(cardOpacity, cardOpacity, cardOpacity, cardOpacity),
rotation,
new Vector2(0, cardTexture.Height / 2.0f),
(float)(scale * scalingAmount),
(float)(scale * scalingAmount),
SpriteEffects.None,
0f
Math.Abs(rotation)
);
}

public void setTexture(Texture2D texture) {
public void setTexture(Texture2D texture)
{
this.texture = texture;
}
}
Expand Down