1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Hey Guest, is it this your first time on the forums?

    Visit the Beginner's Box

    Introduce yourself, read some of the ins and outs of the community, access to useful links and information.

    Dismiss Notice

Q's Practical Guide to KAG Modding #2: Advanced Blobs

Discussion in 'Modding [KAG]' started by -Q, Apr 1, 2015.

  1. -Q

    -Q Donator

    Messages:
    153
    Q's Practical Guide to KAG Modding #2: Advanced Blobs
    Written: Build 1435, 2015-03-31
    In this guide we will focus on what constitutes a blob, as well as how advanced functionality can be encoded into them. After discussing a little theory, we will solidify this knowledge through the step-by-step creation of a new blob.

    What blobs are, and how they work

    File-wise, blobs consist of one or multiple .as files, and one .cfg file. Conceptually, the blob configuration file can be thought of as the singular definition of a blob, as it defines both the blob internal name, as well as any scripts it includes. The filename of a blob configuration file does not have to equate with that blob's internal name.

    Code-wise, a single blob often has multiple concurrently running scripts to handle it. This is usually done to give a blob functionality that is shared by multiple types of blobs. As an example, blobs that act 'wooden' include "Wooden.as" in their .cfg file. "Wooden.as", located in "../Base/Entities/Common/Fabric/Wooden.as" ensures the blob produces wood sounds when dropped, produces wooden particles when hit, etc. There is a stone variant as well.

    On the lowest data level, blobs consist of a physics Box2d object, with position, velocity, rotation, and rotational speed. Although they are rarely used, a blob also has immutable physical properties such as mass, radius, friction, elasticity, buoyancy, and drag. All of these parameters constitute the "Shape" object of the blob, and is configured through the blob configuration file. Some of these physical variables can be modified through a script on-the-fly through functions in either the "blob" object or the "shape" object.

    Each instantiation of a blob also has its own set of script-customizable data, which is shared across all scripts who access the blob. This data is general usage, and can be used to store any data type, though the function blob.get_[datatype]("variable_name"). This allows individual blobs to have state information that isn't shared between all instantiations of that script, which would occur if the variable was defined outside of a function scope. That method of defining variables is useful for constants, which of course would be the same for every blob instantiation.


    How to produce new blobs, from start to finish

    For the purposes of learning, our goal will be to produce a 'bouncy ball' that never loses energy. When we throw it at a tile or player, it will bounce off without losing or gaining velocity. Image-wise, it will be a pink cell-shaded rubber circle. It will crush and gib players of the opposite team, and they cannot grab it, and it will be quite hilarious. It won't damage tiles or shops, and will have a maximum velocity to avoid clipping glitches. If possible, it will bounce on the surface of water, but destroy itself if released in water. It will have enough health to survive two sword slashes, but not three. If slashed, it will bounce away in the opposite direction, but not change teams. It will be pretty big, like a boulder.

    We begin by creating the directory "../Mods/Q's_Bouncy_Ball/Entities/Items/Bouncyball/". In this directory we create "Bouncyball.cfg" to define the basic properties of our blob. As the structure of the .cfg file is currently undocumented elsewhere, we will take a moment to look over the basic syntax, via the configuration file of a mod I'm currently working on:

    Code:
    ###########################
    ###########################
    ## Q's PERFECT LANTERN CONFIG
    ###########################
    ###########################
    
    
    ##################################################
    ############## SPRITE DATA
    ##################################################
    
    $sprite_factory = generic_sprite
    
    @$sprite_scripts = Wooden.as;
    $sprite_texture = Lantern.png
    s32_sprite_frame_width = 8
    s32_sprite_frame_height = 8
    f32 sprite_offset_x = 0
    f32 sprite_offset_y = 0
    
    $sprite_gibs_start = *start*
    $sprite_gibs_end = *end*
    
    ##################################################
    ############## ANIMATION DATA
    ##################################################
    
    $sprite_animation_start = *start*
    
    # Empty Lantern
    $sprite_animation_fire_off_name = fire_off
    u16_sprite_animation_fire_off_time = 3
    u8_sprite_animation_fire_off_loop = 0
    @u16_sprite_animation_fire_off_frames = 3;
    
    # Default Fire
    $sprite_animation_fire_default_name = fire_default
    u16_sprite_animation_fire_default_time = 3
    u8_sprite_animation_fire_default_loop = 1
    @u16_sprite_animation_fire_default_frames   = 0; 1; 2;
    
    # Red Fire
    $sprite_animation_fire_red_name = fire_red
    u16_sprite_animation_fire_red_time = 3
    u8_sprite_animation_fire_red_loop = 1
    @u16_sprite_animation_fire_red_frames = 4; 5; 6;
    
    # Green Fire
    $sprite_animation_fire_green_name = fire_green
    u16_sprite_animation_fire_green_time = 3
    u8_sprite_animation_fire_green_loop = 1
    @u16_sprite_animation_fire_green_frames = 8; 9; 10;
    
    # Blue Fire
    $sprite_animation_fire_blue_name = fire_blue
    u16_sprite_animation_fire_blue_time = 3
    u8_sprite_animation_fire_blue_loop = 1
    @u16_sprite_animation_fire_blue_frames = 12; 13; 14;
    
    # Yellow Fire
    $sprite_animation_fire_yellow_name = fire_yellow
    u16_sprite_animation_fire_yellow_time = 3
    u8_sprite_animation_fire_yellow_loop = 1
    @u16_sprite_animation_fire_yellow_frames = 16; 17; 18;
    
    # Cyan Fire
    $sprite_animation_fire_cyan_name = fire_cyan
    u16_sprite_animation_fire_cyan_time = 3
    u8_sprite_animation_fire_cyan_loop = 1
    @u16_sprite_animation_fire_cyan_frames = 20; 21; 22;
    
    # Magneta Fire
    $sprite_animation_fire_magenta_name = fire_magenta
    u16_sprite_animation_fire_magenta_time = 3
    u8_sprite_animation_fire_magenta_loop = 1
    @u16_sprite_animation_fire_magenta_frames = 24; 25; 26;
    
    $sprite_animation_end = *end*
    
    ##################################################
    ############## SHAPE DATA
    ##################################################
    
    $shape_factory = box2d_shape
    
    @$shape_scripts =
    f32 shape_mass = 3.0
    f32 shape_radius = 3.0
    f32 shape_friction = 0.7
    f32 shape_elasticity = 0.35
    f32 shape_buoyancy = 0.8
    f32 shape_drag = 0.2
    bool shape_collides = yes
    bool shape_ladder = no
    bool shape_platform = no
    
    ##################################################
    ############## BLOCK DATA
    ##################################################
    
    @f32 verticesXY = 0.0; 0.0;
        4.0; 0.0;
        4.0; 6.0;
        0.0; 6.0;
    u8 block_support = 0
    bool block_background = no
    bool block_lightpasses = no
    bool block_snaptogrid = no
    
    $movement_factory =
    $brain_factory =
    
    $attachment_factory = box2d_attachment
    @$attachment_scripts =
    @$attachment_points = PICKUP; 0; 0; 1; 0; 0;
    
    $inventory_factory =
                
    ##################################################
    ############## GENERAL DATA
    ##################################################
    
    $name = lantern
    @$scripts = Wooden.as;
        DecayInWater.as;
        Lantern.as;
        NoPlayerCollision.as;
        SetTeamToCarrier.as;
        DecayIfSpammed.as;
    f32 health = 3.0
    
    ##################################################
    ############## INVENTORY DATA
    ##################################################
    
    $inventory_name = Lantern
    $inventory_icon = LanternIcons.png
    u8 inventory_icon_frame = 0
    u8 inventory_icon_frame_width = 16
    u8 inventory_icon_frame_height = 16
    u8 inventory_used_width = 1
    u8 inventory_used_height = 1
    u8 inventory_max_stacks = 0    
    The syntax has only three three basic operands of function:
    • The # character denotes a comment, and is ignored.
    • The $ character defines the following variable as a string data type. The string is then defined 'raw', without quotation marks.
    • The @ character defines an array of the following data type, such that each element in the array is ended with a semicolon ;.
    The order of each variable definition is important, and leads to a crash if manipulated or left undefined. You will also notice that there are six sections of blob data, each dedicated to a particular property of the blob:
    • Sprite Data: This section defines the filename of the main spritesheet, and the frame size. Frame size is the size of each individual 'sprite' in a file. Multiple sprites are used in cases of animation or color variance.
    • Animation Data: This section defines the various animations which this blob can execute. The script controlling animation, which you often write yourself, accesses each animation via the name. The 'time' property is how much time the animation takes in milliseconds, the 'loop' is if the animation loops or not (1 for true, 0 for false), and the frames array defines which frames the animation consists of in the main sprite sheet.
    • Shape Data: This defines various physical constants, which determine the behavior of the blob via the underlying physics engine. Parameters of particular interest to us are "mass", "friction", "elasticity", and "drag":
      • Mass: Mass is simply the 'weight' of the object, controlling how much force is used in gravity via the famous equation F = ma. In practice, this isn't used often, if at all, in the game.
      • Friction: By my reckoning, this controls how much velocity is lost when 'sliding' or 'rolling' on another object, or on tiles. For a friction of '0', it will slide without stopping, and for a high friction is will stop instantly.
      • Elasticity: This one is important to us: The elasticity of a collision controls how much momentum is transferred to the object we hit. For completely inelastic collisions (elasticity = 0), things stick together when they collide. For perfectly elastic collisions (elasticity = 1), two things colliding bounce off of each other, their speed inversely depending on their mass.
      • Drag: Drag is 'air friction'; it determines how much speed is lost just by moving through the air. We likely want this to be zero, if we want our bouncy ball to continually bounce until manually stopped.
    • Block Data: This defines how the blob collides with tiles and blocks, through a polygonal bounding box that is defined with respect to the sprite. You can use this to allow pixel-perfect collisions in the case of rectangles, but our blob is going to be a circle, which doesn't need this.
    • General Data: Arguably the most important part, this section defines the name of the blob (which you can use to spawn it using !<blob_name>), as well as the array of scripts the blob always includes, and it maximum health.
    • Inventory Data: This section defines how the blob behaves in other inventories, rather the properties of its own inventory. Specifically, it defines the image it displays in inventories, the frame size of that image, and how many slots it takes up. I recommend 16x16 inventory sprites only, due to the way 8x8 inventory sprites appear wrong in the bottom inventory display tab.
    Now, by liberally deleting elements and modifying appropriate values, we can derive the following bare-bones configuration file:
    Code:
    
    ###########################
    ###########################
    ## Q's BOUNCY BALL CONFIG
    ###########################
    ###########################
    
    
    ##################################################
    ############## SPRITE DATA
    ##################################################
    
    $sprite_factory = generic_sprite
    
    @$sprite_scripts =
    $sprite_texture = Bouncyball.png
    s32_sprite_frame_width = 16
    s32_sprite_frame_height = 16
    f32 sprite_offset_x = 0
    f32 sprite_offset_y = 0
    
    $sprite_gibs_start = *start*
    $sprite_gibs_end = *end*
    
    ##################################################
    ############## ANIMATION DATA
    ##################################################
    
    $sprite_animation_start = *start*
    
    $sprite_animation_end = *end*
    
    ##################################################
    ############## SHAPE DATA
    ##################################################
    
    $shape_factory = box2d_shape
    
    @$shape_scripts =
    f32 shape_mass = 300.0
    f32 shape_radius = 6.0
    f32 shape_friction = 0.1
    f32 shape_elasticity = 0.1
    f32 shape_buoyancy = 0.8
    f32 shape_drag = 0.1
    bool shape_collides = yes
    bool shape_ladder = no
    bool shape_platform = no
    ##################################################
    ############## BLOCK DATA
    ##################################################
    
    @f32 verticesXY =
    u8 block_support = 0
    bool block_background = no
    bool block_lightpasses = no
    bool block_snaptogrid = no
    
    $movement_factory =
    $brain_factory =
    
    $attachment_factory = box2d_attachment
    @$attachment_scripts =
    @$attachment_points = PICKUP; 0; 0; 1; 0; 0;
    
    $inventory_factory =
          
    ##################################################
    ############## GENERAL DATA
    ##################################################
    
    $name = bouncyball
    @$scripts = Bouncyball.as;
    f32 health = 3.0
    
    ##################################################
    ############## INVENTORY DATA
    ##################################################
    
    $inventory_name = Bouncy Ball
    $inventory_icon = Bouncyball.png
    u8 inventory_icon_frame = 0
    u8 inventory_icon_frame_width = 16
    u8 inventory_icon_frame_height = 16
    u8 inventory_used_width = 2
    u8 inventory_used_height = 2
    u8 inventory_max_stacks = 0 
    This is a very generic script, but it requires both a Bouncyball.as and Bouncyball.png file. The .as file is pretty simple; although the angelscript cannot load completely empty scripts, the following should work:
    Code:
    
    // Bouncyball.as
    
    void onInit(CBlob@ this)
    {
       ;
    }
    
    void onTick(CBlob@ this)
    {
       ;
    }
    For the image file, we can load up a simple image editor and draw a pixelated pink circle. I used Paint.NET for this, although it came out slightly hexagonal:
    ball.png
    Save whatever image you design as Bouncyball.png, in next to Bouncyball.cfg and Bouncyball.as. Of course, the names of the files themselves are arbitrary, and you can choose whatever filenames you want as long as everything is linked properly.

    Once you have all three files, load up the game and see our result-so-far. If you don't know how to load up mods locally, check the first guide in this series. To spawn the bouncy ball, use the !bouncyball command, which is a specific example of the ever-useful !<blobname> command. Again, this command won't work unless you have your mod development environment properly set up (See guide #1)

    If you throw it around, you'll notice that it slides around and has the propensity for pushing objects. This is due to the large mass we gave it. Further, while it slides nicely, it does not bounce. In reality, we want it to bounce off of things without pushing them, so let's give it a low mass instead. Finally, because we want something bouncy, let's give it an elasticity of one.

    Code:
    f32 shape_mass = 0.1
    f32 shape_radius = 6.0
    f32 shape_friction = 0.1
    f32 shape_elasticity = 1
    f32 shape_buoyancy = 0.8
    f32 shape_drag = 0.1
    Because we're editing the blob configuration file, we need to run /restartserver rather than rebuild, since rebuild only recompiles scripts - it doesn't reload blobs.

    When you do so, you'll notice that yay, it does bounce! However, its horizontal velocity does decrease pretty quickly, until it's only bouncing in-place. Let's see if making drag and friction zero would fix that. Don't forget to /restartserver

    Code:
    f32 shape_mass = 0.1
    f32 shape_radius = 6.0
    f32 shape_friction = 0.0
    f32 shape_elasticity = 1
    f32 shape_buoyancy = 0.8
    f32 shape_drag = 0.0
    Ah, this works! Now it keeps bouncing for a while. It eventually stops due to simple entropy - as it bounces off of complex map surfaces, it tends to get stuck in a hole somewhere. It looks like the problem of a "continuously bouncing ball" isn't quite as simple as we thought. So we will scrap that idea, until we want to implement a bouncy ball brain to avoid such situations.

    We're not nearly done, though. We have an object that physically acts like we want it to, but not we need to make it logically behave how we specify. Namely, we want to try to implement the following features:
    1. Actually bounce off of the sides of the map - which it currently doesn't do.
    2. Crush enemies in a cloud of gore.
    3. Not allow enemies to grab it.
    4. Bounce on the surface of water.
    5. Survive two sword slashes, but not three.
    6. Bounce in the opposite direction when someone slashes it.
    7. Don't go above a certain maximum velocity.
    This seems gargantuan, but probably does not actually boil down to that much code. My guess is 500 total - note: Yes, I really making this as I write. We will address these problems in order.

    Bounce off of the sides of the map

    While playing around with it, you might've noticed that its horizontal velocity is set to zero when it collides the side of the map, rather than precisely opposite the current horizontal velocity.

    In the end, I came up with the following snippet of code, which I will thoroughly explain. I apologize if it seems like I pulled it out of my ass, but I went through many possible solutions until I found one that worked consistently, and detailing that long trial & error process is unreasonable.

    Code:
    void onTick(CBlob@ this)
    {
       Vec2f vel = this.getVelocity();
       Vec2f oldvel = this.getOldVelocity();
       Vec2f pos = this.getPosition();
       CMap@ map = this.getMap();
    
       float map_right = map.tilesize*map.tilemapwidth;
    
       if (pos.x >= map_right - 5 && vel.x > 0)
       {
         print("right map collision detected");
         vel.x = -oldvel.x;
         this.setVelocity(vel);
       }
    
       if (pos.x <= 5 && vel.x < 0)
       {
         print("left map collision detected");
         vel.x = -oldvel.x;
         this.setVelocity(vel);
       }
    }
    The first four lines are simply calls to the blob to get various necessary data & objects that we'll need. I consider it good practice to do this at the top of your function, although some argue that variables should be defined immediately before the line that they're first used.

    The map_right float variable defines width of the map in pixels, which are the most fundamental unit of size and position in this engine. Since there is no way to get it from the map directly, we need to calculate it from the width of each tile with the tile width of the tilemap itself.

    The first if block checks if the bouncy ball is colliding with the left wall, and if so, bounces the ball left. The entire block may be read as "If the bouncy ball is close to the right end of the map, and it's moving right, set its horizontal velocity to opposite of the current horizontal velocity".

    The second if block does the same check, only with the left end of the map.
    Originally I was planning on going through the further polishing of this blob, but I've now decided to leave the rest of the implementation as an exercise to the reader.

    So, if you want further practice, try implementing some of the other features I described. You should now have no trouble designing a completely new item with its own behavior, and should understand the relationship between CBlobs, CScripts, CInventory, etc.
     
    Last edited: Apr 2, 2015
  2. Great guide! But that ball is octagonal not hexagonal ::P:
     
    JaytleBee and Blue_Tiger like this.
  3. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Some corrections based on internal stuff:
    • Blobs are actually essentially a "house" for components, not the shape itself (CShape is a separate thing).
    • Each "whatever_factory" element is a separate component. if you omit the factory string you can omit everything until the next factory string. A few components are sort of "required" for most objects; things don't work well without a shape if they're meant to be moving around, but things like the music blobs dont have any components at all.
    • The animation data is part of the sprite. You can access it though CSprite's interface
    • You missed out a few components like the movement and brain, though you did touch on the inventory.
    Overall pretty good though, and very happy to see tutorials like this coming up.
     
    joshua12131415 and Chukka like this.
  4. -Q

    -Q Donator

    Messages:
    153
    Just working with what I have :). You know, a few docs saying what everything is would clear up a lot. What I most misunderstand is the network and how everything syncs together. Mostly, what situations /would/ cause sycning errors? What happens if a client uses Sync() ?
     
  5. Verrazano

    Verrazano Flat Chested Haggy Old Souless Witchy Witch Witch THD Team Global Moderator Forum Moderator Tester
    1. Practitioners of War Extreme Revolution - POWER

    Messages:
    477
    Sorry for not seeing this tutorial earlier, thanks again @-Q. Just a note when you remove variables from the cfg it can cause undefined behavior. Variables in the cfg aren't read by name but sequentially. So if you reorder or remove variables it can cause issues. I suggest that you make an addendum to your blob cfg that includes all variables even if they are unused/blank. So that future beginners don't run into any problems.

    Today I was talking to somebody that was using this blob config and could get his blob to run CSprite hooks. Once I had him copy a config from Base and modify it, everything worked.
     
  6. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Completely missed this, responding for anyone who's still wondering; Feel free to link people to this post when they're asking about Sync.
    • Sync is a method of CBlob and CRules that is used for synchronising properties over the network. Properties are internal serialised variables set with set_bool, set_u8, set_s8, set_u16, set_s16, set_u32, set_s32, set_string, set_CBitStream, Tag, and Untag. The methods set and get are not Sync-capable!
    • The full call signature is void Sync(string propertyname, bool fromserver) - eg this.Sync("someproperty", true);
    • As the second property hints, Sync can be used from both client and server.
    • Sync calls are a one-off! Automatic syncing is not "turned on" by calling sync, you have to call it each time you change the variable and want the changes to be reflected elsewhere. This seems to be the biggest confusion about Sync in general - as if the second parameter is some on/off flag (it isn't)!
    • Generally one end of the connection should be responsible for syncing, and do all of the modifications to the related property (syncing the results elsewhere)
      • The server should be responsible for most blobs, and almost all rules properties. Use true for the fromserver parameter. use getNet().isServer(), or #define SERVER_ONLY at a script level to limit property modifications (so that only changes synced from the server show up on the client, rather than client-side modifications as well).
      • The client should only be responsible for player-specific things, and only for their own player. Use false for the fromserver parameter. use getNet().isClient() && blob.isMyPlayer() or similar to avoid syncing from every client, wasting bandwidth, and causing jitter in the value.
    • Property Syncs will only actually be sent if something has changed since the last time sync was called - note that this DOES mean that there's always overhead to actually calling it, but its CPU overhead rather than network bandwidth. This means if you want to "auto-sync" a set of properties all you have to do is put a sync call in some onTick hook and they'll stay updated (though this is more CPU intensive than only calling it when you know something has changed).
    • If you can get away with it, avoid syncing entirely; simply updating as normal on both ends can work wonders for anything that doesn't have to be the same on both ends, or where ping delay is unacceptable.
     
    Verrazano and blackjoker77777 like this.
  7. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    The documentation on the CBlob class is... lacking, to say the least. Several methods are simply missing, and central concepts aren't explained.
    Some of the other classes aren't documented at all! (Such as CShape and CMovement)

    Synchronization is a somewhat challenging topic. As people might have noticed, KAG struggles a bit when something is wrong server-side. This is probably not just due to issues with synchronization calls, but also due to games usually communicating with "minimal" networking protocols like UDP. This raises the question if these synchronization calls has any real guarantee of actually being performed. But, anyway...

    How about tags, are they synchronized automatically? If not, how are they synchronized?

    Oh, and I would really appreciate some enlightenment on commands. The ones you set and get with CBlob.addCommandID(string) and CBlob.getCommandID(string) responsively. Then there's a handler void onCommand(CBlob@ this, u8 cmd, CBitStream @params). What is the practical application of this way of doing things versus setting properties/tags and performing regular checks? I mean, other than the obvious aspect of saving computation time. Are these commands sent between the client and the server (I would assume since it's dealing with bit streams), or is it internal? Is onCommand a client-side only function? I was looking at the Hall entity, and pretty much all that function does is sprite and client chat related, but those calls are also client-specific.

    //
    On a side note; I noticed you wrote
    Untag, but in the code I've been looking at it's written unTag. are calls in scripts not case-sensitive, or was there a typo?
     
  8. Verrazano

    Verrazano Flat Chested Haggy Old Souless Witchy Witch Witch THD Team Global Moderator Forum Moderator Tester
    1. Practitioners of War Extreme Revolution - POWER

    Messages:
    477
    Yes documentation is pretty lacking, because it's not exactly fun to do. There is some (very little extra) available in irc, and some on the website which I imagine you've found.

    I'm not entirely sure what you are trying to get at when you are saying this:

    I've not noticed any issues with kag "struggling server side", besides cpu hogging maybe. Also UDP is the preferred protocol for any real time game. Using TCP is pretty silly, you can read any number of articles on why it's used in games. We use a library, called enet, which operates on top of udp. It allows us to send packets that are guaranteed as long as the client is connected, but that's only used in critical cases. If you are still connected to a server when a sync happens you will receive the sync at some point, there is a guarantee.

    Tags are just a boolean property.

    this.Tag("something"); is the same as this.set_bool("something", true); therefore you can sync a tag by simply doing this.Sync("something", true); like all other properties.

    The practical application of this is mostly architectural. Instead of having to check every cycle to see when a state has changed (which also requires you to have known the previous state). You simply wait for the hook to be called, then you can update states accordingly.

    Commands are sent to all clients and server when performed, regardless of where the originated. They can be made client/server only, by adding a getNet().isServer/Client check in the onCommand hook.

    In terms of use cases, commands should be treated as function calls. Where as properties are just variables. Syncing a property is used when you don't need to know when it's changed and it'll just work. Commands are used when you need to know that something occurred to update.

    Angelscript as every other programming/scripting language is case sensitive. It was just a typo in Geti's post.
     
    Last edited: Jan 23, 2016
  9. Vermilicious

    Vermilicious Ballista Bolt Thrower

    Messages:
    232
    Understandable (it's a general problem in the industry, after all), but not a good excuse ;o)

    I guess I was a bit unfair at that point; the most trouble I've seen is with certain mods, not the official game modes. As for UDP, I'm aware how it is suitable for games; it was just a comment I made in regards to "synchronizing" something that strictly isn't synchronized in the first place (because packets can be lost or received out of order) ;o)

    Thanks for clearing that up. I should've suspected it.

    Thanks for clearing up on commands too; it is as I suspected, but as you say, it happens whatever environment it's run in.

    Are you sure about that? ;o)
     
  10. Geti

    Geti Please avoid PMing me (poke a mod instead) THD Team Administrator Global Moderator

    Messages:
    3,730
    Re: Untag - no, its spelled that way.
    [​IMG]
    not sure where you've been reading unTag?

    Re: ENet and synchronisation - enet's reliable transport is used more exclusively than verra suggests - each frame's deltas are generally packaged up into one large(ish) packet, compressed, and sent with enet's reliable flag. This makes the networking layer handle out of order or missing packets. If a packet gets lost the whole stream is held until it can be resent (which is basically TCP but doesn't happen at each node in the network and later packets that still make it are buffered on the client). We dont use multiple streams in crimson for legacy reasons (MM didn't understand how they'd be useful until the code was past critical "i dont want to rewrite that" mass).
    There is indeed the issue of the idea of "connected" being rather arbitrary - there's no sessions in UDP so we're relying on enet detecting dropped players and the like. But for normal use (and low ping players) there's only so much that can go wrong.

    Re: commands - extra care has to be taken there to make sure that each command gets read properly and competely anywhere it arrives, or you'll get "weird" crashes and problems in general. They can be thought of as a remote procedure call, and can be sent to a specific player from the server if needed. When SendCommand is called on the server, the handler function is called locally straight away and the command is synced over the network. When SendCommand is called on the client, it's just sent - the server will echo it and that's when the client will fire off the handler - this is why stuff based on commands can be "Laggy" - there's a full ping between calling SendCommand and actually running code related to it. This can cause all sorts of issues with timers in code that doesn't understand this (eg firing off _more_ commands for the entire ping period).
    As such, they're a blessing and a curse - writing stuff in terms of commands can basically ensure it's all properly synchronised "easily" if done correctly (and if it works locally it "should" work on net), but introduces lag and can be nasty to debug.

    Re: case sensitivity - there are some programming languages that are not case sensitive. AngelScript is not one of them.
     
    Vermilicious and bunnie like this.
  11. joshua12131415

    joshua12131415 Bison Rider Tester

    Messages:
    190
    Can you make a #3?
     
  12. Olimarrex

    Olimarrex Ballista Bolt Thrower Official Server Admin

    Messages:
    177
    Demn, every time I try and get into modding I just end up coming out more impressed by people that do do it... I'm gettin' closer tho cx.
     
    Geti, makmoud98 and king-george like this.