├── README.md ├── BehaviorTree3 ├── Actions │ ├── Error.lua │ ├── Failer.lua │ ├── Runner.lua │ ├── Succeeder.lua │ └── Wait.lua ├── Core │ ├── Action.lua │ ├── Composite.lua │ ├── Decorator.lua │ ├── Condition.lua │ ├── Tick.lua │ ├── BaseNode.lua │ ├── Blackborad.lua │ └── BehaviorTree.lua ├── Composites │ ├── Priority.lua │ ├── Sequence.lua │ ├── MemPriority.lua │ ├── MemSequence.lua │ └── WeightSelector.lua ├── Decorators │ ├── Inverter.lua │ ├── MaxTime.lua │ ├── Limiter.lua │ ├── Repeater.lua │ ├── RepeatUntilFailure.lua │ └── RepeatUntilSuccess.lua ├── Com.lua ├── b3.lua └── uuid.lua └── LICENSE /README.md: -------------------------------------------------------------------------------- 1 | # BehaviorTree_Cocos2dx 2 | A light BehaviorTree framework for cocos2dx in lua 3 | 4 | base on : http://behavior3.com/ 5 | Behavior3 Editor : http://editor.behavior3.com/#/dash/projects 6 | -------------------------------------------------------------------------------- /BehaviorTree3/Actions/Error.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 16:54:06 4 | -- 5 | local Error = class("Error",b3.Action) 6 | 7 | function Error:onTick(tick) 8 | return b3.Com.ERROR 9 | end 10 | 11 | return Error -------------------------------------------------------------------------------- /BehaviorTree3/Actions/Failer.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 16:56:21 4 | -- 5 | local Failer = class("Failer",b3.Action) 6 | 7 | function Failer:onTick(tick) 8 | return b3.Com.FAILURE 9 | end 10 | 11 | return Failer -------------------------------------------------------------------------------- /BehaviorTree3/Actions/Runner.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 16:57:35 4 | -- 5 | local Runner = class("Runner",b3.Action) 6 | 7 | function Runner:onTick(tick) 8 | return b3.Com.RUNNING 9 | end 10 | 11 | return Runner -------------------------------------------------------------------------------- /BehaviorTree3/Actions/Succeeder.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 16:58:31 4 | -- 5 | local Succeeder = class("Succeeder",b3.Action) 6 | 7 | function Succeeder:onTick(tick) 8 | return b3.Com.SUCCESS 9 | end 10 | 11 | return Succeeder -------------------------------------------------------------------------------- /BehaviorTree3/Core/Action.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 15:04:56 4 | -- 5 | local Action = class("Action",b3.BaseNode) 6 | 7 | function Action:ctor(nodeData) 8 | Action.super.ctor(self,nodeData) 9 | self.category = b3.Com.ACTION 10 | end 11 | 12 | return Action -------------------------------------------------------------------------------- /BehaviorTree3/Core/Composite.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 11:40:57 4 | -- 5 | local Composite = class("Composite",b3.BaseNode) 6 | 7 | function Composite:ctor(nodeData) 8 | Composite.super.ctor(self,nodeData) 9 | self.category = b3.Com.COMPOSITE 10 | self.children = {} --存放子节点 11 | end 12 | 13 | return Composite -------------------------------------------------------------------------------- /BehaviorTree3/Core/Decorator.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 12:31:24 4 | -- 5 | local Decorator = class("Decorator",b3.BaseNode) 6 | 7 | function Decorator:ctor(nodeData) 8 | Decorator.super.ctor(self,nodeData) 9 | self.category = b3.Com.DECORATOR 10 | self.child = nil --存放唯一子节点 11 | end 12 | 13 | return Decorator -------------------------------------------------------------------------------- /BehaviorTree3/Core/Condition.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 15:03:13 4 | -- 5 | local Condition = class("Condition",b3.BaseNode) 6 | 7 | function Condition:ctor(nodeData) 8 | Condition.super.ctor(self,nodeData) 9 | self.category = b3.Com.CONDITION 10 | end 11 | 12 | function Condition:onTick(tick) 13 | return b3.Com.SUCCESS 14 | end 15 | 16 | return Condition -------------------------------------------------------------------------------- /BehaviorTree3/Composites/Priority.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 12:06:42 4 | -- 5 | local Priority = class("Priority",b3.Composite) 6 | 7 | function Priority:onTick(tick) 8 | for i=1,#self.children do 9 | local status = self.children[i]:execute(tick) 10 | if status ~= b3.Com.FAILURE then 11 | return status 12 | end 13 | end 14 | return b3.Com.FAILURE 15 | end 16 | 17 | return Priority -------------------------------------------------------------------------------- /BehaviorTree3/Composites/Sequence.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 11:39:40 4 | -- 5 | local Sequence = class("Sequence",b3.Composite) 6 | 7 | function Sequence:onTick(tick) 8 | for i=1,#self.children do 9 | local status = self.children[i]:execute(tick) 10 | if status ~= b3.Com.SUCCESS then 11 | return status 12 | end 13 | end 14 | return b3.Com.SUCCESS 15 | end 16 | 17 | return Sequence -------------------------------------------------------------------------------- /BehaviorTree3/Decorators/Inverter.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 19:21:57 4 | -- 5 | local Inverter = class("Inverter",b3.Decorator) 6 | 7 | function Inverter:onTick(tick) 8 | if not self.child then 9 | return b3.Com.ERROR 10 | end 11 | 12 | local status = self.child:execute(tick) 13 | 14 | if status == b3.Com.SUCCESS then 15 | status = b3.Com.FAILURE 16 | elseif status == b3.Com.FAILURE then 17 | status = b3.Com.SUCCESS 18 | end 19 | 20 | return status 21 | end 22 | 23 | return Inverter -------------------------------------------------------------------------------- /BehaviorTree3/Actions/Wait.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 17:01:32 4 | -- 5 | local Wait = class("Wait",b3.Action) 6 | 7 | function Wait:onCreate(properties) 8 | self.endTime = properties.milliseconds or 0 9 | end 10 | 11 | function Wait:onOpen(tick) 12 | local startTime = os.time() 13 | tick.agent:set("startTime",startTime, tick.tree.id, self.id) 14 | end 15 | 16 | function Wait:onTick(tick) 17 | local currTime = os.time() 18 | local startTime = tick.agent:get("startTime",tick.tree.id,self.id) 19 | if currTime - startTime > self.endTime then 20 | return b3.Com.SUCCESS 21 | end 22 | return b3.Com.RUNNING 23 | end 24 | 25 | return Wait -------------------------------------------------------------------------------- /BehaviorTree3/Composites/MemPriority.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 18:48:52 4 | -- 5 | local MemPriority = class("MemPriority",b3.Composite) 6 | 7 | function MemPriority:onOpen(tick) 8 | tick.agent:set("runningChild",1,tick.tree.id,self.id) 9 | end 10 | 11 | function MemPriority:onTick(tick) 12 | local child = tick.agent:get("runningChild",tick.tree.id,self.id) 13 | for i=child,#self.children do 14 | local status = self.children[i]:execute(tick) 15 | if status ~= b3.Com.FAILURE then 16 | if status == b3.Com.RUNNING then 17 | tick.agent:set("runningChild",i,tick.tree.id,self.id) 18 | end 19 | return status 20 | end 21 | end 22 | return b3.Com.FAILURE 23 | end 24 | 25 | return MemPriority -------------------------------------------------------------------------------- /BehaviorTree3/Composites/MemSequence.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 18:10:49 4 | -- 5 | local MemSequence = class("MemSequence",b3.Composite) 6 | 7 | function MemSequence:onOpen(tick) 8 | tick.agent:set("runningChild",1,tick.tree.id,self.id) 9 | end 10 | 11 | function MemSequence:onTick(tick) 12 | local child = tick.agent:get("runningChild",tick.tree.id,self.id) 13 | for i=child,#self.children do 14 | local status = self.children[i]:execute(tick) 15 | 16 | if status ~= b3.Com.SUCCESS then 17 | if status == b3.Com.RUNNING then 18 | tick.agent:set("runningChild",i,tick.tree.id,self.id) 19 | end 20 | return status 21 | end 22 | end 23 | return b3.Com.SUCCESS 24 | end 25 | 26 | return MemSequence -------------------------------------------------------------------------------- /BehaviorTree3/Com.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-02 14:11:53 4 | -- 5 | local B3Com = {} 6 | local uuid = import(".uuid") 7 | assert(uuid,"uuid is nil") 8 | B3Com.Version = '0.1.0' 9 | 10 | B3Com.Default_Weight = 10 11 | 12 | -- Returning status 13 | B3Com.SUCCESS = 1 14 | B3Com.FAILURE = 2 15 | B3Com.RUNNING = 3 16 | B3Com.ERROR = 4 17 | 18 | --Node categories 19 | B3Com.COMPOSITE = 'composite' 20 | B3Com.DECORATOR = 'decorator' 21 | B3Com.ACTION = 'action'; 22 | B3Com.CONDITION = 'condition' 23 | 24 | --[[This function is used to create unique IDs for trees and nodes. 25 | 26 | (consult http://www.ietf.org/rfc/rfc4122.txt). 27 | 28 | @class createUUID 29 | @constructor 30 | @return {String} A unique ID.]] 31 | 32 | function B3Com.createUUID() 33 | return uuid.new() 34 | end 35 | 36 | return B3Com -------------------------------------------------------------------------------- /BehaviorTree3/Decorators/MaxTime.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 20:26:26 4 | -- 5 | local MaxTime = class("MaxTime",b3.Decorator) 6 | 7 | function MaxTime:onCreate(properties) 8 | assert(properties.maxTime, "maxTime parameter in MaxTime decorator is an obligatory parameter") 9 | self.maxTime = params.maxTime 10 | end 11 | 12 | function MaxTime:onOpen(tick) 13 | local startTime = os.time() 14 | tick.agent:set("startTime",startTime,tick.tree.id,self.id) 15 | end 16 | 17 | function MaxTime:onTick(tick) 18 | if not self.child then 19 | return b3.Com.ERROR 20 | end 21 | 22 | local currTime = os.time() 23 | local startTime = tick.agent:get("startTime",tick.tree.id,self.id) 24 | 25 | if currTime - startTime > self.maxTime then 26 | return b3.Com.FAILURE 27 | end 28 | 29 | local status = self.child:execute(tick) 30 | 31 | return status 32 | end 33 | 34 | return MaxTime -------------------------------------------------------------------------------- /BehaviorTree3/Decorators/Limiter.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 20:21:19 4 | -- 5 | local Limiter = class("Limiter",b3.Decorator) 6 | 7 | function Limiter:onCreate(properties) 8 | assert(properties.maxLoop,"maxLoop parameter in Limiter decorator is an obligatory parameter") 9 | self.maxLoop = properties.maxLoop 10 | end 11 | 12 | function Limiter:onOpen(tick) 13 | tick.agent:set("i",0,tick.tree.id,self.id) 14 | end 15 | 16 | function Limiter:onTick(tick) 17 | if not self.child then 18 | return b3.Com.ERROR 19 | end 20 | 21 | local i = tick.agent:get("i",tick.tree.id,self.id) 22 | 23 | if i < self.maxLoop then 24 | local status = self.child:execute(tick) 25 | 26 | if status == b3.Com.SUCCESS or status == b3.Com.FAILURE then 27 | tick.agent:set("i",i+1,tick.tree.id,self.id) 28 | end 29 | return status 30 | end 31 | return b3.Com.FAILURE 32 | end 33 | 34 | return Limiter -------------------------------------------------------------------------------- /BehaviorTree3/Decorators/Repeater.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 20:34:00 4 | -- 5 | local Repeater = class("Repeater",b3.Decorator) 6 | 7 | function Repeater:onCreate(properties) 8 | assert(properties.maxLoop,"maxLoop parameter in maxLoop decorator is an obligatory parameter") 9 | self.maxLoop = properties.maxLoop or -1 10 | end 11 | 12 | function Repeater:onOpen(tick) 13 | tick.agent:set("i",0,tick.tree.id,self.id) 14 | end 15 | 16 | function Repeater:onTick(tick) 17 | if not self.child then 18 | return b3.Com.ERROR 19 | end 20 | 21 | local i = tick.agent:get("i",tick.tree.id,self.id) 22 | local status = b3.Com.SUCCESS 23 | 24 | while self.maxLoop < 0 or i < self.maxLoop do 25 | status = self.child:execute(tick) 26 | if status == b3.Com.SUCCESS or status == b3.Com.FAILURE then 27 | i = i + 1 28 | else 29 | break 30 | end 31 | end 32 | 33 | tick.agent:set("i",i,tick.tree.id,self.id) 34 | return status 35 | end 36 | 37 | return Repeater -------------------------------------------------------------------------------- /BehaviorTree3/Decorators/RepeatUntilFailure.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 20:42:15 4 | -- 5 | local RepeatUntilFailure = class("RepeatUntilFailure",b3.Decorator) 6 | 7 | function RepeatUntilFailure:onCreate(properties) 8 | assert(properties.maxLoop,"maxLoop parameter in maxLoop decorator is an obligatory parameter") 9 | self.maxLoop = properties.maxLoop or -1 10 | end 11 | 12 | function RepeatUntilFailure:onOpen(tick) 13 | tick.agent:set("i",0,tick.tree.id,self.id) 14 | end 15 | 16 | function RepeatUntilFailure:onTick(tick) 17 | if not self.child then 18 | return b3.Com.ERROR 19 | end 20 | local i = tick.agent:get("i",tick.tree.id,self.id) 21 | local status = b3.Com.ERROR 22 | 23 | while self.maxLoop < 0 or i < self.maxLoop do 24 | status = self.child:execute(tick) 25 | if status == b3.Com.SUCCESS then 26 | i = i + 1 27 | else 28 | break 29 | end 30 | end 31 | tick.agent:set("i",i,tick.tree.id,self.id) 32 | return status 33 | end 34 | 35 | return RepeatUntilFailure -------------------------------------------------------------------------------- /BehaviorTree3/Decorators/RepeatUntilSuccess.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 21:10:50 4 | -- 5 | local RepeatUntilSuccess = class("RepeatUntilSuccess",b3.Decorator) 6 | 7 | function RepeatUntilSuccess:onCreate(properties) 8 | assert(properties.maxLoop,"maxLoop parameter in maxLoop decorator is an obligatory parameter") 9 | self.maxLoop = properties.maxLoop or -1 10 | end 11 | 12 | function RepeatUntilSuccess:onOpen(tick) 13 | tick.agent:set("i",0,tick.tree.id,self.id) 14 | end 15 | 16 | function RepeatUntilSuccess:onTick(tick) 17 | if not self.child then 18 | return b3.Com.ERROR 19 | end 20 | 21 | local i = tick.agent:get("i",tick.tree.id,self.id) 22 | local status = b3.Com.ERRO 23 | 24 | while self.maxLoop < 0 or i < self.maxLoop do 25 | status = self.child:execute(tick) 26 | if status == b3.Com.SUCCESS then 27 | i = i + 1 28 | else 29 | break 30 | end 31 | end 32 | tick.agent:set("i",i,tick.tree.id,self.id) 33 | return status 34 | end 35 | 36 | return RepeatUntilSuccess -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ZhaoTianze 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /BehaviorTree3/Composites/WeightSelector.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-04 11:00:50 4 | -- 5 | local WeightSelector = class("WeightSelector",b3.Composite) 6 | 7 | function WeightSelector:onOpen(tick) 8 | printf("WeightSelector:onOpen") 9 | tick.agent:set("runningChild",nil,tick.tree.id,self.id) 10 | end 11 | 12 | function WeightSelector:onTick(tick) 13 | --初始化权重值 14 | if not self.totalWeight then 15 | local totalWeight = 0 16 | for i=1,#self.children do 17 | local child = self.children[i] 18 | totalWeight = totalWeight + child.weight 19 | end 20 | self.totalWeight = totalWeight 21 | end 22 | --优先选择上次正在执行中的节点 23 | local childIndex = tick.agent:get("runningChild",tick.tree.id,self.id) 24 | if not childIndex then 25 | -- return self.children[childIndex]:execute(tick) 26 | local randomValue = math.random(1,self.totalWeight) 27 | local currentWeight = 0 28 | for i=1,#self.children do 29 | local child = self.children[i] 30 | currentWeight = currentWeight + child.weight 31 | if currentWeight >= randomValue then 32 | childIndex = i 33 | break 34 | end 35 | end 36 | end 37 | --从childIndex开始遍历执行execute,当某一节点返回非FAILURE的时候结束遍历并返回 38 | for i=childIndex,#self.children do 39 | local status = self:executeChild(i,tick) 40 | if status ~= b3.Com.FAILURE then 41 | return status 42 | end 43 | end 44 | --从1到 childIndex - 1 遍历 45 | for i=1,childIndex - 1 do 46 | local status = self:executeChild(i, tick) 47 | if status ~= b3.Com.FAILURE then 48 | return status 49 | end 50 | end 51 | 52 | return b3.Com.FAILURE 53 | end 54 | 55 | function WeightSelector:executeChild(index,tick) 56 | local status = self.children[index]:execute(tick) 57 | if status == b3.Com.RUNNING then 58 | tick.agent:set("runningChild",index,tick.tree.id,self.id) 59 | end 60 | return status 61 | end 62 | 63 | return WeightSelector -------------------------------------------------------------------------------- /BehaviorTree3/b3.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-02 15:35:49 4 | -- BehaviorTree3 init && require lua file 5 | b3 = {} 6 | b3.Com = require('cocos.framework.BehaviorTree3.Com') 7 | 8 | --core 9 | b3.BaseNode = require('cocos.framework.BehaviorTree3.Core.BaseNode') 10 | b3.Action = require('cocos.framework.BehaviorTree3.Core.Action') 11 | b3.Decorator = require('cocos.framework.BehaviorTree3.Core.Decorator') 12 | b3.Condition = require('cocos.framework.BehaviorTree3.Core.Condition') 13 | b3.Composite = require('cocos.framework.BehaviorTree3.Core.Composite') 14 | b3.BehaviorTree = require('cocos.framework.BehaviorTree3.Core.BehaviorTree') 15 | b3.Blackborad = require('cocos.framework.BehaviorTree3.Core.Blackborad') 16 | b3.Tick = require('cocos.framework.BehaviorTree3.Core.Tick') 17 | 18 | --Action 19 | b3.Error = require('cocos.framework.BehaviorTree3.Actions.Error') 20 | b3.Failer = require('cocos.framework.BehaviorTree3.Actions.Failer') 21 | b3.Runner = require('cocos.framework.BehaviorTree3.Actions.Runner') 22 | b3.Succeeder = require('cocos.framework.BehaviorTree3.Actions.Succeeder') 23 | b3.Wait = require('cocos.framework.BehaviorTree3.Actions.Wait') 24 | 25 | --Composites 26 | b3.Sequence = require('cocos.framework.BehaviorTree3.Composites.Sequence') 27 | b3.Priority = require('cocos.framework.BehaviorTree3.Composites.Priority') 28 | b3.MemSequence = require('cocos.framework.BehaviorTree3.Composites.MemSequence') 29 | b3.MemPriority = require('cocos.framework.BehaviorTree3.Composites.MemPriority') 30 | b3.WeightSelector = require('cocos.framework.BehaviorTree3.Composites.WeightSelector') 31 | 32 | --Decorators 33 | b3.Inverter = require('cocos.framework.BehaviorTree3.Decorators.Inverter') 34 | b3.Limiter = require('cocos.framework.BehaviorTree3.Decorators.Limiter') 35 | b3.MaxTime = require('cocos.framework.BehaviorTree3.Decorators.MaxTime') 36 | b3.Repeater = require('cocos.framework.BehaviorTree3.Decorators.Repeater') 37 | b3.RepeatUntilFailure = require('cocos.framework.BehaviorTree3.Decorators.RepeatUntilFailure') 38 | b3.RepeatUntilSuccess = require('cocos.framework.BehaviorTree3.Decorators.RepeatUntilSuccess') 39 | -------------------------------------------------------------------------------- /BehaviorTree3/Core/Tick.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 16:22:09 4 | -- 5 | 6 | --[[A new Tick object is instantiated every tick by BehaviorTree. It is passed 7 | as parameter to the nodes through the tree during the traversal. 8 | 9 | The role of the Tick class is to store the instances of tree, debug, 10 | target and blackboard. So, all nodes can access these informations. 11 | 12 | For internal uses, the Tick also is useful to store the open node after 13 | the tick signal, in order to let `BehaviorTree` to keep track and close 14 | them when necessary. 15 | 16 | This class also makes a bridge between nodes and the debug, passing the 17 | node state to the debug if the last is provided. 18 | 19 | @module b3 20 | @class Tick 21 | ]] 22 | 23 | local Tick = class("Tick") 24 | 25 | function Tick:ctor() 26 | --[[ 27 | The tree reference. 28 | @property {b3.BehaviorTree} tree 29 | @readOnly 30 | ]] 31 | self.tree = nil 32 | --[[ 33 | The debug reference. 34 | @property {Object} debug 35 | @readOnly 36 | ]] 37 | self.debug = nil 38 | --[[ 39 | The agent object reference. Which include a blackboard 40 | @property {Object} target 41 | @readOnly 42 | ]] 43 | self.agent = nil 44 | --[[ 45 | 46 | ]] 47 | self.worldBlackboard = nil 48 | --[[ 49 | The list of open nodes. Update during the tree traversal. 50 | @property {Array} _openNodes 51 | @protected 52 | @readOnly 53 | ]] 54 | self.openNodes = {} 55 | --[[ 56 | The number of nodes entered during the tick. Update during the tree 57 | traversal. 58 | 59 | @property {Integer} _nodeCount 60 | @protected 61 | @readOnly 62 | ]] 63 | self.nodeCount = 0 64 | end 65 | 66 | function Tick:reset() 67 | self.tree = nil 68 | self.openNodes = {} 69 | self.nodeCount = 0 70 | end 71 | --[[ 72 | Called when entering a node (called by BaseNode). 73 | @method _enterNode 74 | @param {Object} node The node that called this method. 75 | @protected 76 | ]] 77 | function Tick:enterNode(node) 78 | self.nodeCount = self.nodeCount + 1 79 | table.insert(self.openNodes,node) 80 | --TODO:call debug here 81 | end 82 | --[[ 83 | Called when entering a node (called by BaseNode). 84 | @method _enterNode 85 | @param {Object} node The node that called this method. 86 | @protected 87 | ]] 88 | function Tick:openNode(node) 89 | --TODO:call debug here 90 | end 91 | --[[ 92 | Callback when ticking a node (called by BaseNode). 93 | @method _tickNode 94 | @param {Object} node The node that called this method. 95 | @protected 96 | ]] 97 | function Tick:tickNode(node,result) 98 | --TODO:call debug here 99 | end 100 | --[[ 101 | Callback when closing a node (called by BaseNode). 102 | @method _closeNode 103 | @param {Object} node The node that called this method. 104 | @protected 105 | ]] 106 | function Tick:closeNode(node) 107 | --remove last node 108 | table.remove(self.openNodes,#self.openNodes) 109 | --TODO:call debug here 110 | end 111 | --[[ 112 | Callback when exiting a node (called by BaseNode). 113 | @method _exitNode 114 | @param {Object} node The node that called this method. 115 | @protected 116 | ]] 117 | function Tick:exitNode(node) 118 | --TODO:call debug here 119 | end 120 | return Tick -------------------------------------------------------------------------------- /BehaviorTree3/Core/BaseNode.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-02 15:16:40 4 | -- 5 | local BaseNode = class("BaseNode") 6 | 7 | function BaseNode:ctor(nodeData) 8 | nodeData = nodeData or {} 9 | self.id = nodeData.id or b3.Com.createUUID() 10 | self.name = nodeData.name or "" 11 | self.title = nodeData.title or "" 12 | self.description = nodeData.description or "" 13 | self.parameters = nodeData.parameters or {} 14 | self.properties = nodeData.properties or {} 15 | self.category = nil 16 | self.weight = nodeData.properties.weight or b3.Com.Default_Weight --随机权重 17 | 18 | self:onCreate(self.properties) 19 | end 20 | 21 | function BaseNode:onCreate(nodeData) 22 | end 23 | 24 | function BaseNode:execute(tick) 25 | --Enter 26 | self:enter(tick) 27 | --open 28 | if not tick.agent:get("isOpen",tick.tree.id,self.id) then 29 | self:open(tick) 30 | end 31 | --tick 32 | local status = self:tick(tick) 33 | --close 34 | if status ~= b3.Com.RUNNING then 35 | self:close(tick) 36 | end 37 | --exit 38 | self:exit(tick) 39 | 40 | return status 41 | end 42 | 43 | --[[Wrapper for enter method. 44 | @method _enter 45 | @param {Tick} tick A tick instance. 46 | @protected 47 | ]] 48 | function BaseNode:enter(tick) 49 | tick:enterNode(self) 50 | self:onEnter(tick) 51 | end 52 | 53 | --[[Wrapper for enter method. 54 | @method _enter 55 | @param {Tick} tick A tick instance. 56 | @protected 57 | ]] 58 | function BaseNode:open(tick) 59 | tick:openNode(self) 60 | tick.agent:set("isOpen", true, tick.tree.id, self.id) 61 | self:onOpen(tick) 62 | end 63 | 64 | --[[Wrapper for tick method. 65 | @method _tick 66 | @param {Tick} tick A tick instance. 67 | @return {Constant} A state constant. 68 | @protected 69 | ]] 70 | function BaseNode:tick(tick) 71 | local result = self:onTick(tick) 72 | tick:tickNode(self,result) 73 | return result 74 | end 75 | 76 | --[[Wrapper for close method. 77 | @method _close 78 | @param {Tick} tick A tick instance. 79 | @protected 80 | ]] 81 | function BaseNode:close(tick) 82 | tick:closeNode(self) 83 | tick.agent:set("isOpen", false, tick.tree.id, self.id) 84 | self:onClose(tick) 85 | end 86 | 87 | --[[ 88 | Wrapper for exit method. 89 | @method _exit 90 | @param {Tick} tick A tick instance. 91 | @protected 92 | ]] 93 | function BaseNode:exit(tick) 94 | tick:exitNode(self) 95 | self:onExit(tick) 96 | end 97 | 98 | --[[ 99 | Enter method, override this to use. It is called every time a node is 100 | asked to execute, before the tick itself. 101 | @method enter 102 | @param {Tick} tick A tick instance. 103 | ]] 104 | function BaseNode:onEnter(tick) 105 | end 106 | 107 | --[[ 108 | Open method, override this to use. It is called only before the tick 109 | callback and only if the not isn't closed. 110 | Note: a node will be closed if it returned `b3.RUNNING` in the tick. 111 | @method open 112 | @param {Tick} tick A tick instance. 113 | ]] 114 | function BaseNode:onOpen(tick) 115 | end 116 | 117 | --[[ 118 | Tick method, override this to use. This method must contain the real 119 | execution of node (perform a task, call children, etc.). It is called 120 | every time a node is asked to execute. 121 | @method tick 122 | @param {Tick} tick A tick instance. 123 | ]] 124 | function BaseNode:onTick(tick) 125 | end 126 | --[[ 127 | Close method, override this to use. This method is called after the tick 128 | callback, and only if the tick return a state different from 129 | `b3.RUNNING`. 130 | @method close 131 | @param {Tick} tick A tick instance. 132 | ]] 133 | function BaseNode:onClose(tick) 134 | end 135 | --[[ 136 | Exit method, override this to use. Called every time in the end of the 137 | execution. 138 | @method exit 139 | @param {Tick} tick A tick instance. 140 | ]] 141 | function BaseNode:onExit(tick) 142 | end 143 | 144 | return BaseNode -------------------------------------------------------------------------------- /BehaviorTree3/Core/Blackborad.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-03 14:08:24 4 | -- 5 | local Blackborad = class("Blackborad") 6 | 7 | function Blackborad:ctor() 8 | self.baseMemory = {} 9 | self.treeMemory = {} 10 | end 11 | 12 | --[[Internal method to retrieve the tree context memory. If the memory does 13 | not exist, this method creates it. 14 | 15 | @method _getTreeMemory 16 | @param {string} treeScope The id of the tree in scope. 17 | @return {Object} The tree memory. 18 | @protected 19 | ]] 20 | 21 | function Blackborad:getTreeMemory_(treeScope) 22 | if not self.treeMemory[treeScope] then 23 | self.treeMemory[treeScope] = {} 24 | self.treeMemory[treeScope].nodeMemory = {} 25 | self.treeMemory[treeScope].openNodes = {} 26 | self.traversalDepth = 0 27 | self.traversalCycle = 0 28 | end 29 | return self.treeMemory[treeScope] 30 | end 31 | 32 | --[[Internal method to retrieve the node context memory, given the tree 33 | memory. If the memory does not exist, this method creates is. 34 | 35 | @method _getNodeMemory 36 | @param {String} treeMemory the tree memory. 37 | @param {String} nodeScope The id of the node in scope. 38 | @return {Object} The node memory. 39 | @protected 40 | ]] 41 | 42 | function Blackborad:getNodeMemory_(treeeMemory, nodeScope) 43 | local memory = treeeMemory.nodeMemory 44 | memory[nodeScope] = memory[nodeScope] or {} 45 | return memory[nodeScope] 46 | end 47 | 48 | 49 | --[[Internal method to retrieve the context memory. If treeScope and 50 | nodeScope are provided, this method returns the per node per tree 51 | memory. If only the treeScope is provided, it returns the per tree 52 | memory. If no parameter is provided, it returns the global memory. 53 | Notice that, if only nodeScope is provided, this method will still 54 | return the global memory. 55 | 56 | @method _getMemory 57 | @param {String} treeScope The id of the tree scope. 58 | @param {String} nodeScope The id of the node scope. 59 | @return {Object} A memory object. 60 | @protected 61 | ]] 62 | 63 | function Blackborad:getMemory_(treeScope,nodeScope) 64 | local memory = self.baseMemory 65 | if treeScope then 66 | memory = self:getTreeMemory_(treeScope) 67 | if nodeScope then 68 | assert(memory,string.format("treeMemory = %s",tostring(memory))) 69 | memory = self:getNodeMemory_(memory, nodeScope) 70 | end 71 | end 72 | return memory 73 | end 74 | 75 | --[[Stores a value in the blackboard. If treeScope and nodeScope are 76 | provided, this method will save the value into the per node per tree 77 | memory. If only the treeScope is provided, it will save the value into 78 | the per tree memory. If no parameter is provided, this method will save 79 | the value into the global memory. Notice that, if only nodeScope is 80 | provided (but treeScope not), this method will still save the value into 81 | the global memory. 82 | 83 | @method set 84 | @param {String} key The key to be stored. 85 | @param {String} value The value to be stored. 86 | @param {String} treeScope The tree id if accessing the tree or node 87 | memory. 88 | @param {String} nodeScope The node id if accessing the node memory. 89 | ]] 90 | 91 | 92 | function Blackborad:set(key,value,treeScope, nodeScope) 93 | local memory = self:getMemory_(treeScope, nodeScope) 94 | memory[key] = value 95 | end 96 | 97 | --[[Retrieves a value in the blackboard. If treeScope and nodeScope are 98 | provided, this method will retrieve the value from the per node per tree 99 | memory. If only the treeScope is provided, it will retrieve the value 100 | from the per tree memory. If no parameter is provided, this method will 101 | retrieve from the global memory. If only nodeScope is provided (but 102 | treeScope not), this method will still try to retrieve from the global 103 | memory. 104 | 105 | @method get 106 | @param {String} key The key to be retrieved. 107 | @param {String} treeScope The tree id if accessing the tree or node 108 | memory. 109 | @param {String} nodeScope The node id if accessing the node memory. 110 | @return {Object} The value stored or undefined. 111 | ]] 112 | 113 | function Blackborad:get(key,treeScope,nodeScope) 114 | local memory = self:getMemory_(treeScope, nodeScope) 115 | return memory[key] 116 | end 117 | 118 | return Blackborad -------------------------------------------------------------------------------- /BehaviorTree3/Core/BehaviorTree.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- Author: zen.zhao88@gmail.com 3 | -- Date: 2015-12-02 15:34:00 4 | -- 5 | local BehaviorTree = class("BehaviorTree") 6 | 7 | function BehaviorTree:ctor(data,customNodeList) 8 | self.title = 'The behavior tree' 9 | self.description = 'Default description' 10 | self.properties = {} 11 | self.root = nil 12 | self.debug = nil 13 | 14 | self:load(data,customNodeList) 15 | end 16 | -- 通过json配置表构建行为树 17 | function BehaviorTree:load(data,nodeList) 18 | if type(data) ~= "table" then 19 | return false 20 | end 21 | nodeList = nodeList or {} 22 | 23 | self.id = data.id or b3.Com.createUUID() 24 | self.title = data.title or self.title 25 | self.description = data.description or self.description 26 | 27 | local nodes = {} 28 | local node 29 | for id,nodeData in pairs(data.nodes) do 30 | -- print(k,v) 31 | local Cls = nodeList[nodeData.name] or b3[nodeData.name] 32 | assert(Cls,string.format("unkonw node name:%s",nodeData.name)) 33 | node = Cls.new(nodeData) 34 | nodes[id] = node 35 | end 36 | --Connect the nodes 37 | for id,nodeData in pairs(data.nodes) do 38 | node = nodes[id] 39 | if node.category == b3.Com.COMPOSITE and nodeData.children then 40 | for i=1,#nodeData.children do 41 | local cid = nodeData.children[i] 42 | table.insert(node.children, nodes[cid]) 43 | end 44 | elseif node.category == b3.Com.DECORATOR and nodeData.child then 45 | node.child = nodes[nodeData.child] 46 | assert(node.child,"not have a child") 47 | end 48 | end 49 | self.root = nodes[data.root] 50 | end 51 | 52 | 53 | --This method dump the current BT into a data structure. 54 | 55 | --Note: This method does not record the current node parameters. Thus, 56 | --it may not be compatible with load for now. 57 | 58 | --@method dump 59 | --@return {Object} A data object representing this tree. 60 | 61 | function BehaviorTree:dump() 62 | local data = {} 63 | local customNames = {} 64 | 65 | data.title = self.title 66 | data.description = self.description 67 | data.properties = self.properties 68 | data.nodes = {} 69 | data.custom_nodes = {} 70 | if self.root then 71 | data.root = self.root.id 72 | else 73 | return data 74 | end 75 | 76 | local stack = {self.root} 77 | while #stack > 0 do 78 | local node = table.remove(stack,#stack) 79 | local nodeData = {} 80 | nodeData.id = node.id; 81 | nodeData.name = node.name; 82 | nodeData.title = node.title; 83 | nodeData.description = node.description; 84 | nodeData.properties = node.properties; 85 | nodeData.parameters = node.parameters; 86 | 87 | --verify custom node 88 | local proto 89 | if node.constructor then 90 | proto = node.constructor.prototype 91 | end 92 | local nodeName = (proto and proto.name) or node.name 93 | if not b3[nodeName] and not customNames[nodeName] then 94 | local subdata = {} 95 | subdata.name = nodeName 96 | subdata.title = (proto and proto.title) or node.title 97 | subdata.category = node.category 98 | customNames[nodeName] = true 99 | table.insert(data.custom_nodes,subdata) 100 | end 101 | 102 | --store children/child 103 | local category = node.category 104 | if category == b3.Com.COMPOSITE and node.children then 105 | local children = {} 106 | for i=1,#node.children do 107 | table.insert(children, node.children[i].id) 108 | table.insert(stack,node.children[i]) 109 | end 110 | nodeData.children = children 111 | elseif category == b3.DECORATOR and node.child then 112 | table.insert(stack,node.child) 113 | nodeData.child = node.child.id 114 | end 115 | data.nodes[node.id] = nodeData 116 | end 117 | return data 118 | end 119 | 120 | function BehaviorTree:tick(tick) 121 | assert(tick, 'tick object is important for tick method') 122 | assert(tick.agent, 'agent is important for tick method') 123 | assert(tick.worldBlackboard, 'worldBlackboard is important for tick method') 124 | --Add object to a tick object 125 | tick.debuge = self.debug 126 | tick.tree = self 127 | --TICK NODE 128 | local state = self.root:execute(tick) 129 | --close nodes from last tick, if needed 130 | local agentBlackboard = tick.agent 131 | local lastOpenNodes = agentBlackboard:get("openNodes", self.id) 132 | local currOpenNodes = tick.openNodes 133 | --does not close if it is still open in this tick 134 | local start = 1 135 | local lastOpenNodesNum = #lastOpenNodes 136 | for i=1,math.min(lastOpenNodesNum,#currOpenNodes) do 137 | start = i+1 138 | if lastOpenNodes[i] ~= currOpenNodes[i] then 139 | break 140 | end 141 | end 142 | --close the nodes 143 | if lastOpenNodesNum > 0 then 144 | for i=lastOpenNodesNum,start,-1 do 145 | lastOpenNodes[i]:close(tick) 146 | end 147 | end 148 | --populate blackboard 149 | agentBlackboard:set("openNodes",currOpenNodes, self.id) 150 | agentBlackboard:set("nodeCount",tick.nodeCount,self.id) 151 | 152 | return state 153 | end 154 | 155 | return BehaviorTree -------------------------------------------------------------------------------- /BehaviorTree3/uuid.lua: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------------- 2 | -- Copyright 2012 Rackspace (original), 2013 Thijs Schreijer (modifications) 3 | -- 4 | -- Licensed under the Apache License, Version 2.0 (the "License"); 5 | -- you may not use this file except in compliance with the License. 6 | -- You may obtain a copy of the License at 7 | -- 8 | -- http://www.apache.org/licenses/LICENSE-2.0 9 | -- 10 | -- Unless required by applicable law or agreed to in writing, software 11 | -- distributed under the License is distributed on an "AS-IS" BASIS, 12 | -- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | -- See the License for the specific language governing permissions and 14 | -- limitations under the License. 15 | -- 16 | -- see http://www.ietf.org/rfc/rfc4122.txt 17 | -- 18 | -- Note that this is not a true version 4 (random) UUID. Since `os.time()` precision is only 1 second, it would be hard 19 | -- to guarantee spacial uniqueness when two hosts generate a uuid after being seeded during the same second. This 20 | -- is solved by using the node field from a version 1 UUID. It represents the mac address. 21 | -- 22 | -- 28-apr-2013 modified by Thijs Schreijer from the original [Rackspace code](https://github.com/kans/zirgo/blob/807250b1af6725bad4776c931c89a784c1e34db2/util/uuid.lua) as a generic Lua module. 23 | -- Regarding the above mention on `os.time()`; the modifications use the `socket.gettime()` function from LuaSocket 24 | -- if available and hence reduce that problem (provided LuaSocket has been loaded before uuid). 25 | -- 26 | -- **6-nov-2015 Please take note of this issue**; [https://github.com/Mashape/kong/issues/478](https://github.com/Mashape/kong/issues/478) 27 | -- It demonstrates the problem of using time as a random seed. Specifically when used from multiple processes. 28 | -- So make sure to seed only once, application wide. And to not have multiple processes do that 29 | -- simultaneously (like nginx does for example). 30 | 31 | local M = {} 32 | local math = require('math') 33 | local os = require('os') 34 | local string = require('string') 35 | 36 | local bitsize = 32 -- bitsize assumed for Lua VM. See randomseed function below. 37 | local lua_version = tonumber(_VERSION:match("%d%.*%d*")) -- grab Lua version used 38 | 39 | local MATRIX_AND = {{0,0},{0,1} } 40 | local MATRIX_OR = {{0,1},{1,1}} 41 | local HEXES = '0123456789abcdef' 42 | 43 | local math_floor = math.floor 44 | local math_random = math.random 45 | local math_abs = math.abs 46 | local string_sub = string.sub 47 | local to_number = tonumber 48 | local assert = assert 49 | local type = type 50 | 51 | -- performs the bitwise operation specified by truth matrix on two numbers. 52 | local function BITWISE(x, y, matrix) 53 | local z = 0 54 | local pow = 1 55 | while x > 0 or y > 0 do 56 | z = z + (matrix[x%2+1][y%2+1] * pow) 57 | pow = pow * 2 58 | x = math_floor(x/2) 59 | y = math_floor(y/2) 60 | end 61 | return z 62 | end 63 | 64 | local function INT2HEX(x) 65 | local s,base = '',16 66 | local d 67 | while x > 0 do 68 | d = x % base + 1 69 | x = math_floor(x/base) 70 | s = string_sub(HEXES, d, d)..s 71 | end 72 | while #s < 2 do s = "0" .. s end 73 | return s 74 | end 75 | 76 | ---------------------------------------------------------------------------- 77 | -- Creates a new uuid. Either provide a unique hex string, or make sure the 78 | -- random seed is properly set. The module table itself is a shortcut to this 79 | -- function, so `my_uuid = uuid.new()` equals `my_uuid = uuid()`. 80 | -- 81 | -- For proper use there are 3 options; 82 | -- 83 | -- 1. first require `luasocket`, then call `uuid.seed()`, and request a uuid using no 84 | -- parameter, eg. `my_uuid = uuid()` 85 | -- 2. use `uuid` without `luasocket`, set a random seed using `uuid.randomseed(some_good_seed)`, 86 | -- and request a uuid using no parameter, eg. `my_uuid = uuid()` 87 | -- 3. use `uuid` without `luasocket`, and request a uuid using an unique hex string, 88 | -- eg. `my_uuid = uuid(my_networkcard_macaddress)` 89 | -- 90 | -- @return a properly formatted uuid string 91 | -- @param hwaddr (optional) string containing a unique hex value (e.g.: `00:0c:29:69:41:c6`), to be used to compensate for the lesser `math_random()` function. Use a mac address for solid results. If omitted, a fully randomized uuid will be generated, but then you must ensure that the random seed is set properly! 92 | -- @usage 93 | -- local uuid = require("uuid") 94 | -- print("here's a new uuid: ",uuid()) 95 | function M.new(hwaddr) 96 | -- bytes are treated as 8bit unsigned bytes. 97 | local bytes = { 98 | math_random(0, 255), 99 | math_random(0, 255), 100 | math_random(0, 255), 101 | math_random(0, 255), 102 | math_random(0, 255), 103 | math_random(0, 255), 104 | math_random(0, 255), 105 | math_random(0, 255), 106 | math_random(0, 255), 107 | math_random(0, 255), 108 | math_random(0, 255), 109 | math_random(0, 255), 110 | math_random(0, 255), 111 | math_random(0, 255), 112 | math_random(0, 255), 113 | math_random(0, 255) 114 | } 115 | 116 | if hwaddr then 117 | assert(type(hwaddr)=="string", "Expected hex string, got "..type(hwaddr)) 118 | -- Cleanup provided string, assume mac address, so start from back and cleanup until we've got 12 characters 119 | local i,str = #hwaddr, hwaddr 120 | hwaddr = "" 121 | while i>0 and #hwaddr<12 do 122 | local c = str:sub(i,i):lower() 123 | if HEXES:find(c, 1, true) then 124 | -- valid HEX character, so append it 125 | hwaddr = c..hwaddr 126 | end 127 | i = i - 1 128 | end 129 | assert(#hwaddr == 12, "Provided string did not contain at least 12 hex characters, retrieved '"..hwaddr.."' from '"..str.."'") 130 | 131 | -- no split() in lua. :( 132 | bytes[11] = to_number(hwaddr:sub(1, 2), 16) 133 | bytes[12] = to_number(hwaddr:sub(3, 4), 16) 134 | bytes[13] = to_number(hwaddr:sub(5, 6), 16) 135 | bytes[14] = to_number(hwaddr:sub(7, 8), 16) 136 | bytes[15] = to_number(hwaddr:sub(9, 10), 16) 137 | bytes[16] = to_number(hwaddr:sub(11, 12), 16) 138 | end 139 | 140 | -- set the version 141 | bytes[7] = BITWISE(bytes[7], 0x0f, MATRIX_AND) 142 | bytes[7] = BITWISE(bytes[7], 0x40, MATRIX_OR) 143 | -- set the variant 144 | bytes[9] = BITWISE(bytes[7], 0x3f, MATRIX_AND) 145 | bytes[9] = BITWISE(bytes[7], 0x80, MATRIX_OR) 146 | return INT2HEX(bytes[1])..INT2HEX(bytes[2])..INT2HEX(bytes[3])..INT2HEX(bytes[4]).."-".. 147 | INT2HEX(bytes[5])..INT2HEX(bytes[6]).."-".. 148 | INT2HEX(bytes[7])..INT2HEX(bytes[8]).."-".. 149 | INT2HEX(bytes[9])..INT2HEX(bytes[10]).."-".. 150 | INT2HEX(bytes[11])..INT2HEX(bytes[12])..INT2HEX(bytes[13])..INT2HEX(bytes[14])..INT2HEX(bytes[15])..INT2HEX(bytes[16]) 151 | end 152 | 153 | ---------------------------------------------------------------------------- 154 | -- Improved randomseed function. 155 | -- Lua 5.1 and 5.2 both truncate the seed given if it exceeds the integer 156 | -- range. If this happens, the seed will be 0 or 1 and all randomness will 157 | -- be gone (each application run will generate the same sequence of random 158 | -- numbers in that case). This improved version drops the most significant 159 | -- bits in those cases to get the seed within the proper range again. 160 | -- @param seed the random seed to set (integer from 0 - 2^32, negative values will be made positive) 161 | -- @return the (potentially modified) seed used 162 | -- @usage 163 | -- local socket = require("socket") -- gettime() has higher precision than os.time() 164 | -- local uuid = require("uuid") 165 | -- -- see also example at uuid.seed() 166 | -- uuid.randomseed(socket.gettime()*10000) 167 | -- print("here's a new uuid: ",uuid()) 168 | function M.randomseed(seed) 169 | seed = math_floor(math_abs(seed)) 170 | if seed >= (2^bitsize) then 171 | -- integer overflow, so reduce to prevent a bad seed 172 | seed = seed - math_floor(seed / 2^bitsize) * (2^bitsize) 173 | end 174 | if lua_version < 5.2 then 175 | -- 5.1 uses (incorrect) signed int 176 | math.randomseed(seed - 2^(bitsize-1)) 177 | else 178 | -- 5.2 uses (correct) unsigned int 179 | math.randomseed(seed) 180 | end 181 | return seed 182 | end 183 | 184 | ---------------------------------------------------------------------------- 185 | -- Seeds the random generator. 186 | -- It does so in 2 possible ways; 187 | -- 188 | -- 1. use `os.time()`: this only offers resolution to one second (used when 189 | -- LuaSocket hasn't been loaded yet 190 | -- 2. use luasocket `gettime()` function, but it only does so when LuaSocket 191 | -- has been required already. 192 | -- @usage 193 | -- local socket = require("socket") -- gettime() has higher precision than os.time() 194 | -- -- LuaSocket loaded, so below line does the same as the example from randomseed() 195 | -- uuid.seed() 196 | -- print("here's a new uuid: ",uuid()) 197 | function M.seed() 198 | if package.loaded["socket"] and package.loaded["socket"].gettime then 199 | return M.randomseed(package.loaded["socket"].gettime()*10000) 200 | else 201 | return M.randomseed(os.time()) 202 | end 203 | end 204 | 205 | return setmetatable( M, { __call = function(self, hwaddr) return self.new(hwaddr) end} ) --------------------------------------------------------------------------------