From f54f6aa123545eccb4622152c41a88742cd6e8a1 Mon Sep 17 00:00:00 2001 From: kralverde Date: Thu, 17 Oct 2024 20:19:38 -0400 Subject: [PATCH 1/5] add favicon to config --- docs/config/basic.md | 8 ++++++++ pumpkin-config/src/lib.rs | 4 ++++ pumpkin/src/server/connection_cache.rs | 5 +++-- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/config/basic.md b/docs/config/basic.md index 7d646c33..f7635e54 100644 --- a/docs/config/basic.md +++ b/docs/config/basic.md @@ -100,6 +100,14 @@ The server's description displayed on the status screen. motd=true ``` +## Favicon + +The path to the server's favicon + +```toml +favicon_path= +``` + ## Default gamemode The default game mode for players diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index 49821864..eeca5d97 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -97,6 +97,9 @@ pub struct BasicConfiguration { /// Whether to remove IPs from logs or not #[serde_inline_default(true)] pub scrub_ips: bool, + /// Path to server favicon + #[serde(default = "String::new")] + pub favicon_path: String, } fn default_server_address() -> SocketAddr { @@ -119,6 +122,7 @@ impl Default for BasicConfiguration { motd: "A Blazing fast Pumpkin Server!".to_string(), default_gamemode: GameMode::Survival, scrub_ips: true, + favicon_path: "".to_string(), } } } diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index 1a57dffe..da972d10 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -57,8 +57,9 @@ impl CachedStatus { } pub fn build_response(config: &BasicConfiguration) -> StatusResponse { - let icon_path = "/icon.png"; - let icon = if Path::new(icon_path).exists() { + let icon_path = &config.favicon_path; + + let icon = if !icon_path.is_empty() && Path::new(icon_path).exists() { Some(Self::load_icon(icon_path)) } else { None From 6b7d6249de6cad1a14ee956e713dcc2b120b2862 Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 18 Oct 2024 15:58:12 -0400 Subject: [PATCH 2/5] fix png encoding, default to ./icon.png, use built-in default --- pumpkin/icon.png => assets/default_icon.png | Bin pumpkin-macros/Cargo.toml | 4 ++ pumpkin-macros/src/icon.rs | 30 ++++++++++ pumpkin-macros/src/lib.rs | 7 +++ pumpkin/Cargo.toml | 3 +- pumpkin/src/server/connection_cache.rs | 61 +++++++++++++++----- 6 files changed, 90 insertions(+), 15 deletions(-) rename pumpkin/icon.png => assets/default_icon.png (100%) create mode 100644 pumpkin-macros/src/icon.rs diff --git a/pumpkin/icon.png b/assets/default_icon.png similarity index 100% rename from pumpkin/icon.png rename to assets/default_icon.png diff --git a/pumpkin-macros/Cargo.toml b/pumpkin-macros/Cargo.toml index 5dca73b9..0e351b0d 100644 --- a/pumpkin-macros/Cargo.toml +++ b/pumpkin-macros/Cargo.toml @@ -13,3 +13,7 @@ syn = "2.0" serde.workspace = true itertools.workspace = true serde_json = "1.0.128" + +# icon loading +base64 = "0.22.1" +png = "0.17.14" diff --git a/pumpkin-macros/src/icon.rs b/pumpkin-macros/src/icon.rs new file mode 100644 index 00000000..73d3b6fd --- /dev/null +++ b/pumpkin-macros/src/icon.rs @@ -0,0 +1,30 @@ +use std::{io::Cursor, sync::LazyLock}; + +use base64::{engine::general_purpose, Engine as _}; +use proc_macro::TokenStream; +use quote::quote; + +// TODO: This is the same as pumpkin/src/server/connection_cache.rs, but we cannot reference that in +// this crate +fn load_icon(data: &[u8]) -> String { + let icon = png::Decoder::new(Cursor::new(data)); + let reader = icon.read_info().unwrap(); + let info = reader.info(); + assert!(info.width == 64, "Icon width must be 64"); + assert!(info.height == 64, "Icon height must be 64"); + + // Once we validate the dimensions, we can encode the image as-is + let mut result = "data:image/png;base64,".to_owned(); + general_purpose::STANDARD.encode_string(data, &mut result); + result +} + +static ICON: LazyLock<&[u8]> = LazyLock::new(|| include_bytes!("../../assets/default_icon.png")); + +pub fn create_icon_impl() -> TokenStream { + let encoded_icon = load_icon(&ICON); + quote! { + #encoded_icon + } + .into() +} diff --git a/pumpkin-macros/src/lib.rs b/pumpkin-macros/src/lib.rs index 3c27f91a..a4fced0f 100644 --- a/pumpkin-macros/src/lib.rs +++ b/pumpkin-macros/src/lib.rs @@ -40,3 +40,10 @@ pub fn blocks_enum(_item: TokenStream) -> TokenStream { pub fn block_categories_enum(_item: TokenStream) -> TokenStream { block_state::block_type_enum_impl() } + +mod icon; +#[proc_macro] +/// Creates the default server icon +pub fn create_icon(_item: TokenStream) -> TokenStream { + icon::create_icon_impl() +} diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 8e737ab6..7808c0df 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] # pumpkin pumpkin-core = { path = "../pumpkin-core" } +pumpkin-macros = { path = "../pumpkin-macros" } pumpkin-config = { path = "../pumpkin-config" } pumpkin-inventory = { path = "../pumpkin-inventory" } pumpkin-world = { path = "../pumpkin-world" } @@ -53,7 +54,7 @@ thiserror = "1.0" # icon loading base64 = "0.22.1" -png = "0.17.14" +png = "0.17.14" # logging simple_logger = { version = "5.0.0", features = ["threads"] } diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index da972d10..03d462c7 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -1,4 +1,9 @@ -use std::{fs::File, path::Path}; +use core::error; +use std::{ + fs::File, + io::{Cursor, Read}, + path::Path, +}; use base64::{engine::general_purpose, Engine as _}; use pumpkin_config::{BasicConfiguration, BASIC_CONFIG}; @@ -59,9 +64,37 @@ impl CachedStatus { pub fn build_response(config: &BasicConfiguration) -> StatusResponse { let icon_path = &config.favicon_path; - let icon = if !icon_path.is_empty() && Path::new(icon_path).exists() { - Some(Self::load_icon(icon_path)) + let icon = if icon_path.is_empty() { + // See if an icon exists at ./icon.png + let default_local_path = "./icon.png"; + if Path::new(default_local_path).exists() { + log::info!("Loading server icon from {}", default_local_path); + let maybe_icon = Self::load_icon(default_local_path); + match maybe_icon { + Ok(result) => Some(result), + Err(e) => { + log::warn!("Failed to load icon: {:?}", e); + None + } + } + } else { + log::info!("Using default server icon"); + Some(pumpkin_macros::create_icon!().to_string()) + } + } else if Path::new(icon_path).exists() { + log::info!("Loading server icon from {}", icon_path); + let maybe_icon = Self::load_icon(icon_path); + match maybe_icon { + Ok(result) => Some(result), + Err(e) => { + log::warn!("Failed to load icon: {:?}", e); + None + } + } } else { + // TODO: Add definitive option to have no icon? + // Currently can just use a bad path + log::warn!("Failed to load server icon at path {}", icon_path); None }; @@ -84,20 +117,20 @@ impl CachedStatus { } } - fn load_icon>(path: P) -> String { - let icon = png::Decoder::new(File::open(path).expect("Failed to load icon")); - let mut reader = icon.read_info().unwrap(); + fn load_icon>(path: P) -> Result> { + let mut icon_file = File::open(path).expect("Failed to load icon"); + let mut buf = Vec::new(); + icon_file.read_to_end(&mut buf)?; + + let icon = png::Decoder::new(Cursor::new(&buf)); + let reader = icon.read_info()?; let info = reader.info(); assert!(info.width == 64, "Icon width must be 64"); assert!(info.height == 64, "Icon height must be 64"); - // Allocate the output buffer. - let mut buf = vec![0; reader.output_buffer_size()]; - // Read the next frame. An APNG might contain multiple frames. - let info = reader.next_frame(&mut buf).unwrap(); - // Grab the bytes of the image. - let bytes = &buf[..info.buffer_size()]; + + // Reader consumes the image. Once we verify dimensions, we want to encode the entire raw image let mut result = "data:image/png;base64,".to_owned(); - general_purpose::STANDARD.encode_string(bytes, &mut result); - result + general_purpose::STANDARD.encode_string(&buf, &mut result); + Ok(result) } } From a79f11461b8ccb08f65e0fb6624ad221fb655d91 Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 18 Oct 2024 16:25:26 -0400 Subject: [PATCH 3/5] condense icon macro --- pumpkin-macros/src/icon.rs | 30 ------------------------------ pumpkin-macros/src/lib.rs | 19 ++++++++++++++++--- 2 files changed, 16 insertions(+), 33 deletions(-) delete mode 100644 pumpkin-macros/src/icon.rs diff --git a/pumpkin-macros/src/icon.rs b/pumpkin-macros/src/icon.rs deleted file mode 100644 index 73d3b6fd..00000000 --- a/pumpkin-macros/src/icon.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::{io::Cursor, sync::LazyLock}; - -use base64::{engine::general_purpose, Engine as _}; -use proc_macro::TokenStream; -use quote::quote; - -// TODO: This is the same as pumpkin/src/server/connection_cache.rs, but we cannot reference that in -// this crate -fn load_icon(data: &[u8]) -> String { - let icon = png::Decoder::new(Cursor::new(data)); - let reader = icon.read_info().unwrap(); - let info = reader.info(); - assert!(info.width == 64, "Icon width must be 64"); - assert!(info.height == 64, "Icon height must be 64"); - - // Once we validate the dimensions, we can encode the image as-is - let mut result = "data:image/png;base64,".to_owned(); - general_purpose::STANDARD.encode_string(data, &mut result); - result -} - -static ICON: LazyLock<&[u8]> = LazyLock::new(|| include_bytes!("../../assets/default_icon.png")); - -pub fn create_icon_impl() -> TokenStream { - let encoded_icon = load_icon(&ICON); - quote! { - #encoded_icon - } - .into() -} diff --git a/pumpkin-macros/src/lib.rs b/pumpkin-macros/src/lib.rs index a4fced0f..0e775bf6 100644 --- a/pumpkin-macros/src/lib.rs +++ b/pumpkin-macros/src/lib.rs @@ -1,3 +1,6 @@ +use std::{io::Cursor, sync::LazyLock}; + +use base64::{engine::general_purpose, Engine as _}; use proc_macro::TokenStream; use quote::quote; @@ -41,9 +44,19 @@ pub fn block_categories_enum(_item: TokenStream) -> TokenStream { block_state::block_type_enum_impl() } -mod icon; +static ICON: LazyLock<&[u8]> = LazyLock::new(|| include_bytes!("../../assets/default_icon.png")); #[proc_macro] -/// Creates the default server icon +/// Returns a base64 string encoding of the default server favicon pub fn create_icon(_item: TokenStream) -> TokenStream { - icon::create_icon_impl() + let icon = png::Decoder::new(Cursor::new(ICON.as_ref())); + let reader = icon.read_info().unwrap(); + let info = reader.info(); + assert!(info.width == 64, "Icon width must be 64"); + assert!(info.height == 64, "Icon height must be 64"); + + // Once we validate the dimensions, we can encode the image as-is + let mut result = "data:image/png;base64,".to_owned(); + general_purpose::STANDARD.encode_string(ICON.as_ref(), &mut result); + + quote! {#result}.into() } From 086e347d3a8c76eb4a4fe17a978ae50c34b2510d Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 18 Oct 2024 16:53:49 -0400 Subject: [PATCH 4/5] rework favicon selection process --- docs/config/basic.md | 12 +++- icon.png | Bin 0 -> 7601 bytes pumpkin-config/src/lib.rs | 8 ++- pumpkin-macros/Cargo.toml | 4 -- pumpkin-macros/src/lib.rs | 20 ------- pumpkin/Cargo.toml | 1 - pumpkin/src/server/connection_cache.rs | 79 +++++++++++-------------- 7 files changed, 50 insertions(+), 74 deletions(-) create mode 100644 icon.png diff --git a/docs/config/basic.md b/docs/config/basic.md index f7635e54..7eda8546 100644 --- a/docs/config/basic.md +++ b/docs/config/basic.md @@ -100,12 +100,20 @@ The server's description displayed on the status screen. motd=true ``` -## Favicon +## Use favicon + +Whether to use a server favicon or not + +```toml +use_favicon=true +``` + +## Favicon path The path to the server's favicon ```toml -favicon_path= +favicon_path=./icon.png ``` ## Default gamemode diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..81b083f4d8ffe6a235c46ae35cabcc31d7249fcc GIT binary patch literal 7601 zcmeHKXH=6}w@&EBhS(@d38Ek*q);PBlMYe^q#9Bn0wE0uNL6VgqKH%x89@+4M3gE; z5k*8m=|$;XX(G}D?i(!QtoyBX*Q|BFfAhXMIc@Lf?DOn>c5)(3PwH-m2tq(0&~|;j z6Xrmz&MG`yz;_Ob+6z=~2{!sfef^^#9-sqAf;m87mJCq9zqQ%G>>zevEDR`oK$|7Q zDuQ6n%{mHb3;k$c2ilT9WLP>#FdJw)P^SO|1+@17bq-KS;osM)1lp{JXY>6g1p?Ut zy`QzNzOkt&3XVj;(Mo_m0wsq*C;=Z)BnpF;$DonG`rx#`EMg&zp0(}->E1jMN7Jy+)s>nk!a%vS2us1eiiDiA$7Xmii>BIHji*S2TF&1$aA zS2IeKAbFhJLHq9QfH#(5N+9R^cSbzA5PSR5^(6;$*Ih#gYgNk^D`b88!0W`gY@!L9 zseJvbjBA?ks-qlEyKW1-q7&f+47Jg;MhnhO#usAku0XfX>&qh5z5vE

