jojoleprowebsite

[Done] The Sauce of https://jojolepro.com
git clone https://git.jojolepro.com/jojoleprowebsite.git
Log | Files | Refs | README | LICENSE

commit d45a1675643a86ba3c4ea0c5764c8130f43d8d27
parent c604add6e8aa65f11643f016b451d45a44b7d4d4
Author: jojolepro <jojolepro@jojolepro.com>
Date:   Wed,  2 Jun 2021 12:59:42 -0400

pretify website.

Diffstat:
Mbuild.sh | 3+++
Msrc/blog/2021-05-31_minigene_and_the_future/index.txt | 8++++----
Msrc/blog/2021-06-01_getting_started_with_ecs/index.txt | 120++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtarget/gemini/blog/2021-05-31_minigene_and_the_future/index.txt | 8++++----
Mtarget/gemini/blog/2021-06-01_getting_started_with_ecs/index.txt | 120++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtarget/html/blog/2020-03-31_extracting_data_from_websites/index.html | 37+++++++++++++++----------------------
Mtarget/html/blog/2020-04-09_simple_school_documents/index.html | 37+++++++++++++++----------------------
Mtarget/html/blog/2020-05-04_why_i_moved_from_wordpress/index.html | 22++++++++++------------
Mtarget/html/blog/2020-07-07_minimalist_alternatives/index.html | 61+++++++++++++++++++++++--------------------------------------
Mtarget/html/blog/2020-08-20_event_chaining/index.html | 76++++++++++++++++++++++++++++------------------------------------------------
Mtarget/html/blog/2020-09-17_the_lost_of_autonomy/index.html | 16++++++++--------
Mtarget/html/blog/2020-11-17_the_fall_of_the_giant/index.html | 7+++++--
Mtarget/html/blog/2021-01-13_planck_ecs/index.html | 40+++++++++++++++++-----------------------
Mtarget/html/blog/2021-01-13_removing_the_us/index.html | 19+++++++++----------
Mtarget/html/blog/2021-05-31_minigene_and_the_future/index.html | 54++++++++++++++++++++++--------------------------------
Mtarget/html/blog/2021-05-31_minigene_and_the_future/index.txt | 8++++----
Mtarget/html/blog/2021-06-01_getting_started_with_ecs/index.html | 193++++++++++++++++++++++++++++++++++++-------------------------------------------
Mtarget/html/blog/2021-06-01_getting_started_with_ecs/index.txt | 120++++++++++++++++++++++++++++++++++++++++----------------------------------------
Mtarget/html/blog/index.html | 2++
Mtarget/html/focks/index.html | 22++++++++++------------
Mtarget/html/index.html | 16++++++++--------
Mtarget/html/projects/index.html | 49+++++++++++++++++++------------------------------
Mtarget/html/quotes/index.html | 7+++++--
Mtemplate.html | 2++
24 files changed, 480 insertions(+), 567 deletions(-)

