<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Quentin Laffont blog - React / Node / Full-Stack / etc]]></title><description><![CDATA[Quentin Laffont blog - React / Node / Full-Stack / etc]]></description><link>https://blog.qlaffont.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 10 Jun 2026 08:51:30 GMT</lastBuildDate><atom:link href="https://blog.qlaffont.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Validate Your Environment Variables Easily with env-vars-validator]]></title><description><![CDATA[As a developer, you know that working with environment variables is a fundamental aspect of building applications. However, validating them can be a bit tricky. This is where env-vars-validator comes into play.
env-vars-validator is a TypeScript libr...]]></description><link>https://blog.qlaffont.com/validate-your-environment-variables-easily-with-env-vars-validator</link><guid isPermaLink="true">https://blog.qlaffont.com/validate-your-environment-variables-easily-with-env-vars-validator</guid><category><![CDATA[Node.js]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Environment variables]]></category><category><![CDATA[variables]]></category><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Wed, 15 Mar 2023 12:07:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1678881998350/9cbdd40b-6f5f-4a34-b22b-e69db8e96a12.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a developer, you know that working with environment variables is a fundamental aspect of building applications. However, validating them can be a bit tricky. This is where <code>env-vars-validator</code> comes into play.</p>
<p><code>env-vars-validator</code> is a TypeScript library that enables you to validate your environment variables easily. It's available on <a target="_blank" href="https://github.com/qlaffont/env-vars-validator">GitHub</a> and on <a target="_blank" href="https://www.npmjs.com/package/env-vars-validator">NPM</a>.</p>
<h2 id="heading-installation">Installation</h2>
<p>You can install <code>env-vars-validator</code> with NPM using the following command:</p>
<pre><code class="lang-bash">npm install env-vars-validator
pnpm install env-vars-validator
yarn install env-vars-validator
</code></pre>
<h2 id="heading-usage">Usage</h2>
<p>Using <code>env-vars-validator</code> is simple. First, import the <code>validateEnv</code> function from the library:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> { validateEnv } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"env-vars-validator"</span>);
</code></pre>
<p>Then, you can use it to validate your environment variables from an AJV schema:</p>
<pre><code class="lang-typescript">validateEnv(
  {
    NODE_ENV: { <span class="hljs-keyword">type</span>: <span class="hljs-string">'string'</span> },
    PORT: { <span class="hljs-keyword">type</span>: <span class="hljs-string">'integer'</span> },
  },
  {
    requiredProperties: [<span class="hljs-string">'NODE_ENV'</span>],
  },
);
</code></pre>
<p>This example validates that <code>NODE_ENV</code> is a string and that <code>PORT</code> is an integer. Additionally, it requires that <code>NODE_ENV</code> is present in the environment variables.</p>
<h2 id="heading-api">API</h2>
<p><code>env-vars-validator</code> provides several additional functions that can help you work with environment variables:</p>
<ul>
<li><p><code>currentEnv()</code>: Returns the current <code>NODE_ENV</code> without spaces and in lowercase format.</p>
</li>
<li><p><code>isProductionEnv()</code>: Returns <code>true</code> if the current <code>NODE_ENV</code> is equal to <code>production</code>.</p>
</li>
<li><p><code>isPreProductionEnv()</code>: Returns <code>true</code> if the current <code>NODE_ENV</code> is equal to <code>production</code>.</p>
</li>
<li><p><code>isStagingEnv()</code>: Returns <code>true</code> if the current <code>NODE_ENV</code> is equal to <code>staging</code>.</p>
</li>
<li><p><code>isDevelopmentEnv()</code>: Returns <code>true</code> if the current <code>NODE_ENV</code> is equal to <code>development</code>.</p>
</li>
<li><p><code>isTestEnv()</code>: Returns <code>true</code> if the current <code>NODE_ENV</code> is equal to <code>test</code>.</p>
</li>
<li><p><code>isDeployedEnv()</code>: Returns <code>true</code> if the current <code>NODE_ENV</code> is <strong>not</strong> equal to <code>development</code> or <code>test</code>.</p>
</li>
</ul>
<h2 id="heading-conclusion">Conclusion</h2>
<p><code>env-vars-validator</code> is a simple and powerful library that makes it easy to validate your environment variables. With its intuitive API and TypeScript support, it's a great choice for any project that needs to work with environment variables. Try it out today!</p>
]]></content:encoded></item><item><title><![CDATA[Create a portfolio : 2023 Edition]]></title><description><![CDATA[Hello 👋,
As a developer, you need to have a portfolio to show many things :

What can you do ? 
What do you like ?
Which projects did you realize ?
How to contact you ?

To answer these questions, a simple website can be enough. But, to make the dif...]]></description><link>https://blog.qlaffont.com/create-a-portfolio-2023-edition</link><guid isPermaLink="true">https://blog.qlaffont.com/create-a-portfolio-2023-edition</guid><category><![CDATA[portfolio]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[Vercel]]></category><category><![CDATA[Developer]]></category><category><![CDATA[professional]]></category><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Tue, 02 Aug 2022 06:19:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1659421085870/YE_8ZGGDi.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello 👋,</p>
<p>As a developer, you need to have a portfolio to show many things :</p>
<ul>
<li>What can you do ? </li>
<li>What do you like ?</li>
<li>Which projects did you realize ?</li>
<li>How to contact you ?</li>
</ul>
<p>To answer these questions, a simple website can be enough. But, to make the difference, you can optimize your website with several ways !</p>
<h2 id="heading-use-a-well-known-framework">⚙️ Use a well known framework</h2>
<p>In my case, I love React. </p>
<p>So I'm using a well known React framework called <a target="_blank" href="https://nextjs.org/">Next.js</a> (maintained by Vercel). I'm using it for many reasons, but the main reason is how it manages rendering and do optimizations. I can strongly advise you to check their <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching/overview">documentation</a> on SSR / SSG / ISR / CSR. </p>
<p>To handle design, I'm using <a target="_blank" href="https://tailwindcss.com/">TailwindCSS</a> especially for his simplicity and optimizations from PostCSS.</p>
<h2 id="heading-focus-on-speed">⚡ Focus on speed</h2>
<p>A portfolio need to be consulted everywhere, on a train, on a subway, at home, at office, etc. </p>
<p>As Google recommends, <strong>your website need to be around 500 KB</strong>. It's very small but to give you an example, a page with <strong>1.49 MB need 7 seconds on a fast 3G network</strong> to download and consult.</p>
<p>So you need to use as much as possible to use SVG or WebP and compress it if it is possible. On Next.js, you can generate WebP on the fly with <a target="_blank" href="https://nextjs.org/docs/api-reference/next/image#required-props">next/image</a>.</p>
<p>You can use a CDN to make your website faster across the world. Personally, I use <a target="_blank" href="https://vercel.com/">Vercel</a> who have this feature.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1659420422271/noilmmdHa.png" alt="Screenshot_2022-07-19_08-15-12.png" /></p>
<h2 id="heading-focus-on-seo">🖥️ Focus on SEO</h2>
<p>SEO or Search Engine Optimization is a complicated term to signify a simple thing. How can you optimize your website to be sure that your website will be displayed on Google / Bing / etc.</p>
<p>It doesn't exist a secret formula because everything is opaque, but there are some common things to do :</p>
<ul>
<li>Generate Meta tags to help robot indexation (<a target="_blank" href="https://github.com/garmeeh/next-seo">next-seo</a>)</li>
<li>Respect h1, h2, h3 order </li>
<li>Have a domain with your first name and last name and keep it as long as possible</li>
<li>All your IMG tags need to have an alt attribute</li>
</ul>
<p>You can use several tools who will help you to optimize your websites :</p>
<ul>
<li>Meta tags : <a target="_blank" href="metatags.io">https://metatags.io/</a></li>
<li>SEO : <a target="_blank" href="web.dev">https://web.dev</a></li>
</ul>
<h2 id="heading-translate-your-websites">💬 Translate your websites</h2>
<p>In my case, I'm a French developer. So I need to translate my website in French but in English too.</p>
<p>Why ? </p>
<ul>
<li>To be seen by French recruiter for local jobs</li>
<li>To be seen by International recruiter for local or full-remote jobs</li>
</ul>
<p>Stupid things, but very important to maximize your chances for your next opportunity !</p>
<h2 id="heading-make-your-website-automated">🤖 Make your website automated</h2>
<p>I have always heard that developer are lazy people. I can confirm, it is true !</p>
<p>Why did you need to develop a new website (and lose time) every time you create a new project, you publish a new article, you have been hired to a new company ?</p>
<p>I'm a strong fan of <a target="_blank" href="notion.so/">Notion</a>. I'm using it for my personal projects and professionally. </p>
<p>I have recently seen that is it possible to use Notion as a CMS (Content Management System). </p>
<p>So for each dynamic part, a Notion page have been created and with Next.js, I refresh and redeploy my website every 10 minutes to refresh changes with <a target="_blank" href="https://nextjs.org/docs/basic-features/data-fetching/incremental-static-regeneration">ISR</a>.</p>
<p>For me, dynamic part are :</p>
<ul>
<li>Experiences</li>
<li>Diplomas</li>
<li>Projects</li>
<li>Tools</li>
<li>News (For this, I'm using <a target="_blank" href="https://catalins.tech/hashnode-api-how-to-display-your-blog-articles-on-your-portfolio-page">Hashnode API</a>)</li>
</ul>
<p>In automation, <strong>I have pushed the limit further with resume generation</strong>. On my website, you can download my Resume in PDF, who will be generated from Notion data.</p>
<h2 id="heading-bonus-open-source-your-website">📃 Bonus : Open source your website</h2>
<p>As a developer, open source is very important because every library we used is developed by community and can be freely edited or used.</p>
<p>To make development less shady, I can encourage you to make your code freely accessible to share your knowledge with everyone. </p>
<p>Maybe some developers will see some issue with your code or will see some language issue and can summit a merge request. 😊</p>
<h2 id="heading-a-working-example">🥳 A working example</h2>
<p>I have recently updated my website to enter 2023 year ! </p>
<p>You can consult it on <a target="_blank" href="qlaffont.com">https://qlaffont.com</a> and the entire code can be consulted it on <a target="_blank" href="https://github.com/qlaffont/qlaffont-website/">Github</a>. </p>
<p>You can duplicate my Notion pages if you want too !</p>
<p>Have fun and let's code ! 🧑‍💻</p>
]]></content:encoded></item><item><title><![CDATA[How to download a file with fetch and send it with fetch (without fs) ? 🤔]]></title><description><![CDATA[Hello 👋,
I wrote this article because I struggle a lot to do a simple thing in Node.JS and I want to share it with you 🙂.
On a personal scrapping projects, I'm caching images to render it on a PDF. To do this, I save scrapped images on a dedicated ...]]></description><link>https://blog.qlaffont.com/how-to-download-a-file-with-fetch-and-send-it-with-fetch-without-fs</link><guid isPermaLink="true">https://blog.qlaffont.com/how-to-download-a-file-with-fetch-and-send-it-with-fetch-without-fs</guid><category><![CDATA[Node.js]]></category><category><![CDATA[fetch]]></category><category><![CDATA[fs]]></category><category><![CDATA[Electron]]></category><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Tue, 19 Jul 2022 06:00:55 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1658210373187/rn5t_g6of.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello 👋,</p>
<p>I wrote this article because I struggle a lot to do a simple thing in Node.JS and I want to share it with you 🙂.</p>
<p>On a personal scrapping projects, I'm caching images to render it on a PDF. To do this, I save scrapped images on a dedicated server 📁. </p>
<p>But my problem is on my Electron App, If I'm on macOS, I can't save my temp image on App Installation folder. Fs access is forbidden ❌.</p>
<p>So with node-fetch, I try to simulate File API with some libraries, send Stream, Buffer, etc. None of these solutions have been working. </p>
<p>Except this one ✅ :</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> FormData <span class="hljs-keyword">from</span> <span class="hljs-string">'form-data'</span>;
<span class="hljs-keyword">import</span> fetch <span class="hljs-keyword">from</span> <span class="hljs-string">'node-fetch'</span>;

<span class="hljs-comment">// Download original Image</span>
<span class="hljs-keyword">const</span> res = <span class="hljs-keyword">await</span> fetch(imageUrl);

<span class="hljs-comment">// Convert response to buffer</span>
<span class="hljs-keyword">const</span> arrayBuffer = <span class="hljs-keyword">await</span> res.arrayBuffer();
<span class="hljs-keyword">const</span> buffer = Buffer.from(
  arrayBuffer,
  <span class="hljs-literal">undefined</span>,
  arrayBuffer.byteLength,
);

<span class="hljs-keyword">const</span> formData = <span class="hljs-keyword">new</span> FormData();

formData.append(<span class="hljs-string">'file'</span>, buffer, {
  <span class="hljs-attr">contentType</span>: <span class="hljs-string">'text/plain'</span>,
  <span class="hljs-attr">name</span>: <span class="hljs-string">'file'</span>,
  <span class="hljs-attr">filename</span>: <span class="hljs-string">'test.png'</span>,
});

<span class="hljs-keyword">try</span> {
  <span class="hljs-comment">//Send it to new server</span>
  <span class="hljs-keyword">await</span> fetch(
    <span class="hljs-string">`https://my-server.com/image`</span>,
    {
      <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
      <span class="hljs-attr">body</span>: formData,
      <span class="hljs-attr">headers</span>: formData.getHeaders(),
    },
  );
} <span class="hljs-keyword">catch</span> (error) {
  <span class="hljs-built_in">console</span>.log(error);
}
</code></pre>
<p>I hope this solution will help you too. Because I have lost nearly 4 hours with this problem 😅</p>
]]></content:encoded></item><item><title><![CDATA[Monetize your Discord with Chargebee & Stripe]]></title><description><![CDATA[Hello everyone 👋,
As a developer and as a community owner, I have created many Discord servers to handle every product support 🫂 or to create gaming communities 🎮.
I wanted to create some restricted role to have exclusive content or to have a prio...]]></description><link>https://blog.qlaffont.com/monetize-your-discord-with-chargebee-and-stripe</link><guid isPermaLink="true">https://blog.qlaffont.com/monetize-your-discord-with-chargebee-and-stripe</guid><category><![CDATA[stripe]]></category><category><![CDATA[PayPal]]></category><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Wed, 20 Oct 2021 05:48:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1634708882489/4B1SU2kRw.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hello everyone 👋,</p>
<p>As a developer and as a community owner, I have created many Discord servers to handle every product support 🫂 or to create gaming communities 🎮.</p>
<p>I wanted to create some restricted role to have exclusive content or to have a priority support, but no service exist to handle this. This service need to allow me to assign role if user subscribe to one of my products 💳.</p>
<p>To solve that, I created OnlyDfans. It’s a simple service where you can connect your Stripe account or your Chargebee credentials in a couple of minutes.</p>
<p><strong>This service is FREE during beta and the subscription will be $40 by server by month.</strong></p>
<p>Let’s see how it works !</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634708575128/obAO_XBuM.png" alt="Screenshot from 2021-10-20 07-16-45.png" /></p>
<h2 id="i-go-to-the-app">I. Go to the app</h2>
<p>You need to go to https://app.onlydfans.io and you need to connect to Discord.</p>
<h2 id="ii-create-a-new-server">II. Create a new server</h2>
<p>In this page, you will see all your servers, but you will see, too, all your subscriptions in each server.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634708615957/PnSogzPdW.png" alt="Screenshot from 2021-10-20 07-20-07.png" /></p>
<p>Now you can press on plus button and select one of your servers. Only servers where you are owner are listed !</p>
<h2 id="iii-configure-your-server">III. Configure your server</h2>
<p>You need to click on “Manage server” and click after that to “
Setup server”.</p>
<p>You need to :</p>
<ul>
<li>Invite Discord bot to your server</li>
<li>Select your payment provider</li>
<li>Create webhook configuration in your payment provider panel (Products, Customer Portal, etc.)</li>
<li>Enter keys and other requirements for your payment provider</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634708680154/sAC42fzk4.png" alt="Screenshot from 2021-10-20 07-26-15.png" /></p>
<h2 id="iv-create-your-product">IV. Create your product</h2>
<p>With OnlyDfans, you can provide subscription with many use case</p>
<ul>
<li>Free (without restriction)</li>
<li>Password Restricted</li>
<li>Time Restricted</li>
<li>Quantity Restricted</li>
</ul>
<p>So now, you can create your product :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634708723832/lzRjNJBuq.png" alt="Screenshot from 2021-10-20 07-32-21.png" /></p>
<h2 id="v-share-your-link">V. Share your link</h2>
<p>Now you can go to your product detail to get the checkout link.
And share it to your future customers.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634708753640/RHIIMeKva.png" alt="1*NHZSlqsEmBaVtThOnSP5CA.png" /></p>
<h2 id="conclusion">Conclusion</h2>
<p>It takes us only 5 minutes to set up everything. If you need help, we can help you to set up everything in our Discord server : https://discord.gg/cY58pMA2Hb. Now it’s time to make money 🤑.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1634708777713/CyFJKgKt7.gif" alt="giphy.gif" /></p>
]]></content:encoded></item><item><title><![CDATA[Shadow ou PC ? Finalement PC]]></title><description><![CDATA[Bonjour tout le monde,
Plusieurs personnes m’ont demandées une suite de mon article “**Pourquoi j’ai mis ma tour de jeu de 1500 € au placard ?**”. Cette article où j’expliquais pourquoi j’ai vendu mon PC pour prendre un Shadow. Depuis la situation a ...]]></description><link>https://blog.qlaffont.com/shadow-ou-pc-finalement-pc-740f4539bd71</link><guid isPermaLink="true">https://blog.qlaffont.com/shadow-ou-pc-finalement-pc-740f4539bd71</guid><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Tue, 08 Dec 2020 13:26:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599307631/-Y0JWy5fA.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Bonjour tout le monde,</p>
<p>Plusieurs personnes m’ont demandées une suite de mon article “<a target="_blank" href="https://medium.com/@qlaffont13/pourquoi-jai-mis-ma-tour-de-jeu-de-1500-au-placard-18fd1160f0aa">**Pourquoi j’ai mis ma tour de jeu de 1500 € au placard ?</a>**”. Cette article où j’expliquais pourquoi j’ai vendu mon PC pour prendre un Shadow. Depuis la situation a changée, parlons en !</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599283321/vSjSvKXcm.gif" alt /></p>
<blockquote>
<p>Cette article fait partie d’une série d’article :</p>
<ul>
<li><a target="_blank" href="https://qlaffont.medium.com/pourquoi-jai-mis-ma-tour-de-jeu-de-1500-au-placard-18fd1160f0aa">Pourquoi j’ai mis ma tour de jeu de 1500 € au placard ?</a></li>
<li><a target="_blank" href="https://qlaffont.medium.com/vr-oculus-quest-shadow-est-ce-possible-90877d16d398">VR (Oculus Quest) + Shadow ? Est ce possible ?</a></li>
<li><a target="_blank" href="https://qlaffont.medium.com/shadow-ou-pc-finalement-pc-740f4539bd71">Shadow ou PC ? Finalement PC ?</a></li>
</ul>
</blockquote>
<h2 id="1-constat-avec-la-covid">1. Constat avec la COVID</h2>
<p><em>(Attention, je parle de la période du 14 Janvier 2020 au 20 juin 2020)</em></p>
<p>Pour information à l’époque où j’avais écris le premier article j’étais encore sur Paris, je possédais un modeste 30 m2 et j’étais en vadrouille entre Marseille, Poitiers, Montpellier, New York, etc. Depuis je suis redescendu dans le sud de la France pour me remettre au vert (<em>juste avant la COVID, ouf !</em>) et je bouge plus.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599287749/rwDvlxpgK.gif" alt /></p>
<p><strong>Premier constat,</strong> <strong>c’essssttt llleeennnttt</strong> alors que je suis fibré et que j’ai une bonne connexion. Après vérification, les serveurs Shadow ont été plus soutenu qu’avant et leurs infrastructures n’ont pas permis de ternir la charge.</p>
<p><strong>Deuxième constat, des mesures draconiennes illustrant un souci de fond. </strong>Depuis les soucis de lenteurs, Shadow a pris des mesures pour pouvoir réduire la charge serveur mais qui cependant ont eu certains effets :</p>
<ul>
<li><p>Mise en pause automatique si aucune activité de souris est détecté au bout de 15 minutes : Super, j’utilise la VR notamment pour jouer à Half-Life Alyx, je suis obligé de m’arrêter toutes les 15 minutes pour faire bouger la souris du Shadow …</p>
</li>
<li><p>Des latences de plus en plus présente …</p>
</li>
<li><p>Une file d’attente 0-o : J’ai du attendre plusieurs minutes pour accéder à mon PC et comme dès fois je fais autres choses, je reviens mon Shadow est éteint et je dois ré-attendre pour y accéder de nouveau…</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599296149/SPqcneuyP.gif" alt /></p>
<h2 id="2-shadow-lentreprise-victime-de-son-succes">2. Shadow, l’entreprise victime de son succès</h2>
<p>Depuis le début des annonces de Shadow Ultra, j’avais pré-commandé l’évolution de mon Shadow. Cependant je n’avais toujours rien reçu et chaque jour je regardais quand ces produits seraient de nouveau en stock.</p>
<p>De plus, avec la COVID, les délais d’attente se sont rallongés.</p>
<p>J’en avais pour <strong>plus d’1 an d’attente</strong> et tout ça en tournant sur des cartes graphiques où il y avait 1 à 2 générations de retard, un vieux processeur, etc…</p>
<p>Super …</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599299700/ukmYFoyeE.gif" alt /></p>
<h2 id="3-un-gout-de-deja-vu">3. Un goût de déjà vu</h2>
<p>Depuis je suis retourné au source, à la bonne vielle tour. J’ai mis des sous de côté et je me suis remonté une tour sur <a target="_blank" href="https://www.topachat.com">**Top Achat</a>**.</p>
<p>Elle a beau prendre la poussière, prendre de l’espace et faire un peu de bruit mais au moins je peux y accéder quand je veux, j’ai une bonne connexion et si je veux y accéder en remote j’utilise le logiciel <a target="_blank" href="https://parsecgaming.com/">**Parsec</a> **qui fait largement le taff !</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599302478/FtWDrBNMD.gif" alt /></p>
<h2 id="conclusion">Conclusion</h2>
<p>Pour conclure, je pense que Shadow est une boite tech innovante et que leurs produits sont cool mais ils n’ont pas assez de stabilité, et de financement pour vraiment proposer une solution béton.</p>
<p>Je conseille Shadow si vous êtes passioné de tech et que vous voulez tester et voir ce qu’on fait maintenant en virtualisation mais je le conseille pas en utilisation quotidienne.</p>
<p>Voilà, n’hésitez pas à me faire un retour sur l’article et à me suivre sur <a target="_blank" href="https://twitter.com/qlaffont">Twitter </a>^^.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599305207/nNo4KO4Ig.gif" alt /></p>
]]></content:encoded></item><item><title><![CDATA[Discord x Stripe : Simple payment integration ⚡️]]></title><description><![CDATA[I have created a service who is called OnlyDfans to make this configuration for you ! More information to https://onlydfans.io

As a developer, I have coded many bot with Discord.js.
I have created a bot recently, “Discord Protector”. It’s a Global B...]]></description><link>https://blog.qlaffont.com/discord-x-stripe-simple-payment-integration</link><guid isPermaLink="true">https://blog.qlaffont.com/discord-x-stripe-simple-payment-integration</guid><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Mon, 31 Aug 2020 13:10:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599328601/LJ2XRGgoZ.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>I have created a service who is called OnlyDfans to make this configuration for you ! More information to https://onlydfans.io</p>
</blockquote>
<p>As a developer, I have coded many bot with Discord.js.</p>
<p>I have created a bot recently, <strong>“Discord Protector”</strong>. It’s a Global Ban Management bot with many extra features (<a target="_blank" href="https://discord-protector.qlaffont.com">https://discord-protector.qlaffont.com</a>).</p>
<p>I want to enable extra features with subscription quickly and in a simple way. Solution: <strong>Stripe</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599318916/M9M3Ax_7K.png" alt="Let’s start !" /><em>Let’s start !</em></p>
<blockquote>
<p>If you are interested and you want to have a solution like this, I can do it ! Contact me at <strong>contact@qlaffont.com</strong></p>
</blockquote>
<h2 id="todo-list">Todo-list</h2>
<p>To enable extra features, I need to achieve this todo-list :</p>
<p>1 — 💳 Redirect user to Stripe to pay subscription</p>
<p>2 — 👨‍💻 Create a customer platform to manage subscriptions</p>
<p>3 — 🖥 Receive Stripe webhook to enable extra features</p>
<h2 id="i-stripe-checkout">I. 💳 Stripe Checkout</h2>
<p>For people who don’t know, <a target="_blank" href="https://stripe.com">Stripe</a> is a payments infrastructure. It’s a famous platform and many website use it (Deliveroo, Banking, Asos, etc.).</p>
<p>One of the most famous product is <strong>Stripe Checkout</strong>. This product create for you a checkout page with everything (Branding, Product details, etc.).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599321533/MR_32W9Iy.gif" alt="Stripe Checkout" /><em>Stripe Checkout</em></p>
<p>This product <strong>will reduce for me a lot of coding time</strong>.</p>
<p>After reading <a target="_blank" href="https://stripe.com/docs/payments/checkout">Stripe Checkout Documentation</a>, I know that I need to create a Stripe Customer and generate a Stripe URL when user want to pay his subscription. (In this case, when user type in channel <em>!banp pay</em>)</p>
<pre><code><span class="hljs-keyword">const</span> stripeUser = <span class="hljs-keyword">await</span> stripe.customers.create();

<span class="hljs-comment">//Save stripe customer id in db -&gt; stripeUser.id</span>

<span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> stripe.checkout.sessions.create(
{
      <span class="hljs-attr">payment_method_types</span>: [<span class="hljs-string">"card"</span>],
      <span class="hljs-attr">line_items</span>: [
        {
          <span class="hljs-attr">price</span>: PRODUCT_PRICEID,
          <span class="hljs-attr">quantity</span>: <span class="hljs-number">1</span>,
        },
      ],
      <span class="hljs-attr">customer</span>: stripeUser.id,
      <span class="hljs-attr">mode</span>: <span class="hljs-string">"subscription"</span>,
      <span class="hljs-attr">success_url</span>: <span class="hljs-string">"MY_SUCCESS_URL"</span>,
      <span class="hljs-attr">cancel_url</span>: <span class="hljs-string">"MY_CANCEL_URL"</span>
});

<span class="hljs-comment">// Url to send to user in private message</span>
url = <span class="hljs-string">`/payment-page?CHECKOUT_SESSION_ID=<span class="hljs-subst">${session.id}</span>`</span>;
</code></pre><p>And I have created a webpage to redirect user to checkout page :</p>
<pre><code><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://js.stripe.com/v3/"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span><span class="javascript">
      <span class="hljs-keyword">var</span> stripe = Stripe(<span class="hljs-string">'MY_STRIPE_KEY'</span>);

      <span class="hljs-keyword">var</span> urlParams = <span class="hljs-keyword">new</span> URLSearchParams(<span class="hljs-built_in">window</span>.location.search);

      stripe.redirectToCheckout({
      <span class="hljs-attr">sessionId</span>: urlParams.get(<span class="hljs-string">'CHECKOUT_SESSION_ID'</span>)
      });
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre><p><strong>Step 1 - Done</strong> ✅</p>
<h2 id="ii-stripe-billing-customer-portal"><strong>II. 👨‍💻 Stripe Billing Customer Portal</strong></h2>
<p>Recently, I have seen on ProductHunt a <a target="_blank" href="https://www.producthunt.com/posts/stripe-billing-customer-portal">post</a> for Stripe Billing Customer Portal.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599323965/_dhbaQELi.png" alt="Stripe Billing Customer Portal" /><em>Stripe Billing Customer Portal</em></p>
<p>It’s a new product released by Stripe Team who <strong>create a Stripe-hosted page for customers to manage subscriptions (Change Plan, Cancel Plan) and to manage invoices</strong>.</p>
<p>Perfect, now I know that this step is simple like step 1.</p>
<p>After reading <a target="_blank" href="https://stripe.com/docs/billing/subscriptions/integrating-customer-portal">Stripe Billing Customer Portal</a>, I know that I just need to communicate a stripe url to user.</p>
<pre><code>const <span class="hljs-keyword">session</span> = await stripe.billingPortal.sessions.<span class="hljs-keyword">create</span>({
   customer: <span class="hljs-keyword">server</span>.stripeCustomerId,
   return_url: "MY_RETURN_URL",
});

//Send url <span class="hljs-keyword">to</span> <span class="hljs-keyword">user</span> <span class="hljs-keyword">in</span> private message -&gt; <span class="hljs-keyword">session</span>.url
</code></pre><p><strong>Step 2 - Done</strong> ✅</p>
<h2 id="iii-stripe-webhook-integration">III. 🖥 Stripe Webhook Integration</h2>
<p>To handle Stripe Webhook, I need to create an HTTP Route to receive events. To do that, I use <a target="_blank" href="https://www.fastify.io/">Fastify</a> but you can use what you want to make an HTTP Server.</p>
<pre><code>f.post(<span class="hljs-string">"/webhook"</span>, <span class="hljs-keyword">async</span> (request: FastifyRequest, reply: FastifyReply) =&gt; {
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">event</span> = request.body;
    <span class="hljs-keyword">switch</span> (<span class="hljs-keyword">event</span>.type) {
      <span class="hljs-keyword">case</span> <span class="hljs-string">"checkout.session.completed"</span>:

        <span class="hljs-comment">//Save Customer ID and save the fact that now he can use Stripe Billing Customer Portal</span>

        <span class="hljs-keyword">break</span>;
      <span class="hljs-keyword">case</span> <span class="hljs-string">"customer.subscription.updated"</span>:

        <span class="hljs-comment">// Check product id and save subscription date end (event.data.object.current_period_end)</span>

        <span class="hljs-keyword">break</span>;
      <span class="hljs-keyword">case</span> <span class="hljs-string">"customer.subscription.deleted"</span>:

        <span class="hljs-comment">// Check product id and save subscription date end (event.data.object.current_period_end)</span>

      <span class="hljs-keyword">default</span>:
        <span class="hljs-comment">// Unexpected event type</span>
        <span class="hljs-keyword">return</span> reply.status(<span class="hljs-number">400</span>).send();
    }

    reply.send({ received: <span class="hljs-literal">true</span> });
  });
</code></pre><p><strong>Step 3 - Done</strong> ✅</p>
<p>(Step 4 - Test with <a target="_blank" href="https://stripe.com/docs/testing">Stripe testing cards</a> to check if everything work.)</p>
<h2 id="conclusion">Conclusion</h2>
<p>It tooks me less than 1 day to integrate payment in my Discord.js bot.</p>
<p>Stripe documentation is simple and integration is very fast ⚡️. Now you know how to integrate Stripe in Discord.js bot without any website to create.</p>
<p>If you have any question, you can contact me on Twitter <strong>@qlaffont 😎</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599326602/9CcWHNa8K.gif" alt /></p>
]]></content:encoded></item><item><title><![CDATA[VR (Oculus Quest) + Shadow ? Est ce possible ?]]></title><description><![CDATA[Cette article fait partie d’une série d’article :

Pourquoi j’ai mis ma tour de jeu de 1500 € au placard ?
VR (Oculus Quest) + Shadow ? Est ce possible ?
Shadow ou PC ? Finalement PC ?

Depuis la sortie de l’Oculus Quest, j’étais très impatient de po...]]></description><link>https://blog.qlaffont.com/vr-oculus-quest-shadow-est-ce-possible-90877d16d398</link><guid isPermaLink="true">https://blog.qlaffont.com/vr-oculus-quest-shadow-est-ce-possible-90877d16d398</guid><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Thu, 16 Apr 2020 08:39:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599926410/ttM7_ByL0.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cette article fait partie d’une série d’article :</p>
<ul>
<li>Pourquoi j’ai mis ma tour de jeu de 1500 € au placard ?</li>
<li>VR (Oculus Quest) + Shadow ? Est ce possible ?</li>
<li>Shadow ou PC ? Finalement PC ?</li>
</ul>
<p>Depuis la sortie de l’<a target="_blank" href="https://www.oculus.com/quest/?locale=fr_FR">Oculus Quest</a>, j’étais très impatient de pouvoir tester ce petit bijou de tech qui permet de faire de <strong>la réalité virtuelle sans fil</strong>. De plus, j’ai pu le tester quasiment à sa sortie (grâce à Romain Cochet, 👋 <a target="_blank" href="https://www.smiirl.com/">Smiirl</a>).</p>
<p>Le feeling du casque était immédiat:</p>
<ul>
<li><p>Pouvoir jouer à de la VR ! 👀</p>
</li>
<li><p>Sans fil à connecter à un PC et du coup avoir une sensation de liberté ! 🖥</p>
</li>
<li><p>La possibilité de pouvoir définir une zone de jeu très facilement et dans plusieurs endroit différents ! 🏘</p>
</li>
<li><p>Un prix relativement honnête pour ce qui est embarqué dans le casque ! 💸</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599907313/vqRmqUxmc.gif" alt /></p>
<p>Cependant je n’ai pas sauté sur l’occasion pour acheter le casque. Trop chère pour le moment, et trop de projet à côté.</p>
<p>Un confinement plus tard, je me décide à l’acheter pour re-tester le casque et voir si il est possible de le connecter à Shadow.</p>
<h2 id="i-le-communique-officiel-de-shadow">I. Le communiqué officiel de Shadow 📄</h2>
<p>Depuis que je suis chez Shadow, je lis chaque Shadow News pour savoir si il est possible de se connecter à Shadow via l’Oculus Quest. J’ai pu remarquer, que Shadow lance le <a target="_blank" href="https://community.shadow.tech/usen/blog/news/shadow-vr-exploration">**Shadow VR Exploration Program</a> **notamment dédié pour l’Oculus Quest.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599909251/gOJlET_a2.jpeg" alt /></p>
<p>La désillusion fut rude car j’ai pu réalisé que sur Reddit + autres sources, que ce programme est <strong>réservé uniquement aux clients US </strong>🇺🇸 car l’équipe de dev en charge du projet est basé là bas. 😭</p>
<h2 id="ii-discussion-sur-le-discord-shadow">II. Discussion sur le discord Shadow 🗣</h2>
<p>Malgré cette échec, je me met à communiquer sur le <a target="_blank" href="https://discord.gg/shadowtech">Discord officiel Francophone de Shadow</a> 🇫🇷 afin de poser la question. Un des admins me réponds qu’il faut que j’aille sur le <a target="_blank" href="https://discord.gg/jMv4gy3">Discord officiel Américain de Shadow</a> 🇺🇸 car il y a un canal de discussion dédié qui permet de parler de ce sujet.</p>
<p>Comment vous dire, <strong>je fonce </strong>🏃‍♂️ !</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599912188/amUbnCDo0.gif" alt /></p>
<p>Je remarque notamment dans le channel <strong>#experiment-vr </strong>des messages épinglés. Plein de message qui parle de la fameuse bêta en cours de test. Mais je remarque un lien vers <a target="_blank" href="https://www.reddit.com/r/OculusQuest/comments/frx8yw/how_to_set_up_shadowpc_virtual_desktop_quest_to/?utm_source=share&amp;utm_medium=ios_app&amp;utm_name=iossmf">Reddit</a> sur une procédure à réaliser le temps que Shadow publie officiellement la fonctionnalité. Je clique, et là, <strong>Bingo !</strong></p>
<p>Je vois une procédure un peu longue mais clair ! Je vais vous faire une traduction dans le prochain chapitre avec mes commentaires.</p>
<h2 id="iii-procedure-shadow-sur-oculus-quest">III. Procédure Shadow sur Oculus Quest 👨‍💻</h2>
<p>Pour rappel, vous pouvez retrouver la procédure <a target="_blank" href="https://www.reddit.com/r/OculusQuest/comments/frx8yw/how_to_set_up_shadowpc_virtual_desktop_quest_to/?utm_source=share&amp;utm_medium=ios_app&amp;utm_name=iossmf">sur ce lien</a>.</p>
<p><strong>Matériel Requis :</strong></p>
<ul>
<li><p>Oculus Quest</p>
</li>
<li><p><strong>Une Connexion Wifi en 5Ghz</strong> <strong>stable + une bonne connexion internet !</strong></p>
</li>
<li><p>Ordinateur Windows</p>
</li>
<li><p>Une Session Shadow</p>
</li>
<li><p>Un câble USB C &lt;-&gt; USB (le câble de votre téléphone suffit)</p>
</li>
</ul>
<p><strong>Procédure :</strong></p>
<ul>
<li><p>Acheter sur l’Oculus Store l’application “Virtual Desktop” et installé là sur votre Oculus Quest.</p>
</li>
<li><p>Suivez la procédure pour installer SideQuest sur votre ordinateur et déverrouiller votre casque. <a target="_blank" href="https://sidequestvr.com/#/setup-howto">Procédure Side Quest</a></p>
</li>
<li><p>Installer les dernières mise à jour Windows + Nvidia sur votre Shadow.</p>
</li>
<li><p>Installer le logiciel <a target="_blank" href="https://www.oculus.com/rift/setup/?locale=fr_FR">Oculus Desktop</a> sur votre Shadow (Ne faites pas l’étape où le logiciel vous demande de connecter votre casque).</p>
</li>
<li><p>Redémarrer votre session Shadow pour être sur que tous les logiciels sont bien installé.</p>
</li>
<li><p>Installer le logiciel <a target="_blank" href="https://www.vrdesktop.net/">Virtual Desktop Streamer App</a></p>
</li>
<li><p>Lancer le logiciel Virtual Desktop Streamer App. Il faut saisir votre pseudo Oculus dans le logiciel puis avoir la même configuration que présenté dans la photo ci-dessous</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599915211/W7QuhnDhy.png" alt="Configuration Virtual Desktop Streamer App" /><em>Configuration Virtual Desktop Streamer App</em></p>
<ul>
<li><p>Installer <a target="_blank" href="https://store.steampowered.com/about/">Steam</a> + <a target="_blank" href="https://store.steampowered.com/app/250820/SteamVR/?l=french">SteamVR</a> sur votre session Shadow</p>
</li>
<li><p>Installer des jeux VR sur steam pour tester comme <a target="_blank" href="https://store.steampowered.com/app/450390/The_Lab/?l=french">The Lab</a></p>
</li>
<li><p>La configuration est terminé ! En réalité, il reste quelques étapes à faire, mais vous allez voir.</p>
</li>
</ul>
<h2 id="iv-lancer-des-jeux-steam-vr-sur-shadow">IV. Lancer des jeux Steam VR sur Shadow 🎮</h2>
<ul>
<li><p>Démarrer votre session Shadow sur votre ordinateur (prenez soin de régler la bande passante alloué à<strong> 5 Mb/s pour réduire l’impact sur votre connexion internet</strong>.</p>
</li>
<li><p>Lancer le logiciel <strong>Virtual Desktop Streamer App</strong> sur votre session Shadow.</p>
</li>
<li><p>Sur votre Oculus Quest, allez dans “<strong>Bibliothèque</strong>”, dans “<strong>Sources inconnues</strong>”, et lancer l’application “<strong>Quest App Launcher</strong>”.</p>
</li>
<li><p>Vous allez retrouver l’application <strong>Virtual Desktop</strong> et lancez là.</p>
</li>
<li><p>Ensuite allez dans <strong>Settings</strong> et régler <strong>tous les paramètres en low</strong> si vous avez l’offre Basic ou l’offre de base de Shadow (Je suis en waiting list pour la Ultra, je vous donnerai la configuration à faire avec la nouvelle config).</p>
</li>
<li><p>Enfin vous allez retrouver la liste des jeux VR installé dans la section <strong>Games</strong>.</p>
</li>
<li><p>Et lancé le jeu que vous souhaitez utiliser ^^ !</p>
</li>
</ul>
<h2 id="v-retour-dexperience">V. Retour d’expérience 👍</h2>
<p>Personnellement, même si la procédure est longue, je trouve que c’est vraiment cool de jouer à de la VR sans avoir un fil. De plus, je retrouve notamment tout le catalogue Steam sur mon Oculus Quest et notamment LE jeu VR du moment <a target="_blank" href="https://store.steampowered.com/app/546560/HalfLife_Alyx/">Half-Life: Alyx</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599917884/6h63Qsb7m.gif" alt /></p>
<p>Par contre j’ai pu remarquer quelques bugs:</p>
<ul>
<li><p>Quelques rare freeze mais surement dû à ma connexion wifi</p>
</li>
<li><p>Quand on tourne la tête un peu rapidement on peux apercevoir quelques contour noir sur les extrémités.</p>
</li>
</ul>
<p>Malgré ces bugs, <strong>l’expérience globale est plutôt très bonne</strong> ! Et j’ai hâte de tester la fonctionnalité de Shadow quand elle sortira pour expérimenter la nouvelle procédure qui devrait être plus rapide !</p>
<p>Si vous avez des questions, ou des suggestions n’hésitez pas à m’envoyer un message sur Twitter à <strong>@qlaffont </strong>ou vous pouvez répondre en commentaire :).</p>
]]></content:encoded></item><item><title><![CDATA[Pourquoi j’ai mis ma tour de jeu de 1500 € au placard ?]]></title><description><![CDATA[Cette article fait partie d’une série d’article :

Pourquoi j’ai mis ma tour de jeu de 1500 € au placard ?
VR (Oculus Quest) + Shadow ? Est ce possible ?
Shadow ou PC ? Finalement PC ?

Bonjour à tous, pour ceux qui me connaissent vous savez que je j...]]></description><link>https://blog.qlaffont.com/pourquoi-jai-mis-ma-tour-de-jeu-de-1500-au-placard-18fd1160f0aa</link><guid isPermaLink="true">https://blog.qlaffont.com/pourquoi-jai-mis-ma-tour-de-jeu-de-1500-au-placard-18fd1160f0aa</guid><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Fri, 26 Jul 2019 10:00:15 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599276176/6UKhsEjNs.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cette article fait partie d’une série d’article :</p>
<ul>
<li>Pourquoi j’ai mis ma tour de jeu de 1500 € au placard ?</li>
<li>VR (Oculus Quest) + Shadow ? Est ce possible ?</li>
<li>Shadow ou PC ? Finalement PC ?</li>
</ul>
<p>Bonjour à tous, pour ceux qui me connaissent vous savez que je joue depuis longtemps et que j’ai fondé des communautés de jeux (Family of Gaming, The Spirit Brothers, Fortnite France, Rainbow 6 FR, FiveM France). 
<strong>Le jeu vidéo c’est une passion </strong>et je stream aussi de temps sur twitch (<a target="_blank" href="https://twitch.tv/mirardes">https://twitch.tv/mirardes</a> un petit peu de pub ne fait pas de mal).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599256121/a0dWr0sF3.gif" alt /></p>
<p>J’ai longtemps monté des PCs perso(ou pour des amis) pour jouer à des jeux gourmands en graphisme. Mais depuis près d’un an, j’ai vendu ma tour et j’utilise un service nommée <a target="_blank" href="https://shadow.tech/">Shadow</a>. <strong>Je vais donc vous donner en détails mon utilisation et pourquoi aujourd’hui je ne regrette pas d’avoir changé.</strong></p>
<p><em>Update: J’ai quitté Shadow et je me suis acheté un PC Fixe car le délai d’attente pour upgrade ses composants est trop long… Au final, j’utilise le service Rainway qui fait la même chose avec un PC Fixe.</em></p>
<h2 id="i-pour-quelle-utilisation">I. Pour quelle utilisation ?</h2>
<p>Je suis ce qu’on appelle un casual gamer, c’est à dire un joueur qui joue de temps en temps. Cependant étant un grand fan de FPS et de jeu de tir, je suis assez exigant sur l’input lag et la fréquence de rafraichissement (du coup <strong>144Ghz obligatoire</strong>).</p>
<p>Mais <strong>je veux aussi avoir une console de jeu </strong>que je peux connecter à la télé du salon afin de pouvoir jouer sur un plus grand écran quand j’ai des amis à la maison.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599258403/tLX4itR5N.gif" alt /></p>
<p>Donc il me faut une solution mobile mais aussi une solution qui tiens la charge de graphique poussé avec un bon temps de rafraichissement.</p>
<h2 id="ii-configuration-avec-ma-tour">II. Configuration avec ma tour</h2>
<p>Concernant la configuration avec ma tour de jeu, je faisais du stream dessus et je recordais mon jeu dessus. Configuration simple (1 PC qui fait tout) mais cependant <strong>j’étais obligé de baisser les graphiques et le nombre de FPS </strong>(du coup de 144 fps à 60 fps) afin que le stream et l’expérience de jeu sois fluide. <em>Pas cool :/</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599261104/g565avoa3.gif" alt /></p>
<p>Ensuite concernant l’expérience sur mon salon, j’utilisais un <a target="_blank" href="https://store.steampowered.com/app/353380/Steam_Link/?l=french">Steam Link</a>. Tout l’équipement fonctionnait niquel mais aujourd’hui <a target="_blank" href="https://www.numerama.com/pop-culture/440900-destocke-a-275-e-le-steam-link-de-valve-est-officiellement-mis-a-la-retraite.html">Valve a décidé de ne plus supporter ce produit</a> génialissime qui permettait de streamer le contenu de mon PC à l’autre bout de la pièce (sans avoir besoin d’internet).<em> Pas cool n°2 :/</em></p>
<h2 id="iii-configuration-avec-mon-shadow">III. Configuration avec mon Shadow</h2>
<p>Concernant l’expérience de jeu, j’utilise un vieux pc portable de jeu qui me permet de stream et qui va capturer l’expérience de jeu sur l’application Windows Shadow. <strong>Ce qui permet de séparer la puissance exigé pour le stream, de la puissance pour faire tourner les jeux.</strong></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599263848/7ZmriLouu.gif" alt /></p>
<p>Concernant l’expérience de jeu salon, j’utilise mon Macbook Pro qui fait tourner l’application Shadow, connecté à des manettes Xbox One en bluetooth (j’ai connecté 4 manettes, <a target="_blank" href="https://apple.stackexchange.com/questions/228960/is-there-a-limit-to-amount-of-bluetooth-devices-connected-to-a-macbook-pro">plus d’infos ici</a>).</p>
<h2 id="iv-comparaison">IV. Comparaison</h2>
<p>On va énumérer ici des points positifs que j’ai pu noter pour chaque solution. C’est un avis strictement personnel, donc je ne donne que mon avis et l’expérience que j’ai pu en avoir. (+ = point positif | — = point négatif)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599266533/gKi7f3Y_4.gif" alt /></p>
<p>PC :</p>
<ul>
<li><ul>
<li>Mes données sont stockées en local.</li>
</ul>
</li>
<li><ul>
<li>Toujours fonctionnel même quand il y a pas d’internet.</li>
</ul>
</li>
<li><ul>
<li>Je connais mon PCs et je sais quoi faire évoluer dessus.</li>
</ul>
</li>
<li><ul>
<li>La poussière (<strong>Faire la poussière tous les 3 mois…</strong>)</li>
</ul>
</li>
<li><ul>
<li>La température <strong>(Radiateur en plein été…)</strong></li>
</ul>
</li>
<li><ul>
<li>L’entretien <strong>(Changer les pièces, pas de redondance)</strong></li>
</ul>
</li>
<li><ul>
<li>La place occupée par la tour.</li>
</ul>
</li>
</ul>
<p>Shadow :</p>
<ul>
<li><ul>
<li>On fait évoluer mes composants automatiquement sans surplus.</li>
</ul>
</li>
<li><ul>
<li>Le côté mobile.</li>
</ul>
</li>
<li><ul>
<li>La simplicité d’utilisation.</li>
</ul>
</li>
<li><ul>
<li>Des lags (ça m’arrivent de temps en temps de lag sans raison apparente :/)</li>
</ul>
</li>
<li><ul>
<li>L’espace Disque faible ! (<strong>extension à 3€/mois 1 To obligatoire</strong>)</li>
</ul>
</li>
<li><ul>
<li>La connexion fibré <strong>O B L I G A T O I R E.</strong></li>
</ul>
</li>
<li><p>-<strong> Sans Internet, Pas de PC</strong> ( Pas de PC, ben … Pas de PC)</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599268578/KzNxVD-Bm.gif" alt /></p>
<h2 id="v-conclusion">V. Conclusion</h2>
<p>Du coup pour conclure, aujourd’hui <strong>ça fait 1 an et demi que je suis sur Shadow</strong> et il est hors de question que je reswitch sur mon ancien pc. J’ai des besoins de mobilité de plus en plus fréquent (<em>#HearthStoneEnPauseDej</em>) et le fait de ne pas m’occuper de l’entretien me fait une charge mentale en moins.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599270869/NfWQx4PAO.gif" alt /></p>
<p>Attention, Shadow n’est pas pour tout le monde ! Si vous êtes un joueur pro ou un hardcore gamer, je ne vous le conseille pas. Je peux même vous dire que pour avoir organiser la GA2018/OES2018/GA2019, je n’ai vu aucune personne jouer un tournoi avec un Shadow. Je vous conseille de tester un mois et de voir si ça correspond à votre utilisation :).</p>
<p>Voilà, hésitez pas à me faire un retour sur l’article et me suivre sur <a target="_blank" href="https://twitter.com/qlaffont">Twitter</a> ^^.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599273907/uNTIm3n-o.gif" alt /></p>
<p>PS: Voici mon code partenaire Shadow ;) : <a target="_blank" href="https://shop.shadow.tech/invite/QUEULU3J"><em>*</em>QUEULU3J</a> (10 € cadeau le premier mois)<em>*</em></p>
]]></content:encoded></item><item><title><![CDATA[How I hack a Smiirl Custom Counter with a physical button !]]></title><description><![CDATA[As a developer at Smiirl, I have always want to make a Custom Counter who increase his number when I click on a physical button. So, for my own purpose I have started a project to connect a Custom Counter with a physical button.
Example : Number of m...]]></description><link>https://blog.qlaffont.com/how-i-hack-a-smiirl-custom-counter-with-a-physical-button-be34e4dabe5c</link><guid isPermaLink="true">https://blog.qlaffont.com/how-i-hack-a-smiirl-custom-counter-with-a-physical-button-be34e4dabe5c</guid><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Tue, 04 Sep 2018 09:00:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599949188/aiVGWDNmo.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>As a developer at Smiirl, I have always want to make a Custom Counter who increase his number when I click on a physical button. So, for my own purpose I have started a project to connect a Custom Counter with a physical button.</p>
<p>Example : Number of my bad jokes in the last month ! (Yes, I do so much time …) But how to do this ?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599932468/C9jx7SL9F.gif" alt /></p>
<p><em>PS: You can contact me if you want my help or my services ;) <a target="_blank" href="https://qlaffont.com">https://qlaffont.com</a></em></p>
<h3 id="what-i-need">What I need ?</h3>
<p>Actually, I have make this experimentation with :</p>
<ul>
<li><p>A <a target="_blank" href="https://smiirl.com">Smiirl</a> Custom Counter of course ! (5 Digits : <a target="_blank" href="https://www.smiirl.com/en/counter/custom#5D">Buy Here</a> / 7 Digits : <a target="_blank" href="https://www.smiirl.com/en/counter/custom#7D">Buy Here</a>)</p>
</li>
<li><p>The Internet Button by <a target="_blank" href="https://particle.io">Particle.io</a> (<a target="_blank" href="https://store.particle.io/products/internet-button">Buy Here</a>)</p>
</li>
<li><p>A server to run a Node.JS Application and a MongoDB Server</p>
</li>
<li><p>Wifi for sure !</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599934811/aMQY9v_Jr.gif" alt /></p>
<h3 id="i-server-application-with-nodejs">I. Server Application with Node.JS</h3>
<p>We need to setup an endpoint who will expose 3 things :</p>
<ul>
<li><p><em>/counter/{counterID}</em> : Return JSON with number to display</p>
</li>
<li><p><em>/counter/increase/{counterID}</em> : Increment our Number</p>
</li>
<li><p><em>/counter/set/{counterID}/{number}</em> : Set number to display on our counter</p>
</li>
</ul>
<p>{param} -&gt; Express Route Param</p>
<p>And the code of course :</p>
<p><strong>package.json &amp;&amp; do <code>npm install</code></strong></p>
<pre><code>{

 <span class="hljs-attr">"name"</span>: <span class="hljs-string">"smiirl-button-test"</span>,

 <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,

 <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Smiirl Boutton Test"</span>,

 <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,

 <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Quentin Laffont"</span>,

 <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,

 <span class="hljs-attr">"dependencies"</span>: {

  <span class="hljs-attr">"body-parser"</span>: <span class="hljs-string">"^1.18.3"</span>,

  <span class="hljs-attr">"express"</span>: <span class="hljs-string">"^4.16.3"</span>,

  <span class="hljs-attr">"mongoose"</span>: <span class="hljs-string">"^5.2.1"</span>

 }

}
</code></pre><p><strong>index.js (Node.JS File to run)</strong></p>
<pre><code><span class="hljs-meta">"use strict"</span>;

