Dominik Madarász 2023-08-11 21:46:46 +02:00
parent 458f0e6bd4
commit 69b7782b2e
5 changed files with 613 additions and 52 deletions

View File

@ -473,7 +473,29 @@ if "%1"=="fwk" (
if /i not "!filename:~0,4!"=="3rd_" ( if /i not "!filename:~0,4!"=="3rd_" (
echo Processing: %%f echo Processing: %%f
powershell -command "(Get-Content '%%f') -creplace 'v4k', 'fwk' -creplace 'V4K', 'FWK' | Set-Content '%%f'" powershell -command "(Get-Content '%%f') -creplace 'v4k', 'fwk' -creplace 'V4K', 'FWK' | Set-Content '%%f'"
rem powershell -command "(Get-Content '%%f') -replace 'V4K', 'FWK' | Set-Content '%%f'" ) else (
echo Skipping %%f
)
)
echo All done.
endlocal
exit /b
)
if "%1"=="back" (
setlocal enabledelayedexpansion
for %%f in ("_fwk\fwk*") do (
set "filename=%%~nf"
set "newname=v4k!filename:fwk=!%%~xf"
echo Copying and renaming "%%f" to "engine\split\!newname!"
copy "%%f" "engine\split\!newname!"
)
for %%f in (engine\split\*) do (
set "filename=%%~nxf"
if /i not "!filename:~0,4!"=="3rd_" (
echo Processing: %%f
powershell -command "(Get-Content '%%f') -creplace 'fwk', 'v4k' -creplace 'FWK', 'V4K' | Set-Content '%%f'"
) else ( ) else (
echo Skipping %%f echo Skipping %%f
) )

276
demos/3rd_bt.h 100644
View File

@ -0,0 +1,276 @@
// Behavior trees: decision planning and decision making.
// Supersedes finite state-machines (FSM) and hierarchical finite state-machines (HFSM).
// - rlyeh, public domain.
//
// [ref] https://outforafight.wordpress.com/2014/07/15/behaviour-behavior-trees-for-ai-dudes-part-1/
// [ref] https://www.gameaipro.com/GameAIPro/GameAIPro_Chapter06_The_Behavior_Tree_Starter_Kit.pdf
// [ref] https://gitlab.com/NotYetGames/DlgSystem/-/wikis/Dialogue-Manual
// [ref] https://towardsdatascience.com/designing-ai-agents-behaviors-with-behavior-trees-b28aa1c3cf8a
// [ref] https://docs.nvidia.com/isaac/packages/behavior_tree/doc/behavior_trees.html
// [ref] https://docs.unrealengine.com/4.26/en-US/InteractiveExperiences/ArtificialIntelligence/BehaviorTrees/
// [ref] gdc ChampandardDaweHernandezCerpa_BehaviorTrees.pdf @todo debugging
// [ref] https://docs.cocos.com/cocos2d-x/manual/en/actions/
#if 0
The nodes in a behavior tree can be broadly categorized into three main types: control nodes, task nodes, and decorator nodes. Here is a brief description of each category:
Control Nodes: Control nodes are used to control the flow of the behavior tree. They determine the order in which child nodes are executed and how the results of those nodes are combined. They are usually parent nodes.
Action Nodes: Action nodes are used to perform specific actions or tasks within the behavior tree. They can include actions such as moving, attacking, or interacting with objects in the game world. They are usually leaf nodes.
Decorator Nodes: Decorator nodes are used to modify the behavior of child nodes in some way. They can be used to repeat child nodes, invert the result of a child node, or add a cooldown period between executions. They are usually located between control and action nodes.
--- VARIABLES
Key Prefixes:
{visiblity:(l)ocal,s(q)uad,(r)ace,(f)action,(g)lobal}
[persistence:(t)emp,(u)serdata,(s)avegame,(c)loud] + '_' + name.
[persistence:(tmp),(usr)data,(sav)egame,(net)cloud] + '_' + name.
Ie, l_health = 123.4, gsav_player = "john"
--- ACTION NODES
[ ] * Actions/Behaviors have a common structure:
[ ] - Entry point (function name) for a C call or Lua script.
[ ] * Status:
[ ] - Uninitialized (never run)
[ ] - Running (in progress)
[ ] - Suspended (on hold till resumed)
[ ] - Success (finished and succeeded)
[ ] - Failure (finished and failed)
[ ] * Optional callbacks:
[ ] - on_enter
[ ] - on_leave
[ ] - on_success
[ ] - on_failure
[ ] - on_suspend
[ ] - on_resume
[x] Action Node: This node performs a single action, such as moving to a specific location or attacking a target.
[ ] Blackboard Node: Node that reads and writes data to a shared memory space known as a blackboard. The blackboard can be used to store information that is relevant to multiple nodes in the behavior tree.
[ ] SetKey(keyVar,object)
[ ] HasKey(keyVar)
[ ] CompareKeys(keyVar1, operator < <= > >= == !=, keyVar2)
[ ] SetTags(names=blank,cooldownTime=inf,bIsCooldownAdditive=false)
[ ] HasTags(names=blank,bAllRequired=true)
[ ] PushToStack(keyVar,itemObj): creates a new stack if one doesnt exist, and stores it in the passed variable name, and then pushes item object onto it.
[ ] PopFromStack(keyVar,itemVar): pop pops an item off the stack, and stores it in the itemVar variable, failing if the stack is already empty.
[ ] IsEmptyStack(keyVar): checks if the stack passed is empty and returns success if it is, and failure if its not.
[ ] Communication Node: This is a type of action node that allows an AI agent to communicate with other agents or entities in the game world. The node takes an input specifying the message to be communicated and the recipient(s) of the message (wildmask,l/p/f/g prefixes). The node then sends the message to the designated recipient(s) and returns success when the communication is completed. This node can be useful for implementing behaviors that require the AI agent to coordinate with other agents or to convey information to the player. It could use a radius argument to specify the maximum allowed distance for the recipients.
[ ] Condition Node: A leaf node that checks a specific condition, such as the distance to an object, the presence of an enemy, or the status of a health bar.
[ ] Distance Condition Node: This is a type of condition node that evaluates whether an AI agent is within a specified distance of a target object or location. The node takes two inputs: the current position of the AI agent and the position of the target object or location. If the distance between the two is within a specified range, the node returns success. If the distance is outside of the specified range, the node returns failure. This node can be useful for implementing behaviors that require the AI agent to maintain a certain distance from a target, such as following or avoiding an object. Could use a flag to disambiguate between linear distance and path distance.
[ ] Query Node: This node checks a condition and returns success or failure based on the result. For example, a query node could check whether an enemy is within range or whether a door is locked.
[ ] Query Node: A type of decorator node that retrieves information from a database or external system, such as a web service or file system. The Query node can be used to retrieve data that is not available within the game engine, such as weather or traffic conditions.
[ ] A condition is made of:
[ ] - Optional [!] negate
[ ] - Mandatory Value1(Int/Flt/Bool/VarName/FuncName)
[ ] - Optional operator [< <= > >= == !=] and Value2(Int/Flt/Bool/VarName/FuncName)
[ ] AllConditions(...) : SUCCESS if ( empty array || all conditions met)
[ ] AnyConditions(...) : SUCCESS if (!empty array && one conditions met)
--- DECORATOR NODES
[ ] Cooldown Node: Decorator node that adds a cooldown period between the execution of a child node, preventing it from being executed again until the cooldown period has elapsed.
[x] Counter Node: Decorator node that limits the number of times that a child node can execute. For example, if the child node has executed a certain number of times, the Counter node will return a failure.
[x] Once Node: Decorator node that triggers a specified action when a condition is met for the first time. This can be useful for triggering a one-time event, such as when an AI agent discovers a hidden item or reaches a new area of the game world.
[x] Inverter Node: Decorator node that inverts the result of a child node, returning success if the child node fails and failure if the child node succeeds. This can be useful for negating the outcome of a particular behavior, such as avoiding a certain area of the game world.
[x] Repeater Node: Decorator node that repeats the execution of a child node a specified number of times or indefinitely.
[x] Repeat(times=inf): Runs child node given times. These are often used at the very base of the tree, to make the tree to run continuously.
[ ] RepeatIf(strong/weak condition): Runs child node as long as the conditions are met.
[ ] RepeatIfOk(times=inf): Runs child node if it succeedes, max given times.
[ ] RepeatIfFail(times=inf): Runs child node if it fails, max given times.
[ ] Branch Node: 2 children [0] for true, [1] for false
[ ] Resource Node: Decorator node that manages a shared resource, such as a limited supply of ammunition or a limited amount of processing power. The Resource node The node can be used to decide when to use or conserve the resource. For example, if the AI agent is low on ammunition, the node may decide to switch to a melee weapon or to retreat to a safer location. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions about resource use.
[x] Result Node: Decorator node that tracks the result of a child action node and returns either success or failure depending on the outcome. This can be useful for determining whether an action was successful or not, and then adjusting the AI agent's behavior accordingly.
[x] Succeeder Node: Decorator node that always returns success, regardless of the result of its child node. This can be useful for ensuring that certain behaviors are always executed, regardless of the overall success or failure of the behavior tree.
[x] Success(): FAILURE becomes SUCCESS (TRUE).
[x] Failure(): SUCCESS becomes FAILURE (FALSE).
[ ] Throttle Node: Decorator node that limits the rate at which a child node can execute. For example, a Throttle node might ensure that an AI agent can only fire its weapon, or using a special ability, only a certain number of times per second.
[x] Delay Node: Decorator node that adds a delay to the execution of a child node. The delay might be configured to sleep before the execution, after the execution, or both.
[x] Defer Delay(duration_ss=1): Runs the child node, then sleeps for given seconds.
[ ] Ease(time,name): Clamps child time to [0,1] range, and applies easing function on it.
[ ] Dilate(Mul=1,Add=0): Dilates child time
--- CONTROL NODES
[x] Root Node: The topmost node in a behavior tree that represents the start of the decision-making process. Returns success if any of its child nodes suceedes.
[x] Root-Failure Node: Control node that only returns failure if all of its child nodes fail. This can be useful for ensuring that a behavior tree is not prematurely terminated if one or more child nodes fail.
[ ] Event(name): When name event is raised, it suspends current tree and calls child. "Incoming projectile" -> Evade. Stimulus types: may be disabled by event, or autodisabled.
[ ] Raise(name): Triggers event name.
[ ] Checkpoint Node: Control node that saves a state in memory and then continues the tree execution from that point the next time the tree is executed. It can be useful in situations where the behavior tree needs to be interrupted and resumed later.
[ ] Decision Making Node: Control node that implements a decision-making process for the AI agent. The node takes input specifying the available options for the AI agent and the criteria for evaluating each option. The node then evaluates each option based on the specified criteria and selects the best option. This node can be useful for implementing behaviors that require the AI agent to make strategic decisions, such as choosing a target or selecting a path through the game world.
[ ] Could be extended with GOAP if dynamically inserting the scores on each update then calling a Probability Selector Node (0.2,0.3,0.5)
[ ] https://cdn.cloudflare.steamstatic.com/apps/valve/2012/GDC2012_Ruskin_Elan_DynamicDialog.pdf
[ ] Evaluate Node / Recheck():
[ ] Actively rechecks all existing sub-conditions on a regular basis after having made decisions about them.
[ ] Use this feature to dynamically check for risks or opportunities in selected parts of the tree.
[ ] For example, interrupting a patrol with a search behavior if a disturbance is reported.
[ ] Interrupt Node: Control node that interrupts the execution of a lower-priority node when a higher-priority node needs to be executed. It can be useful for handling urgent tasks or emergency situations.
[ ] Monitor Node: Control node that continuously monitors a condition and triggers a specified action when the condition is met. For example, a Monitor node might monitor the distance between an AI agent and a target and trigger a "retreat" behavior when the distance falls below a certain threshold.
[ ] Observer Node: Control node that monitors the state of the game world and triggers a specified action when certain conditions are met. For example, an Observer node might trigger a "hide" behavior when an enemy is spotted.
[ ] Parallel Node: Control node that executes multiple child nodes simultaneously. The Parallel node continues to execute even if some of its child nodes fail.
[ ] Parallel All Node(required_successes=100%): Control node that executes multiple child nodes simultaneously. Returns false when first child fails, and aborts any other running tasks. Returns true if all its children succeed.
[ ] Parallel One Node(required_successes=1): Control node that executes multiple child nodes simultaneously. Returns true when first child suceedes, and aborts any other running tasks. Returns false if all its children fail.
[ ] Subsumption Node: Control node that allows multiple behaviors to be executed simultaneously, with higher-priority behaviors taking precedence over lower-priority ones. This can be useful for implementing complex, multi-level behaviors in autonomous systems.
[ ] Semaphore Node: Control node that blocks the execution of its child nodes until a certain condition is met. For example, a Semaphore node might block the execution of a behavior until a certain object is in range or a certain event occurs.
[ ] Semaphore Wait Node: Control node that blocks the execution of its child nodes until a resource becomes available. This can be useful for controlling access to shared resources such as a pathfinding system or a communication channel.
[ ] WaitTags(tags=blank,timeout_ss=inf): Stops execution of child node until the cooldown tag(s) do expire. May return earlier if timed out.
[ ] WaitEvent(name=blank,timeout_ss=inf): Stops execution of child node until event is raised. May return earlier if timed out.
[x] Sequence Node(reversed,iterator(From,To)): Control node that executes a series of child nodes in order. If any of the child nodes fail, the Sequence node stops executing and returns a failure.
[ ] Dynamic Sequence Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Sequence node might give higher priority to a child node that has recently failed or that is more likely to succeed.
[ ] Reverse(): iterates children in reversed order (Iterator:(-1,0) equivalent)
[ ] Iterator(from,to): allows negative indexing. increments if (abs from < abs to), decrements otherwise
[x] Selector Node: Control node that selects a child node to execute based on a predefined priority or set of conditions. The Selector node stops executing child nodes as soon as one of them succeeds.
[ ] Priority Selector Node: Control node that executes its child nodes in order of priority. If the first child node fails, it moves on to the next child node in the list until a successful node is found. This can be useful for implementing behaviors that require a specific order of execution, such as a patrol route or a search pattern.
[ ] Probability Selector Node: Control node that selects a child node to execute based on a probability distribution. For example, if there are three child nodes with probabilities of 0.2, 0.3, and 0.5, the Probability Selector node will execute the third child node 50% of the time.
[ ] Dynamic Selector Node: Control node that dynamically changes the order in which its child nodes are executed based on certain conditions. For example, a Dynamic Selector node might give higher priority to a child node that has recently succeeded or that is more likely to succeed.
[ ] Dynamic Selector Node: Control node that dynamically changes the order in which child nodes are executed based on certain conditions. For example, it may prioritize certain nodes when health is low, and other nodes when resources are scarce.
[ ] Dynamic Priority Node: Control node that dynamically changes the priority of its child nodes based on certain conditions. For example, if a child node is more likely to result in success, the Dynamic Priority node will give it a higher priority.
[ ] Weighted / Cover Selection Node: This is a type of selector node that evaluates the available cover options in the game world and selects the best cover position for the AI agent. The node takes input specifying the current position of the AI agent and the location of potential cover positions. The node then evaluates the cover positions based on criteria such as distance to enemy positions, line of sight, and available cover points, and selects the best option. This node can be useful for implementing behaviors that require the AI agent to take cover and avoid enemy fire.
[ ] Random Selector Node: Control node that selects a child node to execute at random. This can be useful for introducing variety and unpredictability into the behavior of an AI agent.
[ ] Random Weight / Stochastic Node(0.2,0.3,0.5): Control node that introduces randomness into the decision-making process of an AI agent. For example, a Stochastic node might randomly select a child node to execute, with the probability of each child node being proportional to its likelihood of success.
[ ] Break(bool): breaks parent sequence or selector. may return SUCCESS or FAILURE.
[ ] Hybrid Node: Control node that combines the functionality of multiple control nodes, such as a sequence node and a selector node. It can be useful for creating complex behavior patterns that require multiple control structures. The Hybrid Node has two modes of operation: strict mode and flexible mode. In strict mode, the child nodes are executed in a fixed order, similar to a Sequence Node. If any child node fails, the entire Hybrid Node fails. In flexible mode, the child nodes can be executed in any order, similar to a Selector Node. If any child node succeeds, the entire Hybrid Node succeeds. The Hybrid Node is often used when you need to create complex behavior patterns that require multiple control structures. For example, you might use a Hybrid Node to implement a search behavior where the AI agent first moves in a fixed direction for a certain distance (strict mode), but then switches to a more exploratory mode where it randomly explores nearby areas (flexible mode).
[ ] Subtree Node: Control node that calls another behavior tree as a subroutine. This is useful for breaking down complex behaviors into smaller, more manageable parts.
[ ] Call(name): Calls to the child node with the matching name within behavior tree. Returns FAILURE if name is invalid or if invoked behavior returns FAILURE.
[ ] Return(boolean): Exits current Call.
[ ] AttachTree(Name,bDetachAfterUse=true): Attaches subtree to main tree. When a NPC founds the actor, it attaches the behavior to the tree. Ie, Level specific content: Patrols, Initial setups, Story driven events, etc. Ie, DLCs: Behaviors are added to actors in the DLC level (enticers).
[ ] DetachTree(Name)
[ ] Switch Node: Control node that evaluates a condition and then selects one of several possible child nodes to execute based on the result of the condition.
[ ] Switch(name): Jumps to the child node with the matching name within behavior tree. If name is invalid will defer to Fallback child node.
[ ] Timeout Node: Control node that aborts the execution of its child node after a certain amount of time has elapsed. This can be useful for preventing an AI agent from getting stuck in a loop or waiting indefinitely for a particular event to occur.
[ ] TimeLimit(timeout_ss=inf): Give the child any amount of time to finish before it gets canceled. The timer is reset every time the node gains focus.
[ ] Timer Node: Control node which invokes its child node every XX seconds. The timer can repeat the action any given number of times, or indefinitely.
## Proposal -----------------------------------------------------------------------------
BehaviorTrees as case-insensitive INI files. Then,
- INI -> C INTERPRETER, OR
- INI -> LUA TRANSPILER -> LUA BYTECODE -> LUA VM.
```ini
[bt]
recheck
sequence ;; Approach the player if seen!
conditions=IsPlayerVisible,
action=MoveTowardsPlayer
sequence ;; Attack the player if seen!
conditions=IsPlayerInRange,
repeat=3
action=FireAtPlayer
sequence ;; Search near last known position
conditions=HaveWeGotASuspectedLocation,
action=MoveToPlayersLastKnownPosition
action=LookAround
sequence ;; Randomly scanning nearby
action=MoveToRandomPosition
action=LookAround
event=IncomingProjectile
action=Evade
```
#endif
typedef int (*bt_func)();
map(char*, bt_func) binds;
void bt_addfun(const char *name, int(*func)()){
do_once map_init_str(binds);
map_find_or_add_allocated_key(binds, STRDUP(name), func);
}
bt_func bt_findfun(const char *name) {
bt_func *found = map_find(binds, (char*)name);
return found ? *found : 0;
}
char * bt_funcname(bt_func fn) {
for each_map(binds,char*,k,bt_func,f) {
if( f == fn ) return k;
}
return 0;
}
typedef struct bt_t {
uint64_t type;
int (*action)();
union {
int argi;
float argf;
};
//struct bt_t *parent;
array(struct bt_t) children;
} bt_t;
bt_t bt(const char *ini_file, unsigned flags) {
bt_t z = {0}, root = z;
array(char*) m = strsplit(vfs_read(ini_file), "\r\n");
bt_t *self = &root;
self->type = cc8(root);
//self->parent = self;
for( int i = 0; i < array_count(m); ++i ) {
// parse ini
int level = strspn(m[i], " \t");
char *k = m[i] + level;
if( k[0] == '[' ) {
if( strcmp(k+1, "bt]") ) return z; // we only support [bt]
continue;
}
int sep = strcspn(k, " =:");
char *v = k + sep; if(sep) *v++ = '\0'; else v = k + strlen(k); // v = (char*)"";
array(char*) args = *v ? strsplit(v, " ") : NULL;
// insert node in tree
bt_t *out = self;
while( level-- ) {
out = array_back(out->children);
}
array_push(out->children, ((bt_t){0}));
//array_back(out->children)->parent = out;
out = array_back(out->children);
// config node
out->type = *(uint64_t*)va("%-8s", k);
if( array_count(args) ) out->argf = atof(args[0]);
if( !strcmp(k, "run") ) out->action = bt_findfun(v);
}
return root;
}
int bt_run(bt_t *b) {
int rc = 0;
/**/ if( b->type == cc8( run) ) { return b->action ? b->action() : 0; }
else if( b->type == cc8( not) ) { return !bt_run(b->children + 0); }
else if( b->type == cc8( sleep) ) { return sleep_ss(b->argf), bt_run(b->children + 0); }
else if( b->type == cc8( defer) ) { rc = bt_run(b->children + 0); return sleep_ss(b->argf), rc; }
else if( b->type == cc8( loop) ) { int rc; for(int i = 0; i < b->argf; ++i) rc = bt_run(b->children + 0); return rc; }
else if( b->type == cc8( once) ) { return b->argf ? 0 : (b->argf = 1), bt_run(b->children + 0); }
else if( b->type == cc8( count) ) { return b->argf <= 0 ? 0 : --b->argf, bt_run(b->children + 0); }
else if( b->type == cc8( pass) ) { return bt_run(b->children + 0), 1; }
else if( b->type == cc8( fail) ) { return bt_run(b->children + 0), 0; }
else if( b->type == cc8( result) ) { return bt_run(b->children + 0), !!b->argf; }
else if( b->type == cc8( all) ) { for( int i = 0; i < array_count(b->children); ++i ) if(!bt_run(b->children+i)) return 0; return 1; }
else if( b->type == cc8( any) ) { for( int i = 0; i < array_count(b->children); ++i ) if( bt_run(b->children+i)) return 1; return 0; }
else if( b->type == cc8( root) ) { for( int i = 0; i < array_count(b->children); ++i ) rc|=bt_run(b->children+i); return rc; }
else if( b->type == cc8(rootfail) ) { rc = 1; for( int i = 0; i < array_count(b->children); ++i ) rc&=~bt_run(b->children+i); return rc; }
return 0;
}
void ui_bt(bt_t *b) {
if( b ) {
char *info = bt_funcname(b->action);
if(!info) info = va("%d", array_count(b->children));
if( ui_collapse(va("%s (%s)", cc8str(b->type), info), va("bt%p",b)) ) {
for( int i = 0; i < array_count(b->children); ++i) {
ui_bt(b->children + i);
}
ui_collapse_end();
}
}
}

263
demos/99-bt.c 100644
View File

@ -0,0 +1,263 @@
#define COOK_ON_DEMAND 1
#include "v4k.h"
#include "3rd_bt.h"
bool topdown_cam = 1;
int yes() {
return puts("yes"),1;
}
int no() {
return puts("no"),0;
}
int hi() {
return puts("hi"),1;
}
void move_players();
int main() {
window_create(75, 0); // WINDOW_MSAA4);
window_title(__FILE__);
bt_addfun("yes", yes);
bt_addfun("no", no);
bt_addfun("hi", hi);
bt_t b = {0}; // bt("bt.ini", 0);
// game loop
while( window_swap() ) {
if( input(KEY_LALT) && input_down(KEY_Z) ) window_record(__FILE__ ".mp4");
move_players();
if( input_down(MOUSE_R) ) topdown_cam ^= 1;
if( ui_panel("AI", 0) ) {
ui_bool("Top-down camera (RMB)", &topdown_cam);
int choice = ui_buttons(2, "BT Run", "BT Reload");
if(choice == 1) printf("rc:%d\n", bt_run(&b));
if(choice == 2) b = bt("bt1.ini", 0);
ui_separator();
ui_bt(&b);
ui_panel_end();
}
// font_print(va(FONT_TOP FONT_RIGHT "bt.node: %d bytes", (int)sizeof(bt_t)));
}
}
struct player_t {
const char *name;
model_t mdl;
anim_t idle, run; // anim clips
float keys[4], scale; // up,down,left,right
vec2 inertia; // [forward,yaw]
vec3 pivot, speed; // [pitch,yaw,roll] [turn speed,forward speed,anim speed fps]
vec3 pos, dir, pad; // [position] [facing dir] [gamepad accumulator]
bool notified;
float brain[4]; // AI
};
// every chunk is a 2D grid [200x200 units], center at (W/2,H/2), bit:1 means traversable
array(unsigned) chunk = 0; // chunk partition in world
array(aabb) blockers = 0; // static blockers in world
vec3 from, to; bool recast;
void draw_scene() {
enum { W = 200, Z = 200 };
do_once array_resize(chunk, W*Z);
enum { NUM_BLOCKS = 200 };
do_once
for( int i = 0; i < NUM_BLOCKS; ++i) {
float ox = randi(-W/2,W/2);
float oz = randi(-Z/2,Z/2);
float ww = randi(1,10);
float dd = randi(1,10);
float hh = randi(3,10);
aabb b = { vec3(ox,0,oz), vec3(ox+ww,hh,oz+dd) };
array_push(blockers, b);
for( int y = oz; y < (oz+dd); ++y )
for( int x = ox; x < (ox+ww); ++x )
chunk[ (x + W/2) + (y + Z/2) * W ] |= 1;
}
ddraw_color(BLUE);
for( int i = 0; i < NUM_BLOCKS; ++i) {
ddraw_aabb(blockers[i].min, blockers[i].max);
}
// debug traversal visualization
if( input(KEY_F1) ) {
ddraw_color(GREEN);
for( int z = 0; z < Z; ++z )
for( int x = 0; x < W; ++x )
if( !chunk[x+z*W] ) ddraw_cube( vec3(x-W/2-0.5,0,z-Z/2-0.5), 1 );
ddraw_color(RED);
for( int z = 0; z < Z; ++z )
for( int x = 0; x < W; ++x )
if( chunk[x+z*W] ) ddraw_cube( vec3(x-W/2-0.5,0,z-Z/2-0.5), 1 );
}
static vec2i path[W*Z] = {0};
static int path_count = 0;
static uint64_t taken = 0;
if( recast || input_down(KEY_SPACE) ) {
recast = 0;
taken =- time_ns();
path_count = pathfind_astar(W, Z, chunk, vec2i(clampf(from.x+W/2,0,W-1),clampf(from.z+Z/2,0,Z-1)), vec2i(clampf(to.x+W/2,0,W-1),clampf(to.z+Z/2,0,Z-1)), path, countof(path));
taken += time_ns();
}
font_print(va(FONT_TOP FONT_RIGHT "%d (%5.2fms)", path_count, taken / 1e6));
// path visualization
if( path_count ) {
ddraw_color(YELLOW); // wizard of oz
vec2i* p = path;
for( int i = 0; i < path_count; ++i, ++p ) {
ddraw_point(vec3(p->x-W/2,0,p->y-Z/2));
}
}
}
void move_players() {
static struct player_t player[3];
do_once
player[0] = (struct player_t)
{ "PLAYER-1", model("kgirls01.fbx", 0), loop(0,60,0.25,0), loop(66,85,0.25,0), // idle anim [0..60], run anim [66..85]
{KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT}, 2, {0.90,0.80}, {-100}, {3, 0.30, 30}, {0}, {1} },
player[1] = (struct player_t)
{ "PLAYER-2", model("george.fbx", 0), loop(0,100,0.25,0), loop(372,396,0.25,0), // idle anim [0..100], run anim [372..396]
{KEY_I,KEY_K,KEY_J,KEY_L}, 1, {0.95,0.90}, {-90,-90}, {1.75, 0.25, 24}, {-5}, {1} },
player[2] = (struct player_t)
{ "PLAYER-3", model("alien.fbx", 0), loop(110,208,0.25,0), loop(360,380,0.25,0), // idle anim [110..208], run anim [360..380]
{KEY_W,KEY_S,KEY_A,KEY_D}, 0.85, {0.85,0.75}, {-90,-90}, {3.5, 0.35, 60}, {5}, {1} };
static camera_t cam; do_once cam = camera();
static skybox_t sky; do_once sky = skybox("cubemaps/stardust", 0);
skybox_render(&sky, cam.proj, cam.view);
for( int i = 0; i < countof(player); ++i )
ddraw_position_dir(player[i].pos, player[i].dir, 1.0f);
ddraw_ground(0);
draw_scene();
ddraw_flush();
// move and render players
for( int i = 0; i < countof(player); ++i ) {
struct player_t *p = &player[i];
// capture inputs
p->brain[0] = input(p->keys[0]);
p->brain[1] = input(p->keys[1]);
p->brain[2] = input(p->keys[2]);
p->brain[3] = input(p->keys[3]);
// setup waypoints for PLAYER-1
static array(vec3) points;
if( input_down(MOUSE_L) && !ui_hover() ) {
vec3 pt = editor_pick(input(MOUSE_X), input(MOUSE_Y));
hit *h = ray_hit_plane(ray(cam.position, pt), plane(vec3(0,0,0),vec3(0,1,0)));
if(h) array_push(points, h->p), from = player[0].pos, to = *array_back(points), recast = 1;
}
// ddraw waypoints
ddraw_color(YELLOW);
for( int i = 1; i < array_count(points); ++i) ddraw_line(points[i-1],points[i]);
for( int i = 0; i < array_count(points); ++i) ddraw_circle(points[i], vec3(0,1,0), 1); // prism(points[i], 1, 0, vec3(0,1,0), 4);
ddraw_color(RED);
for( int i = 0; i < array_count(points); ++i) ddraw_point(points[i]);
ddraw_color(WHITE);
// move thru waypoints (PLAYER-1 only)
if( i == 0 && array_count(points) ) {
struct player_t *p = &player[i];
vec3 dst = points[0];
vec3 vector1 = norm3(vec3(p->dir.x,0,p->dir.z));
vec3 vector2 = norm3(sub3(dst,p->pos));
float angle = atan2(vector2.z, vector2.x) - atan2(vector1.z, vector1.x);
angle *= 180 / C_PI;
// range [0, 360)
// if (angle < 0) { angle += 2 * 180; }
// range (-180, 180]
if (angle > 180) { angle -= 2 * 180; }
else if (angle <= -180) { angle += 2 * 180; }
float dist = len3(sub3(p->pos, dst));
if(dist < 1) {
// goal
array_pop_front(points);
}
else {
if( dist < 10 && abs(angle) > 10 ) {
// spin only
p->brain[ angle < 0 ? 2 : 3 ] = 1;
}
else {
// spin
p->brain[ angle < 0 ? 2 : 3 ] = 1;
// move forward
p->brain[ 0 ] = 1;
}
}
}
// accumulate movement
float yaw = p->brain[2] - p->brain[3];
float fwd = p->brain[0] - p->brain[1]; if(fwd<0) fwd = 0;
p->pad.x = p->pad.x * (p->inertia.y) + yaw * (1-p->inertia.y);
p->pad.y = p->pad.y * (p->inertia.x) + fwd * (1-p->inertia.x);
// rotate yaw dir, then apply into position
p->dir = rotatey3(p->dir, p->speed.x * p->pad.x);
p->pos = add3(p->pos, scale3(p->dir, p->speed.y * p->pad.y));
// animate clips and blend
anim_t *primary = fwd ? &p->run : &p->idle;
anim_t *secondary = fwd ? &p->idle : &p->run;
model_animate_blends(p->mdl, primary, secondary, window_delta() * p->speed.z);
// render model. transforms on top of initial pivot and scale
mat44 M; compose44(M, p->pos, eulerq(add3(p->pivot,vec3(atan2(p->dir.z,p->dir.x)*180/C_PI,0,0))),vec3(p->scale,p->scale,p->scale));
model_render(p->mdl, cam.proj, cam.view, M, 0);
// ui
if( yaw||fwd ) if( !p->notified ) p->notified = 1, ui_notify(0, va(ICON_MD_GAMEPAD " %s joined the game.", p->name));
ddraw_text(p->pos, 0.01, va("%s: %6.3f", fwd?"run":"idle", (fwd ? p->run : p->idle).timer ));
}
static int reset_topdown_cam = 1;
if( topdown_cam ) {
if( reset_topdown_cam ) reset_topdown_cam = 0, camera_teleport(&cam, vec3(0,80,0)), camera_lookat(&cam, vec3(cam.position.x,0,cam.position.z+1));
// gta1 cam style: top-down view, look-at player, smoothed values. optional rotation
if(0) { static vec3 smooth1; camera_lookat(&cam, smooth1 = mix3(add3(player[0].pos,player[0].dir),smooth1,0.95)); }
if(1) { static vec3 smooth2; camera_teleport(&cam, smooth2 = mix3(add3(player[0].pos,vec3(0.1,80,0.1)),smooth2,0.95)); }
camera_fov(&cam, cam.fov * 0.99 + 60 * 0.01);
} else {
reset_topdown_cam = 1;
// look at the players that are moving; center of their triangle otherwise.
float A = len3(player[0].pad); if(A<0.01) A=0;
float B = len3(player[1].pad); if(B<0.01) B=0;
float C = len3(player[2].pad); if(C<0.01) C=0;
float weight = A + B + C;
if( weight ) A /= weight, B /= weight, C /= weight; else A = B = C = 0.33333;
// smooth target before sending to camera
vec3 target = add3(add3(scale3(player[0].pos,A), scale3(player[1].pos,B)), scale3(player[2].pos,C));
static vec3 smooth1; camera_lookat(&cam, smooth1 = mix3(target,smooth1,!weight ? 0.98 : 0.95));
target = vec3(10,10,10);
static vec3 smooth2; camera_teleport(&cam, smooth2 = mix3(target,smooth2,!weight ? 0.98 : 0.95));
camera_fov(&cam, cam.fov * 0.99 + 45 * 0.01);
}
// ui
if( ui_panel("Controls", 0) ) {
ui_label2("Girl", ICON_MD_MOUSE " Set Waypoint");
ui_label2("Girl", ICON_MD_GAMEPAD " CURSOR keys");
ui_label2("Alien", ICON_MD_GAMEPAD " W,A,S,D keys");
ui_label2("Robot", ICON_MD_GAMEPAD " I,J,K,L keys");
ui_panel_end();
}
}

View File

@ -35571,7 +35571,7 @@ int tls_remote_error(struct TLSContext *context);
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
*/ */
/******************************************************************************** /********************************************************************************
Copyright (c) 2016-2021, Eduard Suica Copyright (c) 2016-2023, Eduard Suica
All rights reserved. All rights reserved.
Redistribution and use in source and binary forms, with or without modification, Redistribution and use in source and binary forms, with or without modification,
@ -35681,13 +35681,13 @@ int tls_remote_error(struct TLSContext *context);
// #endif // #endif
#ifndef TLS_MALLOC #ifndef TLS_MALLOC
#define TLS_MALLOC(size) malloc(size) #define TLS_MALLOC MALLOC //< @r-lyeh
#endif #endif
#ifndef TLS_REALLOC #ifndef TLS_REALLOC
#define TLS_REALLOC(ptr, size) realloc(ptr, size) #define TLS_REALLOC REALLOC //< @r-lyeh
#endif #endif
#ifndef TLS_FREE #ifndef TLS_FREE
#define TLS_FREE(ptr) if (ptr) free(ptr) #define TLS_FREE FREE //< @r-lyeh
#endif #endif
#define TLS_ERROR(err, statement) if (err) statement; #define TLS_ERROR(err, statement) if (err) statement;
@ -42997,7 +42997,7 @@ int tls_parse_verify_tls13(struct TLSContext *context, const unsigned char *buf,
unsigned short signature = ntohs(*(unsigned short *)&buf[3]); unsigned short signature = ntohs(*(unsigned short *)&buf[3]);
unsigned short signature_size = ntohs(*(unsigned short *)&buf[5]); unsigned short signature_size = ntohs(*(unsigned short *)&buf[5]);
int valid = 0; int valid = 0;
CHECK_SIZE(7 + size, buf_len, TLS_NEED_MORE_DATA) CHECK_SIZE(7 + signature_size, buf_len, TLS_NEED_MORE_DATA)
switch (signature) { switch (signature) {
#ifdef TLS_ECDSA_SUPPORTED #ifdef TLS_ECDSA_SUPPORTED
case 0x0403: case 0x0403:
@ -46726,15 +46726,14 @@ https_status_t https_process( https_t* https )
#pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect #pragma warning( disable: 4548 ) // expression before comma has no effect; expected expression with side-effect
FD_SET( internal->socket, &sockets_to_check ); FD_SET( internal->socket, &sockets_to_check );
#pragma warning( pop ) #pragma warning( pop )
struct timeval timeout; timeout.tv_sec = 0; timeout.tv_usec = 0;
int buflen = 65536 * 1024;//< @r-lyeh heap allocated buffer struct timeval timeout;
static __thread char *buffer = 0; //< @r-lyeh heap allocated buffer while( (timeout.tv_sec = 1, timeout.tv_usec = 0), select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
if(!buffer) memset(buffer = (char*)HTTPS_MALLOC(internal->memctx, buflen), 0, buflen); //< @r-lyeh heap allocated buffer
while( select( (int)( internal->socket + 1 ), &sockets_to_check, NULL, NULL, &timeout ) == 1 )
{ {
enum { buflen = 4 * 1024 };
char buffer[ buflen ];
int size = recv( internal->socket, buffer, buflen, 0 ); int size = recv( internal->socket, buffer, buflen, 0 );
if( size == -1 ) if( size == -1 )
{ {
@ -46742,8 +46741,9 @@ https_status_t https_process( https_t* https )
return https->status; return https->status;
} }
#if is(tcc) && is(64) #if is(tcc) && is(win32)
// hexdump(buffer, size); //< @r-lyeh // hexdump(buffer, size); //< @r-lyeh
// printf(">+=%d,%d/%d\n", size,internal->data_size,internal->data_capacity);
#endif #endif
tls_consume_stream( internal->tls_context, (unsigned char*) buffer, size, 0 ); tls_consume_stream( internal->tls_context, (unsigned char*) buffer, size, 0 );
@ -46782,7 +46782,6 @@ https_status_t https_process( https_t* https )
else if( size == 0 ) else if( size == 0 )
{ {
char const* status_line = (char const*) internal->data; char const* status_line = (char const*) internal->data;
int header_size = 0; int header_size = 0;
char const* header_end = strstr( status_line, "\r\n\r\n" ); char const* header_end = strstr( status_line, "\r\n\r\n" );
if( header_end ) if( header_end )

View File

@ -713,7 +713,8 @@ struct thread_queue_t
} THREADNAME_INFO; } THREADNAME_INFO;
#pragma pack(pop) #pragma pack(pop)
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
#ifndef _GNU_SOURCE #ifndef _GNU_SOURCE
#define _GNU_SOURCE //< @r-lyeh: pthread_setname_np() #define _GNU_SOURCE //< @r-lyeh: pthread_setname_np()
#endif #endif
@ -737,7 +738,7 @@ thread_id_t thread_current_thread_id( void )
return (void*) (uintptr_t)GetCurrentThreadId(); return (void*) (uintptr_t)GetCurrentThreadId();
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return (void*) pthread_self(); return (void*) pthread_self();
@ -753,7 +754,7 @@ void thread_yield( void )
SwitchToThread(); SwitchToThread();
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
sched_yield(); sched_yield();
@ -769,7 +770,7 @@ void thread_exit( int return_code )
ExitThread( (DWORD) return_code ); ExitThread( (DWORD) return_code );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_exit( (void*)(uintptr_t) return_code ); pthread_exit( (void*)(uintptr_t) return_code );
@ -810,13 +811,13 @@ thread_ptr_t thread_init( int (*thread_proc)( void* ), void* user_data, char con
return (thread_ptr_t) handle; return (thread_ptr_t) handle;
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_t thread; pthread_t thread;
if( 0 != pthread_create( &thread, NULL, ( void* (*)( void * ) ) thread_proc, user_data ) ) if( 0 != pthread_create( &thread, NULL, ( void* (*)( void * ) ) thread_proc, user_data ) )
return NULL; return NULL;
#if !defined( __APPLE__ ) && !defined( __EMSCRIPTEN__ ) && !defined( EMSCRIPTEN ) // max doesn't support pthread_setname_np. alternatives? //< @r-lyeh, ems #if !defined( __APPLE__ ) && !defined( __EMSCRIPTEN__ ) // max doesn't support pthread_setname_np. alternatives? //< @r-lyeh, ems
if( name ) pthread_setname_np( thread, name ); if( name ) pthread_setname_np( thread, name );
#endif #endif
@ -835,7 +836,7 @@ void thread_term( thread_ptr_t thread )
WaitForSingleObject( (HANDLE) thread, INFINITE ); WaitForSingleObject( (HANDLE) thread, INFINITE );
CloseHandle( (HANDLE) thread ); CloseHandle( (HANDLE) thread );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_join( (pthread_t) thread, NULL ); pthread_join( (pthread_t) thread, NULL );
@ -854,7 +855,7 @@ int thread_join( thread_ptr_t thread )
GetExitCodeThread( (HANDLE) thread, &retval ); GetExitCodeThread( (HANDLE) thread, &retval );
return (int) retval; return (int) retval;
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
void* retval; void* retval;
pthread_join( (pthread_t) thread, &retval ); pthread_join( (pthread_t) thread, &retval );
@ -872,7 +873,7 @@ int thread_detach( thread_ptr_t thread )
return CloseHandle( (HANDLE) thread ) != 0; return CloseHandle( (HANDLE) thread ) != 0;
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return pthread_detach( (pthread_t) thread ) == 0; return pthread_detach( (pthread_t) thread ) == 0;
@ -888,7 +889,7 @@ void thread_set_high_priority( void )
SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST ); SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_HIGHEST );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
struct sched_param sp; struct sched_param sp;
memset( &sp, 0, sizeof( sp ) ); memset( &sp, 0, sizeof( sp ) );
@ -913,7 +914,7 @@ void thread_mutex_init( thread_mutex_t* mutex )
InitializeCriticalSectionAndSpinCount( (CRITICAL_SECTION*) mutex, 32 ); InitializeCriticalSectionAndSpinCount( (CRITICAL_SECTION*) mutex, 32 );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
// Compile-time size check // Compile-time size check
struct x { char thread_mutex_type_too_small : ( sizeof( thread_mutex_t ) < sizeof( pthread_mutex_t ) ? 0 : 1 ); }; struct x { char thread_mutex_type_too_small : ( sizeof( thread_mutex_t ) < sizeof( pthread_mutex_t ) ? 0 : 1 ); };
@ -932,7 +933,7 @@ void thread_mutex_term( thread_mutex_t* mutex )
DeleteCriticalSection( (CRITICAL_SECTION*) mutex ); DeleteCriticalSection( (CRITICAL_SECTION*) mutex );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_mutex_destroy( (pthread_mutex_t*) mutex ); pthread_mutex_destroy( (pthread_mutex_t*) mutex );
@ -948,7 +949,7 @@ void thread_mutex_lock( thread_mutex_t* mutex )
EnterCriticalSection( (CRITICAL_SECTION*) mutex ); EnterCriticalSection( (CRITICAL_SECTION*) mutex );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_mutex_lock( (pthread_mutex_t*) mutex ); pthread_mutex_lock( (pthread_mutex_t*) mutex );
@ -964,7 +965,7 @@ void thread_mutex_unlock( thread_mutex_t* mutex )
LeaveCriticalSection( (CRITICAL_SECTION*) mutex ); LeaveCriticalSection( (CRITICAL_SECTION*) mutex );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_mutex_unlock( (pthread_mutex_t*) mutex ); pthread_mutex_unlock( (pthread_mutex_t*) mutex );
@ -986,7 +987,7 @@ struct thread_internal_signal_t
HANDLE event; HANDLE event;
#endif #endif
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_mutex_t mutex; pthread_mutex_t mutex;
pthread_cond_t condition; pthread_cond_t condition;
@ -1018,7 +1019,7 @@ void thread_signal_init( thread_signal_t* signal )
internal->event = CreateEvent( NULL, FALSE, FALSE, NULL ); internal->event = CreateEvent( NULL, FALSE, FALSE, NULL );
#endif #endif
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_mutex_init( &internal->mutex, NULL ); pthread_mutex_init( &internal->mutex, NULL );
pthread_cond_init( &internal->condition, NULL ); pthread_cond_init( &internal->condition, NULL );
@ -1042,7 +1043,7 @@ void thread_signal_init( thread_signal_t* signal )
CloseHandle( internal->event ); CloseHandle( internal->event );
#endif #endif
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_mutex_destroy( &internal->mutex ); pthread_mutex_destroy( &internal->mutex );
pthread_cond_destroy( &internal->condition ); pthread_cond_destroy( &internal->condition );
@ -1068,7 +1069,7 @@ void thread_signal_raise( thread_signal_t* signal )
SetEvent( internal->event ); SetEvent( internal->event );
#endif #endif
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_mutex_lock( &internal->mutex ); pthread_mutex_lock( &internal->mutex );
internal->value = 1; internal->value = 1;
@ -1103,7 +1104,7 @@ int thread_signal_wait( thread_signal_t* signal, int timeout_ms )
return !failed; return !failed;
#endif #endif
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
struct timespec ts; struct timespec ts;
if( timeout_ms >= 0 ) if( timeout_ms >= 0 )
@ -1147,7 +1148,7 @@ int thread_atomic_int_load( thread_atomic_int_t* atomic )
return InterlockedCompareExchange( &atomic->i, 0, 0 ); return InterlockedCompareExchange( &atomic->i, 0, 0 );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return (int)__sync_fetch_and_add( &atomic->i, 0 ); return (int)__sync_fetch_and_add( &atomic->i, 0 );
@ -1163,7 +1164,7 @@ void thread_atomic_int_store( thread_atomic_int_t* atomic, int desired )
InterlockedExchange( &atomic->i, desired ); InterlockedExchange( &atomic->i, desired );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
__sync_fetch_and_and( &atomic->i, 0 ); __sync_fetch_and_and( &atomic->i, 0 );
__sync_fetch_and_or( &atomic->i, desired ); __sync_fetch_and_or( &atomic->i, desired );
@ -1180,7 +1181,7 @@ int thread_atomic_int_inc( thread_atomic_int_t* atomic )
return InterlockedIncrement( &atomic->i ) - 1; return InterlockedIncrement( &atomic->i ) - 1;
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return (int)__sync_fetch_and_add( &atomic->i, 1 ); return (int)__sync_fetch_and_add( &atomic->i, 1 );
@ -1196,7 +1197,7 @@ int thread_atomic_int_dec( thread_atomic_int_t* atomic )
return InterlockedDecrement( &atomic->i ) + 1; return InterlockedDecrement( &atomic->i ) + 1;
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return (int)__sync_fetch_and_sub( &atomic->i, 1 ); return (int)__sync_fetch_and_sub( &atomic->i, 1 );
@ -1212,7 +1213,7 @@ int thread_atomic_int_add( thread_atomic_int_t* atomic, int value )
return InterlockedExchangeAdd ( &atomic->i, value ); return InterlockedExchangeAdd ( &atomic->i, value );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return (int)__sync_fetch_and_add( &atomic->i, value ); return (int)__sync_fetch_and_add( &atomic->i, value );
@ -1228,7 +1229,7 @@ int thread_atomic_int_sub( thread_atomic_int_t* atomic, int value )
return InterlockedExchangeAdd( &atomic->i, -value ); return InterlockedExchangeAdd( &atomic->i, -value );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return (int)__sync_fetch_and_sub( &atomic->i, value ); return (int)__sync_fetch_and_sub( &atomic->i, value );
@ -1244,7 +1245,7 @@ int thread_atomic_int_swap( thread_atomic_int_t* atomic, int desired )
return InterlockedExchange( &atomic->i, desired ); return InterlockedExchange( &atomic->i, desired );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
int old = (int)__sync_lock_test_and_set( &atomic->i, desired ); int old = (int)__sync_lock_test_and_set( &atomic->i, desired );
__sync_lock_release( &atomic->i ); __sync_lock_release( &atomic->i );
@ -1262,7 +1263,7 @@ int thread_atomic_int_compare_and_swap( thread_atomic_int_t* atomic, int expecte
return InterlockedCompareExchange( &atomic->i, desired, expected ); return InterlockedCompareExchange( &atomic->i, desired, expected );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return (int)__sync_val_compare_and_swap( &atomic->i, expected, desired ); return (int)__sync_val_compare_and_swap( &atomic->i, expected, desired );
@ -1278,7 +1279,7 @@ void* thread_atomic_ptr_load( thread_atomic_ptr_t* atomic )
return InterlockedCompareExchangePointer( &atomic->ptr, 0, 0 ); return InterlockedCompareExchangePointer( &atomic->ptr, 0, 0 );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return __sync_fetch_and_add( &atomic->ptr, 0 ); return __sync_fetch_and_add( &atomic->ptr, 0 );
@ -1300,7 +1301,7 @@ void thread_atomic_ptr_store( thread_atomic_ptr_t* atomic, void* desired )
#pragma warning( pop ) #pragma warning( pop )
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
__sync_lock_test_and_set( &atomic->ptr, desired ); __sync_lock_test_and_set( &atomic->ptr, desired );
__sync_lock_release( &atomic->ptr ); __sync_lock_release( &atomic->ptr );
@ -1322,7 +1323,7 @@ void* thread_atomic_ptr_swap( thread_atomic_ptr_t* atomic, void* desired )
return InterlockedExchangePointer( &atomic->ptr, desired ); return InterlockedExchangePointer( &atomic->ptr, desired );
#pragma warning( pop ) #pragma warning( pop )
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
void* old = __sync_lock_test_and_set( &atomic->ptr, desired ); void* old = __sync_lock_test_and_set( &atomic->ptr, desired );
__sync_lock_release( &atomic->ptr ); __sync_lock_release( &atomic->ptr );
@ -1340,7 +1341,7 @@ void* thread_atomic_ptr_compare_and_swap( thread_atomic_ptr_t* atomic, void* exp
return InterlockedCompareExchangePointer( &atomic->ptr, desired, expected ); return InterlockedCompareExchangePointer( &atomic->ptr, desired, expected );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return __sync_val_compare_and_swap( &atomic->ptr, expected, desired ); return __sync_val_compare_and_swap( &atomic->ptr, expected, desired );
@ -1367,7 +1368,7 @@ void thread_timer_init( thread_timer_t* timer )
*(HANDLE*)timer = CreateWaitableTimer( NULL, TRUE, NULL ); *(HANDLE*)timer = CreateWaitableTimer( NULL, TRUE, NULL );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
// Nothing // Nothing
@ -1387,7 +1388,7 @@ void thread_timer_term( thread_timer_t* timer )
if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR ) if( timeGetDevCaps( &tc, sizeof( TIMECAPS ) ) == TIMERR_NOERROR )
timeEndPeriod( tc.wPeriodMin ); timeEndPeriod( tc.wPeriodMin );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
// Nothing // Nothing
@ -1407,7 +1408,7 @@ void thread_timer_wait( thread_timer_t* timer, THREAD_U64 nanoseconds )
(void) b; (void) b;
WaitForSingleObject( *(HANDLE*)timer, INFINITE ); WaitForSingleObject( *(HANDLE*)timer, INFINITE );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
struct timespec rem; struct timespec rem;
struct timespec req; struct timespec req;
@ -1432,7 +1433,7 @@ thread_tls_t thread_tls_create( void )
else else
return (thread_tls_t) (uintptr_t) tls; return (thread_tls_t) (uintptr_t) tls;
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_key_t tls; pthread_key_t tls;
if( pthread_key_create( &tls, NULL ) == 0 ) if( pthread_key_create( &tls, NULL ) == 0 )
@ -1452,7 +1453,7 @@ void thread_tls_destroy( thread_tls_t tls )
TlsFree( (DWORD) (uintptr_t) tls ); TlsFree( (DWORD) (uintptr_t) tls );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_key_delete( (pthread_key_t) (uintptr_t) tls ); //< @r-lyeh: uintptr_t pthread_key_delete( (pthread_key_t) (uintptr_t) tls ); //< @r-lyeh: uintptr_t
@ -1468,7 +1469,7 @@ void thread_tls_set( thread_tls_t tls, void* value )
TlsSetValue( (DWORD) (uintptr_t) tls, value ); TlsSetValue( (DWORD) (uintptr_t) tls, value );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
pthread_setspecific( (pthread_key_t) (uintptr_t) tls, value ); //< @r-lyeh: uintptr_t pthread_setspecific( (pthread_key_t) (uintptr_t) tls, value ); //< @r-lyeh: uintptr_t
@ -1484,7 +1485,7 @@ void* thread_tls_get( thread_tls_t tls )
return TlsGetValue( (DWORD) (uintptr_t) tls ); return TlsGetValue( (DWORD) (uintptr_t) tls );
#elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) || defined( EMSCRIPTEN ) //< @r-lyeh, ems #elif defined( __linux__ ) || defined( __APPLE__ ) || defined( __ANDROID__ ) || defined( __EMSCRIPTEN__ ) //< @r-lyeh, ems
return pthread_getspecific( (pthread_key_t) (uintptr_t) tls ); //< @r-lyeh: uintptr_t return pthread_getspecific( (pthread_key_t) (uintptr_t) tls ); //< @r-lyeh: uintptr_t