I’ve been using Fedora Kinoite on my main laptop for about a year now. Kinoite is a spin of Fedora Silverblue. This is the first and only immutable operating system I’ve used to date.

To explain why immutability is so important in my view, let’s take a look at some examples from other systems.

Mutability by bad decisions

Let’s start with Windows. In my opinion the worst wide-spread operating system of them all. It’s basically a collection of bad decisions upon bad decisions dating back to the 90s dictated by the requirements of back-compatibility on legacy features that enterprises wish to drag along with them forever.

An example of one of those outdated decisions is that Windows was always designed to be a mutable OS.

What does that mean? you can go into your Windows system folder and delete anything you want. Windows may not stop you. In fact you need to do this every now and then to clean your system folder from 10s of GBs of Windows logs and/or temporary install files that their updater (another broken legacy system) just ignores.

Another example of one of those bad decisions is the registry. You can just mess with it to your hearts content, delete whatever you want, and it’ll mess up the whole system!

Mutability by Design

Linux as an operating system was designed from the ground up to be mutable but it’s done right in my opinion. The user has full complete freedom to do anything they want in the system allowing advanced users to do whatever they can think of.

You want to experiment on a different OS scheduler? a different way to install device drivers? have at it at your own peril.

The drawback of course is that this absolute mutability is hostile to its users. I can’t count how many times I killed my installed distro by attempting to update an NVIDIA display driver.

BSD is an interesting variation on the mutability by design philosophy. It actually achieves better stability and is more friendly to its users simply by being a larger OS that comes with many more batteries included and a slower more sensible evolution process.

The Walled Garden

On the other extreme all modern mainstream phones are powered by immutable systems that require a jailbreak to escape from. An immutable system cannot be changed in anyway by its user, only by the system or software makers themselves issuing updates to it.

An operating system with a walled garden design proved to be vital for stability and long term dependable robust operation. This is part of the reason why we depend on our mobile devices so much. We know they are very unlikely to misbehave or break internally like a Windows laptop often does.

This is also reflected in all Apple OSX devices like Macbooks. They are super dependable and clean.

However these systems come with a large drawback: the user does not really control the system, and the system controls what the user can and cannot do.

Immutable Operating Systems

Between the fragility of mutable systems and the authoritarianism of walled gardens lies a sensible middle.

A system that offers the same immutability you find in OSX/iOS/Android so it’s dependable, robust, and maybe impossible to break by the end user. Yet, it maintains the ability to let the user change most things in their system if they really want to, while also providing a method to reverse these changes non-destructively!

So what are the elements that Fedora Silverblue provides that let’s it achieve all that?

  • Immutable system: neither you nor any software can change any system specific files/folders directly as they are protected from writing
  • Atomic image updates: the system gets updated as an image rather than the usual package manager approach. This eliminates any issues that may rise from wrong dependencies as well as provide a straight forward way to jump back to a previous image of the system or a future one still in beta to test things out non-destructively
  • The system image operates as a version control system via rpm-ostree which acts as a package manager of sorts. It allows you to push changes on top of current system image, this way you can apply any system changes you need (such as install NVIDIA drivers or any tools you may need in the OS) and these changes “shadow” any existing data. At anytime you can revert or cherry-pick these changes or altogether discard all commits and restore original system image. When system image is updated, your committed changes get pushed on top of the new image.
  • You may install any software you want from flathub (flatpak is supported by default) and you can also install snapd by committing the change on top of the system image using rpm-ostree to enable snapcraft. And finally my favorite method: AppImage
  • For developers, one of my favorite features in Kinoite/Silverblue is toolbox. You can create as many toolboxes as you want each acts as a self-contained mutable install of Fedora/Centos where you can use dnf to your hearts content and can even install and run GUI applications seemlessly like VS Code. I naturally setup a toolbox per project and can go crazy in any of these toolboxes without worrying about my actual system getting poisoned in the process

In my opinion this is the perfect developer OS: as solid as OSX or iOS without sacrificing flexibility to change the system, best dev containers system I’ve used so far (much better than manually creating dev containers), and it still works perfectly for gaming by installing steam using rpm-ostree and steam runs in its own sandbox so that’s all you need.