<span class="hljs-comment">//Import Dependencies</span>
<span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>);
<span class="hljs-keyword">const</span> bodyParser = <span class="hljs-built_in">require</span>(<span class="hljs-string">"body-parser"</span>);
<span class="hljs-keyword">const</span> mongoose = <span class="hljs-built_in">require</span>(<span class="hljs-string">"mongoose"</span>);

<span class="hljs-comment">//MODEL</span>
<span class="hljs-keyword">const</span> Schema = mongoose.Schema;

<span class="hljs-keyword">const</span> CounterSchema = Schema({
    <span class="hljs-attr">id_Counter</span>: {
        <span class="hljs-attr">type</span>: <span class="hljs-built_in">String</span>,
        <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
    },
    <span class="hljs-attr">nb</span>: {
        <span class="hljs-attr">type</span>: <span class="hljs-built_in">Number</span>,
        <span class="hljs-attr">default</span>: <span class="hljs-number">1</span>
    }
});

<span class="hljs-keyword">const</span> Counter = mongoose.model(<span class="hljs-string">"Counter"</span>, CounterSchema);

<span class="hljs-comment">//MONGO</span>
mongoose.connect(
    <span class="hljs-string">"mongodb://127.0.0.1:27017/smiirl-test-button"</span>,
    { <span class="hljs-attr">useNewUrlParser</span>: <span class="hljs-literal">true</span> }
);

