Je vais dodo fleme de finir maintenant
All checks were successful
Rust Checks / checks (push) Successful in 2m54s
Rust Checks / checks (pull_request) Successful in 19s

This commit is contained in:
Tipragot 2024-01-09 23:26:43 +01:00
parent 3ec6d37dfa
commit 2c1756e704
10 changed files with 331 additions and 903 deletions

2
.gitea/template Normal file
View file

@ -0,0 +1,2 @@
Cargo.toml
Cargo.lock

19
.gitea/workflows/rust.yml Normal file
View file

@ -0,0 +1,19 @@
on: [push, pull_request]
name: Rust Checks
jobs:
checks:
runs-on: main
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Use cache
run: mkdir -p /cache/${{ gitea.repository }} && ln -s /cache/${{ gitea.repository }} target
- name: Cargo fmt
run: cargo fmt --check
- name: Cargo build
run: cargo build
- name: Cargo test
run: cargo test
- name: Cargo clippy
run: cargo clippy -- -D warnings

1015
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,12 +1,21 @@
[package]
name = "ponguito-serv"
name = "ponguito-server"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
# [lints.rust]
# missing_docs = "warn"
# [lints.clippy]
# missing_docs_in_private_items = "warn"
# unwrap_in_result = "warn"
# unwrap_used = "warn"
# nursery = "warn"
[dependencies]
chrono = "0.4.31"
rocket = { version = "0.5.0", features = ["json"] }
serde = "1.0.195"
axum = "0.7.3"
axum-client-ip = "0.5.0"
chrono = { version = "0.4.31", features = ["serde"] }
serde = { version = "1.0.195", features = ["derive"] }
serde_json = "1.0.111"
tokio = { version = "1.35.1", features = ["full"] }

View file

@ -6,5 +6,5 @@ RUN cargo build --release
FROM alpine:latest
WORKDIR /app
COPY --from=builder /app/target/release/ponguito-serv .
CMD ["./ponguito-serv"]
COPY --from=builder /app/target/release/ponguito-server .
CMD ["./ponguito-server"]

View file

@ -1 +0,0 @@
[]

View file

@ -1,10 +0,0 @@
import requests as rq
IP = "127.0.0.1"
post = {"name": "coco_sol", "score": 10}
rq.post(f"http://{IP}/new_score", post)
rq.post(f"http://{IP}/new_score", post)
print(rq.get(f"http://{IP}/data").json())

18
rustfmt.toml Normal file
View file

@ -0,0 +1,18 @@
use_try_shorthand = true
use_field_init_shorthand = true
version = "Two"
error_on_line_overflow = true
error_on_unformatted = true
format_code_in_doc_comments = true
format_macro_bodies = true
format_macro_matchers = true
format_strings = true
imports_granularity = "Module"
group_imports = "StdExternalCrate"
normalize_doc_attributes = true
normalize_comments = true
wrap_comments = true

8
scores.log Normal file
View file

@ -0,0 +1,8 @@
{"name": "Tipragot", "ip": "86.206.50.244"}
sqdsq
sqdsqqs
d
q
sqdsqd

View file

@ -1,80 +1,88 @@
use chrono::Utc;
use rocket::form::Form;
use rocket::serde::json::Json;
use rocket::{get, launch, Config, FromForm};
use rocket::{post, routes};
use serde::Deserialize;
use serde::Serialize;
use std::fs::File;
use std::io::{Read, Write};
//! Un simple serveur http pour enregistrer des scores du jeu Ponguito.
use std::collections::HashMap;
use std::net::IpAddr;
#[derive(Deserialize, Serialize, FromForm, Clone, Debug)]
struct NewScore {
use axum::extract::Path;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::{Json, Router};
use axum_client_ip::LeftmostXForwardedFor;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use tokio::fs::{File, OpenOptions};
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
/// Structure du score.
#[derive(Serialize, Deserialize)]
struct Score {
ip: IpAddr,
time: DateTime<Utc>,
name: String,
score: u32,
}
#[post("/new_score", data = "<score>")]
fn new_score(score: Form<NewScore>) -> Option<()> {
let name = score.name.clone();
let score = score.score.clone();
#[tokio::main]
async fn main() {
let app = Router::new()
.route("/best-scores", get(get_best_scores))
.route("/register/:name/:score", get(register_score));
save_data(name, score, Utc::now().to_string());
Some(())
let listener = tokio::net::TcpListener::bind("0.0.0.0:80").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
#[get("/all-data")]
fn all_data() -> Json<Vec<(u32, String, String)>> {
let best_scores: Vec<_> = load_data().into_iter().map(|(k, v, d)| (v, k, d)).collect();
/// Récupération des meilleurs scores pour chaque joueur.
async fn get_best_scores() -> impl IntoResponse {
let mut lines = BufReader::new(
File::open("scores.log")
.await
.expect("failed to load scores file"),
)
.lines();
Json(best_scores)
let mut players: HashMap<String, u32> = HashMap::new();
while let Ok(Some(line)) = lines.next_line().await {
let score: Score = serde_json::from_str(&line).expect("failed to deserialize score");
players
.entry(score.name)
.and_modify(|last_score| {
if score.score > *last_score {
*last_score = score.score
}
})
.or_insert(score.score);
}
Json(players)
}
#[get("/data")]
fn data() -> Json<Vec<(u32, String, String)>> {
let mut best_scores: Vec<_> = load_data().into_iter().map(|(k, v, d)| (v, k, d)).collect();
/// Enregistrement d'un nouveau score.
async fn register_score(
LeftmostXForwardedFor(ip): LeftmostXForwardedFor,
Path((name, score)): Path<(String, u32)>,
) -> impl IntoResponse {
// Création du fichier de logs des scores si il n'existe pas
let mut file = OpenOptions::new()
.create(true)
.append(true)
.open("scores.log")
.await
.expect("failed to load scores file");
let nb = 5.min(best_scores.len());
// Enregistrement du score
let score = Score {
ip,
time: Utc::now(),
name,
score,
};
let mut data = serde_json::to_vec(&score).expect("failed to serialize score");
data.push(b'\n');
file.write_all(&data)
.await
.expect("failed to write to scores file");
best_scores.sort_by(|a, b| b.0.cmp(&a.0)); // sort by score();
best_scores.truncate(nb);
Json(best_scores)
}
fn save_data(name: String, score: u32, date: String) {
let mut data = load_data();
data.push((name, score, date));
let json = serde_json::to_string(&data).unwrap();
let mut file = File::create("data.json").unwrap();
file.write_all(json.as_bytes()).unwrap();
}
fn load_data() -> Vec<(String, u32, String)> {
let mut file = File::open("data.json").unwrap();
let mut contents = String::new();
file.read_to_string(&mut contents).unwrap();
serde_json::from_str(&contents).unwrap()
}
// The main function of the website.
#[launch]
async fn rocket() -> _ {
// generate a secret key and figment the rocket server
let figment = Config::figment()
.merge(("port", 80))
.merge(("worker_count", 4))
.merge(("log_level", rocket::config::LogLevel::Critical))
.merge(("address", IpAddr::from([0, 0, 0, 0])));
// create a config
let config = Config::from(figment);
// launch the server with different routes
rocket::build()
// apply the config
.configure(config)
.mount("/", routes![new_score, data, all_data])
// Renvoi du statut
StatusCode::CREATED
}