However it’s anything but perfect. Here are some of the challenges I’ve run into so far:

  • sometimes when an OS image update is available, some changes in it conflict with packages you installed using rpm-ostree and it fails to update as a result. The error and info you get are vague but the easiest way is to simply uninstall whichever packages are conflicting with the update
  • I could not for the life of me get an NVIDIA RTX GPU to work via Thunderbolt, I followed every single guide I found and eventually gave up and installed Windows on an external drive for playing newer games
  • Running GUI applications from within toolbox works, however there is a really annoying bug that occurs when you quickly move between app UI elements with the mouse, the whole system pauses for a second (mouse included) and resumes after. This seems to be much worst in electron apps sigh
  • Updating your software works from Fedora’s software manager (Discover) but sometimes some updates just fail for no apparent reason, retrying gets those updates to succeed most of the time
  • Can’t change the wallpaper in the Login Screen as it’s in a system image path (might be a Kinoite specific issue?)
  • Installing Lutris and other game launchers (ubi, epic, etc) is problematic as wine is not part of the default system image and must be applied using rpm-ostree

And that’s that! since Silverblue there has been other immutable Linux distros, this awesome list keeps track of everything immutable.

Discuss...

Published

19 September 2022

Category

linux

Tags

GoVolDot is out! Released it a few days ago after not doing anything with it for a few weeks.

I have this weird release hesitation sometimes, I think I’m worried to let a project go, let it fly out of the window into the wild harsh world.

GoVolDot was a very interesting experience despite its apparent simplicity. The goal I had for it besides that I always wanted to remake this game and actually play it, is to run a project through an entire production cycle in preparation for bigger things in the future.

What Went Wrong 🤧

Estimation Fail

My estimate for this project from beginnings to release was about 2 weeks. My actual total dev time was around 4 weeks of full time work.

While this is accurate for gamedev (2x estimated time). It still feels a little too much for a minimalist 2D shooter!

Some excuses:

  • I was honestly surprised how many enemies with unique behaviors there were, around 25 unique behavior enemies. Took me full days of work for the most complex enemies.
  • Levels had very specific sequences for the game and the boss battles and I tried to reproduce them accurately. There are a LOT of events, so it took most of a day to play that level in the original game then clone all the events into my spread-editor.
  • I was also using Godot for a full-scale game for the first time, so had a lot to learn there and attempted many different ways to do things

Messy assets

The project started quite organically so I threw together a large texture in affinity designer and used their sliced exports to spit out spritesheets per-object. However as the game scaled up to include more and more stuff, that simple process turned into a bit of a time-hole and produced many small sprite files sometimes a few pixels wide. I should have developed a better pipeline for that stuff.

govoldot_spritesheet

Unfruitful pursuits

I spent a lot of time playing around with different ideas and directions. Attempted to re-mix Volguard’s music theme twice then gave up due to not really being any good at music lol.

Here I had this crazy idea to switch to 3D for intro, syncretize, and reinforcement sequences:

govoldot_3d

And here is a taste of that awful remix I mentioned, you’ve been warned:


What Went Right! 🕺

Nailed the feels!

I got the feeling just right! the game is not 100% one-to-one to the original Volguard but it’s close enough - I’d say around 75% accurate.

There are a few bugs here and there but nothing major I’m aware of. All in all, it’s a playable game and one that I can replay myself and still be challenged and have some fun 🙂

Also I really like the font:

govoldot_font

Spread-editor?

For GoVolDot I needed a way to describe precise sequences of events across a long timeline. I could not at the time come up with a way to build that into Godot quickly, but for whatever reason I thought a Google spreadsheet might be ideal for this and it’s immediately available!

spreadeditor

Horizontal-axis is time at 0.25 secs steps. Vertical represents Y coordinate to spawn the enemy.

I could embed metadata about each spawned enemy behavior/look in its cell using this smooth-brain format: bandit:edir=-45:vel=[-80 0]

This then gets dumped into a CSV file… yes.. into a specific path.

Then from there a constantly-running python script watching for any mission file changes, detects the change, parses the CSV and throws it into a GDScript file.

Then the game parses the actual CSV data at runtime and turns them into game events.

As hilariously complex as this spread-editor was, it actually worked and once all the moving parts were in-place I forgot it was there. So I consider it a win 💃

Scope Discipline

Did I already mention I wanted to turn parts of GoVolDot into 3D sequences? yeah, I’m glad I didn’t dive into that hole.

Also, multiplayer support.

Coroutines

In GoVolDot I used coroutines for enemies behavior (written a post about that), and it simplified things a lot. I re-used that approach as many times as I could! it doesn’t work with all types of behaviors though. I found that if behavior needs to change based on external events it’s better to just implement that using a traditional state machine to avoid having to pass around variables for the coroutine to check against.

