• 0 Posts
  • 24 Comments
Joined 1 year ago
cake
Cake day: June 21st, 2023

help-circle
  • Some thoughts on the comments on Discord:

    I can understand most of the arguments against Discord, and there are some problematic communies on it of course, but I’m not sure I understand how using Discord over an alternative puts someone more at risk of exposure to those communities. People are free to join and leave servers at will, so is the issue that these servers built around FOSS projects have toxic communities? If so, how would being on any other platform solve this?

    Speaking from experience, just about all the servers I’m in have some kind of “no politics” rule, a very inclusive “be nice” rule, and a pronoun selector. Maybe it’s just the servers I join, but hate speech gets people banned pretty quickly.

    Anyway, there are plenty of arguments that can be made about discoverability, lack of control, privacy, and the non-FOSS nature of the platform to justify its presence on that list.


  • Not quite a “gaming PC” since, at least if they’re using something like Nvidia’s Hopper GPUs (or relying on another service that does), they’re not designed for gaming (and in the price range of $10k-$100kish), buuut if you ignore the finer details then fundamentally it’s basically like that. They’d send the image to their “very expensive gaming PC server” where the inferencing would be done.


  • I’ve seen this in a few places on desktop, and I have no clue why it’s even a feature. I’m not aware of anyone using it anywhere (although to be fair I haven’t thought to ask).

    As for why it’s enabled by default, probably for visibility. The easiest way to get people to use a feature is to make them use it and make them explicitly disable it (if even an option). For AI training, they could theoretically just capture typing data and messages regardless of if the feature is enabled/disabled anyway.



  • GPT, at least from my limited understanding, is a tool designed to continue the input. You feed it a sequence of tokens, it returns a tokens which it “believes” come next. While your impression is valid, it’s still a “completion engine”. ChatGPT and other products use GPT but have built a product around it. They are not simply frontends for GPT - they do a lot more processing than that.

    Also, not trying to understate your impression. It’s pretty impressive how good it is, despite the compute needed for it. I would caution against overestimating its responses though. It does not “reason”, “think”, etc. Its purpose is to continue a sequence of tokens following a (super complex) pattern it has been trained on (super basically). When it claims to reason something, it’s because the people it trained off of did reason it.



  • My favorite tests are the ones I don’t need to remember to write. I haven’t needed to write a test for what happens when a function receives null in a while thanks to TS/mypy/C#'s nullable reference types/Rust’s Option/etc. Similarly, I generally don’t need to write tests for functions receiving the wrong type of values (strings vs numbers, for example), and with Rust, I generally don’t even need to write tests for things like thread safety and sometimes even invalid states (since usually valid states can be represented by enum variants, and it’s often impossible to have an invalid state because of that).

    There is a point where it becomes too much, though. While I’d like it if the compiler ensured arbitrary preconditions like “x will always be between 2 and 4”, I can’t imagine what kinds of constraints that’d impose on actually writing the code in order to enforce that. Rust does have NonZero* types, but those are checked at runtime, not compile time.



  • Man there have been hot take after hot take in the programming communities over the past few days. Here, I’ll give my hot take since nobody asked:

    If I have to touch your code and I can’t tell what inputs it’s supposed to accept, what it should do with those inputs, and what outputs it should produce, I’m probably deleting your code and rewriting it from scratch. Same goes for if I can trivially produce inputs or states that break it. If your code is buggy, it’s getting fixed, even if that takes a rewrite.

    When working with others, write readable and maintainable code that someone with much less context than you can pick up and work on. It really doesn’t matter if you need to use TypeScript, mypy, tabs, doc comments, or whatever to do it.

    When doing your own project, it doesn’t matter. It’s your code, and if you can’t understand it when you come back to it then you’ll probably rewrite it into something better anyway.






  • Wait CSS supports nesting? Since when?

    Also, SASS’s mixins, variables (CSS has variables too but they work differently), and many of the other features are hard to beat, which is why I feel like it’s almost never a negative to get it up and running on any project I work on (unless the project is a tiny one-off, then it’s probably not worth it). One thing I like is that it’s close to CSS - increasing the transparency - while still providing abstractions that let you save time and increase consistency across the project.


  • Then again, some people might think the obfuscation in 20+ classes is somehow a good thing…

    I’d argue that CSS is itself an obfuscation (read: abstraction), and isn’t even implemented consistently across browsers. If the abstraction results in less duplication, more consistency across the website, and higher productivity, then I don’t think that’s a bad thing. (Of course, the flip side is that if using the abstraction results in the same learning curve with less transparency, then the benefits might not outweigh the cost.)

    Having never used tailwind, I can’t give a personal opinion on that, but I find it hard to work on any decently sized project without SCSS. Sure I can write pure CSS, but SCSS provides so many QoL features over raw CSS that it’s hard to pass it up, and it’s easy enough to get setup.


  • I would definitely be concerned about the performance in this case, unless you can do some kind of AoT compilation of the scripts being executed (at least in production builds, development builds probably can’t do that if you need hot reloading). Unfortunately the execution time budget is really tight if you want to get frequent updates and decent frame times (although if the system in question runs parallel to the rest of your systems, for example, then it might have more time to operate with).

    That being said, with the interpreter pre-compiling those scripts, performance could still be really good. Sharing memory would become less relevant since your scripts would now operate on more than whatever’s in your component storages and could now, in theory, work with variables local to the system, assuming you’re meaning that the interpreter is called ad-hoc basically and isn’t the entirety of the system.

    This is all in theory of course, in practice you’d need to find an interpreter that supports pre-compiling (or at least pre-optimizing in some form) all the scripts you’ll want to run if you want to maximize performance.


  • In this case, would the components be combined into a list? Basically you’d have a BubbleComponent[] attached to the entity instead of just a BubbleComponent? Maybe I’m misunderstanding what the system is, is click_handler in the post a system, and if so, do systems only declare a single component of an entity as their input? From my experience, you often want to work with multiple components of the same entity in a system, for example:

    -- Mark characters with 0 or less life as dead
    function mark_dead(character: BoundaryComponent) {
        with character as movement: MovementComponent {
            character.x += movement.velocity.x * delta_secs;
            character.y += movement.velocity.y * delta_secs;
        }
    }
    

    Would there be a simpler way to query these components in the system, and what if I wanted to query for both BoundaryComponent and BubbleComponent, what would that look like?


  • Yes. I vaguely remembered that some ECS can apparently do that. If not, you’d probably settle for a branch or an optional type instead.

    I don’t doubt this is possible, but I’m really curious how querying would work. On the other hand, a component which essentially is just a wrapper for BubbleComponent[] is possible, but querying is straightforward since you’d just get the full list of BubbleComponents per iteration.

    The boundary is supposed to BE the position. So some rendering system would have rendered the speech bubble in the middle of the boundary. Maybe I should have called the boundary area instead…

    My idea behind using positions relative to the BoundaryComponent is along the lines of having each new “bubble entity” hold a reference to the “boundary entity”. Then you’d have a script which updates the transforms of the bubble entities to match that of the boundary entity:

    function inherit_parent_boundaries(
        child: BoundaryComponent & ParentReferenceComponent,
        boundaries: Query
    ) {
        -- This updates the child's boundary to match its parent's boundary
        child.boundary = boundaries.get(child.parent).boundary;
    }
    

    This would keep the bubbles as their own entities and avoid the need for a single entity to hold multiple of the same component, which I think would keep the ECS overall a lot simpler. This doesn’t account for parents of parents or anything like that, but if boundaries can be something like Query, you can recurse up the chain until you’ve updated all the ancestors as well, and all the leaves of the tree will eventually be updated by this system.

    function inherit_parent_boundaries(
        child: BoundaryComponent & ParentReferenceComponent,
        boundaries: Query
    ) {
        -- This updates the child's boundary and all its ancestors to match the boundary of the root of the ancestry tree
        for (
            var cur = child, var parent = boundaries.get(cur.parent);
            parent != null;
            cur = parent, parent = boundaries.get(cur.parent)
        ) {
            cur.boundary = parent.boundary;
        }
    }
    

  • I do not consider programming in Rust scripting.

    I don’t think most people do to be honest, I was providing it as reference because it’s a strongly typed ECS. One of the other challenges with scripts is they often don’t have static type checkers since they’re intended to be executed right away. mypy and TypeScript have helped tremendously with Python and JavaScript, but they’re still extra steps that need to be executed before you can run your code.

    an embedded interpreter seems kind of off the table.

    An embedded interpreter can still be highly performant, assuming it has a decent JIT compiler. Sending data between the host and the interpreter would be a concern, but it might be possible to allow the script to share memory with the host and directly access the components (while holding locks on those components’ storages). I tried experimenting with this a while back using WASI, but unfortunately the runtimes I played with (wasmer + wasmtime) weren’t yet mature enough from my experience to be realistically useful, and I couldn’t figure out how to get the modules to share memory with each other.

    I know there are people playing around with scripting capabilities in bevy though, so I’m sure this will be possible at some point. The other challenge, of course, is having a scheduler that’s flexible enough to handle dynamically added/removed systems, and systems which execute runtime-specified queries.

    Edit: I should add that a large part of the performance of an ECS comes from the ability of an ECS runtime to parallelize the systems. If your interpreter can execute the systems in parallel, then you still get to keep that benefit as long as your scheduler knows which systems are safe to run in parallel.


  • For inspiration, I recommend looking at bevy. It does a great job with efficiently querying for components in a type-safe way. From their book, this is an example of what a system looks like:

    fn greet_people(query: Query<&Name, With>) {
        for name in &query {
            println!("hello {}!", name.0);
        }
    }
    

    Name and Person are both components.

    You might think that a scripting language for ECS-based systems is pointless. And in the majority of cases you would be right.

    This comes as a surprise to me. I think scripting capabilities would be incredibly useful, especially if you can hot reload the scripts during development time. For core engine functionality, this might be less relevant, but for the gameplay itself it could be really nice.

    You may wonder why the entity (usually some sort of ID) is part of its component. It wouldn’t be too convenient if we had to query and pass around entities manually.

    This one I’m curious about. How common is the case when you need to operate with the entity directly, and is it worth the cost of duplicating the entity ID onto each component? In bevy’s case, you can query for the entity ID like you would a component using Entity, which I’ve found to be easy enough to do.

    function click_handler(boundary: BoundaryComponent) {
         -- This creates a new bubble component for the entity stored in boundary.
         boundary with new BubbleComponent("Hi at {boundary.x}, {boundary.y}!");
    
         -- From here on we can use boundary with functions/methods/members
         -- registered for both components, because the type system knows.
         boundary.show_on_top();
    }
    

    Does this mean that each entity can have any number of components of a particular type in this implementation? Would each component also need its own ID to distinguish between two components of one type on the same entity?

    Another option here is that instead of creating a BubbleComponent that’s part of the same entity as the BoundaryComponent, it might make more sense to create a new entity that’s at the same position as the BoundaryComponent’s entity, possibly using some kind of relative transformation to keep the bubble at the same position as the boundary.

    The next example which seems to create two bubbles on the same entity is just as confusing to me. If I were to query for those bubbles, would I iterate over each of those bubbles in separate iterations, but all other components that are part of this query are the same? What about in situations where two or more component types have multiple instances per entity, like if one entity had two BubbleComponents and two BoundaryComponents? Would I iterate over the permutations of those entities, so (bubble 0, boundary 0), (bubble 0, boundary 1), (bubble 1, boundary 0), (bubble 1, boundary 1)?

    I like the ideas presented in the article otherwise. I vaguely remember TypeScript having some sort of reflection support that Angular took advantage of or something. I wonder if that can be used to create a scriptable ECS like proposed in this article?