o+~3&GgdRi<#2T==3_y$d zh2=4jiW-xG#kmm}qRs>t5?K{G^P~zYO2Vr`t>ujo#uP1rD@o6fMzHWZX^Hc5!ztsT zYR4feObh_vL117-nI7(BI);id|;0Rl2Fa5w%EH7tdwMZcp-^C6^rw9u6l3FG@MQWA763kEnOKS} z5{{7d@R0pogU-9Xlfx3wFeL;U1;fb` z0^(7!?Hlj#gB8Ao720pM^FfP*6v@dzwI0Vao+Q-;YYqZMFSC3z$ai9@25 z5za)EBJy_#6B-GKO04_uRRT z0%6k{JPxDdN%O!0?j(6&T?n!ivdiWKi*SsFslF-{1xNffV(N}%5CH{Ms1b?m#r$i+ zlH@_KU|?BnA{9|+BoYlg2(+?-9O^G2D*}xUL?R1wGkI>#u-t+HHUkigWyL7~usIIw z1*1hHU>Tk?OHWUCRp_s@`)k@5I8Jyh1A79?AON7h60zmaL@bR!VG!TVXTceJ;z>l` z|4W*6ctlkIdBEj*Bs#FZ@8;0=6JNV6kb8@@91<- zBEttuBWSn)JOW$+;ko$}B`Ue;D5<|o`?wNVUH}M#A%Kuo`bil22Vt^5HZ04^89#Td zBKyBMQP~vuEy)0S-^YOD1>{26U&-(XXRNgIUwnS};=i~80R8VG|A^mz>H3$hf5gB) z68<;3{-x_5G4PLs|BbHyH@YByU8e|S;9rmra9NTXd^`wTw78rNbx(jcSZ`WIRxB{G zg`#(w4g$f&Sq03zqvj0^ax?UewYkT6cp&0pDy9Vy!0pXg|AdAmvwJGp_v(SRfLgT~ zuf_L|k_oa?XRQkkF+?9cL* z3S4vCsGRZf52_-U$Rvyu*vDVVk1#A{;v!C~E2j-996Qbjl7XCYt$Pssc^<_mIkVSMR8Q7l?GCU%$ab6~kSlj>-TFcL*|P_eEY-5g$}FKH%y_ZZR-30z zuG1+)&F#%^)L#xA5i%E^MNt~glWvo(;;;g2MT+OhZpA0rK89osDLinH0|{!q;Y1N zwlqYremz!InS*LYX!ATi_w{!3-B|rz<>aBHbj=5MLwt9^Y_BmvE#eVjJeF2A1;@D6 zm_258+(zq+(-n7<2CWBzrJ#XY0_KUUJBpb?iwlu2z$QTzTP~z+(bx(N3}YYgOD+s= zUJ;VbYI!*yCw`KkLeY2)^~6Gp|0+K8Pg~KsydW_7fG`V~!_PBP2fep5`w$(dC%D&AV`! z|H-o6%helw87Cpn%2lPzV{F%U%uv5oson+Lo0D-03-y8x|{ehP&Pg z5O|Bitda%Sr`sGW0&guglr*Rr3xY0-y%s#G?cXrj`Q@|kEspwIRfS&rGcakT2_08R z{@caYvX!3=(ji-HFRRRuUX?9ZfsSv92h)ZvkM+=2x-PS?)QyxB1&>(Gi)Up5mp`0Kzm+KO@-QQpFXN&`tytT(sWrw<$-ov5znwu)XsG0ECBIq8 z8eMHi>_$H}NxL|+=nbz__QEPGx-!Qc62|#9Cj!gJI{5MFmc0`UrdPFP39`n+$(9^I z)URm58+fhbJE-FUB3uPmsi*tGYy*t-ww+o}34%ChKDmGp@Hk zWvTLo*nZ|+q)g6{^nFXkZ5<~*4_(_buEZ9}zAUY(K-ym+FO}qkGe%ue>u?TZ!wQ_| zez5iK!6VlXPel()w#AqF=M89m)IGuhI7mqNNnLS1R8A?^ZU2{puQ!SgQug#b=}*l( z1Fr9g-S(14V&p5FgOt^Oa1jpS=^u!+eR|)}#oA5KN*zCn=uLK7sg^O> zyQr{pw&lLQW4BwZ_hBwmhoo#rmGh?BARCp2Bf$cf#o|per2JNMCnH4pLxep!K2lRQUS)M>Ss@-wzz;C|4$Lwb^rfVEQnfdcx<~JD>So8}@Z22aM7Z zn!ePX*D%_q`oZHC=j?-cfkaO8_jTZeoffmh`x1^6mt5y2fUSAQJ{3rHoAb~@FMP^b z25<2vV|n^YVCv5+6dS*ewPc`kaX0Iw9~E<#(n9V)*p%O}t@~+TuZ<^lud3ugL#b*= z%DxQUyoKF!^=3oQqG%Qe)FN$?=RZ%-6J>H2z(%`fN&P-UULmLEb1q4ajmGu(LWE_| z27jz)u}`$RQYYu;%0PA^b&sB;N$CubeR*@l!yG%p!-tYnp42t(Bqs8z4n}KdF0QIh z|3L-6<%gE%YxJ7i87Z5j%XNY$lFSp+cQ;f&Ydn=aeI4`sLJpLRe)KX`KD+w(K+)lt zEXCod>kS-192h^>T=eC>8S2>O4o-3jA(gPV$sl=pTkji)RP9M8aSdjca{YESlW6`A z-wHeL?nAxje9hKeT*w~KEzzVmqrv-f0x68Zm1|IXKS3TTW%;NSK+?sC5f2TcV)o@v zn|hm{o!97vz2(d`8q>&*j*%F@a! z*=CoXugrw2n8EV~_oo}#+PcT$KJ~2g^(A}t2rtCE zYYCGGRke+X(H<%nPLD7O39gDlDq&Os*-ukI6 zPUWM*z5RNbPBFJCkKBn2J+!g*XZ^7w@;a4orT9$4$;6}g8vcNMnS~=`q#JF{`WQ&5 za|EojcXnL^cUF!>Jjc~v(v8m##iY*eF*WCAtPMPhFB_d75w{r_6<@XvaVl+;RK8DE z>i2WB;n=B_^JEKoZklgSHL%RFq9Vi z5cfbJb0}xZxP@c(@LCu5>r49$u=@2K&R+<7%rNIj!s%(wKRDBc|#&SllE<)q_q?Sb)?g%Ur%B>t7ueA>y% z-EsA^y_IVR+*_ua3sP_BS$xTADo==s&f040Ys_1Wp4eM-nu@LR?rNL-gG6BSzp(BZ zr*()cPiu5Zc;<)+j)>HqRw zZ@gA&?)I&X8R~JNm2lUtx|eJB4@|Xk7e2Gt?Qw}+Wo|~u{icfIg<`F%H}l}~ zo+j}JOiBjVmo=9kU*q5g&$^FyM&Fzqt-XQbTk5D_zq^Hvl82OP?&~zM z^M2HDar+rIvQNmTf(bnx{{lzNPQ}5I@%bm_y^$At_Z9o*Wn>ye-c$QL-%uLAJ-ns2 zRKm`IN1FI&^hj5`Z`QLp%lIsF;RodjErv&fXWz=Y_a2WwrPiI;0CJM?YQLsul%-Yn z(z(8jz41avBOaZlQcXU+vxK4h4`w{pykk;5aqMgBq_Tc3G?Odk@&{)5LC7=f?e&L}I#obE{knEaxYmb{76xmT~{6M5pVc7mp zcD}-|k~&`7r zH?pfkDFa!v@btd=n&?%hZ>z`H2BYt3>b&K{HQsnFRbAP<4e{}8;J4A>Ns`YO>%1^x zi)=CD=-PX$v4H-$l7_N6YW@~IT*QswTO0vz&bcO;D7LRKJd(stUKn877A`7%n)x~` z6rS?tdK%BSp&ZG!m_eOHJ?V-^^3s9JMm(fh|I;_8g1xhdeWqgXeprj)zx#zrZ+uR$C=GGP93`wE}l7 OkiPcG6M33v&i@aB&`^Z{ literal 0 HcmV?d00001 diff --git a/pumpkin-config/src/lib.rs b/pumpkin-config/src/lib.rs index eeca5d97..ebe9c99c 100644 --- a/pumpkin-config/src/lib.rs +++ b/pumpkin-config/src/lib.rs @@ -97,8 +97,11 @@ pub struct BasicConfiguration { /// Whether to remove IPs from logs or not #[serde_inline_default(true)] pub scrub_ips: bool, + /// Whether to use a server favicon + #[serde_inline_default(true)] + pub use_favicon: bool, /// Path to server favicon - #[serde(default = "String::new")] + #[serde_inline_default("icon.png".to_string())] pub favicon_path: String, } @@ -122,7 +125,8 @@ impl Default for BasicConfiguration { motd: "A Blazing fast Pumpkin Server!".to_string(), default_gamemode: GameMode::Survival, scrub_ips: true, - favicon_path: "".to_string(), + use_favicon: true, + favicon_path: "icon.png".to_string(), } } } diff --git a/pumpkin-macros/Cargo.toml b/pumpkin-macros/Cargo.toml index 0e351b0d..5dca73b9 100644 --- a/pumpkin-macros/Cargo.toml +++ b/pumpkin-macros/Cargo.toml @@ -13,7 +13,3 @@ syn = "2.0" serde.workspace = true itertools.workspace = true serde_json = "1.0.128" - -# icon loading -base64 = "0.22.1" -png = "0.17.14" diff --git a/pumpkin-macros/src/lib.rs b/pumpkin-macros/src/lib.rs index 0e775bf6..3c27f91a 100644 --- a/pumpkin-macros/src/lib.rs +++ b/pumpkin-macros/src/lib.rs @@ -1,6 +1,3 @@ -use std::{io::Cursor, sync::LazyLock}; - -use base64::{engine::general_purpose, Engine as _}; use proc_macro::TokenStream; use quote::quote; @@ -43,20 +40,3 @@ pub fn blocks_enum(_item: TokenStream) -> TokenStream { pub fn block_categories_enum(_item: TokenStream) -> TokenStream { block_state::block_type_enum_impl() } - -static ICON: LazyLock<&[u8]> = LazyLock::new(|| include_bytes!("../../assets/default_icon.png")); -#[proc_macro] -/// Returns a base64 string encoding of the default server favicon -pub fn create_icon(_item: TokenStream) -> TokenStream { - let icon = png::Decoder::new(Cursor::new(ICON.as_ref())); - let reader = icon.read_info().unwrap(); - let info = reader.info(); - assert!(info.width == 64, "Icon width must be 64"); - assert!(info.height == 64, "Icon height must be 64"); - - // Once we validate the dimensions, we can encode the image as-is - let mut result = "data:image/png;base64,".to_owned(); - general_purpose::STANDARD.encode_string(ICON.as_ref(), &mut result); - - quote! {#result}.into() -} diff --git a/pumpkin/Cargo.toml b/pumpkin/Cargo.toml index 7808c0df..89091407 100644 --- a/pumpkin/Cargo.toml +++ b/pumpkin/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] # pumpkin pumpkin-core = { path = "../pumpkin-core" } -pumpkin-macros = { path = "../pumpkin-macros" } pumpkin-config = { path = "../pumpkin-config" } pumpkin-inventory = { path = "../pumpkin-inventory" } pumpkin-world = { path = "../pumpkin-world" } diff --git a/pumpkin/src/server/connection_cache.rs b/pumpkin/src/server/connection_cache.rs index 03d462c7..b90a7d16 100644 --- a/pumpkin/src/server/connection_cache.rs +++ b/pumpkin/src/server/connection_cache.rs @@ -3,6 +3,7 @@ use std::{ fs::File, io::{Cursor, Read}, path::Path, + sync::LazyLock, }; use base64::{engine::general_purpose, Engine as _}; @@ -14,6 +15,29 @@ use pumpkin_protocol::{ use super::CURRENT_MC_VERSION; +static DEFAULT_ICON: LazyLock<&[u8]> = + LazyLock::new(|| include_bytes!("../../../assets/default_icon.png")); + +fn load_icon_from_file>(path: P) -> Result> { + let mut icon_file = File::open(path)?; + let mut buf = Vec::new(); + icon_file.read_to_end(&mut buf)?; + load_icon_from_bytes(&buf) +} + +fn load_icon_from_bytes(png_data: &[u8]) -> Result> { + let icon = png::Decoder::new(Cursor::new(&png_data)); + let reader = icon.read_info()?; + let info = reader.info(); + assert!(info.width == 64, "Icon width must be 64"); + assert!(info.height == 64, "Icon height must be 64"); + + // Reader consumes the image. Once we verify dimensions, we want to encode the entire raw image + let mut result = "data:image/png;base64,".to_owned(); + general_purpose::STANDARD.encode_string(png_data, &mut result); + Ok(result) +} + pub struct CachedStatus { _status_response: StatusResponse, // We cache the json response here so we don't parse it every time someone makes a Status request. @@ -62,39 +86,21 @@ impl CachedStatus { } pub fn build_response(config: &BasicConfiguration) -> StatusResponse { - let icon_path = &config.favicon_path; - - let icon = if icon_path.is_empty() { - // See if an icon exists at ./icon.png - let default_local_path = "./icon.png"; - if Path::new(default_local_path).exists() { - log::info!("Loading server icon from {}", default_local_path); - let maybe_icon = Self::load_icon(default_local_path); - match maybe_icon { - Ok(result) => Some(result), - Err(e) => { - log::warn!("Failed to load icon: {:?}", e); - None - } - } - } else { - log::info!("Using default server icon"); - Some(pumpkin_macros::create_icon!().to_string()) - } - } else if Path::new(icon_path).exists() { - log::info!("Loading server icon from {}", icon_path); - let maybe_icon = Self::load_icon(icon_path); - match maybe_icon { + let icon = if config.use_favicon { + let icon_path = &config.favicon_path; + log::info!("Loading server favicon from '{}'", icon_path); + match load_icon_from_file(icon_path).or_else(|err| { + log::warn!("Failed to load icon from '{}': {}", icon_path, err); + load_icon_from_bytes(DEFAULT_ICON.as_ref()) + }) { Ok(result) => Some(result), - Err(e) => { - log::warn!("Failed to load icon: {:?}", e); + Err(err) => { + log::warn!("Failed to load default icon: {}", err); None } } } else { - // TODO: Add definitive option to have no icon? - // Currently can just use a bad path - log::warn!("Failed to load server icon at path {}", icon_path); + log::info!("Not using a server favicon"); None }; @@ -116,21 +122,4 @@ impl CachedStatus { enforce_secure_chat: false, } } - - fn load_icon>(path: P) -> Result> { - let mut icon_file = File::open(path).expect("Failed to load icon"); - let mut buf = Vec::new(); - icon_file.read_to_end(&mut buf)?; - - let icon = png::Decoder::new(Cursor::new(&buf)); - let reader = icon.read_info()?; - let info = reader.info(); - assert!(info.width == 64, "Icon width must be 64"); - assert!(info.height == 64, "Icon height must be 64"); - - // Reader consumes the image. Once we verify dimensions, we want to encode the entire raw image - let mut result = "data:image/png;base64,".to_owned(); - general_purpose::STANDARD.encode_string(&buf, &mut result); - Ok(result) - } } From 037315bfd5e416f323134a27a767bf6200c43164 Mon Sep 17 00:00:00 2001 From: kralverde Date: Fri, 18 Oct 2024 16:54:09 -0400 Subject: [PATCH 5/5] remove icon --- icon.png | Bin 7601 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 icon.png diff --git a/icon.png b/icon.png deleted file mode 100644 index 81b083f4d8ffe6a235c46ae35cabcc31d7249fcc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7601 zcmeHKXH=6}w@&EBhS(@d38Ek*q);PBlMYe^q#9Bn0wE0uNL6VgqKH%x89@+4M3gE; z5k*8m=|$;XX(G}D?i(!QtoyBX*Q|BFfAhXMIc@Lf?DOn>c5)(3PwH-m2tq(0&~|;j z6Xrmz&MG`yz;_Ob+6z=~2{!sfef^^#9-sqAf;m87mJCq9zqQ%G>>zevEDR`oK$|7Q zDuQ6n%{mHb3;k$c2ilT9WLP>#FdJw)P^SO|1+@17bq-KS;osM)1lp{JXY>6g1p?Ut zy`QzNzOkt&3XVj;(Mo_m0wsq*C;=Z)BnpF;$DonG`rx#`EMg&zp0(}->E1jMN7Jy+)s>nk!a%vS2us1eiiDiA$7Xmii>BIHji*S2TF&1$aA zS2IeKAbFhJLHq9QfH#(5N+9R^cSbzA5PSR5^(6;$*Ih#gYgNk^D`b88!0W`gY@!L9 zseJvbjBA?ks-qlEyKW1-q7&f+47Jg;MhnhO#usAku0XfX>&qh5z5vE

o+~3&GgdRi<#2T==3_y$d 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 OkiPcG6M33v&i@aB&`^Z{