<span class="hljs-keyword">const</span> app = express();

<span class="hljs-comment">//Accept JSON &amp; Url Encoded</span>
app.use(
    bodyParser.urlencoded({
        <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span>
    })
);

app.use(
    bodyParser.json({
        <span class="hljs-attr">limit</span>: <span class="hljs-string">"50mb"</span>
    })
);

<span class="hljs-comment">//EXPRESS : Express Configuration</span>
app.all(<span class="hljs-string">"*"</span>, <span class="hljs-function">(<span class="hljs-params">req, res, next</span>) =&gt;</span> {
    res.header(
        <span class="hljs-string">"Access-Control-Allow-Methods"</span>,
        <span class="hljs-string">"GET, PUT, DELETE, POST, OPTIONS"</span>
    );
    res.header(
        <span class="hljs-string">"Access-Control-Allow-Headers"</span>,
        <span class="hljs-string">"Origin, X-Requested-With, Content-Type, Accept"</span>
    );
    res.header(<span class="hljs-string">"Access-Control-Allow-Origin"</span>, <span class="hljs-string">"*"</span>);
    next();
});

<span class="hljs-comment">// //GET</span>
app.get(<span class="hljs-string">"/counter/:id"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    Counter.findOne({ <span class="hljs-attr">id_Counter</span>: req.params.id }, <span class="hljs-function">(<span class="hljs-params">err, result</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (result) {
            res.json({
                <span class="hljs-attr">number</span>: <span class="hljs-built_in">parseInt</span>(result.nb)
            });
        } <span class="hljs-keyword">else</span> {
            res.json({
                <span class="hljs-attr">number</span>: <span class="hljs-number">0</span>
            });
        }
    });
});