Conclusion

I am happy with what I got at the end. This project has been on my mind since the day I had a “real” computer as I used to call x86 PCs when I was 19 years old.

Consider this checkbox.. checked!

And I gotta say, I’m really impressed with the level of depth and attention to detail this old game has! it’s beyond anything else I’ve had on my PC-6001 at the time and to imagine that the original developers created this game using Z80 assembly? and somehow managed to cram all required data and all those behaviors into 64KB!

Discuss...

Published

22 May 2022

Category

gamedev

Tags

I’ve recently decided to dedicate myself to my passion in GameDev and attempt to make it self-employed. This was approximately 3 weeks ago.

I will try to write weekly or bi-weekly updates logging what I’ve done so far.

Week 1: Set my heart a flutter

I’ve been preping this for a while and one unfinished thing I had to get through is figuring out a suitable pipeline for me to develop Android apps for tooling that I want to manage my time and budget.

The last thing I’ve done in this domain is attempt to utilize Python for Android app making, which is possible but what I tried was horrendous in its own way. I’ve looked at both kivy and BeeWare on my Fedora Silverblue laptop.

Immediately ran into issues with kivy that made it a pain to get things running, so I moved on to BeeWare. Now, BeeWare works but there it’s anything but focused and compact. Lots of moving parts and things to set up and when you finally get it working, there isn’t much documentation and I didn’t feel like I got it after doing the tutorials they had available which by the way are incomplete and outdated.

I hate to say it but for the first time, Python failed me. So I decided to move on and focus on modern programming languages that are not derivatives or layers on top of Java like Kotlin is because I dislike Java and its ecosystem. I remember hearing about Flutter somewhere so I jumped there and it’s based on this current gen language called Dart. I liked Dart immediately as it reminded me of TypeScript.

When I started doing the tutorials for Flutter, it just clicked and I got what the high-level design was. You define UI layouts in a similar way to writing a JSON spec. Constructors within constructors, kinda like this:

Widget build(BuildContext context) {
  return MaterialApp(
    title: 'Hello World',
    home: Scaffold(
      appBar: AppBar(title: const Text('Hello World'),
      body: const Center(
        child: Text('Hello World!'),
        ..etc

It gets a little too LISP-like with the parentheses you need to manage, but nothing a good editor can’t figure out.

With this I was able to put together a simple alternative time app that allows setting up timers through Android’s native timers app:

CMG::Time

it’s pretty simple, here it is in github. But it demonstrated to me this is totally usable and has a low time cost for developing tools like that.

Week 1: Unfinished pixel business

Last year during the holidays, I started remaking my childhood game as a tribute to it. It’s an old forgotten game called VOLGUARD that ran on my first computer, an NEC PC-6001 Mk2 SR produced and localized in Iraq.

As a kid, that game inspired and awed me. It just seemed to be too much for my humble machine and I could never figure out how they managed to squeeze that much stuff into it.

VOLGUARD

It appears to me that the VOLGUARD version on PC-6001 was the originally developed one. MSX and PC-8801 both got ports with better graphics but they are missing some vital elements like the intro music.

So my goal is to re-make this game maintaining its exact feel and style using Godot. This first week I picked back up what I was doing last year, I’ve had some good progress going and thought perhaps this is going to be a few days only to finish.. estimated it at 6 days of work to completion..

Week 2: 90% == 50%

There is this funny thing in gamedev, that if you’re working on a project and you believe you’re at 90% progress and only a few things left to do… you still got 90% more to go.

I re-learned this lesson with VOLGUARD. A game I gave 6 days but it took nearly double that to get anywhere close to completion.

The biggest time sink is the enemies behavior. Unlike the majority of shovelware games out there that tend to implement enemies with a single braincell that basically says: “see player? run at and attack player”. VOLGUARD has actual relatively complex behaviors. A ton of them. Out of nearly 28 variants of enemies, there are roughly 20 of them with unique behaviors!

I developed a good pace in implementing these behaviors and tweaking them to approximate the original game. My last blog post was actually based on that.

There are 5 missions. Each introduce new enemies and more variations in behaviors. This game goes to the school of Souls.. you’ll die alot but it’s because you made a mistake and you can do better. Make no mistakes and you’ll breeze right through.

VOLGUARD also had this unique design decision where they allowed you to play any mission out of the 5 at anytime.

At the end of week 2 I had mission 1 fully implemented and tested with all behaviors and enemies, and mission 2 about two-thirds in.

So 6 days in.. progress = 1.5/5.0, not even 50%.

Week 3: The Light of Release

Last week’s work focused on the more complex enemies. I’ve had to revise or re-implement several enemies as later on in the game I started running into variations in behaviors I didn’t account for in my initial implementation.

In Week 3, I finished Mission 2 and was most of the way through Mission 3. In addition to implementation of all the complex enemies. Remaining enemies are easier as they are similar to already implemented enemies with variations here and there making them cost less to implement.

This is when I started seeing some light, that perhaps I could get it released the following week! (this week)

Discuss...

Published

11 April 2022

Category

gamedev

Tags

Say we’re working on a GTA-style game and we want to create a behavior sequence for a mission:

  • Car (with an AI driver) spawns at location A and waits for the player to get in.
  • Car drives to location B.
  • Player gets out.
  • Car waits for player to get back in with item within 5 minutes.
  • Mission success!

Manual State Management

The problem with straight-forward gameplay programming is it’s made of an Update() function that gets called ±60 times a second.

This means we must manage time between events manually.

It also means we must store necessary states and update them organically. If we have many overlapping inter-dependent states we may end up with unpredictable hard-to-debug gameplay issues.

Here’s some pseudo-code for the manual gameplay programming approach:

player_first_got_in = False
player_first_got_out = False
player_second_got_in = False
at_location_b = False
wait_time = 5 * 60
waiting_for = 0.0

def Update(delta):
  if not player_first_got_in:
    if car.passenger == player:
      player_first_got_in = True
  elif not player_second_got_in:
    if not at_location_b:
      distance = car.driver.drive_to("location_b"))
      if distance < 10.0 and car.driver.stopped():
        at_location_b = True
    elif not player_first_got_out:
      if car.passenger == None:
        player_first_got_out = True
    elif not player_second_got_in:
      waiting_for += delta
      if car.passenger == player:
        player_second_got_in = True
      elif waiting_for > wait_time:
        announce("mission_failed")
    else:
      if player.has("item"):
        announce("mission_success")
      else:
        announce("mission_failed")

This looks terrible. When the explanation of the task is 10 times easier to understand than the actual code that implements it, we are definitely not in sustainable territory.

And there is a bigger problem.. how do we deal with parallel gameplay complexities? say an event occurs that interrupts the supposedly straight forward mission that would interrupt our logic. Examples: the player gets out of the car BEFORE it gets to location B, the car is damaged too much and explodes, or the player destroys the package instead of picking it up.

There is no choice but to handle every single special scenario and to keep updating that everytime we have a new gameplay feature that could potentially complicate things.

Do not do this!

Once you catch yourself doing state management via endless if-else chains, switch it to a state machine.

State Machines

They aren’t magic, but they provide us with much needed cleanliness and simplicity in addition to some nifty abilities like being able to go to any state at any time!

enum State {
  BEGIN,
  DRIVE_TO_LOCATION_B,
  WAIT_FOR_PLAYER_ITEM,
  CONCLUDE
}
state = State.BEGIN

at_location_b = False
wait_time = 5 * 60
waiting_for = 0.0

def Update(delta):
  switch state:
    case State.BEGIN:
      if car.passenger == player:
        state = State.DRIVE_TO_LOCATION_B
    case State.DRIVE_TO_LOCATION_B:
      distance = car.driver.drive_to("location_b")
      if distance < 10.0 and car.driver.stopped():
        at_location_b = True
        state = State.WAIT_FOR_PLAYER_ITEM
    case State.WAIT_FOR_PLAYER_ITEM:
      waiting_for += delta
      if car.passenger == player:
        state = State.CONCLUDE
      elif waiting_for > wait_time:
        announce("mission_failed")
    case State.CONCLUDE:
      if car.alive() and at_location_b and car.passenger == player and player.has("item"):
        announce("mission_success")
      else:
        announce("mission_failed")

Notice that this state machine implementation is not shorter than the previous manual state management implementation, but it’s so much easier to understand and allows us to add a level of resilience through fallback states to handle exceptional interruptions.

We could actually do better than the state machine! A game like this has many missions that require a car to wait for a passenger or particular conditions related to car’s location or passenger inventory. It would be really annoying if we had to re-write that code in 100 different variations..

Job Systems

We could instead create a sort of a job driven mission description. In such a system, missions are defined as lists of jobs. Each job is a self-contained gameplay activity that can be customized with parameters and reused as many times as we want.

If we had such a system in place.. how would our mission description look like? Well for starters we no longer need to manually track everything in Update!

def Start():
  Jobs.executePackage([
    Job(type=WAIT_FOR_PLAYER),
    Job(type=DRIVE_TO, {location="location_b"}),
    Job(type=WAIT_FOR_PLAYER, {time_limit=5*60}),
    JobSuccess(type=IF_INVENTORY_HAS, "item"),
    JobFailure()
  ])

With this approach we could magically eliminate the Update function! I mean in reality the Job system is doing that internally, but hey.. makes life easier.

Of course designing and developing a job system like this for a large game is not a trivial task and this example is too simplified to reflect what a real world implementation might look like. However, a game with 100s of missions like that would definitely benefit from this.

The job driven mission system is perfect for this particular case. It’s a great example of the perfect tool for the job. It also opens up some interesting capabilities. We now have a mission system that’s data-driven! this means we can define missions visually, from within the game, allows us to implement mod support, etc.

This implementation is suitable for an ECS driven-engine and is multi-threading friendly as well.

Speaking of multi-threading..

Coroutines and States

I was first exposed to this approach with Unreal Engine 3. Its UnrealScript language has a built-in way to define a state machine per script. What’s special about those state machines is that they run in parallel with that script’s conventional Update() function. This appears unimpressive at first glance, but practically? you could write your code in a way that forgoes many of the annoyances of manual frame-to-frame state management.

So the last approach which happens to be my current favorite, utilizes coroutines (or any similar feature). In this context coroutines are essentially functions that run concurrently with your Update() and normal event handling callbacks until completion:

  • Each coroutine encapsulates a specific self-contained task.
  • Coroutines can be chained one after the other producing a sequence of events executing one after the other across time.
  • These coroutines continue until their completion or until the object containing them is freed.
  • This means we can actually have a while true: style infinite loop running a continuous sequence of events until the object is freed! think enemies that perform pattern attacks.

How would our mission sequence look like with this approach? Due to the variation in implementations between different engines and languages, I’m gonna base my example here on how Godot implements coroutines:

enum State {
  RUN,
  MISSION
}

state = State.RUN
# if the player takes longer than 10mins, mission fails
mission_time_limit = 10 * 60

def Update(delta):
  switch state:
    case State.RUN:
      if car.passenger == player:
        run_sequence()
        state = State.MISSION
    case State.MISSION:
      # Here we can handle any special interruptions we want to account for
      mission_time_limit -= delta
      if mission_time_limit <= 0:
        announce("mission_failed")      

def run_sequence():
  yield(Task.DriveTo(what=car, dest="location_b"), "task_complete")
  yield(Task.WaitForPassenger(what=car, who=player, time_limit=5*60), "task_complete")
  if car.passenger == None or not player.has("item"):
    announce("mission_failed")
  else:
    announce("mission_success")

Note that:

  • We don’t really need a state machine here but I included it to illustrate how it can work together with coroutines.
  • We assume the announce function kills the mission object interrupting any ongoing coroutines.

This might be the best general approach for state management. It produces clean compact readable code and costs almost no time/effort to implement the runtime for. If your language of choice has some form of coroutines, you’re good to go!

I’m interested to see how this coroutines approach would look like in Unity? also C++20? or anything else you use, tweet/comment me an implementation :)

Discuss...

Published

03 April 2022

Category

gamedev

Tags

I’ve always claimed that I have proof the universe is full of intelligent life (according to our definition of intelligence), and it’s simply that we ARE the perfect example of intelligent life in our universe as there is literally nothing about our existential circumstances that’s special or unique and most of our planet’s complex life evolved within the last 600 million years. Now look at all we know about the universe from its largest parts (galaxies?) to the smallest (quantum particles?), there is not one thing in our universe that’s one of itself. Everything has billions, trillions, and beyond, of itself; even at the scale of a single galaxy.

Our recent technological awakening is laughably small compared to the age of this solar system or life on this planet; or even how long homosapiens have been around. Electricty was discovered in the 1800 as well as radio waves and first combustion engine invention. Took about a hundred years or so to get to the first airplane. Then about 70 to send humans to the moon, and 15 years after, the internet. We are on a crazy accelerating ride towards a completely unkown reality and at any given point we have no concept or notion of what possibilities lie beyond the next major civilization disrupter until after the thing actually happens!

Imagine attempting to explain to a normal person in 1900 that someone will create a machine that allow men to fly in the sky 3 years from today, you’d be laughed at or called insane. We are so shit at predicting how our reality is going to change next, it’s a genuine miracle we made it this far.

Our tiny technological awakening means that 100% (or pretty damn close) of all intelligent life in our galaxy has been around significantly longer. Imagine another intelligence that had a tiny technological headstart on us, could we ever be capable of comprehending something that will be invented after 5000 years of exponential technological advancement from where we are now? that’s impossible.

Some great scifi writers were able to somehow navigate beyond the limits of our imagination and explore what may lie on the highway to wherever all of this is going, one of the best examples of this I’ve ever read is a short story by Isaac Asimov called “The Last Question”. In it Asimov imagines how the very nature of humans will change across vast amounts of technological time co-evolving and eventually superseded by a thing that suspiciously resembles where Google is going for.

But what about the Fermi paradox?

That thing is not a law of physics or even a theory, it’s simply a philosophical question making many assumptions that could be completely wrong. In other words, it’s nothing more than a question without a clear answer and doesn’t really prove or disprove anything.

The biggest problem I have with the Fermi paradox is its assumption that what we know at this very moment about our reality, physics, and the universe.. are most of everything there is to know. That has already been proven false in so many ways. For example there is an assumption that radio waves are how all other intelligent civilizations communicate simply because that’s how we do it? Well, I’m connected to the internet via a focused light-beam travelling through an optical fiber cable.. no radio waves involved and it’s impossible to detect from outside the system. How about quantum particles? gravitational waves? whatever the heck is that weird energy pushing the universe to expand? or many other things we haven’t discovered or know anything about yet.. any of them could be used for communication somehow.

So I say.. the Fermi paradox is no longer valid.

UAPs?

UAPs (Unidentified Aerial Phenomenon) are what the US government calls what previously was known as UFOs. They changed the name because UFO became synonymous with crazy people trying so hard to make space ships and little green aliens a reality because they believe it almost on a religious level without following a valid process to prove or disprove. And also because a lot of the alleged sightings are not even an object but a weather phenomenon of some sort or a reflection of something.

Ouch my conspiracy theory

One of the most wide spread and oldest conspiracy theories is that the american government are hiding that they are in posession of alien technology and/or they are in contact with some aliens on some level. You must have heard of area 51, roswell, alien abductions, or the endless stream of holywood movies portraying the government covering up alien involvement in things.

I was watching a series of short videos that investigate and interview military personale involved in the recent government released UAP videos and I realized there is indeed a conspiracy, but it isn’t that someone has some secret information.. rather that the US government wants to pretend none of this is happening because executive military/government people who like ambition ladders consider any “aliens” like talk to be a threat to their ladder-climbing so they look the other way when it comes up and bury it in any way they can when no one is looking. There is a lot of stigma about it and it’s not just in the military but in society at large. This stigma was behind how long it took for this phenomenon to be confirmed.

What happened?

A bunch of videos of these UAPs that were leaked previously were confirmed and re-released by the Pentagon, in essence proving that a portion of that UFO nonsense may actually have been real all along. What’s even more is that some military people who were present and involved in these recorded incidents stepped forward and talked about their experience and all they know and they all repeated the same thing: the government doesn’t care about this even when it’s evaluated as a potential risk to equipment or life.

The same reluctance and stigma are echoed by the majority of the science community. They have been constantly pushing anything related to possible intelligent life or looking for signs of extraterrestrial technology under the rug. This is going as far as blocking funding to research or projects that openly mention anything that can be connected directly or indirectly to “aliens”.

Here’s a podcast by Lex interviewing David Fravor who literally attempted to engage one of these things in an F/A-18 IRL:

So now we believe in UFOs/UAPs?

Only that these things are impossible to identify and appear to be technological in nature and purposeful. If you saw those confirmed videos and heard what the eye witnesses involved said, these things whatever they are do not come from space but actually seem to hangout in and around our own oceans!

I was considering what this means. What they could be and how to find a way to logically think about this whole thing. And I felt I want to share my perspective.

But this post is long enough already. I want to write a lot about this as the non-sensical nature of these things intrigue me lol.. so in the next part I’ll examine some fun theories as to what these things could be in my very-madeup opinion.

Discuss...

Published

04 May 2021

Category

futurism

Tags