diff --git a/build.sh b/build.sh @@ -52,6 +52,9 @@ while read -r page; do sed "s/&/\&amp;/g" "../../src/$page" | sed "s/</\&lt;/g" | sed "s/>/\&gt;/g" | + awk 'BEGIN {lastline="";} /=====/ {lastline="<pre style=\"color: lime;\">" lastline "</pre>";} {if ($0 !~ /=====/) {print lastline; lastline=$0;}} END {print lastline;}' | + awk 'BEGIN {lastline="";} /-----/ {lastline="<pre style=\"color: magenta;\">" lastline "</pre>";} {if ($0 !~ /-----/) {print lastline; lastline=$0;}} END {print lastline;}' | + awk 'BEGIN {in_quote = false;} /^#```/ {if (in_quote) {print "</pre>";} else {print "<pre style=\"color: orange\">";} in_quote = !in_quote;} {print $0}' | sed '/^#```/d' | sed -E "s|([^=][^\'\"])(https[:]//[^ )]*)|\1<a href='\2'>\2</a>|g" | diff --git a/src/blog/2021-05-31_minigene_and_the_future/index.txt b/src/blog/2021-05-31_minigene_and_the_future/index.txt @@ -68,10 +68,10 @@ minigene, we place the different features into groups and hide them behind feature gates. You can enable those features using: -``` +#``` minigene = { git = "https://github.com/jojolepro/minigene", features = ["feature1", "feature2", "..."] } -``` +#``` Finally, layer 4 is the game you want to make. Usually, it will import minigene and use some of its features. @@ -150,11 +150,11 @@ Bracket-lib drives the execution, either through its own event loop or through web assembly, and then calls Minigene's Engine::engine_frame function. The game can be tested using: -``` +#``` git clone https://github.com/amethyst/shotcaller cd shotcaller git checkout ac07a7bfb4224e8af4bd43e6df3afcaa879d4a48 -``` +#``` In the case this doesn't work, there is also an online version: https://shotcaller.jojolepro.com/ diff --git a/src/blog/2021-06-01_getting_started_with_ecs/index.txt b/src/blog/2021-06-01_getting_started_with_ecs/index.txt @@ -9,17 +9,17 @@ Creating our test project -------------------------------------------------------------------------------- Let's start a project so you can follow along. -``` +#``` cargo new --bin start_with_ecs cd start_with_ecs -``` +#``` Great! Let's add our dependency in Cargo.toml: -``` +#``` [dependencies] planck_ecs = "1.2.0" -``` +#``` Now, we are ready to start talking about the ECS! @@ -87,18 +87,18 @@ Open up src/main.rs We will first import Planck ECS' features into our game. Add the following line at the start of the file: -``` +#``` use planck_ecs::*; -``` +#``` Next, we will need something to contain the data we create. Let's create a new World inside of the main function: -``` +#``` fn main() { let mut world = World::default(); } -``` +#``` Our First Resource -------------------------------------------------------------------------------- @@ -106,12 +106,12 @@ Let's insert our first Resource into the world. We will use the Entities resource, which is a structure that holds a list of entities that exist in the World. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Entities>(); } -``` +#``` The initialize function will insert Entities inside of the World. @@ -125,13 +125,13 @@ Our First Entity -------------------------------------------------------------------------------- Let's now use our Entities Resource to create our first entity: -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Entities>(); let entity1 = world.get_mut::<Entities>().unwrap().create(); } -``` +#``` Here, get_mut gives us access to the Resource in World. Since World doesn't contain every single possible Resource, it returns an Option @@ -145,12 +145,12 @@ away, there is a convenient function that does both steps at the same time: world.get_mut_or_default. Let's rewrite our code using it: -``` +#``` fn main() { let mut world = World::default(); let entity1 = world.get_mut_or_default::<Entities>().create(); } -``` +#``` Our first Component -------------------------------------------------------------------------------- @@ -160,7 +160,7 @@ First, we will create the Color component. This can be anything: a struct, an enum, you choose. Here, let's use an enum. Add the following before or after the main function: -``` +#``` #[derive(Debug, PartialEq)] enum Color { Red, @@ -170,31 +170,31 @@ enum Color { fn main() { [...] -``` +#``` And now, we can tell the World that we want to use this enum as a Component by initializing a Components Resource and specifying that we want the Color enum. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); } -``` +#``` The Components<T> Resource is simply used to contain components of type T in a way that is easy to use and also is quick to access. Finally, let's attach our Component to the Entity! -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut::<Components<Color>>().unwrap().insert(entity1, Color::Red); } -``` +#``` Here we use the insert(entity, component) method available on the Components resource. @@ -202,14 +202,14 @@ resource. There are two simplifications that we can do to our code. First is to tell rust to guess that we want the Color component by using _. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut::<Components<_>>().unwrap().insert(entity1, Color::Red); } -``` +#``` Rust is smart enough to understand from our usage of Color::Red on the right side that what we want is Components<Color>. @@ -217,13 +217,13 @@ right side that what we want is Components<Color>. Now, this code looks strangely like the one we saw when creating the Entity previously. Let's apply the same trick: -``` +#``` fn main() { let mut world = World::default(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); } -``` +#``` Yep! We can use get_mut_or_default here too! @@ -240,14 +240,14 @@ As we saw previously, Systems are just functions that use data from the World. Let's write a System that changes the color of the apple to blue. -``` +#``` fn change_color_system(colors: &mut Components<Color>) -> SystemResult { Ok(()) } fn main() { [...] -``` +#``` We have some things to explain here. The parameters of Systems use references (that's the & and &mut symbols). @@ -255,14 +255,14 @@ A special rule for Systems is that all parameters using &mut must be placed after all parameters using &. For example, the following is invalid: -``` +#``` fn system(a: &mut A, b: &B, c: &mut C) -> SystemResult {...} -``` +#``` It should instead be written as: -``` +#``` fn system(b: &B, a: &mut A, c: &mut C) -> SystemResult {...} -``` +#``` Now, you might wonder what that SystemResult thing is. This is a feature that is unique to Planck ECS: Error handling. @@ -277,14 +277,14 @@ For this tutorial, we will always use Ok. Now, let's change the color of that apple! -``` +#``` fn change_color_system(colors: &mut Components<Color>) -> SystemResult { for color in join!(&mut colors) { *color = Color::Blue; } Ok(()) } -``` +#``` The join!() macro returns an iterator over the Components we reference. Here, we tell the join!() macro to give us all Color Components mutably, so @@ -296,12 +296,12 @@ Running the System -------------------------------------------------------------------------------- To run a System, we use: -``` +#``` fn main () { [...] change_color_system.system().run(&mut world).unwrap(); } -``` +#``` This will automatically get the required Components from World and call the function change_color_system. @@ -310,7 +310,7 @@ Now, what happens if the System uses a Resource which is not in World? It will crash! Let's fix this up: -``` +#``` fn main() { [...] let mut system = change_color_system.system(); @@ -318,7 +318,7 @@ fn main() { system.run(&mut world).unwrap(); } -``` +#``` When calling the initialize function on a System, it will use world.initialize() for every Resource that is in the System's parameters. @@ -334,7 +334,7 @@ They are also very convenient, because without them you would need three lines for each System! Here's how we use a Dispatcher: -``` +#``` fn main() { let mut world = World::default(); let mut dispatcher = DispatcherBuilder::default() @@ -346,7 +346,7 @@ fn main() { dispatcher.run_seq(&mut world).unwrap(); } -``` +#``` The Dispatcher creation is pretty much self explanatory. Simply chain .add(system).add(system2).add(system3) to add Systems, then complete the @@ -369,7 +369,7 @@ Now, let's come back to our game! Let's imagine that we had another Entity that also had a Color Component, but that we don't want to change its Color. -``` +#``` fn main() { let mut world = World::default(); let apple = world.get_mut_or_default::<Entities>().create(); @@ -377,7 +377,7 @@ fn main() { world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); } -``` +#``` With our current System, both entities will have their colors changed to blue. @@ -393,24 +393,24 @@ reusing Systems for more than one type of Entity. Let's create this Component: -``` +#``` struct ColorChanging; -``` +#``` and add it to our apple Entity: -``` +#``` let apple = world.get_mut_or_default::<Entities>().create(); let orange = world.get_mut_or_default::<Entities>().create(); world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); world.get_mut_or_default::<Components<_>>().insert(apple, ColorChanging); //here world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); -``` +#``` Finally, let's make it so our System only runs on Entities that have a ColorChanging Component: -``` +#``` fn change_color_system( changings: &Components<ColorChanging>, colors: &mut Components<Color>) -> SystemResult { @@ -421,7 +421,7 @@ fn change_color_system( Ok(()) } -``` +#``` First, we add the Components<ColorChanging> parameter. @@ -440,13 +440,13 @@ We need this when joining over multiple Components at the same time. This is because the join macro also supports || OR operations. For example, if we used: -``` +#``` join!(&mut colors || &changings) -``` +#``` we would have pairs of Components that look like this: -``` +#``` (&mut Option<Color>, &Option<ColorChanging>) -``` +#``` Because we use &&, we can assume that both Options have a value (Some). But if we use ||, then we cannot assume this, as one of the Entities has a @@ -456,7 +456,7 @@ Making sure everything works -------------------------------------------------------------------------------- It is always a good practice to verify that our Systems are working fine. Let's do this quickly in the main function: -``` +#``` [...] dispatcher.run_seq(&mut world).unwrap(); @@ -465,16 +465,16 @@ Let's do this quickly in the main function: assert_eq!(*world.get::<Components<Color>>().unwrap().get(orange).unwrap(), Color::Red); } -``` +#``` Removing a Component -------------------------------------------------------------------------------- Simple! Use: -``` +#``` world.get_mut::<Components<Color>>().unwrap().remove(apple); -``` +#``` Of course, this works inside of Systems too, since you have Components<Color> as a parameter! @@ -484,18 +484,18 @@ Removing an Entity Also simple! Use: -``` +#``` world.get_mut::<Entities>().unwrap().kill(apple); -``` +#``` However, it is important to know that removing an Entity using kill(entity) will NOT delete its Components that are in the Components<T> Resources. This can be done automatically by using the world.maintain() function: -``` +#``` world.maintain(); -``` +#``` It is recommended to run world.maintain() after using dispatcher.run_seq(&mut world), as this ensures everything is cleaned up! @@ -518,7 +518,7 @@ Full Code -------------------------------------------------------------------------------- Here is the full code created in this tutorial: -``` +#``` use planck_ecs::*; #[derive(Debug, PartialEq)] @@ -582,7 +582,7 @@ fn main() { world.maintain(); } -``` +#``` Patreon rocket goes brrrrrrrrrrrr diff --git a/target/gemini/blog/2021-05-31_minigene_and_the_future/index.txt b/target/gemini/blog/2021-05-31_minigene_and_the_future/index.txt @@ -68,10 +68,10 @@ minigene, we place the different features into groups and hide them behind feature gates. You can enable those features using: -``` +#``` minigene = { git = "https://github.com/jojolepro/minigene", features = ["feature1", "feature2", "..."] } -``` +#``` Finally, layer 4 is the game you want to make. Usually, it will import minigene and use some of its features. @@ -150,11 +150,11 @@ Bracket-lib drives the execution, either through its own event loop or through web assembly, and then calls Minigene's Engine::engine_frame function. The game can be tested using: -``` +#``` git clone https://github.com/amethyst/shotcaller cd shotcaller git checkout ac07a7bfb4224e8af4bd43e6df3afcaa879d4a48 -``` +#``` In the case this doesn't work, there is also an online version: https://shotcaller.jojolepro.com/ diff --git a/target/gemini/blog/2021-06-01_getting_started_with_ecs/index.txt b/target/gemini/blog/2021-06-01_getting_started_with_ecs/index.txt @@ -9,17 +9,17 @@ Creating our test project -------------------------------------------------------------------------------- Let's start a project so you can follow along. -``` +#``` cargo new --bin start_with_ecs cd start_with_ecs -``` +#``` Great! Let's add our dependency in Cargo.toml: -``` +#``` [dependencies] planck_ecs = "1.2.0" -``` +#``` Now, we are ready to start talking about the ECS! @@ -87,18 +87,18 @@ Open up src/main.rs We will first import Planck ECS' features into our game. Add the following line at the start of the file: -``` +#``` use planck_ecs::*; -``` +#``` Next, we will need something to contain the data we create. Let's create a new World inside of the main function: -``` +#``` fn main() { let mut world = World::default(); } -``` +#``` Our First Resource -------------------------------------------------------------------------------- @@ -106,12 +106,12 @@ Let's insert our first Resource into the world. We will use the Entities resource, which is a structure that holds a list of entities that exist in the World. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Entities>(); } -``` +#``` The initialize function will insert Entities inside of the World. @@ -125,13 +125,13 @@ Our First Entity -------------------------------------------------------------------------------- Let's now use our Entities Resource to create our first entity: -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Entities>(); let entity1 = world.get_mut::<Entities>().unwrap().create(); } -``` +#``` Here, get_mut gives us access to the Resource in World. Since World doesn't contain every single possible Resource, it returns an Option @@ -145,12 +145,12 @@ away, there is a convenient function that does both steps at the same time: world.get_mut_or_default. Let's rewrite our code using it: -``` +#``` fn main() { let mut world = World::default(); let entity1 = world.get_mut_or_default::<Entities>().create(); } -``` +#``` Our first Component -------------------------------------------------------------------------------- @@ -160,7 +160,7 @@ First, we will create the Color component. This can be anything: a struct, an enum, you choose. Here, let's use an enum. Add the following before or after the main function: -``` +#``` #[derive(Debug, PartialEq)] enum Color { Red, @@ -170,31 +170,31 @@ enum Color { fn main() { [...] -``` +#``` And now, we can tell the World that we want to use this enum as a Component by initializing a Components Resource and specifying that we want the Color enum. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); } -``` +#``` The Components<T> Resource is simply used to contain components of type T in a way that is easy to use and also is quick to access. Finally, let's attach our Component to the Entity! -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut::<Components<Color>>().unwrap().insert(entity1, Color::Red); } -``` +#``` Here we use the insert(entity, component) method available on the Components resource. @@ -202,14 +202,14 @@ resource. There are two simplifications that we can do to our code. First is to tell rust to guess that we want the Color component by using _. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut::<Components<_>>().unwrap().insert(entity1, Color::Red); } -``` +#``` Rust is smart enough to understand from our usage of Color::Red on the right side that what we want is Components<Color>. @@ -217,13 +217,13 @@ right side that what we want is Components<Color>. Now, this code looks strangely like the one we saw when creating the Entity previously. Let's apply the same trick: -``` +#``` fn main() { let mut world = World::default(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); } -``` +#``` Yep! We can use get_mut_or_default here too! @@ -240,14 +240,14 @@ As we saw previously, Systems are just functions that use data from the World. Let's write a System that changes the color of the apple to blue. -``` +#``` fn change_color_system(colors: &mut Components<Color>) -> SystemResult { Ok(()) } fn main() { [...] -``` +#``` We have some things to explain here. The parameters of Systems use references (that's the & and &mut symbols). @@ -255,14 +255,14 @@ A special rule for Systems is that all parameters using &mut must be placed after all parameters using &. For example, the following is invalid: -``` +#``` fn system(a: &mut A, b: &B, c: &mut C) -> SystemResult {...} -``` +#``` It should instead be written as: -``` +#``` fn system(b: &B, a: &mut A, c: &mut C) -> SystemResult {...} -``` +#``` Now, you might wonder what that SystemResult thing is. This is a feature that is unique to Planck ECS: Error handling. @@ -277,14 +277,14 @@ For this tutorial, we will always use Ok. Now, let's change the color of that apple! -``` +#``` fn change_color_system(colors: &mut Components<Color>) -> SystemResult { for color in join!(&mut colors) { *color = Color::Blue; } Ok(()) } -``` +#``` The join!() macro returns an iterator over the Components we reference. Here, we tell the join!() macro to give us all Color Components mutably, so @@ -296,12 +296,12 @@ Running the System -------------------------------------------------------------------------------- To run a System, we use: -``` +#``` fn main () { [...] change_color_system.system().run(&mut world).unwrap(); } -``` +#``` This will automatically get the required Components from World and call the function change_color_system. @@ -310,7 +310,7 @@ Now, what happens if the System uses a Resource which is not in World? It will crash! Let's fix this up: -``` +#``` fn main() { [...] let mut system = change_color_system.system(); @@ -318,7 +318,7 @@ fn main() { system.run(&mut world).unwrap(); } -``` +#``` When calling the initialize function on a System, it will use world.initialize() for every Resource that is in the System's parameters. @@ -334,7 +334,7 @@ They are also very convenient, because without them you would need three lines for each System! Here's how we use a Dispatcher: -``` +#``` fn main() { let mut world = World::default(); let mut dispatcher = DispatcherBuilder::default() @@ -346,7 +346,7 @@ fn main() { dispatcher.run_seq(&mut world).unwrap(); } -``` +#``` The Dispatcher creation is pretty much self explanatory. Simply chain .add(system).add(system2).add(system3) to add Systems, then complete the @@ -369,7 +369,7 @@ Now, let's come back to our game! Let's imagine that we had another Entity that also had a Color Component, but that we don't want to change its Color. -``` +#``` fn main() { let mut world = World::default(); let apple = world.get_mut_or_default::<Entities>().create(); @@ -377,7 +377,7 @@ fn main() { world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); } -``` +#``` With our current System, both entities will have their colors changed to blue. @@ -393,24 +393,24 @@ reusing Systems for more than one type of Entity. Let's create this Component: -``` +#``` struct ColorChanging; -``` +#``` and add it to our apple Entity: -``` +#``` let apple = world.get_mut_or_default::<Entities>().create(); let orange = world.get_mut_or_default::<Entities>().create(); world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); world.get_mut_or_default::<Components<_>>().insert(apple, ColorChanging); //here world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); -``` +#``` Finally, let's make it so our System only runs on Entities that have a ColorChanging Component: -``` +#``` fn change_color_system( changings: &Components<ColorChanging>, colors: &mut Components<Color>) -> SystemResult { @@ -421,7 +421,7 @@ fn change_color_system( Ok(()) } -``` +#``` First, we add the Components<ColorChanging> parameter. @@ -440,13 +440,13 @@ We need this when joining over multiple Components at the same time. This is because the join macro also supports || OR operations. For example, if we used: -``` +#``` join!(&mut colors || &changings) -``` +#``` we would have pairs of Components that look like this: -``` +#``` (&mut Option<Color>, &Option<ColorChanging>) -``` +#``` Because we use &&, we can assume that both Options have a value (Some). But if we use ||, then we cannot assume this, as one of the Entities has a @@ -456,7 +456,7 @@ Making sure everything works -------------------------------------------------------------------------------- It is always a good practice to verify that our Systems are working fine. Let's do this quickly in the main function: -``` +#``` [...] dispatcher.run_seq(&mut world).unwrap(); @@ -465,16 +465,16 @@ Let's do this quickly in the main function: assert_eq!(*world.get::<Components<Color>>().unwrap().get(orange).unwrap(), Color::Red); } -``` +#``` Removing a Component -------------------------------------------------------------------------------- Simple! Use: -``` +#``` world.get_mut::<Components<Color>>().unwrap().remove(apple); -``` +#``` Of course, this works inside of Systems too, since you have Components<Color> as a parameter! @@ -484,18 +484,18 @@ Removing an Entity Also simple! Use: -``` +#``` world.get_mut::<Entities>().unwrap().kill(apple); -``` +#``` However, it is important to know that removing an Entity using kill(entity) will NOT delete its Components that are in the Components<T> Resources. This can be done automatically by using the world.maintain() function: -``` +#``` world.maintain(); -``` +#``` It is recommended to run world.maintain() after using dispatcher.run_seq(&mut world), as this ensures everything is cleaned up! @@ -518,7 +518,7 @@ Full Code -------------------------------------------------------------------------------- Here is the full code created in this tutorial: -``` +#``` use planck_ecs::*; #[derive(Debug, PartialEq)] @@ -582,7 +582,7 @@ fn main() { world.maintain(); } -``` +#``` Patreon rocket goes brrrrrrrrrrrr diff --git a/target/html/blog/2020-03-31_extracting_data_from_websites/index.html b/target/html/blog/2020-03-31_extracting_data_from_websites/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -Extracting Data From A Website -================================================================================ + + +<pre style="color: lime;">Extracting Data From A Website</pre> Sometimes, you want to get the data or images that is visible on your screen while visiting a websites, and its not too clear how you can get it @@ -64,16 +67,14 @@ life example. If you know how websites work, you can skip the next section. -Web Pages Basics -=============================================================================== +<pre style="color: lime;">Web Pages Basics</pre> Let's first discuss how web pages get filled with data, being images or text/numbers. There is usually two locations where this happens: On the server or on the client. -Filled Server Side --------------------------------------------------------------------------------- +<pre style="color: magenta;">Filled Server Side</pre> This is what older website usually do. A webpage is usually composed of 4 things: @@ -88,8 +89,7 @@ html file. The server takes the local html file, inserts the data into it In this case, getting the data is easy. More on this a bit lower. -Filled Client Side --------------------------------------------------------------------------------- +<pre style="color: magenta;">Filled Client Side</pre> Now, new websites usually want to be more dynamic. That is, they want to load content into the page without completely requesting a new html file. @@ -107,8 +107,7 @@ The good news is that with a bit of intuition, we can pretty easily see what the javascript asks to the server. In this situation, we will pretend to be the javascript getting data. -Identifying What We Want -================================================================================ +<pre style="color: lime;">Identifying What We Want</pre> No matter the way the page was made, we first need to identify what we want. As an example to illustrate what we will be doing, I will be extracting weather @@ -156,8 +155,7 @@ What's happening here is that this website only loads the images once you start playing the animation. This is often what dynamic websites do; they will load data (like images) only when you need to see them. -Server-Filled --------------------------------------------------------------------------------- +<pre style="color: magenta;">Server-Filled</pre> In the case of a web paged filled on the server, you will already have the image in there as soon as the page is first loaded. If this is your case, you can simply use something like the following to extract the image source: @@ -168,8 +166,7 @@ $ wget "https://blablabla" | pup 'img#the-id-of-the-image attr{src}' If your web page is not like this, continue reading. -Understanding The Pattern -================================================================================ +<pre style="color: lime;">Understanding The Pattern</pre> Here, we see that the source (src) of each image is composed of a name, a date and a time. @@ -180,8 +177,7 @@ the javascript asked for them. We will assume that the javascript asked for those names, as it is more common. -Identifying Our Sources -================================================================================ +<pre style="color: lime;">Identifying Our Sources</pre> Let's switch to the "Network" tab of the developer tools and then refresh the page. @@ -224,8 +220,7 @@ got inside. Magically, all our data is now here! <img src='./curl.png'/> -Simplyfing Our Request -================================================================================ +<pre style="color: lime;">Simplyfing Our Request</pre> As you can see, your request is really long and complex. By trial and error, we can remove sections of it. @@ -236,8 +231,7 @@ $ curl "https://weather.gc.ca/radar/xhr.php?action=retrieve&amp;target=images"\ "&amp;region=WUJ&amp;product=PRECIP_SNOW&amp;lang=en-CA&amp;format=json" \ -H 'X-Requested-With:XMLHttpRequest' &gt; /tmp/data.txt -Getting Our Sources -================================================================================ +<pre style="color: lime;">Getting Our Sources</pre> Our query works, now it is time to get the sources for those images. Let's use `jq`, a tool that will make our life much easier to extract the image @@ -268,8 +262,7 @@ and finally, we can download our images! cd /tmp wget -i /tmp/urls.txt -Conclusion -================================================================================ +<pre style="color: lime;">Conclusion</pre> While I tried to make this as simple as possible, this is not a simple topic. It requires knowledge of the web and of the linux tools. diff --git a/target/html/blog/2020-04-09_simple_school_documents/index.html b/target/html/blog/2020-04-09_simple_school_documents/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -Writing Simple School Documents (Groff Articles) -================================================================================ + + +<pre style="color: lime;">Writing Simple School Documents (Groff Articles)</pre> Hello there! Today, I will be teaching you how to write documents really quickly using tools @@ -63,8 +66,7 @@ You are using linux... aren't you? Anyways, today I will be showing you the Groff tool using examples. Let's get started. -Headers --------------------------------------------------------------------------------- +<pre style="color: magenta;">Headers</pre> Every good document starts with a header! Let's do this now. Create a file called "article.ms". @@ -84,8 +86,7 @@ This is text in a paragraph. Pretty simple right? -Getting the result --------------------------------------------------------------------------------- +<pre style="color: magenta;">Getting the result</pre> To generate a pdf from this, use the following command: @@ -101,8 +102,7 @@ The -Tpdf option means that we want to output to be in pdf format. Last but not least, we specify our input file (article.ms). Finally, we redirect the output of groff into an actual file. -Creating Headers With Multiple Levels --------------------------------------------------------------------------------- +<pre style="color: magenta;">Creating Headers With Multiple Levels</pre> Try the following: @@ -118,8 +118,7 @@ Try the following: This should be self explanatory once you see the results. :) -Creating Blocks Of Code --------------------------------------------------------------------------------- +<pre style="color: magenta;">Creating Blocks Of Code</pre> Sometimes, you want to insert text using a monospace font (constant width) that doesn't get formatted. For this, you can use the following: @@ -138,8 +137,7 @@ Let's see what happens here: .ft Changes the font back to normal. .DE Ends the block, re-enabling automatic text formatting. -Writing Mathematical Equations --------------------------------------------------------------------------------- +<pre style="color: magenta;">Writing Mathematical Equations</pre> For this, we will be using a macro package called "eqn". @@ -155,8 +153,7 @@ $ groff -e -ms -Tpdf article.ms &gt; article.pdf Now, you should see a (kind of) pretty equation. :) -Inline Mathematical Equations --------------------------------------------------------------------------------- +<pre style="color: magenta;">Inline Mathematical Equations</pre> Now, let's say that you want your equation to show up in the middle of a sentence. What you can do is set a "delimiter" which you will insert in your @@ -174,8 +171,7 @@ blablabla...blabla $2*2=4$ blablabla... Voilà! -Tables? Tables!! --------------------------------------------------------------------------------- +<pre style="color: magenta;">Tables? Tables!!</pre> Tables: @@ -201,8 +197,7 @@ groff -t -e -ms -Tpdf article.ms &gt; article.pdf Boom! A pretty table. Flip all the tables! -Including Images --------------------------------------------------------------------------------- +<pre style="color: magenta;">Including Images</pre> Last but not least, we will learn to include (postscript) images! @@ -214,8 +209,7 @@ postscript file to a pdf. This is simpler than it sounds. groff -t -e -ms -Tps article.ms | ps2pdf - article.pdf -Using Special Characters (éèëïù...) --------------------------------------------------------------------------------- +<pre style="color: magenta;">Using Special Characters (éèëïù...)</pre> Special characters will have a hard time using the default settings. Using the a preprocessor called preconv, we can fix this easily. @@ -223,8 +217,7 @@ Simply add -k to our command and we are done! :) groff -t -e -k -ms -Tpdf article.ms &gt; article.pdf -Going Further --------------------------------------------------------------------------------- +<pre style="color: magenta;">Going Further</pre> Sadly, the documentation is very sparse and its not obvious to go through it all. diff --git a/target/html/blog/2020-05-04_why_i_moved_from_wordpress/index.html b/target/html/blog/2020-05-04_why_i_moved_from_wordpress/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,16 +53,16 @@ <br/> <article> <pre> -Why I Moved From Wordpress -================================================================================ + + +<pre style="color: lime;">Why I Moved From Wordpress</pre> As some of you might have noticed, I moved away from wordpress and instead created this plaintext website. It might seem like an odd choice (and it is), but I have some reasons for this. -No Maintenance --------------------------------------------------------------------------------- +<pre style="color: magenta;">No Maintenance</pre> Having a plaintext website requires no maintenance. I write something, upload the files and I'm done. @@ -73,25 +75,21 @@ What can break with wordpress? All of the above plus: - Random new vulnerabilities - Automatic updates can fail -Speed --------------------------------------------------------------------------------- +<pre style="color: magenta;">Speed</pre> Plaintext faster than php. -Writing --------------------------------------------------------------------------------- +<pre style="color: magenta;">Writing</pre> It is much easier to write in plaintext than to try to format things properly in a web editor. -Source Control --------------------------------------------------------------------------------- +<pre style="color: magenta;">Source Control</pre> - The code is small and easy to understand. No surprises here. - I can use git to make backups of the website. No need for databases! -Style --------------------------------------------------------------------------------- +<pre style="color: magenta;">Style</pre> I'm not very good at styling and I can't be bothered to spend hours making everything look good. Also, I just like the style of plaintext. &lt;3 diff --git a/target/html/blog/2020-07-07_minimalist_alternatives/index.html b/target/html/blog/2020-07-07_minimalist_alternatives/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -Minimalist Alternatives to Popular Applications -================================================================================ + + +<pre style="color: lime;">Minimalist Alternatives to Popular Applications</pre> This blog post will go through some minimalist alternatives to popular applications. Most or all application mentionned here as alternatives run on @@ -60,14 +63,12 @@ linux. Let's start! -Calendar Applications/Google Calendar/Outlook Calendar --------------------------------------------------------------------------------- +<pre style="color: magenta;">Calendar Applications/Google Calendar/Outlook Calendar</pre> Calcurse is a beatiful program to manage your appointments, note the birthdays of everyone you know and keep track of what you have to do. -Task Lists --------------------------------------------------------------------------------- +<pre style="color: magenta;">Task Lists</pre> I was using an application called TaskWarrior to keep track of my tasks. It is really good and I would recommend it to anyone that needs to track @@ -85,8 +86,7 @@ If you need to keep a specific order in the tasks you do and need priority management, I would suggest using TaskWarrior. Otherwise, a text file is just fine. -Drawing Graphs --------------------------------------------------------------------------------- +<pre style="color: magenta;">Drawing Graphs</pre> For graphs, I use graphviz, which is a collection of different programs that create slightly different graphs depending on your needs. @@ -115,8 +115,7 @@ Of course, it works just fine to create a .png: neato -Tpng entity_model.gv -o entity_model.png -Music Player/Spotify --------------------------------------------------------------------------------- +<pre style="color: magenta;">Music Player/Spotify</pre> I download all my music, usually from the artist's bandcamp if there's a download option. @@ -127,8 +126,7 @@ desktop and cmus on my laptop. I don't know which one I prefer, but they got the job done: They play music and I'm not dependant on an external service. -Video Player/Youtube --------------------------------------------------------------------------------- +<pre style="color: magenta;">Video Player/Youtube</pre> I use a combination of newsboat (a RSS/news reader), youtube-dl (downloads videos from youtube) and mpv OR mplayer (video players). @@ -144,8 +142,7 @@ Here's an example of the ~/.newsboat/urls file I use: http://feeds.nature.com/nature/rss/current?format=xml <a href='https://www.youtube.com/feeds/videos.xml?channel_id=&lt;channel_id&gt;'>https://www.youtube.com/feeds/videos.xml?channel_id=&lt;channel_id&gt;</a> -Emails/Gmail/Outlook --------------------------------------------------------------------------------- +<pre style="color: magenta;">Emails/Gmail/Outlook</pre> I use postfix + dovecot + spamassassin on a scaleway vps as a mailserver. I use neomutt as my mail client. (I intend to try aerc soon.) @@ -153,15 +150,13 @@ I use neomutt as my mail client. (I intend to try aerc soon.) I would suggest using Luke Smith's mutt wizard program to set things up for you more easily. -Powerpoints/Microsoft Office --------------------------------------------------------------------------------- +<pre style="color: magenta;">Powerpoints/Microsoft Office</pre> I don't actually have to create powerpoints, but if I did I would use suckless' sent program. You can find it on <a href='https://suckless.org'>https://suckless.org</a> -Spreadsheets/Excel --------------------------------------------------------------------------------- +<pre style="color: magenta;">Spreadsheets/Excel</pre> My needs for spreadsheets is pretty low now that I'm mostly using plain text files instead. @@ -171,25 +166,21 @@ libreoffice's calc for complex usages involving graphs. I should learn gnuplot soon *thinking*. -Image Viewer --------------------------------------------------------------------------------- +<pre style="color: magenta;">Image Viewer</pre> If using a windows manager: sxiv If not: fbv (a framebuffer image viewer) -Pdf Reader --------------------------------------------------------------------------------- +<pre style="color: magenta;">Pdf Reader</pre> If using a windows manager: zathura If not: fbpdf -Text Editor/Code Editor/VS Code/IntelliJ --------------------------------------------------------------------------------- +<pre style="color: magenta;">Text Editor/Code Editor/VS Code/IntelliJ</pre> neovim all the way. Sorry emacs users ;_; -Password Manager/KeepassX --------------------------------------------------------------------------------- +<pre style="color: magenta;">Password Manager/KeepassX</pre> For a password manager with a graphical interface, KeepassX does the job very well. @@ -197,20 +188,17 @@ very well. Recently I switched to an application called pash, written by Dylan Araps. It is very to use and runs in the terminal. -Terminal Emulator --------------------------------------------------------------------------------- +<pre style="color: magenta;">Terminal Emulator</pre> Suckless Terminal (st). Available on <a href='https://suckless.org'>https://suckless.org</a> I only use the alpha patch to see my desktop background. -Disk Space Explorer --------------------------------------------------------------------------------- +<pre style="color: magenta;">Disk Space Explorer</pre> ncdu exploring a lot of different folders, du -sh (ships with linux) to get the size of some specific folders. -Window Manager --------------------------------------------------------------------------------- +<pre style="color: magenta;">Window Manager</pre> dwm on my desktop. Also available on the suckless website. :&gt; @@ -219,8 +207,7 @@ for a framebuffer-based setup. I'm running tmux inside of a fbpad instance and use framebuffer applications (as listed earlier in this post) to view images/pdfs/videos. -Bookmark Manager --------------------------------------------------------------------------------- +<pre style="color: magenta;">Bookmark Manager</pre> A text file is really all you need, to be honest. I tried spreadsheets, buku, the firefox built-in bookmark manager, but in the end nothing was as @@ -237,13 +224,11 @@ wget -e robots=off -r --level=0 -nc -np &lt;website url&gt; For those website that it makes no sense to scrape (like news website), I will use newsboat to keep track of it. -Background Image Changer --------------------------------------------------------------------------------- +<pre style="color: magenta;">Background Image Changer</pre> feh --bg-scale &lt;image.png&gt; -Conclusion -================================================================================ +<pre style="color: lime;">Conclusion</pre> If you look hard enough, there's very often a simpler way of doing the things you do. diff --git a/target/html/blog/2020-08-20_event_chaining/index.html b/target/html/blog/2020-08-20_event_chaining/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,13 +53,13 @@ <br/> <article> <pre> + + Event Chaining as a Decoupling Method in Entity-Component-System Joël Lupien (Jojolepro, jojolepro@jojolepro.com) -<a href='https://patreon.com/jojolepro'>https://patreon.com/jojolepro</a> -================================================================================ +<pre style="color: lime;"><a href='https://patreon.com/jojolepro</pre>'>https://patreon.com/jojolepro</pre></a> -Context -================================================================================ +<pre style="color: lime;">Context</pre> In game engines, we often have a lot of dependencies between modules. For example, the user interface often depends on the renderer, window and @@ -75,39 +77,33 @@ think of logical dependencies. A depends on B, thus A refers to B.As we have just seen however, it can cause problems of maintainability, because we have strong coupling. -Reducing Coupling -================================================================================ +<pre style="color: lime;">Reducing Coupling</pre> There are multiple ways to reduce coupling, depending on which paradigm you use.Here, I will be specifically exposing a way to reduce coupling in the context of an Entity Component System (ECS). -Fundamentals -================================================================================ +<pre style="color: lime;">Fundamentals</pre> First of all, let's see the building pieces that we have in a ECS.I will be using terminology from the Amethyst Game Engine, since this is what I am most familiar with. -Entities --------------------------------------------------------------------------------- +<pre style="color: magenta;">Entities</pre> Entities are "units" of the game. Each player, item, user interface element, audio source, etc.. are entities.We will not discuss much about entities in this paper. -Components --------------------------------------------------------------------------------- +<pre style="color: magenta;">Components</pre> Components are properties of entities. Again, we will not be discussing those. -Resources --------------------------------------------------------------------------------- +<pre style="color: magenta;">Resources</pre> Resources are simple data that is stored inside of the ECS' context. -Systems --------------------------------------------------------------------------------- +<pre style="color: magenta;">Systems</pre> Systems are what drives the changes in the game. They are functions that take data from the ECS context (resources and entity/components), perform @@ -119,47 +115,40 @@ resources or components (whether they read or write to them).If a system writes to a resource or component, no other system can access this same resource or component at the same time (in parallel). -Event Channels --------------------------------------------------------------------------------- +<pre style="color: magenta;">Event Channels</pre> Single writer/Multiple readers FIFO queues. Most data types can be inserted through those (they only need to be thread safe).You need a registered instance of ReaderId to read from them. ReaderId instances can be created by getting a mutable reference to an Event Channel and calling register_reader(). -Additional Terminology -================================================================================ +<pre style="color: lime;">Additional Terminology</pre> Let's introduce distinct names depending on how those previous concepts are used to improve the clarity of this paper. -Driver --------------------------------------------------------------------------------- +<pre style="color: magenta;">Driver</pre> A System used to "drive" the execution of another system, often through the use of Signals. -Signal --------------------------------------------------------------------------------- +<pre style="color: magenta;">Signal</pre> A Signal is simply an event written in an Event Channel that has for only purpose to drive the execution of one or multiple Systems. If we look at it the opposite way, some System will look for Signals as a way to know if and what kind of work they need to do. -Event Chaining -================================================================================ +<pre style="color: lime;">Event Chaining</pre> -What Are They? --------------------------------------------------------------------------------- +<pre style="color: magenta;">What Are They?</pre> Very similar to the concept of message passing, event chains are a way to communicate between Systems.Simply put, you have one event that is created, which create a second event, which create a third event, which is then consumed (received) by a System. -Compared to Message Passing. --------------------------------------------------------------------------------- +<pre style="color: magenta;">Compared to Message Passing.</pre> In traditional message passing, you often have some System A that sends a message to System B.We say that System A is "aware" of System B's existence. @@ -173,8 +162,7 @@ to read from the Event Channel. The reason for this is so that we don't have a single place where all of the message buffers are and where every system tries to get a mutable reference (which would heavily reduce performance). -How They Solve The Coupling Problem -================================================================================ +<pre style="color: lime;">How They Solve The Coupling Problem</pre> If we continue with the example from the beginning, we can see a way to make the user interface unaware of the input system. @@ -192,8 +180,7 @@ UiEvent::Clicked(label1_entity). <img src='./graph1.png'/> -Drawbacks -================================================================================ +<pre style="color: lime;">Drawbacks</pre> Of course, we have to talk about drawbacks. Let me preface this with the following: There is a performance cost. It is small, but it is there. @@ -208,15 +195,13 @@ the last example, instead of the User Interface System looking each frame for the status of the input device(s), we now have this System running only when signals are present. -Other usages -================================================================================ +<pre style="color: lime;">Other usages</pre> We have seen how this concept of Event Chaining allows to decouple user interfaces from input handling.Now, what else can we apply it to?Well... a lot of things actually. Here are some examples: -Asset Hot Reloading --------------------------------------------------------------------------------- +<pre style="color: magenta;">Asset Hot Reloading</pre> - A FileWatcher System creates a signal when a file is updated on disk. Watched files are configured through a resource, which could itself @@ -236,8 +221,7 @@ the code of what depends on this data (the renderer's mesh to gpu loader). <img src='./graph2.png'/> -Audio Processing --------------------------------------------------------------------------------- +<pre style="color: magenta;">Audio Processing</pre> - As described in the previous point, we have a system that can load files (including audio) and @@ -249,8 +233,7 @@ to the audio data instead of copying the data into the Signal. - We have a AudioSink System that forwards the corresponding audio data into a raw audio sink. -Additional Benefit: Configurability -================================================================================ +<pre style="color: lime;">Additional Benefit: Configurability</pre> In this paper, I mentioned that Drivers can use configuration and context data from ECS Resources.This allows for a super easy way to configure complex @@ -280,16 +263,14 @@ their character, saving both performance and code complexity. <img src='./graph3.png'/> -Conclusion -================================================================================ +<pre style="color: lime;">Conclusion</pre> In conclusion, Event Chains are a powerful and versatile tool to decouple logical dependencies between Systems. They add a bit of complexity and performance overhead, but they are well worth their cost in the context of a general game engine. -General Recommendations -================================================================================ +<pre style="color: lime;">General Recommendations</pre> For those who already worked with something similar to EventChannel and ReaderId, you might have noticed that there are issues when a System that @@ -315,8 +296,7 @@ Here are some ideas of what to document: - The events that each System creates. - The signals that each System consumes. -Supporting Me -================================================================================ +<pre style="color: lime;">Supporting Me</pre> I released this for free/without limitations because I want to contribute to the greater good. diff --git a/target/html/blog/2020-09-17_the_lost_of_autonomy/index.html b/target/html/blog/2020-09-17_the_lost_of_autonomy/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -The Lost of Time -================================================================================ + + +<pre style="color: lime;">The Lost of Time</pre> An opiniated essay on work, desires and autonomy. @@ -67,8 +70,7 @@ grateful that someone else can. - if you can take care of others while still taking care of yourself first, do it. we are all human and in this together. we all want to be happy. -Work --------------------------------------------------------------------------------- +<pre style="color: magenta;">Work</pre> - we don't have time for ourselves in moderns society - specialization, or how to create time. - specialization as an economics basis. @@ -108,8 +110,7 @@ One final remark before we dive in: If specialization creates time, why do we still *feel* like we have less and less of it? -Desires --------------------------------------------------------------------------------- +<pre style="color: magenta;">Desires</pre> - the economy of desires - economics, satisfying infinite desires with limited resources - the creation of desires, too much desires to satisfy them all @@ -138,8 +139,7 @@ pursuing those that are not worth your time and attention. Not doing this has for consequence something affecting society at an unprecedented scale: a loss of autonomy. -Autonomy --------------------------------------------------------------------------------- +<pre style="color: magenta;">Autonomy</pre> In computer science, there are two very important and simple concepts that indicate very strongly the quality of a program. Cohesion and coupling. diff --git a/target/html/blog/2020-11-17_the_fall_of_the_giant/index.html b/target/html/blog/2020-11-17_the_fall_of_the_giant/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -The Fall of the Giant -================================================================================ + + +<pre style="color: lime;">The Fall of the Giant</pre> A short story about management, drama and failure. Amethyst, open source game engine. diff --git a/target/html/blog/2021-01-13_planck_ecs/index.html b/target/html/blog/2021-01-13_planck_ecs/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,16 +53,17 @@ <br/> <article> <pre> -Planck ECS: A Minimalistic Yet Performant Entity-Component-System Library -================================================================================ + + +<pre style="color: lime;">Planck ECS: A Minimalistic Yet Performant Entity-Component-System Library</pre> <a href='https://github.com/jojolepro/planck_ecs'>https://github.com/jojolepro/planck_ecs</a> <a href='https://docs.rs/planck_ecs'>https://docs.rs/planck_ecs</a> -+------------------------------------------------------------------------------+ +<pre style="color: magenta;">#```</pre> | "Perfection is achieved, not when there is nothing more to add, | | but when there is nothing left to take away." | -| - Antoine de Saint-Exupery, 1900 | -+------------------------------------------------------------------------------+ +<pre style="color: magenta;">| - Antoine de Saint-Exupery, 1900 |</pre> +<pre style="color: orange"> If you already know what an ECS is, jump to the "Comparison With Other ECS" section. @@ -82,8 +85,7 @@ Good! Now, let's cover the basics. -The Basics --------------------------------------------------------------------------------- +<pre style="color: magenta;">The Basics</pre> We have four main elements. - Entity: A "thing" that exists in the world. It may be a game character, @@ -100,8 +102,7 @@ thing, that is..) - System: An operation that transforms Entities, Components and Resources. -Making It All Come Together --------------------------------------------------------------------------------- +<pre style="color: magenta;">Making It All Come Together</pre> Here's a quick example of how it looks conceptually: Entity 1: @@ -127,8 +128,7 @@ System: We have a simple system that conditionally modifies the Position component of all entities having one. -Extra Elements --------------------------------------------------------------------------------- +<pre style="color: magenta;">Extra Elements</pre> To make this all work together, we need some more concepts. First, the World. @@ -164,8 +164,7 @@ accessing resources. To do this, Systems need to be built in a way that corresponds to what the Dispatcher can handle. -Constraints On Systems --------------------------------------------------------------------------------- +<pre style="color: magenta;">Constraints On Systems</pre> These are the constraints that specify how systems may be built: 1) Systems must take only references as arguments. @@ -185,8 +184,7 @@ This constraint exists so that resources may be automatically created for you, as well as enforcing that any resource that might not exist is actually handled by the system without any issue. -How It Actually Looks --------------------------------------------------------------------------------- +<pre style="color: magenta;">How It Actually Looks</pre> Importing the library: ``` use planck_ecs::*; @@ -237,8 +235,7 @@ dispatcher.run_par(&amp;mut world).expect("Error in a system!"); world.maintain(); ``` -Joining Components --------------------------------------------------------------------------------- +<pre style="color: magenta;">Joining Components</pre> The last part of the puzzle: How to write the example system from earlier that modifies the position using the time? @@ -280,8 +277,7 @@ fn position_update_if_time(time: &amp;Time, sizes: &amp;Components&lt;Size&gt;, } ``` -Comparison With Other ECS --------------------------------------------------------------------------------- +<pre style="color: magenta;">Comparison With Other ECS</pre> Let's have a quick and informal comparison with other Rust ECS libraries. First, performance: According to the last time we ran benchmarks, we were the @@ -319,13 +315,11 @@ popular ECS libraries: The numbers speak for themselves. -Licensing --------------------------------------------------------------------------------- +<pre style="color: magenta;">Licensing</pre> Published under CC0, Planck ECS is under the public domain and available for free! -Conclusion --------------------------------------------------------------------------------- +<pre style="color: magenta;">Conclusion</pre> In conclusion, Planck ECS is not an innovative piece of software. It does the same thing that the community has been doing for years. It just does it in a better and more safe way. diff --git a/target/html/blog/2021-01-13_removing_the_us/index.html b/target/html/blog/2021-01-13_removing_the_us/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -Getting Rid of The US -================================================================================ + + +<pre style="color: lime;">Getting Rid of The US</pre> Or that one time I tried living without access to servers in or owned by US-based companies. @@ -60,8 +63,7 @@ We all know that the US has a lot of influence over the world. The same is true on the internet. As an experiment, I decided to add block all ip addresses from the US, as well as all ip addresses from companies based in the US. -The Method --------------------------------------------------------------------------------- +<pre style="color: magenta;">The Method</pre> I downloaded the major IP blocks allocated to the US from here: <a href='https://www.nirsoft.net/countryip/us.csv'>https://www.nirsoft.net/countryip/us.csv</a> @@ -97,8 +99,7 @@ while IFS="" read -r range; do done &lt; ips ``` -First Observations --------------------------------------------------------------------------------- +<pre style="color: magenta;">First Observations</pre> The first thing I observed after applying these changes was that discord, steam, github and duckduckgo were still all working. @@ -116,8 +117,7 @@ Finally, I took the cloudflare ip addresses from here: <a href='https://www.cloudflare.com/ips/'>https://www.cloudflare.com/ips/</a> and removed them. This is the moment the internet broke. -What Works? --------------------------------------------------------------------------------- +<pre style="color: magenta;">What Works?</pre> My weather radar still works. This is expected as it fetches data from the Canadian government website, which host their things themselves. @@ -180,8 +180,7 @@ Also, my work requires me to use github, which is already in the exclusion list. Ouch! -Conclusion -================================================================================ +<pre style="color: lime;">Conclusion</pre> Fun experiment, but not very useful except or practical at all. </pre> </article> diff --git a/target/html/blog/2021-05-31_minigene_and_the_future/index.html b/target/html/blog/2021-05-31_minigene_and_the_future/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -Minigene, Shotcaller, WOTFG, Amethyst and the Future of Rust Gamedev -================================================================================ + + +<pre style="color: lime;">Minigene, Shotcaller, WOTFG, Amethyst and the Future of Rust Gamedev</pre> Hello and welcome to your ~~daily~~ ~~monthly~~ yearly status update! This post is rather long, so feel free to skip sections underlined with dashes, @@ -61,8 +64,7 @@ as they are more in-depth explanations of what was mentioned just before them. Let's task about what has been happening in the past few months and what is coming in the future! -Minigene -================================================================================ +<pre style="color: lime;">Minigene</pre> Minigene is a new game engine. It is meant to fill the empty space between indie game development and professional game development. @@ -80,8 +82,7 @@ For instance, here are the layers used for the game Shotcaller: - 2: Glue - 1: Features -Layers Explained --------------------------------------------------------------------------------- +<pre style="color: magenta;">Layers Explained</pre> Let's start at the bottom, with layer 1: the features. This layer is composed of many small and specialized crates. For instance, one crate for i18n, one crate @@ -121,10 +122,10 @@ minigene, we place the different features into groups and hide them behind feature gates. You can enable those features using: -``` +<pre style="color: orange"> minigene = { git = "https://github.com/jojolepro/minigene", features = ["feature1", "feature2", "..."] } -``` +</pre> Finally, layer 4 is the game you want to make. Usually, it will import minigene and use some of its features. @@ -156,8 +157,7 @@ Here, we started with a key press, inserted it in a more convenient structure, created an event specific to the game we are creating, pushed a network event to the server and finally notified all clients in the game that we jumped. -Features Of Minigene --------------------------------------------------------------------------------- +<pre style="color: magenta;">Features Of Minigene</pre> Currently, Minigene has quite a few features but is far from having all of the planned features. @@ -173,8 +173,7 @@ Here are some features that are currently implemented and working: - Bracket-lib integration - Runs on WASM (Web Assembly) -Planned Features and Changes --------------------------------------------------------------------------------- +<pre style="color: magenta;">Planned Features and Changes</pre> The engine has a lot of planned features, but for now we will mention only a few of them. @@ -190,8 +189,7 @@ a few of them. crates. - Mobile device support (high priority!) -Shotcaller -================================================================================ +<pre style="color: lime;">Shotcaller</pre> Shotcaller is a MOBA game inspired by DOTA 2 and League of Legends. <a href='https://github.com/amethyst/shotcaller'>https://github.com/amethyst/shotcaller</a> @@ -203,11 +201,11 @@ Bracket-lib drives the execution, either through its own event loop or through web assembly, and then calls Minigene's Engine::engine_frame function. The game can be tested using: -``` +<pre style="color: orange"> git clone <a href='https://github.com/amethyst/shotcaller'>https://github.com/amethyst/shotcaller</a> cd shotcaller git checkout ac07a7bfb4224e8af4bd43e6df3afcaa879d4a48 -``` +</pre> In the case this doesn't work, there is also an online version: <a href='https://shotcaller.jojolepro.com/'>https://shotcaller.jojolepro.com/</a> @@ -218,8 +216,7 @@ Desktop Version: Old WASM Build: <img src='./shotcaller2.png'/> -WOTFG -================================================================================ +<pre style="color: lime;">WOTFG</pre> World Of The Fox God is a tile-based MMORPG (multiplayer roguelike/roleplay). <a href='https://github.com/jojolepro/wotfg'>https://github.com/jojolepro/wotfg</a> @@ -325,8 +322,7 @@ Crafting: There are way more things I want to talk about, but I will keep those for future blog posts. -Amethyst -================================================================================ +<pre style="color: lime;">Amethyst</pre> <img src='./amethyst.png'/> I recently joined the Board of Director of the Amethyst Game Engine Foundation! @@ -381,8 +377,7 @@ to assume that Amethyst codebase will contain both layer 2 and layer 3. I'm still hoping we can get layer 1 out of amethyst and into reusable crates that will benefit the whole ecosystem! -Call For Help! -================================================================================ +<pre style="color: lime;">Call For Help!</pre> Wew! This was a long blog post! Still here? Good! We need your help! *I* need your help! @@ -390,8 +385,7 @@ We need your help! *I* need your help! There are a lot of ways you can help, even if you are not familiar with programming and even if you didn't read the blog post at all! -Donating on Patreon --------------------------------------------------------------------------------- +<pre style="color: magenta;">Donating on Patreon</pre> Donating on patreon helps me spend more time working on open source projects! Whether it is games or game engines, your monetary donation helps create that! @@ -402,8 +396,7 @@ ecosystem as a whole! <img src='./patreon.png'/> -Contributing Code/Unit Tests --------------------------------------------------------------------------------- +<pre style="color: magenta;">Contributing Code/Unit Tests</pre> There are multiple ways to contribute code. In the case of Amethyst and Minigene, it can be as simple as taking layer 1 code @@ -418,20 +411,17 @@ with the game concepts. Finally, for all of those, adding unit tests to ensure that everything is working like it should is extremely appreciated! -Contributing Documentation --------------------------------------------------------------------------------- +<pre style="color: magenta;">Contributing Documentation</pre> Documentation is also something that is very appreciated. Whether it is just fixing a typo or contributing pages of documentation, we are very grateful towards anyone writing documentation, blog posts, tutorials and books! -Talk about us! --------------------------------------------------------------------------------- +<pre style="color: magenta;">Talk about us!</pre> If none of the previous options sounds like a good fit for you, simply talk about us! Post the link to this blog post to places you know. Every bit helps! -Joining/Contacting us -================================================================================ +<pre style="color: lime;">Joining/Contacting us</pre> Here is how you can contact us for those different projects: Myself: diff --git a/target/html/blog/2021-05-31_minigene_and_the_future/index.txt b/target/html/blog/2021-05-31_minigene_and_the_future/index.txt @@ -68,10 +68,10 @@ minigene, we place the different features into groups and hide them behind feature gates. You can enable those features using: -``` +#``` minigene = { git = "https://github.com/jojolepro/minigene", features = ["feature1", "feature2", "..."] } -``` +#``` Finally, layer 4 is the game you want to make. Usually, it will import minigene and use some of its features. @@ -150,11 +150,11 @@ Bracket-lib drives the execution, either through its own event loop or through web assembly, and then calls Minigene's Engine::engine_frame function. The game can be tested using: -``` +#``` git clone https://github.com/amethyst/shotcaller cd shotcaller git checkout ac07a7bfb4224e8af4bd43e6df3afcaa879d4a48 -``` +#``` In the case this doesn't work, there is also an online version: https://shotcaller.jojolepro.com/ diff --git a/target/html/blog/2021-06-01_getting_started_with_ecs/index.html b/target/html/blog/2021-06-01_getting_started_with_ecs/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,33 +53,32 @@ <br/> <article> <pre> -Getting Started with ECS using Planck ECS -================================================================================ + + +<pre style="color: lime;">Getting Started with ECS using Planck ECS</pre> Hello hello! This tutorial is aiming people having between zero and moderate experience with an ECS. Let's jump right in! -Creating our test project --------------------------------------------------------------------------------- +<pre style="color: magenta;">Creating our test project</pre> Let's start a project so you can follow along. -``` +<pre style="color: orange"> cargo new --bin start_with_ecs cd start_with_ecs -``` +</pre> Great! Let's add our dependency in Cargo.toml: -``` +<pre style="color: orange"> [dependencies] planck_ecs = "1.2.0" -``` +</pre> Now, we are ready to start talking about the ECS! -ECS? --------------------------------------------------------------------------------- +<pre style="color: magenta;">ECS?</pre> Entity-Component-System. It is a way of organising data that is well suited for game projects and @@ -86,16 +87,14 @@ simulations. In this tutorial, we are not going to make parallels with how Object Oriented programming works. We will just talk about the concepts one by one. -Entity --------------------------------------------------------------------------------- +<pre style="color: magenta;">Entity</pre> An entity is a "thing" on which you can attach "properties". For example, what we call an "Apple" is an entity. You are an entity. Every object is an entity. Simply put: Entity is the fancy name we give to "something". -Component --------------------------------------------------------------------------------- +<pre style="color: magenta;">Component</pre> Components are the properties that make entities what they are. For example, an "Apple" have these properties: - Red Color @@ -105,34 +104,28 @@ For example, an "Apple" have these properties: Components are used to describe what the entity is by attaching data (meaning) to it. -System --------------------------------------------------------------------------------- +<pre style="color: magenta;">System</pre> Systems are simply functions. They run code. That's it! -Is this all there is? --------------------------------------------------------------------------------- +<pre style="color: magenta;">Is this all there is?</pre> No, there are a couple extra concepts introduced by Planck ECS. Don't be afraid, they are simple! -World --------------------------------------------------------------------------------- +<pre style="color: magenta;">World</pre> World is a structure which contains everything that the Systems should have access to. -Resource --------------------------------------------------------------------------------- +<pre style="color: magenta;">Resource</pre> Anything contained inside of the World is called a "Resource". It is just a convenient name. -Dispatcher --------------------------------------------------------------------------------- +<pre style="color: magenta;">Dispatcher</pre> Dispatchers are used to execute systems (which are just functions!) using data that is inside of the World. They are optional, but very convenient! -Ready? Set? --------------------------------------------------------------------------------- +<pre style="color: magenta;">Ready? Set?</pre> Go! Open up src/main.rs @@ -140,31 +133,30 @@ Open up src/main.rs We will first import Planck ECS' features into our game. Add the following line at the start of the file: -``` +<pre style="color: orange"> use planck_ecs::*; -``` +</pre> Next, we will need something to contain the data we create. Let's create a new World inside of the main function: -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); } -``` +</pre> -Our First Resource --------------------------------------------------------------------------------- +<pre style="color: magenta;">Our First Resource</pre> Let's insert our first Resource into the world. We will use the Entities resource, which is a structure that holds a list of entities that exist in the World. -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); world.initialize::&lt;Entities&gt;(); } -``` +</pre> The initialize function will insert Entities inside of the World. @@ -174,17 +166,16 @@ In case you ever need to insert a structure which does not implement Default, you can always use Option&lt;YourResource&gt;, since Option's default is always Option::None. -Our First Entity --------------------------------------------------------------------------------- +<pre style="color: magenta;">Our First Entity</pre> Let's now use our Entities Resource to create our first entity: -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); world.initialize::&lt;Entities&gt;(); let entity1 = world.get_mut::&lt;Entities&gt;().unwrap().create(); } -``` +</pre> Here, get_mut gives us access to the Resource in World. Since World doesn't contain every single possible Resource, it returns an Option @@ -198,22 +189,21 @@ away, there is a convenient function that does both steps at the same time: world.get_mut_or_default. Let's rewrite our code using it: -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); } -``` +</pre> -Our first Component --------------------------------------------------------------------------------- +<pre style="color: magenta;">Our first Component</pre> Now, let's add some meaning to this Entity. We will attach a color to it. First, we will create the Color component. This can be anything: a struct, an enum, you choose. Here, let's use an enum. Add the following before or after the main function: -``` +<pre style="color: orange"> #[derive(Debug, PartialEq)] enum Color { Red, @@ -223,31 +213,31 @@ enum Color { fn main() { [...] -``` +</pre> And now, we can tell the World that we want to use this enum as a Component by initializing a Components Resource and specifying that we want the Color enum. -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); world.initialize::&lt;Components&lt;Color&gt;&gt;(); let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); } -``` +</pre> The Components&lt;T&gt; Resource is simply used to contain components of type T in a way that is easy to use and also is quick to access. Finally, let's attach our Component to the Entity! -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); world.initialize::&lt;Components&lt;Color&gt;&gt;(); let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); world.get_mut::&lt;Components&lt;Color&gt;&gt;().unwrap().insert(entity1, Color::Red); } -``` +</pre> Here we use the insert(entity, component) method available on the Components resource. @@ -255,14 +245,14 @@ resource. There are two simplifications that we can do to our code. First is to tell rust to guess that we want the Color component by using _. -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); world.initialize::&lt;Components&lt;Color&gt;&gt;(); let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); world.get_mut::&lt;Components&lt;_&gt;&gt;().unwrap().insert(entity1, Color::Red); } -``` +</pre> Rust is smart enough to understand from our usage of Color::Red on the right side that what we want is Components&lt;Color&gt;. @@ -270,13 +260,13 @@ right side that what we want is Components&lt;Color&gt;. Now, this code looks strangely like the one we saw when creating the Entity previously. Let's apply the same trick: -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); let entity1 = world.get_mut_or_default::&lt;Entities&gt;().create(); world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(entity1, Color::Red); } -``` +</pre> Yep! We can use get_mut_or_default here too! @@ -284,8 +274,7 @@ Let's summarize. Now, we have an Entity stored in the Entities resource. We also have a Color::Red component that is stored in the Components&lt;Color&gt; resource and that is "assigned" to our Entity. -Using Systems --------------------------------------------------------------------------------- +<pre style="color: magenta;">Using Systems</pre> Remember the core concepts? Entity, Component and System. Well, now we are at the System part. @@ -293,14 +282,14 @@ As we saw previously, Systems are just functions that use data from the World. Let's write a System that changes the color of the apple to blue. -``` +<pre style="color: orange"> fn change_color_system(colors: &amp;mut Components&lt;Color&gt;) -&gt; SystemResult { Ok(()) } fn main() { [...] -``` +</pre> We have some things to explain here. The parameters of Systems use references (that's the &amp; and &amp;mut symbols). @@ -308,14 +297,14 @@ A special rule for Systems is that all parameters using &amp;mut must be placed after all parameters using &amp;. For example, the following is invalid: -``` +<pre style="color: orange"> fn system(a: &amp;mut A, b: &amp;B, c: &amp;mut C) -&gt; SystemResult {...} -``` +</pre> It should instead be written as: -``` +<pre style="color: orange"> fn system(b: &amp;B, a: &amp;mut A, c: &amp;mut C) -&gt; SystemResult {...} -``` +</pre> Now, you might wonder what that SystemResult thing is. This is a feature that is unique to Planck ECS: Error handling. @@ -330,14 +319,14 @@ For this tutorial, we will always use Ok. Now, let's change the color of that apple! -``` +<pre style="color: orange"> fn change_color_system(colors: &amp;mut Components&lt;Color&gt;) -&gt; SystemResult { for color in join!(&amp;mut colors) { *color = Color::Blue; } Ok(()) } -``` +</pre> The join!() macro returns an iterator over the Components we reference. Here, we tell the join!() macro to give us all Color Components mutably, so @@ -345,16 +334,15 @@ that we can modify them. Then, we change the color. -Running the System --------------------------------------------------------------------------------- +<pre style="color: magenta;">Running the System</pre> To run a System, we use: -``` +<pre style="color: orange"> fn main () { [...] change_color_system.system().run(&amp;mut world).unwrap(); } -``` +</pre> This will automatically get the required Components from World and call the function change_color_system. @@ -363,7 +351,7 @@ Now, what happens if the System uses a Resource which is not in World? It will crash! Let's fix this up: -``` +<pre style="color: orange"> fn main() { [...] let mut system = change_color_system.system(); @@ -371,15 +359,14 @@ fn main() { system.run(&amp;mut world).unwrap(); } -``` +</pre> When calling the initialize function on a System, it will use world.initialize() for every Resource that is in the System's parameters. Convenient, right? -Dispatcher --------------------------------------------------------------------------------- +<pre style="color: magenta;">Dispatcher</pre> Remember Dispatchers? We saw that they are used to execute multiple Systems. Well they do more than that, they also call system.initialize for you! @@ -387,7 +374,7 @@ They are also very convenient, because without them you would need three lines for each System! Here's how we use a Dispatcher: -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); let mut dispatcher = DispatcherBuilder::default() @@ -399,7 +386,7 @@ fn main() { dispatcher.run_seq(&amp;mut world).unwrap(); } -``` +</pre> The Dispatcher creation is pretty much self explanatory. Simply chain .add(system).add(system2).add(system3) to add Systems, then complete the @@ -416,13 +403,12 @@ other. Planck ECS also has a feature where you can execute multiple Systems at the same time. We will not use it in this tutorial. -Multiple Components --------------------------------------------------------------------------------- +<pre style="color: magenta;">Multiple Components</pre> Now, let's come back to our game! Let's imagine that we had another Entity that also had a Color Component, but that we don't want to change its Color. -``` +<pre style="color: orange"> fn main() { let mut world = World::default(); let apple = world.get_mut_or_default::&lt;Entities&gt;().create(); @@ -430,7 +416,7 @@ fn main() { world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(apple, Color::Red); world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(orange, Color::Red); } -``` +</pre> With our current System, both entities will have their colors changed to blue. @@ -446,24 +432,24 @@ reusing Systems for more than one type of Entity. Let's create this Component: -``` +<pre style="color: orange"> struct ColorChanging; -``` +</pre> and add it to our apple Entity: -``` +<pre style="color: orange"> let apple = world.get_mut_or_default::&lt;Entities&gt;().create(); let orange = world.get_mut_or_default::&lt;Entities&gt;().create(); world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(apple, Color::Red); world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(apple, ColorChanging); //here world.get_mut_or_default::&lt;Components&lt;_&gt;&gt;().insert(orange, Color::Red); -``` +</pre> Finally, let's make it so our System only runs on Entities that have a ColorChanging Component: -``` +<pre style="color: orange"> fn change_color_system( changings: &amp;Components&lt;ColorChanging&gt;, colors: &amp;mut Components&lt;Color&gt;) -&gt; SystemResult { @@ -474,7 +460,7 @@ fn change_color_system( Ok(()) } -``` +</pre> First, we add the Components&lt;ColorChanging&gt; parameter. @@ -493,23 +479,22 @@ We need this when joining over multiple Components at the same time. This is because the join macro also supports || OR operations. For example, if we used: -``` +<pre style="color: orange"> join!(&amp;mut colors || &amp;changings) -``` +</pre> we would have pairs of Components that look like this: -``` +<pre style="color: orange"> (&amp;mut Option&lt;Color&gt;, &amp;Option&lt;ColorChanging&gt;) -``` +</pre> Because we use &amp;&amp;, we can assume that both Options have a value (Some). But if we use ||, then we cannot assume this, as one of the Entities has a Color but no ColorChanging Component. -Making sure everything works --------------------------------------------------------------------------------- +<pre style="color: magenta;">Making sure everything works</pre> It is always a good practice to verify that our Systems are working fine. Let's do this quickly in the main function: -``` +<pre style="color: orange"> [...] dispatcher.run_seq(&amp;mut world).unwrap(); @@ -518,43 +503,40 @@ Let's do this quickly in the main function: assert_eq!(*world.get::&lt;Components&lt;Color&gt;&gt;().unwrap().get(orange).unwrap(), Color::Red); } -``` +</pre> -Removing a Component --------------------------------------------------------------------------------- +<pre style="color: magenta;">Removing a Component</pre> Simple! Use: -``` +<pre style="color: orange"> world.get_mut::&lt;Components&lt;Color&gt;&gt;().unwrap().remove(apple); -``` +</pre> Of course, this works inside of Systems too, since you have Components&lt;Color&gt; as a parameter! -Removing an Entity --------------------------------------------------------------------------------- +<pre style="color: magenta;">Removing an Entity</pre> Also simple! Use: -``` +<pre style="color: orange"> world.get_mut::&lt;Entities&gt;().unwrap().kill(apple); -``` +</pre> However, it is important to know that removing an Entity using kill(entity) will NOT delete its Components that are in the Components&lt;T&gt; Resources. This can be done automatically by using the world.maintain() function: -``` +<pre style="color: orange"> world.maintain(); -``` +</pre> It is recommended to run world.maintain() after using dispatcher.run_seq(&amp;mut world), as this ensures everything is cleaned up! -Conclusion --------------------------------------------------------------------------------- +<pre style="color: magenta;">Conclusion</pre> I hope this was instructive! It might seem like there are lots of things to learn, but also consider that you @@ -567,11 +549,10 @@ consider donating on Patreon. It is your donations that enable me to continue creating Rust libraries and learning material like this one! -Full Code --------------------------------------------------------------------------------- +<pre style="color: magenta;">Full Code</pre> Here is the full code created in this tutorial: -``` +<pre style="color: orange"> use planck_ecs::*; #[derive(Debug, PartialEq)] @@ -635,7 +616,7 @@ fn main() { world.maintain(); } -``` +</pre> Patreon rocket goes brrrrrrrrrrrr diff --git a/target/html/blog/2021-06-01_getting_started_with_ecs/index.txt b/target/html/blog/2021-06-01_getting_started_with_ecs/index.txt @@ -9,17 +9,17 @@ Creating our test project -------------------------------------------------------------------------------- Let's start a project so you can follow along. -``` +#``` cargo new --bin start_with_ecs cd start_with_ecs -``` +#``` Great! Let's add our dependency in Cargo.toml: -``` +#``` [dependencies] planck_ecs = "1.2.0" -``` +#``` Now, we are ready to start talking about the ECS! @@ -87,18 +87,18 @@ Open up src/main.rs We will first import Planck ECS' features into our game. Add the following line at the start of the file: -``` +#``` use planck_ecs::*; -``` +#``` Next, we will need something to contain the data we create. Let's create a new World inside of the main function: -``` +#``` fn main() { let mut world = World::default(); } -``` +#``` Our First Resource -------------------------------------------------------------------------------- @@ -106,12 +106,12 @@ Let's insert our first Resource into the world. We will use the Entities resource, which is a structure that holds a list of entities that exist in the World. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Entities>(); } -``` +#``` The initialize function will insert Entities inside of the World. @@ -125,13 +125,13 @@ Our First Entity -------------------------------------------------------------------------------- Let's now use our Entities Resource to create our first entity: -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Entities>(); let entity1 = world.get_mut::<Entities>().unwrap().create(); } -``` +#``` Here, get_mut gives us access to the Resource in World. Since World doesn't contain every single possible Resource, it returns an Option @@ -145,12 +145,12 @@ away, there is a convenient function that does both steps at the same time: world.get_mut_or_default. Let's rewrite our code using it: -``` +#``` fn main() { let mut world = World::default(); let entity1 = world.get_mut_or_default::<Entities>().create(); } -``` +#``` Our first Component -------------------------------------------------------------------------------- @@ -160,7 +160,7 @@ First, we will create the Color component. This can be anything: a struct, an enum, you choose. Here, let's use an enum. Add the following before or after the main function: -``` +#``` #[derive(Debug, PartialEq)] enum Color { Red, @@ -170,31 +170,31 @@ enum Color { fn main() { [...] -``` +#``` And now, we can tell the World that we want to use this enum as a Component by initializing a Components Resource and specifying that we want the Color enum. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); } -``` +#``` The Components<T> Resource is simply used to contain components of type T in a way that is easy to use and also is quick to access. Finally, let's attach our Component to the Entity! -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut::<Components<Color>>().unwrap().insert(entity1, Color::Red); } -``` +#``` Here we use the insert(entity, component) method available on the Components resource. @@ -202,14 +202,14 @@ resource. There are two simplifications that we can do to our code. First is to tell rust to guess that we want the Color component by using _. -``` +#``` fn main() { let mut world = World::default(); world.initialize::<Components<Color>>(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut::<Components<_>>().unwrap().insert(entity1, Color::Red); } -``` +#``` Rust is smart enough to understand from our usage of Color::Red on the right side that what we want is Components<Color>. @@ -217,13 +217,13 @@ right side that what we want is Components<Color>. Now, this code looks strangely like the one we saw when creating the Entity previously. Let's apply the same trick: -``` +#``` fn main() { let mut world = World::default(); let entity1 = world.get_mut_or_default::<Entities>().create(); world.get_mut_or_default::<Components<_>>().insert(entity1, Color::Red); } -``` +#``` Yep! We can use get_mut_or_default here too! @@ -240,14 +240,14 @@ As we saw previously, Systems are just functions that use data from the World. Let's write a System that changes the color of the apple to blue. -``` +#``` fn change_color_system(colors: &mut Components<Color>) -> SystemResult { Ok(()) } fn main() { [...] -``` +#``` We have some things to explain here. The parameters of Systems use references (that's the & and &mut symbols). @@ -255,14 +255,14 @@ A special rule for Systems is that all parameters using &mut must be placed after all parameters using &. For example, the following is invalid: -``` +#``` fn system(a: &mut A, b: &B, c: &mut C) -> SystemResult {...} -``` +#``` It should instead be written as: -``` +#``` fn system(b: &B, a: &mut A, c: &mut C) -> SystemResult {...} -``` +#``` Now, you might wonder what that SystemResult thing is. This is a feature that is unique to Planck ECS: Error handling. @@ -277,14 +277,14 @@ For this tutorial, we will always use Ok. Now, let's change the color of that apple! -``` +#``` fn change_color_system(colors: &mut Components<Color>) -> SystemResult { for color in join!(&mut colors) { *color = Color::Blue; } Ok(()) } -``` +#``` The join!() macro returns an iterator over the Components we reference. Here, we tell the join!() macro to give us all Color Components mutably, so @@ -296,12 +296,12 @@ Running the System -------------------------------------------------------------------------------- To run a System, we use: -``` +#``` fn main () { [...] change_color_system.system().run(&mut world).unwrap(); } -``` +#``` This will automatically get the required Components from World and call the function change_color_system. @@ -310,7 +310,7 @@ Now, what happens if the System uses a Resource which is not in World? It will crash! Let's fix this up: -``` +#``` fn main() { [...] let mut system = change_color_system.system(); @@ -318,7 +318,7 @@ fn main() { system.run(&mut world).unwrap(); } -``` +#``` When calling the initialize function on a System, it will use world.initialize() for every Resource that is in the System's parameters. @@ -334,7 +334,7 @@ They are also very convenient, because without them you would need three lines for each System! Here's how we use a Dispatcher: -``` +#``` fn main() { let mut world = World::default(); let mut dispatcher = DispatcherBuilder::default() @@ -346,7 +346,7 @@ fn main() { dispatcher.run_seq(&mut world).unwrap(); } -``` +#``` The Dispatcher creation is pretty much self explanatory. Simply chain .add(system).add(system2).add(system3) to add Systems, then complete the @@ -369,7 +369,7 @@ Now, let's come back to our game! Let's imagine that we had another Entity that also had a Color Component, but that we don't want to change its Color. -``` +#``` fn main() { let mut world = World::default(); let apple = world.get_mut_or_default::<Entities>().create(); @@ -377,7 +377,7 @@ fn main() { world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); } -``` +#``` With our current System, both entities will have their colors changed to blue. @@ -393,24 +393,24 @@ reusing Systems for more than one type of Entity. Let's create this Component: -``` +#``` struct ColorChanging; -``` +#``` and add it to our apple Entity: -``` +#``` let apple = world.get_mut_or_default::<Entities>().create(); let orange = world.get_mut_or_default::<Entities>().create(); world.get_mut_or_default::<Components<_>>().insert(apple, Color::Red); world.get_mut_or_default::<Components<_>>().insert(apple, ColorChanging); //here world.get_mut_or_default::<Components<_>>().insert(orange, Color::Red); -``` +#``` Finally, let's make it so our System only runs on Entities that have a ColorChanging Component: -``` +#``` fn change_color_system( changings: &Components<ColorChanging>, colors: &mut Components<Color>) -> SystemResult { @@ -421,7 +421,7 @@ fn change_color_system( Ok(()) } -``` +#``` First, we add the Components<ColorChanging> parameter. @@ -440,13 +440,13 @@ We need this when joining over multiple Components at the same time. This is because the join macro also supports || OR operations. For example, if we used: -``` +#``` join!(&mut colors || &changings) -``` +#``` we would have pairs of Components that look like this: -``` +#``` (&mut Option<Color>, &Option<ColorChanging>) -``` +#``` Because we use &&, we can assume that both Options have a value (Some). But if we use ||, then we cannot assume this, as one of the Entities has a @@ -456,7 +456,7 @@ Making sure everything works -------------------------------------------------------------------------------- It is always a good practice to verify that our Systems are working fine. Let's do this quickly in the main function: -``` +#``` [...] dispatcher.run_seq(&mut world).unwrap(); @@ -465,16 +465,16 @@ Let's do this quickly in the main function: assert_eq!(*world.get::<Components<Color>>().unwrap().get(orange).unwrap(), Color::Red); } -``` +#``` Removing a Component -------------------------------------------------------------------------------- Simple! Use: -``` +#``` world.get_mut::<Components<Color>>().unwrap().remove(apple); -``` +#``` Of course, this works inside of Systems too, since you have Components<Color> as a parameter! @@ -484,18 +484,18 @@ Removing an Entity Also simple! Use: -``` +#``` world.get_mut::<Entities>().unwrap().kill(apple); -``` +#``` However, it is important to know that removing an Entity using kill(entity) will NOT delete its Components that are in the Components<T> Resources. This can be done automatically by using the world.maintain() function: -``` +#``` world.maintain(); -``` +#``` It is recommended to run world.maintain() after using dispatcher.run_seq(&mut world), as this ensures everything is cleaned up! @@ -518,7 +518,7 @@ Full Code -------------------------------------------------------------------------------- Here is the full code created in this tutorial: -``` +#``` use planck_ecs::*; #[derive(Debug, PartialEq)] @@ -582,7 +582,7 @@ fn main() { world.maintain(); } -``` +#``` Patreon rocket goes brrrrrrrrrrrr diff --git a/target/html/blog/index.html b/target/html/blog/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; diff --git a/target/html/focks/index.html b/target/html/focks/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,23 +53,22 @@ <br/> <article> <pre> -Focks Development Team -================================================================================ + + +<pre style="color: lime;">Focks Development Team</pre> Welcome! This is the home page of the Focks Development Team! We are programmers dedicated to high quality, minimalist software and libraries. -Values -================================================================================ +<pre style="color: lime;">Values</pre> - Minimalism: Reduce the number of user-facing api to a maximum. - Testing: Every public function is tested. - Safety: Usage of unsafe is reserved as a last-resort option only. - Learnability: Everything is documented. Examples are available. - Compatibility: Compatible and tested with a maximum number of platforms. -Licensing Choice -================================================================================ +<pre style="color: lime;">Licensing Choice</pre> Since making very high quality software takes time, we think it is fair that people should give something back. @@ -90,8 +91,7 @@ decide on the pricing when the project joins the group. By default, only the AGPL-3.0 license is used. The maintainer of each library is free to authorise any users without payment even under the hybrid model. -Pricing -================================================================================ +<pre style="color: lime;">Pricing</pre> Here is the pricing of commercial licenses for each library that has them. All prices are USD and can change over time. Licenses are permanent so the price you buy them at is the final price. @@ -102,8 +102,7 @@ Game Clock: 5$ Plank ECS: 10$ <a href='https://git.jojolepro.com/plank_ecs/'>https://git.jojolepro.com/plank_ecs/</a> -Payment -================================================================================ +<pre style="color: lime;">Payment</pre> 1) Send an email with the list of software/libraries you are buying licenses for and the name under which the license will be used @@ -118,8 +117,7 @@ the authorised users list of each project. If you want to use some other payment processor, contact us! -FAQ -================================================================================ +<pre style="color: lime;">FAQ</pre> Q: I am doing a project under the Apache 2.0/MIT license. Can I use the projects without a commercial license? A: No, you need a license. diff --git a/target/html/index.html b/target/html/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -I Press Keys on my Keyboard... A Lot -================================================================================ + + +<pre style="color: lime;">I Press Keys on my Keyboard... A Lot</pre> About Me: - Board Director and Tech Lead at Amethyst - Double Diploma in Computer Science @@ -88,8 +91,7 @@ Game Engine Experience: - JMonkeyEngine - LWJGL -Work -================================================================================ +<pre style="color: lime;">Work</pre> English CV: <a href='https://jojolepro.com/CVENJoelLupien.pdf'>https://jojolepro.com/CVENJoelLupien.pdf</a> French CV: @@ -97,15 +99,13 @@ French CV: Portfolio : <a href='https://jojolepro.com/PortfolioJoelLupien.pdf'>https://jojolepro.com/PortfolioJoelLupien.pdf</a> -Donate -================================================================================ +<pre style="color: lime;">Donate</pre> If you like the work I do, please consider donating on Patreon: <a href='https://patreon.com/jojolepro'>https://patreon.com/jojolepro</a> Or by Bitcoin: 15NDruDUDr3KaMjt87BvUJaayEzy5c765Z -Contact Me -================================================================================ +<pre style="color: lime;">Contact Me</pre> In English or French mailto: jojolepro [at] jojolepro [dot] com </pre> diff --git a/target/html/projects/index.html b/target/html/projects/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -Most Notable Projects -================================================================================ + + +<pre style="color: lime;">Most Notable Projects</pre> For longer descriptions of those projects, scroll down to the description section. @@ -67,8 +70,7 @@ section. - ScalEngine - Shotcaller -List Of All Active or Completed Projects -================================================================================ +<pre style="color: lime;">List Of All Active or Completed Projects</pre> These projects, if not included in the Most Notable Projects section, *may* be found in my portfolio with a description and pictures. @@ -109,8 +111,7 @@ Table Of Content: - World of the Focks God -List Of Prototypes -================================================================================ +<pre style="color: lime;">List Of Prototypes</pre> The description of those projects can be found in the respective git repositories at <a href='https://git.jojolepro.com'>https://git.jojolepro.com</a> @@ -152,19 +153,16 @@ Table Of Content: - Arduino Projects - Dozen of others :) -Short Note -================================================================================ +<pre style="color: lime;">Short Note</pre> As you can easily notice, I made a *lot* of projects. Most of them were never completed and probably never will be. In the past, I used to dream big but change projects a lot, while nowadays I aim at doing smaller projects with a very high focus on quality and completion. -Descriptions of Notable Projects -================================================================================ +<pre style="color: lime;">Descriptions of Notable Projects</pre> -AltCom.io --------------------------------------------------------------------------------- +<pre style="color: magenta;">AltCom.io</pre> Description: A website collecting statistics about people playing on dedicated game servers. It provides statistics, achievements and a shop where you can buy items from points bought or collected from unlocking achievements. @@ -188,8 +186,7 @@ Images: Videos: <a href='https://youtu.be/oELsg7HBCCo'>https://youtu.be/oELsg7HBCCo</a> -Amethyst Engine --------------------------------------------------------------------------------- +<pre style="color: magenta;">Amethyst Engine</pre> Description: A 3D ECS based open source game engine. I have been a lead developer for multiple years. Technologies: @@ -205,8 +202,7 @@ Images: Videos: <a href='https://raw.githubusercontent.com/amethyst/space-menace/master/demo.gif'>https://raw.githubusercontent.com/amethyst/space-menace/master/demo.gif</a> -Back2Life --------------------------------------------------------------------------------- +<pre style="color: magenta;">Back2Life</pre> Description: 3D Multiplayer First Person Shooter game experiment. Has most core features you expect from a modern FPS: Weapon spray, recoil, fire modes, fire rate, ammo count, dynamic movement, animations, etc... @@ -222,8 +218,7 @@ Images: Videos: <a href='https://youtu.be/Z8DZrxgHi84'>https://youtu.be/Z8DZrxgHi84</a> -Game Features --------------------------------------------------------------------------------- +<pre style="color: magenta;">Game Features</pre> Description: A collection of generic and *extremely* reusable features for games. Contains the core logic that will fit with most games, starting from extremely simple platformers all the way to complex MMORPG. Contains an @@ -238,8 +233,7 @@ Images: <img src='./gamefeatures1.png'/> Videos: - -Heart Core --------------------------------------------------------------------------------- +<pre style="color: magenta;">Heart Core</pre> Description: 2D platformer game made during a game jam. Going through a variety of puzzles to complete while surviving dangerous threats, you must survive and evolve your cybernetic core into something more... @@ -255,8 +249,7 @@ Images: Videos: <a href='https://youtu.be/VwAjpK45JMI'>https://youtu.be/VwAjpK45JMI</a> -Hoppin World --------------------------------------------------------------------------------- +<pre style="color: magenta;">Hoppin World</pre> Description: 3D “bunny hopping” game. At the core, it is a platformer game with a twist. Your base speed is slow, but you can get to very high speeds by performing what is known as “air-strafing”. This is done by pressing the @@ -277,8 +270,7 @@ Images: Videos: <a href='https://youtu.be/8Jw-v2RPtvw'>https://youtu.be/8Jw-v2RPtvw</a> -Minigene --------------------------------------------------------------------------------- +<pre style="color: magenta;">Minigene</pre> Description: A game engine specialized in 2d ascii and tiled games. A heavy focus is given on prototyping speed and simplicity. Technologies: @@ -297,8 +289,7 @@ Images: <img src='./shotcaller1.png'/> Videos: - -Plank ECS --------------------------------------------------------------------------------- +<pre style="color: magenta;">Plank ECS</pre> Description: An entity-component-system library with a very heavy focus on simplicity, performance, safety and learnability. At the time of writing, it is the second fastest Rust ECS, while having approximately 1000 lines of code. @@ -313,8 +304,7 @@ Git: Images: - Videos: - -ScalEngine --------------------------------------------------------------------------------- +<pre style="color: magenta;">ScalEngine</pre> Description: 3D Game engine written in Scala. Originally actor based (Akka framework), then ECS (Entity-Component-System) based. Though it was never completed, the engine had 3d text and model rendering, a 3d @@ -332,8 +322,7 @@ Videos: <a href='https://www.youtube.com/watch?v=Zj2sPz5xJDY'>https://www.youtube.com/watch?v=Zj2sPz5xJDY</a> (It might look horrible, but the features are there. You can see I'm no artist.) -Shotcaller --------------------------------------------------------------------------------- +<pre style="color: magenta;">Shotcaller</pre> Description: An RTS / MOBA game with multiple frontends: ASCII-rendered or tiles-rendered. Technologies: diff --git a/target/html/quotes/index.html b/target/html/quotes/index.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch; @@ -51,8 +53,9 @@ <br/> <article> <pre> -Ridiculous arguments or quotes from everyday life. Sometimes intelligent stuff. -================================================================================ + + +<pre style="color: lime;">Ridiculous arguments or quotes from everyday life. Sometimes intelligent stuff.</pre> 2020-09-17: The most common addiction in the world is the draw of comfort. It wrecks dreams and breaks people. diff --git a/template.html b/template.html @@ -6,6 +6,7 @@ <title>Jojolepro.com</title> <meta charset=utf-8> <meta name=description content="A bunch of stuff and things!"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> <style> body{ text-align:center; @@ -19,6 +20,7 @@ pre{ text-align:left; display:inline-block; + width: 80ch; } img{ max-width:80ch;