<span class="hljs-comment">//SET</span>
app.get(<span class="hljs-string">"/counter/set/:id/:number"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    Counter.findOne({ <span class="hljs-attr">id_Counter</span>: req.params.id }, <span class="hljs-function">(<span class="hljs-params">err, result</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (result) {
            result.nb = <span class="hljs-built_in">parseInt</span>(req.params.number);

result.save(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
                res.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"OK"</span> });
            });
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">let</span> newCounter = <span class="hljs-keyword">new</span> Counter({
                <span class="hljs-attr">id_Counter</span>: req.params.id,
                <span class="hljs-attr">nb</span>: <span class="hljs-built_in">parseInt</span>(req.params.number)
            });

newCounter.save(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
                res.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"OK"</span> });
            });
        }
    });
});

<span class="hljs-comment">//INCREASE</span>
app.get(<span class="hljs-string">"/counter/increase/:id"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
    Counter.findOne({ <span class="hljs-attr">id_Counter</span>: req.params.id }, <span class="hljs-function">(<span class="hljs-params">err, result</span>) =&gt;</span> {
        <span class="hljs-keyword">if</span> (result) {
            result.nb = <span class="hljs-built_in">parseInt</span>(result.nb) + <span class="hljs-number">1</span>;

result.save(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
                res.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"OK"</span> });
            });
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">let</span> newCounter = <span class="hljs-keyword">new</span> Counter({
                <span class="hljs-attr">id_Counter</span>: req.params.id,
                <span class="hljs-attr">nb</span>: <span class="hljs-built_in">parseInt</span>(<span class="hljs-number">1</span>)
            });

