Compare commits
432 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 45095191f4 | |||
| e44a3cd98b | |||
| fff3601041 | |||
| 1639de5d0f | |||
| 59462df9a6 | |||
| 72733394ac | |||
| dc9cee5a3e | |||
| 71ab702a75 | |||
| 1bb7410210 | |||
| ec5bf74009 | |||
| 7ea6873c1f | |||
| 8c3d66a7b1 | |||
| f5e95b8c9f | |||
| 2f11a80a28 | |||
| 8b6b56164a | |||
| bbc83acc0f | |||
|
|
0d6a9f92ad | ||
|
|
4cf80517eb | ||
| 35a86a6cda | |||
| 019855d81f | |||
| 2a47de9fd8 | |||
| 0d3de6e31d | |||
| d6d4934a7e | |||
| 6369450d2f | |||
| a90b1d09b1 | |||
| 92090f0a0a | |||
| 5cfd155241 | |||
| 0b2b2f9613 | |||
| a5b59ee601 | |||
| f240588edd | |||
| 6abfb9c3d9 | |||
| e5ac4d3d9e | |||
| 7353fde799 | |||
| 4c088b9080 | |||
| b727fb15b5 | |||
| 57b4871a85 | |||
| 3ae2acbd78 | |||
| df429b617e | |||
| 0d3a7b9393 | |||
| 6de93c10af | |||
| a14cbb45d3 | |||
| 827d3d8c45 | |||
| 7bbb217018 | |||
| 62e05788d2 | |||
| 772095431f | |||
| bb7fab0857 | |||
| 1546076c80 | |||
| 8f3eb6d9a3 | |||
| ccc4a15121 | |||
| 9050e65d93 | |||
| 8f7183f998 | |||
| 3d07e774a0 | |||
| 99ba411781 | |||
| 23eaf42cad | |||
| 5bca4e3383 | |||
|
|
e2bb964423 | ||
|
|
0e2119a87d | ||
|
|
7dfbf16bc1 | ||
|
|
bac91574a2 | ||
| bf2f495812 | |||
| beddecc742 | |||
| 86f31bbe52 | |||
| d8f3db6972 | |||
|
|
494baf1e32 | ||
| e14fa83681 | |||
|
|
278b982aff | ||
|
|
89e76ce31d | ||
|
|
831abeaccf | ||
|
|
f74f85759b | ||
| 7b70838567 | |||
| fcc0717115 | |||
| 47d8ac30c4 | |||
| 3646a1a074 | |||
| b0ab40d410 | |||
| 440995b1c8 | |||
| 8da4c4c1de | |||
| 2cc2a57a46 | |||
| 6527dc1aaa | |||
| bd49805c25 | |||
| 40dd293bf8 | |||
| 568c95fa73 | |||
| d30ee3788e | |||
| 79f58a79f9 | |||
| d8aeefd202 | |||
| c0fb94ddbb | |||
| e8a3cd731d | |||
| 03ffb6bc0a | |||
| 4240737e00 | |||
| 5172dcdf01 | |||
| fc18a303dd | |||
| 04d5500374 | |||
| 72e24e8a9b | |||
| 48bba84c08 | |||
| e9a0e7bbf7 | |||
| 264867a0da | |||
| 3f046afaa1 | |||
| 2acce5001d | |||
| 78cd15681a | |||
| 3401a8ac61 | |||
| 19ac257204 | |||
| c14a469069 | |||
| 03cea2d71a | |||
| 593bfd0d8c | |||
| bf60a354d4 | |||
| 6c73220a52 | |||
| c3a9ddfdbd | |||
| e05f2ea400 | |||
| a60aae02c6 | |||
| 207c5b8d69 | |||
| a9111f2fa3 | |||
| b572bf218d | |||
| 49c0bd3930 | |||
| cdb4bfda11 | |||
| 3fbead60d9 | |||
| 2b122f5c77 | |||
| 472d1748ee | |||
| 47178dd3b3 | |||
| 3fcba8825b | |||
| e194a06427 | |||
| 32f7b4492b | |||
| 913745a1bc | |||
| 889dc6ca68 | |||
| 2805137648 | |||
| f7167cf972 | |||
| 9e1ecb3583 | |||
| b9b9b51d12 | |||
| a708fb3f83 | |||
| 4335f3ed14 | |||
| 8580d92c9c | |||
| f1f6e30a98 | |||
| 6c1e9f26f0 | |||
| d98f353936 | |||
| 14c8665910 | |||
| f7452db3ec | |||
| 588923e1b7 | |||
| 4877f64ca1 | |||
| b8b31253d4 | |||
| af4672245e | |||
| 609613dbe9 | |||
| b16593425b | |||
| 997ea48b54 | |||
| 537dcf0db1 | |||
| b74a6c006e | |||
| b3453d028c | |||
| 6842147522 | |||
| de34b9dc59 | |||
| dff19d865f | |||
| 544aa78d70 | |||
| 91d0b5f7be | |||
| 27e03a2546 | |||
| 7dbcd01c33 | |||
| cc20914391 | |||
| c2aa449a65 | |||
| 57563688ae | |||
| 317dacd0de | |||
| a7ba146a64 | |||
| efa30e30cc | |||
| 197b418fc5 | |||
| d3d53599f7 | |||
| bf517facd1 | |||
| 9cff2735ba | |||
| ea77b934b6 | |||
| 74bfd571a5 | |||
| 06132fc1dd | |||
| ade5172f26 | |||
| bdc657771d | |||
| 8c24bcbbb0 | |||
| 804a117ed0 | |||
| 37afd37f9e | |||
| 4399fb6424 | |||
| 02a54e13ea | |||
| bb2c7d6440 | |||
| d1b8ed1922 | |||
| 9992a2c091 | |||
| 155466dc71 | |||
| cea6508d68 | |||
| 726707eb8a | |||
| 9a9d28f62f | |||
| ed924a3d9d | |||
| 2d239c65ea | |||
| fd8e77555a | |||
| b60aab9602 | |||
| 180176e2cf | |||
| 952b592b97 | |||
| d2ce7e070b | |||
| 300827b7bd | |||
| 9d97eac146 | |||
| 61dcb9da01 | |||
| b597fbdf9b | |||
| 8fbaa76fe9 | |||
| abb3da416f | |||
| 801c9e71d8 | |||
| 8ba489dc58 | |||
| 6039d6bf29 | |||
| 5ef2e0610c | |||
| dee687f4b1 | |||
| 2d281fae90 | |||
| 5d363f4fa7 | |||
| 7e506f79c9 | |||
| aeb88e4bfc | |||
| b44c46b953 | |||
| ddd365c5b8 | |||
| 68ac49fad3 | |||
| 5da0be08af | |||
| 2a55b044ef | |||
| 8063aa432c | |||
| 363fd4e639 | |||
| ff7f46393f | |||
| 08831e1dcb | |||
| b251758790 | |||
| 696891bded | |||
| 3a2bb0c737 | |||
| f5b9f093d6 | |||
| 16b0354c42 | |||
| 5b401ef455 | |||
| 87467afd17 | |||
| f737516009 | |||
| 3f147443bc | |||
| 8bd1e6d3f2 | |||
| 00a3c6731c | |||
| f47a750ef9 | |||
| 0d23aab726 | |||
| 500734d0db | |||
| 1f38b4dcb6 | |||
| 9fc89c6f5f | |||
| 56bce25519 | |||
| e1446c009d | |||
| 4384dbbaee | |||
| 8f5973343d | |||
| ebe87ed69a | |||
| ead2fd6f2c | |||
| cf6cdcfc6e | |||
| 4578c816d3 | |||
| 5435ee485f | |||
| 265777be4e | |||
| ce9cc7f3a2 | |||
| 5cceaef837 | |||
| aff459ef68 | |||
| ef151893ab | |||
| d797875317 | |||
| ec8c8c7074 | |||
| 927e45db77 | |||
| 173ad37c9b | |||
| 91abd762cb | |||
| a5b71985a6 | |||
| 1926d3e26c | |||
| e9e2096999 | |||
| 0446dd0bea | |||
| 2521a90712 | |||
| 06574d2d45 | |||
| aaff2244b1 | |||
| 6dbf2f5e93 | |||
| c251567804 | |||
| 26245032f7 | |||
| 64cd56d633 | |||
| 46029e3d60 | |||
| b9928ea8a0 | |||
| ab8f227f71 | |||
| 8484065479 | |||
| 504186a852 | |||
| bcb7e97184 | |||
| af88b2a054 | |||
| 05456fa25e | |||
| 48038429d0 | |||
| 91090e6dea | |||
| b3b9e12fb8 | |||
| 19de228834 | |||
| 1984c167d0 | |||
| a1d4d59c57 | |||
| 5377e3131f | |||
| 9c74f6c265 | |||
| 71b56023bb | |||
| 3ea801a2c4 | |||
| ae9f76d938 | |||
| c2488ed5ce | |||
| aa92282b8a | |||
| 689133e71f | |||
| 296d56d233 | |||
| 7dae5d5e32 | |||
| 29c8282efb | |||
| 498aa1f9aa | |||
| 3e795ede05 | |||
| 0476164cf6 | |||
| 564db18933 | |||
| 3bbd63ba62 | |||
| 7126e28530 | |||
| 11cfb390bb | |||
| afee0ace94 | |||
| 8afef44252 | |||
| 31f1e10cfc | |||
| 98d06ee027 | |||
| 87d8cfbb5b | |||
| 050549aeef | |||
| 5a05cb7525 | |||
| da259bc99d | |||
| 6714878ae9 | |||
| 2fad69a1ec | |||
| 391625674a | |||
| 4272397678 | |||
| 8f0f404c36 | |||
| 11cd2805c8 | |||
| 28a4e37c51 | |||
| 20647f4738 | |||
| 0fd604e356 | |||
| 03c8f364b2 | |||
| 41725dc01f | |||
| b5bc0f8e91 | |||
| 110055ffc4 | |||
| 4040e0d9d0 | |||
| 0abd8183b5 | |||
| 9b5acbd23e | |||
| 040d8842a2 | |||
| 72dcc95400 | |||
| 86845f4230 | |||
| dfc75fa56c | |||
| 358779c793 | |||
| c382eb46dc | |||
| 4bccb655b6 | |||
| 515f037c0a | |||
| d1f8bc9492 | |||
| 29cc0b8935 | |||
| 7eb9cd37e9 | |||
| eebd942555 | |||
| 8db42e19f9 | |||
| 21aed09d6d | |||
| 76dbf26793 | |||
| 9b67727a01 | |||
| 31e51ce3b3 | |||
| bbce42f0aa | |||
| d1507219ff | |||
| 5e8c50082c | |||
| 88dbb867a7 | |||
| dce6ea201e | |||
| fae39b79e7 | |||
| 2447526a12 | |||
| d4f41c5aa7 | |||
| aec5a88360 | |||
| fdfb3a6388 | |||
| 291c30a7ac | |||
| c88ad2e73b | |||
| cd3aae9c1e | |||
| 3a46c4ae44 | |||
| 979a6d8674 | |||
| 138d61de85 | |||
| 644044901d | |||
| de2b3ee100 | |||
| 02cdbc3bc2 | |||
| c7734a2afa | |||
| 3a7aea4131 | |||
| 9c3e66e9cc | |||
| 89b4901e06 | |||
| a07fe49880 | |||
| 67499cc5ba | |||
| e32720b687 | |||
| 509086295c | |||
| 6c559b0e52 | |||
| 03d632c3da | |||
| 641845a0fa | |||
| 181723e1c7 | |||
| fc4d53a4cc | |||
| 285fa7e933 | |||
| 842274e663 | |||
| 6d57c7654b | |||
| eaaf5d80a7 | |||
| ef1cf06416 | |||
| e0c72cb8ea | |||
| 854862f2c8 | |||
| 506e8eeecd | |||
| f466f80513 | |||
| 584e8be3a2 | |||
| 0203d384f0 | |||
| 209f833b3f | |||
| 01b5f865f9 | |||
| 112e20f788 | |||
| 367982898a | |||
| fe123f879e | |||
| 03c91d84e3 | |||
| 6863704352 | |||
| 8557f2d3f8 | |||
| e396c18b80 | |||
| d7e8231203 | |||
| 943afd438a | |||
| 6f18a129f1 | |||
| dcb98ab66b | |||
| a414def307 | |||
| 4c3c09a109 | |||
| ac1dd5f79b | |||
| 8997c7a301 | |||
| f339b7bc22 | |||
| 8b9fe6070b | |||
| 7601fc636d | |||
| e7d82d5681 | |||
| 38f8234d20 | |||
| 1635a1633c | |||
| 561a5466e0 | |||
| 4761475845 | |||
| fd39b09dd3 | |||
| 699744e16b | |||
| 1b9972d1b7 | |||
| 34853f7fb6 | |||
| 361012b23b | |||
| bf5078f0bc | |||
| cf22651949 | |||
| f34b200979 | |||
| ca1d05899e | |||
| 817c419930 | |||
| 97cbc628a9 | |||
| 66b96cf567 | |||
| f1c32efb8b | |||
| 5209ef20a7 | |||
| 04528ef56c | |||
| 2116ed34e5 | |||
| 4cbb4b0982 | |||
| bb3c814325 | |||
| 74cc5ffde8 | |||
| de4de84d2e | |||
| 0f7b5fc6af | |||
| 6b980b52a5 | |||
| 8cf45ac5dd | |||
| 81a88e5b89 | |||
| 5b95dbec38 | |||
| 0968219926 | |||
| 11cdb91702 | |||
| 0356875f3c | |||
| 34ec6b25f9 | |||
| 5edea3a6a4 | |||
| f072c2cfde | |||
| bf011646f7 | |||
| 091cd90a0d | |||
| acbfef10cc | |||
| aad031062f | |||
| 3ff72de1e0 |
25
.github/workflows/love.yml
vendored
Normal file
25
.github/workflows/love.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
name: Build & Run tests Love2d
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install love2d
|
||||
run: |
|
||||
sudo apt install fuse
|
||||
wget https://github.com/love2d/love/releases/download/11.4/love-11.4-x86_64.AppImage -O love.AppImage
|
||||
sudo chmod +x love.AppImage
|
||||
- name: Run Tests
|
||||
run: |
|
||||
./love.AppImage tests
|
||||
41
.github/workflows/nix_ci.yml
vendored
Normal file
41
.github/workflows/nix_ci.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: Build & Run tests Ubuntu
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
build-type: [Release] # Debug
|
||||
lua: ["lua 5.1", "lua 5.2", "lua 5.3", "lua 5.4", "luajit 2.1.0-beta3"]
|
||||
os: ["ubuntu-latest"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Setup env
|
||||
run: |
|
||||
pip install hererocks
|
||||
hererocks lua-pkg --${{ matrix.lua }} -rlatest
|
||||
|
||||
- name: Install lanes and multi
|
||||
run: |
|
||||
source ${{github.workspace}}/lua-pkg/bin/activate
|
||||
luarocks install lanes
|
||||
luarocks install rockspecs/multi-16.0-0.rockspec
|
||||
|
||||
- name: Run Tests
|
||||
run: |
|
||||
source ${{github.workspace}}/lua-pkg/bin/activate
|
||||
lua tests/runtests.lua
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.code-workspace
|
||||
lua5.4/*
|
||||
test.lua
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
26
NetworkManager.md
Normal file
26
NetworkManager.md
Normal file
@ -0,0 +1,26 @@
|
||||
# NTHREAD Namespace
|
||||
- [ ] NTHREAD.set(name, val)
|
||||
- [ ] NTHREAD.get(name, val)
|
||||
- [ ] NTHREAD.waitFor(name)
|
||||
- [ ] NTHREAD.getCores()*
|
||||
- [ ] NTHREAD.getConsole()
|
||||
- [ ] NTHREAD.getThreads()
|
||||
- [ ] NTHREAD.kill()
|
||||
- [ ] NTHREAD.getName()
|
||||
- [ ] NTHREAD.getID()
|
||||
- [ ] NTHREAD.pushStatus(...)
|
||||
- [ ] NTHREAD.sleep(n)
|
||||
- [ ] NTHREAD.hold(n)
|
||||
- [ ] NTHREAD.setENV(env)
|
||||
- [ ] NTHREAD.getENV()
|
||||
|
||||
# Extensions
|
||||
- [ ] multi:newNetworkThreadedQueue(name)
|
||||
- [ ] multi:newNetworkThreadedTable(name)
|
||||
- [ ] multi:newNetworkThreadedJobQueue(n)
|
||||
- [ ] multi:newNetworkThreadedConnection(name)
|
||||
|
||||
# Core
|
||||
- [ ] NTHREAD:newFunction(func, holdme)
|
||||
- [ ] NTHREAD:newNetworkThread(name, func, ...)
|
||||
- [ ] mulit:newNetworkThread(name, func, ...)
|
||||
810
README.md
810
README.md
@ -1,756 +1,94 @@
|
||||
# multi Version: 1.5.0
|
||||
Updated from 1.4.1
|
||||
Added:
|
||||
- An easy way to manage timeouts
|
||||
- Small bug fixes
|
||||
# Multi Version: 16.0.1 - Bug fix
|
||||
|
||||
Example at end of the readme
|
||||
Found an issue? Please [submit it](https://github.com/rayaman/multi/issues) and someone will look into it!
|
||||
|
||||
My multitasking library for lua</br>
|
||||
To install copy the multi folder into your enviroment and you are good to go</br>
|
||||
My multitasking library for lua. It is a pure lua binding, with exceptions of the integrations.
|
||||
|
||||
It is a pure lua binding if you ingore the intergrations (WIP)</br>
|
||||
</br>
|
||||
|
||||
If you find any bugs or have any issues please let me know :)
|
||||
Progress is being made in [v16.1.0](https://github.com/rayaman/multi/tree/v16.1.0)
|
||||
---
|
||||
|
||||
Also I will eventually add an example folder with a lot of examples for how you can use this library. Don't konw when that will be though :P
|
||||
</br>
|
||||
|
||||
# Discord
|
||||
For real-time assistance with my libraries! A place where you can ask questions and get help with any of my libraries</br>
|
||||
https://discord.gg/U8UspuA</br>
|
||||
INSTALLING
|
||||
----------
|
||||
Link to optional dependencies:
|
||||
- [lanes](https://github.com/LuaLanes/lanes) `luarocks install lanes`
|
||||
|
||||
Usage:</br>
|
||||
```lua
|
||||
--Basic usage
|
||||
require("multi.all") -- gets the entire library
|
||||
alarm=multi:newAlarm(3) -- in seconds can go to .001 uses the built in os.clock()
|
||||
alarm:OnRing(function(a)
|
||||
print("3 Seconds have passed!")
|
||||
a:Reset(n) -- if n were nil it will reset back to 3, or it would reset to n seconds
|
||||
end)
|
||||
multi:mainloop() -- the main loop of the program, multi:umanager() exists as well to allow intergration in other loops Ex: love2d love.update function. More on this binding in the wiki!
|
||||
```
|
||||
The library is modular so you only need to require what you need to. Because of this, the global enviroment is altered</br>
|
||||
- [chronos](https://github.com/ldrumm/chronos) `luarocks install chronos`
|
||||
|
||||
There are many useful objects that you can use</br>
|
||||
Check out the wiki for detailed usage, but here are the objects:</br>
|
||||
- Process#</br>
|
||||
- Queuer#</br>
|
||||
- Alarm</br>
|
||||
- Loop</br>
|
||||
- Event</br>
|
||||
- Step</br>
|
||||
- Range</br>
|
||||
- TStep</br>
|
||||
- TLoop</br>
|
||||
- Condition</br>
|
||||
- Connection</br>
|
||||
- Timer</br>
|
||||
- Updater</br>
|
||||
- Thread*</br>
|
||||
- Trigger**</br>
|
||||
- Task</br>
|
||||
- Job</br>
|
||||
- Function</br>
|
||||
- Watcher***</br>
|
||||
#Both a process and queue act like the multi namespace, but allows for some cool things. Because they use the other objects an example on them will be done last</br>
|
||||
*Uses the built in coroutine features of lua, these have an interesting interaction with the other means of multi-tasking</br>
|
||||
**Triggers are kind of useless after the creation of the Connection</br>
|
||||
***Watchers have no real purpose as well I made it just because.</br>
|
||||
- [love2d](https://love2d.org/)
|
||||
|
||||
# Examples of each object being used</br>
|
||||
We already showed alarms in action so lets move on to a Loop object
|
||||
When using love2d add multi:uManager() or any processor to love.update()
|
||||
|
||||
Throughout these examples I am going to do some strange things in order to show other features of the library!
|
||||
|
||||
# LOOPS
|
||||
```lua
|
||||
-- Loops
|
||||
require("multi.all") -- gets the entire library
|
||||
count=0
|
||||
loop=multi:newLoop(function(dt,self) -- dt is delta time and self is a reference to itself
|
||||
count=count+1
|
||||
if count > 10 then
|
||||
self:Break() -- All methods on the multi objects are upper camel case, where as methods on the multi or process/queuer namespace are lower camel case
|
||||
-- self:Break() will stop the loop and trigger the OnBreak(func) method
|
||||
-- Stopping is the act of Pausing and deactivating the object! All objects can have the multiobj:Break() command on it!
|
||||
else
|
||||
print("Loop #"..count.."!")
|
||||
end
|
||||
end)
|
||||
loop:OnBreak(function(self)
|
||||
print("You broke me :(")
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
Loop #1!</br>
|
||||
Loop #2!</br>
|
||||
Loop #3!</br>
|
||||
Loop #4!</br>
|
||||
Loop #5!</br>
|
||||
Loop #6!</br>
|
||||
Loop #7!</br>
|
||||
Loop #8!</br>
|
||||
Loop #9!</br>
|
||||
Loop #10!</br>
|
||||
You broke me :(</br>
|
||||
|
||||
|
||||
With loops out of the way lets go down the line
|
||||
|
||||
This library aims to be Async like. In reality everything is still on one thread *unless you are using the lanes intergration module WIP* (More on that later)
|
||||
|
||||
# EVENTS
|
||||
```lua
|
||||
-- Events, these were the first objects introduced into the library. I seldomly use them in their pure form though, but later on you'll see their advance uses!
|
||||
-- Events on there own don't really do much... We are going to need 2 objects at least to get something going
|
||||
require("multi.all") -- gets the entire library
|
||||
count=0
|
||||
-- lets use the loop again to add to count!
|
||||
loop=multi:newLoop(function(dt,self)
|
||||
count=count+1
|
||||
end)
|
||||
event=multi:newEvent(function() return count==100 end) -- set the event
|
||||
event:OnEvent(function(self) -- connect to the event object
|
||||
loop:Pause() -- pauses the loop from running!
|
||||
print("Stopped that loop!")
|
||||
end) -- events like alarms need to be reset the Reset() command works here as well
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
Stopped that loop!
|
||||
|
||||
# STEPS
|
||||
```lua
|
||||
require("multi.all")
|
||||
-- Steps, are like for loops but non blocking... You can run a loop to infintity and everything will still run I will combine Steps with Ranges in this example.
|
||||
step1=multi:newStep(1,10,1,0) -- Some explaining is due. Argument 1 is the Start # Argument 2 is the ResetAt # (inclusive) Argument 3 is the count # (in our case we are counting by +1, this can be -1 but you need to adjust your start and resetAt numbers)
|
||||
-- The 4th Argument is for skipping. This is useful for timing and for basic priority management. A priority management system is included!
|
||||
step2=multi:newStep(10,1,-1,1) -- a second step, notice the slight changes!
|
||||
step1:OnStart(function(self)
|
||||
print("Step Started!")
|
||||
end)
|
||||
step1:OnStep(function(pos,self)
|
||||
if pos<=10 then -- what what is this? the step only goes to 10!!!
|
||||
print("Stepping... "..pos)
|
||||
else
|
||||
print("How did I get here?")
|
||||
```lua
|
||||
function love.update(dt)
|
||||
multi:uManager()
|
||||
end
|
||||
end)
|
||||
step1:OnEnd(function(self)
|
||||
print("Done!")
|
||||
-- We finished here, but I feel like we could have reused this step in some way... Yeah I soule Reset() it, but what if i wanted to change it...
|
||||
if self.endAt==10 then -- lets only loop once
|
||||
self:Update(1,11,1,0) -- oh now we can reach that else condition!
|
||||
end
|
||||
-- Note Update() will restart the step!
|
||||
```
|
||||
|
||||
To install copy the multi folder into your environment and you are good to go</br>
|
||||
If you want to use the system threads, then you'll need to install lanes or love2d game engine!
|
||||
|
||||
```
|
||||
luarocks install multi
|
||||
```
|
||||
|
||||
Discord
|
||||
-------
|
||||
Have a question or need realtime assistance? Feel free to join the discord!</br>
|
||||
https://discord.gg/U8UspuA
|
||||
|
||||
Planned features/TODO
|
||||
---------------------
|
||||
- [x] ~~Create test suite (In progress, mostly done)~~
|
||||
- [ ] Network Parallelism rework
|
||||
|
||||
Usage: [Check out the documentation for more info](https://github.com/rayaman/multi/blob/master/Documentation.md)
|
||||
-----
|
||||
|
||||
You can run tests in 2 ways:
|
||||
```
|
||||
lua tests/runtests.lua (Runs all tests, attempts to use lanes)
|
||||
love tests (Runs all tests in love2d env)
|
||||
```
|
||||
|
||||
```lua
|
||||
local multi, thread = require("multi"):init()
|
||||
GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||
|
||||
multi:newSystemThread("System Thread",function()
|
||||
while true do
|
||||
THREAD.sleep(.1)
|
||||
io.write(" World")
|
||||
THREAD.kill()
|
||||
end
|
||||
end)
|
||||
|
||||
-- step2 is bored lets give it some love :P
|
||||
step2.range=step2:newRange() -- Set up a range object to have a nested step in a sense! Each nest requires a new range
|
||||
-- it is in your interest not to share ranges between objects! You can however do it if it suits your needs though
|
||||
step2:OnStep(function(pos,self)
|
||||
-- for 1=1,math.huge do
|
||||
-- print("Haha I am holding the code up because I can!!!")
|
||||
--end
|
||||
-- We dont want to hold things up, but we want to nest.
|
||||
-- Note a range is not nessary if the nested for loop has a small range, if however the range is rather large you may want to allow other objects to do some work
|
||||
for i in self.range(1,100) do
|
||||
print(pos,i) -- Now our nested for loop is using a range object which allows for other objects to get some cpu time while this one is running
|
||||
end
|
||||
end)
|
||||
-- TSteps are just like alarms and steps mixed together, the only difference in construction is the 4th Argument. On a TStep that argument controls time. The defualt is 1
|
||||
-- The Reset(n) works just like you would figure!
|
||||
step3=multi:newTStep(1,10,.5,2) -- lets go from 1 to 10 counting by .5 every 2 seconds
|
||||
step3:OnStep(function(pos,self)
|
||||
print("Ok "..pos.."!")
|
||||
multi:newThread("Coroutine Based Thread",function()
|
||||
while true do
|
||||
io.write("Hello")
|
||||
thread.sleep(.1)
|
||||
thread.kill()
|
||||
end
|
||||
end)
|
||||
|
||||
multi:newTLoop(function(loop)
|
||||
print("!")
|
||||
loop:Destroy()
|
||||
os.exit()
|
||||
end,.3)
|
||||
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
|
||||
Note: the output on this one is huge!!! So I had to ... some parts! You need to run this for your self to see what is going on!</br>
|
||||
Step Started!</br>
|
||||
Stepping... 1</br>
|
||||
10 1</br>
|
||||
Stepping... 2</br>
|
||||
10 2</br>
|
||||
Stepping... 3</br>
|
||||
10 3</br>
|
||||
...</br>
|
||||
Ok 9.5!</br>
|
||||
Ok 10!</br>
|
||||
|
||||
# TLOOPS
|
||||
```lua
|
||||
require("multi.all")
|
||||
-- TLoops are loops that run ever n second. We will also look at condition objects as well
|
||||
-- Here we are going to modify the old loop to be a little different
|
||||
count=0
|
||||
loop=multi:newTLoop(function(self) -- We are only going to coult with this loop, but doing so using a condition!
|
||||
while self:condition(self.cond) do
|
||||
count=count+1
|
||||
end
|
||||
print("Count is "..count.."!")
|
||||
self:Destroy() -- Lets destroy this object, casting it to the dark abyss MUHAHAHA!!!
|
||||
-- the reference to this object will be a phantom object that does nothing!
|
||||
end,1) -- Notice the ',1' after the function! This is where you put your time value!
|
||||
loop.cond=multi:newCondition(function() return count<=100 end) -- conditions need a bit of work before i am happy with them
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
Count is 101!
|
||||
|
||||
# Connections
|
||||
These are my favorite objects and you'll see why. They are very useful objects for ASync connections!
|
||||
|
||||
```lua
|
||||
require("multi.all")
|
||||
-- Lets create the events
|
||||
yawn={} -- ill just leave that there
|
||||
OnCustomSafeEvent=multi:newConnection(true) -- lets pcall the calls incase something goes wrong defualt
|
||||
OnCustomEvent=multi:newConnection(false) -- lets pcall the calls incase something goes wrong
|
||||
OnCustomEvent:Bind(yawn) -- create the connection lookup data in yawn
|
||||
|
||||
-- Lets connect to them, a recent update adds a nice syntax to connect to these
|
||||
cd1=OnCustomSafeEvent:Connect(function(arg1,arg2,...)
|
||||
print("CSE1",arg1,arg2,...)
|
||||
end,"bob") -- lets give this connection a name
|
||||
cd2=OnCustomSafeEvent:Connect(function(arg1,arg2,...)
|
||||
print("CSE2",arg1,arg2,...)
|
||||
end,"joe") -- lets give this connection a name
|
||||
cd3=OnCustomSafeEvent:Connect(function(arg1,arg2,...)
|
||||
print("CSE3",arg1,arg2,...)
|
||||
end) -- lets not give this connection a name
|
||||
|
||||
-- no need for connect, but I kept that function because of backwards compatibility
|
||||
OnCustomEvent(function(arg1,arg2,...)
|
||||
print(arg1,arg2,...)
|
||||
end)
|
||||
|
||||
-- Now within some loop/other object you trigger the connection like
|
||||
OnCustomEvent:Fire(1,2,"Hello!!!") -- fire all conections
|
||||
|
||||
-- You may have noticed that some events have names! See the following example!
|
||||
OnCustomSafeEvent:getConnection("bob"):Fire(1,100,"Bye!") -- fire only bob!
|
||||
OnCustomSafeEvent:getConnection("joe"):Fire(1,100,"Hello!") -- fire only joe!!
|
||||
OnCustomSafeEvent:Fire(1,100,"Hi Ya Folks!!!") -- fire them all!!!
|
||||
|
||||
-- Connections have more to them than that though!
|
||||
-- As seen above cd1-cd3 these are hooks to the connection object. This allows you to remove a connection
|
||||
-- For Example:
|
||||
cd1:Remove() -- remove this connection from the master connection object
|
||||
print("------")
|
||||
OnCustomSafeEvent:Fire(1,100,"Hi Ya Folks!!!") -- fire them all again!!!
|
||||
-- To remove all connections use:
|
||||
OnCustomSafeEvent:Remove()
|
||||
print("------")
|
||||
OnCustomSafeEvent:Fire(1,100,"Hi Ya Folks!!!") -- fire them all again!!!
|
||||
```
|
||||
# Output
|
||||
1 2 Hello!!!</br>
|
||||
CSE1 1 100 Bye!</br>
|
||||
CSE2 1 100 Hello!</br>
|
||||
CSE1 1 100 Hi Ya Folks!!!</br>
|
||||
CSE2 1 100 Hi Ya Folks!!!</br>
|
||||
CSE3 1 100 Hi Ya Folks!!!</br>
|
||||
------</br>
|
||||
CSE2 1 100 Hi Ya Folks!!!</br>
|
||||
CSE3 1 100 Hi Ya Folks!!!</br>
|
||||
------</br>
|
||||
|
||||
You may think timers should be bundled with alarms, but they are a bit different and have cool features</br>
|
||||
# TIMERS
|
||||
```lua
|
||||
-- You see the thing is that all time based objects use timers eg. Alarms, TSteps, and Loops. Timers are more low level!
|
||||
require("multi.all")
|
||||
local clock = os.clock
|
||||
function sleep(n) -- seconds
|
||||
local t0 = clock()
|
||||
while clock() - t0 <= n do end
|
||||
end -- we will use this later!
|
||||
|
||||
timer=multi:newTimer()
|
||||
timer:Start()
|
||||
-- lets do a mock alarm
|
||||
set=3 -- 3 seconds
|
||||
a=0
|
||||
while timer:Get()<=set do
|
||||
-- waiting...
|
||||
a=a+1
|
||||
end
|
||||
print(set.." second(s) have passed!")
|
||||
-- Timers can do one more thing that is interesting and that is pausing them!
|
||||
timer:Pause()
|
||||
print(timer:Get()) -- should be really close to 'set'
|
||||
sleep(3)
|
||||
print(timer:Get()) -- should be really close to 'set'
|
||||
timer:Resume()
|
||||
sleep(1)
|
||||
print(timer:Get()) -- should be really close to the value of set + 1
|
||||
timer:Pause()
|
||||
print(timer:Get()) -- should be really close to 'set'
|
||||
sleep(3)
|
||||
print(timer:Get()) -- should be really close to 'set'
|
||||
timer:Resume()
|
||||
sleep(1)
|
||||
print(timer:Get()) -- should be really close to the value of set + 2
|
||||
```
|
||||
# Output
|
||||
Note: This will make more sense when you run it for your self</br>
|
||||
3 second(s) have passed!</br>
|
||||
3.001</br>
|
||||
3.001</br>
|
||||
4.002</br>
|
||||
4.002</br>
|
||||
4.002</br>
|
||||
5.003</br>
|
||||
|
||||
# UPDATER
|
||||
```lua
|
||||
require("multi.all")
|
||||
updater=multi:newUpdater(5) -- really simple, think of a look with the skip feature of a step
|
||||
updater:OnUpdate(function(self)
|
||||
--print("updating...")
|
||||
end)
|
||||
-- Here every 5 steps the updater will do stuff!
|
||||
-- But I feel it is now time to touch into priority management, so lets get into basic priority stuff and get into a more advance version of it
|
||||
--[[
|
||||
multi.Priority_Core -- Highest form of priority
|
||||
multi.Priority_High
|
||||
multi.Priority_Above_Normal
|
||||
multi.Priority_Normal -- The defualt form of priority
|
||||
multi.Priority_Below_Normal
|
||||
multi.Priority_Low
|
||||
multi.Priority_Idle -- Lowest form of priority
|
||||
|
||||
Note: These only take effect when you enable priority, otherwise everything is at a core like level!
|
||||
We aren't going to use regular objects to test priority, but rather benchmarks!
|
||||
to set priority on an object though you would do
|
||||
multiobj:setPriority(one of the above)
|
||||
while true do
|
||||
multi:uManager()
|
||||
end
|
||||
]]
|
||||
-- lets bench for 3 seconds using the 3 forms of priority! First no Priority
|
||||
multi:benchMark(3,nil,"Regular Bench: "):OnBench(function() -- the onbench() allows us to do each bench after each other!
|
||||
print("P1\n---------------")
|
||||
multi:enablePriority()
|
||||
multi:benchMark(3,multi.Priority_Core,"Core:")
|
||||
multi:benchMark(3,multi.Priority_High,"High:")
|
||||
multi:benchMark(3,multi.Priority_Above_Normal,"Above_Normal:")
|
||||
multi:benchMark(3,multi.Priority_Normal,"Normal:")
|
||||
multi:benchMark(3,multi.Priority_Below_Normal,"Below_Normal:")
|
||||
multi:benchMark(3,multi.Priority_Low,"Low:")
|
||||
multi:benchMark(3,multi.Priority_Idle,"Idle:"):OnBench(function()
|
||||
print("P2\n---------------")
|
||||
-- Finally the 3rd form
|
||||
multi:enablePriority2()
|
||||
multi:benchMark(3,multi.Priority_Core,"Core:")
|
||||
multi:benchMark(3,multi.Priority_High,"High:")
|
||||
multi:benchMark(3,multi.Priority_Above_Normal,"Above_Normal:")
|
||||
multi:benchMark(3,multi.Priority_Normal,"Normal:")
|
||||
multi:benchMark(3,multi.Priority_Below_Normal,"Below_Normal:")
|
||||
multi:benchMark(3,multi.Priority_Low,"Low:")
|
||||
multi:benchMark(3,multi.Priority_Idle,"Idle:")
|
||||
end)
|
||||
end)
|
||||
multi:mainloop() -- Notice how the past few examples did not need this, well only actors need to be in a loop! More on this in the wiki.
|
||||
```
|
||||
# Output
|
||||
Note: These numbers will vary drastically depending on your compiler and cpu power</br>
|
||||
Regular Bench: 2094137 Steps in 3 second(s)!</br>
|
||||
P1</br>
|
||||
---------------</br>
|
||||
Below_Normal: 236022 Steps in 3 second(s)!</br>
|
||||
Normal: 314697 Steps in 3 second(s)!</br>
|
||||
Above_Normal: 393372 Steps in 3 second(s)!</br>
|
||||
High: 472047 Steps in 3 second(s)!</br>
|
||||
Core: 550722 Steps in 3 second(s)!</br>
|
||||
Low: 157348 Steps in 3 second(s)!</br>
|
||||
Idle: 78674 Steps in 3 second(s)!</br>
|
||||
P2</br>
|
||||
---------------</br>
|
||||
Core: 994664 Steps in 3 second(s)!</br>
|
||||
High: 248666 Steps in 3 second(s)!</br>
|
||||
Above_Normal: 62166 Steps in 3 second(s)!</br>
|
||||
Normal: 15541 Steps in 3 second(s)!</br>
|
||||
Below_Normal: 3885 Steps in 3 second(s)!</br>
|
||||
Idle: 242 Steps in 3 second(s)!</br>
|
||||
Low: 971 Steps in 3 second(s)!</br>
|
||||
|
||||
Notice: Even though I started each bench at the same time the order that they finished differed the order is likely to vary on your machine as well!</br>
|
||||
|
||||
# Processes
|
||||
A process allows you to group the Actor objects within a controlable interface
|
||||
```lua
|
||||
require("multi.all")
|
||||
proc=multi:newProcess() -- takes an optional file as an argument, but for this example we aren't going to use that
|
||||
-- a process works just like the multi object!
|
||||
b=0
|
||||
loop=proc:newTLoop(function(self)
|
||||
a=a+1
|
||||
proc:Pause() -- pauses the cpu cycler for this processor! Individual objects are not paused, however because they aren't getting cpu time they act as if they were paused
|
||||
end,.1)
|
||||
updater=proc:newUpdater(multi.Priority_Idle) -- priority can be used in skip arguments as well to manage priority without enabling it!
|
||||
updater:OnUpdate(function(self)
|
||||
b=b+1
|
||||
end)
|
||||
a=0 -- a counter
|
||||
loop2=proc:newLoop(function(dt,self)
|
||||
print("Lets Go!")
|
||||
self:hold(3) -- this will keep this object from doing anything! Note: You can only have one hold active at a time! Multiple are possible, but results may not be as they seem see * for how hold works
|
||||
-- Within a process using hold will keep it alive until the hold is satisified!
|
||||
print("Done being held for 1 second")
|
||||
self:hold(function() return a>10 end)
|
||||
print("A is now: "..a.." b is also: "..b)
|
||||
self:Destroy()
|
||||
self.Parent:Pause() -- lets say you don't have the reference to the process!
|
||||
os.exit()
|
||||
end)
|
||||
-- Notice this is now being created on the multi namespace
|
||||
event=multi:newEvent(function() return os.clock()>=1 end)
|
||||
event:OnEvent(function(self)
|
||||
proc:Resume()
|
||||
self:Destroy()
|
||||
end)
|
||||
proc:Start()
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
Lets Go!</br>
|
||||
Done being held for 1 second</br>
|
||||
A is now: 29 b is also: 479</br>
|
||||
|
||||
**Hold: This method works as follows**
|
||||
```lua
|
||||
function multi:hold(task)
|
||||
self:Pause() -- pause the current object
|
||||
self.held=true -- set held
|
||||
if type(task)=='number' then -- a sleep cmd
|
||||
local timer=multi:newTimer()
|
||||
timer:Start()
|
||||
while timer:Get()<task do -- This while loop is what makes using multiple holds tricky... If the outer while is good before the nested one then the outter one will have to wait! There is a way around this though!
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
self:Resume()
|
||||
self.held=false
|
||||
elseif type(task)=='function' then
|
||||
local env=self.Parent:newEvent(task)
|
||||
env:OnEvent(function(envt) envt:Pause() envt.Active=false end)
|
||||
while env.Active do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
env:Destroy()
|
||||
self:Resume()
|
||||
self.held=false
|
||||
else
|
||||
print('Error Data Type!!!')
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
# Queuer (WIP)
|
||||
A queuer works just like a process however objects are processed in order that they were created...
|
||||
```lua
|
||||
queue = multi:newQueuer()
|
||||
queue:newAlarm(3):OnRing(function()
|
||||
print("Ring ring!!!")
|
||||
end)
|
||||
queue:newStep(1,10):OnStep(function(pos,self)
|
||||
print(pos)
|
||||
end)
|
||||
queue:newLoop(function(dt,self)
|
||||
if dt==3 then
|
||||
self:Break()
|
||||
print("Done")
|
||||
end
|
||||
end)
|
||||
queue:Start()
|
||||
multi:mainloop()
|
||||
```
|
||||
# Expected Output
|
||||
Note: the queuer still does not work as expected!</br>
|
||||
Ring ring!!!</br>
|
||||
1</br>
|
||||
2</br>
|
||||
3</br>
|
||||
4</br>
|
||||
5</br>
|
||||
6</br>
|
||||
7</br>
|
||||
8</br>
|
||||
9</br>
|
||||
10</br>
|
||||
Done</br>
|
||||
# Actual Output
|
||||
Done</br>
|
||||
1</br>
|
||||
2</br>
|
||||
3</br>
|
||||
4</br>
|
||||
5</br>
|
||||
6</br>
|
||||
7</br>
|
||||
8</br>
|
||||
9</br>
|
||||
10</br>
|
||||
Ring ring!!!</br>
|
||||
|
||||
# Threads
|
||||
These fix the hold problem that you get with regular objects, and they work exactly the same! They even have some extra features that make them really useful.</br>
|
||||
```lua
|
||||
_require=require -- lets play with the require method a bit
|
||||
function require(path)
|
||||
path=path:gsub("%*","all")
|
||||
_require(path)
|
||||
end
|
||||
require("multi.*") -- now I can use that lovely * symbol to require everything
|
||||
test=multi:newThreadedProcess("main") -- you can thread processors and all Actors see note for a list of actors you can thread!
|
||||
test2=multi:newThreadedProcess("main2")
|
||||
count=0
|
||||
test:newLoop(function(dt,self)
|
||||
count=count+1
|
||||
thread.sleep(.01)
|
||||
end)
|
||||
test2:newLoop(function(dt,self)
|
||||
print("Hello!")
|
||||
thread.sleep(1) -- sleep for some time
|
||||
end)
|
||||
-- threads take a name object then the rest as normal
|
||||
step=multi:newThreadedTStep("step",1,10)
|
||||
step:OnStep(function(p,self)
|
||||
print("step",p)
|
||||
thread.skip(21) -- skip n cycles
|
||||
end)
|
||||
step:OnEnd(function()
|
||||
print("Killing thread!")
|
||||
thread.kill() -- kill the thread
|
||||
end)
|
||||
loop=multi:newThreadedLoop("loop",function(dt,self)
|
||||
print(dt)
|
||||
thread.sleep(1.1)
|
||||
end)
|
||||
loop2=multi:newThreadedLoop("loop",function(dt,self)
|
||||
print(dt)
|
||||
thread.hold(function() return count>=100 end)
|
||||
print("Count is "..count)
|
||||
os.exit()
|
||||
end)
|
||||
alarm=multi:newThreadedAlarm("alarm",1)
|
||||
alarm:OnRing(function(self)
|
||||
print("Ring")
|
||||
self:Reset()
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
Ring</br>
|
||||
0.992</br>
|
||||
0.992</br>
|
||||
Hello!</br>
|
||||
step 1</br>
|
||||
step 2</br>
|
||||
Hello!</br>
|
||||
Ring</br>
|
||||
2.092</br>
|
||||
step 3</br>
|
||||
Hello!</br>
|
||||
Ring</br>
|
||||
Count is 100</br>
|
||||
# Threadable Actors
|
||||
- Alarms
|
||||
- Events
|
||||
- Loop/TLoop
|
||||
- Process
|
||||
- Step/TStep
|
||||
|
||||
# Functions
|
||||
If you ever wanted to pause a function then great now you can
|
||||
The uses of the Function object allows one to have a method that can run free in a sense
|
||||
```lua
|
||||
require("multi.all")
|
||||
func=multi:newFunction(function(self,arg1,arg2,...)
|
||||
self:Pause()
|
||||
return arg1
|
||||
end)
|
||||
print(func("Hello"))
|
||||
print(func("Hello2")) -- returns PAUSED allows for the calling of functions that should only be called once. returns PAUSED instantly if paused
|
||||
func:Resume()
|
||||
print(func("Hello3"))
|
||||
```
|
||||
# Output
|
||||
Hello</br>
|
||||
PAUSED</br>
|
||||
Hello3</br>
|
||||
|
||||
# ThreadedUpdater
|
||||
|
||||
```lua
|
||||
-- Works the same as a regular updater!
|
||||
require("multi.all")
|
||||
multi:newThreadedUpdater("Test",10000):OnUpdate(function(self)
|
||||
print(self.pos)
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
1</br>
|
||||
2</br>
|
||||
...</br>
|
||||
.inf</br>
|
||||
|
||||
# Triggers
|
||||
Triggers were what I used before connections became a thing, also Function objects are a lot like triggers and can be paused as well, while triggers cannot...</br>
|
||||
They are simple to use, but in most cases you are better off using a connection</br>
|
||||
```lua
|
||||
require("multi.trigger")
|
||||
-- They work like connections but can only have one event binded to them
|
||||
trig=multi:newTrigger(function(self,a,b,c,...)
|
||||
print(a,b,c,...)
|
||||
end)
|
||||
trig:Fire(1,2,3)
|
||||
trig:Fire(1,2,3,"Hello",true)
|
||||
```
|
||||
|
||||
# Output
|
||||
1 2 3</br>
|
||||
1 2 3 Hello true</br>
|
||||
|
||||
# Tasks
|
||||
Tasks allow you to run a block of code before the multi mainloops does it thing. Tasks still have a use, but depending on how you program they aren't needed.</br>
|
||||
```lua
|
||||
require("multi.loop")
|
||||
require("multi.task")
|
||||
multi:newTask(function()
|
||||
print("Hi!")
|
||||
end)
|
||||
multi:newLoop(function(dt,self)
|
||||
print("Which came first the task or the loop?")
|
||||
self:Break()
|
||||
end)
|
||||
multi:newTask(function()
|
||||
print("Hello there!")
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
Hi!</br>
|
||||
Hello there!</br>
|
||||
Which came first the task or the loop?</br>
|
||||
|
||||
As seen in the example above the tasks were done before anything else in the mainloop! This is useful when making libraries around the multitasking features and you need things to happen in a certain order!</br>
|
||||
|
||||
# Jobs
|
||||
Jobs were a strange feature that was created for throttling connections! When I was building a irc bot around this library I couldn't have messages posting too fast due to restrictions. Jobs allowed functions to be added to a queue that were executed after a certain amount of time has passed
|
||||
```lua
|
||||
require("multi.alarm") -- jobs use alarms I am pondering if alarms should be added to the core or if jobs should use timers instead...
|
||||
-- jobs are built into the core of the library so no need to require them
|
||||
print(multi:hasJobs())
|
||||
multi:setJobSpeed(1) -- set job speed to 1 second
|
||||
multi:newJob(function()
|
||||
print("A job!")
|
||||
end,"test")
|
||||
|
||||
multi:newJob(function()
|
||||
print("Another job!")
|
||||
multi:removeJob("test") -- removes all jobs with name "test"
|
||||
end,"test")
|
||||
|
||||
multi:newJob(function()
|
||||
print("Almost done!")
|
||||
end,"test")
|
||||
|
||||
multi:newJob(function()
|
||||
print("Final job!")
|
||||
end,"test")
|
||||
print(multi:hasJobs())
|
||||
print("There are "..multi:getJobs().." jobs in the queue!")
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
false 0</br>
|
||||
true 4</br>
|
||||
There are 4 jobs in the queue!</br>
|
||||
A job!</br></br>
|
||||
Another job!</br>
|
||||
|
||||
# Watchers
|
||||
Watchers allow you to monitor a variable and trigger an event when the variable has changed!
|
||||
```lua
|
||||
require("multi.watcher")
|
||||
require("multi.tloop")
|
||||
a=0
|
||||
watcher=multi:newWatcher(_G,"a") -- watch a in the global enviroment
|
||||
watcher:OnValueChanged(function(self,old,new)
|
||||
print(old,new)
|
||||
end)
|
||||
tloop=multi:newTLoop(function(self)
|
||||
a=a+1
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output
|
||||
0 1</br>
|
||||
1 2</br>
|
||||
2 3</br>
|
||||
...</br>
|
||||
.inf-1 inf</br>
|
||||
|
||||
Timeout management
|
||||
```lua
|
||||
-- Note: I used a tloop so I could control the output of the program a bit.
|
||||
require("multi.tloop")
|
||||
a=0
|
||||
inc=1 -- change to 0 to see it not met at all, 1 if you want to see the first condition not met but the second and 2 if you want to see it meet the condition on the first go.
|
||||
loop=multi:newTLoop(function(self)
|
||||
print("Looping...")
|
||||
a=a+inc
|
||||
if a==14 then
|
||||
self:ResolveTimer("1","2","3") -- ... any number of arguments can be passed to the resolve handler
|
||||
-- this will also automatically pause the object that it is binded to
|
||||
end
|
||||
end,.1)
|
||||
loop:SetTime(1)
|
||||
loop:OnTimerResolved(function(self,a,b,c) -- the handler will return the self and the passed arguments
|
||||
print("We did it!",a,b,c)
|
||||
end)
|
||||
loop:OnTimedOut(function(self)
|
||||
if not TheSecondTry then
|
||||
print("Loop timed out!",self.Type,"Trying again...")
|
||||
self:ResetTime(2)
|
||||
self:Resume()
|
||||
TheSecondTry=true
|
||||
else
|
||||
print("We just couldn't do it!") -- print if we don't get anything working
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
# Output (Change the value inc as indicated in the comment to see the outcomes!)
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Loop timed out! tloop Trying again...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
Looping...</br>
|
||||
We did it! 1 2 3</br>
|
||||
|
||||
# TODO (In order of importance)
|
||||
- Finish the wiki stuff. (10% done)</br>
|
||||
- Test for unknown bugs</br>
|
||||
Known Bugs/Issues
|
||||
-----------------
|
||||
Check the [Issues tab](https://github.com/rayaman/multi/issues) for issues
|
||||
|
||||
971
docs/Documentation.md
Normal file
971
docs/Documentation.md
Normal file
@ -0,0 +1,971 @@
|
||||
Current Multi Version: 15.1.0
|
||||
|
||||
# Multi static variables
|
||||
`multi.Version` — The current version of the library
|
||||
|
||||
`multi.TIMEOUT` — The value returned when a timed method times out
|
||||
|
||||
`multi.Priority_Core` — Highest level of pirority that can be given to a process
|
||||
</br>`multi.Priority_Very_High`
|
||||
</br>`multi.Priority_High`
|
||||
</br>`multi.Priority_Above_Normal`
|
||||
</br>`multi.Priority_Normal` — The default level of pirority that is given to a process
|
||||
</br>`multi.Priority_Below_Normal`
|
||||
</br>`multi.Priority_Low`
|
||||
</br>`multi.Priority_Very_Low`
|
||||
</br>`multi.Priority_Idle` — Lowest level of pirority that can be given to a process
|
||||
|
||||
# Multi Runners
|
||||
`multi:lightloop()` — A light version of the mainloop doesn't run Coroutine based threads
|
||||
</br>`multi:loveloop([BOOLEAN: light true])` — Run's all the love related features as well
|
||||
</br>`multi:mainloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running
|
||||
</br>`multi:threadloop([TABLE settings])` — This runs the mainloop by having its own internal while loop running, but prioritizes threads over multi-objects
|
||||
</br>`multi:uManager([TABLE settings])` — This runs the mainloop, but does not have its own while loop and thus needs to be within a loop of some kind.
|
||||
|
||||
# Global Methods
|
||||
|
||||
`multi:init()` — Uesd to initiate the library, should only be called once
|
||||
`multi.getCurrentProcess()` — Returns currently running Process
|
||||
`multi.`
|
||||
|
||||
# Processor Methods
|
||||
|
||||
These methods can be called either on the multi namespace or a process returned by `proc = multi:newProcessor()`
|
||||
|
||||
`proc.Stop()` — Stops the main process/child process. **Note:** If the main process is stopped all child processes are stopped as well
|
||||
`proc:getTasksDetails([STRING: displaytype])` — Gets a table or string of all the running tasks
|
||||
|
||||
Processor Attributes
|
||||
---
|
||||
|
||||
| Attribute | Type | Returns | Description |
|
||||
---|---|---|---
|
||||
Start|Method()|self| Starts the process
|
||||
Stop|Method()|self| Stops the process
|
||||
OnError|Connection|connection| Allows connection to the process error handler
|
||||
Type|Member:`string`|"process"| Contains the type of object
|
||||
Active|Member:`boolean`|variable| If false the process is not active
|
||||
Name|Member:`string`|variable| The name set at process creation
|
||||
process|Thread|thread| A handle to a multi thread object
|
||||
|
||||
[Refer to the objects for more methods](#non-actors)
|
||||
|
||||
Example:
|
||||
```lua
|
||||
package.path = "./?/init.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
|
||||
-- Create a processor object, it works a lot like the multi object
|
||||
sandbox = multi:newProcessor()
|
||||
|
||||
-- On our processor object create a TLoop that prints "testing..." every second
|
||||
sandbox:newTLoop(function()
|
||||
print("testing...")
|
||||
end,1)
|
||||
|
||||
-- Create a thread on the processor object
|
||||
sandbox:newThread("Test Thread",function()
|
||||
-- Create a counter named 'a'
|
||||
local a = 0
|
||||
-- Start of the while loop that ends when a = 10
|
||||
while true do
|
||||
-- pause execution of the thread for 1 second
|
||||
thread.sleep(1)
|
||||
-- increment a by 1
|
||||
a = a + 1
|
||||
-- display the name of the current process
|
||||
print("Thread Test: ".. multi.getCurrentProcess().Name)
|
||||
if a == 10 then
|
||||
-- Stopping the processor stops all objects created inside that process including threads. In the backend threads use a regular multiobject to handle the scheduler and all of the holding functions. These all stop when a processor is stopped. This can be really useful to sandbox processes that might need to turned on and off with ease and not having to think about it.
|
||||
sandbox.Stop()
|
||||
end
|
||||
end
|
||||
-- Catch any errors that may come up
|
||||
end).OnError(function(...)
|
||||
print(...)
|
||||
end)
|
||||
|
||||
sandbox.Start() -- Start the process
|
||||
|
||||
multi:mainloop() -- The main loop that allows all processes to continue
|
||||
```
|
||||
|
||||
# Multi Settings
|
||||
|
||||
**Note:** Most settings have been fined tuned to be at the peak of performance already, however preLoop, protect (Which drastically lowers preformance), and stopOnError should be used freely to fit your needs.
|
||||
|
||||
| Setting | Type: default | Purpose |
|
||||
| --------------- | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| preLoop | function: nil | This is a function that is called after all the important components of the library are loaded. This is called once only. The first and only argument passed is a reference to itself. |
|
||||
| protect | boolean: false | This runs code within a protected call. To catch when errors happen see built in connections |
|
||||
| stopOnError | boolean: false | This setting is used with protect. If an object crashes due to some error should it be paused? |
|
||||
| priority | number: 0 | This sets the priority scheme. Look at the P-Charts below for examples. |
|
||||
| auto_priority | boolean: false | **Note: This overrides any value set for priority!** If auto priority is enabled then priority scheme 3 is used and processes are considered for "recheck" after a certain amount of time. If a process isn't taking too long to complete anymore then it will be reset to core, if it starts to take a lot of time all of a sudden it will be set to idle. |
|
||||
| auto_stretch | number: 1 | For use with auto_priority. Modifies the internal reperesentation of idle time by multiplying multi.Priority_Idle by the value given |
|
||||
| auto_delay | number: 3 | For use with auto_priority. This changes the time in seconds that process are "rechecked" |
|
||||
| auto_lowerbound | number: multi.Priority_Idle | For use with auto_priority. The lowerbound is what is considered to be idle time. A higher value combined with auto_stretch allows one to fine tune how pirority is managed. |
|
||||
|
||||
# P-Chart: Priority 1
|
||||
|
||||
P1 follows a forumla that resembles this: ~n=I*PRank</br>Where **n** is the amount of steps given to an object with PRank and where I is the idle time see chart below. The aim of this priority scheme was to make core objects run fastest while letting idle processes get decent time as well.
|
||||
|
||||
| Priority: n | PRank | Formula |
|
||||
| --------------------- | ----- | ------------ |
|
||||
| Core: 3322269 | 7 | n = ~**I***7 |
|
||||
| High: 2847660 | 6 | n = ~**I***6 |
|
||||
| Above_Normal: 2373050 | 5 | n = ~**I***5 |
|
||||
| Normal: 1898440 | 4 | n = ~**I***4 |
|
||||
| Below_Normal: 1423830 | 3 | n = ~**I***3 |
|
||||
| Low: 949220 | 2 | n = ~**I***2 |
|
||||
| **I**dle: 474610 | 1 | n = ~**I***1 |
|
||||
|
||||
**General Rule:** ~n=**I***PRank
|
||||
|
||||
# P-Chart: Priority 2
|
||||
|
||||
P2 follows a formula that resembles this: ~n=n*4 where n starts as the initial idle time, see chart below. The goal of this one was to make core process’ higher while keeping idle process’ low.
|
||||
|
||||
| Priority: n |
|
||||
|-|
|
||||
| Core: 6700821|
|
||||
| High: 1675205|
|
||||
| Above_Normal: 418801|
|
||||
| Normal: 104700|
|
||||
| Below_Normal: 26175|
|
||||
| Low: 6543|
|
||||
| **I**dle: 1635|
|
||||
|
||||
**General Rule:** `~n=n*4` Where the inital n = **I**
|
||||
|
||||
# P-Chart: Priority 3
|
||||
P3 Ignores using a basic formula and instead bases its processing time on the amount of cpu time is there. If cpu-time is low and a process is set at a lower priority it will get its time reduced. There is no formula, at idle almost all process work at the same speed!
|
||||
|
||||
There are 2 settings for this: Core and Idle. If a process takes too long then it is set to idle. Otherwise it will stay core.
|
||||
|
||||
Example of settings:
|
||||
```lua
|
||||
settings = {
|
||||
preLoop = function(m)
|
||||
print("All settings have been loaded!")
|
||||
end,
|
||||
protect = false,
|
||||
stopOnError = false,
|
||||
priority = 0,
|
||||
auto_priority = false,
|
||||
auto_stretch = 1,
|
||||
auto_delay = 3,
|
||||
auto_lowerbound = multi.Priority_Idle
|
||||
}
|
||||
|
||||
-- Below are how the runners work
|
||||
|
||||
multi:lightloop() -- lighter version of mainloop. Everything except priority management for non service objects will function like normal!
|
||||
|
||||
-- or
|
||||
|
||||
multi:mainloop(settings) -- normal runner
|
||||
|
||||
-- or
|
||||
|
||||
multi:threadloop(settings) -- Prioritizes threads over multi-objs
|
||||
|
||||
-- or
|
||||
|
||||
while true do
|
||||
multi:uManager(settings) -- allows you to run the multi main loop within another loop
|
||||
end
|
||||
```
|
||||
|
||||
# Non-Actors
|
||||
`timer = multi:newTimer()`
|
||||
- `conn = multi:newConnection([BOOLEAN protect true])`
|
||||
- `func = multi:newFunction(FUNCTION func)`
|
||||
|
||||
# Actors
|
||||
- `event = multi:newEvent(FUNCTION task)`
|
||||
- `updater = multi:newUpdater([NUMBER skip 1])`
|
||||
- `alarm = multi:newAlarm([NUMBER 0])`
|
||||
- `loop = multi:newLoop(FUNCTION func)`
|
||||
- `tloop = multi:newTLoop(FUNCTION func ,NUMBER: [set 1])`
|
||||
- `step = multi:newStep(NUMBER start,*NUMBER reset, [NUMBER count 1], [NUMBER skip 0])`
|
||||
- `tstep = multi:newStep(NUMBER start, NUMBER reset, [NUMBER count 1], [NUMBER set 1])`
|
||||
|
||||
**Note:** A lot of methods will return itself as a return. This allows for chaining of methods to work.
|
||||
|
||||
# Non-Actor: Timers
|
||||
`timer = multi:newTimer()` — Creates a timer object that can keep track of time
|
||||
|
||||
- **self** = timer:Start() — Starts the timer
|
||||
- time_elapsed = timer:Get() — Returns the time elapsed since timer:Start() was called
|
||||
- boolean = timer:isPaused() — Returns if the timer is paused or not
|
||||
- **self** = timer:Pause() — Pauses the timer, it skips time that would be counted during the time that it is paused
|
||||
- **self** = timer:Resume() — Resumes a paused timer. **See note below**
|
||||
- **self** = timer:tofile(**STRING** path) — Saves the object to a file at location path
|
||||
|
||||
**Note:** If a timer was paused after 1 second then resumed a second later and Get() was called a second later, timer would have 2 seconds counted though 3 really have passed.
|
||||
|
||||
# Non-Actor: Connections
|
||||
`conn = multi:newConnection([BOOLEAN: protect true],FUNCTION: callback, BOOLEAN: kill false)` —
|
||||
Creates a connection object and defaults to a protective state. All calls will run within pcall() callback if it exists will be triggered each time the connection is fired. kill when set to true makes the connection object work like a queue. Where all the events that are fired is removed from the queue.
|
||||
- `self = conn:HoldUT([NUMBER n 0])` — Will hold futhur execution of the thread until the connection was triggered. If n is supplied the connection must be triggered n times before it will allow ececution to continue.
|
||||
- `conntable_old = conn:Bind(TABLE conntable)` — sets the table to hold the connections. A quick way to destroy all connections is by binding it to a new table.
|
||||
- `conntable = conn:Remove()` — Removes all connections. Returns the conntable
|
||||
- `link = conn:connect(FUNCTION func, [STRING name nil], [NUMBER num #conns+1])` — Connects to the object using function func which will recieve the arguments passed by Fire(...). You can name a connection, which allows you to use conn:getConnection(name). Names must be unique! num is simple the position in the order in which connections are triggered. The return Link is the link to the connected event that was made. You can remove this event or even trigger it specifically if need be.
|
||||
- `link:Fire(...)` — Fires the created event
|
||||
- `bool = link:Destroy()` — returns true if success.
|
||||
- `subConn = conn:getConnection(STRING name, BOOLEAN ingore)` — returns the sub connection which matches name.
|
||||
returns or nil
|
||||
- subConn:Fire() — "returns" if non-nil is a table containing return values from the triggered connections.
|
||||
- `self = conn:tofile(STRING path)` — Saves the object to a file at location path
|
||||
|
||||
The connect feature has some syntax sugar to it as seen below
|
||||
- `link = conn(FUNCTION func, [STRING name nil], [NUMBER #conns+1])`
|
||||
- `combinedconn = conn1 + conn2` — A combined connection is triggered when all connections are triggered. See example [here](#coroutine-based-threading-cbt)
|
||||
|
||||
|
||||
|
||||
Example:
|
||||
```lua
|
||||
multi,thread = require("multi"):init()
|
||||
-- Let’s create the events
|
||||
yawn={}
|
||||
OnCustomSafeEvent=multi:newConnection(true) -- lets pcall the calls in case something goes wrong default
|
||||
OnCustomEvent=multi:newConnection(false) -- let’s not pcall the calls and let errors happen.
|
||||
OnCustomEvent:Bind(yawn) -- create the connection lookup data in yawn
|
||||
|
||||
-- Let’s connect to them, a recent update adds a nice syntax to connect to these
|
||||
cd1=OnCustomSafeEvent:Connect(function(arg1,arg2,...)
|
||||
print("CSE1",arg1,arg2,...)
|
||||
end,"bob") -- let’s give this connection a name
|
||||
cd2=OnCustomSafeEvent:Connect(function(arg1,arg2,...)
|
||||
print("CSE2",arg1,arg2,...)
|
||||
end,"joe") -- let’s give this connection a name
|
||||
cd3=OnCustomSafeEvent:Connect(function(arg1,arg2,...)
|
||||
print("CSE3",arg1,arg2,...)
|
||||
end) -- let’s not give this connection a name
|
||||
|
||||
-- Using syntax sugar
|
||||
OnCustomEvent(function(arg1,arg2,...)
|
||||
print(arg1,arg2,...)
|
||||
end)
|
||||
|
||||
-- Now within some loop/other object you trigger the connection like
|
||||
OnCustomEvent:Fire(1,2,"Hello!!!") -- fire all connections
|
||||
|
||||
-- You may have noticed that some events have names! See the following example!
|
||||
OnCustomSafeEvent:getConnection("bob"):Fire(1,100,"Bye!") -- fire only bob!
|
||||
OnCustomSafeEvent:getConnection("joe"):Fire(1,100,"Hello!") -- fire only joe!!
|
||||
OnCustomSafeEvent:Fire(1,100,"Hi Ya Folks!!!") -- fire them all!!!
|
||||
|
||||
-- Connections have more to them than that though!
|
||||
-- As seen above cd1-cd3 these are hooks to the connection object. This allows you to remove a connection
|
||||
-- For Example:
|
||||
cd1:Remove() -- remove this connection from the master connection object
|
||||
print("------")
|
||||
OnCustomSafeEvent:Fire(1,100,"Hi Ya Folks!!!") -- fire them all again!!!
|
||||
-- To remove all connections use:
|
||||
OnCustomSafeEvent:Remove()
|
||||
print("------")
|
||||
OnCustomSafeEvent:Fire(1,100,"Hi Ya Folks!!!") -- fire them all again!!!
|
||||
```
|
||||
|
||||
# Semi-Actors: timeouts
|
||||
Timeouts are a collection of methods that allow you to handle timeouts. These only work on multi-objs, and much of the functionality can easly be done now using threads!
|
||||
|
||||
```lua
|
||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
|
||||
loop = multi:newLoop(function()
|
||||
-- do stuff
|
||||
end)
|
||||
|
||||
loop:SetTime(3)
|
||||
multi:newAlarm(2):OnRing(function()
|
||||
-- some condition that leads to resolving the timer
|
||||
loop:ResolveTimer(true,"We good")
|
||||
multi:newAlarm(2):OnRing(function()
|
||||
loop:SetTime(2)
|
||||
end)
|
||||
end)
|
||||
|
||||
loop:OnTimedOut(function()
|
||||
print("Timeout")
|
||||
end)
|
||||
|
||||
loop:OnTimerResolved(function(self,...)
|
||||
print(...)
|
||||
end)
|
||||
|
||||
multi:mainloop()
|
||||
```
|
||||
As mentioned above this is made much easier using threads
|
||||
```lua
|
||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||
multi, thread = require("multi"):init()
|
||||
func = thread:newFunction(function(a)
|
||||
return thread.holdFor(3,function()
|
||||
return a==5 and "This is returned" -- Condition being tested!
|
||||
end)
|
||||
end,true)
|
||||
print(func(5))
|
||||
print(func(0))
|
||||
-- You actually do not need the light/mainloop or any runner for threaded functions to work
|
||||
-- multi:lightloop()
|
||||
```
|
||||
|
||||
# Semi-Actors: scheduleJob
|
||||
`multi:scheduleJob(TABLE: time, FUNCTION: callback)`
|
||||
- `TABLE: time`
|
||||
- `NUMBER: time.min` — Minute(0-59) Repeats every hour
|
||||
- `NUMBER: time.hour` — Hour(0-23) Repeats every day
|
||||
- `NUMBER: time.day` — Day of month(1-31) repeats every month
|
||||
- `NUMBER: time.wday` — Weekday(0-6) repeats every week
|
||||
- `NUMBER: time.month` — Month(1-12) repeats every year
|
||||
- `FUNCTION: callback`
|
||||
- Called when the time table is matched
|
||||
|
||||
Example:
|
||||
```lua
|
||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
multi:scheduleJob({min = 30},function() -- Every hour at minute 30 this event will be triggered! You can mix and match as well!
|
||||
print("Hi")
|
||||
end)
|
||||
multi:scheduleJob({min = 30,hour = 0},function() -- Every day at 12:30AM this event will be triggered
|
||||
print("Hi")
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Universal Actor methods
|
||||
All of these functions are found on actors
|
||||
- `self = multiObj:Pause()` — Pauses the actor from running
|
||||
- `self = multiObj:Resume()` — Resumes the actor that was paused
|
||||
- `nil = multiObj:Destroy()` — Removes the object from the mainloop
|
||||
- `bool = multiObj:isPaused()` — Returns true if the object is paused, false otherwise
|
||||
- `string = multiObj:getType()` — Returns the type of the object
|
||||
- `self = multiObj:SetTime(n)` — Sets a timer, and creates a special "timemaster" actor, which will timeout unless ResolveTimer is called
|
||||
- `self = multiObj:ResolveTimer(...)` — Stops the timer that was put onto the multiObj from timing out
|
||||
- `self = multiObj:OnTimedOut(func)` — If ResolveTimer was not called in time this event will be triggered. The function connected to it get a refrence of the original object that the timer was created on as the first argument.
|
||||
- `self = multiObj:OnTimerResolved(func)` — This event is triggered when the timer gets resolved. Same argument as above is passed, but the variable arguments that are accepted in resolvetimer are also passed as well.
|
||||
- `self = multiObj:Reset(n)` — In the cases where it isn't obvious what it does, it acts as Resume()
|
||||
- `self = multiObj:SetName(STRING name)`
|
||||
|
||||
# Actor: Events
|
||||
`event = multi:newEvent(FUNCTION task)` — The object that started it all. These are simply actors that wait for a condition to take place, then auto triggers an event. The event when triggered once isn't triggered again unless you Reset() it.
|
||||
|
||||
- `self = event:SetTask(FUNCTION func)` — This function is not needed if you supplied task at construction time
|
||||
- `self = event:OnEvent(FUNCTION func)` — Connects to the OnEvent event passes argument self to the connectee
|
||||
|
||||
Example:
|
||||
```lua
|
||||
multi,thread = require("multi"):init()
|
||||
count=0
|
||||
-- A loop object is used to demostrate how one could use an event object.
|
||||
loop=multi:newLoop(function(self,dt)
|
||||
count=count+1
|
||||
end)
|
||||
event=multi:newEvent(function() return count==100 end) -- set the event
|
||||
event:OnEvent(function(self) -- connect to the event object
|
||||
loop:Destroy() -- destroys the loop from running!
|
||||
print("Stopped that loop!",count)
|
||||
end) -- events like alarms need to be reset the Reset() command works here as well
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Actor: Updaters
|
||||
`updater = multi:newUpdater([NUMBER skip 1])` — set the amount of steps that are skipped.
|
||||
|
||||
Updaters are a mix between both loops and steps. They were a way to add basic priority management to loops (until a better way was added). Now they aren't as useful, but if you do not want the performance hit of turning on priority then they are useful to auro skip some loops. Note: The performance hit due to priority management is not as bas as it used to be.
|
||||
|
||||
- `self = updater:SetSkip(NUMBER n)` — sets the amount of steps that are skipped
|
||||
- `self = OnUpdate(FUNCTION func)` — connects to the main trigger of the updater which is called every nth step
|
||||
|
||||
Example:
|
||||
```lua
|
||||
multi,thread = require("multi"):init()
|
||||
updater=multi:newUpdater(5000) -- simple, think of a loop with the skip feature of a step
|
||||
updater:OnUpdate(function(self)
|
||||
print("updating...")
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Actor: Alarms
|
||||
`alarm = multi:newAlarm([NUMBER 0])` — creates an alarm which waits n seconds
|
||||
Alarms ring after a certain amount of time, but you need to reset the alarm every time it rings! Use a TLoop if you do not want to have to reset.
|
||||
|
||||
- `self = alarm:Reset([NUMBER sec current_time_set])` — Allows one to reset an alarm, optional argument to change the time until the next ring.
|
||||
- `self = alarm:OnRing(FUNCTION func` — Allows one to connect to the alarm event which is triggerd after a certain amount of time has passed.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
multi,thread = require("multi"):init()
|
||||
alarm=multi:newAlarm(3) -- in seconds can go to .001 uses the built in os.clock()
|
||||
alarm:OnRing(function(a)
|
||||
print("3 Seconds have passed!")
|
||||
a:Reset(n) -- if n were nil it will reset back to 3, or it would reset to n seconds
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Actor: Loops
|
||||
`loop = multi:newLoop(FUNCTION func)` — func the main connection that you can connect to. Is optional, but you can also use OnLoop(func) to connect as well.
|
||||
Loops are events that happen over and over until paused. They act like a while loop.
|
||||
|
||||
- `self = OnLoop(FUNCTION func)` — func the main connection that you can connect to. Alllows multiple connections to one loop if need be.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
local a = 0
|
||||
loop = multi:newLoop(function()
|
||||
a = a + 1
|
||||
if a == 1000 then
|
||||
print("a = 1000")
|
||||
loop:Pause()
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Actor: TLoops
|
||||
`tloop = multi:newTLoop(FUNCTION func ,NUMBER: [set 1])` — TLoops are pretty much the same as loops. The only difference is that they take set which is how long it waits, in seconds, before triggering function func.
|
||||
|
||||
- `self = OnLoop(FUNCTION func)` — func the main connection that you can connect to. Alllows multiple connections to one TLoop if need be.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
local a = 0
|
||||
loop = multi:newTLoop(function()
|
||||
a = a + 1
|
||||
if a == 10 then
|
||||
print("a = 10")
|
||||
loop:Pause()
|
||||
end
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Actor: Steps
|
||||
`step = multi:newStep(NUMBER start,*NUMBER reset, [NUMBER count 1], [NUMBER skip 0])` — Steps were originally introduced to bs used as for loops that can run parallel with other code. When using steps think of it like this: `for i=start,reset,count do` When the skip argument is given, each time the step object is given cpu cycles it will be skipped by n cycles. So if skip is 1 every other cpu cycle will be alloted to the step object.
|
||||
|
||||
- `self = step:OnStart(FUNCTION func(self))` — This connects a function to an event that is triggered everytime a step starts.
|
||||
- `self = step:OnStep(FUNCTION func(self,i))` — This connects a function to an event that is triggered every step or cycle that is alloted to the step object
|
||||
- `self = step:OnEnd(FUNCTION func(self))` — This connects a function to an event that is triggered when a step reaches its goal
|
||||
- `self = step:Update(NUMBER start,*NUMBER reset, [NUMBER count 1], [NUMBER skip 0])` — Update can be used to change the goals of the step.
|
||||
- `self = step:Reset()` — Resets the step
|
||||
|
||||
Example:
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
multi:newStep(1,10,1,0):OnStep(function(step,pos)
|
||||
print(step,pos)
|
||||
end):OnEnd(fucntion(step)
|
||||
step:Destroy()
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Actor: TSteps
|
||||
`tstep = multi:newStep(NUMBER start, NUMBER reset, [NUMBER count 1], [NUMBER set 1])` — TSteps work just like steps, the only difference is that instead of skip, we have set which is how long in seconds it should wait before triggering the OnStep() event.
|
||||
|
||||
- `self = tstep:OnStart(FUNCTION func(self))` — This connects a function to an event that is triggered everytime a step starts.
|
||||
- `self = tstep:OnStep(FUNCTION func(self,i))` — This connects a function to an event that is triggered every step or cycle that is alloted to the step object
|
||||
- `self = tstep:OnEnd(FUNCTION func(self))` — This connects a function to an event that is triggered when a step reaches its goal
|
||||
- `self = tstep:Update(NUMBER start,*NUMBER reset, [NUMBER count 1], [NUMBER set 1])` — Update can be used to change the goals of the step. You should call step:Reset() after using Update to restart the step.
|
||||
- `self = tstep:Reset([NUMBER n set])` — Allows you to reset a tstep that has ended, but also can change the time between each trigger.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
multi:newTStep(1,10,1,1):OnStep(function(step,pos)
|
||||
print(step,pos)
|
||||
end):OnEnd(fucntion(step)
|
||||
step:Destroy()
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# Coroutine based Threading (CBT)
|
||||
Helpful methods are wrapped around the builtin coroutine module which make it feel like real threading.
|
||||
|
||||
**threads.\* used within threaded enviroments**
|
||||
- `thread.sleep(NUMBER n)` — Holds execution of the thread until a certain amount of time has passed
|
||||
- `VARIABLE val = THREAD.hold(FUNCTION|CONNCETION|NUMBER func, TABLE options)` — Holds the current thread until a condition is met
|
||||
|
||||
| Option | Description |
|
||||
---|---
|
||||
| interval | Time between each poll |
|
||||
| cycles | Number of cycles before timing out |
|
||||
| sleep | Number of seconds before timing out |
|
||||
| skip | Number of cycles before testing again, does not cause a timeout! |
|
||||
|
||||
**Note:** cycles and sleep options cannot both be used at the same time. Interval and skip cannot be used at the same time either. Cycles take priority over sleep if both are present! HoldFor and HoldWithin can be emulated using the new features. Old functions will remain for backward compatibility.
|
||||
|
||||
Using cycles, sleep or interval will cause a timeout; returning nil, multi.TIMEOUT
|
||||
|
||||
`func` can be a number and `thread.hold` will act like `thread.sleep`. When `func` is a number the option table will be ignored!
|
||||
|
||||
`func` can be a connection and will hold until the condition is triggered. When using a connection the option table is ignored!
|
||||
|
||||
- `thread.skip(NUMBER n)` — How many cycles should be skipped until I execute again
|
||||
- `thread.kill()` — Kills the thread
|
||||
- `thread.yeild()` — Is the same as using thread.skip(0) or thread.sleep(0), hands off control until the next cycle
|
||||
- `BOOLEAM bool = thread.isThread()` — Returns true if the current running code is inside of a coroutine based thread
|
||||
- `NUMBER conres = thread.getCores()` — Returns the number of cores that the current system has. (used for system threads)
|
||||
- `thread.set(STRING name, VARIABLE val)` — A global interface where threads can talk with eachother. sets a variable with name and its value
|
||||
- `thread.get(STRING name)` — Gets the data stored in name
|
||||
- `VARIABLE val = thread.waitFor(STRING name)` — Holds executon of a thread until variable name exists
|
||||
- `thread.request(THREAD th,STRING cmd, VARIABLE args)` — Sends a request to the selected thread telling it to do a certain command
|
||||
- `th = thread.getRunningThread()` — Returns the currently running thread
|
||||
- `VARIABLE returns or nil, "TIMEOUT" = thread.holdFor(NUMBER: sec, FUNCTION: condition)` — Holds until a condidtion is met, or if there is a timeout nil,"TIMEOUT"
|
||||
- `VARIABLE returns or nil, "TIMEOUT" = thread.holdWithin(NUMBER: skip, FUNCTION: func)` — Holds until a condition is met or n cycles have happened.
|
||||
- `func = thread:newFunction(FUNCTION: func, [BOOLEAN: holdme false])` — func: The function you want to be threaded. holdme: If true the function waits until it has returns and then returns them. Otherwise the function returns a table
|
||||
- `func:Pause()` — Pauses a function, function will return `nil`, `"Function is paused"`
|
||||
- `func:Resume()` — Resumes a paused function
|
||||
- `func:holdMe(BOOLEAN: set)` — Sets the holdme argument to `set`
|
||||
- `handler = func(VARIABLE args)` — Calls the function, will return
|
||||
- `handler.isTFunc` — if true then its a threaded function
|
||||
- `handler.wait()` — waits for the function to finish and returns like normal
|
||||
- `handler.connect(Function: func(returns))` — Connects to the event that is triggered when the returns are avaiable and returns them
|
||||
- `VARIABLE returns = handler.wait()` — Waits until returns are avaiable and then
|
||||
- `handler.OnStatus(connector(VARIABLE args))` — A connection to the running function's status see example below
|
||||
- `handler.OnReturn(connector(VARIABLE args))` — A connection that is triggered when the running function is finished see example below
|
||||
- `handler.OnError(connector(nil,error))`
|
||||
|
||||
Example:
|
||||
|
||||
```lua
|
||||
package.path = "./?/init.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
|
||||
func = thread:newFunction(function(count)
|
||||
local a = 0
|
||||
while true do
|
||||
a = a + 1
|
||||
thread.sleep(.1)
|
||||
thread.pushStatus(a,count)
|
||||
if a == count then break end
|
||||
end
|
||||
return "Done"
|
||||
end)
|
||||
|
||||
multi:newThread("Function Status Test",function()
|
||||
local ret = func(10)
|
||||
local ret2 = func(15)
|
||||
local ret3 = func(20)
|
||||
ret.OnStatus(function(part,whole)
|
||||
--[[ Print out the current status. In this case every second it will update with:
|
||||
10%
|
||||
20%
|
||||
30%
|
||||
...
|
||||
100%
|
||||
|
||||
Function Done!
|
||||
]]
|
||||
print(math.ceil((part/whole)*1000)/10 .."%")
|
||||
end)
|
||||
ret2.OnStatus(function(part,whole)
|
||||
print("Ret2: ",math.ceil((part/whole)*1000)/10 .."%")
|
||||
end)
|
||||
ret3.OnStatus(function(part,whole)
|
||||
print("Ret3: ",math.ceil((part/whole)*1000)/10 .."%")
|
||||
end)
|
||||
-- Connections can now be added together, if you had multiple holds and one finished before others and wasn't consumed it would lock forever! This is now fixed
|
||||
thread.hold(ret2.OnReturn + ret.OnReturn + ret3.OnReturn)
|
||||
print("Function Done!")
|
||||
os.exit()
|
||||
end)
|
||||
```
|
||||
|
||||
<b>\*</b>A note about multi.NIL, this should only be used within the hold and hold like methods. thread.hold(), thread.holdFor(), and thread.holdWithin() methods. This is not needed within threaded functions! The reason hold prevents nil and false is because it is testing for a condition so the first argument needs to be non nil nor false! multi.NIL should not be used anywhere else. Sometimes you may need to pass a 'nil' value or return. While you could always return true or something you could use multi.NIL to force a nil value through a hold like method.
|
||||
|
||||
# CBT: newService(FUNCTION: func)
|
||||
`serv = newService(FUNCTION: func(self,TABLE: data))` — func is called each time the service is updated think of it like a loop multi-obj. self is the service object and data is a private table that only the service can see.
|
||||
- `serv.OnError(FUNCTION: func)` — connection that fired if there is an error
|
||||
- `serv.OnStopped(FUNCTION: func(serv))` — connection that is fired when a service is stopped
|
||||
- `serv.OnStarted(FUNCTION: func(serv))` — connection that is fired when a service is started
|
||||
- `serv.Start()` — Starts the service
|
||||
- `serv.Stop()` — Stops the service and destroys the data table
|
||||
- `serv.Pause()` — Pauses the service
|
||||
- `serv.Resume()` — Resumes the service
|
||||
- `serv.GetUpTime()` — Returns the amount of time the service has been running
|
||||
- `serv.SetPriority(PRIORITY: pri)` — Sets the priority of the service
|
||||
- `multi.Priority_Core`
|
||||
- `multi.Priority_Very_High`
|
||||
- `multi.Priority_High`
|
||||
- `multi.Priority_Above_Normal`
|
||||
- `multi.Priority_Normal` **Default**
|
||||
- `multi.Priority_Below_Normal`
|
||||
- `multi.Priority_Low`
|
||||
- `multi.Priority_Very_Low`
|
||||
- `multi.Priority_Idle`
|
||||
- `serv.SetScheme(NUMBER: n)` — Sets the scheme of the priority management
|
||||
- `1` **Default** — uses a time based style of yielding. thread.sleep()
|
||||
- `2` — uses a cycle based style of yielding. thread.skip()
|
||||
- `CONVERTS(serv) = serv.Destroy()` — Stops the service then Destroys the service triggering all events! The service becomes a destroyed object
|
||||
|
||||
Example:
|
||||
```lua
|
||||
-- Jobs are not natively part of the multi library. I planned on adding them, but decided against it. Below is the code that would have been used.
|
||||
-- Implementing a job manager using services
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
multi.Jobs = multi:newService(function(self,jobs)
|
||||
local job = table.remove(jobs,1)
|
||||
if job and job.removed==nil then
|
||||
job.func()
|
||||
end
|
||||
end)
|
||||
multi.Jobs.OnStarted(function(self,jobs)
|
||||
function self:newJob(func,name)
|
||||
table.insert(jobs,{
|
||||
func = func,
|
||||
name = name,
|
||||
removeJob = function(self) self.removed = true end
|
||||
})
|
||||
end
|
||||
function self:getJobs(name)
|
||||
local tab = {}
|
||||
if not name then return jobs end
|
||||
for i=1,#jobs do
|
||||
if name == jobs[i].name then
|
||||
table.insert(tab,jobs[i])
|
||||
end
|
||||
end
|
||||
return tab
|
||||
end
|
||||
function self:removeJobs(name)
|
||||
for i=1,#jobs do
|
||||
if name ~= nil and name == jobs[i].name then
|
||||
jobs[i]:removeJob()
|
||||
elseif name == nil then
|
||||
jobs[i]:removeJob()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi.Jobs.SetPriority(multi.Priority_Normal)
|
||||
multi.Jobs.Start()
|
||||
|
||||
-- Testing job stuff
|
||||
function pushJobs()
|
||||
multi.Jobs:newJob(function()
|
||||
print("job called")
|
||||
end) -- No name job
|
||||
multi.Jobs:newJob(function()
|
||||
print("job called2")
|
||||
end,"test")
|
||||
multi.Jobs:newJob(function()
|
||||
print("job called3")
|
||||
end,"test2")
|
||||
end
|
||||
pushJobs()
|
||||
pushJobs()
|
||||
local jobs = multi.Jobs:getJobs() -- gets all jobs
|
||||
local jobsn = multi.Jobs:getJobs("test") -- gets all jobs names 'test'
|
||||
jobsn[1]:removeJob() -- Select a job and remove it
|
||||
multi.Jobs:removeJobs("test2") -- Remove all jobs names 'test2'
|
||||
multi.Jobs.SetScheme(1) -- Jobs are internally a service, so setting scheme and priority
|
||||
multi.Jobs.SetPriority(multi.Priority_Core)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# CBT: newThread()
|
||||
`th = multi:newThread([STRING name,] FUNCTION func)` — Creates a new thread with name and function.
|
||||
|
||||
when within a thread, if you have any holding code you will want to use thread.* to give time to other threads while your code is running.
|
||||
Constants
|
||||
---
|
||||
- `th.Name` — Name of thread
|
||||
- `th.Type` — Type="thread"
|
||||
- `th.TID` — Thread ID
|
||||
- `conn = th.OnError(FUNCTION: callback)` — Connect to an event which is triggered when an error is encountered within a thread
|
||||
- `conn = th.OnDeath(FUNCTION: callback)` — Connect to an event which is triggered when the thread had either been killed or stopped running. (Not triggered when there is an error!)
|
||||
- `boolean = th:isPaused()`\* — Returns true if a thread has been paused
|
||||
- `self = th:Pause()`\* — Pauses a thread
|
||||
- `self = th:Resume()`\* — Resumes a paused thread
|
||||
- `self = th:Kill()`\* — Kills a thread
|
||||
- `self = th:Destroy()`\* — Destroys a thread
|
||||
|
||||
<b>*</b>Using these methods on a thread directly you are making a request to a thread! The thread may not accept your request, but it most likely will. You can contorl the thread flow within the thread's function itself
|
||||
|
||||
Examples:
|
||||
```lua
|
||||
package.path="?/init.lua;?.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
multi:newThread("Example of basic usage",function()
|
||||
while true do
|
||||
thread.sleep(1)
|
||||
print("We just made an alarm!")
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# CBT: newISOThread()
|
||||
`th = multi:newThread([STRING name,] FUNCTION func, TABLE: env)` — Creates a new thread with name and function func. Sets the enviroment of the func to env. Both the thread.* and multi.* are automatically placed in the enviroment.
|
||||
|
||||
When within a thread, if you have any holding code you will want to use thread.* to give time to other threads while your code is running. This type of thread does not have access to outside local or globals. Only what is in the env can be seen. (This thread was made so pesudo threading could work)
|
||||
Constants
|
||||
---
|
||||
- `th.Name` — Name of thread
|
||||
- `th.Type` — Type="thread"
|
||||
- `th.TID` — Thread ID
|
||||
- `conn = th.OnError(FUNCTION: callback)` — Connect to an event which is triggered when an error is encountered within a thread
|
||||
- `conn = th.OnDeath(FUNCTION: callback)` — Connect to an event which is triggered when the thread had either been killed or stopped running. (Not triggered when there is an error!)
|
||||
- `boolean = th:isPaused()`\* — Returns true if a thread has been paused
|
||||
- `self = th:Pause()`\* — Pauses a thread
|
||||
- `self = th:Resume()`\* — Resumes a paused thread
|
||||
- `self = th:Kill()`\* — Kills a thread
|
||||
- `self = th:Destroy()`\* — Destroys a thread
|
||||
|
||||
<b>*</b>Using these methods on a thread directly you are making a request to a thread! The thread may not accept your request, but it most likely will. You can contorl the thread flow within the thread's function itself
|
||||
```lua
|
||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
GLOBAL,THREAD = require("multi.integration.threading"):init() -- Auto detects your enviroment and uses what's available
|
||||
|
||||
jq = multi:newSystemThreadedJobQueue(5) -- Job queue with 4 worker threads
|
||||
func = jq:newFunction("test",function(a,b)
|
||||
THREAD.sleep(2)
|
||||
return a+b
|
||||
end)
|
||||
|
||||
for i = 1,10 do
|
||||
func(i,i*3).connect(function(data)
|
||||
print(data)
|
||||
end)
|
||||
end
|
||||
|
||||
local a = true
|
||||
b = false
|
||||
|
||||
multi:newThread("Standard Thread 1",function()
|
||||
while true do
|
||||
thread.sleep(1)
|
||||
print("Testing 1 ...",a,b,test)
|
||||
end
|
||||
end).OnError(function(self,msg)
|
||||
print(msg)
|
||||
end)
|
||||
|
||||
-- All upvalues are stripped! no access to the global, multi and thread are exposed however
|
||||
multi:newISOThread("ISO Thread 2",function()
|
||||
while true do
|
||||
thread.sleep(1)
|
||||
print("Testing 2 ...",a,b,test) -- a and b are nil, but test is true
|
||||
end
|
||||
end,{test=true,print=print})
|
||||
|
||||
.OnError(function(self,msg)
|
||||
print(msg)
|
||||
end)
|
||||
|
||||
multi:mainloop()
|
||||
```
|
||||
# System Threads (ST) - Multi-Integration Getting Started
|
||||
The system threads need to be required seperatly.
|
||||
```lua
|
||||
-- I recommend keeping these as globals. When using lanes you can use local and things will work, but if you use love2d and locals, upvalues are not transfered over threads and this can be an issue
|
||||
GLOBAL, THREAD = require("multi.integration.threading"):init() -- We will talk about the global and thread interface that is returned
|
||||
GLOBAL, THREAD = require("multi.integration.loveManager"):init()
|
||||
GLOBAL, THREAD = require("luvitManager") --*
|
||||
```
|
||||
Using this integration modifies some methods that the multi library has.
|
||||
- `multi:canSystemThread()` — Returns true if system threading is possible.
|
||||
- `multi:getPlatform()` — Returns (for now) either "lanes", "love2d" and "luvit"
|
||||
- `multi.isMainThread = true` — This is only modified on the main thread. So code that moves from one thread to another knows where it's at.
|
||||
|
||||
<b>*</b>GLOBAL and THREAD do not do anything when using the luvit integration
|
||||
|
||||
# ST - THREAD namespace
|
||||
- `THREAD.set(STRING name, VALUE val)` — Sets a value in GLOBAL
|
||||
- `THREAD.get(STRING name)` — Gets a value in GLOBAL
|
||||
- `THREAD.waitFor(STRING name)` — Waits for a value in GLOBAL to exist
|
||||
- `THREAD.getCores()` — Returns the number of actual system threads/cores
|
||||
- `THREAD.kill()` — Kills the thread
|
||||
- `THREAD.getName()` — Returns the name of the working thread
|
||||
- `THREAD.sleep(NUMBER n)` — Sleeps for an amount of time stopping the current thread
|
||||
- `THREAD.hold(FUNCTION func, TABLE options)` — Holds the current thread until a condition is met
|
||||
- `THREAD.getID()` — returns a unique ID for the current thread. This varaiable is visible to the main thread as well as by accessing it through the returned thread object. OBJ.Id
|
||||
|
||||
# ST - GLOBAL namespace
|
||||
Treat global like a table.
|
||||
```lua
|
||||
GLOBAL["name"] = "Ryan"
|
||||
print(GLOBAL["name"])
|
||||
```
|
||||
Removes the need to use THREAD.set() and THREAD.get()
|
||||
|
||||
ST - System Threads
|
||||
-------------------
|
||||
- `systemThread = multi:newSystemThread(STRING thread_name, FUNCTION spawned_function,ARGUMENTS ...)` — Spawns a thread with a certain name.
|
||||
- `systemThread:kill()` — kills a thread; can only be called in the main thread!
|
||||
- `systemThread.OnError(FUNCTION(systemthread,errMsg,errorMsgWithThreadName))`
|
||||
|
||||
System Threads are the feature that allows a user to interact with systen threads. It differs from regular coroutine based thread in how it can interact with variables. When using system threads the GLOBAL table is the "only way"* to send data. Spawning a System thread is really simple once all the required libraries are in place. See example below:
|
||||
|
||||
```lua
|
||||
multi,thread = require("multi"):init() -- keep this global when using lanes or implicitly define multi within the spawned thread
|
||||
local GLOBAL, THREAD = require("multi.integration.threading").init()
|
||||
multi:newSystemThread("Example thread",function()
|
||||
local multi = require("multi") -- we are in a thread so lets not refer to that upvalue!
|
||||
print("We have spawned a thread!")
|
||||
-- we could do work but theres no need to we can save that for other examples
|
||||
print("Lets have a non ending loop!")
|
||||
while true do
|
||||
-- If this was not in a thread execution would halt for the entire process
|
||||
end
|
||||
end,"A message that we are passing") -- There are restrictions on what can be passed!
|
||||
|
||||
tloop = multi:newTLoop(function()
|
||||
print("I'm still kicking!")
|
||||
end,1)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
<b>*</b>This isn't entirely true, as of right now the compatiablity with the lanes library and love2d engine have their own methods to share data, but if you would like to have your code work in both enviroments then using the GLOBAL table and the data structures provided by the multi library will ensure this happens. If you do not plan on having support for both platforms then feel free to use linda's in lanes and channels in love2d.
|
||||
|
||||
**Note:** luvit currently has very basic support, it only allows the spawning of system threads, but no way to send data back and forth as of yet. I do not know if this is doable or not, but I will keep looking into it.
|
||||
|
||||
# ST - System Threaded Objects
|
||||
Great we are able to spawn threads, but unless your working with a process that works on passed data and then uses a socket or writes to the disk I can't do to much with out being able to pass data between threads. This section we will look at how we can share objects between threads. In order to keep the compatibility between both love2d and lanes I had to format the system threaded objects in a strange way, but they are consistant and should work on both enviroments.
|
||||
|
||||
When creating objects with a name they are automatically exposed to the GLOBAL table. Which means you can retrieve them from a spawned thread. For example we have a queue object, which will be discussed in more detail next.
|
||||
|
||||
```lua
|
||||
-- Exposing a queue
|
||||
multi,thread = require("multi"):init()
|
||||
local GLOBAL, THREAD = require("multi.integration.threading").init() -- The standard setup above
|
||||
queue = multi:newSystemThreadedQueue("myQueue"):init() -- We create and initiate the queue for the main thread
|
||||
queue:push("This is a test!") -- We push some data onto the queue that other threads can consume and do stuff with
|
||||
multi:newSystemThread("Example thread",function() -- Create a system thread
|
||||
queue = THREAD.waitFor("myQueue"):init() -- Get the queue. It is good pratice to use the waitFor command when getting objects. If it doesn't exist yet we wait for it, preventing future errors. It is possible for the data to not ve present when a thread is looking for it! Especally when using the love2d module, my fault needs some rewriting data passing on the GLOBAL is quite slow, but the queue internally uses channels so after it is exposed you should have good speeds!
|
||||
local data = queue:pop() -- Get the data
|
||||
print(data) -- print the data
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
# ST - SystemThreadedQueue
|
||||
- `queue(nonInit) = multi:newSystemThreadedQueue(STRING name)` — You must enter a name!
|
||||
- `queue = queue:init()` — initiates the queue, without doing this it will not work
|
||||
- `void = queue:push(DATA data)` — Pushes data into a queue that all threads that have been shared have access to
|
||||
- `data = queue:pop()` — pops data from the queue removing it from all threads
|
||||
- `data = queue:peek()` — looks at data that is on the queue, but dont remove it from the queue
|
||||
|
||||
Let's get into some examples:
|
||||
```lua
|
||||
multi,thread = require("multi"):init()
|
||||
thread_names = {"Thread_A","Thread_B","Thread_C","Thread_D"}
|
||||
local GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||
queue = multi:newSystemThreadedQueue("myQueue"):init()
|
||||
for _,n in pairs(thread_names) do
|
||||
multi:newSystemThread(n,function()
|
||||
queue = THREAD.waitFor("myQueue"):init()
|
||||
local name = THREAD.getName()
|
||||
local data = queue:pop()
|
||||
while data do
|
||||
print(name.." "..data)
|
||||
data = queue:pop()
|
||||
end
|
||||
end)
|
||||
end
|
||||
for i=1,100 do
|
||||
queue:push(math.random(1,1000))
|
||||
end
|
||||
multi:newEvent(function() -- Felt like using the event object, I hardly use them for anything non internal
|
||||
return not queue:peek()
|
||||
end):OnEvent(function()
|
||||
print("No more data within the queue!")
|
||||
os.exit()
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
|
||||
You have probable noticed that the output from this is a total mess! Well I though so too, and created the system threaded console!
|
||||
|
||||
# ST - Using the Console
|
||||
`console = THREAD.getConsole()`
|
||||
|
||||
This does guarantee an order to console output, it does ensure that all things are on nice neat lines
|
||||
```lua
|
||||
multi,thread = require("multi"):init()
|
||||
local GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||
|
||||
console.print("Hello World!")
|
||||
```
|
||||
# ST - SystemThreadedJobQueue
|
||||
`jq = multi:newSystemThreadedJobQueue([NUMBER: threads])` — Creates a system threaded job queue with an optional number of threads
|
||||
- `boolean jq:isEmpty()` — Returns true if the jobqueue is empty false otherwise
|
||||
- `jq.cores = (supplied number) or (the number of cores on your system*2)`
|
||||
- `jq.OnJobCompleted(FUNCTION: func(jID,...))` — Connection that is triggered when a job has been completed. The jobID and returns of the job are supplies as arguments
|
||||
- `self = jq:doToAll(FUNCTION: func)` — Send data to every thread in the job queue. Useful if you want to require a module and have it available on all threads
|
||||
- `self = jq:registerFunction(STRING: name, FUNCTION: func)` — Registers a function on the job queue. Name is the name of function func
|
||||
- `jID = jq:pushJob(STRING: name,[...])` — Pushes a job onto the jobqueue
|
||||
- `handler = jq:newFunction([STRING: name], FUNCTION: func)` — returns a threaded Function that wraps around jq.registerFunction, jq.pushJob() and jq.OnJobCompleted() to provide an easy way to create and work with the jobqueue
|
||||
- `handler.connect(Function: func(returns))` — Connects to the event that is triggered when the returns are avaiable
|
||||
- `VARIAABLE returns = handler.wait()` — Waits until returns are avaiable and then returns them
|
||||
|
||||
**Note:** Created functions using this method act as normal functions on the queue side of things. So you can call the functions from other queue functions as if they were normal functions.
|
||||
|
||||
Example:
|
||||
```lua
|
||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||
local jq = multi:newSystemThreadedJobQueue(4) -- job queue using 4 cores
|
||||
jq:doToAll(function()
|
||||
Important = 15
|
||||
end)
|
||||
jq:registerFunction("test",function(a,b)
|
||||
--print(a,b,a+b)
|
||||
return true
|
||||
end)
|
||||
jq.OnJobCompleted(function(jid,arg)
|
||||
print(jid,arg)
|
||||
end)
|
||||
local jid = jq:pushJob("test",10,5)
|
||||
print("Job pushed! ID = ".. jid)
|
||||
local func = jq:newFunction("test2",function(a,b)
|
||||
print(a,b,a*b)
|
||||
return
|
||||
end)
|
||||
print("Waited",func(10,5).wait())
|
||||
func(5,5).connect(function(ret)
|
||||
print("Connected",ret)
|
||||
os.exit()
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
# ST - SystemThreadedTable
|
||||
`stt = multi:newSystemThreadedTable(STRING: name)`
|
||||
- `stt:init()` — Used to init object over threads
|
||||
- `stt[var] = val`
|
||||
- `val = stt[var]`
|
||||
|
||||
Example:
|
||||
```lua
|
||||
package.path="?.lua;?/init.lua;?.lua;?/?/init.lua;"..package.path
|
||||
multi,thread = require("multi"):init()
|
||||
GLOBAL, THREAD = require("multi.integration.threading"):init()
|
||||
local stt = multi:newSystemThreadedTable("stt")
|
||||
stt["hello"] = "world"
|
||||
multi:newSystemThread("test thread",function()
|
||||
local stt = GLOBAL["stt"]:init()
|
||||
print(stt["hello"])
|
||||
end)
|
||||
multi:mainloop()
|
||||
```
|
||||
# Network Threads - Multi-Integration WIP Being Reworked
|
||||
More of a fun project of mine then anything core to to the library it will be released and documented when it is ready. I do not have a timeframe for this
|
||||
|
||||
3535
docs/changes.md
Normal file
3535
docs/changes.md
Normal file
File diff suppressed because it is too large
Load Diff
106
integration/debugManager/init.lua
Normal file
106
integration/debugManager/init.lua
Normal file
@ -0,0 +1,106 @@
|
||||
local multi, thread = require("multi"):init()
|
||||
|
||||
multi.defaultSettings.debugging = true
|
||||
|
||||
local dbg = {}
|
||||
dbg.__index = dbg
|
||||
dbg.processors = {}
|
||||
|
||||
-- Hooks to all on object created events!
|
||||
local c_cache = {}
|
||||
local d_cache = {}
|
||||
|
||||
local proc = multi:newProcessor("Debug_Processor").Start()
|
||||
|
||||
dbg.OnObjectCreated = function(obj, process)
|
||||
if c_cache[obj] then
|
||||
return false
|
||||
else
|
||||
c_cache[obj] = true
|
||||
proc:newTask(function()
|
||||
c_cache[obj] = false
|
||||
end)
|
||||
return true
|
||||
end
|
||||
end .. multi:newConnection()
|
||||
|
||||
dbg.OnObjectDestroyed = function(obj, process)
|
||||
if d_cache[obj] then
|
||||
return false
|
||||
else
|
||||
d_cache[obj] = true
|
||||
proc:newTask(function()
|
||||
d_cache[obj] = false
|
||||
end)
|
||||
return true
|
||||
end
|
||||
end .. multi:newConnection()
|
||||
|
||||
local creation_hook, destruction_hook
|
||||
local types
|
||||
local objects = {}
|
||||
|
||||
creation_hook = function(obj, process)
|
||||
types = multi:getTypes()
|
||||
if obj.Type == multi.PROCESS and not dbg.processors[obj] then
|
||||
obj.OnObjectCreated(creation_hook)
|
||||
obj.OnObjectDestroyed(destruction_hook)
|
||||
end
|
||||
|
||||
table.insert(objects, obj)
|
||||
|
||||
dbg.OnObjectCreated:Fire(obj, process)
|
||||
end
|
||||
|
||||
destruction_hook = function(obj, process)
|
||||
for i = 1, #objects do
|
||||
if objects[i] == obj then
|
||||
table.remove(objects, i)
|
||||
break
|
||||
end
|
||||
end
|
||||
dbg.OnObjectDestroyed:Fire(obj, process)
|
||||
end
|
||||
|
||||
function dbg:getObjects(typ)
|
||||
if type(typ) == "string" then
|
||||
local objs = {}
|
||||
for i = 1, #objects do
|
||||
if objects[i].Type == typ then
|
||||
objs[#objs+1] = objects[i]
|
||||
end
|
||||
end
|
||||
return objs
|
||||
elseif type(typ) == "table" then -- Process
|
||||
local objs = {}
|
||||
for i = 1, #objects do
|
||||
if objects[i].Parent == typ then
|
||||
objs[#objs+1] = objects[i]
|
||||
end
|
||||
end
|
||||
return objs
|
||||
elseif type(typ) == "function" then
|
||||
local objs = {}
|
||||
-- Keep objects local/private, return true to add to list, false to reject, "break" to break loop
|
||||
for i = 1, #objects do
|
||||
local ret = typ(objects[i])
|
||||
if ret then
|
||||
objs[#objs+1] = objects[i]
|
||||
elseif ret == "break" then
|
||||
break
|
||||
end
|
||||
end
|
||||
return objs
|
||||
end
|
||||
end
|
||||
|
||||
local debug_stats = {}
|
||||
|
||||
local tmulti = multi:getThreadManagerProcess()
|
||||
multi.OnObjectCreated(creation_hook)
|
||||
tmulti.OnObjectCreated(creation_hook)
|
||||
multi.OnObjectDestroyed(destroction_hook)
|
||||
tmulti.OnObjectDestroyed(destroction_hook)
|
||||
|
||||
-- We write to a debug interface in the multi namespace
|
||||
multi.debugging = dbg
|
||||
0
integration/effilManager/extensions.lua
Normal file
0
integration/effilManager/extensions.lua
Normal file
46
integration/effilManager/init.lua
Normal file
46
integration/effilManager/init.lua
Normal file
@ -0,0 +1,46 @@
|
||||
local multi, thread = require("multi"):init{error=true}
|
||||
multi.error("Currntly not supported!")
|
||||
os.exit(1)
|
||||
local effil = require("effil")
|
||||
|
||||
-- I like some of the things that this library offers.
|
||||
-- Current limitations prevent me from being able to use effil,
|
||||
-- but I might fork and work on it myself.
|
||||
|
||||
-- Configs
|
||||
effil.allow_table_upvalues(false)
|
||||
|
||||
local GLOBAL,THREAD = require("multi.integration.effilManager.threads").init()
|
||||
local count = 1
|
||||
local started = false
|
||||
local livingThreads = {}
|
||||
|
||||
function multi:newSystemThread(name, func, ...)
|
||||
local name = name or multi.randomString(16)
|
||||
local rand = math.random(1, 10000000)
|
||||
c = {}
|
||||
c.name = name
|
||||
c.Name = name
|
||||
c.Id = count
|
||||
end
|
||||
|
||||
function THREAD:newFunction(func, holdme)
|
||||
return thread:newFunctionBase(function(...)
|
||||
return multi:newSystemThread("TempSystemThread",func,...)
|
||||
end, holdme, multi.SFUNCTION)()
|
||||
end
|
||||
|
||||
THREAD.newSystemThread = function(...)
|
||||
multi:newSystemThread(...)
|
||||
end
|
||||
|
||||
multi.print("Integrated Effil Threading!")
|
||||
multi.integration = {} -- for module creators
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
require("multi.integration.effilManager.extensions")
|
||||
return {
|
||||
init = function()
|
||||
return GLOBAL, THREAD
|
||||
end
|
||||
}
|
||||
0
integration/effilManager/threads.lua
Normal file
0
integration/effilManager/threads.lua
Normal file
393
integration/lanesManager/extensions.lua
Normal file
393
integration/lanesManager/extensions.lua
Normal file
@ -0,0 +1,393 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi, thread = require("multi"):init()
|
||||
|
||||
if not (GLOBAL and THREAD) then
|
||||
GLOBAL, THREAD = multi.integration.GLOBAL, multi.integration.THREAD
|
||||
else
|
||||
lanes = require("lanes")
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedQueue(name)
|
||||
local name = name or multi.randomString(16)
|
||||
local c = {}
|
||||
c.Name = name
|
||||
c.linda = lanes.linda()
|
||||
c.Type = multi.registerType("s_queue")
|
||||
|
||||
function c:push(v)
|
||||
self.linda:send("Q", v)
|
||||
end
|
||||
|
||||
function c:pop()
|
||||
return ({self.linda:receive(0, "Q")})[2]
|
||||
end
|
||||
|
||||
function c:peek()
|
||||
return self.linda:get("Q")
|
||||
end
|
||||
|
||||
function c:init()
|
||||
return self
|
||||
end
|
||||
|
||||
if multi.isMainThread then
|
||||
multi.integration.GLOBAL[name] = c
|
||||
else
|
||||
GLOBAL[name] = c
|
||||
end
|
||||
|
||||
function c:Hold(opt)
|
||||
local multi, thread = require("multi"):init()
|
||||
if opt.peek then
|
||||
return thread.hold(function()
|
||||
return self:peek()
|
||||
end)
|
||||
else
|
||||
return thread.hold(function()
|
||||
return self:pop()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
self:create(c)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local name = name or multi.randomString(16)
|
||||
local c = {}
|
||||
c.link = lanes.linda()
|
||||
c.Name = name
|
||||
c.Type = multi.registerType("s_table")
|
||||
|
||||
function c:init()
|
||||
return self
|
||||
end
|
||||
|
||||
setmetatable(c,{
|
||||
__index = function(t,k)
|
||||
return c.link:get(k)
|
||||
end,
|
||||
__newindex = function(t,k,v)
|
||||
c.link:set(k, v)
|
||||
end
|
||||
})
|
||||
|
||||
if multi.isMainThread then
|
||||
multi.integration.GLOBAL[name] = c
|
||||
else
|
||||
GLOBAL[name] = c
|
||||
end
|
||||
|
||||
function c:Hold(opt)
|
||||
local multi, thread = require("multi"):init()
|
||||
if opt.key then
|
||||
return thread.hold(function()
|
||||
return self.tab[opt.key]
|
||||
end)
|
||||
else
|
||||
multi.error("Must provide a key to check opt.key = 'key'")
|
||||
end
|
||||
end
|
||||
|
||||
self:create(c)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedJobQueue(n)
|
||||
local c = {}
|
||||
c.cores = n or THREAD.getCores()*2
|
||||
c.Type = multi.registerType("s_jobqueue")
|
||||
c.OnJobCompleted = multi:newConnection()
|
||||
local funcs = multi:newSystemThreadedTable()
|
||||
local queueJob = multi:newSystemThreadedQueue()
|
||||
local queueReturn = multi:newSystemThreadedQueue()
|
||||
local doAll = multi:newSystemThreadedQueue()
|
||||
local ID=1
|
||||
local jid = 1
|
||||
function c:isEmpty()
|
||||
return queueJob:peek()==nil
|
||||
end
|
||||
function c:doToAll(func,...)
|
||||
for i=1,c.cores do
|
||||
doAll:push{ID,func,...}
|
||||
end
|
||||
ID = ID + 1
|
||||
return self
|
||||
end
|
||||
function c:registerFunction(name,func)
|
||||
funcs[name]=func
|
||||
return self
|
||||
end
|
||||
function c:pushJob(name,...)
|
||||
queueJob:push{name,jid,multi.pack(...)}
|
||||
jid = jid + 1
|
||||
return jid-1
|
||||
end
|
||||
local nFunc = 0
|
||||
function c:newFunction(name, func, holup) -- This registers with the queue
|
||||
if type(name)=="function" then
|
||||
holup = func
|
||||
func = name
|
||||
name = "JQ_Function_"..nFunc
|
||||
end
|
||||
nFunc = nFunc + 1
|
||||
c:registerFunction(name,func)
|
||||
return thread:newFunction(function(...)
|
||||
local id = c:pushJob(name,...)
|
||||
local link
|
||||
local rets
|
||||
link = c.OnJobCompleted(function(jid,...)
|
||||
if id==jid then
|
||||
rets = multi.pack(...)
|
||||
end
|
||||
end)
|
||||
return thread.hold(function()
|
||||
if rets then
|
||||
if #rets == 0 then
|
||||
return multi.NIL
|
||||
else
|
||||
return multi.unpack(rets)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end, holup), name
|
||||
end
|
||||
thread:newThread("JobQueueManager",function()
|
||||
while true do
|
||||
local job = thread.hold(function()
|
||||
return queueReturn:pop()
|
||||
end)
|
||||
if job then
|
||||
local id = table.remove(job,1)
|
||||
c.OnJobCompleted:Fire(id,multi.unpack(job))
|
||||
end
|
||||
end
|
||||
end)
|
||||
for i=1,c.cores do
|
||||
multi:newSystemThread("STJQ_"..multi.randomString(8),function(queue)
|
||||
local multi, thread = require("multi"):init()
|
||||
local idle = os.clock()
|
||||
local clock = os.clock
|
||||
local ref = 0
|
||||
_G["__QR"] = queueReturn
|
||||
setmetatable(_G,{__index = funcs})
|
||||
thread:newThread("JobHandler",function()
|
||||
while true do
|
||||
local dat = thread.hold(function()
|
||||
return queueJob:pop()
|
||||
end)
|
||||
idle = clock()
|
||||
thread:newThread("JobQueue-Spawn",function()
|
||||
local name = table.remove(dat, 1)
|
||||
local jid = table.remove(dat, 1)
|
||||
local args = table.remove(dat, 1)
|
||||
queueReturn:push{jid, funcs[name](args[1],args[2],args[3],args[4],args[5],args[6],args[7],args[8]), queue}
|
||||
end)
|
||||
end
|
||||
end)
|
||||
thread:newThread("DoAllHandler",function()
|
||||
while true do
|
||||
local dat = thread.hold(function()
|
||||
return doAll:peek()
|
||||
end)
|
||||
if dat then
|
||||
if dat[1]>ref then
|
||||
ref = table.remove(dat, 1)
|
||||
func = table.remove(dat, 1)
|
||||
idle = clock()
|
||||
func(unpack(dat))
|
||||
doAll:pop()
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
thread:newThread("IdleHandler",function()
|
||||
while true do
|
||||
thread.hold(function()
|
||||
return clock()-idle>3
|
||||
end)
|
||||
THREAD.sleep(.01)
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
end,i)
|
||||
end
|
||||
|
||||
function c:Hold(opt)
|
||||
return thread.hold(self.OnJobCompleted)
|
||||
end
|
||||
|
||||
self:create(c)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedConnection(name)
|
||||
local name = name or multi.randomString(16)
|
||||
local c = {}
|
||||
c.Type = multi.registerType("s_connection")
|
||||
c.CONN = 0x00
|
||||
c.TRIG = 0x01
|
||||
c.PING = 0x02
|
||||
c.PONG = 0x03
|
||||
local function remove(a, b)
|
||||
local ai = {}
|
||||
local r = {}
|
||||
for k,v in pairs(a) do ai[v]=true end
|
||||
for k,v in pairs(b) do
|
||||
if ai[v]==nil then table.insert(r,a[k]) end
|
||||
end
|
||||
return r
|
||||
end
|
||||
c.CID = THREAD_ID
|
||||
c.subscribe = multi:newSystemThreadedQueue("SUB_STC_"..self.Name):init()
|
||||
c.Name = name
|
||||
c.links = {} -- All triggers sent from main connection. When a connection is triggered on another thread, they speak to the main then send stuff out.
|
||||
-- Locals will only live in the thread that creates the original object
|
||||
local ping
|
||||
local pong = function(link, links)
|
||||
local res = thread.hold(function()
|
||||
return link:peek()[1] == c.PONG
|
||||
end,{sleep=3})
|
||||
|
||||
if not res then
|
||||
for i=1,#links do
|
||||
if links[i] == link then
|
||||
table.remove(links,i,link)
|
||||
break
|
||||
end
|
||||
end
|
||||
else
|
||||
link:pop()
|
||||
end
|
||||
end
|
||||
|
||||
ping = thread:newFunction(function(self)
|
||||
ping:Pause()
|
||||
multi.ForEach(self.links, function(link) -- Sync new connections
|
||||
link:push{self.PING}
|
||||
multi:newThread("pong Thread", pong, link, self.links)
|
||||
end)
|
||||
|
||||
thread.sleep(3)
|
||||
|
||||
ping:Resume()
|
||||
end,false)
|
||||
|
||||
local function fire(...)
|
||||
for _, link in pairs(c.links) do
|
||||
link:push {c.TRIG, multi.pack(...)}
|
||||
end
|
||||
end
|
||||
|
||||
thread:newThread("STC_SUB_MAN"..name,function()
|
||||
local item
|
||||
local sub_func = function() -- This will keep things held up until there is something to process
|
||||
return c.subscribe:pop()
|
||||
end
|
||||
while true do
|
||||
thread.yield()
|
||||
-- We need to check on broken connections
|
||||
ping(c) -- Should return instantlly and process this in another thread
|
||||
item = thread.hold(sub_func)
|
||||
if item[1] == c.CONN then
|
||||
multi.ForEach(c.links, function(link) -- Sync new connections
|
||||
item[2]:push{c.CONN, link}
|
||||
end)
|
||||
c.links[#c.links+1] = item[2]
|
||||
elseif item[1] == c.TRIG then
|
||||
fire(multi.unpack(item[2]))
|
||||
c.proxy_conn:Fire(multi.unpack(item[2]))
|
||||
end
|
||||
end
|
||||
end)
|
||||
--- ^^^ This will only exist in the init thread
|
||||
|
||||
function c:Fire(...)
|
||||
local args = multi.pack(...)
|
||||
if self.CID == THREAD_ID then -- Host Call
|
||||
for _, link in pairs(self.links) do
|
||||
link:push {self.TRIG, args}
|
||||
end
|
||||
self.proxy_conn:Fire(...)
|
||||
else
|
||||
self.subscribe:push {self.TRIG, args}
|
||||
end
|
||||
end
|
||||
|
||||
function c:init()
|
||||
local multi, thread = require("multi"):init()
|
||||
self.links = {}
|
||||
self.proxy_conn = multi:newConnection()
|
||||
local mt = getmetatable(self.proxy_conn)
|
||||
local tempMT = {}
|
||||
for i,v in pairs(mt) do
|
||||
tempMT[i] = v
|
||||
end
|
||||
tempMT.__index = self.proxy_conn
|
||||
tempMT.__call = function(t,func) self.proxy_conn(func) end
|
||||
setmetatable(self, tempMT)
|
||||
if self.CID == THREAD_ID then return self end
|
||||
thread:newThread("STC_CONN_MAN"..name,function()
|
||||
local item
|
||||
local link_self_ref = multi:newSystemThreadedQueue()
|
||||
self.subscribe:push{self.CONN, link_self_ref}
|
||||
while true do
|
||||
item = thread.hold(function()
|
||||
return link_self_ref:peek()
|
||||
end)
|
||||
if item[1] == self.PING then
|
||||
link_self_ref:push{self.PONG}
|
||||
link_self_ref:pop()
|
||||
elseif item[1] == self.CONN then
|
||||
if item[2].Name ~= link_self_ref.Name then
|
||||
table.insert(self.links, item[2])
|
||||
end
|
||||
link_self_ref:pop()
|
||||
elseif item[1] == self.TRIG then
|
||||
self.proxy_conn:Fire(multi.unpack(item[2]))
|
||||
link_self_ref:pop()
|
||||
else
|
||||
-- This shouldn't be the case
|
||||
end
|
||||
end
|
||||
end)
|
||||
return self
|
||||
end
|
||||
|
||||
if multi.isMainThread then
|
||||
multi.integration.GLOBAL[name] = c
|
||||
else
|
||||
GLOBAL[name] = c
|
||||
end
|
||||
|
||||
self:create(c)
|
||||
|
||||
return c
|
||||
end
|
||||
require("multi.integration.sharedExtensions")
|
||||
205
integration/lanesManager/init.lua
Normal file
205
integration/lanesManager/init.lua
Normal file
@ -0,0 +1,205 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
|
||||
package.path = "?/init.lua;?.lua;" .. package.path
|
||||
multi, thread = require("multi"):init() -- get it all and have it on all lanes
|
||||
if multi.integration then -- This allows us to call the lanes manager from supporting modules without a hassle
|
||||
return {
|
||||
init = function()
|
||||
return multi.integration.GLOBAL, multi.integration.THREAD
|
||||
end
|
||||
}
|
||||
end
|
||||
-- Step 1 get lanes
|
||||
lanes = require("lanes").configure()
|
||||
multi.SystemThreads = {}
|
||||
multi.isMainThread = true
|
||||
|
||||
_G.THREAD_NAME = "MAIN_THREAD"
|
||||
_G.THREAD_ID = 0
|
||||
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
|
||||
function multi:getPlatform()
|
||||
return "lanes"
|
||||
end
|
||||
|
||||
-- Step 2 set up the Linda objects
|
||||
local __GlobalLinda = lanes.linda() -- handles global stuff
|
||||
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
|
||||
local __ConsoleLinda = lanes.linda() -- handles console stuff
|
||||
local __StatusLinda = lanes.linda() -- handles pushstatus for stfunctions
|
||||
|
||||
local GLOBAL,THREAD = require("multi.integration.lanesManager.threads").init(__GlobalLinda, __SleepingLinda, __StatusLinda, __ConsoleLinda)
|
||||
local count = 1
|
||||
local started = false
|
||||
local livingThreads = {}
|
||||
|
||||
function THREAD:newFunction(func, holdme)
|
||||
return thread:newFunctionBase(function(...)
|
||||
return multi:newSystemThread("TempSystemThread",func,...)
|
||||
end, holdme, multi.registerType("s_function"))()
|
||||
end
|
||||
|
||||
function multi:newSystemThread(name, func, ...)
|
||||
local name = name or multi.randomString(16)
|
||||
multi.InitSystemThreadErrorHandler()
|
||||
local rand = math.random(1, 10000000)
|
||||
local return_linda = lanes.linda()
|
||||
c = {}
|
||||
c.Name = name
|
||||
c.ID = count
|
||||
c.loadString = {"base","package","os","io","math","table","string","coroutine"}
|
||||
livingThreads[count] = {true, name}
|
||||
c.returns = return_linda
|
||||
c.Type = multi.registerType("s_thread")
|
||||
c.creationTime = os.clock()
|
||||
c.alive = true
|
||||
c.priority = THREAD.Priority_Normal
|
||||
local multi_settings = multi.defaultSettings
|
||||
local globe = {
|
||||
THREAD_NAME = name,
|
||||
THREAD_ID = count,
|
||||
THREAD = THREAD,
|
||||
GLOBAL = GLOBAL,
|
||||
_Console = __ConsoleLinda,
|
||||
_DEFER = {}
|
||||
}
|
||||
if GLOBAL["__env"] then
|
||||
for i,v in pairs(GLOBAL["__env"]) do
|
||||
globe[i] = v
|
||||
end
|
||||
end
|
||||
c.thread = lanes.gen("*",
|
||||
{
|
||||
globals = globe,
|
||||
priority = c.priority
|
||||
},function(...)
|
||||
multi, thread = require("multi"):init(multi_settings)
|
||||
require("multi.integration.lanesManager.extensions")
|
||||
require("multi.integration.sharedExtensions")
|
||||
local has_error = true
|
||||
returns = {pcall(func, ...)}
|
||||
return_linda:set("returns", returns)
|
||||
for i,v in pairs(_DEFER) do
|
||||
pcall(v)
|
||||
end
|
||||
has_error = false
|
||||
end)(...)
|
||||
count = count + 1
|
||||
function c:getName()
|
||||
return c.Name
|
||||
end
|
||||
function c:kill()
|
||||
self.thread:cancel()
|
||||
self.alive = false
|
||||
end
|
||||
table.insert(multi.SystemThreads, c)
|
||||
c.OnDeath = multi:newConnection()
|
||||
c.OnError = multi:newConnection()
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
c.OnError(multi.error)
|
||||
|
||||
if self.isActor then
|
||||
self:create(c)
|
||||
else
|
||||
multi.create(multi, c)
|
||||
end
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
THREAD.newSystemThread = function(...)
|
||||
multi:newSystemThread(...)
|
||||
end
|
||||
|
||||
function multi.InitSystemThreadErrorHandler()
|
||||
if started == true then
|
||||
return
|
||||
end
|
||||
started = true
|
||||
thread:newThread("SystemThreadScheduler",function()
|
||||
local threads = multi.SystemThreads
|
||||
local _,data,status,push,temp
|
||||
while true do
|
||||
thread.yield()
|
||||
_,data = __ConsoleLinda:receive(0, "Q")
|
||||
if data then
|
||||
--print(data[1])
|
||||
end
|
||||
for i = #threads, 1, -1 do
|
||||
temp = threads[i]
|
||||
status = temp.thread.status
|
||||
push = __StatusLinda:get(temp.ID)
|
||||
if push then
|
||||
temp.statusconnector:Fire(multi.unpack(({__StatusLinda:receive(nil, temp.ID)})[2]))
|
||||
end
|
||||
if status == "done" or temp.returns:get("returns") then
|
||||
returns = ({temp.returns:receive(0, "returns")})[2]
|
||||
livingThreads[temp.ID] = {false, temp.Name}
|
||||
temp.alive = false
|
||||
if returns[1] == false then
|
||||
temp.OnError:Fire(temp, returns[2])
|
||||
else
|
||||
table.remove(returns,1)
|
||||
temp.OnDeath:Fire(multi.unpack(returns))
|
||||
end
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
table.remove(threads, i)
|
||||
elseif status == "running" then
|
||||
--
|
||||
elseif status == "waiting" then
|
||||
--
|
||||
elseif status == "error" then
|
||||
-- The thread never really errors, we handle this through our linda object
|
||||
elseif status == "cancelled" then
|
||||
livingThreads[temp.ID] = {false, temp.Name}
|
||||
temp.alive = false
|
||||
temp.OnError:Fire(temp,"thread_cancelled")
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
table.remove(threads, i)
|
||||
elseif status == "killed" then
|
||||
livingThreads[temp.ID] = {false, temp.Name}
|
||||
temp.alive = false
|
||||
temp.OnError:Fire(temp,"thread_killed")
|
||||
GLOBAL["__THREADS__"] = livingThreads
|
||||
table.remove(threads, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end).OnError(multi.error)
|
||||
end
|
||||
|
||||
multi.print("Integrated Lanes Threading!")
|
||||
multi.integration = {} -- for module creators
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
require("multi.integration.lanesManager.extensions")
|
||||
return {
|
||||
init = function()
|
||||
return GLOBAL, THREAD
|
||||
end
|
||||
}
|
||||
151
integration/lanesManager/threads.lua
Normal file
151
integration/lanesManager/threads.lua
Normal file
@ -0,0 +1,151 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local function getOS()
|
||||
if package.config:sub(1, 1) == "\\" then
|
||||
return "windows"
|
||||
else
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
|
||||
local function INIT(__GlobalLinda, __SleepingLinda, __StatusLinda, __Console)
|
||||
local THREAD = {}
|
||||
THREAD.Priority_Core = 3
|
||||
THREAD.Priority_High = 2
|
||||
THREAD.Priority_Above_Normal = 1
|
||||
THREAD.Priority_Normal = 0
|
||||
THREAD.Priority_Below_Normal = -1
|
||||
THREAD.Priority_Low = -2
|
||||
THREAD.Priority_Idle = -3
|
||||
|
||||
function THREAD.set(name, val)
|
||||
__GlobalLinda:set(name, val)
|
||||
end
|
||||
|
||||
function THREAD.get(name)
|
||||
return __GlobalLinda:get(name)
|
||||
end
|
||||
|
||||
function THREAD.waitFor(name)
|
||||
local multi, thread = require("multi"):init()
|
||||
return multi.hold(function()
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(.001, "__non_existing_variable")
|
||||
return __GlobalLinda:get(name)
|
||||
end)
|
||||
end
|
||||
|
||||
function THREAD.getCores()
|
||||
return THREAD.__CORES
|
||||
end
|
||||
|
||||
function THREAD.getConsole()
|
||||
local c = {}
|
||||
c.queue = __Console
|
||||
function c.print(...)
|
||||
c.queue:push("Q", table.concat(multi.pack(...), "\t"))
|
||||
end
|
||||
function c.error(err)
|
||||
c.queue:push("Q", "Error in <"..THREAD_NAME..":" .. THREAD_ID .. ">: ".. err)
|
||||
multi.error(err)
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function THREAD.getThreads()
|
||||
return GLOBAL.__THREADS__
|
||||
end
|
||||
|
||||
if os.getOS() == "windows" then
|
||||
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
|
||||
function THREAD.kill() -- trigger the lane destruction
|
||||
error("Thread was killed!\1")
|
||||
end
|
||||
|
||||
function THREAD.sync()
|
||||
-- Maybe do something...
|
||||
end
|
||||
|
||||
function THREAD.pushStatus(...)
|
||||
local args = multi.pack(...)
|
||||
__StatusLinda:send(nil,THREAD_ID, args)
|
||||
end
|
||||
|
||||
_G.THREAD_ID = 0
|
||||
|
||||
function THREAD.sleep(n)
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(n, "__non_existing_variable")
|
||||
end
|
||||
|
||||
function THREAD.hold(n)
|
||||
local function wait()
|
||||
math.randomseed(os.time())
|
||||
__SleepingLinda:receive(.001, "__non_existing_variable")
|
||||
end
|
||||
repeat
|
||||
wait()
|
||||
until n()
|
||||
end
|
||||
|
||||
local GLOBAL = {}
|
||||
setmetatable(GLOBAL, {
|
||||
__index = function(t, k)
|
||||
return __GlobalLinda:get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
__GlobalLinda:set(k, v)
|
||||
end
|
||||
})
|
||||
|
||||
function THREAD.setENV(env, name)
|
||||
GLOBAL[name or "__env"] = env
|
||||
end
|
||||
|
||||
function THREAD.getENV(name)
|
||||
return GLOBAL[name or "__env"]
|
||||
end
|
||||
|
||||
function THREAD.exposeENV(name)
|
||||
name = name or "__env"
|
||||
local env = THREAD.getENV(name)
|
||||
for i,v in pairs(env) do
|
||||
_G[i] = v
|
||||
end
|
||||
end
|
||||
|
||||
function THREAD.defer(func)
|
||||
table.insert(_DEFER, func)
|
||||
end
|
||||
|
||||
return GLOBAL, THREAD
|
||||
end
|
||||
|
||||
return {init = function(g,s,st,c,onexit)
|
||||
return INIT(g,s,st,c,onexit)
|
||||
end}
|
||||
244
integration/loveManager/extensions.lua
Normal file
244
integration/loveManager/extensions.lua
Normal file
@ -0,0 +1,244 @@
|
||||
if not ISTHREAD then
|
||||
multi, thread = require("multi").init()
|
||||
GLOBAL = multi.integration.GLOBAL
|
||||
THREAD = multi.integration.THREAD
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedQueue(name)
|
||||
local name = name or multi.randomString(16)
|
||||
|
||||
local c = {}
|
||||
|
||||
c.Name = name
|
||||
c.Type = multi.registerType("s_queue")
|
||||
c.chan = love.thread.getChannel(name)
|
||||
|
||||
function c:push(dat)
|
||||
self.chan:push(THREAD.packValue(dat))
|
||||
end
|
||||
|
||||
function c:pop()
|
||||
return THREAD.unpackValue(self.chan:pop())
|
||||
end
|
||||
|
||||
function c:peek()
|
||||
return THREAD.unpackValue(self.chan:peek())
|
||||
end
|
||||
|
||||
function c:init()
|
||||
self.chan = love.thread.getChannel(self.Name)
|
||||
return self
|
||||
end
|
||||
|
||||
function c:Hold(opt)
|
||||
local multi, thread = require("multi"):init()
|
||||
if opt.peek then
|
||||
return thread.hold(function()
|
||||
return self:peek()
|
||||
end)
|
||||
else
|
||||
return thread.hold(function()
|
||||
return self:pop()
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
GLOBAL[name] = c
|
||||
|
||||
self:create(c)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local name = name or multi.randomString(16)
|
||||
|
||||
local c = {}
|
||||
|
||||
c.Name = name
|
||||
c.Type = multi.registerType("s_table")
|
||||
c.tab = THREAD.createTable(name)
|
||||
|
||||
function c:init()
|
||||
self.tab = THREAD.createTable(self.Name)
|
||||
setmetatable(self,{
|
||||
__index = function(t, k)
|
||||
return self.tab[k]
|
||||
end,
|
||||
__newindex = function(t,k,v)
|
||||
self.tab[k] = v
|
||||
end
|
||||
})
|
||||
return self
|
||||
end
|
||||
|
||||
c.__init = c.init
|
||||
|
||||
function c:Hold(opt)
|
||||
local multi, thread = require("multi"):init()
|
||||
if opt.key then
|
||||
return thread.hold(function()
|
||||
return self.tab[opt.key]
|
||||
end)
|
||||
else
|
||||
multi.error("Must provide a key to check opt.key = 'key'")
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(c,{
|
||||
__index = function(t, k)
|
||||
return c.tab[k]
|
||||
end,
|
||||
__newindex = function(t,k,v)
|
||||
c.tab[k] = v
|
||||
end
|
||||
})
|
||||
|
||||
GLOBAL[name] = c
|
||||
|
||||
self:create(c)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
local jqc = 1
|
||||
function multi:newSystemThreadedJobQueue(n)
|
||||
local c = {}
|
||||
|
||||
c.cores = n or THREAD.getCores()
|
||||
c.registerQueue = {}
|
||||
c.Type = multi.registerType("s_jobqueue")
|
||||
c.funcs = THREAD.createTable("__JobQueue_"..jqc.."_table")
|
||||
c.queue = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queue")
|
||||
c.queueReturn = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queueReturn")
|
||||
c.queueAll = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queueAll")
|
||||
c.id = 0
|
||||
c.OnJobCompleted = multi:newConnection()
|
||||
|
||||
local allfunc = 0
|
||||
|
||||
function c:doToAll(func)
|
||||
for i = 1, self.cores do
|
||||
self.queueAll:push({allfunc, func})
|
||||
end
|
||||
allfunc = allfunc + 1
|
||||
end
|
||||
function c:registerFunction(name, func)
|
||||
if self.funcs[name] then
|
||||
multi.error("A function by the name "..name.." has already been registered!")
|
||||
end
|
||||
self.funcs[name] = func
|
||||
end
|
||||
function c:pushJob(name,...)
|
||||
self.id = self.id + 1
|
||||
self.queue:push{name,self.id,...}
|
||||
return self.id
|
||||
end
|
||||
function c:isEmpty()
|
||||
return queueJob:peek()==nil
|
||||
end
|
||||
local nFunc = 0
|
||||
function c:newFunction(name,func,holup) -- This registers with the queue
|
||||
if type(name)=="function" then
|
||||
holup = func
|
||||
func = name
|
||||
name = "JQ_Function_"..nFunc
|
||||
end
|
||||
nFunc = nFunc + 1
|
||||
c:registerFunction(name,func)
|
||||
return thread:newFunction(function(...)
|
||||
local id = c:pushJob(name,...)
|
||||
local link
|
||||
local rets
|
||||
link = c.OnJobCompleted(function(jid,...)
|
||||
if id==jid then
|
||||
rets = multi.pack(...)
|
||||
end
|
||||
end)
|
||||
return thread.hold(function()
|
||||
if rets then
|
||||
return multi.unpack(rets) or multi.NIL
|
||||
end
|
||||
end)
|
||||
end,holup),name
|
||||
end
|
||||
thread:newThread("jobManager",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
local dat = c.queueReturn:pop()
|
||||
if dat then
|
||||
c.OnJobCompleted:Fire(multi.unpack(dat))
|
||||
end
|
||||
end
|
||||
end)
|
||||
for i=1,c.cores do
|
||||
multi:newSystemThread("JobQueue_"..jqc.."_worker_"..i,function(jqc)
|
||||
local multi, thread = require("multi"):init()
|
||||
require("love.timer")
|
||||
love.timer.sleep(1)
|
||||
local clock = os.clock
|
||||
local funcs = THREAD.createTable("__JobQueue_"..jqc.."_table")
|
||||
local queue = THREAD.waitFor("__JobQueue_"..jqc.."_queue")
|
||||
local queueReturn = THREAD.waitFor("__JobQueue_"..jqc.."_queueReturn")
|
||||
local lastProc = clock()
|
||||
local queueAll = THREAD.waitFor("__JobQueue_"..jqc.."_queueAll")
|
||||
local registry = {}
|
||||
_G["__QR"] = queueReturn
|
||||
setmetatable(_G,{__index = funcs})
|
||||
thread:newThread("startUp",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
local all = queueAll:peek()
|
||||
if all and not registry[all[1]] then
|
||||
lastProc = os.clock()
|
||||
queueAll:pop()[2]()
|
||||
end
|
||||
end
|
||||
end)
|
||||
thread:newThread("runner",function()
|
||||
thread.sleep(.1)
|
||||
while true do
|
||||
thread.yield()
|
||||
local all = queueAll:peek()
|
||||
if all and not registry[all[1]] then
|
||||
lastProc = os.clock()
|
||||
queueAll:pop()[2]()
|
||||
end
|
||||
local dat = thread.hold(queue)
|
||||
if dat then
|
||||
multi:newThread("Test",function()
|
||||
lastProc = os.clock()
|
||||
local name = table.remove(dat,1)
|
||||
local id = table.remove(dat,1)
|
||||
local tab = {funcs[name](multi.unpack(dat))}
|
||||
table.insert(tab,1,id)
|
||||
--local test = queueReturn.push
|
||||
queueReturn:push(tab)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
thread:newThread("Idler",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
if clock()-lastProc> 2 then
|
||||
THREAD.sleep(.05)
|
||||
else
|
||||
THREAD.sleep(.001)
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
end,jqc)
|
||||
end
|
||||
|
||||
function c:Hold(opt)
|
||||
return thread.hold(self.OnJobCompleted)
|
||||
end
|
||||
|
||||
jqc = jqc + 1
|
||||
|
||||
self:create(c)
|
||||
|
||||
return c
|
||||
end
|
||||
137
integration/loveManager/init.lua
Normal file
137
integration/loveManager/init.lua
Normal file
@ -0,0 +1,137 @@
|
||||
if ISTHREAD then
|
||||
error("You cannot require the loveManager from within a thread!")
|
||||
end
|
||||
|
||||
local ThreadFileData = [[
|
||||
ISTHREAD = true
|
||||
args = {...}
|
||||
THREAD_ID = args[1]
|
||||
THREAD_NAME = args[2]
|
||||
GLOBAL, THREAD, DEFER = require("multi.integration.loveManager.threads"):init()
|
||||
__FUNC = THREAD.unpackValue(args[3])
|
||||
ARGS = THREAD.unpackValue(args[4])
|
||||
settings = args[5]
|
||||
if ARGS == nil then ARGS = {} end
|
||||
math.randomseed(THREAD_ID)
|
||||
math.random()
|
||||
math.random()
|
||||
math.random()
|
||||
stab = THREAD.createTable(THREAD_NAME .. THREAD_ID)
|
||||
if GLOBAL["__env"] then
|
||||
local env = THREAD.getENV()
|
||||
for i,v in pairs(env) do
|
||||
_G[i] = v
|
||||
end
|
||||
end
|
||||
multi, thread = require("multi"):init{error=true, warning=true, print=true, priority=true}
|
||||
multi.defaultSettings.print = true
|
||||
require("multi.integration.loveManager.extensions")
|
||||
require("multi.integration.sharedExtensions")
|
||||
local returns = {pcall(__FUNC, multi.unpack(ARGS))}
|
||||
table.remove(returns,1)
|
||||
stab["returns"] = returns
|
||||
for i,v in pairs(DEFER) do
|
||||
pcall(v)
|
||||
end
|
||||
]]
|
||||
|
||||
_G.THREAD_NAME = "MAIN_THREAD"
|
||||
_G.THREAD_ID = 0
|
||||
|
||||
local multi, thread = require("multi"):init()
|
||||
local GLOBAL, THREAD = require("multi.integration.loveManager.threads"):init()
|
||||
|
||||
multi.registerType("s_function")
|
||||
multi.registerType("s_thread")
|
||||
|
||||
multi.integration = {}
|
||||
multi.isMainThread = true
|
||||
local threads = {}
|
||||
local tid = 0
|
||||
function multi:newSystemThread(name, func, ...)
|
||||
multi.InitSystemThreadErrorHandler()
|
||||
local name = name or multi.randomString(16)
|
||||
tid = tid + 1
|
||||
local c = {}
|
||||
c.Type = multi.STHREAD
|
||||
c.Name = name
|
||||
c.ID = tid
|
||||
c.thread = love.thread.newThread(ThreadFileData)
|
||||
c.thread:start(c.ID, c.Name, THREAD.packValue(func), THREAD.packValue({...}), multi.defaultSettings)
|
||||
c.stab = THREAD.createTable(name .. c.ID)
|
||||
c.creationTime = os.clock()
|
||||
c.OnDeath = multi:newConnection()
|
||||
c.OnError = multi:newConnection()
|
||||
c.status_channel = love.thread.getChannel("__status_channel__" .. c.ID)
|
||||
|
||||
function c:getName() return c.name end
|
||||
|
||||
table.insert(threads, c)
|
||||
|
||||
c.OnError(multi.error)
|
||||
|
||||
if self.isActor then
|
||||
self:create(c)
|
||||
else
|
||||
multi.create(multi, c)
|
||||
end
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
local started = false
|
||||
local console_channel = love.thread.getChannel("__console_channel__")
|
||||
|
||||
function THREAD:newFunction(func, holdme)
|
||||
return thread:newFunctionBase(function(...)
|
||||
return multi:newSystemThread("SystemThreaded Function Handler", func, ...)
|
||||
end, holdme, multi.SFUNCTION)()
|
||||
end
|
||||
|
||||
function love.threaderror(thread, errorstr)
|
||||
multi.error("Thread error! " .. errorstr)
|
||||
end
|
||||
|
||||
function multi.InitSystemThreadErrorHandler()
|
||||
if started == true then return end
|
||||
started = true
|
||||
thread:newThread("Love System Thread Handler", function()
|
||||
while true do
|
||||
thread.yield()
|
||||
for i = #threads, 1, -1 do
|
||||
local th = threads[i]
|
||||
if th.status_channel:peek() ~= nil then
|
||||
th.statusconnector:Fire(multi.unpack(th.status_channel:pop()))
|
||||
end
|
||||
local th_err = th.thread:getError()
|
||||
if th_err == "Thread Killed!\1" then
|
||||
th.OnDeath:Fire("Thread Killed!")
|
||||
table.remove(threads, i)
|
||||
elseif th_err then
|
||||
th.OnError:Fire(th, th_err)
|
||||
table.remove(threads, i)
|
||||
elseif th.stab.returns then
|
||||
th.OnDeath:Fire(multi.unpack(th.stab.returns))
|
||||
th.stab.returns = nil
|
||||
table.remove(threads, i)
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
THREAD.newSystemThread = function(...)
|
||||
multi:newSystemThread(...)
|
||||
end
|
||||
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
require("multi.integration.loveManager.extensions")
|
||||
require("multi.integration.sharedExtensions")
|
||||
multi.print("Integrated Love Threading!")
|
||||
|
||||
return {
|
||||
init = function()
|
||||
return GLOBAL, THREAD
|
||||
end
|
||||
}
|
||||
228
integration/loveManager/threads.lua
Normal file
228
integration/loveManager/threads.lua
Normal file
@ -0,0 +1,228 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
require("love.timer")
|
||||
require("love.system")
|
||||
require("love.data")
|
||||
require("love.thread")
|
||||
local multi, thread = require("multi"):init()
|
||||
|
||||
-- Checks if the given value is a LOVE2D object (i.e. has metatable with __index field) and if that __index field contains functions typical of LOVE2D objects
|
||||
function isLoveObject(value)
|
||||
-- Check if the value has metatable
|
||||
if type(value) == "userdata" and getmetatable(value) then
|
||||
-- Check if the metatable has the __index field
|
||||
local index = getmetatable(value).__index
|
||||
if type(index) == "table" then
|
||||
-- Check if the metatable's __index table contains functions typical of LOVE2D objects
|
||||
if index.draw or index.update or index.getWidth or index.getHeight or index.getString or index.getPointer then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
-- Converts any function values in a table to a string with the value "\1\2:func:<function_string>" where <function_string> is the Lua stringified version of the function
|
||||
function tableToFunctionString(t)
|
||||
if type(t) == "nil" then return "\1\2:nil:" end
|
||||
if type(t) == "function" then return "\1\2:func:"..string.dump(t) end
|
||||
if type(t) ~= "table" then return t end
|
||||
local newtable = {}
|
||||
for k, v in pairs(t) do
|
||||
if type(v) == "function" then
|
||||
newtable[k] = "\1\2:func:"..string.dump(v)
|
||||
elseif type(v) == "table" then
|
||||
newtable[k] = tableToFunctionString(v)
|
||||
elseif isLoveObject(v) then
|
||||
newtable[k] = v
|
||||
elseif type(v) == "userdata" then
|
||||
newtable[k] = tostring(v)
|
||||
else
|
||||
newtable[k] = v
|
||||
end
|
||||
end
|
||||
return newtable
|
||||
end
|
||||
|
||||
-- Converts strings with the value "\1\2:func:<function_string>" back to functions
|
||||
function functionStringToTable(t)
|
||||
if type(t) == "string" and t:sub(1, 8) == "\1\2:func:" then return loadstring(t:sub(9, -1)) end
|
||||
if type(t) == "string" and t:sub(1, 7) == "\1\2:nil:" then return nil end
|
||||
if type(t) ~= "table" then return t end
|
||||
for k, v in pairs(t) do
|
||||
if type(v) == "string" then
|
||||
if v:sub(1, 8) == "\1\2:func:" then
|
||||
t[k] = loadstring(v:sub(9, -1))
|
||||
else
|
||||
t[k] = v
|
||||
end
|
||||
elseif type(v) == "table" then
|
||||
t[k] = functionStringToTable(v)
|
||||
else
|
||||
t[k] = v
|
||||
end
|
||||
end
|
||||
if t.init then
|
||||
t:init()
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local function packValue(t)
|
||||
return tableToFunctionString(t)
|
||||
end
|
||||
|
||||
local function unpackValue(t)
|
||||
return functionStringToTable(t)
|
||||
end
|
||||
|
||||
local function createTable(n)
|
||||
if not n then
|
||||
n = "STAB"..multi.randomString(8)
|
||||
end
|
||||
local __proxy = {}
|
||||
local function set(name, val)
|
||||
local chan = love.thread.getChannel(n .. name)
|
||||
if chan:getCount() == 1 then chan:pop() end
|
||||
__proxy[name] = true
|
||||
chan:push(packValue(val))
|
||||
end
|
||||
local function get(name)
|
||||
return unpackValue(love.thread.getChannel(n .. name):peek())
|
||||
-- if type(data) == "table" and data.init then
|
||||
-- return data:init()
|
||||
-- else
|
||||
-- return data
|
||||
-- end
|
||||
end
|
||||
return setmetatable({},
|
||||
{
|
||||
__index = function(t, k)
|
||||
return get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
set(k,v)
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
|
||||
function INIT()
|
||||
local GLOBAL, THREAD, DEFER = createTable("__GLOBAL__"), {}, {}
|
||||
local status_channel, console_channel = love.thread.getChannel("__status_channel__" .. THREAD_ID),
|
||||
love.thread.getChannel("__console_channel__")
|
||||
|
||||
-- Non portable methods, shouldn't be used unless you know what you are doing
|
||||
THREAD.packValue = packValue
|
||||
THREAD.unpackValue = unpackValue
|
||||
THREAD.createTable = createTable
|
||||
|
||||
function THREAD.set(name, val)
|
||||
GLOBAL[name] = val
|
||||
end
|
||||
|
||||
function THREAD.get(name, val)
|
||||
return GLOBAL[name]
|
||||
end
|
||||
|
||||
THREAD.waitFor = thread:newFunction(function(name)
|
||||
local function wait()
|
||||
math.randomseed(os.time())
|
||||
thread.yield()
|
||||
end
|
||||
repeat
|
||||
wait()
|
||||
until GLOBAL[name] ~= nil
|
||||
return GLOBAL[name]
|
||||
end, true)
|
||||
|
||||
function THREAD.getCores()
|
||||
return love.system.getProcessorCount()
|
||||
end
|
||||
|
||||
function THREAD.getConsole()
|
||||
local c = {}
|
||||
c.queue = console_channel
|
||||
function c.print(...)
|
||||
c.queue:push(table.concat(multi.pack(...), "\t"))
|
||||
end
|
||||
function c.error(err)
|
||||
c.queue:push("Error in <"..THREAD_NAME..":" .. THREAD_ID .. ">: ".. err)
|
||||
multi.error(err)
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function THREAD.getThreads()
|
||||
--
|
||||
end
|
||||
|
||||
function THREAD.kill() -- trigger the lane destruction
|
||||
error("Thread was killed!\1")
|
||||
end
|
||||
|
||||
function THREAD.pushStatus(...)
|
||||
status_channel:push(multi.pack(...))
|
||||
end
|
||||
|
||||
function THREAD.sleep(n)
|
||||
love.timer.sleep(n)
|
||||
end
|
||||
|
||||
THREAD.hold = thread:newFunction(function(n)
|
||||
thread.hold(n)
|
||||
end, true)
|
||||
|
||||
function THREAD.setENV(env, name)
|
||||
GLOBAL[name or "__env"] = env
|
||||
end
|
||||
|
||||
function THREAD.getENV(name)
|
||||
return GLOBAL[name or "__env"]
|
||||
end
|
||||
|
||||
function THREAD.exposeENV(name)
|
||||
name = name or "__env"
|
||||
local env = THREAD.getENV(name)
|
||||
for i,v in pairs(env) do
|
||||
_G[i] = v
|
||||
end
|
||||
end
|
||||
|
||||
function THREAD.defer(func)
|
||||
table.insert(DEFER, func)
|
||||
end
|
||||
|
||||
function THREAD.sync()
|
||||
-- Maybe do something...
|
||||
end
|
||||
|
||||
return GLOBAL, THREAD, DEFER
|
||||
end
|
||||
|
||||
return {
|
||||
init = function()
|
||||
return INIT()
|
||||
end
|
||||
}
|
||||
204
integration/lovrManager/extensions.lua
Normal file
204
integration/lovrManager/extensions.lua
Normal file
@ -0,0 +1,204 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi, thread = require("multi").init()
|
||||
GLOBAL = multi.integration.GLOBAL
|
||||
THREAD = multi.integration.THREAD
|
||||
function multi:newSystemThreadedQueue(name)
|
||||
local c = {}
|
||||
c.Name = name
|
||||
local fRef = {"func",nil}
|
||||
function c:init()
|
||||
local q = {}
|
||||
q.chan = lovr.thread.getChannel(self.Name)
|
||||
function q:push(dat)
|
||||
if type(dat) == "function" then
|
||||
fRef[2] = THREAD.dump(dat)
|
||||
self.chan:push(fRef)
|
||||
return
|
||||
else
|
||||
self.chan:push(dat)
|
||||
end
|
||||
end
|
||||
function q:pop()
|
||||
local dat = self.chan:pop()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
return THREAD.loadDump(dat[2])
|
||||
else
|
||||
return dat
|
||||
end
|
||||
end
|
||||
function q:peek()
|
||||
local dat = self.chan:peek()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
return THREAD.loadDump(dat[2])
|
||||
else
|
||||
return dat
|
||||
end
|
||||
end
|
||||
return q
|
||||
end
|
||||
THREAD.package(name,c)
|
||||
return c
|
||||
end
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local c = {}
|
||||
c.name = name
|
||||
function c:init()
|
||||
return THREAD.createTable(self.name)
|
||||
end
|
||||
THREAD.package(name,c)
|
||||
return c
|
||||
end
|
||||
local jqc = 1
|
||||
function multi:newSystemThreadedJobQueue(n)
|
||||
local c = {}
|
||||
c.cores = n or THREAD.getCores()
|
||||
c.registerQueue = {}
|
||||
c.funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||
c.queue = lovr.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||
c.queueReturn = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||
c.queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||
c.id = 0
|
||||
c.OnJobCompleted = multi:newConnection()
|
||||
local allfunc = 0
|
||||
function c:doToAll(func)
|
||||
local f = THREAD.dump(func)
|
||||
for i = 1, self.cores do
|
||||
self.queueAll:push({allfunc,f})
|
||||
end
|
||||
allfunc = allfunc + 1
|
||||
end
|
||||
function c:registerFunction(name,func)
|
||||
if self.funcs[name] then
|
||||
error("A function by the name "..name.." has already been registered!")
|
||||
end
|
||||
self.funcs[name] = func
|
||||
end
|
||||
function c:pushJob(name,...)
|
||||
self.id = self.id + 1
|
||||
self.queue:push{name,self.id,...}
|
||||
return self.id
|
||||
end
|
||||
function c:isEmpty()
|
||||
return queueJob:peek()==nil
|
||||
end
|
||||
local nFunc = 0
|
||||
function c:newFunction(name,func,holup) -- This registers with the queue
|
||||
if type(name)=="function" then
|
||||
holup = func
|
||||
func = name
|
||||
name = "JQ_Function_"..nFunc
|
||||
end
|
||||
nFunc = nFunc + 1
|
||||
c:registerFunction(name,func)
|
||||
return thread:newFunction(function(...)
|
||||
local id = c:pushJob(name,...)
|
||||
local link
|
||||
local rets
|
||||
link = c.OnJobCompleted(function(jid,...)
|
||||
if id==jid then
|
||||
rets = multi.pack(...)
|
||||
link:Destroy()
|
||||
end
|
||||
end)
|
||||
return thread.hold(function()
|
||||
if rets then
|
||||
return multi.unpack(rets) or multi.NIL
|
||||
end
|
||||
end)
|
||||
end,holup),name
|
||||
end
|
||||
thread:newThread("jobManager",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
local dat = c.queueReturn:pop()
|
||||
if dat then
|
||||
c.OnJobCompleted:Fire(multi.unpack(dat))
|
||||
end
|
||||
end
|
||||
end)
|
||||
for i=1,c.cores do
|
||||
multi:newSystemThread("JobQueue_"..jqc.."_worker_"..i,function(jqc)
|
||||
local multi, thread = require("multi"):init()
|
||||
require("lovr.timer")
|
||||
local function atomic(channel)
|
||||
return channel:pop()
|
||||
end
|
||||
local clock = os.clock
|
||||
local funcs = THREAD.createStaticTable("__JobQueue_"..jqc.."_table")
|
||||
local queue = lovr.thread.getChannel("__JobQueue_"..jqc.."_queue")
|
||||
local queueReturn = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueReturn")
|
||||
local lastProc = clock()
|
||||
local queueAll = lovr.thread.getChannel("__JobQueue_"..jqc.."_queueAll")
|
||||
local registry = {}
|
||||
_G["__QR"] = queueReturn
|
||||
setmetatable(_G,{__index = funcs})
|
||||
thread:newThread("startUp",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
local all = queueAll:peek()
|
||||
if all and not registry[all[1]] then
|
||||
lastProc = os.clock()
|
||||
THREAD.loadDump(queueAll:pop()[2])()
|
||||
end
|
||||
end
|
||||
end)
|
||||
thread:newThread("runner",function()
|
||||
thread.sleep(.1)
|
||||
while true do
|
||||
thread.yield()
|
||||
local all = queueAll:peek()
|
||||
if all and not registry[all[1]] then
|
||||
lastProc = os.clock()
|
||||
THREAD.loadDump(queueAll:pop()[2])()
|
||||
end
|
||||
local dat = queue:performAtomic(atomic)
|
||||
if dat then
|
||||
lastProc = os.clock()
|
||||
local name = table.remove(dat,1)
|
||||
local id = table.remove(dat,1)
|
||||
local tab = {funcs[name](multi.unpack(dat))}
|
||||
table.insert(tab,1,id)
|
||||
queueReturn:push(tab)
|
||||
end
|
||||
end
|
||||
end):OnError(function(...)
|
||||
error(...)
|
||||
end)
|
||||
thread:newThread("Idler",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
if clock()-lastProc> 2 then
|
||||
THREAD.sleep(.05)
|
||||
else
|
||||
THREAD.sleep(.001)
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
end,jqc)
|
||||
end
|
||||
jqc = jqc + 1
|
||||
return c
|
||||
end
|
||||
98
integration/lovrManager/init.lua
Normal file
98
integration/lovrManager/init.lua
Normal file
@ -0,0 +1,98 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
-- TODO make compatible with lovr
|
||||
if ISTHREAD then
|
||||
error("You cannot require the lovrManager from within a thread!")
|
||||
end
|
||||
local ThreadFileData = [[
|
||||
ISTHREAD = true
|
||||
THREAD = require("multi.integration.lovrManager.threads") -- order is important!
|
||||
sThread = THREAD
|
||||
__IMPORTS = {...}
|
||||
__FUNC__=table.remove(__IMPORTS,1)
|
||||
__THREADID__=table.remove(__IMPORTS,1)
|
||||
__THREADNAME__=table.remove(__IMPORTS,1)
|
||||
stab = THREAD.createStaticTable(__THREADNAME__)
|
||||
GLOBAL = THREAD.getGlobal()
|
||||
multi, thread = require("multi").init()
|
||||
stab["returns"] = {THREAD.loadDump(__FUNC__)(multi.unpack(__IMPORTS))}
|
||||
]]
|
||||
local multi, thread = require("multi.compat.lovr2d"):init()
|
||||
local THREAD = {}
|
||||
__THREADID__ = 0
|
||||
__THREADNAME__ = "MainThread"
|
||||
_G.THREAD_NAME = "MAIN_THREAD"
|
||||
_G.THREAD_ID = 0
|
||||
multi.integration={}
|
||||
multi.integration.lovr2d={}
|
||||
local THREAD = require("multi.integration.lovrManager.threads")
|
||||
local GLOBAL = THREAD.getGlobal()
|
||||
local THREAD_ID = 1
|
||||
local OBJECT_ID = 0
|
||||
local stf = 0
|
||||
function THREAD:newFunction(func,holup)
|
||||
stf = stf + 1
|
||||
return function(...)
|
||||
local t = multi:newSystemThread("STF"..stf,func,...)
|
||||
return thread:newFunction(function()
|
||||
return thread.hold(function()
|
||||
if t.stab["returns"] then
|
||||
local dat = t.stab.returns
|
||||
t.stab.returns = nil
|
||||
return multi.unpack(dat)
|
||||
end
|
||||
end)
|
||||
end,holup)()
|
||||
end
|
||||
end
|
||||
function multi:newSystemThread(name,func,...)
|
||||
local c = {}
|
||||
c.name = name
|
||||
c.ID=THREAD_ID
|
||||
c.thread=lovr.thread.newThread(ThreadFileData)
|
||||
c.thread:start(THREAD.dump(func),c.ID,c.name,...)
|
||||
c.stab = THREAD.createStaticTable(name)
|
||||
GLOBAL["__THREAD_"..c.ID] = {ID=c.ID,Name=c.name,Thread=c.thread}
|
||||
GLOBAL["__THREAD_COUNT"] = THREAD_ID
|
||||
THREAD_ID=THREAD_ID+1
|
||||
|
||||
if self.isActor then
|
||||
self:create(c)
|
||||
else
|
||||
multi.create(multi, c)
|
||||
end
|
||||
|
||||
return c
|
||||
end
|
||||
THREAD.newSystemThread = multi.newSystemThread
|
||||
function lovr.threaderror(thread, errorstr)
|
||||
multi.print("Thread error!\n"..errorstr)
|
||||
end
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
require("multi.integration.lovrManager.extensions")
|
||||
multi.print("Integrated lovr Threading!")
|
||||
return {init=function()
|
||||
return GLOBAL,THREAD
|
||||
end}
|
||||
222
integration/lovrManager/threads.lua
Normal file
222
integration/lovrManager/threads.lua
Normal file
@ -0,0 +1,222 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
-- TODO make compatible with lovr
|
||||
require("lovr.timer")
|
||||
require("lovr.system")
|
||||
require("lovr.data")
|
||||
local socket = require("socket")
|
||||
local multi, thread = require("multi").init()
|
||||
local threads = {}
|
||||
function threads.loadDump(d)
|
||||
return loadstring(d:getString())
|
||||
end
|
||||
function threads.dump(func)
|
||||
return lovr.data.newByteData(string.dump(func))
|
||||
end
|
||||
local fRef = {"func",nil}
|
||||
local function manage(channel, value)
|
||||
channel:clear()
|
||||
if type(value) == "function" then
|
||||
fRef[2] = THREAD.dump(value)
|
||||
channel:push(fRef)
|
||||
return
|
||||
else
|
||||
channel:push(value)
|
||||
end
|
||||
end
|
||||
local function RandomVariable(length)
|
||||
local res = {}
|
||||
math.randomseed(socket.gettime()*10000)
|
||||
for i = 1, length do
|
||||
res[#res+1] = string.char(math.random(97, 122))
|
||||
end
|
||||
return table.concat(res)
|
||||
end
|
||||
local GNAME = "__GLOBAL_"
|
||||
local proxy = {}
|
||||
function threads.set(name,val)
|
||||
if not proxy[name] then proxy[name] = lovr.thread.getChannel(GNAME..name) end
|
||||
proxy[name]:performAtomic(manage, val)
|
||||
end
|
||||
function threads.get(name)
|
||||
if not proxy[name] then proxy[name] = lovr.thread.getChannel(GNAME..name) end
|
||||
local dat = proxy[name]:peek()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
return THREAD.loadDump(dat[2])
|
||||
else
|
||||
return dat
|
||||
end
|
||||
end
|
||||
function threads.waitFor(name)
|
||||
if thread.isThread() then
|
||||
return thread.hold(function()
|
||||
return threads.get(name)
|
||||
end)
|
||||
end
|
||||
while threads.get(name)==nil do
|
||||
lovr.timer.sleep(.001)
|
||||
end
|
||||
local dat = threads.get(name)
|
||||
if type(dat) == "table" and dat.init then
|
||||
dat.init = threads.loadDump(dat.init)
|
||||
end
|
||||
return dat
|
||||
end
|
||||
function threads.package(name,val)
|
||||
local init = val.init
|
||||
val.init=threads.dump(val.init)
|
||||
GLOBAL[name]=val
|
||||
val.init=init
|
||||
end
|
||||
function threads.getCores()
|
||||
return lovr.system.getProcessorCount()
|
||||
end
|
||||
function threads.kill()
|
||||
error("Thread Killed!")
|
||||
end
|
||||
function threads.getThreads()
|
||||
local t = {}
|
||||
for i=1,GLOBAL["__THREAD_COUNT"] do
|
||||
t[#t+1]=GLOBAL["__THREAD_"..i]
|
||||
end
|
||||
return t
|
||||
end
|
||||
function threads.getThread(n)
|
||||
return GLOBAL["__THREAD_"..n]
|
||||
end
|
||||
function threads.getName()
|
||||
return __THREADNAME__
|
||||
end
|
||||
function threads.getID()
|
||||
return __THREADID__
|
||||
end
|
||||
function threads.sleep(n)
|
||||
lovr.timer.sleep(n)
|
||||
end
|
||||
function threads.getGlobal()
|
||||
return setmetatable({},
|
||||
{
|
||||
__index = function(t, k)
|
||||
return THREAD.get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
THREAD.set(k,v)
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
function threads.createTable(n)
|
||||
local _proxy = {}
|
||||
local function set(name,val)
|
||||
if not _proxy[name] then _proxy[name] = lovr.thread.getChannel(n..name) end
|
||||
_proxy[name]:performAtomic(manage, val)
|
||||
end
|
||||
local function get(name)
|
||||
if not _proxy[name] then _proxy[name] = lovr.thread.getChannel(n..name) end
|
||||
local dat = _proxy[name]:peek()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
return THREAD.loadDump(dat[2])
|
||||
else
|
||||
return dat
|
||||
end
|
||||
end
|
||||
return setmetatable({},
|
||||
{
|
||||
__index = function(t, k)
|
||||
return get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
set(k,v)
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
function threads.getConsole()
|
||||
local c = {}
|
||||
c.queue = lovr.thread.getChannel("__CONSOLE__")
|
||||
function c.print(...)
|
||||
c.queue:push(multi.pack(...))
|
||||
end
|
||||
function c.error(err)
|
||||
c.queue:push{"ERROR in <"..__THREADNAME__..">: "..err,__THREADID__}
|
||||
error(err)
|
||||
end
|
||||
return c
|
||||
end
|
||||
if not ISTHREAD then
|
||||
local clock = os.clock
|
||||
local lastproc = clock()
|
||||
local queue = lovr.thread.getChannel("__CONSOLE__")
|
||||
thread:newThread("consoleManager",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
dat = queue:pop()
|
||||
if dat then
|
||||
lastproc = clock()
|
||||
print(multi.unpack(dat))
|
||||
end
|
||||
if clock()-lastproc>2 then
|
||||
thread.sleep(.1)
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
function threads.createStaticTable(n)
|
||||
local __proxy = {}
|
||||
local function set(name,val)
|
||||
if __proxy[name] then return end
|
||||
local chan = lovr.thread.getChannel(n..name)
|
||||
if chan:getCount()>0 then return end
|
||||
chan:performAtomic(manage, val)
|
||||
__proxy[name] = val
|
||||
end
|
||||
local function get(name)
|
||||
if __proxy[name] then return __proxy[name] end
|
||||
local dat = lovr.thread.getChannel(n..name):peek()
|
||||
if type(dat)=="table" and dat[1]=="func" then
|
||||
__proxy[name] = THREAD.loadDump(dat[2])
|
||||
return __proxy[name]
|
||||
else
|
||||
__proxy[name] = dat
|
||||
return __proxy[name]
|
||||
end
|
||||
end
|
||||
return setmetatable({},
|
||||
{
|
||||
__index = function(t, k)
|
||||
return get(k)
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
set(k,v)
|
||||
end
|
||||
}
|
||||
)
|
||||
end
|
||||
function threads.hold(n)
|
||||
local dat
|
||||
while not(dat) do
|
||||
dat = n()
|
||||
end
|
||||
end
|
||||
return threads
|
||||
138
integration/luvitManager.lua
Normal file
138
integration/luvitManager.lua
Normal file
@ -0,0 +1,138 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
|
||||
-- This module probably will not be maintained any longer!
|
||||
package.path = "?/init.lua;?.lua;" .. package.path
|
||||
local function _INIT(luvitThread, timer)
|
||||
-- lots of this stuff should be able to stay the same
|
||||
function os.getOS()
|
||||
if package.config:sub(1, 1) == "\\" then
|
||||
return "windows"
|
||||
else
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
-- Step 1 get setup threads on luvit... Sigh how do i even...
|
||||
local multi, thread = require("multi").init()
|
||||
multi.isMainThread = true
|
||||
function multi:canSystemThread()
|
||||
return true
|
||||
end
|
||||
function multi:getPlatform()
|
||||
return "luvit"
|
||||
end
|
||||
local multi = multi
|
||||
-- Step 2 set up the Global table... is this possible?
|
||||
local GLOBAL = {}
|
||||
setmetatable(
|
||||
GLOBAL,
|
||||
{
|
||||
__index = function(t, k)
|
||||
--print("No Global table when using luvit integration!")
|
||||
return nil
|
||||
end,
|
||||
__newindex = function(t, k, v)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
}
|
||||
)
|
||||
local THREAD = {}
|
||||
function THREAD.set(name, val)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.get(name)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
local function randomString(n)
|
||||
local str = ""
|
||||
local strings = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","1","2","3","4","5","6","7","8","9","0","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}
|
||||
for i = 1, n do
|
||||
str = str .. "" .. strings[math.random(1, #strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.testFor(name, val, sym)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.getCores()
|
||||
return THREAD.__CORES
|
||||
end
|
||||
if os.getOS() == "windows" then
|
||||
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
function THREAD.kill() -- trigger the thread destruction
|
||||
error("Thread was Killed!")
|
||||
end
|
||||
-- hmmm if im cleaver I can get this to work... but since data passing isn't going to be a thing its probably not important
|
||||
function THREAD.sleep(n)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
--print("No Global table when using luvit integration!")
|
||||
end
|
||||
-- Step 5 Basic Threads!
|
||||
local function entry(path, name, func, ...)
|
||||
local timer = require "timer"
|
||||
local luvitThread = require "thread"
|
||||
package.path = path
|
||||
loadstring(func)(...)
|
||||
end
|
||||
function multi:newSystemThread(name, func, ...)
|
||||
local c = {}
|
||||
local __self = c
|
||||
c.name = name
|
||||
c.Type = multi.STHREAD
|
||||
c.thread = {}
|
||||
c.func = string.dump(func)
|
||||
function c:kill()
|
||||
-- print("No Global table when using luvit integration!")
|
||||
end
|
||||
luvitThread.start(entry, package.path, name, c.func, ...)
|
||||
return c
|
||||
end
|
||||
THREAD.newSystemThread = multi.newSystemThread
|
||||
multi.print("Integrated Luvit!")
|
||||
multi.integration = {} -- for module creators
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
require("multi.integration.shared")
|
||||
-- Start the main mainloop... This allows you to process your multi objects, but the engine on the main thread will be limited to .001 or 1 millisecond sigh...
|
||||
local interval =
|
||||
timer.setInterval(
|
||||
1,
|
||||
function()
|
||||
multi:uManager()
|
||||
end
|
||||
)
|
||||
return multi
|
||||
end
|
||||
return {init = function(threadHandle, timerHandle)
|
||||
local multi = _INIT(threadHandle, timerHandle)
|
||||
return GLOBAL, THREAD
|
||||
end}
|
||||
34
integration/networkManager/channel.lua
Normal file
34
integration/networkManager/channel.lua
Normal file
@ -0,0 +1,34 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
_G["__CHANNEL__"] = {}
|
||||
local channel = {}
|
||||
channel.__index = channel
|
||||
|
||||
-- Creates/Gets a channel of name
|
||||
function channel:newChannel(name)
|
||||
local chan = _G["__CHANNEL__"]
|
||||
if chan then
|
||||
|
||||
end
|
||||
end
|
||||
46
integration/networkManager/childNode.lua
Normal file
46
integration/networkManager/childNode.lua
Normal file
@ -0,0 +1,46 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi, thread = require("multi"):init()
|
||||
local cmd = require("multi.integration.networkManager.cmds")
|
||||
local node = require("multi.integration.networkManager.node")
|
||||
local net = require("net")
|
||||
local bin = require("bin")
|
||||
local child = {}
|
||||
child.__index = child
|
||||
function multi:newChildNode(cd)
|
||||
local c = {}
|
||||
setmetatable(c,child)
|
||||
local name
|
||||
if cd then
|
||||
if cd.name then
|
||||
name = cd.name
|
||||
end
|
||||
c.node = node:new(cd.nodePort or cmd.defaultPort,nil,name)
|
||||
if cd.managerHost then
|
||||
cd.managerPort = cd.managerPort or cmd.defaultManagerPort
|
||||
c.node:registerWithManager(cd.managerHost,cd.managerPort)
|
||||
end
|
||||
end
|
||||
return c
|
||||
end
|
||||
35
integration/networkManager/clientSide.lua
Normal file
35
integration/networkManager/clientSide.lua
Normal file
@ -0,0 +1,35 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
return function(self,data)
|
||||
local cmd,data = data:match("!(.-)!(.*)")
|
||||
--print(">",cmd,data)
|
||||
if cmd == "PONG" then
|
||||
self:send("!PONG!")
|
||||
elseif cmd == "CHANNEL" then
|
||||
--
|
||||
elseif cmd == "RETURNS" then
|
||||
local rets = bin.new(data):getBlock("t")
|
||||
self.node.master.OnDataReturned:Fire(rets)
|
||||
end
|
||||
end
|
||||
42
integration/networkManager/cmds.lua
Normal file
42
integration/networkManager/cmds.lua
Normal file
@ -0,0 +1,42 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local cmds = {
|
||||
defaultManagerPort = 0XDE2,
|
||||
defaultWait = 0X002,
|
||||
defaultPort = 0X000, -- We will let the OS assign us one
|
||||
standardSkip = 0X018,
|
||||
ERROR = 0X000,
|
||||
PING = 0X001,
|
||||
PONG = 0X002,
|
||||
QUEUE = 0X003,
|
||||
TASK = 0X004,
|
||||
INITNODE = 0X005,
|
||||
INITMASTER = 0X006,
|
||||
GLOBAL = 0X007,
|
||||
LOAD = 0X008,
|
||||
CALL = 0X009,
|
||||
REG = 0X00A,
|
||||
CONSOLE = 0X00B,
|
||||
}
|
||||
return cmds
|
||||
23
integration/networkManager/extensions.lua
Normal file
23
integration/networkManager/extensions.lua
Normal file
@ -0,0 +1,23 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
57
integration/networkManager/init.lua
Normal file
57
integration/networkManager/init.lua
Normal file
@ -0,0 +1,57 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi, thread = require("multi"):init()
|
||||
local net = require("net")
|
||||
--local bin = require("bin")
|
||||
local char = string.char
|
||||
local byte = string.byte
|
||||
bin.setBitsInterface(infinabits)
|
||||
--[[
|
||||
--[=[ Pre reqs:
|
||||
- Network contains nodes
|
||||
- Network can broadcast/has nodemanager/ is simple and can be scanned
|
||||
|
||||
Outline:
|
||||
- multi:newMasterNode(connectionDetails)
|
||||
-- master:setDefaultNode(nodeName) -- Set default node
|
||||
-- master:newNetworkThread(nodeName,func,...) -- Thread is ran on a random node or the default one if set if nodeName is set to nil
|
||||
-- master:newNetworkChannel(nodeName)
|
||||
-- master:sendTo(nodeName,data)
|
||||
- multi:newNode(connectionDetails)
|
||||
- multi:newNodeManager(connectionDetails) -- This will be incharge of a lot of data handling
|
||||
]=]
|
||||
|
||||
local nGLOBAL, nTHREAD = require("multi.integration.networkManager"):init()
|
||||
local master = multi:newMasterNode()
|
||||
master:newNetworkThread("simpleNode",function(a,b,c)
|
||||
print(a,b,c)
|
||||
end,1,2,3)
|
||||
]]
|
||||
|
||||
-- The init file should provide the structure that all the other modules build off of
|
||||
return {
|
||||
init = function()
|
||||
--
|
||||
end
|
||||
}
|
||||
163
integration/networkManager/masterNode.lua
Normal file
163
integration/networkManager/masterNode.lua
Normal file
@ -0,0 +1,163 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi, thread = require("multi"):init()
|
||||
local cmd = require("multi.integration.networkManager.cmds")
|
||||
local node = require("multi.integration.networkManager.node")
|
||||
local net = require("net")
|
||||
local bin = require("bin")
|
||||
local master = {}
|
||||
master.__index = master
|
||||
function master:addNode(ip,port)
|
||||
return node:new(ip,port)
|
||||
end
|
||||
function master:getNodesFromBroadcast()
|
||||
net:newCastedClients("NODE_.+")
|
||||
net.OnCastedClientInfo(function(client, n, ip, port)
|
||||
self.nodes[n] = node:new(client)
|
||||
end)
|
||||
end
|
||||
function master:getNodesFromManager(ip,port)
|
||||
local mn = self.nodes
|
||||
if not self.manager then
|
||||
self.manager = net:newTCPClient(ip,port)
|
||||
if not self.manager then
|
||||
error("Unable to connect to the node Manager! Is it running? Perhaps the hostname or port is incorrect!")
|
||||
end
|
||||
end
|
||||
self.manager.OnDataRecieved(function(self,data,client)
|
||||
local cmd = data:match("!(.+)!")
|
||||
data = data:gsub("!"..cmd.."!","")
|
||||
if cmd == "NODE" then
|
||||
local n,h,p = data:match("(.-)|(.-)|(.+)")
|
||||
mn[n] = node:new(h,tonumber(p))
|
||||
end
|
||||
end)
|
||||
self.manager:send("!NODES!")
|
||||
end
|
||||
function master:setDefaultNode(nodeName)
|
||||
if self:nodeExists(nodeName) then
|
||||
self.defaultNode = nodeName
|
||||
end
|
||||
end
|
||||
function master:getRandomNode()
|
||||
local t = {}
|
||||
for i,v in pairs(self.nodes) do t[#t+1] = i end
|
||||
return t[math.random(1,#t)]
|
||||
end
|
||||
local netID = 0
|
||||
function master:newNetworkThread(nodeName,func,...)
|
||||
local args = {...}
|
||||
local dat = bin.new()
|
||||
local ret
|
||||
local nID = netID
|
||||
local conn = multi:newConnection()
|
||||
thread:newthread(function()
|
||||
dat:addBlock{
|
||||
args = args,
|
||||
func = func,
|
||||
id = netID
|
||||
}
|
||||
netID = netID + 1
|
||||
if type(nodeName) == "function" then
|
||||
func = nodeName
|
||||
nodeName = self.defaultNode or self:getRandomNode()
|
||||
if not func then
|
||||
error("You must provide a function!")
|
||||
end
|
||||
end
|
||||
self:sendTo(nodeName,"!N_THREAD!"..dat.data)
|
||||
self.OnDataReturned(function(rets)
|
||||
if rets.ID == nID then
|
||||
conn:Fire(unpack(rets.rets))
|
||||
end
|
||||
end)
|
||||
end)
|
||||
return conn
|
||||
end
|
||||
function master:newNetworkChannel(nodeName)
|
||||
--
|
||||
end
|
||||
function master:sendTo(nodeName,data)
|
||||
self:queue("send",nodeName,data)
|
||||
end
|
||||
function master:demandNodeExistance(nodeName)
|
||||
if self.nodes[nodeName] then
|
||||
return multi.hold(self.nodes[nodeName]:ping().pong)
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
function master:queue(c,...)
|
||||
table.insert(self._queue,{c,{...}})
|
||||
end
|
||||
function multi:newMasterNode(cd)
|
||||
local c = {}
|
||||
setmetatable(c, master)
|
||||
c.OnNodeDiscovered = multi:newConnection()
|
||||
c.OnNodeRemoved = multi:newConnection()
|
||||
c.OnDataRecieved = multi:newConnection()
|
||||
c.OnDataReturned = multi:newConnection()
|
||||
c.defaultNode = ""
|
||||
c.nodes = {}
|
||||
setmetatable(c.nodes,
|
||||
{__newindex = function(t,k,v)
|
||||
rawset(t,k,v)
|
||||
v.master = c
|
||||
c.OnNodeDiscovered:Fire(k,v)
|
||||
end})
|
||||
c._queue = {}
|
||||
if cd then
|
||||
if cd.nodeHost then
|
||||
cd.nodePort = cd.nodePort or cmd.defaultPort
|
||||
local n,no = c:addNode(cd.nodeHost,cd.nodePort)
|
||||
if n then
|
||||
c.nodes[n] = no
|
||||
end
|
||||
elseif cd.managerHost then
|
||||
cd.managerPort = cd.managerPort or cmd.defaultManagerPort
|
||||
c:getNodesFromManager(cd.managerHost,cd.managerPort)
|
||||
else
|
||||
c:getNodesFromBroadcast()
|
||||
end
|
||||
else
|
||||
c:getNodesFromBroadcast()
|
||||
end
|
||||
thread:newthread("CMDQueueProcessor",function()
|
||||
while true do
|
||||
thread.skip(128)
|
||||
local data = table.remove(c._queue,1)
|
||||
if data then
|
||||
local cmd = data[1]
|
||||
if cmd == "send" then
|
||||
local nodeName = data[2][1]
|
||||
local dat = data[2][2]
|
||||
c.nodes[nodeName]:send(dat)
|
||||
end
|
||||
end
|
||||
end
|
||||
end):OnError(function(...)
|
||||
print(...)
|
||||
end)
|
||||
return c
|
||||
end
|
||||
127
integration/networkManager/node.lua
Normal file
127
integration/networkManager/node.lua
Normal file
@ -0,0 +1,127 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local net = require("net")
|
||||
local cmd = require("multi.integration.networkManager.cmds")
|
||||
local multi,thread = require("multi"):init()
|
||||
local node = {}
|
||||
node.__index = node
|
||||
local rand = {}
|
||||
for i = 65,90 do
|
||||
rand[#rand+1] = string.char(i)
|
||||
end
|
||||
local function randName(n)
|
||||
local str = {}
|
||||
for i=1,(n or 10) do
|
||||
str[#str+1] = rand[math.random(1,#rand)]
|
||||
end
|
||||
return table.concat(str)
|
||||
end
|
||||
local getNames = thread:newFunction(function(names)
|
||||
local listen = socket.udp() -- make a new socket
|
||||
listen:setsockname(net.getLocalIP(), 11111)
|
||||
listen:settimeout(0)
|
||||
local data, ip, port = listen:receivefrom()
|
||||
thread.holdWithin(1,function()
|
||||
if data then
|
||||
local n, tp, ip, port = data:match("(%S-)|(%S-)|(%S-):(%d+)")
|
||||
if n then
|
||||
names[n]=true
|
||||
end
|
||||
end
|
||||
end)
|
||||
return multi.NIL
|
||||
end)
|
||||
local function setName(ref,name)
|
||||
if name then
|
||||
ref.name = "NODE_"..name
|
||||
ref.connection:broadcast(name)
|
||||
return
|
||||
end
|
||||
local names = {}
|
||||
getNames(names).wait() -- Prevents duplicate names from spawning!
|
||||
local name = randName()
|
||||
while names["NODE_"..name] do
|
||||
name = randName()
|
||||
end
|
||||
ref.name = "NODE_"..name
|
||||
ref.connection:broadcast(ref.name)
|
||||
end
|
||||
node.ServerCode = require("multi.integration.networkManager.serverSide")
|
||||
node.ClientCode = require("multi.integration.networkManager.clientSide")
|
||||
function node.random()
|
||||
return randName(12)
|
||||
end
|
||||
function node:registerWithManager(ip,port)
|
||||
if self.type ~= "server" then return end
|
||||
if not self.manager then
|
||||
self.manager = net:newTCPClient(ip,port)
|
||||
if not self.manager then
|
||||
error("Unable to connect to the node Manager! Is it running? Perhaps the hostname or port is incorrect!")
|
||||
end
|
||||
end
|
||||
thread:newFunction(function()
|
||||
thread.hold(function() return self.name end)
|
||||
self.manager:send("!REG_NODE!"..self.name.."|"..net.getLocalIP().."|"..self.connection.port)
|
||||
end)()
|
||||
end
|
||||
function node:new(host,port,name)
|
||||
local c = {}
|
||||
c.links = {}
|
||||
setmetatable(c,node)
|
||||
if type(host)=="number" or type(host)=="nil" then
|
||||
c.connection = net:newTCPServer(host or cmd.defaultPort)
|
||||
c.connection:enableBinaryMode()
|
||||
c.type = "server"
|
||||
c.connection.node = c
|
||||
c.connection.OnDataRecieved(self.ServerCode)
|
||||
setName(c)
|
||||
elseif type(host)=="table" and host.Type == "tcp" then
|
||||
c.connection = host
|
||||
c.connection:enableBinaryMode()
|
||||
c.type = "client"
|
||||
c.connection.node = c
|
||||
c.connection.OnDataRecieved(self.ClientCode)
|
||||
c.name = "MASTER_NODE"
|
||||
elseif type(host) == "string" and type(port)=="number" then
|
||||
c.connection = net:newTCPClient(host, port)
|
||||
c.connection:enableBinaryMode()
|
||||
c.type = "client"
|
||||
c.connection.node = c
|
||||
c.connection.OnDataRecieved(self.ClientCode)
|
||||
c.name = "MASTER_NODE"
|
||||
else
|
||||
error("Invalid arguments!")
|
||||
end
|
||||
return c
|
||||
end
|
||||
function node:ping()
|
||||
if self.type ~= "client" then return end
|
||||
self:send("!PING!")
|
||||
return {pong=self.connection.OnDataRecieved}
|
||||
end
|
||||
function node:send(data)
|
||||
if self.type ~= "client" then return end
|
||||
self.connection:send(data)
|
||||
end
|
||||
return node
|
||||
48
integration/networkManager/nodeManager.lua
Normal file
48
integration/networkManager/nodeManager.lua
Normal file
@ -0,0 +1,48 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi, thread = require("multi"):init()
|
||||
local cmd = require("multi.integration.networkManager.cmds")
|
||||
local net = require("net")
|
||||
local bin = require("bin")
|
||||
local nodes = { -- Testing stuff
|
||||
|
||||
}
|
||||
function multi:newNodeManager(port)
|
||||
print("Running node manager on port: "..(port or cmd.defaultManagerPort))
|
||||
local server = net:newTCPServer(port or cmd.defaultManagerPort)
|
||||
server.OnDataRecieved(function(serv, data, client)
|
||||
local cmd = data:match("!(.+)!")
|
||||
data = data:gsub("!"..cmd.."!","")
|
||||
if cmd == "NODES" then
|
||||
for i,v in ipairs(nodes) do
|
||||
-- Sample data
|
||||
serv:send(client, "!NODE!".. v[1].."|"..v[2].."|"..v[3])
|
||||
end
|
||||
elseif cmd == "REG_NODE" then
|
||||
local name, ip, port = data:match("(.-)|(.-)|(.+)")
|
||||
table.insert(nodes,{name,ip,port})
|
||||
print("Registering Node:",name, ip, port)
|
||||
end
|
||||
end)
|
||||
end
|
||||
47
integration/networkManager/serverSide.lua
Normal file
47
integration/networkManager/serverSide.lua
Normal file
@ -0,0 +1,47 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local bin, bits = require("bin").init()
|
||||
return function(self,data,client)
|
||||
local cmd,data = data:match("!(.-)!(.*)")
|
||||
--print("SERVER",cmd,data)
|
||||
if cmd == "PING" then
|
||||
self:send(client,"!PONG!")
|
||||
elseif cmd == "N_THREAD" then
|
||||
print(1)
|
||||
local dat = bin.new(data)
|
||||
print(2)
|
||||
local t = dat:getBlock("t")
|
||||
print(3)
|
||||
local ret = bin.new()
|
||||
print(4)
|
||||
ret:addBlock{ID = t.id,rets = {t.func(unpack(t.args))}}
|
||||
print(5)
|
||||
print(client,"!RETURNS!"..ret:getData())
|
||||
self:send(client,"!RETURNS!"..ret:getData())
|
||||
print(6)
|
||||
elseif cmd == "CHANNEL" then
|
||||
local dat = bin.new(data):getBlock("t")
|
||||
|
||||
end
|
||||
end
|
||||
23
integration/networkManager/threads.lua
Normal file
23
integration/networkManager/threads.lua
Normal file
@ -0,0 +1,23 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
27
integration/networkManager/utils.lua
Normal file
27
integration/networkManager/utils.lua
Normal file
@ -0,0 +1,27 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local bin = require("bin")
|
||||
local utils = {}
|
||||
-- Will contain data that handles sterilizing and managing data
|
||||
return utils
|
||||
213
integration/priorityManager/init.lua
Normal file
213
integration/priorityManager/init.lua
Normal file
@ -0,0 +1,213 @@
|
||||
-- Advanced process management. Mutates the multi namespace
|
||||
local multi, thread = require("multi"):init()
|
||||
local ok, chronos = pcall(require, "chronos") -- hpc
|
||||
|
||||
if not ok then chronos = nil end
|
||||
|
||||
-- This is an integration, we cannot directly access locals that are in the main file.
|
||||
|
||||
local PList = {
|
||||
multi.Priority_Core,
|
||||
multi.Priority_Very_High,
|
||||
multi.Priority_High,
|
||||
multi.Priority_Above_Normal,
|
||||
multi.Priority_Normal,
|
||||
multi.Priority_Below_Normal,
|
||||
multi.Priority_Low,
|
||||
multi.Priority_Very_Low,
|
||||
multi.Priority_Idle
|
||||
}
|
||||
|
||||
-- Restructered these functions since they rely on local variables from the core library
|
||||
|
||||
local mainloop = multi.mainloopRef
|
||||
local mainloop_p = multi.mainloop_p
|
||||
local uManagerRef = multi.uManagerRef
|
||||
local uManagerRefP = multi.uManagerRefP1
|
||||
|
||||
local PROFILE_COUNT = 5
|
||||
|
||||
-- self:setCurrentProcess() a bit slower than using the local var, but there isn't another option
|
||||
|
||||
local priorityManager = multi:newProcessor("Priority Manager", true)
|
||||
priorityManager.newThread = function() multi.warn("You cannot spawn threads on the priority manager!") end
|
||||
|
||||
priorityManager.setPriorityScheme = function() multi.warn("You cannot set priority on the priorityManager!") end
|
||||
|
||||
local function average(t)
|
||||
local sum = 0
|
||||
for _,v in pairs(t) do
|
||||
sum = sum + v
|
||||
end
|
||||
return sum / #t
|
||||
end
|
||||
|
||||
local function getPriority(obj)
|
||||
local avg = average(obj.__profiling)
|
||||
if avg < 0.0002 then
|
||||
return PList[1]
|
||||
elseif avg < 0.0004 then
|
||||
return PList[2]
|
||||
elseif avg < 0.0008 then
|
||||
return PList[3]
|
||||
elseif avg < 0.001 then
|
||||
return PList[4]
|
||||
elseif avg < 0.0025 then
|
||||
return PList[5]
|
||||
elseif avg < 0.005 then
|
||||
return PList[6]
|
||||
elseif avg < 0.008 then
|
||||
return PList[7]
|
||||
elseif avg < 0.01 then
|
||||
return PList[8]
|
||||
else
|
||||
return PList[9]
|
||||
end
|
||||
end
|
||||
|
||||
local start, stop
|
||||
|
||||
priorityManager.uManager = function(self)
|
||||
-- proc.run already checks if the processor is active
|
||||
self:setCurrentProcess()
|
||||
local Loop=self.Mainloop
|
||||
local ctask
|
||||
for _D=#Loop,1,-1 do
|
||||
ctask = Loop[_D]
|
||||
ctask:setCurrentTask()
|
||||
start = chronos.nanotime()
|
||||
if ctask:Act() then
|
||||
stop = chronos.nanotime()
|
||||
if ctask.__profiling then
|
||||
table.insert(ctask.__profiling, stop - start)
|
||||
end
|
||||
if ctask.__profiling and #ctask.__profiling == PROFILE_COUNT then
|
||||
ctask:setPriority(getPriority(ctask))
|
||||
ctask:reallocate(ctask.__restoreProc)
|
||||
ctask.__restoreProc = nil
|
||||
ctask.__profiling = nil
|
||||
end
|
||||
end
|
||||
self:setCurrentProcess()
|
||||
end
|
||||
end
|
||||
|
||||
local function processHook(obj, proc)
|
||||
if obj.Type == multi.registerType("process", "processes") or not(obj.IsAnActor) then return end
|
||||
obj.__restoreProc = proc
|
||||
obj.__profiling = {}
|
||||
obj:reallocate(priorityManager)
|
||||
end
|
||||
|
||||
local function init()
|
||||
local registry = {}
|
||||
|
||||
multi.priorityScheme = {
|
||||
RoundRobin = "RoundRobin",
|
||||
PriorityBased = "PriorityBased",
|
||||
TimeBased = "TimeBased"
|
||||
}
|
||||
|
||||
function multi:setProfilerCount(count)
|
||||
PROFILE_COUNT = count
|
||||
end
|
||||
|
||||
function multi:recalibrate()
|
||||
if self.__processConn then
|
||||
local items = self.Mainloop
|
||||
for i,v in pairs(items) do
|
||||
processHook(v, self)
|
||||
end
|
||||
else
|
||||
multi.error("Cannot recalibrate the priority if not using Time based mangement!")
|
||||
end
|
||||
end
|
||||
|
||||
function multi:isRegistredScheme(scheme)
|
||||
return registry[name] ~= nil
|
||||
end
|
||||
|
||||
function multi:getRegisteredScheme(scheme)
|
||||
return registry[name].mainloop, registry[name].umanager, registry[name].condition
|
||||
end
|
||||
|
||||
local empty_func = function() return true end
|
||||
function multi:registerScheme(name,options)
|
||||
local mainloop = options.mainloop or multi.error("You must provide a mainloop option when registring a scheme!")
|
||||
local umanager = options.umanager or multi.error("You must provide a umanager option when registring a scheme!")
|
||||
|
||||
if not options.condition then
|
||||
multi.warn("You might want to use condition when registring a scheme! A function that returns true has been auto generated for you!")
|
||||
end
|
||||
|
||||
local condition = options.condition or empty_func
|
||||
|
||||
if registry[name] and not registry[name].static then
|
||||
multi.warn("A scheme named: \"" .. name .. "\" has already been registred, overriting!")
|
||||
else
|
||||
multi.error("A scheme named: \"" .. name .. "\" has already been registred!")
|
||||
end
|
||||
|
||||
registry[name] = {
|
||||
mainloop = mainloop,
|
||||
umanager = umanger,
|
||||
condition = condition,
|
||||
static = options.static or false
|
||||
}
|
||||
|
||||
multi.priorityScheme[name] = name
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function multi:setPriorityScheme(scheme)
|
||||
|
||||
if not self.Type == multi.registerType("process", "processes") or not self.Type == multi.registerType("rootprocess") then
|
||||
multi.warn("You should only invoke setPriorityScheme on a processor object!")
|
||||
end
|
||||
|
||||
if scheme == multi.priorityScheme.RoundRobin then
|
||||
if self.__processConn then self.OnObjectCreated:Unconnect(self.__processConn) self.__processConn = nil end
|
||||
self.mainloop = mainloop
|
||||
self.uManager = uManagerRef
|
||||
elseif scheme == multi.priorityScheme.PriorityBased then
|
||||
if self.__processConn then self.OnObjectCreated:Unconnect(self.__processConn) self.__processConn = nil end
|
||||
self.mainloop = mainloop_p
|
||||
self.uManager = uManagerRefP
|
||||
elseif scheme == multi.priorityScheme.TimeBased then
|
||||
if not chronos then return multi.warn("Unable to use TimeBased Priority without the chronos library!") end
|
||||
if self.__processConn then multi.warn("Already enabled TimeBased Priority!") end
|
||||
self.__processConn = self.OnObjectCreated(processHook)
|
||||
self.mainloop = mainloop_p
|
||||
self.uManager = uManagerRefP
|
||||
elseif self:isRegistredScheme(scheme) then
|
||||
local mainloop, umanager, condition = self:getRegisteredScheme(scheme)
|
||||
if condition() then
|
||||
self.mainloop = mainloop
|
||||
self.uManager = umanager
|
||||
end
|
||||
else
|
||||
self.error("Invalid priority scheme selected!")
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
local function init_chronos()
|
||||
-- Let's implement a higher precision clock
|
||||
multi.setClock(chronos.nanotime) -- This is also in .000 format. So a plug and play works.
|
||||
thread:newThread("System Priority Manager", function()
|
||||
while true do
|
||||
thread.yield()
|
||||
priorityManager.run()
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
if chronos then
|
||||
init_chronos()
|
||||
else
|
||||
multi.warn("In order to have time based priority management, you need to install the chronos library!")
|
||||
end
|
||||
|
||||
init()
|
||||
221
integration/pseudoManager/extensions.lua
Normal file
221
integration/pseudoManager/extensions.lua
Normal file
@ -0,0 +1,221 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
local multi, thread = require("multi"):init()
|
||||
local GLOBAL, THREAD = multi.integration.GLOBAL, multi.integration.THREAD
|
||||
|
||||
local function stripUpValues(func)
|
||||
local dmp = string.dump(func)
|
||||
if setfenv then
|
||||
return loadstring(dmp,"IsolatedThread_PesudoThreading")
|
||||
else
|
||||
return load(dmp,"IsolatedThread_PesudoThreading","bt")
|
||||
end
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedQueue(name)
|
||||
local c = {}
|
||||
c.data = {}
|
||||
c.Type = multi.registerType("s_queue")
|
||||
function c:push(v)
|
||||
table.insert(self.data,v)
|
||||
end
|
||||
function c:pop()
|
||||
return table.remove(self.data,1)
|
||||
end
|
||||
function c:peek()
|
||||
return self.data[1]
|
||||
end
|
||||
function c:init()
|
||||
return self
|
||||
end
|
||||
function c:Hold(opt)
|
||||
if opt.peek then
|
||||
return thread.hold(function()
|
||||
return self:peek()
|
||||
end)
|
||||
else
|
||||
return thread.hold(function()
|
||||
return self:pop()
|
||||
end)
|
||||
end
|
||||
end
|
||||
GLOBAL[name or "_"] = c
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedTable(name)
|
||||
local c = {}
|
||||
c.Type = multi.registerType("s_table")
|
||||
function c:init()
|
||||
return self
|
||||
end
|
||||
function c:Hold(opt)
|
||||
if opt.key then
|
||||
return thread.hold(function()
|
||||
return self.tab[opt.key]
|
||||
end)
|
||||
else
|
||||
multi.error("Must provide a key to check opt.key = 'key'")
|
||||
end
|
||||
end
|
||||
GLOBAL[name or "_"] = c
|
||||
return c
|
||||
end
|
||||
|
||||
local setfenv = multi.isolateFunction
|
||||
|
||||
local jqc = 1
|
||||
function multi:newSystemThreadedJobQueue(n)
|
||||
local c = {}
|
||||
|
||||
c.cores = n or THREAD.getCores()
|
||||
c.registerQueue = {}
|
||||
c.Type = multi.registerType("s_jobqueue")
|
||||
c.funcs = multi:newSystemThreadedTable("__JobQueue_"..jqc.."_table")
|
||||
c.queue = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queue")
|
||||
c.queueReturn = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queueReturn")
|
||||
c.queueAll = multi:newSystemThreadedQueue("__JobQueue_"..jqc.."_queueAll")
|
||||
c.id = 0
|
||||
c.OnJobCompleted = multi:newConnection()
|
||||
|
||||
local allfunc = 0
|
||||
|
||||
function c:doToAll(func)
|
||||
for i = 1, self.cores do
|
||||
self.queueAll:push({allfunc, func})
|
||||
end
|
||||
allfunc = allfunc + 1
|
||||
end
|
||||
function c:registerFunction(name, func)
|
||||
if self.funcs[name] then
|
||||
multi.error("A function by the name "..name.." has already been registered!")
|
||||
end
|
||||
self.funcs[name] = func
|
||||
end
|
||||
function c:pushJob(name,...)
|
||||
self.id = self.id + 1
|
||||
self.queue:push{name,self.id,...}
|
||||
return self.id
|
||||
end
|
||||
function c:isEmpty()
|
||||
return queueJob:peek()==nil
|
||||
end
|
||||
local nFunc = 0
|
||||
function c:newFunction(name,func,holup) -- This registers with the queue
|
||||
if type(name)=="function" then
|
||||
holup = func
|
||||
func = name
|
||||
name = "JQ_Function_"..nFunc
|
||||
end
|
||||
nFunc = nFunc + 1
|
||||
c:registerFunction(name,func)
|
||||
return thread:newFunction(function(...)
|
||||
local id = c:pushJob(name,...)
|
||||
local link
|
||||
local rets
|
||||
link = c.OnJobCompleted(function(jid,...)
|
||||
if id==jid then
|
||||
rets = multi.pack(...)
|
||||
end
|
||||
end)
|
||||
return thread.hold(function()
|
||||
if rets then
|
||||
return multi.unpack(rets) or multi.NIL
|
||||
end
|
||||
end)
|
||||
end,holup),name
|
||||
end
|
||||
thread:newThread("jobManager",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
local dat = c.queueReturn:pop()
|
||||
if dat then
|
||||
c.OnJobCompleted:Fire(multi.unpack(dat))
|
||||
end
|
||||
end
|
||||
end)
|
||||
for i=1,c.cores do
|
||||
multi:newSystemThread("STJQ_"..multi.randomString(8),function(jqc)
|
||||
local GLOBAL, THREAD = require("multi.integration.pseudoManager"):init()
|
||||
local multi, thread = require("multi"):init()
|
||||
local clock = os.clock
|
||||
local funcs = THREAD.waitFor("__JobQueue_"..jqc.."_table")
|
||||
local queue = THREAD.waitFor("__JobQueue_"..jqc.."_queue")
|
||||
local queueReturn = THREAD.waitFor("__JobQueue_"..jqc.."_queueReturn")
|
||||
local queueAll = THREAD.waitFor("__JobQueue_"..jqc.."_queueAll")
|
||||
local registry = {}
|
||||
_G["__QR"] = queueReturn
|
||||
setmetatable(_G,{__index = funcs})
|
||||
thread:newThread("startUp",function()
|
||||
while true do
|
||||
thread.yield()
|
||||
local all = queueAll:peek()
|
||||
if all and not registry[all[1]] then
|
||||
queueAll:pop()[2]()
|
||||
end
|
||||
end
|
||||
end)
|
||||
thread:newThread("runner",function()
|
||||
thread.sleep(.1)
|
||||
while true do
|
||||
thread.yield()
|
||||
local all = queueAll:peek()
|
||||
if all and not registry[all[1]] then
|
||||
queueAll:pop()[2]()
|
||||
end
|
||||
local dat = thread.hold(queue)
|
||||
if dat then
|
||||
multi:newThread("JobSubRunner",function()
|
||||
local name = table.remove(dat,1)
|
||||
local id = table.remove(dat,1)
|
||||
local tab = {multi.isolateFunction(funcs[name],_G)(multi.unpack(dat))}
|
||||
table.insert(tab,1,id)
|
||||
queueReturn:push(tab)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi:mainloop()
|
||||
end, jqc)
|
||||
end
|
||||
|
||||
function c:Hold(opt)
|
||||
return thread.hold(self.OnJobCompleted)
|
||||
end
|
||||
|
||||
jqc = jqc + 1
|
||||
|
||||
self:create(c)
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:newSystemThreadedConnection(name)
|
||||
local conn = multi:newConnection()
|
||||
conn.init = function(self) return self end
|
||||
GLOBAL[name or "_"] = conn
|
||||
return conn
|
||||
end
|
||||
|
||||
require("multi.integration.sharedExtensions")
|
||||
123
integration/pseudoManager/init.lua
Normal file
123
integration/pseudoManager/init.lua
Normal file
@ -0,0 +1,123 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
package.path = "?/init.lua;?.lua;" .. package.path
|
||||
local multi, thread = require("multi"):init()
|
||||
|
||||
local pseudoProcessor = multi:newProcessor()
|
||||
|
||||
if multi.integration then
|
||||
return {
|
||||
init = function()
|
||||
return multi.integration.GLOBAL, multi.integration.THREAD
|
||||
end
|
||||
}
|
||||
end
|
||||
multi.isMainThread = true
|
||||
local activator = require("multi.integration.pseudoManager.threads")
|
||||
local GLOBAL, THREAD = activator.init(thread)
|
||||
|
||||
_G.THREAD_NAME = "MAIN_THREAD"
|
||||
_G.THREAD_ID = 0
|
||||
|
||||
function multi:canSystemThread() -- We are emulating system threading
|
||||
return true
|
||||
end
|
||||
|
||||
function multi:getPlatform()
|
||||
return "pesudo"
|
||||
end
|
||||
|
||||
local function split(str)
|
||||
local tab = {}
|
||||
for word in string.gmatch(str, '([^,]+)') do
|
||||
table.insert(tab,word)
|
||||
end
|
||||
return tab
|
||||
end
|
||||
|
||||
local tab = [[_VERSION,io,os,require,load,debug,assert,collectgarbage,error,getfenv,getmetatable,ipairs,loadstring,module,next,pairs,pcall,print,rawequal,rawget,rawset,select,setfenv,setmetatable,tonumber,tostring,type,xpcall,math,coroutine,string,table]]
|
||||
tab = split(tab)
|
||||
|
||||
local id = 0
|
||||
|
||||
function multi:newSystemThread(name, func, ...)
|
||||
local env
|
||||
env = {
|
||||
GLOBAL = GLOBAL,
|
||||
THREAD = THREAD,
|
||||
THREAD_NAME = tostring(name),
|
||||
__THREADNAME__ = tostring(name),
|
||||
THREAD_ID = id,
|
||||
thread = thread,
|
||||
multi = multi,
|
||||
}
|
||||
|
||||
for i, v in pairs(_G) do
|
||||
if not(env[i]) and not(i == "_G") and not(i == "local_global") then
|
||||
env[i] = v
|
||||
else
|
||||
multi.warn("skipping:",i)
|
||||
end
|
||||
end
|
||||
|
||||
if GLOBAL["__env"] then
|
||||
for i,v in pairs(GLOBAL["__env"]) do
|
||||
env[i] = v
|
||||
end
|
||||
end
|
||||
|
||||
env._G = env
|
||||
|
||||
local GLOBAL, THREAD = activator.init(thread, env)
|
||||
|
||||
local th = pseudoProcessor:newISOThread(name, func, env, ...)
|
||||
th.Type = multi.registerType("s_thread", "pseudoThreads")
|
||||
th.OnError(multi.error)
|
||||
|
||||
id = id + 1
|
||||
|
||||
return th
|
||||
end
|
||||
|
||||
THREAD.newSystemThread = multi.newSystemThread
|
||||
-- System threads as implemented here cannot share memory, but use a message passing system.
|
||||
-- An isolated thread allows us to mimic that behavior so if access data from the "main" thread happens things will not work. This behavior is in line with how the system threading works
|
||||
|
||||
function THREAD:newFunction(func,holdme)
|
||||
return thread:newFunctionBase(function(...)
|
||||
return multi:newSystemThread("TempSystemThread",func,...)
|
||||
end, holdme, multi.registerType("s_function", "pseudoFunctions"))()
|
||||
end
|
||||
|
||||
multi.print("Integrated Pesudo Threading!")
|
||||
multi.integration = {} -- for module creators
|
||||
multi.integration.GLOBAL = GLOBAL
|
||||
multi.integration.THREAD = THREAD
|
||||
require("multi.integration.pseudoManager.extensions")
|
||||
require("multi.integration.sharedExtensions")
|
||||
return {
|
||||
init = function()
|
||||
return GLOBAL, THREAD
|
||||
end
|
||||
}
|
||||
122
integration/pseudoManager/threads.lua
Normal file
122
integration/pseudoManager/threads.lua
Normal file
@ -0,0 +1,122 @@
|
||||
--[[
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
|
||||
local function getOS()
|
||||
if package.config:sub(1, 1) == "\\" then
|
||||
return "windows"
|
||||
else
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
|
||||
local function INIT(thread)
|
||||
local THREAD = {}
|
||||
local GLOBAL = {}
|
||||
|
||||
THREAD.Priority_Core = 3
|
||||
THREAD.Priority_High = 2
|
||||
THREAD.Priority_Above_Normal = 1
|
||||
THREAD.Priority_Normal = 0
|
||||
THREAD.Priority_Below_Normal = -1
|
||||
THREAD.Priority_Low = -2
|
||||
THREAD.Priority_Idle = -3
|
||||
|
||||
function THREAD.set(name, val)
|
||||
GLOBAL[name] = val
|
||||
end
|
||||
|
||||
function THREAD.get(name)
|
||||
return GLOBAL[name]
|
||||
end
|
||||
|
||||
function THREAD.waitFor(name)
|
||||
return thread.hold(function() return GLOBAL[name] end)
|
||||
end
|
||||
|
||||
if getOS() == "windows" then
|
||||
THREAD.__CORES = tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
THREAD.__CORES = tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
|
||||
function THREAD.getCores()
|
||||
return THREAD.__CORES
|
||||
end
|
||||
|
||||
function THREAD.getConsole()
|
||||
local c = {}
|
||||
function c.print(...)
|
||||
print(...)
|
||||
end
|
||||
function c.error(err)
|
||||
error("ERROR in <"..GLOBAL["$__THREADNAME__"]..">: "..err)
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function THREAD.getThreads()
|
||||
return {}--GLOBAL.__THREADS__
|
||||
end
|
||||
|
||||
THREAD.pushStatus = thread.pushStatus
|
||||
|
||||
function THREAD.kill()
|
||||
error("Thread was killed!")
|
||||
end
|
||||
|
||||
THREAD.sleep = thread.sleep
|
||||
|
||||
THREAD.hold = thread.hold
|
||||
|
||||
THREAD.defer = thread.defer
|
||||
|
||||
function THREAD.setENV(env, name)
|
||||
name = name or "__env"
|
||||
GLOBAL[name] = env
|
||||
end
|
||||
|
||||
function THREAD.getENV(name)
|
||||
name = name or "__env"
|
||||
return GLOBAL[name]
|
||||
end
|
||||
|
||||
function THREAD.exposeENV(name)
|
||||
name = name or "__env"
|
||||
local env = THREAD.getENV(name)
|
||||
for i,v in pairs(env) do
|
||||
-- This may need to be reworked!
|
||||
local_global[i] = v
|
||||
end
|
||||
end
|
||||
|
||||
function THREAD.sync()
|
||||
thread.sleep(.5)
|
||||
end
|
||||
|
||||
return GLOBAL, THREAD
|
||||
end
|
||||
|
||||
return {init = function(thread, global)
|
||||
return INIT(thread, global)
|
||||
end}
|
||||
336
integration/sharedExtensions/init.lua
Normal file
336
integration/sharedExtensions/init.lua
Normal file
@ -0,0 +1,336 @@
|
||||
--[[ todo finish the targeted job!
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2023 Ryan Ward
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sub-license, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
]]
|
||||
|
||||
local multi, thread = require("multi"):init()
|
||||
|
||||
-- Returns a handler that allows a user to interact with an object on another thread!
|
||||
-- Create on the thread that you want to interact with, send over the handle
|
||||
|
||||
function multi:chop(obj)
|
||||
if not _G["UIDS"] then
|
||||
_G["UIDS"] = {}
|
||||
end
|
||||
local multi, thread = require("multi"):init()
|
||||
local list = {[0] = multi.randomString(12)}
|
||||
_G[list[0]] = obj
|
||||
for i,v in pairs(obj) do
|
||||
if type(v) == "function" or type(v) == "table" and v.Type == multi.registerType("s_function") then
|
||||
table.insert(list, i)
|
||||
elseif type(v) == "table" and v.Type == multi.registerType("connector", "connections") then
|
||||
table.insert(list, {i, multi:newProxy(multi:chop(v)):init()})
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
function multi:newProxy(list)
|
||||
|
||||
local c = {}
|
||||
|
||||
c.name = multi.randomString(12)
|
||||
c.is_init = false
|
||||
local multi, thread = nil, nil
|
||||
function c:init()
|
||||
local multi, thread = nil, nil
|
||||
local function copy(obj)
|
||||
if type(obj) ~= 'table' then return obj end
|
||||
local res = {}
|
||||
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
|
||||
return res
|
||||
end
|
||||
if not(self.is_init) then
|
||||
THREAD.sync()
|
||||
self.is_init = true
|
||||
local multi, thread = require("multi"):init()
|
||||
self.proxy_link = "PL" .. multi.randomString(12)
|
||||
|
||||
if multi.integration then
|
||||
GLOBAL = multi.integration.GLOBAL
|
||||
THREAD = multi.integration.THREAD
|
||||
end
|
||||
|
||||
GLOBAL[self.proxy_link] = self
|
||||
|
||||
local function check()
|
||||
return self.send:pop()
|
||||
end
|
||||
|
||||
self.send = multi:newSystemThreadedQueue(self.name.."_S"):init()
|
||||
self.recv = multi:newSystemThreadedQueue(self.name.."_R"):init()
|
||||
self.funcs = list
|
||||
self._funcs = copy(list)
|
||||
self.Type = multi.registerType("proxy", "proxies")
|
||||
self.TID = THREAD_ID
|
||||
|
||||
thread:newThread("Proxy_Handler_" .. multi.randomString(4), function()
|
||||
while true do
|
||||
local data = thread.hold(check)
|
||||
if data then
|
||||
-- Let's not hold the main threadloop
|
||||
thread:newThread("Temp_Thread", function()
|
||||
local func = table.remove(data, 1)
|
||||
local sref = table.remove(data, 1)
|
||||
local ret
|
||||
if sref then
|
||||
ret = {_G[list[0]][func](_G[list[0]], multi.unpack(data))}
|
||||
else
|
||||
ret = {_G[list[0]][func](multi.unpack(data))}
|
||||
end
|
||||
|
||||
for i = 1,#ret do
|
||||
if type(ret[i]) == "table" and ret[i].Type ~= nil and ret[i].Type ~= multi.registerType("proxy", "proxies") then
|
||||
ret[i] = "\1PARENT_REF"
|
||||
end
|
||||
if type(ret[i]) == "table" and getmetatable(ret[i]) then
|
||||
setmetatable(ret[i],nil) -- remove that metatable, we do not need it on the other side!
|
||||
end
|
||||
if ret[i] == _G[list[0]] then
|
||||
-- We cannot return itself, that return can contain bad values.
|
||||
ret[i] = "\1SELF_REF"
|
||||
end
|
||||
end
|
||||
table.insert(ret, 1, func)
|
||||
self.recv:push(ret)
|
||||
end)
|
||||
end
|
||||
end
|
||||
end)
|
||||
return self
|
||||
else
|
||||
THREAD.sync()
|
||||
if not self.funcs then return self end
|
||||
local function copy(obj)
|
||||
if type(obj) ~= 'table' then return obj end
|
||||
local res = {}
|
||||
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
|
||||
return res
|
||||
end
|
||||
local multi, thread = require("multi"):init()
|
||||
local me = self
|
||||
local funcs = copy(self.funcs)
|
||||
if multi.integration then
|
||||
GLOBAL = multi.integration.GLOBAL
|
||||
THREAD = multi.integration.THREAD
|
||||
end
|
||||
self.send = THREAD.waitFor(self.name.."_S"):init()
|
||||
self.recv = THREAD.waitFor(self.name.."_R"):init()
|
||||
self.Type = multi.registerType("proxy", "proxies")
|
||||
for _,v in pairs(funcs) do
|
||||
if type(v) == "table" then
|
||||
-- We have a connection
|
||||
v[2]:init(proc_name)
|
||||
self[v[1]] = v[2]
|
||||
v[2].Parent = self
|
||||
setmetatable(v[2],getmetatable(multi:newConnection()))
|
||||
else
|
||||
self[v] = thread:newFunction(function(self,...)
|
||||
if self == me then
|
||||
me.send:push({v, true, ...})
|
||||
else
|
||||
me.send:push({v, false, ...})
|
||||
end
|
||||
return thread.hold(function()
|
||||
local data = me.recv:peek()
|
||||
if data and data[1] == v then
|
||||
me.recv:pop()
|
||||
table.remove(data, 1)
|
||||
for i=1,#data do
|
||||
if data[i] == "\1SELF_REF" then
|
||||
data[i] = me
|
||||
elseif data[i] == "\1PARENT_REF" then
|
||||
data[i] = me.Parent
|
||||
end
|
||||
end
|
||||
return multi.unpack(data)
|
||||
end
|
||||
end)
|
||||
end, true)
|
||||
end
|
||||
end
|
||||
return self
|
||||
end
|
||||
end
|
||||
function c:getTransferable()
|
||||
local cp = {}
|
||||
local multi, thread = require("multi"):init()
|
||||
local function copy(obj)
|
||||
if type(obj) ~= 'table' then return obj end
|
||||
local res = {}
|
||||
for k, v in pairs(obj) do res[copy(k)] = copy(v) end
|
||||
return res
|
||||
end
|
||||
cp.is_init = true
|
||||
cp.proxy_link = self.proxy_link
|
||||
cp.name = self.name
|
||||
cp.funcs = copy(self._funcs)
|
||||
cp.init = function(self)
|
||||
local multi, thread = require("multi"):init()
|
||||
if multi.integration then
|
||||
GLOBAL = multi.integration.GLOBAL
|
||||
THREAD = multi.integration.THREAD
|
||||
end
|
||||
local proxy = THREAD.waitFor(self.proxy_link)
|
||||
proxy.funcs = self.funcs
|
||||
return proxy:init()
|
||||
end
|
||||
return cp
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
|
||||
local targets = {}
|
||||
local references = {}
|
||||
|
||||
local jid = -1
|
||||
function multi:newSystemThreadedProcessor(cores)
|
||||
|
||||
local name = "STP_"..multi.randomString(4) -- set a random name if none was given.
|
||||
|
||||
local autoscale = autoscale or false -- Will scale up the number of cores that the process uses.
|
||||
local c = {}
|
||||
|
||||
setmetatable(c,{__index = multi})
|
||||
|
||||
c.Type = multi.registerType("s_process", "s_processes")
|
||||
c.threads = {}
|
||||
c.cores = cores or 8
|
||||
c.Name = name
|
||||
c.Mainloop = {}
|
||||
c.__count = 0
|
||||
c.processors = {}
|
||||
c.proc_list = {}
|
||||
c.OnObjectCreated = multi:newConnection()
|
||||
c.parent = self
|
||||
c.jobqueue = multi:newSystemThreadedJobQueue(c.cores)
|
||||
|
||||
function c:pushJob(ID, name, ...)
|
||||
local tq = THREAD.waitFor(self.Name .. "_target_tq_" .. ID):init()
|
||||
tq:push{name, jid, multi.pack(...)}
|
||||
jid = jid - 1
|
||||
return jid + 1
|
||||
end
|
||||
|
||||
c.jobqueue:registerFunction("packObj",function(obj)
|
||||
local multi, thread = require("multi"):init()
|
||||
|
||||
local list = multi:chop(obj)
|
||||
obj.__link_name = list[0]
|
||||
|
||||
local proxy = multi:newProxy(list):init()
|
||||
|
||||
return proxy
|
||||
end)
|
||||
|
||||
c.spawnThread = c.jobqueue:newFunction("__spawnThread__", function(name, func, ...)
|
||||
local multi, thread = require("multi"):init()
|
||||
local obj = thread:newThread(name, func, ...)
|
||||
return packObj(obj)
|
||||
end, true)
|
||||
|
||||
c.spawnTask = c.jobqueue:newFunction("__spawnTask__", function(obj, func, ...)
|
||||
local multi, thread = require("multi"):init()
|
||||
local obj = multi[obj](multi, func, ...)
|
||||
return packObj(obj)
|
||||
end, true)
|
||||
|
||||
local implement = {
|
||||
"newLoop",
|
||||
"newTLoop",
|
||||
"newUpdater",
|
||||
"newEvent",
|
||||
"newAlarm",
|
||||
"newStep",
|
||||
"newTStep",
|
||||
"newService"
|
||||
}
|
||||
|
||||
for _, method in pairs(implement) do
|
||||
c[method] = thread:newFunction(function(self, ...)
|
||||
proxy = self.spawnTask(method, ...)
|
||||
proxy:init()
|
||||
references[proxy] = self
|
||||
return proxy
|
||||
end, true)
|
||||
end
|
||||
|
||||
function c:newThread(name, func, ...)
|
||||
proxy = self.spawnThread(name, func, ...):init(self.Name)
|
||||
references[proxy] = self
|
||||
table.insert(self.threads, proxy)
|
||||
return proxy
|
||||
end
|
||||
|
||||
function c:newFunction(func, holdme)
|
||||
return self.jobqueue:newFunction(func, holdme)
|
||||
end
|
||||
|
||||
function c:newSharedTable(name)
|
||||
if not name then multi.error("You must provide a name when creating a table!") end
|
||||
local tbl_name = "TABLE_"..multi.randomString(8)
|
||||
self.jobqueue:doToAll(function(tbl_name, interaction)
|
||||
_G[interaction] = THREAD.waitFor(tbl_name):init()
|
||||
end, tbl_name, name)
|
||||
return multi:newSystemThreadedTable(tbl_name):init()
|
||||
end
|
||||
|
||||
function c:getHandler()
|
||||
return function() end -- return empty function
|
||||
end
|
||||
|
||||
function c:getThreads()
|
||||
return self.threads
|
||||
end
|
||||
|
||||
function c:getFullName()
|
||||
return self.parent:getFullName() .. "." .. c.Name
|
||||
end
|
||||
|
||||
function c:getName()
|
||||
return self.Name
|
||||
end
|
||||
|
||||
function c.run()
|
||||
return self
|
||||
end
|
||||
|
||||
function c.isActive()
|
||||
return true
|
||||
end
|
||||
|
||||
function c.Start()
|
||||
return self
|
||||
end
|
||||
|
||||
function c.Stop()
|
||||
return self
|
||||
end
|
||||
|
||||
function c:Destroy()
|
||||
return false
|
||||
end
|
||||
|
||||
return c
|
||||
end
|
||||
|
||||
13
integration/threading.lua
Normal file
13
integration/threading.lua
Normal file
@ -0,0 +1,13 @@
|
||||
-- We need to detect what enviroment we are running our code in.
|
||||
return {
|
||||
init = function()
|
||||
if love then
|
||||
return require("multi.integration.loveManager"):init()
|
||||
else
|
||||
if pcall(require,"lanes") then
|
||||
return require("multi.integration.lanesManager"):init()
|
||||
end
|
||||
return require("multi.integration.pesudoManager"):init()
|
||||
end
|
||||
end
|
||||
}
|
||||
20
lovethreads/conf.lua
Normal file
20
lovethreads/conf.lua
Normal file
@ -0,0 +1,20 @@
|
||||
function love.conf(t)
|
||||
t.identity = nil -- The name of the save directory (string)
|
||||
t.version = "12.0" -- The LOVE version this game was made for (string)
|
||||
t.console = true -- Attach a console (boolean, Windows only)
|
||||
|
||||
-- t.modules.audio = false -- Enable the audio module (boolean)
|
||||
-- t.modules.event = false -- Enable the event module (boolean)
|
||||
-- t.modules.graphics = false -- Enable the graphics module (boolean)
|
||||
-- t.modules.image = false -- Enable the image module (boolean)
|
||||
-- t.modules.joystick = false -- Enable the joystick module (boolean)
|
||||
-- t.modules.keyboard = false -- Enable the keyboard module (boolean)
|
||||
-- t.modules.math = false -- Enable the math module (boolean)
|
||||
-- t.modules.mouse = false -- Enable the mouse module (boolean)
|
||||
-- t.modules.physics = false -- Enable the physics module (boolean)
|
||||
-- t.modules.sound = false -- Enable the sound module (boolean)
|
||||
-- t.modules.system = true -- Enable the system module (boolean)
|
||||
-- t.modules.timer = true -- Enable the timer module (boolean)
|
||||
-- t.modules.window = false -- Enable the window module (boolean)
|
||||
-- t.modules.thread = true -- Enable the thread module (boolean)
|
||||
end
|
||||
75
lovethreads/main.lua
Normal file
75
lovethreads/main.lua
Normal file
@ -0,0 +1,75 @@
|
||||
package.path = "../?/init.lua;../?.lua;"..package.path
|
||||
local multi, thread = require("multi"):init{print=true, warning = true, error=true}
|
||||
local utils = require("multi.integration.loveManager.utils")
|
||||
|
||||
local people = {1,2,3}
|
||||
|
||||
function dump(o)
|
||||
if type(o) == 'table' then
|
||||
local s = '{ '
|
||||
for k,v in pairs(o) do
|
||||
if type(k) ~= 'number' then k = '"'..k..'"' end
|
||||
s = s .. '['..k..'] = ' .. dump(v) .. '('..type(v):sub(1,1)..'),'
|
||||
end
|
||||
return s .. '} '
|
||||
else
|
||||
return tostring(o)
|
||||
end
|
||||
end
|
||||
|
||||
local fpeople = utils.pack(people)
|
||||
|
||||
print("Pack:", dump(fpeople))
|
||||
|
||||
local people = utils.unpack(fpeople)
|
||||
|
||||
print("Unpack:", dump(people))
|
||||
print(type(people[3]))
|
||||
|
||||
-- GLOBAL, THREAD = require("multi.integration.loveManager"):init()
|
||||
|
||||
-- local queue = multi:newSystemThreadedQueue("TestQueue")
|
||||
-- local tab = multi:newSystemThreadedTable("TestTable")
|
||||
|
||||
-- local test = multi:newSystemThread("Test",function()
|
||||
-- local queue = THREAD.waitFor("TestQueue")
|
||||
-- local tab = THREAD.waitFor("TestTable")
|
||||
-- print("THREAD_ID:",THREAD_ID)
|
||||
-- queue:push("Did it work?")
|
||||
-- tab["Test"] = true
|
||||
-- return 1,2,3
|
||||
-- end)
|
||||
|
||||
-- multi:newThread("QueueTest", function()
|
||||
-- print(thread.hold(queue))
|
||||
-- print(thread.hold(tab, {key="Test"}))
|
||||
-- print("Done!")
|
||||
-- end)
|
||||
|
||||
-- local jq = multi:newSystemThreadedJobQueue(n)
|
||||
|
||||
-- jq:registerFunction("test2",function()
|
||||
-- print("This works!")
|
||||
-- end)
|
||||
|
||||
-- jq:registerFunction("test",function(a, b, c)
|
||||
-- print(a, b+c)
|
||||
-- test2()
|
||||
-- return a+b+c
|
||||
-- end)
|
||||
|
||||
-- print("Job:",jq:pushJob("test",1,2,3))
|
||||
-- print("Job:",jq:pushJob("test",2,3,4))
|
||||
-- print("Job:",jq:pushJob("test",5,6,7))
|
||||
|
||||
-- jq.OnJobCompleted(function(...)
|
||||
-- print("Job Completed!", ...)
|
||||
-- end)
|
||||
|
||||
-- function love.draw()
|
||||
-- --
|
||||
-- end
|
||||
|
||||
-- function love.update()
|
||||
-- multi:uManager()
|
||||
-- end
|
||||
1
lovethreads/multi
Normal file
1
lovethreads/multi
Normal file
@ -0,0 +1 @@
|
||||
../
|
||||
@ -1,42 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newAlarm(set)
|
||||
local c=self:newBase()
|
||||
c.Type='alarm'
|
||||
c.Priority=self.Priority_Low
|
||||
c.timer=self:newTimer()
|
||||
c.set=set or 0
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.set)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
if self.timer:Get()>=self.set then
|
||||
self:Pause()
|
||||
self.Active=false
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:Resume()
|
||||
self.Parent.Resume(self)
|
||||
self.timer:Resume()
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self:Resume()
|
||||
self.timer:Reset()
|
||||
end
|
||||
function c:OnRing(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:Pause()
|
||||
self.timer:Pause()
|
||||
self.Parent.Pause(self)
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,18 +0,0 @@
|
||||
require("multi.alarm")
|
||||
require("multi.function")
|
||||
require("multi.loop")
|
||||
require("multi.tloop")
|
||||
require("multi.step")
|
||||
require("multi.task")
|
||||
require("multi.threading")
|
||||
require("multi.trigger")
|
||||
require("multi.tstep")
|
||||
require("multi.updater")
|
||||
require("multi.watcher")
|
||||
require("multi.threading.alarm")
|
||||
require("multi.threading.event")
|
||||
require("multi.threading.loop")
|
||||
require("multi.threading.process")
|
||||
require("multi.threading.step")
|
||||
require("multi.threading.tstep")
|
||||
require("multi.threading.updater")
|
||||
@ -1,104 +0,0 @@
|
||||
require("multi.all")
|
||||
os.sleep=love.timer.sleep
|
||||
function bin.load(file,s,r)
|
||||
content, size = love.filesystem.read(file)
|
||||
local temp=bin.new(content)
|
||||
temp.filepath=file
|
||||
return temp
|
||||
end
|
||||
function bin:tofile(filename)
|
||||
if not(filename) or self.Stream then return nil end
|
||||
love.filesystem.write(filename,self.data)
|
||||
end
|
||||
function bin.stream(file,l)
|
||||
error("Sorry streaming is not available when using love2d :(, I am looking for a solution though :)")
|
||||
end
|
||||
function love.run()
|
||||
if love.math then
|
||||
love.math.setRandomSeed(os.time())
|
||||
end
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
end
|
||||
if love.load then love.load(arg) end
|
||||
if love.timer then love.timer.step() end
|
||||
local dt = 0
|
||||
while true do
|
||||
-- Process events.
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
if multi.boost then
|
||||
for i=1,multi.boost-1 do
|
||||
multi:uManager(dt)
|
||||
end
|
||||
end
|
||||
multi:uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
multi.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if multi.draw then multi.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
end
|
||||
end
|
||||
multi.drawF={}
|
||||
function multi:dManager()
|
||||
for ii=1,#multi.drawF do
|
||||
multi.drawF[ii]()
|
||||
end
|
||||
end
|
||||
function multi:onDraw(func,i)
|
||||
i=i or 1
|
||||
table.insert(self.drawF,i,func)
|
||||
end
|
||||
function multi:lManager()
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return nil
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
multi:uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
multi.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if multi.draw then multi.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
end
|
||||
@ -1,97 +0,0 @@
|
||||
History: EventManager,EventManager+,MultiManager <-- Current After 6.3.0 Versioning scheme was altered. A.0.0
|
||||
New in 1.0.0
|
||||
Nothing really however a changelog will now be recorded!
|
||||
version.major.minor
|
||||
New in 1.1.0
|
||||
Changed: multi:newConnection(protect) method
|
||||
Changed the way you are able to interact with it by adding the __call metamethod
|
||||
Old usage:
|
||||
OnUpdate=multi:newConnection()
|
||||
OnUpdate:connect(function(...)
|
||||
print("Updating",...)
|
||||
end)
|
||||
OnUpdate:Fire(1,2,3)
|
||||
New usage: notice that connect is no longer needed! Both ways still work! and always will work :)
|
||||
OnUpdate=multi:newConnection()
|
||||
OnUpdate(function(...)
|
||||
print("Updating",...)
|
||||
end)
|
||||
OnUpdate:Fire(1,2,3)
|
||||
New in 1.2.0 (12/31/2016)
|
||||
Added:
|
||||
connectionobj.getConnection(name)
|
||||
returns a list of an instance (or instances) of a single connect made with connectionobj:connect(func,name) or connectionobj(func,name)
|
||||
if you can orginize data before hand you can route info to certain connections thus saving a lot of cpu time. NOTE: only one name per each connection... you can't have 2 of the same names in a dictonary... the last one will be used
|
||||
Changed: obj=multi:newConnection()
|
||||
obj:connect(func,name) and obj(func,name)
|
||||
Added the name argument to allow indexing specific connection objects... Useful when creating an async library
|
||||
New in 1.3.0 (1/29/2017)
|
||||
Added:
|
||||
Load detection!
|
||||
multi.threshold -- minimum amount of cycles that all mObjs should be allotted before the Manager is considered burdened. Defualt: 256
|
||||
multi.threstimed -- amount of time when counting the number of cycles, Greater gives a more accurate view of the load, but takes more time. Defualt: .001
|
||||
multi:setThreshold(n) -- method used to set multi.threshold
|
||||
multi:setThrestimed(n) -- method used to set multi.threstimed
|
||||
multi:getLoad() -- returns a number between 0 and 100
|
||||
New in 1.4.0 (3/20/2017)
|
||||
Added:
|
||||
multiobj:reallocate(ProcessObj) -- changes the parent process of an object
|
||||
ProcessObj:getController() -- returns the mThread so you can opperate on it like a multiobj
|
||||
Example1:
|
||||
require("multimanager") -- require the library
|
||||
int1=multi:newProcess() -- create a process
|
||||
int1.NAME="int1" -- give it a name for example purposes
|
||||
int2=multi:newProcess() -- create another process to reallocate
|
||||
int2.NAME="int2" -- name this a different name
|
||||
step=int1:newTStep(1,10) -- create a TStep so we can slowly see what is going on
|
||||
step:OnStep(function(p,s) -- connect to the onstep event
|
||||
print(p,s.Parent.NAME) -- print the position and process name
|
||||
end)
|
||||
step:OnEnd(function(s) -- when the step ends lets reallocate it to the other process
|
||||
if s.Parent.NAME=="int1" then -- lets only do this if it is in the int1 process
|
||||
s:reallocate(int2) -- send it to int2
|
||||
s:Reset() -- reset the object
|
||||
else
|
||||
print("We are done!")
|
||||
os.exit() -- end the program when int2 did its thing
|
||||
end
|
||||
end)
|
||||
int1:Start() -- start process 1
|
||||
int2:Start() -- start process 2
|
||||
multi:mainloop() -- start the main loop
|
||||
Fixed/Updated:
|
||||
queuer=multi:newQueuer([string: file])
|
||||
Alarms now preform as they should on a queuer
|
||||
Example2:
|
||||
int=multi:newQueuer()
|
||||
step=int:newTStep(1,10,1,.5)
|
||||
alarm=int:newAlarm(2)
|
||||
step2=int:newTStep(1,5,1,.5)
|
||||
step:OnStep(function(p,s)
|
||||
print(p)
|
||||
end)
|
||||
step2:OnStep(function(p,s)
|
||||
print(p,"!")
|
||||
end)
|
||||
alarm:OnRing(function(a)
|
||||
print("Ring1!!!")
|
||||
end)
|
||||
int:OnQueueCompleted(function(s)
|
||||
s:Pause()
|
||||
print("Done!")
|
||||
os.exit()
|
||||
end)
|
||||
int:Start()
|
||||
multi:mainloop()
|
||||
New in 1.4.1 (4/10/2017)
|
||||
Change:
|
||||
small change to the hold method to make it a bit more lightweight
|
||||
Using a timer instead of an alarm object!
|
||||
Limits to hold:
|
||||
cannot hold more than 1 object at a time, and doing so could cause a deadlock!
|
||||
Upcomming:
|
||||
Threaded objects wrapped in corutines, so you can hold/sleep without problems!
|
||||
New in 1.5.1 (6/2/2017)
|
||||
Added:
|
||||
Threaded objects
|
||||
TLoop
|
||||
@ -1,56 +0,0 @@
|
||||
'Current Version: A.4.1 stable
|
||||
MultiManager has 19 Objects: # indicates most commonly used 1-19 1 being the most used by me
|
||||
+Events #7
|
||||
+Alarms #2
|
||||
+Loops #3
|
||||
+Steps #4
|
||||
+TSteps #6
|
||||
+Triggers #16
|
||||
+Tasks #12
|
||||
+Connections #1 -- This is a rather new feature of this library, but has become the most useful for async handling. Knowing this is already 50% of this library
|
||||
+Timers #14 -- this was tricky because these make up both Alarms and TSteps, but in purly using this standalone is almost non existent
|
||||
+Jobs #11
|
||||
+Process #10
|
||||
+Conditions #15
|
||||
+Ranges #8
|
||||
+Threads #13
|
||||
+Functions #5
|
||||
+Queuers #17
|
||||
+Updaters #9
|
||||
+Watchers #18
|
||||
+CustomObjects #19
|
||||
|
||||
Constructors [Runners]
|
||||
---------------------- Note: multi is the main Processor Obj It cannot be paused or destroyed (kinda)
|
||||
ProcessObj=multi:newProcess([string: FILE defualt: nil])
|
||||
ProcessObj=multi:newQueuer([string: FILE defualt: nil])
|
||||
|
||||
NOTE: The multi namespace is also a ProcessObj
|
||||
|
||||
|
||||
Constructors [ACTORS]
|
||||
--------------------- Note: everything is a multiObj!
|
||||
eventObj=multi:newEvent([function: TASK defualt: function() end])
|
||||
alarmObj=multi:newAlarm([number: SET defualt: 0])
|
||||
loopObj=multi:newLoop([function: FUNC])
|
||||
tloopObj=multi:newTLoop([function: FUNC], number: n)
|
||||
stepObj=multi:newStep([number: START defualt: 0],[number: RESET defualt: inf],[number: COUNT defualt: 1],[number: SKIP defualt: 0])
|
||||
tstepObj=multi:newTStep([number: START defualt: 0],[number: RESET defualt: inf],[number: COUNT defualt: 1],[number: SET defualt: 1])
|
||||
updaterObj=multi:newUpdater([number: SKIP defualt: 0])
|
||||
watcherObj=multi:newWatcher(table: NAMESPACE,string: NAME)
|
||||
multiObj=multi:newCustomObject([table: OBJREF],[string: T='process'])
|
||||
|
||||
Constructors [Semi-ACTORS]
|
||||
--------------------------
|
||||
multi:newJob(function: func,[string: name])
|
||||
multi:newRange(number: a,number: b,[number: c])
|
||||
multi:newCondition(func)
|
||||
void=multi:newThread(string: name,function: func)
|
||||
|
||||
Constructors [NON-ACTORS]
|
||||
-------------------------
|
||||
multi:newTrigger(function: func)
|
||||
multi:newTask(function: func)
|
||||
multi:newConnection()
|
||||
multi:newTimer()
|
||||
multi:newFunction(function: func)
|
||||
@ -1,19 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newFunction(func)
|
||||
local c={}
|
||||
c.func=func
|
||||
mt={
|
||||
__index=multi,
|
||||
__call=function(self,...) if self.Active then return self:func(...) end local t={...} return "PAUSED" end
|
||||
}
|
||||
c.Parent=self
|
||||
function c:Pause()
|
||||
self.Active=false
|
||||
end
|
||||
function c:Resume()
|
||||
self.Active=true
|
||||
end
|
||||
setmetatable(c,mt)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,127 +0,0 @@
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=='\\' then
|
||||
return 'windows'
|
||||
else
|
||||
return 'unix'
|
||||
end
|
||||
end
|
||||
-- Step 1 get lanes
|
||||
lanes=require("lanes").configure()
|
||||
package.path="lua/?/init.lua;lua/?.lua;"..package.path
|
||||
require("multi.all") -- get it all and have it on all lanes
|
||||
local multi=multi
|
||||
-- Step 2 set up the linda objects
|
||||
local __GlobalLinda = lanes.linda() -- handles global stuff
|
||||
local __SleepingLinda = lanes.linda() -- handles sleeping stuff
|
||||
-- For convience a GLOBAL table will be constructed to handle requests
|
||||
local GLOBAL={}
|
||||
setmetatable(GLOBAL,{
|
||||
__index=function(t,k)
|
||||
return __GlobalLinda:get(k)
|
||||
end,
|
||||
__newindex=function(t,k,v)
|
||||
__GlobalLinda:set(k,v)
|
||||
end,
|
||||
})
|
||||
-- Step 3 rewrite the thread methods to use lindas
|
||||
local THREAD={}
|
||||
function THREAD.set(name,val)
|
||||
__GlobalLinda:set(name,val)
|
||||
end
|
||||
function THREAD.get(name)
|
||||
__GlobalLinda:get(name)
|
||||
end
|
||||
function THREAD.waitFor(name)
|
||||
--
|
||||
end
|
||||
function THREAD.testFor(name,val,sym)
|
||||
--
|
||||
end
|
||||
function THREAD.getCores()
|
||||
return THREAD.__CORES
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
THREAD.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
THREAD.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
-- Step 4 change the coroutine threading methods to work the same, but with lanes TODO when the lanes scheduler is ready!
|
||||
function THREAD.skip(n)
|
||||
-- Do Nothing
|
||||
end
|
||||
function THREAD.kill() -- trigger the lane destruction
|
||||
-- coroutine.yield({"_kill_",":)"})
|
||||
end
|
||||
--[[ Step 5 We need to get sleeping working so we need a lane to handle timing... We want idle wait not busy wait
|
||||
Idle wait keeps the CPU running better where busy wait wastes CPU cycles... Lanes does not have a sleep method
|
||||
however, a linda recieve will in fact be a idle wait! So when wait is called we can pack the cmd up and send it to
|
||||
the sleeping thread manager to send the variable for the other thread to consume, sending only after a certain time has passed!
|
||||
]]
|
||||
local function randomString(n)
|
||||
local str = ''
|
||||
local strings = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','1','2','3','4','5','6','7','8','9','0','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'}
|
||||
for i=1,n do
|
||||
str = str..''..strings[math.random(1,#strings)]
|
||||
end
|
||||
return str
|
||||
end
|
||||
function THREAD.sleep(n)
|
||||
math.randomseed(os.time())
|
||||
local randKey=randomString(12) -- generate a random string a-Z and 0-9
|
||||
__SleepingLinda:send("tired","SLEEP|"..randKey.."|"..tostring(n)) -- send the data that needs to be managed
|
||||
local dat=__SleepingLinda:receive(randKey)
|
||||
return dat
|
||||
end
|
||||
function THREAD.hold(n)
|
||||
while not(n()) do
|
||||
-- holding
|
||||
end
|
||||
end
|
||||
-- start the time manager lane
|
||||
--~ lanes.gen("*", function()
|
||||
--~ local timers={}
|
||||
--~ while true do -- forever loop!
|
||||
--~ local data=__SleepingLinda:receive(.001,"tired") -- timeout after .001 seconds and handle the other stuff
|
||||
--~ if data then -- the .001 is an entarnal timer that keeps this thread from using too much CPU as well!
|
||||
--~ print(data)
|
||||
--~ local cmd,key,sec=data:match("(%S-)|(%S-)|(.+)")
|
||||
--~ if cmd=="SLEEP" then
|
||||
--~ print("GOT!")
|
||||
--~ timers[#timers+1]={os.clock()+tonumber(sec),key}
|
||||
--~ --__SleepingLinda:set()
|
||||
--~ elseif cmd=="audit" then
|
||||
--~ --
|
||||
--~ end
|
||||
--~ end
|
||||
--~ for i=1,#timers do
|
||||
--~ if os.clock()>=timers[i][1] then
|
||||
--~ __SleepingLinda:send(timers[i][2],true)
|
||||
--~ table.remove(timers,i)
|
||||
--~ end
|
||||
--~ end
|
||||
--~ end
|
||||
--~ end)() -- The global timer is now activated, and it works great!
|
||||
-- Step 6 Basic Threads!
|
||||
function multi:newSystemThread(name,func)
|
||||
local c={}
|
||||
local __self=c
|
||||
c.name=name
|
||||
c.thread=lanes.gen("*", func)()
|
||||
function c:kill()
|
||||
self.status:Destroy()
|
||||
self.thread:cancel()
|
||||
print("Thread: '"..self.name.."' has been stopped!")
|
||||
end
|
||||
c.status=multi:newUpdater(multi.Priority_IDLE)
|
||||
c.status.link=c
|
||||
c.status:OnUpdate(function(self)
|
||||
local v,err,t=self.link.thread:join(.001)
|
||||
if err then
|
||||
print("Error in thread: '"..self.link.name.."' <"..err..">",t)
|
||||
self:Destroy()
|
||||
end
|
||||
end)
|
||||
return c
|
||||
end
|
||||
_G["GLOBAL"]=GLOBAL
|
||||
_G["__GlobalLinda"]=__GlobalLinda
|
||||
@ -1,26 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newLoop(func)
|
||||
local c=self:newBase()
|
||||
c.Type='loop'
|
||||
c.Start=self.clock()
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.Parent.clock()-self.Start,self)
|
||||
end
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,77 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newStep(start,reset,count,skip)
|
||||
local c=self:newBase()
|
||||
think=1
|
||||
c.Type='step'
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.funcS={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end
|
||||
c.Reset=c.Resume
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,4 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newTask(func)
|
||||
table.insert(self.Tasks,func)
|
||||
end
|
||||
@ -1,158 +0,0 @@
|
||||
require("multi.updater")
|
||||
thread={}
|
||||
multi.GlobalVariables={}
|
||||
if os.getOS()=="windows" then
|
||||
thread.__CORES=tonumber(os.getenv("NUMBER_OF_PROCESSORS"))
|
||||
else
|
||||
thread.__CORES=tonumber(io.popen("nproc --all"):read("*n"))
|
||||
end
|
||||
function thread.sleep(n)
|
||||
coroutine.yield({"_sleep_",n})
|
||||
end
|
||||
function thread.hold(n)
|
||||
coroutine.yield({"_hold_",n})
|
||||
end
|
||||
function thread.skip(n)
|
||||
coroutine.yield({"_skip_",n})
|
||||
end
|
||||
function thread.kill()
|
||||
coroutine.yield({"_kill_",":)"})
|
||||
end
|
||||
function thread.yeild()
|
||||
coroutine.yield({"_sleep_",0})
|
||||
end
|
||||
function thread.getCores()
|
||||
return thread.__CORES
|
||||
end
|
||||
function thread.set(name,val)
|
||||
multi.GlobalVariables[name]=val
|
||||
return true
|
||||
end
|
||||
function thread.get(name)
|
||||
return multi.GlobalVariables[name]
|
||||
end
|
||||
function thread.waitFor(name)
|
||||
thread.hold(function() return thread.get(name)~=nil end)
|
||||
return thread.get(name)
|
||||
end
|
||||
function thread.testFor(name,val,sym)
|
||||
thread.hold(function() return thread.get(name)~=nil end)
|
||||
return thread.get(name)
|
||||
end
|
||||
function multi:newTBase(ins)
|
||||
local c = {}
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.ender={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Parent=self
|
||||
c.held=false
|
||||
return c
|
||||
end
|
||||
function multi:newThread(name,func)
|
||||
local c={}
|
||||
c.ref={}
|
||||
c.Name=name
|
||||
c.thread=coroutine.create(func)
|
||||
c.sleep=1
|
||||
c.firstRunDone=false
|
||||
c.timer=multi.scheduler:newTimer()
|
||||
c.ref.Globals=self:linkDomain("Globals")
|
||||
function c.ref:send(name,val)
|
||||
ret=coroutine.yield({Name=name,Value=val})
|
||||
self:syncGlobals(ret)
|
||||
end
|
||||
function c.ref:get(name)
|
||||
return self.Globals[name]
|
||||
end
|
||||
function c.ref:kill()
|
||||
err=coroutine.yield({"_kill_"})
|
||||
if err then
|
||||
error("Failed to kill a thread! Exiting...")
|
||||
end
|
||||
end
|
||||
function c.ref:sleep(n)
|
||||
if type(n)=="function" then
|
||||
ret=coroutine.yield({"_hold_",n})
|
||||
self:syncGlobals(ret)
|
||||
elseif type(n)=="number" then
|
||||
n = tonumber(n) or 0
|
||||
ret=coroutine.yield({"_sleep_",n})
|
||||
self:syncGlobals(ret)
|
||||
else
|
||||
error("Invalid Type for sleep!")
|
||||
end
|
||||
end
|
||||
function c.ref:syncGlobals(v)
|
||||
self.Globals=v
|
||||
end
|
||||
table.insert(self:linkDomain("Threads"),c)
|
||||
if not multi.scheduler:isActive() then
|
||||
multi.scheduler:Resume()
|
||||
end
|
||||
end
|
||||
multi:setDomainName("Threads")
|
||||
multi:setDomainName("Globals")
|
||||
multi.scheduler=multi:newUpdater()
|
||||
multi.scheduler.Type="scheduler"
|
||||
function multi.scheduler:setStep(n)
|
||||
self.skip=tonumber(n) or 24
|
||||
end
|
||||
multi.scheduler.skip=0
|
||||
multi.scheduler.counter=0
|
||||
multi.scheduler.Threads=multi:linkDomain("Threads")
|
||||
multi.scheduler.Globals=multi:linkDomain("Globals")
|
||||
multi.scheduler:OnUpdate(function(self)
|
||||
self.counter=self.counter+1
|
||||
for i=#self.Threads,1,-1 do
|
||||
ret={}
|
||||
if coroutine.status(self.Threads[i].thread)=="dead" then
|
||||
table.remove(self.Threads,i)
|
||||
else
|
||||
if self.Threads[i].timer:Get()>=self.Threads[i].sleep then
|
||||
if self.Threads[i].firstRunDone==false then
|
||||
self.Threads[i].firstRunDone=true
|
||||
self.Threads[i].timer:Start()
|
||||
_,ret=coroutine.resume(self.Threads[i].thread,self.Threads[i].ref)
|
||||
else
|
||||
_,ret=coroutine.resume(self.Threads[i].thread,self.Globals)
|
||||
end
|
||||
if ret==true or ret==false then
|
||||
print("Thread Ended!!!")
|
||||
ret={}
|
||||
end
|
||||
end
|
||||
if ret then
|
||||
if ret[1]=="_kill_" then
|
||||
table.remove(self.Threads,i)
|
||||
elseif ret[1]=="_sleep_" then
|
||||
self.Threads[i].timer:Reset()
|
||||
self.Threads[i].sleep=ret[2]
|
||||
elseif ret[1]=="_skip_" then
|
||||
self.Threads[i].timer:Reset()
|
||||
self.Threads[i].sleep=math.huge
|
||||
local event=multi:newEvent(function(evnt) return multi.scheduler.counter>=evnt.counter end)
|
||||
event.link=self.Threads[i]
|
||||
event.counter=self.counter+ret[2]
|
||||
event:OnEvent(function(evnt)
|
||||
evnt.link.sleep=0
|
||||
end)
|
||||
elseif ret[1]=="_hold_" then
|
||||
self.Threads[i].timer:Reset()
|
||||
self.Threads[i].sleep=math.huge
|
||||
local event=multi:newEvent(ret[2])
|
||||
event.link=self.Threads[i]
|
||||
event:OnEvent(function(evnt)
|
||||
evnt.link.sleep=0
|
||||
end)
|
||||
elseif ret.Name then
|
||||
self.Globals[ret.Name]=ret.Value
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
multi.scheduler:setStep()
|
||||
multi.scheduler:Pause()
|
||||
multi.OnError=multi:newConnection()
|
||||
@ -1,50 +0,0 @@
|
||||
require("multi.threading")
|
||||
function multi:newThreadedAlarm(name,set)
|
||||
local c=self:newTBase()
|
||||
c.Type='alarmThread'
|
||||
c.timer=self:newTimer()
|
||||
c.set=set or 0
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.set)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
self.timer:Resume()
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.rest=false
|
||||
self.timer:Reset(n)
|
||||
end
|
||||
function c:OnRing(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:Pause()
|
||||
self.timer:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=multi.Priority_Low -- skips
|
||||
c.restRate=0 -- secs
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
if c.timer:Get()>=c.set then
|
||||
c:Pause()
|
||||
for i=1,#c.func do
|
||||
c.func[i](c)
|
||||
end
|
||||
end
|
||||
thread.skip(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,7 +0,0 @@
|
||||
require("multi.threading.step")
|
||||
require("multi.threading.tstep")
|
||||
require("multi.threading.updater")
|
||||
require("multi.threading.alarm")
|
||||
require("multi.threading.loop")
|
||||
require("multi.threading.event")
|
||||
require("multi.threading.process")
|
||||
@ -1,43 +0,0 @@
|
||||
require("multi.threading")
|
||||
function multi:newThreadedEvent(name,task)
|
||||
local c=self:newTBase()
|
||||
c.Type='eventThread'
|
||||
c.Task=task or function() end
|
||||
function c:OnEvent(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.Task)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=1
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
ref:sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
if c.Task(self) then
|
||||
for _E=1,#c.func do
|
||||
c.func[_E](c)
|
||||
end
|
||||
c:Pause()
|
||||
end
|
||||
ref:sleep(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,42 +0,0 @@
|
||||
require("multi.threading")
|
||||
function multi:newThreadedLoop(name,func)
|
||||
local c=self:newTBase()
|
||||
c.Type='loopThread'
|
||||
c.Start=os.clock()
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=.75
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
for i=1,#c.func do
|
||||
c.func[i](os.clock()-self.Start,c)
|
||||
end
|
||||
thread.sleep(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,83 +0,0 @@
|
||||
require("multi.threading")
|
||||
function multi:newThreadedProcess(name)
|
||||
local c = {}
|
||||
setmetatable(c, multi)
|
||||
function c:newBase(ins)
|
||||
local ct = {}
|
||||
setmetatable(ct, self.Parent)
|
||||
ct.Active=true
|
||||
ct.func={}
|
||||
ct.ender={}
|
||||
ct.Id=0
|
||||
ct.PId=0
|
||||
ct.Act=function() end
|
||||
ct.Parent=self
|
||||
ct.held=false
|
||||
ct.ref=self.ref
|
||||
table.insert(self.Mainloop,ct)
|
||||
return ct
|
||||
end
|
||||
c.Parent=self
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.Type='process'
|
||||
c.Mainloop={}
|
||||
c.Tasks={}
|
||||
c.Tasks2={}
|
||||
c.Garbage={}
|
||||
c.Children={}
|
||||
c.Paused={}
|
||||
c.Active=true
|
||||
c.Id=-1
|
||||
c.Rest=0
|
||||
c.updaterate=.01
|
||||
c.restRate=.1
|
||||
c.Jobs={}
|
||||
c.queue={}
|
||||
c.jobUS=2
|
||||
c.rest=false
|
||||
function c:getController()
|
||||
return nil
|
||||
end
|
||||
function c:Start()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:Remove()
|
||||
self.ref:kill()
|
||||
end
|
||||
function c:kill()
|
||||
err=coroutine.yield({"_kill_"})
|
||||
if err then
|
||||
error("Failed to kill a thread! Exiting...")
|
||||
end
|
||||
end
|
||||
function c:sleep(n)
|
||||
if type(n)=="function" then
|
||||
ret=coroutine.yield({"_hold_",n})
|
||||
elseif type(n)=="number" then
|
||||
n = tonumber(n) or 0
|
||||
ret=coroutine.yield({"_sleep_",n})
|
||||
else
|
||||
error("Invalid Type for sleep!")
|
||||
end
|
||||
end
|
||||
c.hold=c.sleep
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
ref:Sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
c:uManager()
|
||||
ref:sleep(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
return c
|
||||
end
|
||||
@ -1,92 +0,0 @@
|
||||
require("multi.threading")
|
||||
function multi:newThreadedStep(name,start,reset,count,skip)
|
||||
local c=self:newTBase()
|
||||
local think=1
|
||||
c.Type='stepThread'
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.funcS={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.Reset=c.Resume
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.rest=true
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
c.updaterate=0
|
||||
c.restRate=.1
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
ref:sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
if c~=nil then
|
||||
if c.spos==0 then
|
||||
if c.pos==c.start then
|
||||
for fe=1,#c.funcS do
|
||||
c.funcS[fe](c)
|
||||
end
|
||||
end
|
||||
for i=1,#c.func do
|
||||
c.func[i](c.pos,c)
|
||||
end
|
||||
c.pos=c.pos+c.count
|
||||
if c.pos-c.count==c.endAt then
|
||||
c:Pause()
|
||||
for fe=1,#c.funcE do
|
||||
c.funcE[fe](c)
|
||||
end
|
||||
c.pos=c.start
|
||||
end
|
||||
end
|
||||
end
|
||||
c.spos=c.spos+1
|
||||
if c.spos>=c.skip then
|
||||
c.spos=0
|
||||
end
|
||||
ref:sleep(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,42 +0,0 @@
|
||||
require("multi.threading")
|
||||
function multi:newThreadedTLoop(name,func,n)
|
||||
local c=self:newTBase()
|
||||
c.Type='tloopThread'
|
||||
c.restN=n or 1
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=.75
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
for i=1,#c.func do
|
||||
c.func[i](c)
|
||||
end
|
||||
thread.sleep(c.restN) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,91 +0,0 @@
|
||||
require("multi.threading")
|
||||
function multi:newThreadedTStep(name,start,reset,count,set)
|
||||
local c=self:newTBase()
|
||||
local think=1
|
||||
c.Type='tstepThread'
|
||||
c.Priority=self.Priority_Low
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=os.clock()
|
||||
c.set=set or 1
|
||||
c.funcS={}
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
c.updaterate=0--multi.Priority_Low -- skips
|
||||
c.restRate=0
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
if os.clock()-c.timer>=c.set then
|
||||
c:Reset()
|
||||
if c.pos==c.start then
|
||||
for fe=1,#c.funcS do
|
||||
c.funcS[fe](c)
|
||||
end
|
||||
end
|
||||
for i=1,#c.func do
|
||||
c.func[i](c.pos,c)
|
||||
end
|
||||
c.pos=c.pos+c.count
|
||||
if c.pos-c.count==c.endAt then
|
||||
c:Pause()
|
||||
for fe=1,#c.funcE do
|
||||
c.funcE[fe](c)
|
||||
end
|
||||
c.pos=c.start
|
||||
end
|
||||
end
|
||||
thread.skip(c.updaterate) -- lets rest a bit
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,32 +0,0 @@
|
||||
require("multi.threading")
|
||||
function multi:newThreadedUpdater(name,skip)
|
||||
local c=self:newTBase()
|
||||
c.Type='updaterThread'
|
||||
c.pos=1
|
||||
c.skip=skip or 1
|
||||
function c:Resume()
|
||||
self.rest=false
|
||||
end
|
||||
function c:Pause()
|
||||
self.rest=true
|
||||
end
|
||||
c.OnUpdate=self.OnMainConnect
|
||||
c.rest=false
|
||||
c.updaterate=0
|
||||
c.restRate=.75
|
||||
multi:newThread(name,function(ref)
|
||||
while true do
|
||||
if c.rest then
|
||||
thread.sleep(c.restRate) -- rest a bit more when a thread is paused
|
||||
else
|
||||
for i=1,#c.func do
|
||||
c.func[i](c)
|
||||
end
|
||||
c.pos=c.pos+1
|
||||
thread.skip(c.skip)
|
||||
end
|
||||
end
|
||||
end)
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,40 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newTLoop(func,set)
|
||||
local c=self:newBase()
|
||||
c.Type='tloop'
|
||||
c.set=set or 0
|
||||
c.timer=self:newTimer()
|
||||
c.life=0
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
if self.timer:Get()>=self.set then
|
||||
self.life=self.life+1
|
||||
for i=1,#self.func do
|
||||
self.func[i](self,self.life)
|
||||
end
|
||||
self.timer:Reset()
|
||||
end
|
||||
end
|
||||
function c:Resume()
|
||||
self.Parent.Resume(self)
|
||||
self.timer:Resume()
|
||||
end
|
||||
function c:Pause()
|
||||
self.timer:Pause()
|
||||
self.Parent.Pause(self)
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,17 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newTrigger(func)
|
||||
local c={}
|
||||
c.Type='trigger'
|
||||
c.trigfunc=func or function() end
|
||||
function c:Fire(...)
|
||||
self:trigfunc(...)
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.trigfunc)
|
||||
m:tofile(path)
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,76 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newTStep(start,reset,count,set)
|
||||
local c=self:newBase()
|
||||
think=1
|
||||
c.Type='tstep'
|
||||
c.Priority=self.Priority_Low
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=self.clock()
|
||||
c.set=set or 1
|
||||
c.funcS={}
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=self.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
if self.clock()-self.timer>=self.set then
|
||||
self:Reset()
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=self.clock()
|
||||
self:Resume()
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,22 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newUpdater(skip)
|
||||
local c=self:newBase()
|
||||
c.Type='updater'
|
||||
c.pos=1
|
||||
c.skip=skip or 1
|
||||
function c:Act()
|
||||
if self.pos>=self.skip then
|
||||
self.pos=0
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end
|
||||
self.pos=self.pos+1
|
||||
end
|
||||
function c:setSkip(n)
|
||||
self.skip=n
|
||||
end
|
||||
c.OnUpdate=self.OnMainConnect
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
@ -1,34 +0,0 @@
|
||||
require("multi")
|
||||
function multi:newWatcher(namespace,name)
|
||||
local function WatcherObj(ns,n)
|
||||
if self.Type=='queue' then
|
||||
print("Cannot create a watcher on a queue! Creating on 'multi' instead!")
|
||||
self=multi
|
||||
end
|
||||
local c=self:newBase()
|
||||
c.Type='watcher'
|
||||
c.ns=ns
|
||||
c.n=n
|
||||
c.cv=ns[n]
|
||||
function c:OnValueChanged(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:Act()
|
||||
if self.cv~=self.ns[self.n] then
|
||||
for i=1,#self.func do
|
||||
self.func[i](self,self.cv,self.ns[self.n])
|
||||
end
|
||||
self.cv=self.ns[self.n]
|
||||
end
|
||||
end
|
||||
self:create(c)
|
||||
return c
|
||||
end
|
||||
if type(namespace)~='table' and type(namespace)=='string' then
|
||||
return WatcherObj(_G,namespace)
|
||||
elseif type(namespace)=='table' and (type(name)=='string' or 'number') then
|
||||
return WatcherObj(namespace,name)
|
||||
else
|
||||
print('Warning, invalid arguments! Nothing returned!')
|
||||
end
|
||||
end
|
||||
511
oldversions/BasicCommands.lua
Normal file
511
oldversions/BasicCommands.lua
Normal file
@ -0,0 +1,511 @@
|
||||
--Needs:SystemType.bat
|
||||
function RandomString(num)
|
||||
string = ""
|
||||
strings = {"a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","1","2","3","4","5","6","7","8","9","0","A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"}
|
||||
for i=1,num do
|
||||
h = math.random(1,#strings)
|
||||
string = string..""..strings[h]
|
||||
end
|
||||
return string
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function GetSystemType()
|
||||
return BatCmd([[
|
||||
for /f "skip=1 delims=" %%x in ('wmic cpu get addresswidth') do if not defined AddressWidth set AddressWidth=%%x
|
||||
|
||||
if %AddressWidth%==64 (
|
||||
exit /b 64
|
||||
) else (
|
||||
exit /b 32
|
||||
)
|
||||
]])
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
local clock = os.clock
|
||||
function sleep(n) -- seconds
|
||||
if not n then n=0 end
|
||||
local t0 = clock()
|
||||
while clock() - t0 <= n do end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function SetCounter()
|
||||
return os.clock()
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function GetCounter(count)
|
||||
if count~=nil then
|
||||
return os.clock()-count
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
-- sleep and wait script for n seconds
|
||||
function wait(n)
|
||||
sleep(n)
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
-- pause into any key pressed
|
||||
function pause(msg)
|
||||
if msg ~= nil then
|
||||
print(msg)
|
||||
end
|
||||
io.read()
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
-- gets input
|
||||
function getInput(msg)
|
||||
if msg ~= nil then
|
||||
io.write(msg)
|
||||
end
|
||||
return io.read()
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
-- Print contents of `tbl`, with indentation.
|
||||
-- `indent` sets the initial level of indentation.
|
||||
function print(...)
|
||||
arg={...}
|
||||
if arg[1]==nil then
|
||||
return nil
|
||||
end
|
||||
for i,v in ipairs(arg) do
|
||||
function tprint(tbl, indent)
|
||||
if not indent then indent = 0 end
|
||||
for k, v in pairs(tbl) do
|
||||
formatting = string.rep(" ", indent) .. k .. ": "
|
||||
if type(v) == "table" then
|
||||
print(formatting)
|
||||
tprint(v, indent+1)
|
||||
else
|
||||
print(formatting .. tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
if type(v)=="string" then
|
||||
io.write(v..[[
|
||||
|
||||
]])
|
||||
elseif type(v)=="table" then
|
||||
function test(v)
|
||||
return v:tostring()
|
||||
end
|
||||
if v.tostring ~=nil then
|
||||
print(test(v))
|
||||
else
|
||||
tprint(v)
|
||||
end
|
||||
elseif type(v)=="number" then
|
||||
io.write(tostring(v..[[
|
||||
|
||||
]]))
|
||||
elseif type(v)=="boolean" then
|
||||
if v then
|
||||
io.write("true"..[[
|
||||
|
||||
]])
|
||||
else
|
||||
io.write("false"..[[
|
||||
|
||||
]])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
math.randomseed(clock())
|
||||
----------------------------------------------------------------------------------------------------
|
||||
-- gets the length of a table or a string or the number of digits in a number including the decimal
|
||||
function len(T)
|
||||
if type(T)=="table" then
|
||||
local count = 0
|
||||
for _ in pairs(T) do count = count + 1 end
|
||||
return count
|
||||
elseif type(T)=="string" then
|
||||
return string.len(T)
|
||||
elseif type(T)=="number" then
|
||||
return string.len(tostring(T))
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function dump(t,indent)
|
||||
local names = {}
|
||||
if not indent then indent = "" end
|
||||
for n,g in pairs(t) do
|
||||
table.insert(names,n)
|
||||
end
|
||||
table.sort(names)
|
||||
for i,n in pairs(names) do
|
||||
local v = t[n]
|
||||
if type(v) == "table" then
|
||||
if(v==t) then -- prevent endless loop if table contains reference to itself
|
||||
print(indent..tostring(n)..": <-")
|
||||
else
|
||||
print(indent..tostring(n)..":")
|
||||
dump(v,indent.." ")
|
||||
end
|
||||
else
|
||||
if type(v) == "function" then
|
||||
print(indent..tostring(n).."()")
|
||||
else
|
||||
print(indent..tostring(n)..": "..tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function BuildFromTree(tbl, indent,folder)
|
||||
if not indent then indent = 0 end
|
||||
if not folder then folder = "" end
|
||||
for k, v in pairs(tbl) do
|
||||
formatting = string.rep(" ", indent) .. k .. ":"
|
||||
if type(v) == "table" then
|
||||
--print(formatting)
|
||||
mkdir(folder..string.sub(formatting,1,-2))
|
||||
BuildFromTree(v,0,folder..string.sub(formatting,1,-2).."\\")
|
||||
print(v,0,folder..string.sub(formatting,1,-2).."\\")
|
||||
else
|
||||
a=string.find(tostring(v),":",1,true)
|
||||
file=string.sub(tostring(v),1,a-1)
|
||||
data=string.sub(tostring(v),a+1)
|
||||
mkfile(folder..file,data,"w")
|
||||
print(folder..tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function CopyFile(path,topath)
|
||||
print("Copy "..path.." "..topath)
|
||||
os.execute("Copy "..path.." "..topath)
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function DeleteFile(path)
|
||||
os.remove(path)
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function mkdir(dirname)
|
||||
os.execute("mkdir \"" .. dirname.."\"")
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function mkfile(filename,data,tp)
|
||||
if not(tp) then tp="w" end
|
||||
if not(data) then data="" end
|
||||
file = io.open(filename, tp)
|
||||
file:write(data)
|
||||
file:close()
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function MoveFile(path,topath)
|
||||
CopyFile(path,topath)
|
||||
DeleteFile(path)
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function List_Files(dir)
|
||||
if not(dir) then dir="" end
|
||||
local f = io.popen("dir \""..dir.."\"")
|
||||
if f then
|
||||
return f:read("*a")
|
||||
else
|
||||
print("failed to read")
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function StringLineToTable(s)
|
||||
local t = {} -- table to store the indices
|
||||
local i = 0
|
||||
while true do
|
||||
i = string.find(s, "\n", i+1) -- find 'next' newline
|
||||
if i == nil then return t end
|
||||
table.insert(t, i)
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function GetDirectory(dir,flop)
|
||||
s=List_Files(dir)
|
||||
drive=string.sub(string.match(s,"drive.."),-1)
|
||||
local t = {} -- table to store the indices
|
||||
local i = 0
|
||||
while true do
|
||||
i = string.find(s, "\n", i+1) -- find 'next' newline
|
||||
if i == nil then
|
||||
a,b=string.find(s,drive..":\\",1,true)
|
||||
main = string.gsub(string.sub(s,a,t[4]), "\n", "")
|
||||
if flop then
|
||||
main=main:gsub("%\\", "/")
|
||||
end
|
||||
return main
|
||||
end
|
||||
table.insert(t, i)
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function lines(str)
|
||||
local t = {}
|
||||
local function helper(line) table.insert(t, line) return "" end
|
||||
helper((str:gsub("(.-)\r?\n", helper)))
|
||||
return t
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function File_Exist(path)
|
||||
g=io.open(path or '','r')
|
||||
if path =="" then
|
||||
p="empty path"
|
||||
return nil
|
||||
end
|
||||
if g~=nil and true or false then
|
||||
p=(g~=nil and true or false)
|
||||
end
|
||||
--p=(g~=nil and true or false..(path=='' and 'empty path entered!' or (path or 'arg "path" wasn\'t define to function call!')))
|
||||
if g~=nil then
|
||||
io.close(g)
|
||||
else
|
||||
return false
|
||||
end
|
||||
return p
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function file_check(file_name)
|
||||
if not file_name then print("No path inputed") return false end
|
||||
local file_found=io.open(file_name, "r")
|
||||
if file_found==nil then
|
||||
file_found=false
|
||||
else
|
||||
file_found=true
|
||||
end
|
||||
return file_found
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function Dir_Exist(strFolderName)
|
||||
local fileHandle, strError = io.open(strFolderName.."\\*.*","r")
|
||||
if fileHandle ~= nil then
|
||||
io.close(fileHandle)
|
||||
return true
|
||||
else
|
||||
if string.match(strError,"No such file or directory") then
|
||||
return false
|
||||
else
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function ListItems(dir)
|
||||
if Dir_Exist(dir) then
|
||||
temp=List_Files(dir) -- current directory if blank
|
||||
if GetDirectory(dir)=="C:\\\n" then
|
||||
a,b=string.find(temp,"C:\\",1,true)
|
||||
a=a+2
|
||||
else
|
||||
a,b=string.find(temp,"..",1,true)
|
||||
end
|
||||
temp=string.sub(temp,a+2)
|
||||
list=StringLineToTable(temp)
|
||||
temp=string.sub(temp,1,list[#list-2])
|
||||
slist=lines(temp)
|
||||
table.remove(slist,1)
|
||||
table.remove(slist,#slist)
|
||||
temp={}
|
||||
temp2={}
|
||||
for i=1,#slist do
|
||||
table.insert(temp,string.sub(slist[i],40,-1))
|
||||
end
|
||||
return temp
|
||||
else
|
||||
print("Directory does not exist")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function GetDirectories(dir)
|
||||
temp2={}
|
||||
dirs=ListItems(dir)
|
||||
for i=1,#dirs do
|
||||
if Dir_Exist(string.gsub(GetDirectory(dir).."\\"..dirs[i], "\n", "")) then
|
||||
table.insert(temp2,dirs[i])
|
||||
end
|
||||
end
|
||||
return temp2
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function GetFiles(dir)
|
||||
temp2={}
|
||||
dirs=ListItems(dir)
|
||||
for i=1,#dirs do
|
||||
if Dir_Exist(string.gsub(GetDirectory(dir).."\\"..dirs[i], "\n", "")) then
|
||||
else
|
||||
table.insert(temp2,dirs[i])
|
||||
end
|
||||
end
|
||||
return temp2
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function GetName(name)
|
||||
if name then temp=name else temp=arg[0] end
|
||||
if string.find(temp,"\\",1,true) then
|
||||
temp=string.reverse(temp)
|
||||
a,b=string.find(temp,"\\",1,true)
|
||||
return string.reverse(string.sub(temp,1,b-1))
|
||||
end
|
||||
return arg[0]
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
BuildFromTreeE = function(tbl, indent,folder)
|
||||
if not indent then indent = 0 end
|
||||
if not folder then folder = "" end
|
||||
for k, v in pairs(tbl) do
|
||||
formatting = string.rep(" ", indent) .. k .. ":"
|
||||
if type(v) == "table" then
|
||||
--print(formatting)
|
||||
mkdir(folder..string.sub(formatting,1,-2))
|
||||
BuildFromTreeE(v,0,folder..string.sub(formatting,1,-2).."\\")
|
||||
print(v,0,folder..string.sub(formatting,1,-2).."\\")
|
||||
else
|
||||
a=string.find(tostring(v),":",1,true)
|
||||
file=string.sub(tostring(v),1,a-1)
|
||||
data=string.sub(tostring(v),a+1)
|
||||
mkfile(folder..file,SuperEncode(data),"w")
|
||||
print(folder..tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
readFile = function(file)
|
||||
local f = io.open(file, "rb")
|
||||
local content = f:read("*all")
|
||||
f:close()
|
||||
return content
|
||||
end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
readFileE = function(file)
|
||||
local f = io.open(file, "rb")
|
||||
local content = f:read("*all")
|
||||
f:close()
|
||||
return SuperDecode(content)
|
||||
end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
mkfileE = function(filename,data,tp)
|
||||
if not(tp) then tp="w" end
|
||||
if not(data) then data="" end
|
||||
file = io.open(filename, tp)
|
||||
file:write(SuperEncode(data))
|
||||
file:close()
|
||||
end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
math.randomseed(clock())
|
||||
------------------------------------------------------------------------------------------------------
|
||||
function extension(file)
|
||||
file=GetName(GetDirectory(file).."/"..file)
|
||||
a,b=string.find(file,".",0,true)
|
||||
return string.sub(file,b)
|
||||
end
|
||||
function InstallLua()
|
||||
os.execute("Data\\LuaForWindows.exe")
|
||||
end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
function autoCmd(cmd,a,b)
|
||||
if b==nil then b=true end
|
||||
if File_Exist("tempfile.ahk") and not(File_Exist("tempfile2.ahk")) then fname="tempfile2.ahk" elseif File_Exist("tempfile2.ahk") and not(File_Exist("tempfile.ahk")) then fname="tempfile.ahk" else fname=RandomString(10)..".ahk" end
|
||||
mkfile(fname,[[
|
||||
#SingleInstance off
|
||||
ReturnLua(val)
|
||||
{
|
||||
FileAppend,%val%", %A_WorkingDir%\File.dat
|
||||
Exitapp, 0
|
||||
}
|
||||
|
||||
]]..cmd)
|
||||
g=os.execute([[Data\Win32a\AutoHotkey.exe ]]..fname)
|
||||
if b==true then
|
||||
if not string.find(cmd,"ReturnLua(",1,true) then print("To Return use ReturnLua(value) (Note: values are returned as strings for booleans use '/bT' or '/bF' true/false, also \"\" are needed for string no other way works for now use \\\" to get quotes also for absolute render(does loadstring to the data don't assign a val name in the script do it to this function) call this function autoCmd(cmd,true) )") return false end
|
||||
--repeat wait() until File_Exist("File.dat") or g==nil
|
||||
if not(File_Exist("File.dat")) then
|
||||
print("An Error has occurred")
|
||||
return false
|
||||
end
|
||||
file = io.open("File.dat", "r")
|
||||
size = file:read() -- capture file in a string
|
||||
file:close()
|
||||
Clean()
|
||||
if size==nil then
|
||||
return nil
|
||||
end
|
||||
if a then
|
||||
loadstring("temp="..string.sub(size,1,-2))()
|
||||
return temp
|
||||
end
|
||||
if string.sub(size,1,-2)=="/bT" then
|
||||
return true
|
||||
elseif string.sub(size,1,-2)=="/bF" then
|
||||
return false
|
||||
end
|
||||
return string.sub(size,1,-2)
|
||||
else
|
||||
return g
|
||||
end
|
||||
end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
function BatCmd(cmd)
|
||||
mkfile("temp.bat",cmd)
|
||||
return os.execute([[temp.bat]])
|
||||
end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
inifile = {}
|
||||
|
||||
local lines
|
||||
local write
|
||||
|
||||
if love then
|
||||
lines = love.filesystem.lines
|
||||
write = love.filesystem.write
|
||||
else
|
||||
lines = function(name) return assert(io.open(name)):lines() end
|
||||
write = function(name, contents) return assert(io.open(name, "w")):write(contents) end
|
||||
end
|
||||
|
||||
function inifile.parse(name)
|
||||
local t = {}
|
||||
local section
|
||||
for line in lines(name) do
|
||||
local s = line:match("^%[([^%]]+)%]$")
|
||||
if s then
|
||||
section = s
|
||||
t[section] = t[section] or {}
|
||||
end
|
||||
local key, value = line:match("^(%w+)%s-=%s-(.+)$")
|
||||
if key and value then
|
||||
if tonumber(value) then value = tonumber(value) end
|
||||
if value == "true" then value = true end
|
||||
if value == "false" then value = false end
|
||||
t[section][key] = value
|
||||
end
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function inifile.save(name, t)
|
||||
local contents = ""
|
||||
for section, s in pairs(t) do
|
||||
contents = contents .. ("[%s]\n"):format(section)
|
||||
for key, value in pairs(s) do
|
||||
contents = contents .. ("%s=%s\n"):format(key, tostring(value))
|
||||
end
|
||||
contents = contents .. "\n"
|
||||
end
|
||||
write(name, contents)
|
||||
end
|
||||
---------------
|
||||
function DB(txt)
|
||||
print(txt)
|
||||
end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
function CreateShortcut(Target, Name , Work, Args, Desc, Icon, Short, IconN, Run)
|
||||
if not(Target) or not(Name) then print("Error Target folder or file is needed and the name of the shortcut is needed") return false end
|
||||
if string.sub(Name,-4)~=".lnk" then Name=Name..".lnk" end
|
||||
if not(Work) then Work="," end
|
||||
if not(Args) then Args="," end
|
||||
if not(Desc) then Desc="," end
|
||||
if not(Icon) then Icon="," end
|
||||
if not(Short) then Short="," end
|
||||
if not(IconN) then IconN="," end
|
||||
if not(Run) then Run="" end
|
||||
autoCmd([[FileCreateShortcut, ]]..Target..[[, ]]..Name..[[ ]]..Work..[[ ]]..Args..[[ ]]..Desc..[[ ]]..Icon..[[ ]]..Short..[[ ]]..IconN..[[ ]]..Run)
|
||||
print("--shortcut created at "..Target.." with the name "..Name)
|
||||
end
|
||||
53
oldversions/EventManager(0.0.1).lua
Normal file
53
oldversions/EventManager(0.0.1).lua
Normal file
@ -0,0 +1,53 @@
|
||||
IsEvent=true
|
||||
event={
|
||||
Active=true,
|
||||
CT=false,
|
||||
tag={},
|
||||
Events={},
|
||||
EventTracker={},
|
||||
Steps={},
|
||||
setAlarm=function(self,tag,set) event:new("Alarm_"..tag.."(\""..tag.."\")",[[GetCounter(event:getTracker("_Alarm_]]..tag..[["))>=]]..set,[[event:removeAlarm("]]..tag..[[")]]) event:addTracker("_Alarm_"..tag,SetCounter()) end,
|
||||
setEvent=function(self,fname,condition,also) if not(string.find(fname,"(",1,true)) and not(string.find(fname,")",1,true)) then fname=fname.."()" end event:new("Event_"..fname,condition,also) end,
|
||||
removeAlarm=function(self,tag) event:destroy("Alarm_"..tag) event:removeTracker("_Alarm_"..tag) end,
|
||||
updateAlarm=function(self,tag,set) event:removeAlarm(tag) event:setAlarm(tag,set) end,
|
||||
addTracker=function(self,varname,var) event.EventTracker[varname]=var end,
|
||||
getTracker=function(self,var) return event.EventTracker[var] end,
|
||||
removeTracker=function(self,var) event.EventTracker[var]=nil end,
|
||||
trackerExist=function(self,tag) return event.EventTracker[tag]~=nil end,
|
||||
updateTracker=function(self,tag,val) if event:trackerExist(tag) then event.EventTracker[tag]=val end end,
|
||||
alarmExist=function(self,tag) return (event:getTracker("_Alarm_"..tag)~=nil) end,
|
||||
new=function(self,fname,condition,also) if not(also) then also="" end table.insert(self.Events,"if "..condition.." then "..also.." "..fname.." end") table.insert(event.tag,fname) end,
|
||||
eventExist=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then return true end end return false end,
|
||||
destroy=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then table.remove(event.tag,j) table.remove(event.Events,j) end end end,
|
||||
Stop=function() event.Active=false end,
|
||||
OnCreate=function() end,
|
||||
OnUpdate=function() end,
|
||||
OnClose=function() end,
|
||||
getActive=function() return event.tag end,
|
||||
Manager=function() event.OnCreate() while event.Active==true do for d=1,#event.Events do if event.Active==true then loadstring(event.Events[d])() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end event.OnClose() end,
|
||||
CManager=function() for d=1,#event.Events do if event.Active==true then loadstring(event.Events[d])() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end,
|
||||
UManager=function() if event.CT==false then event.CT=true event.OnCreate() end for d=1,#event.Events do if event.Active==true then loadstring(event.Events[d])() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end,
|
||||
createStep=function(self,tag,reset,endc)
|
||||
if not(endc) then endc=false end
|
||||
if not(reset) then reset=0 end
|
||||
temp={Name=tag,pos=1,endAt=reset,active=true,endc=endc,
|
||||
Step=function(self) if self~=nil then _G.__CStep__=self if self.active==true then loadstring("Step_"..tag.."("..self.pos..",__CStep__)")() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then loadstring("Step_"..self.Name.."_End(__CStep__)")() end end end,
|
||||
Remove=function(self) for as=1,#event.Steps do if event.Steps[as].Name==self.Name then table.remove(event.Steps,as) end end end,
|
||||
Reset=function(self) self.pos=1 end,
|
||||
Set=function(self,amt) self.pos=amt end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Stop=function(self) self:Reset() self:Pause() end,
|
||||
Start=function(self) self:Resume() end,
|
||||
End=function(self) self.pos=self.EndAt self.endc=true end,
|
||||
}
|
||||
table.insert(event.Steps,temp) return temp end,
|
||||
stepExist=function(self,tag)
|
||||
for a_s=1,#event.Steps do
|
||||
if event.Steps[a_s].Name==tag then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
}
|
||||
55
oldversions/EventManager(0.9.0).lua
Normal file
55
oldversions/EventManager(0.9.0).lua
Normal file
@ -0,0 +1,55 @@
|
||||
IsEvent=true
|
||||
event={
|
||||
Active=true,
|
||||
CT=false,
|
||||
tag={},
|
||||
Events={},
|
||||
EventTracker={},
|
||||
Steps={},
|
||||
--addAlarm=function(self,tag) end,
|
||||
setAlarm=function(self,tag,set) event:new("Alarm_"..tag.."(\""..tag.."\")",[[GetCounter(event:getTracker("_Alarm_]]..tag..[["))>=]]..set,[[event:removeAlarm("]]..tag..[[")]]) event:addTracker("_Alarm_"..tag,SetCounter()) assert(loadstring("if Alarm_"..tag.."==nil then function Alarm_"..tag.."() print('No function \"Alarm_"..tag.."()\" exists make sure you created it') end end"))() end,
|
||||
setEvent=function(self,fname,condition,also) if not(string.find(fname,"(",1,true)) and not(string.find(fname,")",1,true)) then fname=fname.."()" end a=string.find(fname,"(",1,true) tempstr=string.sub(fname,1,a-1) event:new("Event_"..fname,condition,also) assert(loadstring("if Event_"..tempstr.."==nil then function Event_"..tempstr.."() print('No function \"Event_"..tempstr.."()\" exists make sure you created it') end end"))() end,
|
||||
removeAlarm=function(self,tag) event:destroy("Alarm_"..tag) event:removeTracker("_Alarm_"..tag) end,
|
||||
updateAlarm=function(self,tag,set) event:removeAlarm(tag) event:setAlarm(tag,set) end,
|
||||
addTracker=function(self,varname,var) event.EventTracker[varname]=var end,
|
||||
getTracker=function(self,var) return event.EventTracker[var] end,
|
||||
removeTracker=function(self,var) event.EventTracker[var]=nil end,
|
||||
trackerExist=function(self,tag) return event.EventTracker[tag]~=nil end,
|
||||
updateTracker=function(self,tag,val) if event:trackerExist(tag) then event.EventTracker[tag]=val end end,
|
||||
alarmExist=function(self,tag) return (event:getTracker("_Alarm_"..tag)~=nil) end,
|
||||
new=function(self,fname,condition,also) if not(also) then also="" end table.insert(self.Events,"if "..condition.." then "..also.." "..fname.." end") table.insert(event.tag,fname) end,
|
||||
eventExist=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then return true end end return false end,
|
||||
destroy=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then table.remove(event.tag,j) table.remove(event.Events,j) end end end,
|
||||
Stop=function() event.Active=false end,
|
||||
OnCreate=function() end,
|
||||
OnUpdate=function() end,
|
||||
OnClose=function() end,
|
||||
getActive=function() return event.tag end,
|
||||
Manager=function() event.OnCreate() while event.Active==true do for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end event.OnClose() end,
|
||||
CManager=function() for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end,
|
||||
UManager=function() if event.CT==false then event.CT=true event.OnCreate() end for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end,
|
||||
RManager=function() event.OnCreate() while event.Active==true do event.OnUpdate() for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end end event.OnClose() end,
|
||||
createStep=function(self,tag,reset,endc)
|
||||
if not(endc) then endc=false end
|
||||
if not(reset) then reset=0 end
|
||||
temp={Name=tag,pos=1,endAt=reset,active=true,endc=endc,
|
||||
Step=function(self) if self~=nil then _G.__CStep__=self if self.active==true then assert(loadstring("Step_"..tag.."("..self.pos..",__CStep__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("Step_"..self.Name.."_End(__CStep__)"))() end end end,
|
||||
Remove=function(self) for as=1,#event.Steps do if event.Steps[as].Name==self.Name then table.remove(event.Steps,as) end end end,
|
||||
Reset=function(self) self.pos=1 end,
|
||||
Set=function(self,amt) self.pos=amt end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Stop=function(self) self:Reset() self:Pause() end,
|
||||
Start=function(self) self:Resume() end,
|
||||
End=function(self) self.pos=self.EndAt self.endc=true end,
|
||||
}
|
||||
table.insert(event.Steps,temp) return temp end,
|
||||
stepExist=function(self,tag)
|
||||
for a_s=1,#event.Steps do
|
||||
if event.Steps[a_s].Name==tag then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
}
|
||||
55
oldversions/EventManager(1.0.0).lua
Normal file
55
oldversions/EventManager(1.0.0).lua
Normal file
@ -0,0 +1,55 @@
|
||||
IsEvent=true
|
||||
event={
|
||||
Active=true,
|
||||
CT=false,
|
||||
tag={},
|
||||
Events={},
|
||||
EventTracker={},
|
||||
Steps={},
|
||||
--addAlarm=function(self,tag) end,
|
||||
setAlarm=function(self,tag,set) event:new("Alarm_"..tag.."(\""..tag.."\")",[[GetCounter(event:getTracker("_Alarm_]]..tag..[["))>=]]..set,[[event:removeAlarm("]]..tag..[[")]]) event:addTracker("_Alarm_"..tag,SetCounter()) assert(loadstring("if Alarm_"..tag.."==nil then function Alarm_"..tag.."() print('No function \"Alarm_"..tag.."()\" exists make sure you created it') end end"))() end,
|
||||
setEvent=function(self,fname,condition,also) if not(string.find(fname,"(",1,true)) and not(string.find(fname,")",1,true)) then fname=fname.."()" end a=string.find(fname,"(",1,true) tempstr=string.sub(fname,1,a-1) event:new("Event_"..fname,condition,also) assert(loadstring("if Event_"..tempstr.."==nil then function Event_"..tempstr.."() print('No function \"Event_"..tempstr.."()\" exists make sure you created it') end end"))() end,
|
||||
removeAlarm=function(self,tag) event:destroy("Alarm_"..tag) event:removeTracker("_Alarm_"..tag) end,
|
||||
updateAlarm=function(self,tag,set) event:removeAlarm(tag) event:setAlarm(tag,set) end,
|
||||
addTracker=function(self,varname,var) event.EventTracker[varname]=var end,
|
||||
getTracker=function(self,var) return event.EventTracker[var] end,
|
||||
removeTracker=function(self,var) event.EventTracker[var]=nil end,
|
||||
trackerExist=function(self,tag) return event.EventTracker[tag]~=nil end,
|
||||
updateTracker=function(self,tag,val) if event:trackerExist(tag) then event.EventTracker[tag]=val end end,
|
||||
alarmExist=function(self,tag) return (event:getTracker("_Alarm_"..tag)~=nil) end,
|
||||
new=function(self,fname,condition,also) if not(also) then also="" end table.insert(self.Events,"if "..condition.." then "..also.." "..fname.." end") table.insert(event.tag,fname) end,
|
||||
eventExist=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then return true end end return false end,
|
||||
destroy=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then table.remove(event.tag,j) table.remove(event.Events,j) end end end,
|
||||
Stop=function() event.Active=false end,
|
||||
OnCreate=function() end,
|
||||
OnUpdate=function() end,
|
||||
OnClose=function() end,
|
||||
getActive=function() return event.tag end,
|
||||
Manager=function() event.OnCreate() while event.Active==true do for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end event.OnClose() end,
|
||||
CManager=function() for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end,
|
||||
UManager=function() if event.CT==false then event.CT=true event.OnCreate() end for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end,
|
||||
RManager=function() event.OnCreate() while event.Active==true do event.OnUpdate() for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end end event.OnClose() end,
|
||||
createStep=function(self,tag,reset,endc)
|
||||
if not(endc) then endc=false end
|
||||
if not(reset) then reset=0 end
|
||||
temp={Name=tag,pos=1,endAt=reset,active=true,endc=endc,
|
||||
Step=function(self) if self~=nil then _G.__CStep__=self if self.active==true then assert(loadstring("Step_"..tag.."("..self.pos..",__CStep__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("Step_"..self.Name.."_End(__CStep__)"))() end end end,
|
||||
Remove=function(self) for as=1,#event.Steps do if event.Steps[as].Name==self.Name then table.remove(event.Steps,as) end end end,
|
||||
Reset=function(self) self.pos=1 end,
|
||||
Set=function(self,amt) self.pos=amt end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Stop=function(self) self:Reset() self:Pause() end,
|
||||
Start=function(self) self:Resume() end,
|
||||
End=function(self) self.pos=self.EndAt self.endc=true end,
|
||||
}
|
||||
table.insert(event.Steps,temp) return temp end,
|
||||
stepExist=function(self,tag)
|
||||
for a_s=1,#event.Steps do
|
||||
if event.Steps[a_s].Name==tag then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
}
|
||||
98
oldversions/EventManager(1.1.0).lua
Normal file
98
oldversions/EventManager(1.1.0).lua
Normal file
@ -0,0 +1,98 @@
|
||||
require("Data/BasicCommands")
|
||||
IsEvent=true
|
||||
event={
|
||||
Active=true,
|
||||
CT=false,
|
||||
tag={},
|
||||
Events={},
|
||||
EventTracker={},
|
||||
Steps={},
|
||||
TSteps={},
|
||||
LoadOrder="/E/S/U",-- types = ESU,EUS,USE,UES,SEU,SUE
|
||||
setLoadOrder=function(self,str) event.LoadOrder=string.upper(str) end,
|
||||
DO_Order=function() LoadOrder=event.LoadOrder LoadOrder=LoadOrder:gsub("/E", "event.RUN_EVENTS(); ") LoadOrder=LoadOrder:gsub("/S", "event.RUN_STEPS(); ") LoadOrder=LoadOrder:gsub("/U", "event.RUN_UPDATES(); ") assert(loadstring(LoadOrder))() end,
|
||||
RUN_EVENTS=function() for d=1,#event.Events do assert(loadstring(event.Events[d]))() end end,
|
||||
RUN_STEPS=function() for s_g=1,#event.Steps do event.Steps[s_g]:Step() end end,
|
||||
RUN_UPDATES=function() event.OnUpdate() end,
|
||||
setAlarm=function(self,tag,set) event:new("Alarm_"..tag.."(\""..tag.."\")",[[GetCounter(event:getTracker("_Alarm_]]..tag..[["))>=]]..set,[[event:removeAlarm("]]..tag..[[")]]) event:addTracker("_Alarm_"..tag,SetCounter()) assert(loadstring("if Alarm_"..tag.."==nil then function Alarm_"..tag.."() print('No function \"Alarm_"..tag.."()\" exists make sure you created it') end end"))() end,
|
||||
setEvent=function(self,fname,condition,also) if not(string.find(fname,"(",1,true)) and not(string.find(fname,")",1,true)) then fname=fname.."()" end a=string.find(fname,"(",1,true) tempstr=string.sub(fname,1,a-1) event:new("Event_"..fname,condition,also) assert(loadstring("if Event_"..tempstr.."==nil then function Event_"..tempstr.."() print('No function \"Event_"..tempstr.."()\" exists make sure you created it') end end"))() end,
|
||||
removeAlarm=function(self,tag) event:destroy("Alarm_"..tag) event:removeTracker("_Alarm_"..tag) end,
|
||||
updateAlarm=function(self,tag,set) event:removeAlarm(tag) event:setAlarm(tag,set) end,
|
||||
addTracker=function(self,varname,var) event.EventTracker[varname]=var end,
|
||||
getTracker=function(self,varname) return event.EventTracker[varname] end,
|
||||
removeTracker=function(self,var) event.EventTracker[var]=nil end,
|
||||
trackerExist=function(self,tag) return event.EventTracker[tag]~=nil end,
|
||||
updateTracker=function(self,tag,val) if event:trackerExist(tag) then event.EventTracker[tag]=val end end,
|
||||
alarmExist=function(self,tag) return (event:getTracker("_Alarm_"..tag)~=nil) end,
|
||||
new=function(self,fname,condition,also) if not(also) then also="" end table.insert(self.Events,"if "..condition.." then "..also.." "..fname.." end") table.insert(event.tag,fname) end,
|
||||
eventExist=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then return true end end return false end,
|
||||
destroy=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then table.remove(event.tag,j) table.remove(event.Events,j) end end end,
|
||||
Stop=function() event.Active=false end,
|
||||
OnCreate=function() end,
|
||||
OnUpdate=function() end,
|
||||
OnClose=function() end,
|
||||
getActive=function() return event.tag end,
|
||||
Manager=function() event.OnCreate() while event.Active==true do event.DO_Order() end event.OnClose() end,
|
||||
--Manager=function() event.OnCreate() while event.Active==true do for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end event.OnClose() end,
|
||||
CManager=function() for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end,
|
||||
UManager=function() if event.CT==false then event.CT=true event.OnCreate() end for d=1,#event.Events do if event.Active==true then assert(loadstring(event.Events[d]))() end end for s_g=1,#event.Steps do event.Steps[s_g]:Step() end event.OnUpdate() end,
|
||||
createStep=function(self,tag,reset,skip,endc)
|
||||
if not(endc) then endc=false end
|
||||
temp=
|
||||
{
|
||||
Name=tag,
|
||||
pos=1,
|
||||
endAt=reset or math.huge,
|
||||
active=true,
|
||||
endc=endc,
|
||||
skip=skip or 0,
|
||||
spos=0,
|
||||
Step=function(self) if self~=nil then if self.spos==0 then _G.__CStep__=self if self.active==true then assert(loadstring("Step_"..tag.."("..self.pos..",__CStep__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("Step_"..self.Name.."_End(__CStep__)"))() end end end self.spos=self.spos+1 if self.spos>=self.skip then self.spos=0 end end,
|
||||
FStep=function(self) if self~=nil then if self.spos==0 then _G.__CStep__=self assert(loadstring("Step_"..tag.."("..self.pos..",__CStep__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("Step_"..self.Name.."_End(__CStep__)"))() end end self.spos=self.spos+1 if self.spos>=self.skip then self.spos=0 end end,
|
||||
Remove=function(self) for as=1,#event.Steps do if event.Steps[as].Name==self.Name then table.remove(event.Steps,as) end end end,
|
||||
Reset=function(self) self.pos=1 end,
|
||||
Set=function(self,amt) self.pos=amt end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Stop=function(self) self:Reset() self:Pause() end,
|
||||
Start=function(self) self:Resume() end,
|
||||
End=function(self) self.pos=self.EndAt self.endc=true end,
|
||||
}
|
||||
table.insert(event.Steps,temp) return temp end,
|
||||
createTStep=function(self,tag,reset,timer,endc)
|
||||
if not(endc) then endc=false end
|
||||
timer=timer or 1
|
||||
temp=
|
||||
{
|
||||
Name=tag,
|
||||
pos=1,
|
||||
endAt=reset or math.huge,
|
||||
active=true,
|
||||
endc=endc,
|
||||
skip= 0,
|
||||
spos=0,
|
||||
Step=function(self) if self~=nil then if self.spos==0 then _G.__CStep__=self if self.active==true then assert(loadstring("TStep_"..tag.."("..self.pos..",__CStep__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("TStep_"..self.Name.."_End(__CStep__)"))() end end end self.spos=self.spos+1 if self.spos>=self.skip then self.spos=0 end end,
|
||||
FStep=function(self) if self~=nil then if self.spos==0 then _G.__CStep__=self assert(loadstring("TStep_"..tag.."("..self.pos..",__CStep__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("TStep_"..self.Name.."_End(__CStep__)"))() end end self.spos=self.spos+1 if self.spos>=self.skip then self.spos=0 end end,
|
||||
Remove=function(self) for as=1,#event.Steps do if event.Steps[as].Name==self.Name then table.remove(event.Steps,as) end end end,
|
||||
Reset=function(self) self.pos=1 end,
|
||||
Set=function(self,amt) self.pos=amt end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Stop=function(self) self:Reset() self:Pause() end,
|
||||
Start=function(self) self:Resume() end,
|
||||
End=function(self) self.pos=self.EndAt self.endc=true end,
|
||||
}
|
||||
event:setAlarm("TStep_"..tag,timer)
|
||||
event:addTracker("_TStep_"..tag,temp)
|
||||
table.insert(event.TSteps,temp)
|
||||
assert(loadstring("function Alarm_TStep_"..tag.."(alarm) event:getTracker(\"_\"..alarm):Step() event:setAlarm(alarm,"..timer..") end"))()
|
||||
return temp end,
|
||||
stepExist=function(self,tag)
|
||||
for a_s=1,#event.Steps do
|
||||
if event.Steps[a_s].Name==tag then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
}
|
||||
203
oldversions/EventManager(1.2.0).lua
Normal file
203
oldversions/EventManager(1.2.0).lua
Normal file
@ -0,0 +1,203 @@
|
||||
function dump(t,indent)
|
||||
local names = {}
|
||||
if not indent then indent = "" end
|
||||
for n,g in pairs(t) do
|
||||
table.insert(names,n)
|
||||
end
|
||||
table.sort(names)
|
||||
for i,n in pairs(names) do
|
||||
local v = t[n]
|
||||
if type(v) == "table" then
|
||||
if(v==t) then -- prevent endless loop if table contains reference to itself
|
||||
print(indent..tostring(n)..": <-")
|
||||
else
|
||||
print(indent..tostring(n)..":")
|
||||
dump(v,indent.." ")
|
||||
end
|
||||
else
|
||||
if type(v) == "function" then
|
||||
print(indent..tostring(n).."()")
|
||||
else
|
||||
print(indent..tostring(n)..": "..tostring(v))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function SetCounter()
|
||||
return os.clock()
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
function GetCounter(count)
|
||||
if count~=nil then
|
||||
return os.clock()-count
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
----------------------------------------------------------------------------------------------------
|
||||
clock=os.clock
|
||||
event={
|
||||
Active=true,
|
||||
CT=false,
|
||||
tag={},
|
||||
Events={},
|
||||
Alarms={},
|
||||
EventTracker={},
|
||||
Steps={},
|
||||
TSteps={},
|
||||
CTask="",
|
||||
LoadOrder="/E/S/A/U",
|
||||
UpdateObj=
|
||||
{
|
||||
LoadOrder="",
|
||||
Resume=function(self) event.LoadOrder=self.LoadOrder end,
|
||||
Pause=function(self) self.LoadOrder=event.LoadOrder event.LoadOrder=event.LoadOrder:gsub("/U", "") end,
|
||||
},
|
||||
setLoadOrder=function(self,str) event.LoadOrder=string.upper(str) end,
|
||||
DO_Order=function() LoadOrder=event.LoadOrder LoadOrder=LoadOrder:gsub("/A", "event.RUN_ALARMS(); ") LoadOrder=LoadOrder:gsub("/E", "event.RUN_EVENTS(); ") LoadOrder=LoadOrder:gsub("/S", "event.RUN_STEPS(); ") LoadOrder=LoadOrder:gsub("/U", "event.RUN_UPDATES(); ") assert(loadstring(LoadOrder))() end,
|
||||
RUN_EVENTS=function() event.CTask="events" for d=1,#event.Events do assert(loadstring(event.Events[d]))() end end,
|
||||
RUN_STEPS=function() event.CTask="steps" for s_g=1,#event.Steps do event.Steps[s_g]:Step() end end,
|
||||
RUN_UPDATES=function() _G.__CAction__=event.UpdateObj event.CTask="updates" event.OnUpdate() end,
|
||||
RUN_ALARMS=function() for i=1,#event.Alarms do event.Alarms[i]:Tick() end end,
|
||||
--System Used Functions
|
||||
Hold=function(self,task) -- as many conditions and times that you want can be used
|
||||
local action=__CAction__
|
||||
action:Pause()
|
||||
if type(task)=="number" then
|
||||
local func=function() end
|
||||
local alarm=event:newAlarm(task,func,true)
|
||||
while alarm.active==true do
|
||||
event.CManager()
|
||||
end
|
||||
alarm:Destroy()
|
||||
action:Resume()
|
||||
elseif type(task)=="string" then
|
||||
assert(loadstring("while not("..task..") do event.CManager() end"))()
|
||||
action:Resume()
|
||||
end
|
||||
end,
|
||||
newAlarm=function(self,set,func,start)
|
||||
if not(start) then timer=0 active=false else timer=clock() active=true end
|
||||
if not(func) then func=(function() end) end
|
||||
Alarm=
|
||||
{
|
||||
active=active,
|
||||
timer=timer,
|
||||
set=set or 0,
|
||||
func=func,
|
||||
Tick=function(self) _G.__CAction__=self if self.active==true then if clock()-self.timer>=self.set then self:Pause() self:Ring() end end end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Set=function(self,amt) self.set=amt self.timer=clock() self:Resume() end,
|
||||
OnRing=function(self,func) self.func=func end,
|
||||
Ring=function(self) self:func(self) end,
|
||||
Reset=function(self) self.timer=clock() self:Resume() end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Destroy=function(self) for i=1,#event.Alarms do if tostring(event.Alarms[i])==tostring(self) then table.remove(event.Alarms,i) end end end,
|
||||
}
|
||||
table.insert(event.Alarms,Alarm)
|
||||
return Alarm
|
||||
end,
|
||||
setAlarm=function(self,tag,set)
|
||||
if event:eventExist("Alarm_"..tag.."(\""..tag.."\")")==false then
|
||||
event:new("Alarm_"..tag.."(\""..tag.."\")",[[GetCounter(event:getTracker("_Alarm_]]..tag..[["))>=]]..set,[[event:removeAlarm("]]..tag..[[")]])
|
||||
event:addTracker("_Alarm_"..tag,SetCounter())
|
||||
assert(loadstring("if Alarm_"..tag.."==nil then function Alarm_"..tag.."() print('No function \"Alarm_"..tag.."()\" exists make sure you created it') end end"))()
|
||||
else
|
||||
event:addTracker("_Alarm_"..tag,SetCounter())
|
||||
end
|
||||
end,
|
||||
setEvent=function(self,fname,condition,also) if not(string.find(fname,"(",1,true)) and not(string.find(fname,")",1,true)) then fname=fname.."()" end a=string.find(fname,"(",1,true) tempstr=string.sub(fname,1,a-1) event:new("Event_"..fname,condition,also) assert(loadstring("if Event_"..tempstr.."==nil then function Event_"..tempstr.."() print('No function \"Event_"..tempstr.."()\" exists make sure you created it') end end"))() end,
|
||||
removeAlarm=function(self,tag) event:destroyEvent("Alarm_"..tag) event:removeTracker("_Alarm_"..tag) end,
|
||||
updateAlarm=function(self,tag,set) event:removeAlarm(tag) event:setAlarm(tag,set) end,
|
||||
addTracker=function(self,varname,var) event.EventTracker[varname]=var end,
|
||||
getTracker=function(self,varname) return event.EventTracker[varname] end,
|
||||
listTrackers=function(self) return event.EventTracker end,
|
||||
removeTracker=function(self,var) event.EventTracker[var]=nil end,
|
||||
trackerExist=function(self,tag) return event.EventTracker[tag]~=nil end,
|
||||
updateTracker=function(self,tag,val) if event:trackerExist(tag) then event.EventTracker[tag]=val end end,
|
||||
alarmExist=function(self,tag) return (event:getTracker("_Alarm_"..tag)~=nil) end,
|
||||
new=function(self,fname,condition,also) if not(also) then also="" end table.insert(self.Events,"if "..condition.." then "..also.." "..fname.." end") table.insert(event.tag,fname) end,
|
||||
eventExist=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then return true end end return false end,
|
||||
destroyEvent=function(self,tag) for j=1,#event.tag do if event.tag[j]==tag then table.remove(event.tag,j) table.remove(event.Events,j) end end end,
|
||||
Stop=function() event.Active=false end,
|
||||
OnCreate=function() end,
|
||||
OnUpdate=function() end,
|
||||
OnClose=function() end,
|
||||
getActive=function() return event.tag end,
|
||||
Manager=function() event.OnCreate() while event.Active==true do event.DO_Order() end event.OnClose() end,
|
||||
CManager=function() if event.Active==true then event.DO_Order() end end,
|
||||
UManager=function() if event.CT==false then event.CT=true event.OnCreate() end if event.Active==true then event.DO_Order() end end,
|
||||
createStep=function(self,tag,reset,skip,endc)
|
||||
if not(endc) then
|
||||
endc=false
|
||||
end
|
||||
temp=
|
||||
{
|
||||
Name=tag,
|
||||
pos=1,
|
||||
endAt=reset or math.huge,
|
||||
active=true,
|
||||
endc=endc,
|
||||
skip=skip or 0,
|
||||
spos=0,
|
||||
Step=function(self) if self~=nil then if self.spos==0 then _G.__CAction__=self if self.active==true then assert(loadstring("Step_"..tag.."("..self.pos..",__CAction__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("Step_"..self.Name.."_End(__CAction__)"))() end end end self.spos=self.spos+1 if self.spos>=self.skip then self.spos=0 end end,
|
||||
FStep=function(self) if self~=nil then if self.spos==0 then _G.__CAction__=self assert(loadstring("Step_"..tag.."("..self.pos..",__CAction__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("Step_"..self.Name.."_End(__CAction__)"))() end end self.spos=self.spos+1 if self.spos>=self.skip then self.spos=0 end end,
|
||||
Remove=function(self) for as=1,#event.Steps do if event.Steps[as].Name==self.Name then table.remove(event.Steps,as) end end end,
|
||||
Reset=function(self) self.pos=1 end,
|
||||
Set=function(self,amt) self.pos=amt end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Stop=function(self) self:Reset() self:Pause() end,
|
||||
Start=function(self) self:Resume() end,
|
||||
End=function(self) _G.__CAction__=self assert(loadstring("Step_"..self.Name.."_End(__CAction__)"))() self:Reset() end,
|
||||
}
|
||||
table.insert(event.Steps,temp) return temp
|
||||
end,
|
||||
createTStep=function(self,tag,reset,timer,endc)
|
||||
if not(endc) then
|
||||
endc=false
|
||||
end
|
||||
timer=timer or 1
|
||||
temp=
|
||||
{
|
||||
Name=tag,
|
||||
pos=1,
|
||||
endAt=reset or math.huge,
|
||||
active=true,
|
||||
endc=endc,
|
||||
skip= 0,
|
||||
spos=0,
|
||||
Step=function(self) if self~=nil then if self.spos==0 then _G.__CAction__=self if self.active==true then assert(loadstring("TStep_"..tag.."("..self.pos..",__CAction__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("TStep_"..self.Name.."_End(__CAction__)"))() end end end self.spos=self.spos+1 if self.spos>=self.skip then self.spos=0 end end,
|
||||
FStep=function(self) if self~=nil then if self.spos==0 then _G.__CAction__=self assert(loadstring("TStep_"..tag.."("..self.pos..",__CAction__)"))() self.pos=self.pos+1 end end if self.endAt+1<=self.pos then self:Reset() if endc==true then assert(loadstring("TStep_"..self.Name.."_End(__CAction__)"))() end end self.spos=self.spos+1 if self.spos>=self.skip then self.spos=0 end end,
|
||||
Remove=function(self) for as=1,#event.Steps do if event.Steps[as].Name==self.Name then table.remove(event.Steps,as) end end end,
|
||||
Reset=function(self) self.pos=1 end,
|
||||
Set=function(self,amt) self.pos=amt end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Stop=function(self) self:Reset() self:Pause() end,
|
||||
Start=function(self) self:Resume() end,
|
||||
End=function(self) self.pos=self.EndAt self.endc=true end,
|
||||
}
|
||||
event:setAlarm("TStep_"..tag,timer)
|
||||
event:addTracker("_TStep_"..tag,temp)
|
||||
table.insert(event.TSteps,temp)
|
||||
assert(loadstring("function Alarm_TStep_"..tag.."(alarm) event:getTracker(\"_\"..alarm):Step() event:updateAlarm(alarm,"..timer..") end"))()
|
||||
return temp
|
||||
end,
|
||||
stepExist=function(self,tag)
|
||||
for a_s=1,#event.Steps do
|
||||
if event.Steps[a_s].Name==tag then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
tstepExist=function(self,tag)
|
||||
for a_s=1,#event.TSteps do
|
||||
if event.TSteps[a_s].Name==tag then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
}
|
||||
728
oldversions/EventManager(1.3.0).lua
Normal file
728
oldversions/EventManager(1.3.0).lua
Normal file
@ -0,0 +1,728 @@
|
||||
function readonlytable(table)
|
||||
return setmetatable({}, {
|
||||
__index = table,
|
||||
__newindex = function(table, key, value)
|
||||
error("Attempt to modify read-only table")
|
||||
end,
|
||||
__metatable = false
|
||||
});
|
||||
end
|
||||
local EventRef=
|
||||
readonlytable{
|
||||
Pause=function(self)
|
||||
self.active=false
|
||||
if not(event.isPaused(self)) then
|
||||
table.insert(event.Paused,self)
|
||||
for _j=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[_j])==tostring(self) then
|
||||
table.remove(event.Mainloop,_j)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
Resume=function(self)
|
||||
self.active=true
|
||||
if event.isPaused(self) then
|
||||
table.insert(event.Mainloop,self)
|
||||
for _j=1,#event.Paused do
|
||||
if tostring(event.Paused[_j])==tostring(self) then
|
||||
table.remove(event.Paused,_j)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
Stop=function(self)
|
||||
self.active=nil
|
||||
end,
|
||||
}
|
||||
|
||||
local StepRef=
|
||||
readonlytable{
|
||||
Step=function(self)
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
_G.__CAction__=self
|
||||
if self.active==true then
|
||||
for i=1,#self.steps do
|
||||
self.steps[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
end
|
||||
end
|
||||
if self.endAt+self.count<=self.pos and self.endAt>self.start then
|
||||
self:Reset()
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
elseif self.pos<=0 then
|
||||
self:Reset()
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end,
|
||||
FStep=function(self)
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
_G.__CAction__=self
|
||||
for i=1,#self.steps do
|
||||
self.steps[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
end
|
||||
if self.endAt+self.count<=self.pos and self.endAt>self.start then
|
||||
self:Reset()
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
elseif self.pos==0 then
|
||||
self:Reset()
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for as=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[as])==tostring(self) then
|
||||
table.remove(event.Mainloop,as)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
Reset=function(self)
|
||||
self.pos=self.start
|
||||
end,
|
||||
Set=function(self,amt)
|
||||
self.pos=amt
|
||||
end,
|
||||
Pause=EventRef.Pause,
|
||||
Resume=EventRef.Resume,
|
||||
Stop=function(self)
|
||||
self:Reset()
|
||||
self.active=nil
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
end,
|
||||
End=function(self)
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
self:Reset()
|
||||
end,
|
||||
Update=function(self,start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end,
|
||||
OnEnd=function(self,func)
|
||||
table.insert(self.funcs,func)
|
||||
end,
|
||||
OnStep=function(self,func)
|
||||
table.insert(self.steps,func)
|
||||
end,
|
||||
FreeConnections=function(self)
|
||||
self.funcs={}
|
||||
self.steps={}
|
||||
end,
|
||||
}
|
||||
--thread and run setup
|
||||
if love then
|
||||
function love.run()
|
||||
if love.math then
|
||||
love.math.setRandomSeed(os.time())
|
||||
end
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
end
|
||||
if love.load then love.load(arg) end
|
||||
if love.timer then love.timer.step() end
|
||||
local dt = 0
|
||||
while true do
|
||||
-- Process events.
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
event.uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
event.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if event.draw then event.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
eThreads={
|
||||
send=function() end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
function RunTasks()
|
||||
for i=1,#event.DoTasks do
|
||||
event.DoTasks[i]()
|
||||
end
|
||||
end
|
||||
event={
|
||||
VERSION="1.0.0 (Build Version: 5.7.3)",
|
||||
Priority_Core=1,
|
||||
Priority_High=2,
|
||||
Priority_Above_Normal=4,
|
||||
Priority_Normal=16,
|
||||
Priority_Low=256,
|
||||
Priority_Idle=65536,
|
||||
Start=0,
|
||||
Active=true,
|
||||
CT=false,
|
||||
Tasks={},
|
||||
DoTasks={},
|
||||
garbage={},
|
||||
Paused={},
|
||||
last={},
|
||||
func={},
|
||||
drawF={},
|
||||
pump=false,
|
||||
pumpvar=0,
|
||||
Mainloop={},
|
||||
PEnabled=true,
|
||||
PCount=1,
|
||||
Triggers={},
|
||||
oneTimeObj=
|
||||
{
|
||||
last={},
|
||||
Resume=function(self) end,
|
||||
Pause=function(self) end,
|
||||
},
|
||||
RemoveAll=function()
|
||||
event.Mainloop={}
|
||||
end,
|
||||
GarbageObj=
|
||||
{
|
||||
Resume=function() end,
|
||||
Pause=function() end
|
||||
},
|
||||
isPaused=function(obj)
|
||||
for _j=1,#event.Paused do
|
||||
if tostring(event.Paused[_j])==tostring(obj) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
DO_Order=function()
|
||||
event.oneTime(RunTasks)
|
||||
event.PCount=event.PCount+1
|
||||
for i=1,#event.Mainloop do
|
||||
if event.Mainloop[i]~=nil then
|
||||
local obj = event.Mainloop[i]
|
||||
if event.PCount%obj.Priority==0 and event.PEnabled then
|
||||
obj:Act()
|
||||
elseif event.PEnabled==false then
|
||||
obj:Act()
|
||||
end
|
||||
if event.PCount>event.Priority_Idle then
|
||||
event.PCount=event.Priority_Core
|
||||
end
|
||||
end
|
||||
end
|
||||
event.MANAGE_GARBAGE()
|
||||
end,
|
||||
MANAGE_GARBAGE=function()
|
||||
_G.__CAction__=event.GarbageObj
|
||||
for _i=1,#event.garbage do
|
||||
event.garbage[_i]:Remove()
|
||||
table.remove(event.garbage,_i)
|
||||
end
|
||||
end,
|
||||
oneTime=function(func)
|
||||
event.oneTimeObj.last=_G.__CAction__
|
||||
_G.__CAction__=event.oneTimeObj
|
||||
for _k=1,#event.Tasks do
|
||||
if event.Tasks[_k]==func then
|
||||
_G.__CAction__=event.oneTimeObj.last
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(event.Tasks,func)
|
||||
func()
|
||||
_G.__CAction__=event.oneTimeObj.last
|
||||
return true
|
||||
end,
|
||||
oneETime=function(func)
|
||||
for _k=1,#event.Tasks do
|
||||
if event.Tasks[_k]==string.dump(func) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(event.Tasks,string.dump(func))
|
||||
func()
|
||||
return true
|
||||
end,
|
||||
hold=function(task) -- as many conditions and times that you want can be used
|
||||
local action=__CAction__
|
||||
action:Pause()
|
||||
if type(task)=="number" then
|
||||
local alarm=event.newAlarm(task,function() end,true)
|
||||
while alarm.active==true do
|
||||
if love then
|
||||
event.lManager()
|
||||
else
|
||||
event.cManager()
|
||||
end
|
||||
end
|
||||
alarm:Remove()
|
||||
action:Resume()
|
||||
elseif type(task)=="function" then
|
||||
local env=event.newEvent(task,function(envt) envt:Pause() envt:Stop() end)
|
||||
while env.active do
|
||||
if love then
|
||||
event.lManager()
|
||||
else
|
||||
event.cManager()
|
||||
end
|
||||
end
|
||||
env:Remove()
|
||||
action:Resume()
|
||||
else
|
||||
print("Error Data Type!!!")
|
||||
end
|
||||
end,
|
||||
waitFor=function(obj)
|
||||
local obj=obj
|
||||
event.hold(function() return not(obj.active) end)
|
||||
end,
|
||||
getType=function(obj)
|
||||
if obj.Type~=nil then
|
||||
return obj.Type
|
||||
end
|
||||
end,
|
||||
newEvent=function(test,task)
|
||||
temp=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
Parent=event.Events,
|
||||
Type="Event",
|
||||
active=true,
|
||||
test=test or (function() end),
|
||||
task={task} or {},
|
||||
Act=function(self)
|
||||
_G.__CAction__=self
|
||||
if self:test(self)==true then
|
||||
self:Pause()
|
||||
self:Stop()
|
||||
for i=1,#self.task do
|
||||
self.task[i](self)
|
||||
end
|
||||
end
|
||||
end,
|
||||
Reset=function(self) self:Resume() end,
|
||||
Stop=EventRef.Stop,
|
||||
Pause=EventRef.Pause,
|
||||
Resume=EventRef.Resume,
|
||||
OnEvent=function(self,func)
|
||||
table.insert(self.task,func)
|
||||
end,
|
||||
FreeConnections=function(self)
|
||||
self.task={}
|
||||
end,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for i=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[i])==tostring(self) then
|
||||
table.remove(event.Mainloop,i)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
}
|
||||
table.insert(event.Mainloop,temp)
|
||||
event.last=temp
|
||||
return temp
|
||||
end,
|
||||
newAlarm=function(set,func,start)
|
||||
if not(start) then
|
||||
timer=0
|
||||
active=false
|
||||
else
|
||||
timer=os.clock()
|
||||
active=true
|
||||
end
|
||||
if not(func) then
|
||||
func=(function() end)
|
||||
end
|
||||
Alarm=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
Parent=event.Alarms,
|
||||
Type="Alarm",
|
||||
active=active,
|
||||
timer=timer,
|
||||
set=set or 0,
|
||||
func={func},
|
||||
Act=function(self)
|
||||
_G.__CAction__=self
|
||||
if self.active==true then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Pause()
|
||||
self:Stop()
|
||||
self:Ring()
|
||||
end
|
||||
end
|
||||
end,
|
||||
FreeConnections=function(self)
|
||||
self.func={}
|
||||
end,
|
||||
Stop=EventRef.Stop,
|
||||
Pause=EventRef.Pause,
|
||||
Set=function(self,amt)
|
||||
self.set=amt
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end,
|
||||
OnRing=function(self,func)
|
||||
table.insert(self.func,func)
|
||||
end,
|
||||
Ring=function(self)
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end,
|
||||
Reset=function(self)
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end,
|
||||
Resume=EventRef.Resume,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for i=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[i])==tostring(self) then
|
||||
table.remove(event.Mainloop,i)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
}
|
||||
table.insert(event.Mainloop,Alarm)
|
||||
event.last=temp
|
||||
return Alarm
|
||||
end,
|
||||
newTask=function(func)
|
||||
table.insert(event.DoTasks,func)
|
||||
end,
|
||||
createLoop=function()
|
||||
temp=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
Parent=event.Loops,
|
||||
Type="Loop",
|
||||
active=true,
|
||||
func={},
|
||||
OnLoop=function(self,func)
|
||||
table.insert(self.func,func)
|
||||
end,
|
||||
Stop=EventRef.Stop,
|
||||
Pause=EventRef.Pause,
|
||||
Resume=EventRef.Resume,
|
||||
Act=function(self)
|
||||
_G.__CAction__=self
|
||||
for i=1,#self.func do
|
||||
self.func[i](os.clock()-event.Start,self)
|
||||
end
|
||||
end,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for i=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[i])==tostring(self) then
|
||||
table.remove(event.Mainloop,i)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
FreeConnections=function(self)
|
||||
self.func={}
|
||||
end,
|
||||
}
|
||||
table.insert(event.Mainloop,temp)
|
||||
event.last=temp
|
||||
return temp
|
||||
end,
|
||||
createStep=function(start,reset,count,skip)
|
||||
think=1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
if not(endc) then
|
||||
endc=false
|
||||
end
|
||||
temp=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
start=start or 1,
|
||||
Parent=event.Steps,
|
||||
Type="Step",
|
||||
pos=start or 1,
|
||||
endAt=reset or math.huge,
|
||||
active=true,
|
||||
skip=skip or 0,
|
||||
spos=0,
|
||||
count=count or 1*think,
|
||||
funcs={},
|
||||
steps={},
|
||||
Act=StepRef.Step,
|
||||
FAct=StepRef.FStep,
|
||||
Remove=StepRef.Remove,
|
||||
Reset=StepRef.Reset,
|
||||
Set=StepRef.Set,
|
||||
Pause=StepRef.Pause,
|
||||
Resume=StepRef.Resume,
|
||||
Stop=StepRef.Stop,
|
||||
End=StepRef.End,
|
||||
Update=StepRef.Update,
|
||||
OnEnd=StepRef.OnEnd,
|
||||
OnStep=StepRef.OnStep,
|
||||
FreeConnections=StepRef.FreeConnections
|
||||
}
|
||||
table.insert(event.Mainloop,temp)
|
||||
event.last=temp
|
||||
return temp
|
||||
end,
|
||||
createTStep=function(start,reset,timer,count)
|
||||
think=1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
if not(endc) then
|
||||
endc=false
|
||||
end
|
||||
timer=timer or 1
|
||||
local _alarm=event.newAlarm(timer,function(alarm) alarm.Link:Act() alarm:Reset() end,true)
|
||||
temp=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
start=start or 1,
|
||||
Parent=event.TSteps,
|
||||
Type="TStep",
|
||||
pos=start or 1,
|
||||
endAt=reset or math.huge,
|
||||
active=true,
|
||||
skip= 0,
|
||||
spos=0,
|
||||
count=count or 1*think,
|
||||
funcs={},
|
||||
steps={},
|
||||
alarm=_alarm,
|
||||
Act=StepRef.Step,
|
||||
FAct=StepRef.FStep,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for as=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[as])==tostring(self) then
|
||||
table.remove(event.Mainloop,as)
|
||||
end
|
||||
end
|
||||
self.alarm:Remove()
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
Reset=StepRef.Reset,
|
||||
Set=StepRef.Set,
|
||||
Pause=StepRef.Pause,
|
||||
Resume=StepRef.Resume,
|
||||
Stop=StepRef.Stop,
|
||||
End=StepRef.End,
|
||||
Update=function(self,start,reset,timer,count)
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
if not(count<0) then
|
||||
print("less")
|
||||
count=-count
|
||||
end
|
||||
end
|
||||
end
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
if timer~=nil then
|
||||
self.alarm:Set(timer)
|
||||
end
|
||||
self.count=count or self.count
|
||||
self.pos=self.start
|
||||
self:Resume()
|
||||
end,
|
||||
OnEnd=StepRef.OnEnd,
|
||||
OnStep=StepRef.OnStep,
|
||||
FreeConnections=StepRef.FreeConnections
|
||||
}
|
||||
_alarm.Link=temp
|
||||
event.last=temp
|
||||
return temp
|
||||
end,
|
||||
createTrigger=function(func)
|
||||
temp={
|
||||
active=true,
|
||||
trigfunc=func,
|
||||
Remove=function(self)
|
||||
for i=1,#event.Triggers do
|
||||
if event.Triggers[i]==self then
|
||||
table.remove(event.Triggers,i)
|
||||
end
|
||||
end
|
||||
end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Fire=function(self,...)
|
||||
if self.active==true then
|
||||
local tempA=__CAction__
|
||||
__CAction__=self
|
||||
self:trigfunc(...)
|
||||
__CAction__=tempA
|
||||
end
|
||||
end,
|
||||
}
|
||||
table.insert(event.Triggers,temp)
|
||||
return temp
|
||||
end,
|
||||
stop=function()
|
||||
event.Active=false
|
||||
end,
|
||||
onStart=function() end,
|
||||
onUpdate=function(func)
|
||||
local temp=event.createLoop()
|
||||
temp:OnLoop(func)
|
||||
temp.Priority=1
|
||||
end,
|
||||
onDraw=function(func)
|
||||
table.insert(event.drawF,func)
|
||||
end,
|
||||
onClose=function() end,
|
||||
manager=function()
|
||||
if not(love) then
|
||||
event.onStart()
|
||||
event.Start=os.clock()
|
||||
while event.Active==true do
|
||||
event.DO_Order()
|
||||
end
|
||||
event.onClose()
|
||||
return os.clock()-event.Start
|
||||
else
|
||||
return false
|
||||
end
|
||||
end,
|
||||
cManager=function()
|
||||
if event.Active==true then
|
||||
event.DO_Order()
|
||||
end
|
||||
end,
|
||||
uManager=function(dt)
|
||||
if event.CT==false then
|
||||
if dt then
|
||||
event.pump=true
|
||||
end
|
||||
event.CT=true
|
||||
event.onStart()
|
||||
event.Start=os.clock()
|
||||
end
|
||||
event.pumpvar=dt
|
||||
if event.Active==true then
|
||||
event.DO_Order()
|
||||
end
|
||||
end,
|
||||
dManager=function()
|
||||
for ii=1,#event.drawF do
|
||||
event.drawF[ii]()
|
||||
end
|
||||
end,
|
||||
lManager=function()
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
event.uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
event.dManager()
|
||||
love.graphics.present()
|
||||
end
|
||||
end,
|
||||
benchMark=function(sec,p)
|
||||
p=p or event.Priority_Normal
|
||||
local temp=event.createStep(10)
|
||||
temp.CC=0
|
||||
temp:OnStep(function(pos,step) step.CC=step.CC+1 end)
|
||||
local Loud=event.newAlarm(sec,nil,true)
|
||||
Loud.Link=temp
|
||||
Loud:OnRing(function(alarm) print((alarm.Link.CC).." steps in "..alarm.set.." second(s)") end)
|
||||
temp.Priority=p
|
||||
Loud.Priority=p
|
||||
return Loud
|
||||
end,
|
||||
}
|
||||
728
oldversions/EventManager(5.7.3).lua
Normal file
728
oldversions/EventManager(5.7.3).lua
Normal file
@ -0,0 +1,728 @@
|
||||
function readonlytable(table)
|
||||
return setmetatable({}, {
|
||||
__index = table,
|
||||
__newindex = function(table, key, value)
|
||||
error("Attempt to modify read-only table")
|
||||
end,
|
||||
__metatable = false
|
||||
});
|
||||
end
|
||||
local EventRef=
|
||||
readonlytable{
|
||||
Pause=function(self)
|
||||
self.active=false
|
||||
if not(event.isPaused(self)) then
|
||||
table.insert(event.Paused,self)
|
||||
for _j=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[_j])==tostring(self) then
|
||||
table.remove(event.Mainloop,_j)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
Resume=function(self)
|
||||
self.active=true
|
||||
if event.isPaused(self) then
|
||||
table.insert(event.Mainloop,self)
|
||||
for _j=1,#event.Paused do
|
||||
if tostring(event.Paused[_j])==tostring(self) then
|
||||
table.remove(event.Paused,_j)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
Stop=function(self)
|
||||
self.active=nil
|
||||
end,
|
||||
}
|
||||
|
||||
local StepRef=
|
||||
readonlytable{
|
||||
Step=function(self)
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
_G.__CAction__=self
|
||||
if self.active==true then
|
||||
for i=1,#self.steps do
|
||||
self.steps[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
end
|
||||
end
|
||||
if self.endAt+self.count<=self.pos and self.endAt>self.start then
|
||||
self:Reset()
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
elseif self.pos<=0 then
|
||||
self:Reset()
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end,
|
||||
FStep=function(self)
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
_G.__CAction__=self
|
||||
for i=1,#self.steps do
|
||||
self.steps[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
end
|
||||
if self.endAt+self.count<=self.pos and self.endAt>self.start then
|
||||
self:Reset()
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
elseif self.pos==0 then
|
||||
self:Reset()
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for as=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[as])==tostring(self) then
|
||||
table.remove(event.Mainloop,as)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
Reset=function(self)
|
||||
self.pos=self.start
|
||||
end,
|
||||
Set=function(self,amt)
|
||||
self.pos=amt
|
||||
end,
|
||||
Pause=EventRef.Pause,
|
||||
Resume=EventRef.Resume,
|
||||
Stop=function(self)
|
||||
self:Reset()
|
||||
self.active=nil
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
end,
|
||||
End=function(self)
|
||||
for i=1,#self.funcs do
|
||||
self.funcs[i](self)
|
||||
end
|
||||
self:Reset()
|
||||
end,
|
||||
Update=function(self,start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end,
|
||||
OnEnd=function(self,func)
|
||||
table.insert(self.funcs,func)
|
||||
end,
|
||||
OnStep=function(self,func)
|
||||
table.insert(self.steps,func)
|
||||
end,
|
||||
FreeConnections=function(self)
|
||||
self.funcs={}
|
||||
self.steps={}
|
||||
end,
|
||||
}
|
||||
--thread and run setup
|
||||
if love then
|
||||
function love.run()
|
||||
if love.math then
|
||||
love.math.setRandomSeed(os.time())
|
||||
end
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
end
|
||||
if love.load then love.load(arg) end
|
||||
if love.timer then love.timer.step() end
|
||||
local dt = 0
|
||||
while true do
|
||||
-- Process events.
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
event.uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
event.dManager()
|
||||
love.graphics.setColor(255,255,255,255)
|
||||
if event.draw then event.draw() end
|
||||
love.graphics.present()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
eThreads={
|
||||
send=function() end,
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
function RunTasks()
|
||||
for i=1,#event.DoTasks do
|
||||
event.DoTasks[i]()
|
||||
end
|
||||
end
|
||||
event={
|
||||
VERSION="1.0.0 (Build Version: 5.7.3)",
|
||||
Priority_Core=1,
|
||||
Priority_High=2,
|
||||
Priority_Above_Normal=4,
|
||||
Priority_Normal=16,
|
||||
Priority_Low=256,
|
||||
Priority_Idle=65536,
|
||||
Start=0,
|
||||
Active=true,
|
||||
CT=false,
|
||||
Tasks={},
|
||||
DoTasks={},
|
||||
garbage={},
|
||||
Paused={},
|
||||
last={},
|
||||
func={},
|
||||
drawF={},
|
||||
pump=false,
|
||||
pumpvar=0,
|
||||
Mainloop={},
|
||||
PEnabled=true,
|
||||
PCount=1,
|
||||
Triggers={},
|
||||
oneTimeObj=
|
||||
{
|
||||
last={},
|
||||
Resume=function(self) end,
|
||||
Pause=function(self) end,
|
||||
},
|
||||
RemoveAll=function()
|
||||
event.Mainloop={}
|
||||
end,
|
||||
GarbageObj=
|
||||
{
|
||||
Resume=function() end,
|
||||
Pause=function() end
|
||||
},
|
||||
isPaused=function(obj)
|
||||
for _j=1,#event.Paused do
|
||||
if tostring(event.Paused[_j])==tostring(obj) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end,
|
||||
DO_Order=function()
|
||||
event.oneTime(RunTasks)
|
||||
event.PCount=event.PCount+1
|
||||
for i=1,#event.Mainloop do
|
||||
if event.Mainloop[i]~=nil then
|
||||
local obj = event.Mainloop[i]
|
||||
if event.PCount%obj.Priority==0 and event.PEnabled then
|
||||
obj:Act()
|
||||
elseif event.PEnabled==false then
|
||||
obj:Act()
|
||||
end
|
||||
if event.PCount>event.Priority_Idle then
|
||||
event.PCount=event.Priority_Core
|
||||
end
|
||||
end
|
||||
end
|
||||
event.MANAGE_GARBAGE()
|
||||
end,
|
||||
MANAGE_GARBAGE=function()
|
||||
_G.__CAction__=event.GarbageObj
|
||||
for _i=1,#event.garbage do
|
||||
event.garbage[_i]:Remove()
|
||||
table.remove(event.garbage,_i)
|
||||
end
|
||||
end,
|
||||
oneTime=function(func)
|
||||
event.oneTimeObj.last=_G.__CAction__
|
||||
_G.__CAction__=event.oneTimeObj
|
||||
for _k=1,#event.Tasks do
|
||||
if event.Tasks[_k]==func then
|
||||
_G.__CAction__=event.oneTimeObj.last
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(event.Tasks,func)
|
||||
func()
|
||||
_G.__CAction__=event.oneTimeObj.last
|
||||
return true
|
||||
end,
|
||||
oneETime=function(func)
|
||||
for _k=1,#event.Tasks do
|
||||
if event.Tasks[_k]==string.dump(func) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(event.Tasks,string.dump(func))
|
||||
func()
|
||||
return true
|
||||
end,
|
||||
hold=function(task) -- as many conditions and times that you want can be used
|
||||
local action=__CAction__
|
||||
action:Pause()
|
||||
if type(task)=="number" then
|
||||
local alarm=event.newAlarm(task,function() end,true)
|
||||
while alarm.active==true do
|
||||
if love then
|
||||
event.lManager()
|
||||
else
|
||||
event.cManager()
|
||||
end
|
||||
end
|
||||
alarm:Remove()
|
||||
action:Resume()
|
||||
elseif type(task)=="function" then
|
||||
local env=event.newEvent(task,function(envt) envt:Pause() envt:Stop() end)
|
||||
while env.active do
|
||||
if love then
|
||||
event.lManager()
|
||||
else
|
||||
event.cManager()
|
||||
end
|
||||
end
|
||||
env:Remove()
|
||||
action:Resume()
|
||||
else
|
||||
print("Error Data Type!!!")
|
||||
end
|
||||
end,
|
||||
waitFor=function(obj)
|
||||
local obj=obj
|
||||
event.hold(function() return not(obj.active) end)
|
||||
end,
|
||||
getType=function(obj)
|
||||
if obj.Type~=nil then
|
||||
return obj.Type
|
||||
end
|
||||
end,
|
||||
newEvent=function(test,task)
|
||||
temp=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
Parent=event.Events,
|
||||
Type="Event",
|
||||
active=true,
|
||||
test=test or (function() end),
|
||||
task={task} or {},
|
||||
Act=function(self)
|
||||
_G.__CAction__=self
|
||||
if self:test(self)==true then
|
||||
self:Pause()
|
||||
self:Stop()
|
||||
for i=1,#self.task do
|
||||
self.task[i](self)
|
||||
end
|
||||
end
|
||||
end,
|
||||
Reset=function(self) self:Resume() end,
|
||||
Stop=EventRef.Stop,
|
||||
Pause=EventRef.Pause,
|
||||
Resume=EventRef.Resume,
|
||||
OnEvent=function(self,func)
|
||||
table.insert(self.task,func)
|
||||
end,
|
||||
FreeConnections=function(self)
|
||||
self.task={}
|
||||
end,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for i=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[i])==tostring(self) then
|
||||
table.remove(event.Mainloop,i)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
}
|
||||
table.insert(event.Mainloop,temp)
|
||||
event.last=temp
|
||||
return temp
|
||||
end,
|
||||
newAlarm=function(set,func,start)
|
||||
if not(start) then
|
||||
timer=0
|
||||
active=false
|
||||
else
|
||||
timer=os.clock()
|
||||
active=true
|
||||
end
|
||||
if not(func) then
|
||||
func=(function() end)
|
||||
end
|
||||
Alarm=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
Parent=event.Alarms,
|
||||
Type="Alarm",
|
||||
active=active,
|
||||
timer=timer,
|
||||
set=set or 0,
|
||||
func={func},
|
||||
Act=function(self)
|
||||
_G.__CAction__=self
|
||||
if self.active==true then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Pause()
|
||||
self:Stop()
|
||||
self:Ring()
|
||||
end
|
||||
end
|
||||
end,
|
||||
FreeConnections=function(self)
|
||||
self.func={}
|
||||
end,
|
||||
Stop=EventRef.Stop,
|
||||
Pause=EventRef.Pause,
|
||||
Set=function(self,amt)
|
||||
self.set=amt
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end,
|
||||
OnRing=function(self,func)
|
||||
table.insert(self.func,func)
|
||||
end,
|
||||
Ring=function(self)
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end,
|
||||
Reset=function(self)
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end,
|
||||
Resume=EventRef.Resume,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for i=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[i])==tostring(self) then
|
||||
table.remove(event.Mainloop,i)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
}
|
||||
table.insert(event.Mainloop,Alarm)
|
||||
event.last=temp
|
||||
return Alarm
|
||||
end,
|
||||
newTask=function(func)
|
||||
table.insert(event.DoTasks,func)
|
||||
end,
|
||||
createLoop=function()
|
||||
temp=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
Parent=event.Loops,
|
||||
Type="Loop",
|
||||
active=true,
|
||||
func={},
|
||||
OnLoop=function(self,func)
|
||||
table.insert(self.func,func)
|
||||
end,
|
||||
Stop=EventRef.Stop,
|
||||
Pause=EventRef.Pause,
|
||||
Resume=EventRef.Resume,
|
||||
Act=function(self)
|
||||
_G.__CAction__=self
|
||||
for i=1,#self.func do
|
||||
self.func[i](os.clock()-event.Start,self)
|
||||
end
|
||||
end,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for i=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[i])==tostring(self) then
|
||||
table.remove(event.Mainloop,i)
|
||||
end
|
||||
end
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
FreeConnections=function(self)
|
||||
self.func={}
|
||||
end,
|
||||
}
|
||||
table.insert(event.Mainloop,temp)
|
||||
event.last=temp
|
||||
return temp
|
||||
end,
|
||||
createStep=function(start,reset,count,skip)
|
||||
think=1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
if not(endc) then
|
||||
endc=false
|
||||
end
|
||||
temp=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
start=start or 1,
|
||||
Parent=event.Steps,
|
||||
Type="Step",
|
||||
pos=start or 1,
|
||||
endAt=reset or math.huge,
|
||||
active=true,
|
||||
skip=skip or 0,
|
||||
spos=0,
|
||||
count=count or 1*think,
|
||||
funcs={},
|
||||
steps={},
|
||||
Act=StepRef.Step,
|
||||
FAct=StepRef.FStep,
|
||||
Remove=StepRef.Remove,
|
||||
Reset=StepRef.Reset,
|
||||
Set=StepRef.Set,
|
||||
Pause=StepRef.Pause,
|
||||
Resume=StepRef.Resume,
|
||||
Stop=StepRef.Stop,
|
||||
End=StepRef.End,
|
||||
Update=StepRef.Update,
|
||||
OnEnd=StepRef.OnEnd,
|
||||
OnStep=StepRef.OnStep,
|
||||
FreeConnections=StepRef.FreeConnections
|
||||
}
|
||||
table.insert(event.Mainloop,temp)
|
||||
event.last=temp
|
||||
return temp
|
||||
end,
|
||||
createTStep=function(start,reset,timer,count)
|
||||
think=1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
if not(endc) then
|
||||
endc=false
|
||||
end
|
||||
timer=timer or 1
|
||||
local _alarm=event.newAlarm(timer,function(alarm) alarm.Link:Act() alarm:Reset() end,true)
|
||||
temp=
|
||||
{
|
||||
Priority=event.Priority_Normal,
|
||||
_Priority=0,
|
||||
start=start or 1,
|
||||
Parent=event.TSteps,
|
||||
Type="TStep",
|
||||
pos=start or 1,
|
||||
endAt=reset or math.huge,
|
||||
active=true,
|
||||
skip= 0,
|
||||
spos=0,
|
||||
count=count or 1*think,
|
||||
funcs={},
|
||||
steps={},
|
||||
alarm=_alarm,
|
||||
Act=StepRef.Step,
|
||||
FAct=StepRef.FStep,
|
||||
Remove=function(self)
|
||||
if self~=_G.__CAction__ then
|
||||
for as=1,#event.Mainloop do
|
||||
if tostring(event.Mainloop[as])==tostring(self) then
|
||||
table.remove(event.Mainloop,as)
|
||||
end
|
||||
end
|
||||
self.alarm:Remove()
|
||||
else
|
||||
table.insert(event.garbage,self)
|
||||
end
|
||||
end,
|
||||
Reset=StepRef.Reset,
|
||||
Set=StepRef.Set,
|
||||
Pause=StepRef.Pause,
|
||||
Resume=StepRef.Resume,
|
||||
Stop=StepRef.Stop,
|
||||
End=StepRef.End,
|
||||
Update=function(self,start,reset,timer,count)
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
if not(count<0) then
|
||||
print("less")
|
||||
count=-count
|
||||
end
|
||||
end
|
||||
end
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
if timer~=nil then
|
||||
self.alarm:Set(timer)
|
||||
end
|
||||
self.count=count or self.count
|
||||
self.pos=self.start
|
||||
self:Resume()
|
||||
end,
|
||||
OnEnd=StepRef.OnEnd,
|
||||
OnStep=StepRef.OnStep,
|
||||
FreeConnections=StepRef.FreeConnections
|
||||
}
|
||||
_alarm.Link=temp
|
||||
event.last=temp
|
||||
return temp
|
||||
end,
|
||||
createTrigger=function(func)
|
||||
temp={
|
||||
active=true,
|
||||
trigfunc=func,
|
||||
Remove=function(self)
|
||||
for i=1,#event.Triggers do
|
||||
if event.Triggers[i]==self then
|
||||
table.remove(event.Triggers,i)
|
||||
end
|
||||
end
|
||||
end,
|
||||
Pause=function(self) self.active=false end,
|
||||
Resume=function(self) self.active=true end,
|
||||
Fire=function(self,...)
|
||||
if self.active==true then
|
||||
local tempA=__CAction__
|
||||
__CAction__=self
|
||||
self:trigfunc(...)
|
||||
__CAction__=tempA
|
||||
end
|
||||
end,
|
||||
}
|
||||
table.insert(event.Triggers,temp)
|
||||
return temp
|
||||
end,
|
||||
stop=function()
|
||||
event.Active=false
|
||||
end,
|
||||
onStart=function() end,
|
||||
onUpdate=function(func)
|
||||
local temp=event.createLoop()
|
||||
temp:OnLoop(func)
|
||||
temp.Priority=1
|
||||
end,
|
||||
onDraw=function(func)
|
||||
table.insert(event.drawF,func)
|
||||
end,
|
||||
onClose=function() end,
|
||||
manager=function()
|
||||
if not(love) then
|
||||
event.onStart()
|
||||
event.Start=os.clock()
|
||||
while event.Active==true do
|
||||
event.DO_Order()
|
||||
end
|
||||
event.onClose()
|
||||
return os.clock()-event.Start
|
||||
else
|
||||
return false
|
||||
end
|
||||
end,
|
||||
cManager=function()
|
||||
if event.Active==true then
|
||||
event.DO_Order()
|
||||
end
|
||||
end,
|
||||
uManager=function(dt)
|
||||
if event.CT==false then
|
||||
if dt then
|
||||
event.pump=true
|
||||
end
|
||||
event.CT=true
|
||||
event.onStart()
|
||||
event.Start=os.clock()
|
||||
end
|
||||
event.pumpvar=dt
|
||||
if event.Active==true then
|
||||
event.DO_Order()
|
||||
end
|
||||
end,
|
||||
dManager=function()
|
||||
for ii=1,#event.drawF do
|
||||
event.drawF[ii]()
|
||||
end
|
||||
end,
|
||||
lManager=function()
|
||||
if love.event then
|
||||
love.event.pump()
|
||||
for e,a,b,c,d in love.event.poll() do
|
||||
if e == "quit" then
|
||||
if not love.quit or not love.quit() then
|
||||
if love.audio then
|
||||
love.audio.stop()
|
||||
end
|
||||
return
|
||||
end
|
||||
end
|
||||
love.handlers[e](a,b,c,d)
|
||||
end
|
||||
end
|
||||
if love.timer then
|
||||
love.timer.step()
|
||||
dt = love.timer.getDelta()
|
||||
end
|
||||
if love.update then love.update(dt) end
|
||||
event.uManager(dt)
|
||||
if love.window and love.graphics and love.window.isCreated() then
|
||||
love.graphics.clear()
|
||||
love.graphics.origin()
|
||||
if love.draw then love.draw() end
|
||||
event.dManager()
|
||||
love.graphics.present()
|
||||
end
|
||||
end,
|
||||
benchMark=function(sec,p)
|
||||
p=p or event.Priority_Normal
|
||||
local temp=event.createStep(10)
|
||||
temp.CC=0
|
||||
temp:OnStep(function(pos,step) step.CC=step.CC+1 end)
|
||||
local Loud=event.newAlarm(sec,nil,true)
|
||||
Loud.Link=temp
|
||||
Loud:OnRing(function(alarm) print((alarm.Link.CC).." steps in "..alarm.set.." second(s)") end)
|
||||
temp.Priority=p
|
||||
Loud.Priority=p
|
||||
return Loud
|
||||
end,
|
||||
}
|
||||
365
oldversions/MultiManager(0.3.0).lua
Normal file
365
oldversions/MultiManager(0.3.0).lua
Normal file
@ -0,0 +1,365 @@
|
||||
multi = {}
|
||||
multi.__index = multi
|
||||
multi.Mainloop={}
|
||||
multi.Tasks={}
|
||||
multi.Tasks2={}
|
||||
multi.Garbage={}
|
||||
multi.Children={}
|
||||
multi.Paused={}
|
||||
multi.MasterId=0
|
||||
multi.Active=true
|
||||
multi.Id=-1
|
||||
-- System
|
||||
function multi:newBase(ins)
|
||||
local c = {}
|
||||
setmetatable(c, multi)
|
||||
c.Parent=self
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.Act=function() end
|
||||
if ins then
|
||||
table.insert(multi.Mainloop,ins,c)
|
||||
else
|
||||
table.insert(multi.Mainloop,c)
|
||||
end
|
||||
multi.MasterId=multi.MasterId+1
|
||||
return c
|
||||
end
|
||||
function multi:reboot(r)
|
||||
multi.Mainloop={}
|
||||
multi.Tasks={}
|
||||
multi.Tasks2={}
|
||||
multi.Garbage={}
|
||||
multi.Children={}
|
||||
multi.Paused={}
|
||||
multi.MasterId=0
|
||||
multi.Active=true
|
||||
multi.Id=-1
|
||||
if r then
|
||||
for i,v in pairs(_G) do
|
||||
if type(i)=="table" then
|
||||
if i.Parent and i.Id and i.Act then
|
||||
i={}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
--Processor
|
||||
function multi.Do_Order()
|
||||
for _D=#multi.Mainloop,1,-1 do
|
||||
if multi.Mainloop[_D]~=nil then
|
||||
multi.Mainloop[_D].Id=_D
|
||||
multi.Mainloop[_D]:Act()
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:benchMark(sec)
|
||||
local temp=multi:newStep(2)
|
||||
temp.CC=0
|
||||
temp:OnStep(function(pos,step) step.CC=step.CC+1 end)
|
||||
local Loud=multi:newAlarm(sec)
|
||||
Loud.Link=temp
|
||||
Loud:OnRing(function(alarm) alarm.Link.CC=alarm.Link.CC print((alarm.Link.CC).." steps in "..alarm.set.." second(s)") alarm.bench=alarm.Link.CC alarm.Link:Destroy() alarm:Destroy() end)
|
||||
return Loud
|
||||
end
|
||||
--Helpers
|
||||
function multi:FreeMainEvent()
|
||||
self.func={}
|
||||
end
|
||||
function multi:isPaused()
|
||||
return not(self.Active)
|
||||
end
|
||||
function multi:Pause(n)
|
||||
if not(n) then
|
||||
self.Active=false
|
||||
table.remove(multi.Mainloop,self.Id)
|
||||
table.insert(multi.Paused,self)
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
end
|
||||
function multi:Resume()
|
||||
if self:isPaused() then
|
||||
self.Active=true
|
||||
table.remove(multi.Paused,self.Id)
|
||||
table.insert(multi.Mainloop,self)
|
||||
end
|
||||
end
|
||||
function multi:Remove()
|
||||
self:Pause()
|
||||
self:Destroy()
|
||||
end
|
||||
function multi:Destroy()
|
||||
self:Pause()
|
||||
if self:isPaused() then
|
||||
for i=1,#multi.Paused do
|
||||
if multi.Paused[i]==self then
|
||||
table.remove(multi.Paused,i)
|
||||
return
|
||||
end
|
||||
end
|
||||
else
|
||||
table.remove(multi.Mainloop,self.Id)
|
||||
end
|
||||
self.Act=function() end
|
||||
end
|
||||
function multi:hold(task)
|
||||
self:Pause()
|
||||
if type(task)=="number" then
|
||||
local alarm=multi:newAlarm(task)
|
||||
while alarm.Active==true do
|
||||
if love then
|
||||
multi.lManager()
|
||||
else
|
||||
multi.Do_Order()
|
||||
end
|
||||
end
|
||||
alarm:Destroy()
|
||||
self:Resume()
|
||||
elseif type(task)=="function" then
|
||||
local env=multi:newEvent(task)
|
||||
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
|
||||
while env.Active do
|
||||
if love then
|
||||
multi.lManager()
|
||||
else
|
||||
multi.Do_Order()
|
||||
end
|
||||
end
|
||||
env:Destroy()
|
||||
self:Resume()
|
||||
else
|
||||
print("Error Data Type!!!")
|
||||
end
|
||||
end
|
||||
function multi:oneTime(func,...)
|
||||
for _k=1,#multi.Tasks2 do
|
||||
if multi.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(multi.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
end
|
||||
--Constructors
|
||||
function multi:newEvent(task)
|
||||
local c=multi:newBase()
|
||||
c.Type="Event"
|
||||
c.Task=task or function() end
|
||||
function c:Act()
|
||||
if self.Task(self) and self.Active==true then
|
||||
self:Pause()
|
||||
for _E=1,#self.func do
|
||||
self.func[_E](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnEvent(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newAlarm(set)
|
||||
local c=multi:newBase()
|
||||
c.Type="Alarm"
|
||||
c.timer=os.clock()
|
||||
c.set=set or 0
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Pause()
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:OnRing(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTask(func)
|
||||
table.insert(multi.Tasks,func)
|
||||
end
|
||||
function multi:newLoop()
|
||||
local c=multi:newBase()
|
||||
c.Type="Loop"
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
for i=1,#self.func do
|
||||
self.func[i](os.clock()-multi.Start,self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newStep(start,reset,count,skip)
|
||||
local c=multi:newBase()
|
||||
think=1
|
||||
c.Type="Step"
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:Act()
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
if self.Active==true then
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
c:OnStep(function(p,s)
|
||||
if s.count>0 and s.endAt==p then
|
||||
for fe=1,#s.funcE do
|
||||
s.funcE[fe](s)
|
||||
end
|
||||
s.pos=s.start-1
|
||||
elseif s.count<0 and s.endAt==p then
|
||||
for fe=1,#s.funcE do
|
||||
s.funcE[fe](s)
|
||||
end
|
||||
s.pos=s.start-1
|
||||
end
|
||||
end)
|
||||
return c
|
||||
end
|
||||
function multi:newTStep(start,reset,count,set)
|
||||
local c=multi:newBase()
|
||||
think=1
|
||||
c.Type="TStep"
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=os.clock()
|
||||
c.set=set or 1
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Reset()
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
if self.endAt==self.pos then
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start-1
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
|
||||
function multi:inQueue(func)
|
||||
if self.Id==-1 then
|
||||
print("Error: Can't queue the multi object")
|
||||
return
|
||||
end
|
||||
local c=multi:newBase(self.Id)
|
||||
self.Id=self.Id-1
|
||||
c.Type="Queue"
|
||||
c.Task=func
|
||||
c.Link=self
|
||||
function c:Act()
|
||||
self.Task(self.Link)
|
||||
self:Destroy()
|
||||
end
|
||||
end
|
||||
function multi:newTrigger(func)
|
||||
local c=multi:newBase()
|
||||
c.Type="Trigger"
|
||||
c.trigfunc=func or function() end
|
||||
function c:Fire(...)
|
||||
self:trigfunc(self,...)
|
||||
end
|
||||
return c
|
||||
end
|
||||
--Managers
|
||||
function multi:mainloop()
|
||||
for i=1,#multi.Tasks do
|
||||
multi.Tasks[i]()
|
||||
end
|
||||
multi.Start=os.clock()
|
||||
while self.Active do
|
||||
multi.Do_Order()
|
||||
end
|
||||
end
|
||||
function multi._tFunc(dt)
|
||||
if dt then
|
||||
multi.pump=true
|
||||
end
|
||||
multi.pumpvar=dt
|
||||
multi.Start=os.clock()
|
||||
end
|
||||
function multi:uManager(dt)
|
||||
multi:oneTime(multi._tFunc,dt)
|
||||
multi.Do_Order()
|
||||
end
|
||||
506
oldversions/MultiManager(0.4.0-1).lua
Normal file
506
oldversions/MultiManager(0.4.0-1).lua
Normal file
@ -0,0 +1,506 @@
|
||||
multi = {}
|
||||
multi.Version="4.0.1"
|
||||
multi.__index = multi
|
||||
multi.Mainloop={}
|
||||
multi.Tasks={}
|
||||
multi.Tasks2={}
|
||||
multi.Garbage={}
|
||||
multi.Children={}
|
||||
multi.Paused={}
|
||||
multi.MasterId=0
|
||||
multi.Active=true
|
||||
multi.Id=-1
|
||||
multi.Type="mainint"
|
||||
multi.Rest=0
|
||||
multi._type=type
|
||||
--[[function type(v)
|
||||
local t={}
|
||||
if multi._type(v)=="table" then
|
||||
t=getmetatable(v)
|
||||
if v.Type~=nil then
|
||||
if multi._type(v.Type)=="string" then
|
||||
return v.Type
|
||||
end
|
||||
end
|
||||
end
|
||||
if t.__type~=nil then
|
||||
return t.__type
|
||||
else
|
||||
return multi._type(v)
|
||||
end
|
||||
end]]
|
||||
-- System
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=="\\" then
|
||||
return "windows"
|
||||
else
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
function os.sleep(n)
|
||||
if n > 0 then os.execute("ping -n " .. tonumber(n+1) .. " localhost > NUL") end
|
||||
end
|
||||
else
|
||||
function os.sleep(n)
|
||||
os.execute("sleep " .. tonumber(n))
|
||||
end
|
||||
end
|
||||
function multi:newBase(ins)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
|
||||
local c = {}
|
||||
if self.Type=="int" then
|
||||
setmetatable(c, self.Parent)
|
||||
else
|
||||
setmetatable(c, self)
|
||||
end
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Act=function() end
|
||||
c.Parent=self
|
||||
if ins then
|
||||
table.insert(self.Mainloop,ins,c)
|
||||
else
|
||||
table.insert(self.Mainloop,c)
|
||||
end
|
||||
self.MasterId=self.MasterId+1
|
||||
return c
|
||||
end
|
||||
function multi:reboot(r)
|
||||
self.Mainloop={}
|
||||
self.Tasks={}
|
||||
self.Tasks2={}
|
||||
self.Garbage={}
|
||||
self.Children={}
|
||||
self.Paused={}
|
||||
self.MasterId=0
|
||||
self.Active=true
|
||||
self.Id=-1
|
||||
if r then
|
||||
for i,v in pairs(_G) do
|
||||
if type(i)=="table" then
|
||||
if i.Parent and i.Id and i.Act then
|
||||
i={}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:getChildren()
|
||||
return self.Mainloop
|
||||
end
|
||||
--Processor
|
||||
function multi:Do_Order()
|
||||
for _D=#self.Mainloop,1,-1 do
|
||||
if self.Mainloop[_D]~=nil then
|
||||
self.Mainloop[_D].Id=_D
|
||||
self.Mainloop[_D]:Act()
|
||||
end
|
||||
if self.Mainloop[_D]~=nil then
|
||||
if self.Mainloop[_D].rem~=nil then
|
||||
table.remove(self.Mainloop,_D)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.Rest>0 then
|
||||
os.sleep(self.Rest)
|
||||
end
|
||||
end
|
||||
function multi:benchMark(sec)
|
||||
local temp=self:newLoop(function(t,self)
|
||||
if os.clock()-self.init>self.sec then
|
||||
print(self.c.." steps in "..self.sec.." second(s)")
|
||||
self.tt(self.sec)
|
||||
self:Destroy()
|
||||
else
|
||||
self.c=self.c+1
|
||||
end
|
||||
end)
|
||||
function temp:OnBench(func)
|
||||
self.tt=func
|
||||
end
|
||||
self.tt=function() end
|
||||
temp.sec=sec
|
||||
temp.init=os.clock()
|
||||
temp.c=0
|
||||
return temp
|
||||
end
|
||||
function multi:newInterface()
|
||||
if not(self.Type=="mainint") then error("Can only create an interface on the multi obj") return false end
|
||||
local c = {}
|
||||
setmetatable(c, self)
|
||||
c.Parent=self
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.Type="int"
|
||||
c.Mainloop={}
|
||||
c.Tasks={}
|
||||
c.Tasks2={}
|
||||
c.Garbage={}
|
||||
c.Children={}
|
||||
c.Paused={}
|
||||
c.MasterId=0
|
||||
c.Active=true
|
||||
c.Id=-1
|
||||
c.Rest=0
|
||||
function c:Start()
|
||||
if self.l then
|
||||
self.l:Resume()
|
||||
else
|
||||
self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end)
|
||||
end
|
||||
end
|
||||
function c:Stop()
|
||||
if self.l then
|
||||
self.l:Pause()
|
||||
end
|
||||
end
|
||||
function c:Remove()
|
||||
self:Destroy()
|
||||
self.l:Destroy()
|
||||
end
|
||||
return c
|
||||
end
|
||||
--Helpers
|
||||
function multi:FreeMainEvent()
|
||||
self.func={}
|
||||
end
|
||||
function multi:isPaused()
|
||||
return not(self.Active)
|
||||
end
|
||||
function multi:Pause(n)
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
self.Active=false
|
||||
if not(n) then
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Pause()
|
||||
end
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
else
|
||||
if n==nil then
|
||||
self.Active=false
|
||||
if self.Parent.Mainloop[self.Id]~=nil then
|
||||
table.remove(self.Parent.Mainloop,self.Id)
|
||||
table.insert(self.Parent.Paused,self)
|
||||
self.PId=#self.Parent.Paused
|
||||
end
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:Resume()
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
self.Active=true
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Resume()
|
||||
end
|
||||
else
|
||||
if self:isPaused() then
|
||||
table.remove(self.Parent.Paused,self.PId)
|
||||
table.insert(self.Parent.Mainloop,self)
|
||||
self.Id=#self.Parent.Mainloop
|
||||
self.Active=true
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:Destroy()
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Destroy()
|
||||
end
|
||||
else
|
||||
self.rem=true
|
||||
self.Active=false
|
||||
end
|
||||
end
|
||||
function multi:hold(task)
|
||||
self:Pause()
|
||||
if type(task)=="number" then
|
||||
local alarm=self.Parent:newAlarm(task)
|
||||
while alarm.Active==true do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
alarm:Destroy()
|
||||
self:Resume()
|
||||
elseif type(task)=="function" then
|
||||
local env=self.Parent:newEvent(task)
|
||||
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
|
||||
while env.Active do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
env:Destroy()
|
||||
self:Resume()
|
||||
else
|
||||
print("Error Data Type!!!")
|
||||
end
|
||||
end
|
||||
function multi:oneTime(func,...)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then
|
||||
for _k=1,#self.Parent.Tasks2 do
|
||||
if self.Parent.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(self.Parent.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
else
|
||||
for _k=1,#self.Tasks2 do
|
||||
if self.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(self.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
end
|
||||
end
|
||||
--Constructors
|
||||
function multi:newEvent(task)
|
||||
local c=self:newBase()
|
||||
c.Type="event"
|
||||
c.Task=task or function() end
|
||||
function c:Act()
|
||||
if self.Task(self) and self.Active==true then
|
||||
self:Pause()
|
||||
for _E=1,#self.func do
|
||||
self.func[_E](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnEvent(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newAlarm(set)
|
||||
local c=self:newBase()
|
||||
c.Type="alarm"
|
||||
c.timer=os.clock()
|
||||
c.set=set or 0
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Pause()
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
self.Active=true
|
||||
end
|
||||
function c:OnRing(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTask(func)
|
||||
table.insert(self.Tasks,func)
|
||||
end
|
||||
function multi:newLoop(func)
|
||||
local c=self:newBase()
|
||||
c.Type="loop"
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
for i=1,#self.func do
|
||||
self.func[i](os.clock()-self.Parent.Start,self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newStep(start,reset,count,skip)
|
||||
local c=self:newBase()
|
||||
think=1
|
||||
c.Type="step"
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.funcS={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:Act()
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
if self.Active==true then
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTStep(start,reset,count,set)
|
||||
local c=self:newBase()
|
||||
think=1
|
||||
c.Type="tstep"
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=os.clock()
|
||||
c.set=set or 1
|
||||
c.funcS={}
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Reset()
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTrigger(func)
|
||||
local c=self:newBase()
|
||||
c.Type="trigger"
|
||||
c.trigfunc=func or function() end
|
||||
function c:Fire(...)
|
||||
self:trigfunc(self,...)
|
||||
end
|
||||
return c
|
||||
end
|
||||
--Managers
|
||||
function multi:mainloop()
|
||||
for i=1,#self.Tasks do
|
||||
self.Tasks[i](self)
|
||||
end
|
||||
self.Start=os.clock()
|
||||
while self.Active do
|
||||
self:Do_Order()
|
||||
end
|
||||
end
|
||||
function multi._tFunc(self,dt)
|
||||
for i=1,#self.Tasks do
|
||||
self.Tasks[i](self)
|
||||
end
|
||||
if dt then
|
||||
self.pump=true
|
||||
end
|
||||
self.pumpvar=dt
|
||||
self.Start=os.clock()
|
||||
end
|
||||
function multi:uManager(dt)
|
||||
if self.Active then
|
||||
self:oneTime(self._tFunc,self,dt)
|
||||
self:Do_Order()
|
||||
end
|
||||
end
|
||||
596
oldversions/MultiManager(0.4.0-5).lua
Normal file
596
oldversions/MultiManager(0.4.0-5).lua
Normal file
@ -0,0 +1,596 @@
|
||||
multi = {}
|
||||
multi.Version="4.0.5"
|
||||
multi.__index = multi
|
||||
multi.Mainloop={}
|
||||
multi.Tasks={}
|
||||
multi.Tasks2={}
|
||||
multi.Garbage={}
|
||||
multi.Children={}
|
||||
multi.Paused={}
|
||||
multi.MasterId=0
|
||||
multi.Active=true
|
||||
multi.Id=-1
|
||||
multi.Type="mainint"
|
||||
multi.Rest=0
|
||||
multi._type=type
|
||||
multi.Jobs={}
|
||||
multi.queue={}
|
||||
multi.jobUS=2
|
||||
--[[function type(v)
|
||||
local t={}
|
||||
if multi._type(v)=="table" then
|
||||
t=getmetatable(v)
|
||||
if v.Type~=nil then
|
||||
if multi._type(v.Type)=="string" then
|
||||
return v.Type
|
||||
end
|
||||
end
|
||||
end
|
||||
if t.__type~=nil then
|
||||
return t.__type
|
||||
else
|
||||
return multi._type(v)
|
||||
end
|
||||
end]]
|
||||
-- System
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=="\\" then
|
||||
return "windows"
|
||||
else
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
function os.sleep(n)
|
||||
if n > 0 then os.execute("ping -n " .. tonumber(n+1) .. " localhost > NUL") end
|
||||
end
|
||||
else
|
||||
function os.sleep(n)
|
||||
os.execute("sleep " .. tonumber(n))
|
||||
end
|
||||
end
|
||||
function multi:newBase(ins)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
|
||||
local c = {}
|
||||
if self.Type=="int" then
|
||||
setmetatable(c, self.Parent)
|
||||
else
|
||||
setmetatable(c, self)
|
||||
end
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Act=function() end
|
||||
c.Parent=self
|
||||
if ins then
|
||||
table.insert(self.Mainloop,ins,c)
|
||||
else
|
||||
table.insert(self.Mainloop,c)
|
||||
end
|
||||
self.MasterId=self.MasterId+1
|
||||
return c
|
||||
end
|
||||
function multi:reboot(r)
|
||||
self.Mainloop={}
|
||||
self.Tasks={}
|
||||
self.Tasks2={}
|
||||
self.Garbage={}
|
||||
self.Children={}
|
||||
self.Paused={}
|
||||
self.MasterId=0
|
||||
self.Active=true
|
||||
self.Id=-1
|
||||
if r then
|
||||
for i,v in pairs(_G) do
|
||||
if type(i)=="table" then
|
||||
if i.Parent and i.Id and i.Act then
|
||||
i={}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:getChildren()
|
||||
return self.Mainloop
|
||||
end
|
||||
--Processor
|
||||
function multi:Do_Order()
|
||||
for _D=#self.Mainloop,1,-1 do
|
||||
if self.Mainloop[_D]~=nil then
|
||||
self.Mainloop[_D].Id=_D
|
||||
self.Mainloop[_D]:Act()
|
||||
end
|
||||
if self.Mainloop[_D]~=nil then
|
||||
if self.Mainloop[_D].rem~=nil then
|
||||
table.remove(self.Mainloop,_D)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.Rest~=0 then
|
||||
os.sleep(self.Rest)
|
||||
end
|
||||
end
|
||||
function multi:benchMark(sec)
|
||||
local temp=self:newLoop(function(t,self)
|
||||
if os.clock()-self.init>self.sec then
|
||||
print(self.c.." steps in "..self.sec.." second(s)")
|
||||
self.tt(self.sec)
|
||||
self:Destroy()
|
||||
else
|
||||
self.c=self.c+1
|
||||
end
|
||||
end)
|
||||
function temp:OnBench(func)
|
||||
self.tt=func
|
||||
end
|
||||
self.tt=function() end
|
||||
temp.sec=sec
|
||||
temp.init=os.clock()
|
||||
temp.c=0
|
||||
return temp
|
||||
end
|
||||
function multi:newInterface()
|
||||
if not(self.Type=="mainint") then error("Can only create an interface on the multi obj") return false end
|
||||
local c = {}
|
||||
setmetatable(c, self)
|
||||
c.Parent=self
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.Type="int"
|
||||
c.Mainloop={}
|
||||
c.Tasks={}
|
||||
c.Tasks2={}
|
||||
c.Garbage={}
|
||||
c.Children={}
|
||||
c.Paused={}
|
||||
c.MasterId=0
|
||||
c.Active=true
|
||||
c.Id=-1
|
||||
c.Rest=0
|
||||
c.Jobs={}
|
||||
c.queue={}
|
||||
c.jobUS=2
|
||||
function c:Start()
|
||||
if self.l then
|
||||
self.l:Resume()
|
||||
else
|
||||
self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end)
|
||||
end
|
||||
end
|
||||
function c:Stop()
|
||||
if self.l then
|
||||
self.l:Pause()
|
||||
end
|
||||
end
|
||||
function c:Remove()
|
||||
self:Destroy()
|
||||
self.l:Destroy()
|
||||
end
|
||||
return c
|
||||
end
|
||||
--Helpers
|
||||
function multi:FreeMainEvent()
|
||||
self.func={}
|
||||
end
|
||||
function multi:isPaused()
|
||||
return not(self.Active)
|
||||
end
|
||||
function multi:Pause(n)
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
self.Active=false
|
||||
if not(n) then
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Pause()
|
||||
end
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
else
|
||||
if n==nil then
|
||||
self.Active=false
|
||||
if self.Parent.Mainloop[self.Id]~=nil then
|
||||
table.remove(self.Parent.Mainloop,self.Id)
|
||||
table.insert(self.Parent.Paused,self)
|
||||
self.PId=#self.Parent.Paused
|
||||
end
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:Resume()
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
self.Active=true
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Resume()
|
||||
end
|
||||
else
|
||||
if self:isPaused() then
|
||||
table.remove(self.Parent.Paused,self.PId)
|
||||
table.insert(self.Parent.Mainloop,self)
|
||||
self.Id=#self.Parent.Mainloop
|
||||
self.Active=true
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:Destroy()
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Destroy()
|
||||
end
|
||||
else
|
||||
self.rem=true
|
||||
self.Active=false
|
||||
end
|
||||
end
|
||||
function multi:hold(task)
|
||||
self:Pause()
|
||||
if type(task)=="number" then
|
||||
local alarm=self.Parent:newAlarm(task)
|
||||
while alarm.Active==true do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
alarm:Destroy()
|
||||
self:Resume()
|
||||
elseif type(task)=="function" then
|
||||
local env=self.Parent:newEvent(task)
|
||||
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
|
||||
while env.Active do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
env:Destroy()
|
||||
self:Resume()
|
||||
else
|
||||
print("Error Data Type!!!")
|
||||
end
|
||||
end
|
||||
function multi:oneTime(func,...)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then
|
||||
for _k=1,#self.Parent.Tasks2 do
|
||||
if self.Parent.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(self.Parent.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
else
|
||||
for _k=1,#self.Tasks2 do
|
||||
if self.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(self.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
end
|
||||
end
|
||||
--Constructors
|
||||
function multi:newEvent(task)
|
||||
local c=self:newBase()
|
||||
c.Type="event"
|
||||
c.Task=task or function() end
|
||||
function c:Act()
|
||||
if self.Task(self) and self.Active==true then
|
||||
self:Pause()
|
||||
for _E=1,#self.func do
|
||||
self.func[_E](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnEvent(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newAlarm(set)
|
||||
local c=self:newBase()
|
||||
c.Type="alarm"
|
||||
c.timer=os.clock()
|
||||
c.set=set or 0
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Pause()
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
self.Active=true
|
||||
end
|
||||
function c:OnRing(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTask(func)
|
||||
table.insert(self.Tasks,func)
|
||||
end
|
||||
function multi:newLoop(func)
|
||||
local c=self:newBase()
|
||||
c.Type="loop"
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
for i=1,#self.func do
|
||||
self.func[i](os.clock()-self.Parent.Start,self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newStep(start,reset,count,skip)
|
||||
local c=self:newBase()
|
||||
think=1
|
||||
c.Type="step"
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.funcS={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:Act()
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
if self.Active==true then
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTStep(start,reset,count,set)
|
||||
local c=self:newBase()
|
||||
think=1
|
||||
c.Type="tstep"
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=os.clock()
|
||||
c.set=set or 1
|
||||
c.funcS={}
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Reset()
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTrigger(func)
|
||||
local c={}--self:newBase()
|
||||
c.Type="trigger"
|
||||
c.trigfunc=func or function() end
|
||||
function c:Fire(...)
|
||||
self:trigfunc(self,...)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newConnection()
|
||||
local c={}
|
||||
c.Type="connector"
|
||||
c.func={}
|
||||
function c:Fire(...)
|
||||
for i=1,#self.func do
|
||||
t,e=pcall(self.func[i],...)
|
||||
if not(t) then
|
||||
print(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:connect(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newJob(func,name)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
|
||||
local c = {}
|
||||
if self.Type=="int" then
|
||||
setmetatable(c, self.Parent)
|
||||
else
|
||||
setmetatable(c, self)
|
||||
end
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Parent=self
|
||||
c.Type="job"
|
||||
c.trigfunc=func or function() end
|
||||
function c:Act()
|
||||
self:trigfunc(self)
|
||||
end
|
||||
table.insert(self.Jobs,{c,name})
|
||||
if self.JobRunner==nil then
|
||||
self.JobRunner=self:newAlarm(self.jobUS)
|
||||
self.JobRunner:OnRing(function(self)
|
||||
if #self.Parent.Jobs>0 then
|
||||
if self.Parent.Jobs[1] then
|
||||
self.Parent.Jobs[1][1]:Act()
|
||||
table.remove(self.Parent.Jobs,1)
|
||||
end
|
||||
end
|
||||
self:Reset(self.Parent.jobUS)
|
||||
end)
|
||||
end
|
||||
end
|
||||
function multi:setJobSpeed(n)
|
||||
self.jobUS=n
|
||||
end
|
||||
function multi:hasJobs()
|
||||
return #self.Jobs>0,#self.Jobs
|
||||
end
|
||||
function multi:getJobs()
|
||||
return #self.Jobs
|
||||
end
|
||||
function multi:removeJob(name)
|
||||
for i=#self.Jobs,1,-1 do
|
||||
if self.Jobs[i][2]==name then
|
||||
table.remove(self.Jobs,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
--Incomplete
|
||||
function multi:addToQueue(name,job)
|
||||
if self.queue[name]~=nil then
|
||||
table.insert(self.queue[name],job)
|
||||
else
|
||||
self.queue[name]={}
|
||||
end
|
||||
if self.QRunner==nil then
|
||||
self.QRunner=self:newAlarm(.5)
|
||||
self.QRunner:OnRing(function(self)
|
||||
if #self.Parent.queue>0 then
|
||||
local w=math.random(1,#self.Parent.Jobs)
|
||||
self.Parent.Jobs[w]:Act()
|
||||
table.remove(self.Parent.Jobs,w)
|
||||
end
|
||||
self:Reset()
|
||||
end)
|
||||
end
|
||||
end
|
||||
--Managers
|
||||
function multi:mainloop()
|
||||
for i=1,#self.Tasks do
|
||||
self.Tasks[i](self)
|
||||
end
|
||||
self.Start=os.clock()
|
||||
while self.Active do
|
||||
self:Do_Order()
|
||||
end
|
||||
end
|
||||
function multi._tFunc(self,dt)
|
||||
for i=1,#self.Tasks do
|
||||
self.Tasks[i](self)
|
||||
end
|
||||
if dt then
|
||||
self.pump=true
|
||||
end
|
||||
self.pumpvar=dt
|
||||
self.Start=os.clock()
|
||||
end
|
||||
function multi:uManager(dt)
|
||||
if self.Active then
|
||||
self:oneTime(self._tFunc,self,dt)
|
||||
self:Do_Order()
|
||||
end
|
||||
end
|
||||
767
oldversions/MultiManager(0.5.1-6).lua
Normal file
767
oldversions/MultiManager(0.5.1-6).lua
Normal file
@ -0,0 +1,767 @@
|
||||
multi = {}
|
||||
multi.Version="5.1.6"
|
||||
multi.__index = multi
|
||||
multi.Mainloop={}
|
||||
multi.Tasks={}
|
||||
multi.Tasks2={}
|
||||
multi.Garbage={}
|
||||
multi.Children={}
|
||||
multi.Paused={}
|
||||
multi.MasterId=0
|
||||
multi.Active=true
|
||||
multi.Id=-1
|
||||
multi.Type="mainint"
|
||||
multi.Rest=0
|
||||
multi._type=type
|
||||
multi.Jobs={}
|
||||
multi.queue={}
|
||||
multi.jobUS=2
|
||||
-- System
|
||||
function multi:Stop()
|
||||
self.Active=false
|
||||
end
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=="\\" then
|
||||
return "windows"
|
||||
else
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
function os.sleep(n)
|
||||
if n > 0 then os.execute("ping -n " .. tonumber(n+1) .. " localhost > NUL") end
|
||||
end
|
||||
else
|
||||
function os.sleep(n)
|
||||
os.execute("sleep " .. tonumber(n))
|
||||
end
|
||||
end
|
||||
function multi:newBase(ins)
|
||||
if not(self.Type=="mainint" or self.Type=="int" or self.Type=="stack") then error("Can only create an object on multi or an interface obj") return false end
|
||||
local c = {}
|
||||
if self.Type=="int" or self.Type=="stack" then
|
||||
setmetatable(c, self.Parent)
|
||||
else
|
||||
setmetatable(c, self)
|
||||
end
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.ender={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Act=function() end
|
||||
c.Parent=self
|
||||
if ins then
|
||||
table.insert(self.Mainloop,ins,c)
|
||||
else
|
||||
table.insert(self.Mainloop,c)
|
||||
end
|
||||
self.MasterId=self.MasterId+1
|
||||
return c
|
||||
end
|
||||
function multi:reboot(r)
|
||||
self.Mainloop={}
|
||||
self.Tasks={}
|
||||
self.Tasks2={}
|
||||
self.Garbage={}
|
||||
self.Children={}
|
||||
self.Paused={}
|
||||
self.MasterId=0
|
||||
self.Active=true
|
||||
self.Id=-1
|
||||
if r then
|
||||
for i,v in pairs(_G) do
|
||||
if type(i)=="table" then
|
||||
if i.Parent and i.Id and i.Act then
|
||||
i={}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:getChildren()
|
||||
return self.Mainloop
|
||||
end
|
||||
--Processor
|
||||
function multi:getError()
|
||||
if self.error then
|
||||
return self.error
|
||||
end
|
||||
end
|
||||
function multi:Do_Order()
|
||||
for _D=#self.Mainloop,1,-1 do
|
||||
if self.Mainloop[_D]~=nil then
|
||||
self.Mainloop[_D].Id=_D
|
||||
self.Mainloop[_D]:Act()
|
||||
end
|
||||
if self.Mainloop[_D]~=nil then
|
||||
if self.Mainloop[_D].rem~=nil then
|
||||
table.remove(self.Mainloop,_D)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.Rest~=0 then
|
||||
os.sleep(self.Rest)
|
||||
end
|
||||
end
|
||||
function multi:benchMark(sec)
|
||||
local temp=self:newLoop(function(t,self)
|
||||
if os.clock()-self.init>self.sec then
|
||||
print(self.c.." steps in "..self.sec.." second(s)")
|
||||
self.tt(self.sec)
|
||||
self:Destroy()
|
||||
else
|
||||
self.c=self.c+1
|
||||
end
|
||||
end)
|
||||
function temp:OnBench(func)
|
||||
self.tt=func
|
||||
end
|
||||
self.tt=function() end
|
||||
temp.sec=sec
|
||||
temp.init=os.clock()
|
||||
temp.c=0
|
||||
return temp
|
||||
end
|
||||
function multi:newInterface()
|
||||
if not(self.Type=="mainint") then error("Can only create an interface on the multi obj") return false end
|
||||
local c = {}
|
||||
setmetatable(c, self)
|
||||
c.Parent=self
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.Type="int"
|
||||
c.Mainloop={}
|
||||
c.Tasks={}
|
||||
c.Tasks2={}
|
||||
c.Garbage={}
|
||||
c.Children={}
|
||||
c.Paused={}
|
||||
c.MasterId=0
|
||||
c.Active=true
|
||||
c.Id=-1
|
||||
c.Rest=0
|
||||
c.Jobs={}
|
||||
c.queue={}
|
||||
c.jobUS=2
|
||||
function c:Start()
|
||||
if self.l then
|
||||
self.l:Resume()
|
||||
else
|
||||
self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end)
|
||||
end
|
||||
end
|
||||
function c:Stop()
|
||||
if self.l then
|
||||
self.l:Pause()
|
||||
end
|
||||
end
|
||||
function c:Remove()
|
||||
self:Destroy()
|
||||
self.l:Destroy()
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newStack(file)
|
||||
local c=self:newInterface()
|
||||
c.Type="stack"
|
||||
stack=c
|
||||
c.last={}
|
||||
c.funcE={}
|
||||
if file then
|
||||
dofile(file)
|
||||
end
|
||||
function c:OnStackCompleted(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
--Helpers
|
||||
function multi:protect()
|
||||
function self:Do_Order()
|
||||
for _D=#self.Mainloop,1,-1 do
|
||||
if self.Mainloop[_D]~=nil then
|
||||
self.Mainloop[_D].Id=_D
|
||||
local status, err=pcall(self.Mainloop[_D].Act,self.Mainloop[_D])
|
||||
if err and not(self.Mainloop[_D].error) then
|
||||
self.Mainloop[_D].error=err
|
||||
print(err..": Ingoring error continuing...")
|
||||
end
|
||||
end
|
||||
if self.Mainloop[_D]~=nil then
|
||||
if self.Mainloop[_D].rem~=nil then
|
||||
table.remove(self.Mainloop,_D)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.Rest~=0 then
|
||||
os.sleep(self.Rest)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:unProtect()
|
||||
function self:Do_Order()
|
||||
for _D=#self.Mainloop,1,-1 do
|
||||
if self.Mainloop[_D]~=nil then
|
||||
self.Mainloop[_D].Id=_D
|
||||
self.Mainloop[_D]:Act()
|
||||
end
|
||||
if self.Mainloop[_D]~=nil then
|
||||
if self.Mainloop[_D].rem~=nil then
|
||||
table.remove(self.Mainloop,_D)
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.Rest~=0 then
|
||||
os.sleep(self.Rest)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:setJobSpeed(n)
|
||||
self.jobUS=n
|
||||
end
|
||||
function multi:hasJobs()
|
||||
return #self.Jobs>0,#self.Jobs
|
||||
end
|
||||
function multi:getJobs()
|
||||
return #self.Jobs
|
||||
end
|
||||
function multi:removeJob(name)
|
||||
for i=#self.Jobs,1,-1 do
|
||||
if self.Jobs[i][2]==name then
|
||||
table.remove(self.Jobs,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:FreeMainEvent()
|
||||
self.func={}
|
||||
end
|
||||
function multi:connectFinal(func)
|
||||
if self.Type=="event" then
|
||||
self:OnEvent(func)
|
||||
elseif self.Type=="alarm" then
|
||||
self:OnRing(func)
|
||||
elseif self.Type=="step" or self.Type=="tstep" then
|
||||
self:OnEnd(func)
|
||||
elseif self.Type=="loop" then
|
||||
self:OnBreak(func)
|
||||
else
|
||||
error("No final event exists for: "..self.Type)
|
||||
end
|
||||
end
|
||||
function multi:Break()
|
||||
self:Pause()
|
||||
self.Active=nil
|
||||
for i=1,#self.ender do
|
||||
self.ender[i](self)
|
||||
end
|
||||
end
|
||||
function multi:OnBreak(func)
|
||||
table.insert(self.ender,func)
|
||||
end
|
||||
function multi:isPaused()
|
||||
return not(self.Active)
|
||||
end
|
||||
function multi:Pause(n)
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
self.Active=false
|
||||
if not(n) then
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Pause()
|
||||
end
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
else
|
||||
if n==nil then
|
||||
self.Active=false
|
||||
if self.Parent.Mainloop[self.Id]~=nil then
|
||||
table.remove(self.Parent.Mainloop,self.Id)
|
||||
table.insert(self.Parent.Paused,self)
|
||||
self.PId=#self.Parent.Paused
|
||||
end
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:Resume()
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
self.Active=true
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Resume()
|
||||
end
|
||||
else
|
||||
if self:isPaused() then
|
||||
table.remove(self.Parent.Paused,self.PId)
|
||||
table.insert(self.Parent.Mainloop,self)
|
||||
self.Id=#self.Parent.Mainloop
|
||||
self.Active=true
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:Destroy()
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Destroy()
|
||||
end
|
||||
else
|
||||
self.rem=true
|
||||
self.Active=false
|
||||
end
|
||||
end
|
||||
function multi:hold(task)
|
||||
self:Pause()
|
||||
if type(task)=="number" then
|
||||
local alarm=self.Parent:newAlarm(task)
|
||||
while alarm.Active==true do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
alarm:Destroy()
|
||||
self:Resume()
|
||||
elseif type(task)=="function" then
|
||||
local env=self.Parent:newEvent(task)
|
||||
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
|
||||
while env.Active do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
env:Destroy()
|
||||
self:Resume()
|
||||
else
|
||||
print("Error Data Type!!!")
|
||||
end
|
||||
end
|
||||
function multi:oneTime(func,...)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then
|
||||
for _k=1,#self.Parent.Tasks2 do
|
||||
if self.Parent.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(self.Parent.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
else
|
||||
for _k=1,#self.Tasks2 do
|
||||
if self.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(self.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
end
|
||||
end
|
||||
function multi:Reset(n)
|
||||
self:Resume()
|
||||
end
|
||||
function multi:isDone()
|
||||
return self.Active~=true
|
||||
end
|
||||
--Constructors
|
||||
function multi:newEvent(task)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
c.Type="event"
|
||||
c.Task=task or function() end
|
||||
function c:Act()
|
||||
if self.Task(self) and self.Active==true then
|
||||
self:Pause()
|
||||
for _E=1,#self.func do
|
||||
self.func[_E](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnEvent(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
self:Destroy()
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newAlarm(set)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
c.Type="alarm"
|
||||
c.timer=os.clock()
|
||||
c.set=set or 0
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Pause()
|
||||
self.Active=false
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:Resume()
|
||||
self.Parent.Resume(self)
|
||||
self.timer=os.clock()
|
||||
self.Active=true
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
self.Active=true
|
||||
end
|
||||
function c:OnRing(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
table.remove(self.Parent.Mainloop,#self.Parent.Mainloop)
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTask(func)
|
||||
table.insert(self.Tasks,func)
|
||||
end
|
||||
function multi:newLoop(func)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
c.Type="loop"
|
||||
c.Start=os.clock()
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
for i=1,#self.func do
|
||||
self.func[i](os.clock()-self.Start,self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
self:Destroy()
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newStep(start,reset,count,skip)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
think=1
|
||||
c.Type="step"
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.funcS={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:Act()
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
if self.Active==true then
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
self:Destroy()
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTStep(start,reset,count,set)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
think=1
|
||||
c.Type="tstep"
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=os.clock()
|
||||
c.set=set or 1
|
||||
c.funcS={}
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active then
|
||||
if os.clock()-self.timer>=self.set then
|
||||
self:Reset()
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=os.clock()
|
||||
self:Resume()
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
self:Destroy()
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTrigger(func)
|
||||
local c={}
|
||||
c.Type="trigger"
|
||||
c.trigfunc=func or function() end
|
||||
function c:Fire(...)
|
||||
self:trigfunc(self,...)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newConnection()
|
||||
local c={}
|
||||
c.Type="connector"
|
||||
c.func={}
|
||||
function c:Fire(...)
|
||||
for i=1,#self.func do
|
||||
t,e=pcall(self.func[i],...)
|
||||
if not(t) then
|
||||
print(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:bind(t)
|
||||
self.func=t
|
||||
end
|
||||
function c:connect(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newJob(func,name)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
|
||||
local c = {}
|
||||
if self.Type=="int" then
|
||||
setmetatable(c, self.Parent)
|
||||
else
|
||||
setmetatable(c, self)
|
||||
end
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Parent=self
|
||||
c.Type="job"
|
||||
c.trigfunc=func or function() end
|
||||
function c:Act()
|
||||
self:trigfunc(self)
|
||||
end
|
||||
table.insert(self.Jobs,{c,name})
|
||||
if self.JobRunner==nil then
|
||||
self.JobRunner=self:newAlarm(self.jobUS)
|
||||
self.JobRunner:OnRing(function(self)
|
||||
if #self.Parent.Jobs>0 then
|
||||
if self.Parent.Jobs[1] then
|
||||
self.Parent.Jobs[1][1]:Act()
|
||||
table.remove(self.Parent.Jobs,1)
|
||||
end
|
||||
end
|
||||
self:Reset(self.Parent.jobUS)
|
||||
end)
|
||||
end
|
||||
end
|
||||
--Managers
|
||||
function multi:mainloop()
|
||||
for i=1,#self.Tasks do
|
||||
self.Tasks[i](self)
|
||||
end
|
||||
self.Start=os.clock()
|
||||
while self.Active do
|
||||
self:Do_Order()
|
||||
end
|
||||
end
|
||||
function multi._tFunc(self,dt)
|
||||
for i=1,#self.Tasks do
|
||||
self.Tasks[i](self)
|
||||
end
|
||||
if dt then
|
||||
self.pump=true
|
||||
end
|
||||
self.pumpvar=dt
|
||||
self.Start=os.clock()
|
||||
end
|
||||
function multi:uManager(dt)
|
||||
if self.Active then
|
||||
self:oneTime(self._tFunc,self,dt)
|
||||
self:Do_Order()
|
||||
end
|
||||
end
|
||||
929
oldversions/MultiManager(0.6.1-6).lua
Normal file
929
oldversions/MultiManager(0.6.1-6).lua
Normal file
@ -0,0 +1,929 @@
|
||||
multi = {}
|
||||
multi.Version={6,1,6}-- History: EventManager,EventManager+,MultiManager <-- Current
|
||||
multi.stage="stable"
|
||||
multi.Features=multi.Version[1].."."..multi.Version[2].."."..multi.Version[3].." "..multi.stage..[[
|
||||
Objects:
|
||||
Event
|
||||
Alarm
|
||||
Loop
|
||||
Step
|
||||
TStep
|
||||
Trigger
|
||||
Task
|
||||
Connection
|
||||
Timer
|
||||
Job
|
||||
]]
|
||||
multi.__index = multi
|
||||
multi.Mainloop={}
|
||||
multi.Tasks={}
|
||||
multi.Tasks2={}
|
||||
multi.Garbage={}
|
||||
multi.Children={}
|
||||
multi.Paused={}
|
||||
multi.Active=true
|
||||
multi.Id=-1
|
||||
multi.Type="mainint"
|
||||
multi.Rest=0
|
||||
multi._type=type
|
||||
multi.Jobs={}
|
||||
multi.queue={}
|
||||
multi.jobUS=2
|
||||
multi.clock=os.clock
|
||||
multi.time=os.time
|
||||
-- System
|
||||
function multi:Stop()
|
||||
self.Active=false
|
||||
end
|
||||
function os.getOS()
|
||||
if package.config:sub(1,1)=="\\" then
|
||||
return "windows"
|
||||
else
|
||||
return "unix"
|
||||
end
|
||||
end
|
||||
if os.getOS()=="windows" then
|
||||
function os.sleep(n)
|
||||
if n > 0 then os.execute("ping -n " .. tonumber(n+1) .. " localhost > NUL") end
|
||||
end
|
||||
else
|
||||
function os.sleep(n)
|
||||
os.execute("sleep " .. tonumber(n))
|
||||
end
|
||||
end
|
||||
function multi.executeFunction(name,...)
|
||||
if type(_G[name])=="function" then
|
||||
_G[name](...)
|
||||
else
|
||||
print("Error: Not a function")
|
||||
end
|
||||
end
|
||||
function multi:newBase(ins)
|
||||
if not(self.Type=="mainint" or self.Type=="int" or self.Type=="stack") then error("Can only create an object on multi or an interface obj") return false end
|
||||
local c = {}
|
||||
if self.Type=="int" or self.Type=="stack" then
|
||||
setmetatable(c, self.Parent)
|
||||
else
|
||||
setmetatable(c, self)
|
||||
end
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.ender={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Act=function() end
|
||||
c.Parent=self
|
||||
if ins then
|
||||
table.insert(self.Mainloop,ins,c)
|
||||
else
|
||||
table.insert(self.Mainloop,c)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:reboot(r)
|
||||
local before=collectgarbage("count")
|
||||
self.Mainloop={}
|
||||
self.Tasks={}
|
||||
self.Tasks2={}
|
||||
self.Garbage={}
|
||||
self.Children={}
|
||||
self.Paused={}
|
||||
self.Active=true
|
||||
self.Id=-1
|
||||
if r then
|
||||
for i,v in pairs(_G) do
|
||||
if type(i)=="table" then
|
||||
if i.Parent and i.Id and i.Act then
|
||||
i={}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
collectgarbage()
|
||||
local after=collectgarbage("count")
|
||||
print([[Before rebooting total Ram used was ]]..before..[[Kb
|
||||
After rebooting total Ram used is ]]..after..[[ Kb
|
||||
A total of ]]..(before-after)..[[Kb was cleaned up]])
|
||||
end
|
||||
function multi:getChildren()
|
||||
return self.Mainloop
|
||||
end
|
||||
--Processor
|
||||
function multi:getError()
|
||||
if self.error then
|
||||
return self.error
|
||||
end
|
||||
end
|
||||
function multi:Do_Order()
|
||||
local Loop=self.Mainloop
|
||||
for _D=#Loop,1,-1 do
|
||||
if Loop[_D]~=nil then
|
||||
Loop[_D].Id=_D
|
||||
Loop[_D]:Act()
|
||||
end
|
||||
end
|
||||
if self.Rest~=0 then
|
||||
os.sleep(self.Rest)
|
||||
end
|
||||
end
|
||||
function multi:fromfile(path,int)
|
||||
int=int or multi
|
||||
local test2={}
|
||||
local test=bin.load(path)
|
||||
local tp=test:getBlock("s")
|
||||
if tp=="event" then
|
||||
test2=int:newEvent(test:getBlock("f"))
|
||||
local t=test:getBlock("t")
|
||||
for i=1,#t do
|
||||
test2:OnEvent(t[i])
|
||||
end
|
||||
elseif tp=="alarm" then
|
||||
test2=int:newAlarm(test:getBlock("n"))
|
||||
elseif tp=="loop" then
|
||||
test2=int:newLoop(test:getBlock("t")[1])
|
||||
elseif tp=="step" or tp=="tstep" then
|
||||
local func=test:getBlock("t")
|
||||
local funcE=test:getBlock("t")
|
||||
local funcS=test:getBlock("t")
|
||||
local tab=test:getBlock("t")
|
||||
test2=int:newStep()
|
||||
table.merge(test2,tab)
|
||||
test2.funcE=funcE
|
||||
test2.funcS=funcS
|
||||
test2.func=func
|
||||
elseif tp=="trigger" then
|
||||
test2=int:newTrigger(test:getBlock("f"))
|
||||
elseif tp=="connector" then
|
||||
test2=int:newConnection()
|
||||
test2.func=test:getBlock("t")
|
||||
elseif tp=="timer" then
|
||||
test2=int:newTimer()
|
||||
test2.count=tonumber(test:getBlock("n"))
|
||||
else
|
||||
print("Error: The file you selected is not a valid multi file object!")
|
||||
return false
|
||||
end
|
||||
return test2
|
||||
end
|
||||
function multi:benchMark(sec)
|
||||
local temp=self:newLoop(function(t,self)
|
||||
if multi.clock()-self.init>self.sec then
|
||||
print(self.c.." steps in "..self.sec.." second(s)")
|
||||
self.tt(self.sec)
|
||||
self:Destroy()
|
||||
else
|
||||
self.c=self.c+1
|
||||
end
|
||||
end)
|
||||
function temp:OnBench(func)
|
||||
self.tt=func
|
||||
end
|
||||
self.tt=function() end
|
||||
temp.sec=sec
|
||||
temp.init=multi.clock()
|
||||
temp.c=0
|
||||
return temp
|
||||
end
|
||||
function multi:tofile(path)
|
||||
local items=self:getChildren()
|
||||
io.mkDir(io.getName(path))
|
||||
for i=1,#items do
|
||||
items[i]:tofile(io.getName(path).."\\item"..item[i]..".dat")
|
||||
end
|
||||
local int=bin.new()
|
||||
int:addBlock("int")
|
||||
int:addBlock(io.getName(path))
|
||||
int:addBlock(#self.Mainloop)
|
||||
int:addBlock(self.Active)
|
||||
int:addBlock(self.Rest)
|
||||
int:addBlock(self.Jobs)
|
||||
int:tofile()
|
||||
end
|
||||
function multi:newInterface(file)
|
||||
if not(self.Type=="mainint") then error("Can only create an interface on the multi obj") return false end
|
||||
local c = {}
|
||||
setmetatable(c, self)
|
||||
c.Parent=self
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.Type="int"
|
||||
c.Mainloop={}
|
||||
c.Tasks={}
|
||||
c.Tasks2={}
|
||||
c.Garbage={}
|
||||
c.Children={}
|
||||
c.Paused={}
|
||||
c.Active=true
|
||||
c.Id=-1
|
||||
c.Rest=0
|
||||
c.Jobs={}
|
||||
c.queue={}
|
||||
c.jobUS=2
|
||||
function c:Start()
|
||||
if self.l then
|
||||
self.l:Resume()
|
||||
else
|
||||
self.l=self.Parent:newLoop(function(dt) c:uManager(dt) end)
|
||||
end
|
||||
end
|
||||
function c:Stop()
|
||||
if self.l then
|
||||
self.l:Pause()
|
||||
end
|
||||
end
|
||||
function c:Remove()
|
||||
self:Destroy()
|
||||
self.l:Destroy()
|
||||
end
|
||||
if file then
|
||||
multi.Cself=c
|
||||
loadstring("interface=multi.Cself "..io.open(file,"rb"):read("*all"))()
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newStack(file)
|
||||
local c=self:newInterface()
|
||||
c.Type="stack"
|
||||
c.last={}
|
||||
c.funcE={}
|
||||
if file then
|
||||
multi.Cself=c
|
||||
loadstring("stack=multi.Cself "..io.open(file,"rb"):read("*all"))()
|
||||
end
|
||||
function c:OnStackCompleted(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
return c
|
||||
end
|
||||
--Helpers
|
||||
function multi:protect()
|
||||
function self:Do_Order()
|
||||
local Loop=self.Mainloop
|
||||
for _D=#Loop,1,-1 do
|
||||
if Loop[_D]~=nil then
|
||||
Loop[_D].Id=_D
|
||||
local status, err=pcall(Loop[_D].Act,Loop[_D])
|
||||
if err and not(Loop[_D].error) then
|
||||
Loop[_D].error=err
|
||||
print(err..": Ingoring error continuing...")
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.Rest~=0 then
|
||||
os.sleep(self.Rest)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:unProtect()
|
||||
function self:Do_Order()
|
||||
for _D=#Loop,1,-1 do
|
||||
if Loop[_D]~=nil then
|
||||
Loop[_D].Id=_D
|
||||
Loop[_D]:Act()
|
||||
end
|
||||
end
|
||||
if self.Rest~=0 then
|
||||
os.sleep(self.Rest)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:setJobSpeed(n)
|
||||
self.jobUS=n
|
||||
end
|
||||
function multi:hasJobs()
|
||||
return #self.Jobs>0,#self.Jobs
|
||||
end
|
||||
function multi:getJobs()
|
||||
return #self.Jobs
|
||||
end
|
||||
function multi:removeJob(name)
|
||||
for i=#self.Jobs,1,-1 do
|
||||
if self.Jobs[i][2]==name then
|
||||
table.remove(self.Jobs,i)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:FreeMainEvent()
|
||||
self.func={}
|
||||
end
|
||||
function multi:connectFinal(func)
|
||||
if self.Type=="event" then
|
||||
self:OnEvent(func)
|
||||
elseif self.Type=="alarm" then
|
||||
self:OnRing(func)
|
||||
elseif self.Type=="step" or self.Type=="tstep" then
|
||||
self:OnEnd(func)
|
||||
elseif self.Type=="loop" then
|
||||
self:OnBreak(func)
|
||||
else
|
||||
error("No final event exists for: "..self.Type)
|
||||
end
|
||||
end
|
||||
function multi:Break()
|
||||
self:Pause()
|
||||
self.Active=nil
|
||||
for i=1,#self.ender do
|
||||
self.ender[i](self)
|
||||
end
|
||||
end
|
||||
function multi:OnBreak(func)
|
||||
table.insert(self.ender,func)
|
||||
end
|
||||
function multi:isPaused()
|
||||
return not(self.Active)
|
||||
end
|
||||
function multi:Pause(n)
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
self.Active=false
|
||||
if not(n) then
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Pause()
|
||||
end
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
else
|
||||
if n==nil then
|
||||
self.Active=false
|
||||
if self.Parent.Mainloop[self.Id]~=nil then
|
||||
table.remove(self.Parent.Mainloop,self.Id)
|
||||
table.insert(self.Parent.Paused,self)
|
||||
self.PId=#self.Parent.Paused
|
||||
end
|
||||
else
|
||||
self:hold(n)
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:Resume()
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
self.Active=true
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Resume()
|
||||
end
|
||||
else
|
||||
if self:isPaused() then
|
||||
table.remove(self.Parent.Paused,self.PId)
|
||||
table.insert(self.Parent.Mainloop,self)
|
||||
self.Id=#self.Parent.Mainloop
|
||||
self.Active=true
|
||||
end
|
||||
end
|
||||
end
|
||||
function multi:resurrect()
|
||||
table.insert(self.Parent.Mainloop,self)
|
||||
self.Active=true
|
||||
end
|
||||
function multi:Destroy()
|
||||
if self.Type=="int" or self.Type=="mainint" then
|
||||
local c=self:getChildren()
|
||||
for i=1,#c do
|
||||
c[i]:Destroy()
|
||||
end
|
||||
else
|
||||
for i=1,#self.Parent.Mainloop do
|
||||
if self.Parent.Mainloop[i]==self then
|
||||
table.remove(self.Parent.Mainloop,i)
|
||||
break
|
||||
end
|
||||
end
|
||||
self.Active=false
|
||||
end
|
||||
end
|
||||
function multi:hold(task)
|
||||
self:Pause()
|
||||
if type(task)=="number" then
|
||||
local alarm=self.Parent:newAlarm(task)
|
||||
while alarm.Active==true do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
alarm:Destroy()
|
||||
self:Resume()
|
||||
elseif type(task)=="function" then
|
||||
local env=self.Parent:newEvent(task)
|
||||
env:OnEvent(function(envt) envt:Pause() envt:Stop() end)
|
||||
while env.Active do
|
||||
if love then
|
||||
self.Parent:lManager()
|
||||
else
|
||||
self.Parent:Do_Order()
|
||||
end
|
||||
end
|
||||
env:Destroy()
|
||||
self:Resume()
|
||||
else
|
||||
print("Error Data Type!!!")
|
||||
end
|
||||
end
|
||||
function multi:oneTime(func,...)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then
|
||||
for _k=1,#self.Parent.Tasks2 do
|
||||
if self.Parent.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(self.Parent.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
else
|
||||
for _k=1,#self.Tasks2 do
|
||||
if self.Tasks2[_k]==func then
|
||||
return false
|
||||
end
|
||||
end
|
||||
table.insert(self.Tasks2,func)
|
||||
func(...)
|
||||
return true
|
||||
end
|
||||
end
|
||||
function multi:Reset(n)
|
||||
self:Resume()
|
||||
end
|
||||
function multi:isDone()
|
||||
return self.Active~=true
|
||||
end
|
||||
--Constructors
|
||||
function multi:newEvent(task)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
c.Type="event"
|
||||
c.Task=task or function() end
|
||||
function c:Act()
|
||||
if self.Task(self) and self.Active==true then
|
||||
self:Pause()
|
||||
for _E=1,#self.func do
|
||||
self.func[_E](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnEvent(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.Task)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
self:Destroy()
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newAlarm(set)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
c.Type="alarm"
|
||||
c.timer=self:newTimer()
|
||||
c.set=set or 0
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.set)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
if self.timer:Get()>=self.set then
|
||||
self:Pause()
|
||||
self.Active=false
|
||||
for i=1,#self.func do
|
||||
self.func[i](self)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:Resume()
|
||||
self.Parent.Resume(self)
|
||||
self.timer:Resume()
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self:Resume()
|
||||
self.timer:Reset()
|
||||
end
|
||||
function c:OnRing(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:Pause()
|
||||
self.timer:Pause()
|
||||
self.Parent.Pause(self)
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
c:Pause()
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
table.remove(self.Parent.Mainloop,#self.Parent.Mainloop)
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
else
|
||||
c.timer:Start()
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTimer()
|
||||
local c={}
|
||||
c.Type="timer"
|
||||
c.time=0
|
||||
c.count=0
|
||||
function c:Start()
|
||||
self.time=multi.clock()
|
||||
end
|
||||
function c:Get()
|
||||
return (multi.clock()-self.time)+self.count
|
||||
end
|
||||
c.Reset=c.Start
|
||||
function c:Pause()
|
||||
self.time=self:Get()
|
||||
end
|
||||
function c:Resume()
|
||||
self.time=multi.clock()-self.time
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
self.count=self.count+self:Get()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.count)
|
||||
m:tofile(path)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTask(func)
|
||||
table.insert(self.Tasks,func)
|
||||
end
|
||||
function multi:newLoop(func)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
c.Type="loop"
|
||||
c.Start=multi.clock()
|
||||
if func then
|
||||
c.func={func}
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active==true then
|
||||
for i=1,#self.func do
|
||||
self.func[i](multi.clock()-self.Start,self)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnLoop(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
self:Destroy()
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newStep(start,reset,count,skip)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
think=1
|
||||
c.Type="step"
|
||||
c.pos=start or 1
|
||||
c.endAt=reset or math.huge
|
||||
c.skip=skip or 0
|
||||
c.spos=0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.funcS={}
|
||||
c.start=start or 1
|
||||
if start~=nil and reset~=nil then
|
||||
if start>reset then
|
||||
think=-1
|
||||
end
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,spos=self.spos,count=self.count,start=self.start})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
if self~=nil then
|
||||
if self.spos==0 then
|
||||
if self.Active==true then
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.spos=self.spos+1
|
||||
if self.spos>=self.skip then
|
||||
self.spos=0
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,1,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Update(start,reset,count,skip)
|
||||
self.start=start or self.start
|
||||
self.endAt=reset or self.endAt
|
||||
self.skip=skip or self.skip
|
||||
self.count=count or self.count
|
||||
self:Resume()
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
self:Destroy()
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTStep(start,reset,count,set)
|
||||
local c={}
|
||||
if self.Type=="stack" then
|
||||
c=self:newBase(1)
|
||||
self.last=c
|
||||
else
|
||||
c=self:newBase()
|
||||
end
|
||||
think=1
|
||||
c.Type="tstep"
|
||||
c.start=start or 1
|
||||
local reset = reset or math.huge
|
||||
c.endAt=reset
|
||||
c.pos=start or 1
|
||||
c.skip=skip or 0
|
||||
c.count=count or 1*think
|
||||
c.funcE={}
|
||||
c.timer=multi.clock()
|
||||
c.set=set or 1
|
||||
c.funcS={}
|
||||
function c:Update(start,reset,count,set)
|
||||
self.start=start or self.start
|
||||
self.pos=start
|
||||
self.endAt=reset or self.endAt
|
||||
self.set=set or self.set
|
||||
self.count=count or self.count or 1
|
||||
self.timer=multi.clock()
|
||||
self:Resume()
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:addBlock(self.funcE)
|
||||
m:addBlock(self.funcS)
|
||||
m:addBlock({pos=self.pos,endAt=self.endAt,skip=self.skip,timer=self.timer,count=self.count,start=self.start,set=self.set})
|
||||
m:addBlock(self.Active)
|
||||
m:tofile(path)
|
||||
end
|
||||
function c:Act()
|
||||
if self.Active then
|
||||
if multi.clock()-self.timer>=self.set then
|
||||
self:Reset()
|
||||
if self.pos==self.start then
|
||||
for fe=1,#self.funcS do
|
||||
self.funcS[fe](self)
|
||||
end
|
||||
end
|
||||
for i=1,#self.func do
|
||||
self.func[i](self.pos,self)
|
||||
end
|
||||
self.pos=self.pos+self.count
|
||||
if self.pos-self.count==self.endAt then
|
||||
self:Pause()
|
||||
for fe=1,#self.funcE do
|
||||
self.funcE[fe](self)
|
||||
end
|
||||
self.pos=self.start
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:OnStart(func)
|
||||
table.insert(self.funcS,func)
|
||||
end
|
||||
function c:OnStep(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:OnEnd(func)
|
||||
table.insert(self.funcE,func)
|
||||
end
|
||||
function c:Break()
|
||||
self.Active=nil
|
||||
end
|
||||
function c:Reset(n)
|
||||
if n then self.set=n end
|
||||
self.timer=multi.clock()
|
||||
self:Resume()
|
||||
end
|
||||
if self.Type=="stack" then
|
||||
if #self.Mainloop>1 then
|
||||
c:Pause()
|
||||
end
|
||||
c:connectFinal(function(self)
|
||||
if self.Parent.last==self then
|
||||
for i=1,#self.Parent.funcE do
|
||||
self.Parent.funcE[i](self)
|
||||
end
|
||||
self.Parent:Remove()
|
||||
end
|
||||
self:Destroy()
|
||||
self.Parent.Mainloop[#self.Parent.Mainloop]:Resume()
|
||||
end)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newTrigger(func)
|
||||
local c={}
|
||||
c.Type="trigger"
|
||||
c.trigfunc=func or function() end
|
||||
function c:Fire(...)
|
||||
self:trigfunc(self,...)
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.trigfunc)
|
||||
m:tofile(path)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newConnection()
|
||||
local c={}
|
||||
c.Type="connector"
|
||||
c.func={}
|
||||
function c:Fire(...)
|
||||
for i=1,#self.func do
|
||||
t,e=pcall(self.func[i],...)
|
||||
if not(t) then
|
||||
print(e)
|
||||
end
|
||||
end
|
||||
end
|
||||
function c:bind(t)
|
||||
self.func=t
|
||||
end
|
||||
function c:connect(func)
|
||||
table.insert(self.func,func)
|
||||
end
|
||||
function c:tofile(path)
|
||||
local m=bin.new()
|
||||
m:addBlock(self.Type)
|
||||
m:addBlock(self.func)
|
||||
m:tofile(path)
|
||||
end
|
||||
return c
|
||||
end
|
||||
function multi:newJob(func,name)
|
||||
if not(self.Type=="mainint" or self.Type=="int") then error("Can only create an object on multi or an interface obj") return false end
|
||||
local c = {}
|
||||
if self.Type=="int" then
|
||||
setmetatable(c, self.Parent)
|
||||
else
|
||||
setmetatable(c, self)
|
||||
end
|
||||
c.Active=true
|
||||
c.func={}
|
||||
c.Id=0
|
||||
c.PId=0
|
||||
c.Parent=self
|
||||
c.Type="job"
|
||||
c.trigfunc=func or function() end
|
||||
function c:Act()
|
||||
self:trigfunc(self)
|
||||
end
|
||||
table.insert(self.Jobs,{c,name})
|
||||
if self.JobRunner==nil then
|
||||
self.JobRunner=self:newAlarm(self.jobUS)
|
||||
self.JobRunner:OnRing(function(self)
|
||||
if #self.Parent.Jobs>0 then
|
||||
if self.Parent.Jobs[1] then
|
||||
self.Parent.Jobs[1][1]:Act()
|
||||
table.remove(self.Parent.Jobs,1)
|
||||
end
|
||||
end
|
||||
self:Reset(self.Parent.jobUS)
|
||||
end)
|
||||
end
|
||||
end
|
||||
--Managers
|
||||
function multi:mainloop()
|
||||
for i=1,#self.Tasks do
|
||||
self.Tasks[i](self)
|
||||
end
|
||||
rawset(self,"Start",multi.clock())
|
||||
while self.Active do
|
||||
self:Do_Order()
|
||||
end
|
||||
end
|
||||
function multi._tFunc(self,dt)
|
||||
for i=1,#self.Tasks do
|
||||
self.Tasks[i](self)
|
||||
end
|
||||
if dt then
|
||||
self.pump=true
|
||||
end
|
||||
self.pumpvar=dt
|
||||
rawset(self,"Start",multi.clock())
|
||||
end
|
||||
function multi:uManager(dt)
|
||||
if self.Active then
|
||||
self:oneTime(self._tFunc,self,dt)
|
||||
self:Do_Order()
|
||||
end
|
||||
end
|
||||
1056
oldversions/MultiManager(0.6.2).lua
Normal file
1056
oldversions/MultiManager(0.6.2).lua
Normal file
File diff suppressed because it is too large
Load Diff
1112
oldversions/MultiManager(0.6.3).lua
Normal file
1112
oldversions/MultiManager(0.6.3).lua
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1391
oldversions/MultiManager(1.0.1).lua
Normal file
1391
oldversions/MultiManager(1.0.1).lua
Normal file
File diff suppressed because it is too large
Load Diff
1434
oldversions/MultiManager(1.2.0).lua
Normal file
1434
oldversions/MultiManager(1.2.0).lua
Normal file
File diff suppressed because it is too large
Load Diff
1460
oldversions/MultiManager(1.3.0).lua
Normal file
1460
oldversions/MultiManager(1.3.0).lua
Normal file
File diff suppressed because it is too large
Load Diff
1518
oldversions/MultiManager(1.4.1).lua
Normal file
1518
oldversions/MultiManager(1.4.1).lua
Normal file
File diff suppressed because it is too large
Load Diff
33
rockspecs/multi-1.10-0.rockspec
Normal file
33
rockspecs/multi-1.10-0.rockspec
Normal file
@ -0,0 +1,33 @@
|
||||
package = "multi"
|
||||
version = "1.10.0"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.10.0",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multiobjs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1 due to love2d and lua lanes and now luvit (See ReadMe on gotchas) being lua 5.1 only!
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
"bin",
|
||||
"lanes"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
|
||||
["multi.integration.shared"] = "multi/integration/shared.lua"
|
||||
}
|
||||
}
|
||||
32
rockspecs/multi-1.11-0.rockspec
Normal file
32
rockspecs/multi-1.11-0.rockspec
Normal file
@ -0,0 +1,32 @@
|
||||
package = "multi"
|
||||
version = "1.11.0"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.11.0",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multi-objs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d)
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
"bin",
|
||||
"lanes"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
|
||||
["multi.integration.shared"] = "multi/integration/shared.lua"
|
||||
}
|
||||
}
|
||||
30
rockspecs/multi-1.8-2.rockspec
Normal file
30
rockspecs/multi-1.8-2.rockspec
Normal file
@ -0,0 +1,30 @@
|
||||
package = "multi"
|
||||
version = "1.8-2"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.8.2",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multi objs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1, < 5.2"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.shared.shared"] = "multi/integration/shared/shared.lua"
|
||||
}
|
||||
}
|
||||
30
rockspecs/multi-1.8-3.rockspec
Normal file
30
rockspecs/multi-1.8-3.rockspec
Normal file
@ -0,0 +1,30 @@
|
||||
package = "multi"
|
||||
version = "1.8-3"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.8.3",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multiobjs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1, < 5.2"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.shared.shared"] = "multi/integration/shared/shared.lua"
|
||||
}
|
||||
}
|
||||
30
rockspecs/multi-1.8-4.rockspec
Normal file
30
rockspecs/multi-1.8-4.rockspec
Normal file
@ -0,0 +1,30 @@
|
||||
package = "multi"
|
||||
version = "1.8-4"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.8.4",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multiobjs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1, < 5.2"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.shared.shared"] = "multi/integration/shared/shared.lua"
|
||||
}
|
||||
}
|
||||
30
rockspecs/multi-1.8-5.rockspec
Normal file
30
rockspecs/multi-1.8-5.rockspec
Normal file
@ -0,0 +1,30 @@
|
||||
package = "multi"
|
||||
version = "1.8-5"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.8.5",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multiobjs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1, < 5.2"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.shared.shared"] = "multi/integration/shared/shared.lua"
|
||||
}
|
||||
}
|
||||
30
rockspecs/multi-1.8-6.rockspec
Normal file
30
rockspecs/multi-1.8-6.rockspec
Normal file
@ -0,0 +1,30 @@
|
||||
package = "multi"
|
||||
version = "1.8-6"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.8.6",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multiobjs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1 due to love2d and lua lanes being lua 5.1 only!
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1, < 5.2"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.shared"] = "multi/integration/shared.lua"
|
||||
}
|
||||
}
|
||||
31
rockspecs/multi-1.9-1.rockspec
Normal file
31
rockspecs/multi-1.9-1.rockspec
Normal file
@ -0,0 +1,31 @@
|
||||
package = "multi"
|
||||
version = "1.9-1"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.9.1",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multiobjs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1 due to love2d and lua lanes and now luvit (See ReadMe on gotchas) being lua 5.1 only!
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1, < 5.2"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
|
||||
["multi.integration.shared"] = "multi/integration/shared.lua"
|
||||
}
|
||||
}
|
||||
33
rockspecs/multi-1.9-2.rockspec
Normal file
33
rockspecs/multi-1.9-2.rockspec
Normal file
@ -0,0 +1,33 @@
|
||||
package = "multi"
|
||||
version = "1.9-2"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.9.2",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multiobjs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1 due to love2d and lua lanes and now luvit (See ReadMe on gotchas) being lua 5.1 only!
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
"bin",
|
||||
"lanes"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
|
||||
["multi.integration.shared"] = "multi/integration/shared.lua"
|
||||
}
|
||||
}
|
||||
33
rockspecs/multi-1.9-3.rockspec
Normal file
33
rockspecs/multi-1.9-3.rockspec
Normal file
@ -0,0 +1,33 @@
|
||||
package = "multi"
|
||||
version = "1.9-3"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v1.9.3",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multiobjs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d. Optional) The core of the library works on lua 5.1+ however the systemthreading features are limited to 5.1 due to love2d and lua lanes and now luvit (See ReadMe on gotchas) being lua 5.1 only!
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
"bin",
|
||||
"lanes"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
-- Note the required Lua syntax when listing submodules as keys
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.all"] = "multi/all.lua",
|
||||
["multi.compat.backwards[1,5,0]"] = "multi/compat/backwards[1,5,0].lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
|
||||
["multi.integration.shared"] = "multi/integration/shared.lua"
|
||||
}
|
||||
}
|
||||
32
rockspecs/multi-12.0-0.rockspec
Normal file
32
rockspecs/multi-12.0-0.rockspec
Normal file
@ -0,0 +1,32 @@
|
||||
package = "multi"
|
||||
version = "12.0-0"
|
||||
source = {
|
||||
url = "git://github.com/rayaman/multi.git",
|
||||
tag = "v12.0.0",
|
||||
}
|
||||
description = {
|
||||
summary = "Lua Multi tasking library",
|
||||
detailed = [[
|
||||
This library contains many methods for multi tasking. From simple side by side code using multi-objs, to using coroutine based Threads and System threads(When you have lua lanes installed or are using love2d)
|
||||
]],
|
||||
homepage = "https://github.com/rayaman/multi",
|
||||
license = "MIT"
|
||||
}
|
||||
dependencies = {
|
||||
"lua >= 5.1",
|
||||
"bin",
|
||||
"lanes",
|
||||
"lua-net"
|
||||
}
|
||||
build = {
|
||||
type = "builtin",
|
||||
modules = {
|
||||
["multi.init"] = "multi/init.lua",
|
||||
["multi.compat.love2d"] = "multi/compat/love2d.lua",
|
||||
["multi.integration.lanesManager"] = "multi/integration/lanesManager.lua",
|
||||
["multi.integration.loveManager"] = "multi/integration/loveManager.lua",
|
||||
["multi.integration.luvitManager"] = "multi/integration/luvitManager.lua",
|
||||
["multi.integration.networkManager"] = "multi/integration/networkManager.lua",
|
||||
["multi.integration.shared"] = "multi/integration/shared.lua"
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user