Skip to content
Merged
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
Empty file removed .gitmodules
Empty file.
46 changes: 5 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,49 +11,13 @@ To build and run on the DCU, do the following from `./onboard/frontend`:
```
dotnet publish -c Release -r linux-x64 --no-self-contained
```
And the following fron `./onboard/backend`:
And the following from `./onboard/backend`:
```
cargo build --release --target x86_64-unknown-linux-gnu
```
Note: The backend requires the nightly compiler to build, as it uses features that have not been merged into stable rustc

To put it on the DCU, compress the `publish` folder located at `./onboard/frontend/bin/Release/netcoreapp3.1/linux-x64` and `scp` that to the DCU.
You'll also want to `scp` `./onboard/backend/target/release` to the DCU.

## The DCU

### Prereqs

Debian >=10

A user named `devcade`

`apt install xterm openbox compton` and friends (I dont actually know what all is installed)

### Daemon

_daemons are always watching. They are always with you. So is Willard._

The Devcade DCU is running Debian 10 with a very _very_ simple Xorg server setup. It has [xlogin](https://github.com/joukewitteveen/xlogin) configured to launch the onboarding program, along with said xorg server, as the `devcade` user.

You can find everything(tm) you need to set up the Devcade DCU in `/dcu`. This repo has a submodule, `xlogin` that can be cloned down with `git submodule update --init --recursive`.

1. Run the `update_onboard.sh` script in `HACKING/`

2. `cp dcu/.xinitrc /home/devcade/`

2. `mkdir /home/devcade/.config/openbox && cp dcu/rc.xml /home/devcade/.config/openbox/rc.xml`

3. To install `xlogin`, do the following

```
cd dcu/xlogin
sudo make install
sudo systemctl enable --now xlogin@devcade
```

_Helpful Tip: Remember to `chmod +x onboard`. You may get weird syntax errors if you don't_

## HACKING

To setup and launch a development environment, you can do the following:
Expand All @@ -63,13 +27,13 @@ To setup and launch a development environment, you can do the following:
There is a file called .env.template in the `./onboard` folder. Fill this in with appropriate values for the backend and frontend.



### Running outside a container

In onboard/frontend, run dotnet-run
In onboard/backend, run cargo run
In onboard/frontend, run `dotnet run`

In onboard/backend, run `cargo run`

The frontend will log errors about not being able to connect until the backend is up and running
The frontend will log warnings about not being able to connect until the backend is up and running

### Building and Launching the Container

Expand Down
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
3 changes: 0 additions & 3 deletions onboard/backend/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#![feature(async_closure)]
#![feature(path_file_prefix)]

/**
* All the servers run by the backend that communicate with other processes on devcade
*/
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