newCounter.save(<span class="hljs-function"><span class="hljs-params">err</span> =&gt;</span> {
                res.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">"OK"</span> });
            });
        }
    });
});

<span class="hljs-comment">//EXPRESS : Start Server</span>
app.disable(<span class="hljs-string">"x-powered-by"</span>);
app.listen(<span class="hljs-built_in">parseInt</span>(process.env.PORT) || <span class="hljs-number">8000</span>);
</code></pre><p>After that, you can do a <strong>node index.js</strong>. This command will expose your app to this port : 8000. (You can change it with an environment variable named PORT).</p>
<h3 id="ii-configure-the-button">II. Configure the Button</h3>
<p>To do that, you need to go to <a target="_blank" href="https://build.particle.io">build.particle.io</a> (Here, I will suppose you have already link your button to a wifi network).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599937122/NhOsxylhS.png" alt /></p>
<p>After that you need to add 2 libraries :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599938889/zpbWC--eL.png" alt="Librairies needed !" /><em>Librairies needed !</em></p>
<p>After that you can add on your code this :</p>
<pre><code><span class="hljs-comment">// This #include statement was automatically added by the Particle IDE.</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;InternetButton.h&gt;</span></span>

<span class="hljs-comment">// This #include statement was automatically added by the Particle IDE.</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;HttpClient.h&gt;</span></span>

InternetButton b = InternetButton();

HttpClient http;

<span class="hljs-comment">// Headers currently need to be set at init, useful for API keys etc.</span>
<span class="hljs-keyword">http_header_t</span> headers[] = {
    { <span class="hljs-string">"Accept"</span> , <span class="hljs-string">"*/*"</span>},
    { <span class="hljs-literal">NULL</span>, <span class="hljs-literal">NULL</span> } <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> Always terminate headers will NULL</span>
};

<span class="hljs-keyword">http_request_t</span> request;
<span class="hljs-keyword">http_response_t</span> response;

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rdm</span><span class="hljs-params">(<span class="hljs-keyword">int</span> maxVal)</span></span>;

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">rdm</span><span class="hljs-params">(<span class="hljs-keyword">int</span> maxRand)</span>
</span>{
    <span class="hljs-keyword">return</span> rand() % maxRand + <span class="hljs-number">1</span>;
}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// INIT INTERNET BUTTON</span>
    b.begin();

    <span class="hljs-comment">//YOU NEED TO CHANGE THIS VALUE TO YOUR SERVER IP</span>
    request.hostname = <span class="hljs-string">"myserver.com"</span>;

    request.port = <span class="hljs-number">8000</span>;


}

<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span></span>{
    <span class="hljs-comment">//WHITE COLOR &amp;&amp; DECREASE BRIGHTNESS</span>
    b.allLedsOn(<span class="hljs-number">255</span>,<span class="hljs-number">255</span>,<span class="hljs-number">255</span>);
    b.setBrightness(<span class="hljs-number">50</span>);

    <span class="hljs-comment">//INCREASE</span>
    <span class="hljs-keyword">if</span>(b.buttonOn(<span class="hljs-number">1</span>) || b.buttonOn(<span class="hljs-number">2</span>) || b.buttonOn(<span class="hljs-number">3</span>) || b.buttonOn(<span class="hljs-number">4</span>)){
        <span class="hljs-keyword">int</span> color_r = rdm(<span class="hljs-number">255</span>);
        <span class="hljs-keyword">int</span> color_g = rdm(<span class="hljs-number">255</span>);
        <span class="hljs-keyword">int</span> color_b = rdm(<span class="hljs-number">255</span>);

        b.allLedsOff();
        b.ledOn(<span class="hljs-number">11</span>,color_r, color_g, color_b);
        b.ledOn(<span class="hljs-number">1</span>,color_r, color_g, color_b);
        delay(<span class="hljs-number">1000</span>);


        request.path = <span class="hljs-string">"/counter/increase/mycustomcounter"</span>;
        http.get(request, response, headers);

        b.allLedsOff();
    }
}
</code></pre><p>Now you can save your code and click on flash (This step will put this code on your button).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599940203/35Y7WYl04.png" alt="Flash Button on build.particle.io" /><em>Flash Button on build.particle.io</em></p>
<p>Now we just need to setup our counter !</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599941801/Zj63PnUY6.jpeg" alt="Our button after flash step !" /><em>Our button after flash step !</em></p>
<h3 id="3-setup-counter">3. Setup counter</h3>
<p>(Here, I will suppose you have already link your counter to a wifi network).</p>
<p>First you need to go to <a target="_blank" href="https://my.smiirl.com">**https://my.smiirl.com</a>. **Enter your credentials and go to your counter page. After that you can enter, your display url from your node.js application. Exemple : <a target="_blank" href="http://myserver.com/counter/mycustomcounter">http://myserver.com/counter/mycustomcounter</a></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599943550/k1Z0YLTCG.gif" alt /></p>
<h2 id="now-its-your-time">Now it’s your time !</h2>
<p>You can click on the button, and your button is increase ! 😍</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599946448/OBNousEiY.gif" alt="Smiirl Custom Counter Increase ❤️ when I press button" /><em>Smiirl Custom Counter Increase ❤️ when I press button</em></p>
<p>PS: If you want to reset your number, you can do a <a target="_blank" href="http://myserver.com/counter/set/mycustomcounter/1">http://myserver.com/counter/set/mycustomcounter/1</a> (You can’t do 0, because Smiirl Counter doesn’t support it.(<em>But you can do 1000000 for 5D or 100000000 for 7D, this will display zero in each flap !</em>))</p>
]]></content:encoded></item><item><title><![CDATA[FiveM ou comment des “développeurs” tues un jeu !]]></title><description><![CDATA[Bonjour à tous,
Il y a quelques temps le nom de FiveM (GTA 5) vous parlez peut être encore. Beaucoup de Youtubeurs comme Aiekillu, Sankah, ou Wankil Studio souhaitait jouer sur des serveurs qu’on appelle RP (Rôle Play) tournant sur le moteur de FiveM...]]></description><link>https://blog.qlaffont.com/fivem-ou-comment-des-developpeurs-tues-un-jeu</link><guid isPermaLink="true">https://blog.qlaffont.com/fivem-ou-comment-des-developpeurs-tues-un-jeu</guid><dc:creator><![CDATA[Quentin LAFFONT]]></dc:creator><pubDate>Fri, 03 Nov 2017 12:05:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599342858/aa0PekTg8.gif" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Bonjour à tous,
Il y a quelques temps le nom de FiveM (GTA 5) vous parlez peut être encore. Beaucoup de Youtubeurs comme Aiekillu, Sankah, ou Wankil Studio souhaitait jouer sur des serveurs qu’on appelle RP (Rôle Play) tournant sur le moteur de FiveM. Sauf qu’aujourd’hui on en entend presque plus parler, Pourquoi ?</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599334418/W6y8zUATT.gif" alt /></p>
<h2 id="un-contexte-complique">Un contexte compliqué</h2>
<p>Aujourd’hui sur la plateforme officielle de FiveM il est impossible de parler en français. Ce qui est met déjà dans une ambiance certainement mauvaise pour commencer. <em>Si vous avez le malheur de parler en français, vous serez automatiquement banni pendant quelques jours ou définitivement.</em></p>
<p>De ce fait de nombreuses communautés annexes comme <a target="_blank" href="https://fivem-france.net/">FiveM-France</a> (<em>Oui je fais ma pub ;)</em>) se sont créées dans le but de donner la parole aux développeurs francophone souhaitant développer FiveM et mettre en place leurs serveurs.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599337000/-ud4NxBvt.gif" alt /></p>
<h2 id="une-mentalite-laissant-a-desirer-cote-francophone">Une mentalité laissant à désirer côté francophone …</h2>
<p>FiveM est un moteur GTA 5 permettant de mettre en place et créer un serveur multijoueur. De ce fait, le nombre de personne non majeur et très souvent jeune <em>(~14 ans)</em> est tout à fait normal. Je contextualise dès à présent pour que vous puissiez comprendre la suite.</p>
<p>Avez le boom de l’open source, de nombreux développeurs ont commencé à partager leurs scripts (Lua/C) pour pouvoir développer l’ensemble des fonctionnalités que permettait FiveM. Des projets comme N3MTV étaient des projets très attendu par les non-développeurs car il permettait de mettre en place un serveur RP en 5min sans connaissances requises en développement.</p>
<p>Cependant suite à du harcèlement et d’injures envers l’auteur du projet, le projet a été retiré. Certains diront “<em>C’est internet, des personnes stupides, il y en a partout !</em>” et d’autres “<em>C’est la faute du développeur car son projet était rempli de bugs !</em>”. Dans les deux cas, ces remarques ont fait retirer un gros projet communautaire qui a inspiré de nombreux développeurs auparavant !</p>
<p>Enfin la plupart des personnes qui souhaitent monter un serveur, ne sont pas des développeurs et ne parlent pas anglais. Vous rajoutez donc un défi technique et linguistique à cette communauté assez jeune.</p>
<h2 id="mais-aussi-hors-francophone">Mais aussi hors francophone …</h2>
<p>Lors de la création de serveur FiveM, 90 % des développeurs utilisaient un script nommé “<a target="_blank" href="https://forum.fivem.net/t/release-essentialmode-base/3665">*EssentialMode</a><em>”. Ce script développé par <em>*Kanersps </em></em>permettait d’avoir une base de développement plutôt pratique.</p>
<p>Auparavant, on pouvait utiliser librement sa ressource sans aucun problème licence. Bref le script <strong>essentiel </strong>à tout serveur.</p>
<p>Cependant un jour, <strong>Kanersps </strong>a décidé de poursuivre toutes les personnes qui partagées sont script pour qu’il n’y est plus que lui qui puisse partager son script “<em>EssentialMode</em>”. On ne connaît pas les raisons de ce retrait, mais cela a tué de nombreux projets qui souhaitait partager leurs serveurs avec tous les développeurs (et contenant toutes les ressources). <strong>Dommage !</strong></p>
<h2 id="un-moteur-gta5-bancal">Un moteur GTA5 bancal :/</h2>
<p>FiveM est aujourd’hui le moteur principal des serveurs GTA5. Le moteur historique nommé <em>CitMP </em>est le plus aboutit cependant il souffre de nombreux bugs, latences, crashs. De ce fait l’équipe a retravaillé son moteur afin de fournir un moteur plus stable nommé <em>FX Server </em>et a rendu son utilisation obligatoire récemment.</p>
<p>Le problème de ce tout nouveau moteur, c’est que la plupart des ressources ne fonctionnait plus avec le nouveau moteur. De ce fait lors du grand changement, plus de la moitié des serveurs ont fermés car les ressources ne sont plus compatibles et des développeurs comme moi qui ont passés plus de 2 mois à développer un serveur se retrouve aujourd’hui à tout refaire.</p>
<p>Donc des serveurs ont fermés et des développeurs ont abandonnés leurs projets sur FiveM.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1619599340079/T-r995RO0.gif" alt /></p>
<h2 id="des-projets-persistent-mais-plus-la-meme-attente">Des projets persistent mais plus la même attente !</h2>
<p>Personnellement j’ai raccroché avec FiveM. Trop de changement, peu de considération et une mentalité négative. Mon dernier projet fut la création d’un pseudo Git pour FiveM nommé <a target="_blank" href="https://github.com/qlaffont/fvm-installer">fvm-installer</a>. Utilisé par certains développeurs, c’est un outil que FiveM avait besoin d’utiliser. Mais ça sera mon dernier projet.</p>
<p>Enfin pour d’autres développeurs, des projets persistent comme <a target="_blank" href="https://github.com/FivemTools">FT</a>, <a target="_blank" href="https://github.com/ESX-Org">ESX</a>, etc. Mais il faut se rendre à l’évidence que de plus en plus de joueurs abandonnent la plateforme et les serveurs FiveM ferment chacun à leurs tours. Une plateforme ambitieuse mais qui aujourd’hui disparaît à petit feu…</p>
]]></content:encoded></item></channel></rss>