diff --git a/spine-as3/spine-as3/src/spine/IkConstraint.as b/spine-as3/spine-as3/src/spine/IkConstraint.as index 4955545d5..1e472df8d 100644 --- a/spine-as3/spine-as3/src/spine/IkConstraint.as +++ b/spine-as3/spine-as3/src/spine/IkConstraint.as @@ -144,7 +144,7 @@ public class IkConstraint implements Updatable { if (u) { l2 *= psx; var cos:Number = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2); - if (cos < -1) + if (cos < -1) cos = -1; else if (cos > 1) cos = 1; a2 = Math.acos(cos) * bendDir; diff --git a/spine-as3/spine-as3/src/spine/PathConstraint.as b/spine-as3/spine-as3/src/spine/PathConstraint.as index 3fea8f3b0..8bfc165a4 100644 --- a/spine-as3/spine-as3/src/spine/PathConstraint.as +++ b/spine-as3/spine-as3/src/spine/PathConstraint.as @@ -173,7 +173,7 @@ public class PathConstraint implements Updatable { } this._world.length = 8; world = this._world; - var o:int, curve:int; + var o:int, curve:int; for (i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) { var space:Number = spaces[i]; position += space; diff --git a/spine-as3/spine-as3/src/spine/Skeleton.as b/spine-as3/spine-as3/src/spine/Skeleton.as index 25a029c3e..c4b23573c 100644 --- a/spine-as3/spine-as3/src/spine/Skeleton.as +++ b/spine-as3/spine-as3/src/spine/Skeleton.as @@ -277,7 +277,7 @@ public class Skeleton { public function setSlotsToSetupPose () : void { var i:int = 0; - for each (var slot:Slot in slots) { + for each (var slot:Slot in slots) { drawOrder[i++] = slot; slot.setToSetupPose(); } @@ -353,8 +353,8 @@ public class Skeleton { return _skin == null ? null : _skin._name; } - /** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}. - * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was + /** Sets the skin used to look up attachments before looking in the {@link SkeletonData#getDefaultSkin() default skin}. + * Attachments from the new skin are attached if the corresponding attachment from the old skin was attached. If there was * no old skin, each slot's setup mode attachment is attached from the new skin. * @param newSkin May be null. */ public function set skin (newSkin:Skin) : void { diff --git a/spine-as3/spine-as3/src/spine/SkeletonJson.as b/spine-as3/spine-as3/src/spine/SkeletonJson.as index 6f0a2ba43..34951584c 100644 --- a/spine-as3/spine-as3/src/spine/SkeletonJson.as +++ b/spine-as3/spine-as3/src/spine/SkeletonJson.as @@ -366,14 +366,14 @@ public class SkeletonJson { var bones:Vector. = new Vector.(verticesLength * 3); bones.length = 0; for (i = 0, n = vertices.length; i < n;) { - var boneCount:int = int(vertices[i++]); - bones.push(boneCount); - for (var nn:int = i + boneCount * 4; i < nn; i+=4) { + var boneCount:int = int(vertices[i++]); + bones.push(boneCount); + for (var nn:int = i + boneCount * 4; i < nn; i+=4) { bones.push(int(vertices[i])); weights.push(vertices[i + 1] * scale); weights.push(vertices[i + 2] * scale); weights.push(vertices[i + 3]); - } + } } attachment.bones = bones; attachment.vertices = weights; diff --git a/spine-as3/spine-as3/src/spine/animation/Animation.as b/spine-as3/spine-as3/src/spine/animation/Animation.as index f8d70b269..297ece419 100644 --- a/spine-as3/spine-as3/src/spine/animation/Animation.as +++ b/spine-as3/spine-as3/src/spine/animation/Animation.as @@ -121,7 +121,7 @@ public class Animation { } return 0; // Can't happen. } - + static public function linearSearch (values:Vector., target:Number, step:int) : int { for (var i:int = 0, last:int = values.length - step; i <= last; i += step) if (values[i] > target) diff --git a/spine-as3/spine-as3/src/spine/animation/AnimationState.as b/spine-as3/spine-as3/src/spine/animation/AnimationState.as index 086f9faa6..f174b5eee 100644 --- a/spine-as3/spine-as3/src/spine/animation/AnimationState.as +++ b/spine-as3/spine-as3/src/spine/animation/AnimationState.as @@ -53,7 +53,7 @@ public class AnimationState { for (var i:int = 0; i < _tracks.length; i++) { var current:TrackEntry = _tracks[i]; if (!current) continue; - + current.time += delta * current.timeScale; if (current.previous) { var previousDelta:Number = delta * current.previous.timeScale; @@ -76,15 +76,15 @@ public class AnimationState { for (var i:int = 0; i < _tracks.length; i++) { var current:TrackEntry = _tracks[i]; if (!current) continue; - + _events.length = 0; - + var time:Number = current.time; var lastTime:Number = current.lastTime; var endTime:Number = current.endTime; var loop:Boolean = current.loop; if (!loop && time > endTime) time = endTime; - + var previous:TrackEntry = current.previous; if (!previous) { if (current.mix == 1) @@ -95,7 +95,7 @@ public class AnimationState { var previousTime:Number = previous.time; if (!previous.loop && previousTime > previous.endTime) previousTime = previous.endTime; previous.animation.apply(skeleton, previousTime, previousTime, previous.loop, null); - + var alpha:Number = current.mixTime / current.mixDuration * current.mix; if (alpha >= 1) { alpha = 1; @@ -103,7 +103,7 @@ public class AnimationState { } current.animation.mix(skeleton, current.lastTime, time, loop, _events, alpha); } - + for each (var event:Event in _events) { if (current.onEvent != null) current.onEvent(i, event); onEvent.invoke(i, event); @@ -123,9 +123,9 @@ public class AnimationState { public function clearTracks () : void { for (var i:int = 0, n:int = _tracks.length; i < n; i++) clearTrack(i); - _tracks.length = 0; + _tracks.length = 0; } - + public function clearTrack (trackIndex:int) : void { if (trackIndex >= _tracks.length) return; var current:TrackEntry = _tracks[trackIndex]; @@ -136,14 +136,14 @@ public class AnimationState { _tracks[trackIndex] = null; } - + private function expandToIndex (index:int) : TrackEntry { if (index < _tracks.length) return _tracks[index]; while (index >= _tracks.length) _tracks[_tracks.length] = null; return null; } - + private function setCurrent (index:int, entry:TrackEntry) : void { var current:TrackEntry = expandToIndex(index); if (current) { @@ -164,19 +164,19 @@ public class AnimationState { entry.previous = current; } } - + _tracks[index] = entry; if (entry.onStart != null) entry.onStart(index); onStart.invoke(index); } - + public function setAnimationByName (trackIndex:int, animationName:String, loop:Boolean) : TrackEntry { var animation:Animation = _data._skeletonData.findAnimation(animationName); if (!animation) throw new ArgumentError("Animation not found: " + animationName); return setAnimation(trackIndex, animation, loop); } - + /** Set the current animation. Any queued animations are cleared. */ public function setAnimation (trackIndex:int, animation:Animation, loop:Boolean) : TrackEntry { var entry:TrackEntry = new TrackEntry(); @@ -186,13 +186,13 @@ public class AnimationState { setCurrent(trackIndex, entry); return entry; } - + public function addAnimationByName (trackIndex:int, animationName:String, loop:Boolean, delay:Number) : TrackEntry { var animation:Animation = _data._skeletonData.findAnimation(animationName); if (!animation) throw new ArgumentError("Animation not found: " + animationName); return addAnimation(trackIndex, animation, loop, delay); } - + /** Adds an animation to be played delay seconds after the current or last queued animation. * @param delay May be <= 0 to use duration of previous animation minus any mix duration plus the negative delay. */ public function addAnimation (trackIndex:int, animation:Animation, loop:Boolean, delay:Number) : TrackEntry { @@ -200,7 +200,7 @@ public class AnimationState { entry.animation = animation; entry.loop = loop; entry.endTime = animation.duration; - + var last:TrackEntry = expandToIndex(trackIndex); if (last) { while (last.next) @@ -208,7 +208,7 @@ public class AnimationState { last.next = entry; } else _tracks[trackIndex] = entry; - + if (delay <= 0) { if (last) delay += last.endTime - _data.getMix(last.animation, animation); @@ -216,10 +216,10 @@ public class AnimationState { delay = 0; } entry.delay = delay; - + return entry; } - + /** May be null. */ public function getCurrent (trackIndex:int) : TrackEntry { if (trackIndex >= _tracks.length) return null; diff --git a/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as b/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as index 2aa10ebbd..b58c54690 100644 --- a/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/CurveTimeline.as @@ -37,7 +37,7 @@ import spine.Skeleton; public class CurveTimeline implements Timeline { static private const LINEAR:Number = 0; static private const STEPPED:Number = 1; - static private const BEZIER:Number = 2; + static private const BEZIER:Number = 2; static private const BEZIER_SIZE:int = 10 * 2 - 1; private var curves:Vector.; // type, x, y, ... diff --git a/spine-as3/spine-as3/src/spine/animation/DeformTimeline.as b/spine-as3/spine-as3/src/spine/animation/DeformTimeline.as index 336af315d..328fe1f5c 100644 --- a/spine-as3/spine-as3/src/spine/animation/DeformTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/DeformTimeline.as @@ -84,8 +84,8 @@ public class DeformTimeline extends CurveTimeline { var frame:int = Animation.binarySearch1(frames, time); var prevVertices:Vector. = frameVertices[int(frame - 1)]; var nextVertices:Vector. = frameVertices[frame]; - var frameTime:Number = frames[frame]; - var percent:Number = getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); + var frameTime:Number = frames[frame]; + var percent:Number = getCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime)); var prev:Number; if (alpha < 1) { diff --git a/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as b/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as index 29928c2bc..68975ca38 100644 --- a/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/DrawOrderTimeline.as @@ -70,7 +70,7 @@ public class DrawOrderTimeline implements Timeline { for each (var slot:Slot in slots) drawOrder[i++] = slot; } else { - for each (var setupIndex:int in drawOrderToSetupIndex) + for each (var setupIndex:int in drawOrderToSetupIndex) drawOrder[i++] = slots[setupIndex]; } } diff --git a/spine-as3/spine-as3/src/spine/animation/EventTimeline.as b/spine-as3/spine-as3/src/spine/animation/EventTimeline.as index 61a6a303f..be3269b2c 100644 --- a/spine-as3/spine-as3/src/spine/animation/EventTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/EventTimeline.as @@ -54,14 +54,14 @@ public class EventTimeline implements Timeline { /** Fires events for frames > lastTime and <= time. */ public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (!firedEvents) return; - + if (lastTime > time) { // Fire events after last time for looped animations. apply(skeleton, lastTime, int.MAX_VALUE, firedEvents, alpha); lastTime = -1; } else if (lastTime >= frames[int(frameCount - 1)]) // Last time is after last frame. return; if (time < frames[0]) return; // Time is before first frame. - + var frame:int; if (lastTime < frames[0]) frame = 0; diff --git a/spine-as3/spine-as3/src/spine/animation/Listeners.as b/spine-as3/spine-as3/src/spine/animation/Listeners.as index 0aa32d4a8..571dc3a9f 100644 --- a/spine-as3/spine-as3/src/spine/animation/Listeners.as +++ b/spine-as3/spine-as3/src/spine/animation/Listeners.as @@ -31,14 +31,14 @@ package spine.animation { public class Listeners { private var _listeners:Vector. = new Vector.(); - - public function Listeners () { + + public function Listeners () { } - + public function get listeners () : Vector. { return _listeners; } - + public function add (listener:Function) : void { if (listener == null) throw new ArgumentError("listener cannot be null."); diff --git a/spine-as3/spine-as3/src/spine/animation/PathConstraintMixTimeline.as b/spine-as3/spine-as3/src/spine/animation/PathConstraintMixTimeline.as index 11d594f69..cca88e4ea 100644 --- a/spine-as3/spine-as3/src/spine/animation/PathConstraintMixTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/PathConstraintMixTimeline.as @@ -39,13 +39,13 @@ public class PathConstraintMixTimeline extends CurveTimeline { static internal const ROTATE:int = 1, TRANSLATE:int = 2; public var pathConstraintIndex:int; - + public var frames:Vector.; // time, rotate mix, translate mix, ... public function PathConstraintMixTimeline (frameCount:int) { super(frameCount); frames = new Vector.(frameCount * ENTRIES, true); - } + } /** Sets the time and mixes of the specified keyframe. */ public function setFrame (frameIndex:int, time:Number, rotateMix:Number, translateMix:Number) : void { @@ -55,7 +55,7 @@ public class PathConstraintMixTimeline extends CurveTimeline { frames[frameIndex + TRANSLATE] = translateMix; } - override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { + override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (time < frames[0]) return; // Time is before first frame. var constraint:PathConstraint = skeleton.pathConstraints[pathConstraintIndex]; diff --git a/spine-as3/spine-as3/src/spine/animation/PathConstraintPositionTimeline.as b/spine-as3/spine-as3/src/spine/animation/PathConstraintPositionTimeline.as index 7bbbe6a04..821f1b704 100644 --- a/spine-as3/spine-as3/src/spine/animation/PathConstraintPositionTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/PathConstraintPositionTimeline.as @@ -32,7 +32,7 @@ package spine.animation { import spine.PathConstraint; import spine.Event; import spine.Skeleton; - + public class PathConstraintPositionTimeline extends CurveTimeline { static public const ENTRIES:int = 2; static internal const PREV_TIME:int = -2, PREV_VALUE:int = -1; @@ -54,7 +54,7 @@ public class PathConstraintPositionTimeline extends CurveTimeline { frames[frameIndex + VALUE] = value; } - override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { + override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (time < frames[0]) return; // Time is before first frame. var constraint:PathConstraint = skeleton.pathConstraints[pathConstraintIndex]; diff --git a/spine-as3/spine-as3/src/spine/animation/PathConstraintSpacingTimeline.as b/spine-as3/spine-as3/src/spine/animation/PathConstraintSpacingTimeline.as index aa5af6a5c..3f6e75cbd 100644 --- a/spine-as3/spine-as3/src/spine/animation/PathConstraintSpacingTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/PathConstraintSpacingTimeline.as @@ -38,7 +38,7 @@ public class PathConstraintSpacingTimeline extends PathConstraintPositionTimelin super(frameCount); } - override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { + override public function apply (skeleton:Skeleton, lastTime:Number, time:Number, firedEvents:Vector., alpha:Number) : void { if (time < frames[0]) return; // Time is before first frame. var constraint:PathConstraint = skeleton.pathConstraints[pathConstraintIndex]; diff --git a/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as b/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as index 4b584035a..140bb1af9 100644 --- a/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/RotateTimeline.as @@ -58,7 +58,7 @@ public class RotateTimeline extends CurveTimeline { return; // Time is before first frame. var bone:Bone = skeleton.bones[boneIndex]; - + if (time >= frames[int(frames.length - 2)]) { // Time is after last frame. var amount:Number = bone.data.rotation + frames[int(frames.length + PREV_ROTATION)] - bone.rotation; while (amount > 180) @@ -72,7 +72,7 @@ public class RotateTimeline extends CurveTimeline { // Interpolate between the previous frame and the current frame. var frame:int = Animation.binarySearch(frames, time, ENTRIES); var prevRotation:Number = frames[int(frame + PREV_ROTATION)]; - var frameTime:Number = frames[frame]; + var frameTime:Number = frames[frame]; var percent:Number = getCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime)); amount = frames[int(frame + ROTATION)] - prevRotation; diff --git a/spine-as3/spine-as3/src/spine/animation/TrackEntry.as b/spine-as3/spine-as3/src/spine/animation/TrackEntry.as index 2ec83ef49..2d9cb6073 100644 --- a/spine-as3/spine-as3/src/spine/animation/TrackEntry.as +++ b/spine-as3/spine-as3/src/spine/animation/TrackEntry.as @@ -38,10 +38,10 @@ public class TrackEntry { public var delay:Number, time:Number = 0, lastTime:Number = -1, endTime:Number, timeScale:Number = 1; internal var mixTime:Number, mixDuration:Number, mix:Number = 1; public var onStart:Function, onEnd:Function, onComplete:Function, onEvent:Function; - - public function TrackEntry () { + + public function TrackEntry () { } - + public function toString () : String { return animation == null ? "" : animation.name; } diff --git a/spine-as3/spine-as3/src/spine/animation/TransformConstraintTimeline.as b/spine-as3/spine-as3/src/spine/animation/TransformConstraintTimeline.as index 3f649c856..b7593e5ea 100644 --- a/spine-as3/spine-as3/src/spine/animation/TransformConstraintTimeline.as +++ b/spine-as3/spine-as3/src/spine/animation/TransformConstraintTimeline.as @@ -45,7 +45,7 @@ public class TransformConstraintTimeline extends CurveTimeline { super(frameCount); frames = new Vector.(frameCount * ENTRIES, true); } - + /** Sets the time and mixes of the specified keyframe. */ public function setFrame (frameIndex:int, time:Number, rotateMix:Number, translateMix:Number, scaleMix:Number, shearMix:Number) : void { frameIndex *= ENTRIES; diff --git a/spine-as3/spine-as3/src/spine/flash/FlashTextureLoader.as b/spine-as3/spine-as3/src/spine/flash/FlashTextureLoader.as index 6501a383d..a8b908890 100644 --- a/spine-as3/spine-as3/src/spine/flash/FlashTextureLoader.as +++ b/spine-as3/spine-as3/src/spine/flash/FlashTextureLoader.as @@ -40,7 +40,7 @@ public class FlashTextureLoader implements TextureLoader { public var bitmapDatas:Object = {}; public var singleBitmapData:BitmapData; - /** @param bitmaps A Bitmap or BitmapData for an atlas that has only one page, or for a multi page atlas an object where the + /** @param bitmaps A Bitmap or BitmapData for an atlas that has only one page, or for a multi page atlas an object where the * key is the image path and the value is the Bitmap or BitmapData. */ public function FlashTextureLoader (bitmaps:Object) { if (bitmaps is BitmapData) { diff --git a/spine-c/include/spine/Bone.h b/spine-c/include/spine/Bone.h index 2af95b2be..eb3bd6adc 100644 --- a/spine-c/include/spine/Bone.h +++ b/spine-c/include/spine/Bone.h @@ -67,7 +67,7 @@ struct spBone { a(0), b(0), worldX(0), c(0), d(0), worldY(0), worldSignX(0), worldSignY(0), - + sorted(0) { } #endif diff --git a/spine-c/include/spine/IkConstraint.h b/spine-c/include/spine/IkConstraint.h index d6025ab3b..20a9db149 100644 --- a/spine-c/include/spine/IkConstraint.h +++ b/spine-c/include/spine/IkConstraint.h @@ -42,10 +42,10 @@ struct spSkeleton; typedef struct spIkConstraint { spIkConstraintData* const data; - + int bonesCount; spBone** bones; - + spBone* target; int bendDirection; float mix; diff --git a/spine-c/include/spine/IkConstraintData.h b/spine-c/include/spine/IkConstraintData.h index e6d639924..92813dd42 100644 --- a/spine-c/include/spine/IkConstraintData.h +++ b/spine-c/include/spine/IkConstraintData.h @@ -39,10 +39,10 @@ extern "C" { typedef struct spIkConstraintData { const char* const name; - + int bonesCount; spBoneData** bones; - + spBoneData* target; int bendDirection; float mix; diff --git a/spine-c/src/spine/Animation.c b/spine-c/src/spine/Animation.c index ebe7abe09..87576a7bc 100644 --- a/spine-c/src/spine/Animation.c +++ b/spine-c/src/spine/Animation.c @@ -463,7 +463,7 @@ void _spColorTimeline_apply (const spTimeline* timeline, spSkeleton* skeleton, f frameTime = self->frames[frame]; percent = spCurveTimeline_getCurvePercent(SUPER(self), frame / COLOR_ENTRIES - 1, - 1 - (time - frameTime) / (self->frames[frame + COLOR_PREV_TIME] - frameTime)); + 1 - (time - frameTime) / (self->frames[frame + COLOR_PREV_TIME] - frameTime)); r += (self->frames[frame + COLOR_R] - r) * percent; g += (self->frames[frame + COLOR_G] - g) * percent; @@ -918,7 +918,7 @@ static const int PATHCONSTRAINTPOSITION_PREV_VALUE = -1; static const int PATHCONSTRAINTPOSITION_VALUE = 1; void _spPathConstraintPositionTimeline_apply(const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, - spEvent** firedEvents, int* eventsCount, float alpha) { + spEvent** firedEvents, int* eventsCount, float alpha) { int frame; float frameTime, percent, position; spPathConstraint* constraint; @@ -963,7 +963,7 @@ static const int PATHCONSTRAINTSPACING_PREV_VALUE = -1; static const int PATHCONSTRAINTSPACING_VALUE = 1; void _spPathConstraintSpacingTimeline_apply(const spTimeline* timeline, spSkeleton* skeleton, float lastTime, float time, - spEvent** firedEvents, int* eventsCount, float alpha) { + spEvent** firedEvents, int* eventsCount, float alpha) { int frame; float frameTime, percent, spacing; spPathConstraint* constraint; diff --git a/spine-c/src/spine/Atlas.c b/spine-c/src/spine/Atlas.c index 7da01e6d9..0937dabde 100644 --- a/spine-c/src/spine/Atlas.c +++ b/spine-c/src/spine/Atlas.c @@ -202,7 +202,7 @@ spAtlas* spAtlas_create (const char* begin, int length, const char* dir, void* r switch (readTuple(&begin, end, tuple)) { case 0: return abortAtlas(self); - case 2: /* size is only optional for an atlas packed with an old TexturePacker. */ + case 2: /* size is only optional for an atlas packed with an old TexturePacker. */ page->width = toInt(tuple); page->height = toInt(tuple + 1); if (!readTuple(&begin, end, tuple)) return abortAtlas(self); diff --git a/spine-c/src/spine/PathConstraint.c b/spine-c/src/spine/PathConstraint.c index e2bb64634..709237ed2 100644 --- a/spine-c/src/spine/PathConstraint.c +++ b/spine-c/src/spine/PathConstraint.c @@ -126,7 +126,7 @@ void spPathConstraint_apply (spPathConstraint* self) { } positions = spPathConstraint_computeWorldPositions(self, attachment, spacesCount, tangents, - data->positionMode == SP_POSITION_MODE_PERCENT, spacingMode == SP_SPACING_MODE_PERCENT); + data->positionMode == SP_POSITION_MODE_PERCENT, spacingMode == SP_SPACING_MODE_PERCENT); skeleton = self->target->bone->skeleton; skeletonX = skeleton->x, skeletonY = skeleton->y; boneX = positions[0], boneY = positions[1], offsetRotation = self->data->offsetRotation; @@ -192,7 +192,7 @@ static void _addAfterPosition (float p, float* temp, int i, float* out, int o) { } static void _addCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, - float* out, int o, int/*bool*/tangents) { + float* out, int o, int/*bool*/tangents) { float tt, ttt, u, uu, uuu; float ut, ut3, uut3, utt3; float x, y; @@ -283,7 +283,7 @@ float* spPathConstraint_computeWorldPositions(spPathConstraint* self, spPathAtta spPathAttachment_computeWorldVertices1(path, target, curve * 6 + 2, 8, world, 0); } _addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7], out, o, - tangents || (i > 0 && space == 0)); + tangents || (i > 0 && space == 0)); } return out; } diff --git a/spine-c/src/spine/SkeletonJson.c b/spine-c/src/spine/SkeletonJson.c index bd6f49c40..21bb53c5f 100644 --- a/spine-c/src/spine/SkeletonJson.c +++ b/spine-c/src/spine/SkeletonJson.c @@ -353,7 +353,7 @@ static spAnimation* _spSkeletonJson_readAnimation (spSkeletonJson* self, Json* r timeline->pathConstraintIndex = constraintIndex; for (valueMap = timelineMap->child, frameIndex = 0; valueMap; valueMap = valueMap->next, ++frameIndex) { spPathConstraintMixTimeline_setFrame(timeline, frameIndex, Json_getFloat(valueMap, "time", 0), - Json_getFloat(valueMap, "rotateMix", 1), Json_getFloat(valueMap, "translateMix", 1)); + Json_getFloat(valueMap, "rotateMix", 1), Json_getFloat(valueMap, "translateMix", 1)); readCurve(valueMap, SUPER(timeline), frameIndex); } animation->timelines[animation->timelinesCount++] = SUPER_CAST(spTimeline, timeline); @@ -576,14 +576,14 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha oldLocale = strdup(setlocale(LC_NUMERIC, NULL)); setlocale(LC_NUMERIC, "C"); #endif - + root = Json_create(json); - + #ifndef __ANDROID__ setlocale(LC_NUMERIC, oldLocale); free(oldLocale); #endif - + if (!root) { _spSkeletonJson_setError(self, 0, "Invalid skeleton JSON: ", Json_getError()); return 0; @@ -594,7 +594,7 @@ spSkeletonData* spSkeletonJson_readSkeletonData (spSkeletonJson* self, const cha skeleton = Json_getItem(root, "skeleton"); if (skeleton) { MALLOC_STR(skeletonData->hash, Json_getString(skeleton, "hash", 0)); - MALLOC_STR(skeletonData->version, Json_getString(skeleton, "spine", 0)); + MALLOC_STR(skeletonData->version, Json_getString(skeleton, "spine", 0)); skeletonData->width = Json_getFloat(skeleton, "width", 0); skeletonData->height = Json_getFloat(skeleton, "height", 0); } diff --git a/spine-corona/config.lua b/spine-corona/config.lua index 643443087..af8c3f22d 100644 --- a/spine-corona/config.lua +++ b/spine-corona/config.lua @@ -1,7 +1,7 @@ application = { content = { width = 320, - height = 480, + height = 480, scale = "letterBox", fps = 60, }, diff --git a/spine-corona/main.lua b/spine-corona/main.lua index 01ac6bce9..819b3f57c 100644 --- a/spine-corona/main.lua +++ b/spine-corona/main.lua @@ -7,56 +7,56 @@ local activeSkeleton = 1 local lastTime = 0 function loadSkeleton(atlasFile, jsonFile, x, y, scale, animation, skin) - -- to load an atlas, we need to define a function that returns - -- a Corona paint object. This allows you to resolve images - -- however you see fit - local imageLoader = function (path) - local paint = { type = "image", filename = "data/" .. path } - return paint - end - - -- load the atlas - local atlas = spine.TextureAtlas.new(spine.utils.readFile("data/" .. atlasFile), imageLoader) - - -- load the JSON and create a Skeleton from it - local json = spine.SkeletonJson.new(spine.AtlasAttachmentLoader.new(atlas)) - json.scale = scale - local skeletonData = json:readSkeletonDataFile("data/" .. jsonFile) - local skeleton = spine.Skeleton.new(skeletonData) - skeleton.flipY = true -- Corona's coordinate system has its y-axis point downwards - skeleton.group.x = x - skeleton.group.y = y - - -- Set the skin if we got one - if skin then skeleton:setSkin(skin) end - - -- create an animation state object to apply animations to the skeleton - local animationStateData = spine.AnimationStateData.new(skeletonData) - local animationState = spine.AnimationState.new(animationStateData) - animationState:setAnimationByName(0, animation, true) - - -- set the skeleton invisible - skeleton.group.isVisible = false - - -- set a name on the group of the skeleton so we can find it during debugging - skeleton.group.name = jsonFile - - -- set some event callbacks - animationState.onStart = function (trackIndex) - print(trackIndex.." start: "..animationState:getCurrent(trackIndex).animation.name) - end - animationState.onEnd = function (trackIndex) - print(trackIndex.." end: "..animationState:getCurrent(trackIndex).animation.name) - end - animationState.onComplete = function (trackIndex, loopCount) - print(trackIndex.." complete: "..animationState:getCurrent(trackIndex).animation.name..", "..loopCount) - end - animationState.onEvent = function (trackIndex, event) - print(trackIndex.." event: "..animationState:getCurrent(trackIndex).animation.name..", "..event.data.name..", "..event.intValue..", "..event.floatValue..", '"..(event.stringValue or "").."'") - end - - -- return the skeleton an animation state - return { skeleton = skeleton, state = animationState } + -- to load an atlas, we need to define a function that returns + -- a Corona paint object. This allows you to resolve images + -- however you see fit + local imageLoader = function (path) + local paint = { type = "image", filename = "data/" .. path } + return paint + end + + -- load the atlas + local atlas = spine.TextureAtlas.new(spine.utils.readFile("data/" .. atlasFile), imageLoader) + + -- load the JSON and create a Skeleton from it + local json = spine.SkeletonJson.new(spine.AtlasAttachmentLoader.new(atlas)) + json.scale = scale + local skeletonData = json:readSkeletonDataFile("data/" .. jsonFile) + local skeleton = spine.Skeleton.new(skeletonData) + skeleton.flipY = true -- Corona's coordinate system has its y-axis point downwards + skeleton.group.x = x + skeleton.group.y = y + + -- Set the skin if we got one + if skin then skeleton:setSkin(skin) end + + -- create an animation state object to apply animations to the skeleton + local animationStateData = spine.AnimationStateData.new(skeletonData) + local animationState = spine.AnimationState.new(animationStateData) + animationState:setAnimationByName(0, animation, true) + + -- set the skeleton invisible + skeleton.group.isVisible = false + + -- set a name on the group of the skeleton so we can find it during debugging + skeleton.group.name = jsonFile + + -- set some event callbacks + animationState.onStart = function (trackIndex) + print(trackIndex.." start: "..animationState:getCurrent(trackIndex).animation.name) + end + animationState.onEnd = function (trackIndex) + print(trackIndex.." end: "..animationState:getCurrent(trackIndex).animation.name) + end + animationState.onComplete = function (trackIndex, loopCount) + print(trackIndex.." complete: "..animationState:getCurrent(trackIndex).animation.name..", "..loopCount) + end + animationState.onEvent = function (trackIndex, event) + print(trackIndex.." event: "..animationState:getCurrent(trackIndex).animation.name..", "..event.data.name..", "..event.intValue..", "..event.floatValue..", '"..(event.stringValue or "").."'") + end + + -- return the skeleton an animation state + return { skeleton = skeleton, state = animationState } end table.insert(skeletons, loadSkeleton("spineboy.atlas", "spineboy.json", 240, 300, 0.4, "walk")) @@ -68,26 +68,26 @@ table.insert(skeletons, loadSkeleton("vine.atlas", "vine.json", 240, 300, 0.3, " display.setDefault("background", 0.2, 0.2, 0.2, 1) -Runtime:addEventListener("enterFrame", function (event) +Runtime:addEventListener("enterFrame", function (event) local currentTime = event.time / 1000 local delta = currentTime - lastTime - lastTime = currentTime - - skeleton = skeletons[activeSkeleton].skeleton - skeleton.group.isVisible = true - state = skeletons[activeSkeleton].state - - state:update(delta) - state:apply(skeleton) - skeleton:updateWorldTransform() - - -- uncomment if you want to know how many batches a skeleton renders to - -- print(skeleton.batches) + lastTime = currentTime + + skeleton = skeletons[activeSkeleton].skeleton + skeleton.group.isVisible = true + state = skeletons[activeSkeleton].state + + state:update(delta) + state:apply(skeleton) + skeleton:updateWorldTransform() + + -- uncomment if you want to know how many batches a skeleton renders to + -- print(skeleton.batches) end) - + Runtime:addEventListener("tap", function(event) - skeletons[activeSkeleton].skeleton.group.isVisible = false - activeSkeleton = activeSkeleton + 1 - if activeSkeleton > #skeletons then activeSkeleton = 1 end - skeletons[activeSkeleton].skeleton.group.isVisible = true + skeletons[activeSkeleton].skeleton.group.isVisible = false + activeSkeleton = activeSkeleton + 1 + if activeSkeleton > #skeletons then activeSkeleton = 1 end + skeletons[activeSkeleton].skeleton.group.isVisible = true end) \ No newline at end of file diff --git a/spine-corona/mobdebug.lua b/spine-corona/mobdebug.lua index 31ef8af06..57e891ea2 100644 --- a/spine-corona/mobdebug.lua +++ b/spine-corona/mobdebug.lua @@ -5,13 +5,13 @@ -- local mobdebug = { - _NAME = "mobdebug", - _VERSION = 0.542, - _COPYRIGHT = "Paul Kulchenko", - _DESCRIPTION = "Mobile Remote Debugger for the Lua programming language", - port = os and os.getenv and os.getenv("MOBDEBUG_PORT") or 8172, - checkcount = 200, - yieldtimeout = 0.02, + _NAME = "mobdebug", + _VERSION = 0.542, + _COPYRIGHT = "Paul Kulchenko", + _DESCRIPTION = "Mobile Remote Debugger for the Lua programming language", + port = os and os.getenv and os.getenv("MOBDEBUG_PORT") or 8172, + checkcount = 200, + yieldtimeout = 0.02, } local coroutine = coroutine @@ -41,21 +41,21 @@ local jit = rawget(genv, "jit") local MOAICoroutine = rawget(genv, "MOAICoroutine") if not setfenv then -- Lua 5.2 - -- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html - -- this assumes f is a function - local function findenv(f) - local level = 1 - repeat - local name, value = debug.getupvalue(f, level) - if name == '_ENV' then return level, value end - level = level + 1 - until name == nil - return nil end - getfenv = function (f) return(select(2, findenv(f)) or _G) end - setfenv = function (f, t) - local level = findenv(f) - if level then debug.setupvalue(f, level, t) end - return f end + -- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html + -- this assumes f is a function + local function findenv(f) + local level = 1 + repeat + local name, value = debug.getupvalue(f, level) + if name == '_ENV' then return level, value end + level = level + 1 + until name == nil + return nil end + getfenv = function (f) return(select(2, findenv(f)) or _G) end + setfenv = function (f, t) + local level = findenv(f) + if level then debug.setupvalue(f, level, t) end + return f end end -- check for OS and convert file names to lower case on windows @@ -63,9 +63,9 @@ end -- breakpoint on x:\Foo.lua will not work if the file was loaded as X:\foo.lua. -- OSX and Windows behave the same way (case insensitive, but case preserving) local iscasepreserving = os and os.getenv and (os.getenv('WINDIR') - or (os.getenv('OS') or ''):match('[Ww]indows') - or os.getenv('DYLD_LIBRARY_PATH')) - or not io.open("/proc") + or (os.getenv('OS') or ''):match('[Ww]indows') + or os.getenv('DYLD_LIBRARY_PATH')) + or not io.open("/proc") -- turn jit off based on Mike Pall's comment in this discussion: -- http://www.freelists.org/post/luajit/Debug-hooks-and-JIT,2 @@ -98,9 +98,9 @@ local iobase = {print = print} local basedir = "" local deferror = "execution aborted at default debugee" local debugee = function () - local a = 1 - for _ = 1, 10 do a = a + 1 end - error(deferror) + local a = 1 + for _ = 1, 10 do a = a + 1 end + error(deferror) end local function q(s) return s:gsub('([%(%)%.%%%+%-%*%?%[%^%$%]])','%%%1') end @@ -111,1335 +111,1335 @@ local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[- local badtype = {thread = true, userdata = true, cdata = true} local keyword, globals, G = {}, {}, (_G or _ENV) for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false', - 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', - 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end + 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat', + 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end for k,v in pairs(G) do globals[v] = k end -- build func to name mapping for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do - for k,v in pairs(G[g]) do globals[v] = g..'.'..k end end + for k,v in pairs(G[g]) do globals[v] = g..'.'..k end end local function s(t, opts) - local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum - local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge - local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge) - local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge) - local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0 - local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)", - -- tostring(val) is needed because __tostring may return a non-string value - function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return syms[s] end)) end - local function safestr(s) return type(s) == "number" and (huge and snum[tostring(s)] or s) - or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 - or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end - local function comment(s,l) return comm and (l or 0) < comm and ' --[['..tostring(s)..']]' or '' end - local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal - and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end - local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] - local n = name == nil and '' or name - local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] - local safe = plain and n or '['..safestr(n)..']' - return (path or '')..(plain and path and '.' or '')..safe, safe end - local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding - local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'} - local function padnum(d) return ("%0"..maxn.."d"):format(d) end - table.sort(k, function(a,b) - -- sort numeric keys first: k[key] is non-nil for numeric keys - return (k[a] and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum)) - < (k[b] and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end - local function val2str(t, name, indent, insref, path, plainindex, level) - local ttype, level, mt = type(t), (level or 0), getmetatable(t) - local spath, sname = safename(path, name) - local tag = plainindex and - ((type(name) == "number") and '' or name..space..'='..space) or - (name ~= nil and sname..space..'='..space or '') - if seen[t] then -- already seen this element - sref[#sref+1] = spath..space..'='..space..seen[t] - return tag..'nil'..comment('ref', level) end - if type(mt) == 'table' and (mt.__serialize or mt.__tostring) then -- knows how to serialize itself - seen[t] = insref or spath - if mt.__serialize then t = mt.__serialize(t) else t = tostring(t) end - ttype = type(t) end -- new value falls through to be serialized - if ttype == "table" then - if level >= maxl then return tag..'{}'..comment('max', level) end - seen[t] = insref or spath - if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty - local maxn, o, out = math.min(#t, maxnum or #t), {}, {} - for key = 1, maxn do o[key] = key end - if not maxnum or #o < maxnum then - local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables - for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end - if maxnum and #o > maxnum then o[maxnum+1] = nil end - if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end - local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) - for n, key in ipairs(o) do - local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse - if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing - or opts.keyallow and not opts.keyallow[key] - or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types - or sparse and value == nil then -- skipping nils; do nothing - elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then - if not seen[key] and not globals[key] then - sref[#sref+1] = 'placeholder' - local sname = safename(iname, gensym(key)) -- iname is table for local variables - sref[#sref] = val2str(key,sname,indent,sname,iname,true) end - sref[#sref+1] = 'placeholder' - local path = seen[t]..'['..(seen[key] or globals[key] or gensym(key))..']' - sref[#sref] = path..space..'='..space..(seen[value] or val2str(value,nil,indent,path)) - else - out[#out+1] = val2str(value,key,indent,insref,seen[t],plainindex,level+1) - end - end - local prefix = string.rep(indent or '', level) - local head = indent and '{\n'..prefix..indent or '{' - local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space)) - local tail = indent and "\n"..prefix..'}' or '}' - return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level) - elseif badtype[ttype] then - seen[t] = insref or spath - return tag..globerr(t, level) - elseif ttype == 'function' then - seen[t] = insref or spath - local ok, res = pcall(string.dump, t) - local func = ok and ((opts.nocode and "function() --[[..skipped..]] end" or - "loadstring("..safestr(res)..",'@serialized')")..comment(t, level)) - return tag..(func or globerr(t, level)) - else return tag..safestr(t) end -- handle all other types - end - local sepr = indent and "\n" or ";"..space - local body = val2str(t, name, indent) -- this call also populates sref - local tail = #sref>1 and table.concat(sref, sepr)..sepr or '' - local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or '' - return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end" + local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum + local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge + local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge) + local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge) + local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0 + local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)", + -- tostring(val) is needed because __tostring may return a non-string value + function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return syms[s] end)) end + local function safestr(s) return type(s) == "number" and (huge and snum[tostring(s)] or s) + or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026 + or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end + local function comment(s,l) return comm and (l or 0) < comm and ' --[['..tostring(s)..']]' or '' end + local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal + and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end + local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r'] + local n = name == nil and '' or name + local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n] + local safe = plain and n or '['..safestr(n)..']' + return (path or '')..(plain and path and '.' or '')..safe, safe end + local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding + local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'} + local function padnum(d) return ("%0"..maxn.."d"):format(d) end + table.sort(k, function(a,b) + -- sort numeric keys first: k[key] is non-nil for numeric keys + return (k[a] and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum)) + < (k[b] and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end + local function val2str(t, name, indent, insref, path, plainindex, level) + local ttype, level, mt = type(t), (level or 0), getmetatable(t) + local spath, sname = safename(path, name) + local tag = plainindex and + ((type(name) == "number") and '' or name..space..'='..space) or + (name ~= nil and sname..space..'='..space or '') + if seen[t] then -- already seen this element + sref[#sref+1] = spath..space..'='..space..seen[t] + return tag..'nil'..comment('ref', level) end + if type(mt) == 'table' and (mt.__serialize or mt.__tostring) then -- knows how to serialize itself + seen[t] = insref or spath + if mt.__serialize then t = mt.__serialize(t) else t = tostring(t) end + ttype = type(t) end -- new value falls through to be serialized + if ttype == "table" then + if level >= maxl then return tag..'{}'..comment('max', level) end + seen[t] = insref or spath + if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty + local maxn, o, out = math.min(#t, maxnum or #t), {}, {} + for key = 1, maxn do o[key] = key end + if not maxnum or #o < maxnum then + local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables + for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end + if maxnum and #o > maxnum then o[maxnum+1] = nil end + if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end + local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output) + for n, key in ipairs(o) do + local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse + if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing + or opts.keyallow and not opts.keyallow[key] + or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types + or sparse and value == nil then -- skipping nils; do nothing + elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then + if not seen[key] and not globals[key] then + sref[#sref+1] = 'placeholder' + local sname = safename(iname, gensym(key)) -- iname is table for local variables + sref[#sref] = val2str(key,sname,indent,sname,iname,true) end + sref[#sref+1] = 'placeholder' + local path = seen[t]..'['..(seen[key] or globals[key] or gensym(key))..']' + sref[#sref] = path..space..'='..space..(seen[value] or val2str(value,nil,indent,path)) + else + out[#out+1] = val2str(value,key,indent,insref,seen[t],plainindex,level+1) + end + end + local prefix = string.rep(indent or '', level) + local head = indent and '{\n'..prefix..indent or '{' + local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space)) + local tail = indent and "\n"..prefix..'}' or '}' + return (custom and custom(tag,head,body,tail) or tag..head..body..tail)..comment(t, level) + elseif badtype[ttype] then + seen[t] = insref or spath + return tag..globerr(t, level) + elseif ttype == 'function' then + seen[t] = insref or spath + local ok, res = pcall(string.dump, t) + local func = ok and ((opts.nocode and "function() --[[..skipped..]] end" or + "loadstring("..safestr(res)..",'@serialized')")..comment(t, level)) + return tag..(func or globerr(t, level)) + else return tag..safestr(t) end -- handle all other types + end + local sepr = indent and "\n" or ";"..space + local body = val2str(t, name, indent) -- this call also populates sref + local tail = #sref>1 and table.concat(sref, sepr)..sepr or '' + local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or '' + return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end" end local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s, - dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end, - line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end, - block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end } + dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end, + line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end, + block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end } end)() ---- end of Serpent module local function removebasedir(path, basedir) - if iscasepreserving then - -- check if the lowercased path matches the basedir - -- if so, return substring of the original path (to not lowercase it) - return path:lower():find('^'..q(basedir:lower())) - and path:sub(#basedir+1) or path - else - return string.gsub(path, '^'..q(basedir), '') - end + if iscasepreserving then + -- check if the lowercased path matches the basedir + -- if so, return substring of the original path (to not lowercase it) + return path:lower():find('^'..q(basedir:lower())) + and path:sub(#basedir+1) or path + else + return string.gsub(path, '^'..q(basedir), '') + end end local function stack(start) - local function vars(f) - local func = debug.getinfo(f, "f").func - local i = 1 - local locals = {} - while true do - local name, value = debug.getlocal(f, i) - if not name then break end - if string.sub(name, 1, 1) ~= '(' then locals[name] = {value, tostring(value)} end - i = i + 1 - end - i = 1 - local ups = {} - while func and true do -- check for func as it may be nil for tail calls - local name, value = debug.getupvalue(func, i) - if not name then break end - ups[name] = {value, tostring(value)} - i = i + 1 - end - return locals, ups - end + local function vars(f) + local func = debug.getinfo(f, "f").func + local i = 1 + local locals = {} + while true do + local name, value = debug.getlocal(f, i) + if not name then break end + if string.sub(name, 1, 1) ~= '(' then locals[name] = {value, tostring(value)} end + i = i + 1 + end + i = 1 + local ups = {} + while func and true do -- check for func as it may be nil for tail calls + local name, value = debug.getupvalue(func, i) + if not name then break end + ups[name] = {value, tostring(value)} + i = i + 1 + end + return locals, ups + end - local stack = {} - for i = (start or 0), 100 do - local source = debug.getinfo(i, "Snl") - if not source then break end + local stack = {} + for i = (start or 0), 100 do + local source = debug.getinfo(i, "Snl") + if not source then break end - local src = source.source - if src:find("@") == 1 then - src = src:sub(2):gsub("\\", "/") - if src:find("%./") == 1 then src = src:sub(3) end - end + local src = source.source + if src:find("@") == 1 then + src = src:sub(2):gsub("\\", "/") + if src:find("%./") == 1 then src = src:sub(3) end + end - table.insert(stack, { -- remove basedir from source - {source.name, removebasedir(src, basedir), source.linedefined, - source.currentline, source.what, source.namewhat, source.short_src}, - vars(i+1)}) - if source.what == 'main' then break end - end - return stack + table.insert(stack, { -- remove basedir from source + {source.name, removebasedir(src, basedir), source.linedefined, + source.currentline, source.what, source.namewhat, source.short_src}, + vars(i+1)}) + if source.what == 'main' then break end + end + return stack end local function set_breakpoint(file, line) - if file == '-' and lastfile then file = lastfile - elseif iscasepreserving then file = string.lower(file) end - if not breakpoints[line] then breakpoints[line] = {} end - breakpoints[line][file] = true + if file == '-' and lastfile then file = lastfile + elseif iscasepreserving then file = string.lower(file) end + if not breakpoints[line] then breakpoints[line] = {} end + breakpoints[line][file] = true end local function remove_breakpoint(file, line) - if file == '-' and lastfile then file = lastfile - elseif iscasepreserving then file = string.lower(file) end - if breakpoints[line] then breakpoints[line][file] = nil end + if file == '-' and lastfile then file = lastfile + elseif iscasepreserving then file = string.lower(file) end + if breakpoints[line] then breakpoints[line][file] = nil end end local function has_breakpoint(file, line) - return breakpoints[line] - and breakpoints[line][iscasepreserving and string.lower(file) or file] + return breakpoints[line] + and breakpoints[line][iscasepreserving and string.lower(file) or file] end local function restore_vars(vars) - if type(vars) ~= 'table' then return end + if type(vars) ~= 'table' then return end - -- locals need to be processed in the reverse order, starting from - -- the inner block out, to make sure that the localized variables - -- are correctly updated with only the closest variable with - -- the same name being changed - -- first loop find how many local variables there is, while - -- the second loop processes them from i to 1 - local i = 1 - while true do - local name = debug.getlocal(3, i) - if not name then break end - i = i + 1 - end - i = i - 1 - local written_vars = {} - while i > 0 do - local name = debug.getlocal(3, i) - if not written_vars[name] then - if string.sub(name, 1, 1) ~= '(' then - debug.setlocal(3, i, rawget(vars, name)) - end - written_vars[name] = true - end - i = i - 1 - end + -- locals need to be processed in the reverse order, starting from + -- the inner block out, to make sure that the localized variables + -- are correctly updated with only the closest variable with + -- the same name being changed + -- first loop find how many local variables there is, while + -- the second loop processes them from i to 1 + local i = 1 + while true do + local name = debug.getlocal(3, i) + if not name then break end + i = i + 1 + end + i = i - 1 + local written_vars = {} + while i > 0 do + local name = debug.getlocal(3, i) + if not written_vars[name] then + if string.sub(name, 1, 1) ~= '(' then + debug.setlocal(3, i, rawget(vars, name)) + end + written_vars[name] = true + end + i = i - 1 + end - i = 1 - local func = debug.getinfo(3, "f").func - while true do - local name = debug.getupvalue(func, i) - if not name then break end - if not written_vars[name] then - if string.sub(name, 1, 1) ~= '(' then - debug.setupvalue(func, i, rawget(vars, name)) - end - written_vars[name] = true - end - i = i + 1 - end + i = 1 + local func = debug.getinfo(3, "f").func + while true do + local name = debug.getupvalue(func, i) + if not name then break end + if not written_vars[name] then + if string.sub(name, 1, 1) ~= '(' then + debug.setupvalue(func, i, rawget(vars, name)) + end + written_vars[name] = true + end + i = i + 1 + end end local function capture_vars(level) - local vars = {} - local func = debug.getinfo(level or 3, "f").func - local i = 1 - while true do - local name, value = debug.getupvalue(func, i) - if not name then break end - if string.sub(name, 1, 1) ~= '(' then vars[name] = value end - i = i + 1 - end - i = 1 - while true do - local name, value = debug.getlocal(level or 3, i) - if not name then break end - if string.sub(name, 1, 1) ~= '(' then vars[name] = value end - i = i + 1 - end - -- returned 'vars' table plays a dual role: (1) it captures local values - -- and upvalues to be restored later (in case they are modified in "eval"), - -- and (2) it provides an environment for evaluated chunks. - -- getfenv(func) is needed to provide proper environment for functions, - -- including access to globals, but this causes vars[name] to fail in - -- restore_vars on local variables or upvalues with `nil` values when - -- 'strict' is in effect. To avoid this `rawget` is used in restore_vars. - setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) }) - return vars + local vars = {} + local func = debug.getinfo(level or 3, "f").func + local i = 1 + while true do + local name, value = debug.getupvalue(func, i) + if not name then break end + if string.sub(name, 1, 1) ~= '(' then vars[name] = value end + i = i + 1 + end + i = 1 + while true do + local name, value = debug.getlocal(level or 3, i) + if not name then break end + if string.sub(name, 1, 1) ~= '(' then vars[name] = value end + i = i + 1 + end + -- returned 'vars' table plays a dual role: (1) it captures local values + -- and upvalues to be restored later (in case they are modified in "eval"), + -- and (2) it provides an environment for evaluated chunks. + -- getfenv(func) is needed to provide proper environment for functions, + -- including access to globals, but this causes vars[name] to fail in + -- restore_vars on local variables or upvalues with `nil` values when + -- 'strict' is in effect. To avoid this `rawget` is used in restore_vars. + setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) }) + return vars end local function stack_depth(start_depth) - for i = start_depth, 0, -1 do - if debug.getinfo(i, "l") then return i+1 end - end - return start_depth + for i = start_depth, 0, -1 do + if debug.getinfo(i, "l") then return i+1 end + end + return start_depth end local function is_safe(stack_level) - -- the stack grows up: 0 is getinfo, 1 is is_safe, 2 is debug_hook, 3 is user function - if stack_level == 3 then return true end - for i = 3, stack_level do - -- return if it is not safe to abort - local info = debug.getinfo(i, "S") - if not info then return true end - if info.what == "C" then return false end - end - return true + -- the stack grows up: 0 is getinfo, 1 is is_safe, 2 is debug_hook, 3 is user function + if stack_level == 3 then return true end + for i = 3, stack_level do + -- return if it is not safe to abort + local info = debug.getinfo(i, "S") + if not info then return true end + if info.what == "C" then return false end + end + return true end local function in_debugger() - local this = debug.getinfo(1, "S").source - -- only need to check few frames as mobdebug frames should be close - for i = 3, 7 do - local info = debug.getinfo(i, "S") - if not info then return false end - if info.source == this then return true end - end - return false + local this = debug.getinfo(1, "S").source + -- only need to check few frames as mobdebug frames should be close + for i = 3, 7 do + local info = debug.getinfo(i, "S") + if not info then return false end + if info.source == this then return true end + end + return false end local function is_pending(peer) - -- if there is something already in the buffer, skip check - if not buf and checkcount >= mobdebug.checkcount then - peer:settimeout(0) -- non-blocking - buf = peer:receive(1) - peer:settimeout() -- back to blocking - checkcount = 0 - end - return buf + -- if there is something already in the buffer, skip check + if not buf and checkcount >= mobdebug.checkcount then + peer:settimeout(0) -- non-blocking + buf = peer:receive(1) + peer:settimeout() -- back to blocking + checkcount = 0 + end + return buf end local function debug_hook(event, line) - -- (1) LuaJIT needs special treatment. Because debug_hook is set for - -- *all* coroutines, and not just the one being debugged as in regular Lua - -- (http://lua-users.org/lists/lua-l/2011-06/msg00513.html), - -- need to avoid debugging mobdebug's own code as LuaJIT doesn't - -- always correctly generate call/return hook events (there are more - -- calls than returns, which breaks stack depth calculation and - -- 'step' and 'step over' commands stop working; possibly because - -- 'tail return' events are not generated by LuaJIT). - -- the next line checks if the debugger is run under LuaJIT and if - -- one of debugger methods is present in the stack, it simply returns. - if jit then - -- when luajit is compiled with LUAJIT_ENABLE_LUA52COMPAT, - -- coroutine.running() returns non-nil for the main thread. - local coro, main = coroutine.running() - if not coro or main then coro = 'main' end - local disabled = coroutines[coro] == false - or coroutines[coro] == nil and coro ~= (coro_debugee or 'main') - if coro_debugee and disabled or not coro_debugee and (disabled or in_debugger()) - then return end - end + -- (1) LuaJIT needs special treatment. Because debug_hook is set for + -- *all* coroutines, and not just the one being debugged as in regular Lua + -- (http://lua-users.org/lists/lua-l/2011-06/msg00513.html), + -- need to avoid debugging mobdebug's own code as LuaJIT doesn't + -- always correctly generate call/return hook events (there are more + -- calls than returns, which breaks stack depth calculation and + -- 'step' and 'step over' commands stop working; possibly because + -- 'tail return' events are not generated by LuaJIT). + -- the next line checks if the debugger is run under LuaJIT and if + -- one of debugger methods is present in the stack, it simply returns. + if jit then + -- when luajit is compiled with LUAJIT_ENABLE_LUA52COMPAT, + -- coroutine.running() returns non-nil for the main thread. + local coro, main = coroutine.running() + if not coro or main then coro = 'main' end + local disabled = coroutines[coro] == false + or coroutines[coro] == nil and coro ~= (coro_debugee or 'main') + if coro_debugee and disabled or not coro_debugee and (disabled or in_debugger()) + then return end + end - -- (2) check if abort has been requested and it's safe to abort - if abort and is_safe(stack_level) then error(abort) end + -- (2) check if abort has been requested and it's safe to abort + if abort and is_safe(stack_level) then error(abort) end - -- (3) also check if this debug hook has not been visited for any reason. - -- this check is needed to avoid stepping in too early - -- (for example, when coroutine.resume() is executed inside start()). - if not seen_hook and in_debugger() then return end + -- (3) also check if this debug hook has not been visited for any reason. + -- this check is needed to avoid stepping in too early + -- (for example, when coroutine.resume() is executed inside start()). + if not seen_hook and in_debugger() then return end - if event == "call" then - stack_level = stack_level + 1 - elseif event == "return" or event == "tail return" then - stack_level = stack_level - 1 - elseif event == "line" then - -- may need to fall through because of the following: - -- (1) step_into - -- (2) step_over and stack_level <= step_level (need stack_level) - -- (3) breakpoint; check for line first as it's known; then for file - -- (4) socket call (only do every Xth check) - -- (5) at least one watch is registered - if not ( - step_into or step_over or breakpoints[line] or watchescnt > 0 - or is_pending(server) - ) then checkcount = checkcount + 1; return end + if event == "call" then + stack_level = stack_level + 1 + elseif event == "return" or event == "tail return" then + stack_level = stack_level - 1 + elseif event == "line" then + -- may need to fall through because of the following: + -- (1) step_into + -- (2) step_over and stack_level <= step_level (need stack_level) + -- (3) breakpoint; check for line first as it's known; then for file + -- (4) socket call (only do every Xth check) + -- (5) at least one watch is registered + if not ( + step_into or step_over or breakpoints[line] or watchescnt > 0 + or is_pending(server) + ) then checkcount = checkcount + 1; return end - checkcount = mobdebug.checkcount -- force check on the next command + checkcount = mobdebug.checkcount -- force check on the next command - -- this is needed to check if the stack got shorter or longer. - -- unfortunately counting call/return calls is not reliable. - -- the discrepancy may happen when "pcall(load, '')" call is made - -- or when "error()" is called in a function. - -- in either case there are more "call" than "return" events reported. - -- this validation is done for every "line" event, but should be "cheap" - -- as it checks for the stack to get shorter (or longer by one call). - -- start from one level higher just in case we need to grow the stack. - -- this may happen after coroutine.resume call to a function that doesn't - -- have any other instructions to execute. it triggers three returns: - -- "return, tail return, return", which needs to be accounted for. - stack_level = stack_depth(stack_level+1) + -- this is needed to check if the stack got shorter or longer. + -- unfortunately counting call/return calls is not reliable. + -- the discrepancy may happen when "pcall(load, '')" call is made + -- or when "error()" is called in a function. + -- in either case there are more "call" than "return" events reported. + -- this validation is done for every "line" event, but should be "cheap" + -- as it checks for the stack to get shorter (or longer by one call). + -- start from one level higher just in case we need to grow the stack. + -- this may happen after coroutine.resume call to a function that doesn't + -- have any other instructions to execute. it triggers three returns: + -- "return, tail return, return", which needs to be accounted for. + stack_level = stack_depth(stack_level+1) - local caller = debug.getinfo(2, "S") + local caller = debug.getinfo(2, "S") - -- grab the filename and fix it if needed - local file = lastfile - if (lastsource ~= caller.source) then - file, lastsource = caller.source, caller.source - -- technically, users can supply names that may not use '@', - -- for example when they call loadstring('...', 'filename.lua'). - -- Unfortunately, there is no reliable/quick way to figure out - -- what is the filename and what is the source code. - -- The following will work if the supplied filename uses Unix path. - if file:find("^@") then - file = file:gsub("^@", ""):gsub("\\", "/") - -- need this conversion to be applied to relative and absolute - -- file names as you may write "require 'Foo'" to - -- load "foo.lua" (on a case insensitive file system) and breakpoints - -- set on foo.lua will not work if not converted to the same case. - if iscasepreserving then file = string.lower(file) end - if file:find("%./") == 1 then file = file:sub(3) - else file = file:gsub('^'..q(basedir), '') end - -- some file systems allow newlines in file names; remove these. - file = file:gsub("\n", ' ') - else - -- this is either a file name coming from loadstring("chunk", "file"), - -- or the actual source code that needs to be serialized (as it may - -- include newlines); assume it's a file name if it's all on one line. - file = file:find("[\r\n]") and serpent.line(file) or file - end + -- grab the filename and fix it if needed + local file = lastfile + if (lastsource ~= caller.source) then + file, lastsource = caller.source, caller.source + -- technically, users can supply names that may not use '@', + -- for example when they call loadstring('...', 'filename.lua'). + -- Unfortunately, there is no reliable/quick way to figure out + -- what is the filename and what is the source code. + -- The following will work if the supplied filename uses Unix path. + if file:find("^@") then + file = file:gsub("^@", ""):gsub("\\", "/") + -- need this conversion to be applied to relative and absolute + -- file names as you may write "require 'Foo'" to + -- load "foo.lua" (on a case insensitive file system) and breakpoints + -- set on foo.lua will not work if not converted to the same case. + if iscasepreserving then file = string.lower(file) end + if file:find("%./") == 1 then file = file:sub(3) + else file = file:gsub('^'..q(basedir), '') end + -- some file systems allow newlines in file names; remove these. + file = file:gsub("\n", ' ') + else + -- this is either a file name coming from loadstring("chunk", "file"), + -- or the actual source code that needs to be serialized (as it may + -- include newlines); assume it's a file name if it's all on one line. + file = file:find("[\r\n]") and serpent.line(file) or file + end - -- set to true if we got here; this only needs to be done once per - -- session, so do it here to at least avoid setting it for every line. - seen_hook = true - lastfile = file - end + -- set to true if we got here; this only needs to be done once per + -- session, so do it here to at least avoid setting it for every line. + seen_hook = true + lastfile = file + end - local vars, status, res - if (watchescnt > 0) then - vars = capture_vars() - for index, value in pairs(watches) do - setfenv(value, vars) - local ok, fired = pcall(value) - if ok and fired then - status, res = coroutine.resume(coro_debugger, events.WATCH, vars, file, line, index) - break -- any one watch is enough; don't check multiple times - end - end - end + local vars, status, res + if (watchescnt > 0) then + vars = capture_vars() + for index, value in pairs(watches) do + setfenv(value, vars) + local ok, fired = pcall(value) + if ok and fired then + status, res = coroutine.resume(coro_debugger, events.WATCH, vars, file, line, index) + break -- any one watch is enough; don't check multiple times + end + end + end - -- need to get into the "regular" debug handler, but only if there was - -- no watch that was fired. If there was a watch, handle its result. - local getin = (status == nil) and - (step_into - or (step_over and stack_level <= step_level) - or has_breakpoint(file, line) - or is_pending(server)) + -- need to get into the "regular" debug handler, but only if there was + -- no watch that was fired. If there was a watch, handle its result. + local getin = (status == nil) and + (step_into + or (step_over and stack_level <= step_level) + or has_breakpoint(file, line) + or is_pending(server)) - if getin then - vars = vars or capture_vars() - step_into = false - step_over = false - status, res = coroutine.resume(coro_debugger, events.BREAK, vars, file, line) - end + if getin then + vars = vars or capture_vars() + step_into = false + step_over = false + status, res = coroutine.resume(coro_debugger, events.BREAK, vars, file, line) + end - -- handle 'stack' command that provides stack() information to the debugger - if status and res == 'stack' then - while status and res == 'stack' do - -- resume with the stack trace and variables - if vars then restore_vars(vars) end -- restore vars so they are reflected in stack values - -- this may fail if __tostring method fails at run-time - local ok, snapshot = pcall(stack, 4) - status, res = coroutine.resume(coro_debugger, ok and events.STACK or events.BREAK, snapshot, file, line) - end - end + -- handle 'stack' command that provides stack() information to the debugger + if status and res == 'stack' then + while status and res == 'stack' do + -- resume with the stack trace and variables + if vars then restore_vars(vars) end -- restore vars so they are reflected in stack values + -- this may fail if __tostring method fails at run-time + local ok, snapshot = pcall(stack, 4) + status, res = coroutine.resume(coro_debugger, ok and events.STACK or events.BREAK, snapshot, file, line) + end + end - -- need to recheck once more as resume after 'stack' command may - -- return something else (for example, 'exit'), which needs to be handled - if status and res and res ~= 'stack' then - if abort == nil and res == "exit" then os.exit(1); return end - abort = res - -- only abort if safe; if not, there is another (earlier) check inside - -- debug_hook, which will abort execution at the first safe opportunity - if is_safe(stack_level) then error(abort) end - elseif not status and res then - error(res, 2) -- report any other (internal) errors back to the application - end + -- need to recheck once more as resume after 'stack' command may + -- return something else (for example, 'exit'), which needs to be handled + if status and res and res ~= 'stack' then + if abort == nil and res == "exit" then os.exit(1); return end + abort = res + -- only abort if safe; if not, there is another (earlier) check inside + -- debug_hook, which will abort execution at the first safe opportunity + if is_safe(stack_level) then error(abort) end + elseif not status and res then + error(res, 2) -- report any other (internal) errors back to the application + end - if vars then restore_vars(vars) end - end + if vars then restore_vars(vars) end + end end local function stringify_results(status, ...) - if not status then return status, ... end -- on error report as it + if not status then return status, ... end -- on error report as it - local t = {...} - for i,v in pairs(t) do -- stringify each of the returned values - local ok, res = pcall(serpent.line, v, {nocode = true, comment = 1}) - t[i] = ok and res or ("%q"):format(res):gsub("\010","n"):gsub("\026","\\026") - end - -- stringify table with all returned values - -- this is done to allow each returned value to be used (serialized or not) - -- intependently and to preserve "original" comments - return pcall(serpent.dump, t, {sparse = false}) + local t = {...} + for i,v in pairs(t) do -- stringify each of the returned values + local ok, res = pcall(serpent.line, v, {nocode = true, comment = 1}) + t[i] = ok and res or ("%q"):format(res):gsub("\010","n"):gsub("\026","\\026") + end + -- stringify table with all returned values + -- this is done to allow each returned value to be used (serialized or not) + -- intependently and to preserve "original" comments + return pcall(serpent.dump, t, {sparse = false}) end local function debugger_loop(sev, svars, sfile, sline) - local command - local app, osname - local eval_env = svars or {} - local function emptyWatch () return false end - local loaded = {} - for k in pairs(package.loaded) do loaded[k] = true end + local command + local app, osname + local eval_env = svars or {} + local function emptyWatch () return false end + local loaded = {} + for k in pairs(package.loaded) do loaded[k] = true end - while true do - local line, err - local wx = rawget(genv, "wx") -- use rawread to make strict.lua happy - if (wx or mobdebug.yield) and server.settimeout then server:settimeout(mobdebug.yieldtimeout) end - while true do - line, err = server:receive() - if not line and err == "timeout" then - -- yield for wx GUI applications if possible to avoid "busyness" - app = app or (wx and wx.wxGetApp and wx.wxGetApp()) - if app then - local win = app:GetTopWindow() - local inloop = app:IsMainLoopRunning() - osname = osname or wx.wxPlatformInfo.Get():GetOperatingSystemFamilyName() - if win and not inloop then - -- process messages in a regular way - -- and exit as soon as the event loop is idle - if osname == 'Unix' then wx.wxTimer(app):Start(10, true) end - local exitLoop = function() - win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_IDLE) - win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_TIMER) - app:ExitMainLoop() - end - win:Connect(wx.wxEVT_IDLE, exitLoop) - win:Connect(wx.wxEVT_TIMER, exitLoop) - app:MainLoop() - end - elseif mobdebug.yield then mobdebug.yield() - end - elseif not line and err == "closed" then - error("Debugger connection unexpectedly closed", 0) - else - -- if there is something in the pending buffer, prepend it to the line - if buf then line = buf .. line; buf = nil end - break - end - end - if server.settimeout then server:settimeout() end -- back to blocking - command = string.sub(line, string.find(line, "^[A-Z]+")) - if command == "SETB" then - local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+(.-)%s+(%d+)%s*$") - if file and line then - set_breakpoint(file, tonumber(line)) - server:send("200 OK\n") - else - server:send("400 Bad Request\n") - end - elseif command == "DELB" then - local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+(.-)%s+(%d+)%s*$") - if file and line then - remove_breakpoint(file, tonumber(line)) - server:send("200 OK\n") - else - server:send("400 Bad Request\n") - end - elseif command == "EXEC" then - local _, _, chunk = string.find(line, "^[A-Z]+%s+(.+)$") - if chunk then - local func, res = loadstring(chunk) - local status - if func then - setfenv(func, eval_env) - status, res = stringify_results(pcall(func)) - end - if status then - server:send("200 OK " .. #res .. "\n") - server:send(res) - else - server:send("401 Error in Expression " .. #res .. "\n") - server:send(res) - end - else - server:send("400 Bad Request\n") - end - elseif command == "LOAD" then - local _, _, size, name = string.find(line, "^[A-Z]+%s+(%d+)%s+(%S.-)%s*$") - size = tonumber(size) + while true do + local line, err + local wx = rawget(genv, "wx") -- use rawread to make strict.lua happy + if (wx or mobdebug.yield) and server.settimeout then server:settimeout(mobdebug.yieldtimeout) end + while true do + line, err = server:receive() + if not line and err == "timeout" then + -- yield for wx GUI applications if possible to avoid "busyness" + app = app or (wx and wx.wxGetApp and wx.wxGetApp()) + if app then + local win = app:GetTopWindow() + local inloop = app:IsMainLoopRunning() + osname = osname or wx.wxPlatformInfo.Get():GetOperatingSystemFamilyName() + if win and not inloop then + -- process messages in a regular way + -- and exit as soon as the event loop is idle + if osname == 'Unix' then wx.wxTimer(app):Start(10, true) end + local exitLoop = function() + win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_IDLE) + win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_TIMER) + app:ExitMainLoop() + end + win:Connect(wx.wxEVT_IDLE, exitLoop) + win:Connect(wx.wxEVT_TIMER, exitLoop) + app:MainLoop() + end + elseif mobdebug.yield then mobdebug.yield() + end + elseif not line and err == "closed" then + error("Debugger connection unexpectedly closed", 0) + else + -- if there is something in the pending buffer, prepend it to the line + if buf then line = buf .. line; buf = nil end + break + end + end + if server.settimeout then server:settimeout() end -- back to blocking + command = string.sub(line, string.find(line, "^[A-Z]+")) + if command == "SETB" then + local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+(.-)%s+(%d+)%s*$") + if file and line then + set_breakpoint(file, tonumber(line)) + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "DELB" then + local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+(.-)%s+(%d+)%s*$") + if file and line then + remove_breakpoint(file, tonumber(line)) + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "EXEC" then + local _, _, chunk = string.find(line, "^[A-Z]+%s+(.+)$") + if chunk then + local func, res = loadstring(chunk) + local status + if func then + setfenv(func, eval_env) + status, res = stringify_results(pcall(func)) + end + if status then + server:send("200 OK " .. #res .. "\n") + server:send(res) + else + server:send("401 Error in Expression " .. #res .. "\n") + server:send(res) + end + else + server:send("400 Bad Request\n") + end + elseif command == "LOAD" then + local _, _, size, name = string.find(line, "^[A-Z]+%s+(%d+)%s+(%S.-)%s*$") + size = tonumber(size) - if abort == nil then -- no LOAD/RELOAD allowed inside start() - if size > 0 then server:receive(size) end - if sfile and sline then - server:send("201 Started " .. sfile .. " " .. sline .. "\n") - else - server:send("200 OK 0\n") - end - else - -- reset environment to allow required modules to load again - -- remove those packages that weren't loaded when debugger started - for k in pairs(package.loaded) do - if not loaded[k] then package.loaded[k] = nil end - end + if abort == nil then -- no LOAD/RELOAD allowed inside start() + if size > 0 then server:receive(size) end + if sfile and sline then + server:send("201 Started " .. sfile .. " " .. sline .. "\n") + else + server:send("200 OK 0\n") + end + else + -- reset environment to allow required modules to load again + -- remove those packages that weren't loaded when debugger started + for k in pairs(package.loaded) do + if not loaded[k] then package.loaded[k] = nil end + end - if size == 0 and name == '-' then -- RELOAD the current script being debugged - server:send("200 OK 0\n") - coroutine.yield("load") - else - -- receiving 0 bytes blocks (at least in luasocket 2.0.2), so skip reading - local chunk = size == 0 and "" or server:receive(size) - if chunk then -- LOAD a new script for debugging - local func, res = loadstring(chunk, "@"..name) - if func then - server:send("200 OK 0\n") - debugee = func - coroutine.yield("load") - else - server:send("401 Error in Expression " .. #res .. "\n") - server:send(res) - end - else - server:send("400 Bad Request\n") - end - end - end - elseif command == "SETW" then - local _, _, exp = string.find(line, "^[A-Z]+%s+(.+)%s*$") - if exp then - local func, res = loadstring("return(" .. exp .. ")") - if func then - watchescnt = watchescnt + 1 - local newidx = #watches + 1 - watches[newidx] = func - server:send("200 OK " .. newidx .. "\n") - else - server:send("401 Error in Expression " .. #res .. "\n") - server:send(res) - end - else - server:send("400 Bad Request\n") - end - elseif command == "DELW" then - local _, _, index = string.find(line, "^[A-Z]+%s+(%d+)%s*$") - index = tonumber(index) - if index > 0 and index <= #watches then - watchescnt = watchescnt - (watches[index] ~= emptyWatch and 1 or 0) - watches[index] = emptyWatch - server:send("200 OK\n") - else - server:send("400 Bad Request\n") - end - elseif command == "RUN" then - server:send("200 OK\n") + if size == 0 and name == '-' then -- RELOAD the current script being debugged + server:send("200 OK 0\n") + coroutine.yield("load") + else + -- receiving 0 bytes blocks (at least in luasocket 2.0.2), so skip reading + local chunk = size == 0 and "" or server:receive(size) + if chunk then -- LOAD a new script for debugging + local func, res = loadstring(chunk, "@"..name) + if func then + server:send("200 OK 0\n") + debugee = func + coroutine.yield("load") + else + server:send("401 Error in Expression " .. #res .. "\n") + server:send(res) + end + else + server:send("400 Bad Request\n") + end + end + end + elseif command == "SETW" then + local _, _, exp = string.find(line, "^[A-Z]+%s+(.+)%s*$") + if exp then + local func, res = loadstring("return(" .. exp .. ")") + if func then + watchescnt = watchescnt + 1 + local newidx = #watches + 1 + watches[newidx] = func + server:send("200 OK " .. newidx .. "\n") + else + server:send("401 Error in Expression " .. #res .. "\n") + server:send(res) + end + else + server:send("400 Bad Request\n") + end + elseif command == "DELW" then + local _, _, index = string.find(line, "^[A-Z]+%s+(%d+)%s*$") + index = tonumber(index) + if index > 0 and index <= #watches then + watchescnt = watchescnt - (watches[index] ~= emptyWatch and 1 or 0) + watches[index] = emptyWatch + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "RUN" then + server:send("200 OK\n") - local ev, vars, file, line, idx_watch = coroutine.yield() - eval_env = vars - if ev == events.BREAK then - server:send("202 Paused " .. file .. " " .. line .. "\n") - elseif ev == events.WATCH then - server:send("203 Paused " .. file .. " " .. line .. " " .. idx_watch .. "\n") - elseif ev == events.RESTART then - -- nothing to do - else - server:send("401 Error in Execution " .. #file .. "\n") - server:send(file) - end - elseif command == "STEP" then - server:send("200 OK\n") - step_into = true + local ev, vars, file, line, idx_watch = coroutine.yield() + eval_env = vars + if ev == events.BREAK then + server:send("202 Paused " .. file .. " " .. line .. "\n") + elseif ev == events.WATCH then + server:send("203 Paused " .. file .. " " .. line .. " " .. idx_watch .. "\n") + elseif ev == events.RESTART then + -- nothing to do + else + server:send("401 Error in Execution " .. #file .. "\n") + server:send(file) + end + elseif command == "STEP" then + server:send("200 OK\n") + step_into = true - local ev, vars, file, line, idx_watch = coroutine.yield() - eval_env = vars - if ev == events.BREAK then - server:send("202 Paused " .. file .. " " .. line .. "\n") - elseif ev == events.WATCH then - server:send("203 Paused " .. file .. " " .. line .. " " .. idx_watch .. "\n") - elseif ev == events.RESTART then - -- nothing to do - else - server:send("401 Error in Execution " .. #file .. "\n") - server:send(file) - end - elseif command == "OVER" or command == "OUT" then - server:send("200 OK\n") - step_over = true - - -- OVER and OUT are very similar except for - -- the stack level value at which to stop - if command == "OUT" then step_level = stack_level - 1 - else step_level = stack_level end + local ev, vars, file, line, idx_watch = coroutine.yield() + eval_env = vars + if ev == events.BREAK then + server:send("202 Paused " .. file .. " " .. line .. "\n") + elseif ev == events.WATCH then + server:send("203 Paused " .. file .. " " .. line .. " " .. idx_watch .. "\n") + elseif ev == events.RESTART then + -- nothing to do + else + server:send("401 Error in Execution " .. #file .. "\n") + server:send(file) + end + elseif command == "OVER" or command == "OUT" then + server:send("200 OK\n") + step_over = true + + -- OVER and OUT are very similar except for + -- the stack level value at which to stop + if command == "OUT" then step_level = stack_level - 1 + else step_level = stack_level end - local ev, vars, file, line, idx_watch = coroutine.yield() - eval_env = vars - if ev == events.BREAK then - server:send("202 Paused " .. file .. " " .. line .. "\n") - elseif ev == events.WATCH then - server:send("203 Paused " .. file .. " " .. line .. " " .. idx_watch .. "\n") - elseif ev == events.RESTART then - -- nothing to do - else - server:send("401 Error in Execution " .. #file .. "\n") - server:send(file) - end - elseif command == "BASEDIR" then - local _, _, dir = string.find(line, "^[A-Z]+%s+(.+)%s*$") - if dir then - basedir = iscasepreserving and string.lower(dir) or dir - -- reset cached source as it may change with basedir - lastsource = nil - server:send("200 OK\n") - else - server:send("400 Bad Request\n") - end - elseif command == "SUSPEND" then - -- do nothing; it already fulfilled its role - elseif command == "STACK" then - -- first check if we can execute the stack command - -- as it requires yielding back to debug_hook it cannot be executed - -- if we have not seen the hook yet as happens after start(). - -- in this case we simply return an empty result - local vars, ev = {} - if seen_hook then - ev, vars = coroutine.yield("stack") - end - if ev and ev ~= events.STACK then - server:send("401 Error in Execution " .. #vars .. "\n") - server:send(vars) - else - local ok, res = pcall(serpent.dump, vars, {nocode = true, sparse = false}) - if ok then - server:send("200 OK " .. res .. "\n") - else - server:send("401 Error in Execution " .. #res .. "\n") - server:send(res) - end - end - elseif command == "OUTPUT" then - local _, _, stream, mode = string.find(line, "^[A-Z]+%s+(%w+)%s+([dcr])%s*$") - if stream and mode and stream == "stdout" then - -- assign "print" in the global environment - genv.print = mode == 'd' and iobase.print or coroutine.wrap(function(...) - -- wrapping into coroutine.wrap protects this function from - -- being stepped through in the debugger - local tbl = {...} - while true do - if mode == 'c' then iobase.print(unpack(tbl)) end - for n = 1, #tbl do - tbl[n] = select(2, pcall(serpent.line, tbl[n], {nocode = true, comment = false})) end - local file = table.concat(tbl, "\t").."\n" - server:send("204 Output " .. stream .. " " .. #file .. "\n" .. file) - tbl = {coroutine.yield()} - end - end) - server:send("200 OK\n") - else - server:send("400 Bad Request\n") - end - elseif command == "EXIT" then - server:send("200 OK\n") - coroutine.yield("exit") - else - server:send("400 Bad Request\n") - end - end + local ev, vars, file, line, idx_watch = coroutine.yield() + eval_env = vars + if ev == events.BREAK then + server:send("202 Paused " .. file .. " " .. line .. "\n") + elseif ev == events.WATCH then + server:send("203 Paused " .. file .. " " .. line .. " " .. idx_watch .. "\n") + elseif ev == events.RESTART then + -- nothing to do + else + server:send("401 Error in Execution " .. #file .. "\n") + server:send(file) + end + elseif command == "BASEDIR" then + local _, _, dir = string.find(line, "^[A-Z]+%s+(.+)%s*$") + if dir then + basedir = iscasepreserving and string.lower(dir) or dir + -- reset cached source as it may change with basedir + lastsource = nil + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "SUSPEND" then + -- do nothing; it already fulfilled its role + elseif command == "STACK" then + -- first check if we can execute the stack command + -- as it requires yielding back to debug_hook it cannot be executed + -- if we have not seen the hook yet as happens after start(). + -- in this case we simply return an empty result + local vars, ev = {} + if seen_hook then + ev, vars = coroutine.yield("stack") + end + if ev and ev ~= events.STACK then + server:send("401 Error in Execution " .. #vars .. "\n") + server:send(vars) + else + local ok, res = pcall(serpent.dump, vars, {nocode = true, sparse = false}) + if ok then + server:send("200 OK " .. res .. "\n") + else + server:send("401 Error in Execution " .. #res .. "\n") + server:send(res) + end + end + elseif command == "OUTPUT" then + local _, _, stream, mode = string.find(line, "^[A-Z]+%s+(%w+)%s+([dcr])%s*$") + if stream and mode and stream == "stdout" then + -- assign "print" in the global environment + genv.print = mode == 'd' and iobase.print or coroutine.wrap(function(...) + -- wrapping into coroutine.wrap protects this function from + -- being stepped through in the debugger + local tbl = {...} + while true do + if mode == 'c' then iobase.print(unpack(tbl)) end + for n = 1, #tbl do + tbl[n] = select(2, pcall(serpent.line, tbl[n], {nocode = true, comment = false})) end + local file = table.concat(tbl, "\t").."\n" + server:send("204 Output " .. stream .. " " .. #file .. "\n" .. file) + tbl = {coroutine.yield()} + end + end) + server:send("200 OK\n") + else + server:send("400 Bad Request\n") + end + elseif command == "EXIT" then + server:send("200 OK\n") + coroutine.yield("exit") + else + server:send("400 Bad Request\n") + end + end end local function connect(controller_host, controller_port) - return (socket.connect4 or socket.connect)(controller_host, controller_port) + return (socket.connect4 or socket.connect)(controller_host, controller_port) end local function isrunning() - return coro_debugger and coroutine.status(coro_debugger) == 'suspended' + return coro_debugger and coroutine.status(coro_debugger) == 'suspended' end local lasthost, lastport -- Starts a debug session by connecting to a controller local function start(controller_host, controller_port) - -- only one debugging session can be run (as there is only one debug hook) - if isrunning() then return end + -- only one debugging session can be run (as there is only one debug hook) + if isrunning() then return end - lasthost = controller_host or lasthost - lastport = controller_port or lastport + lasthost = controller_host or lasthost + lastport = controller_port or lastport - controller_host = lasthost or "localhost" - controller_port = lastport or mobdebug.port + controller_host = lasthost or "localhost" + controller_port = lastport or mobdebug.port - local err - server, err = (socket.connect4 or socket.connect)(controller_host, controller_port) - if server then - -- correct stack depth which already has some calls on it - -- so it doesn't go into negative when those calls return - -- as this breaks subsequence checks in stack_depth(). - -- start from 16th frame, which is sufficiently large for this check. - stack_level = stack_depth(16) + local err + server, err = (socket.connect4 or socket.connect)(controller_host, controller_port) + if server then + -- correct stack depth which already has some calls on it + -- so it doesn't go into negative when those calls return + -- as this breaks subsequence checks in stack_depth(). + -- start from 16th frame, which is sufficiently large for this check. + stack_level = stack_depth(16) - -- provide our own traceback function to report the error remotely - do - local dtraceback = debug.traceback - debug.traceback = function (...) - if select('#', ...) >= 1 then - local err, lvl = ... - if err and type(err) ~= 'thread' then - local trace = dtraceback(err, (lvl or 2)+1) - if genv.print == iobase.print then -- no remote redirect - return trace - else - genv.print(trace) -- report the error remotely - return -- don't report locally to avoid double reporting - end - end - end - -- direct call to debug.traceback: return the original. - -- debug.traceback(nil, level) doesn't work in Lua 5.1 - -- (http://lua-users.org/lists/lua-l/2011-06/msg00574.html), so - -- simply remove first frame from the stack trace - return (dtraceback(...):gsub("(stack traceback:\n)[^\n]*\n", "%1")) - end - end - coro_debugger = coroutine.create(debugger_loop) - debug.sethook(debug_hook, "lcr") - seen_hook = nil -- reset in case the last start() call was refused - step_into = true -- start with step command - return true - else - print(("Could not connect to %s:%s: %s") - :format(controller_host, controller_port, err or "unknown error")) - end + -- provide our own traceback function to report the error remotely + do + local dtraceback = debug.traceback + debug.traceback = function (...) + if select('#', ...) >= 1 then + local err, lvl = ... + if err and type(err) ~= 'thread' then + local trace = dtraceback(err, (lvl or 2)+1) + if genv.print == iobase.print then -- no remote redirect + return trace + else + genv.print(trace) -- report the error remotely + return -- don't report locally to avoid double reporting + end + end + end + -- direct call to debug.traceback: return the original. + -- debug.traceback(nil, level) doesn't work in Lua 5.1 + -- (http://lua-users.org/lists/lua-l/2011-06/msg00574.html), so + -- simply remove first frame from the stack trace + return (dtraceback(...):gsub("(stack traceback:\n)[^\n]*\n", "%1")) + end + end + coro_debugger = coroutine.create(debugger_loop) + debug.sethook(debug_hook, "lcr") + seen_hook = nil -- reset in case the last start() call was refused + step_into = true -- start with step command + return true + else + print(("Could not connect to %s:%s: %s") + :format(controller_host, controller_port, err or "unknown error")) + end end local function controller(controller_host, controller_port, scratchpad) - -- only one debugging session can be run (as there is only one debug hook) - if isrunning() then return end + -- only one debugging session can be run (as there is only one debug hook) + if isrunning() then return end - lasthost = controller_host or lasthost - lastport = controller_port or lastport + lasthost = controller_host or lasthost + lastport = controller_port or lastport - controller_host = lasthost or "localhost" - controller_port = lastport or mobdebug.port + controller_host = lasthost or "localhost" + controller_port = lastport or mobdebug.port - local exitonerror = not scratchpad - local err - server, err = (socket.connect4 or socket.connect)(controller_host, controller_port) - if server then - local function report(trace, err) - local msg = err .. "\n" .. trace - server:send("401 Error in Execution " .. #msg .. "\n") - server:send(msg) - return err - end + local exitonerror = not scratchpad + local err + server, err = (socket.connect4 or socket.connect)(controller_host, controller_port) + if server then + local function report(trace, err) + local msg = err .. "\n" .. trace + server:send("401 Error in Execution " .. #msg .. "\n") + server:send(msg) + return err + end - seen_hook = true -- allow to accept all commands - coro_debugger = coroutine.create(debugger_loop) + seen_hook = true -- allow to accept all commands + coro_debugger = coroutine.create(debugger_loop) - while true do - step_into = true -- start with step command - abort = false -- reset abort flag from the previous loop - if scratchpad then checkcount = mobdebug.checkcount end -- force suspend right away + while true do + step_into = true -- start with step command + abort = false -- reset abort flag from the previous loop + if scratchpad then checkcount = mobdebug.checkcount end -- force suspend right away - coro_debugee = coroutine.create(debugee) - debug.sethook(coro_debugee, debug_hook, "lcr") - local status, err = coroutine.resume(coro_debugee) + coro_debugee = coroutine.create(debugee) + debug.sethook(coro_debugee, debug_hook, "lcr") + local status, err = coroutine.resume(coro_debugee) - -- was there an error or is the script done? - -- 'abort' state is allowed here; ignore it - if abort then - if tostring(abort) == 'exit' then break end - else - if status then -- normal execution is done - break - elseif err and not tostring(err):find(deferror) then - -- report the error back - -- err is not necessarily a string, so convert to string to report - report(debug.traceback(coro_debugee), tostring(err)) - if exitonerror then break end - -- resume once more to clear the response the debugger wants to send - -- need to use capture_vars(2) as three would be the level of - -- the caller for controller(), but because of the tail call, - -- the caller may not exist; - -- This is not entirely safe as the user may see the local - -- variable from console, but they will be reset anyway. - -- This functionality is used when scratchpad is paused to - -- gain access to remote console to modify global variables. - local status, err = coroutine.resume(coro_debugger, events.RESTART, capture_vars(2)) - if not status or status and err == "exit" then break end - end - end - end - else - print(("Could not connect to %s:%s: %s") - :format(controller_host, controller_port, err or "unknown error")) - return false - end - return true + -- was there an error or is the script done? + -- 'abort' state is allowed here; ignore it + if abort then + if tostring(abort) == 'exit' then break end + else + if status then -- normal execution is done + break + elseif err and not tostring(err):find(deferror) then + -- report the error back + -- err is not necessarily a string, so convert to string to report + report(debug.traceback(coro_debugee), tostring(err)) + if exitonerror then break end + -- resume once more to clear the response the debugger wants to send + -- need to use capture_vars(2) as three would be the level of + -- the caller for controller(), but because of the tail call, + -- the caller may not exist; + -- This is not entirely safe as the user may see the local + -- variable from console, but they will be reset anyway. + -- This functionality is used when scratchpad is paused to + -- gain access to remote console to modify global variables. + local status, err = coroutine.resume(coro_debugger, events.RESTART, capture_vars(2)) + if not status or status and err == "exit" then break end + end + end + end + else + print(("Could not connect to %s:%s: %s") + :format(controller_host, controller_port, err or "unknown error")) + return false + end + return true end local function scratchpad(controller_host, controller_port) - return controller(controller_host, controller_port, true) + return controller(controller_host, controller_port, true) end local function loop(controller_host, controller_port) - return controller(controller_host, controller_port, false) + return controller(controller_host, controller_port, false) end local function on() - if not (isrunning() and server) then return end + if not (isrunning() and server) then return end - -- main is set to true under Lua5.2 for the "main" chunk. - -- Lua5.1 returns co as `nil` in that case. - local co, main = coroutine.running() - if main then co = nil end - if co then - coroutines[co] = true - debug.sethook(co, debug_hook, "lcr") - else - if jit then coroutines.main = true end - debug.sethook(debug_hook, "lcr") - end + -- main is set to true under Lua5.2 for the "main" chunk. + -- Lua5.1 returns co as `nil` in that case. + local co, main = coroutine.running() + if main then co = nil end + if co then + coroutines[co] = true + debug.sethook(co, debug_hook, "lcr") + else + if jit then coroutines.main = true end + debug.sethook(debug_hook, "lcr") + end end local function off() - if not (isrunning() and server) then return end + if not (isrunning() and server) then return end - -- main is set to true under Lua5.2 for the "main" chunk. - -- Lua5.1 returns co as `nil` in that case. - local co, main = coroutine.running() - if main then co = nil end + -- main is set to true under Lua5.2 for the "main" chunk. + -- Lua5.1 returns co as `nil` in that case. + local co, main = coroutine.running() + if main then co = nil end - -- don't remove coroutine hook under LuaJIT as there is only one (global) hook - if co then - coroutines[co] = false - if not jit then debug.sethook(co) end - else - if jit then coroutines.main = false end - if not jit then debug.sethook() end - end + -- don't remove coroutine hook under LuaJIT as there is only one (global) hook + if co then + coroutines[co] = false + if not jit then debug.sethook(co) end + else + if jit then coroutines.main = false end + if not jit then debug.sethook() end + end - -- check if there is any thread that is still being debugged under LuaJIT; - -- if not, turn the debugging off - if jit then - local remove = true - for co, debugged in pairs(coroutines) do - if debugged then remove = false; break end - end - if remove then debug.sethook() end - end + -- check if there is any thread that is still being debugged under LuaJIT; + -- if not, turn the debugging off + if jit then + local remove = true + for co, debugged in pairs(coroutines) do + if debugged then remove = false; break end + end + if remove then debug.sethook() end + end end -- Handles server debugging commands local function handle(params, client, options) - local _, _, command = string.find(params, "^([a-z]+)") - local file, line, watch_idx - if command == "run" or command == "step" or command == "out" - or command == "over" or command == "exit" then - client:send(string.upper(command) .. "\n") - client:receive() -- this should consume the first '200 OK' response - while true do - local done = true - local breakpoint = client:receive() - if not breakpoint then - print("Program finished") - os.exit() - return -- use return here for those cases where os.exit() is not wanted - end - local _, _, status = string.find(breakpoint, "^(%d+)") - if status == "200" then - -- don't need to do anything - elseif status == "202" then - _, _, file, line = string.find(breakpoint, "^202 Paused%s+(.-)%s+(%d+)%s*$") - if file and line then - print("Paused at file " .. file .. " line " .. line) - end - elseif status == "203" then - _, _, file, line, watch_idx = string.find(breakpoint, "^203 Paused%s+(.-)%s+(%d+)%s+(%d+)%s*$") - if file and line and watch_idx then - print("Paused at file " .. file .. " line " .. line .. " (watch expression " .. watch_idx .. ": [" .. watches[watch_idx] .. "])") - end - elseif status == "204" then - local _, _, stream, size = string.find(breakpoint, "^204 Output (%w+) (%d+)$") - if stream and size then - local msg = client:receive(tonumber(size)) - print(msg) - if outputs[stream] then outputs[stream](msg) end - -- this was just the output, so go back reading the response - done = false - end - elseif status == "401" then - local _, _, size = string.find(breakpoint, "^401 Error in Execution (%d+)$") - if size then - local msg = client:receive(tonumber(size)) - print("Error in remote application: " .. msg) - os.exit(1) - return nil, nil, msg -- use return here for those cases where os.exit() is not wanted - end - else - print("Unknown error") - os.exit(1) - -- use return here for those cases where os.exit() is not wanted - return nil, nil, "Debugger error: unexpected response '" .. breakpoint .. "'" - end - if done then break end - end - elseif command == "setb" then - _, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$") - if file and line then - -- if this is a file name, and not a file source - if not file:find('^".*"$') then - file = string.gsub(file, "\\", "/") -- convert slash - file = removebasedir(file, basedir) - end - client:send("SETB " .. file .. " " .. line .. "\n") - if client:receive() == "200 OK" then - set_breakpoint(file, line) - else - print("Error: breakpoint not inserted") - end - else - print("Invalid command") - end - elseif command == "setw" then - local _, _, exp = string.find(params, "^[a-z]+%s+(.+)$") - if exp then - client:send("SETW " .. exp .. "\n") - local answer = client:receive() - local _, _, watch_idx = string.find(answer, "^200 OK (%d+)%s*$") - if watch_idx then - watches[watch_idx] = exp - print("Inserted watch exp no. " .. watch_idx) - else - local _, _, size = string.find(answer, "^401 Error in Expression (%d+)$") - if size then - local err = client:receive(tonumber(size)):gsub(".-:%d+:%s*","") - print("Error: watch expression not set: " .. err) - else - print("Error: watch expression not set") - end - end - else - print("Invalid command") - end - elseif command == "delb" then - _, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$") - if file and line then - -- if this is a file name, and not a file source - if not file:find('^".*"$') then - file = string.gsub(file, "\\", "/") -- convert slash - file = removebasedir(file, basedir) - end - client:send("DELB " .. file .. " " .. line .. "\n") - if client:receive() == "200 OK" then - remove_breakpoint(file, line) - else - print("Error: breakpoint not removed") - end - else - print("Invalid command") - end - elseif command == "delallb" then - for line, breaks in pairs(breakpoints) do - for file, _ in pairs(breaks) do - client:send("DELB " .. file .. " " .. line .. "\n") - if client:receive() == "200 OK" then - remove_breakpoint(file, line) - else - print("Error: breakpoint at file " .. file .. " line " .. line .. " not removed") - end - end - end - elseif command == "delw" then - local _, _, index = string.find(params, "^[a-z]+%s+(%d+)%s*$") - if index then - client:send("DELW " .. index .. "\n") - if client:receive() == "200 OK" then - watches[index] = nil - else - print("Error: watch expression not removed") - end - else - print("Invalid command") - end - elseif command == "delallw" then - for index, exp in pairs(watches) do - client:send("DELW " .. index .. "\n") - if client:receive() == "200 OK" then - watches[index] = nil - else - print("Error: watch expression at index " .. index .. " [" .. exp .. "] not removed") - end - end - elseif command == "eval" or command == "exec" - or command == "load" or command == "loadstring" - or command == "reload" then - local _, _, exp = string.find(params, "^[a-z]+%s+(.+)$") - if exp or (command == "reload") then - if command == "eval" or command == "exec" then - exp = (exp:gsub("%-%-%[(=*)%[.-%]%1%]", "") -- remove comments - :gsub("%-%-.-\n", " ") -- remove line comments - :gsub("\n", " ")) -- convert new lines - if command == "eval" then exp = "return " .. exp end - client:send("EXEC " .. exp .. "\n") - elseif command == "reload" then - client:send("LOAD 0 -\n") - elseif command == "loadstring" then - local _, _, _, file, lines = string.find(exp, "^([\"'])(.-)%1%s+(.+)") - if not file then - _, _, file, lines = string.find(exp, "^(%S+)%s+(.+)") - end - client:send("LOAD " .. #lines .. " " .. file .. "\n") - client:send(lines) - else - local file = io.open(exp, "r") - if not file and pcall(require, "winapi") then - -- if file is not open and winapi is there, try with a short path; - -- this may be needed for unicode paths on windows - winapi.set_encoding(winapi.CP_UTF8) - file = io.open(winapi.short_path(exp), "r") - end - if not file then error("Cannot open file " .. exp) end - -- read the file and remove the shebang line as it causes a compilation error - local lines = file:read("*all"):gsub("^#!.-\n", "\n") - file:close() + local _, _, command = string.find(params, "^([a-z]+)") + local file, line, watch_idx + if command == "run" or command == "step" or command == "out" + or command == "over" or command == "exit" then + client:send(string.upper(command) .. "\n") + client:receive() -- this should consume the first '200 OK' response + while true do + local done = true + local breakpoint = client:receive() + if not breakpoint then + print("Program finished") + os.exit() + return -- use return here for those cases where os.exit() is not wanted + end + local _, _, status = string.find(breakpoint, "^(%d+)") + if status == "200" then + -- don't need to do anything + elseif status == "202" then + _, _, file, line = string.find(breakpoint, "^202 Paused%s+(.-)%s+(%d+)%s*$") + if file and line then + print("Paused at file " .. file .. " line " .. line) + end + elseif status == "203" then + _, _, file, line, watch_idx = string.find(breakpoint, "^203 Paused%s+(.-)%s+(%d+)%s+(%d+)%s*$") + if file and line and watch_idx then + print("Paused at file " .. file .. " line " .. line .. " (watch expression " .. watch_idx .. ": [" .. watches[watch_idx] .. "])") + end + elseif status == "204" then + local _, _, stream, size = string.find(breakpoint, "^204 Output (%w+) (%d+)$") + if stream and size then + local msg = client:receive(tonumber(size)) + print(msg) + if outputs[stream] then outputs[stream](msg) end + -- this was just the output, so go back reading the response + done = false + end + elseif status == "401" then + local _, _, size = string.find(breakpoint, "^401 Error in Execution (%d+)$") + if size then + local msg = client:receive(tonumber(size)) + print("Error in remote application: " .. msg) + os.exit(1) + return nil, nil, msg -- use return here for those cases where os.exit() is not wanted + end + else + print("Unknown error") + os.exit(1) + -- use return here for those cases where os.exit() is not wanted + return nil, nil, "Debugger error: unexpected response '" .. breakpoint .. "'" + end + if done then break end + end + elseif command == "setb" then + _, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$") + if file and line then + -- if this is a file name, and not a file source + if not file:find('^".*"$') then + file = string.gsub(file, "\\", "/") -- convert slash + file = removebasedir(file, basedir) + end + client:send("SETB " .. file .. " " .. line .. "\n") + if client:receive() == "200 OK" then + set_breakpoint(file, line) + else + print("Error: breakpoint not inserted") + end + else + print("Invalid command") + end + elseif command == "setw" then + local _, _, exp = string.find(params, "^[a-z]+%s+(.+)$") + if exp then + client:send("SETW " .. exp .. "\n") + local answer = client:receive() + local _, _, watch_idx = string.find(answer, "^200 OK (%d+)%s*$") + if watch_idx then + watches[watch_idx] = exp + print("Inserted watch exp no. " .. watch_idx) + else + local _, _, size = string.find(answer, "^401 Error in Expression (%d+)$") + if size then + local err = client:receive(tonumber(size)):gsub(".-:%d+:%s*","") + print("Error: watch expression not set: " .. err) + else + print("Error: watch expression not set") + end + end + else + print("Invalid command") + end + elseif command == "delb" then + _, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$") + if file and line then + -- if this is a file name, and not a file source + if not file:find('^".*"$') then + file = string.gsub(file, "\\", "/") -- convert slash + file = removebasedir(file, basedir) + end + client:send("DELB " .. file .. " " .. line .. "\n") + if client:receive() == "200 OK" then + remove_breakpoint(file, line) + else + print("Error: breakpoint not removed") + end + else + print("Invalid command") + end + elseif command == "delallb" then + for line, breaks in pairs(breakpoints) do + for file, _ in pairs(breaks) do + client:send("DELB " .. file .. " " .. line .. "\n") + if client:receive() == "200 OK" then + remove_breakpoint(file, line) + else + print("Error: breakpoint at file " .. file .. " line " .. line .. " not removed") + end + end + end + elseif command == "delw" then + local _, _, index = string.find(params, "^[a-z]+%s+(%d+)%s*$") + if index then + client:send("DELW " .. index .. "\n") + if client:receive() == "200 OK" then + watches[index] = nil + else + print("Error: watch expression not removed") + end + else + print("Invalid command") + end + elseif command == "delallw" then + for index, exp in pairs(watches) do + client:send("DELW " .. index .. "\n") + if client:receive() == "200 OK" then + watches[index] = nil + else + print("Error: watch expression at index " .. index .. " [" .. exp .. "] not removed") + end + end + elseif command == "eval" or command == "exec" + or command == "load" or command == "loadstring" + or command == "reload" then + local _, _, exp = string.find(params, "^[a-z]+%s+(.+)$") + if exp or (command == "reload") then + if command == "eval" or command == "exec" then + exp = (exp:gsub("%-%-%[(=*)%[.-%]%1%]", "") -- remove comments + :gsub("%-%-.-\n", " ") -- remove line comments + :gsub("\n", " ")) -- convert new lines + if command == "eval" then exp = "return " .. exp end + client:send("EXEC " .. exp .. "\n") + elseif command == "reload" then + client:send("LOAD 0 -\n") + elseif command == "loadstring" then + local _, _, _, file, lines = string.find(exp, "^([\"'])(.-)%1%s+(.+)") + if not file then + _, _, file, lines = string.find(exp, "^(%S+)%s+(.+)") + end + client:send("LOAD " .. #lines .. " " .. file .. "\n") + client:send(lines) + else + local file = io.open(exp, "r") + if not file and pcall(require, "winapi") then + -- if file is not open and winapi is there, try with a short path; + -- this may be needed for unicode paths on windows + winapi.set_encoding(winapi.CP_UTF8) + file = io.open(winapi.short_path(exp), "r") + end + if not file then error("Cannot open file " .. exp) end + -- read the file and remove the shebang line as it causes a compilation error + local lines = file:read("*all"):gsub("^#!.-\n", "\n") + file:close() - local file = string.gsub(exp, "\\", "/") -- convert slash - file = removebasedir(file, basedir) - client:send("LOAD " .. #lines .. " " .. file .. "\n") - if #lines > 0 then client:send(lines) end - end - while true do - local params, err = client:receive() - if not params then - return nil, nil, "Debugger connection " .. (err or "error") - end - local done = true - local _, _, status, len = string.find(params, "^(%d+).-%s+(%d+)%s*$") - if status == "200" then - len = tonumber(len) - if len > 0 then - local status, res - local str = client:receive(len) - -- handle serialized table with results - local func, err = loadstring(str) - if func then - status, res = pcall(func) - if not status then err = res - elseif type(res) ~= "table" then - err = "received "..type(res).." instead of expected 'table'" - end - end - if err then - print("Error in processing results: " .. err) - return nil, nil, "Error in processing results: " .. err - end - print(unpack(res)) - return res[1], res - end - elseif status == "201" then - _, _, file, line = string.find(params, "^201 Started%s+(.-)%s+(%d+)%s*$") - elseif status == "202" or params == "200 OK" then - -- do nothing; this only happens when RE/LOAD command gets the response - -- that was for the original command that was aborted - elseif status == "204" then - local _, _, stream, size = string.find(params, "^204 Output (%w+) (%d+)$") - if stream and size then - local msg = client:receive(tonumber(size)) - print(msg) - if outputs[stream] then outputs[stream](msg) end - -- this was just the output, so go back reading the response - done = false - end - elseif status == "401" then - len = tonumber(len) - local res = client:receive(len) - print("Error in expression: " .. res) - return nil, nil, res - else - print("Unknown error") - return nil, nil, "Debugger error: unexpected response after EXEC/LOAD '" .. params .. "'" - end - if done then break end - end - else - print("Invalid command") - end - elseif command == "listb" then - for l, v in pairs(breakpoints) do - for f in pairs(v) do - print(f .. ": " .. l) - end - end - elseif command == "listw" then - for i, v in pairs(watches) do - print("Watch exp. " .. i .. ": " .. v) - end - elseif command == "suspend" then - client:send("SUSPEND\n") - elseif command == "stack" then - client:send("STACK\n") - local resp = client:receive() - local _, _, status, res = string.find(resp, "^(%d+)%s+%w+%s+(.+)%s*$") - if status == "200" then - local func, err = loadstring(res) - if func == nil then - print("Error in stack information: " .. err) - return nil, nil, err - end - local ok, stack = pcall(func) - if not ok then - print("Error in stack information: " .. stack) - return nil, nil, stack - end - for _,frame in ipairs(stack) do - print(serpent.line(frame[1], {comment = false})) - end - return stack - elseif status == "401" then - local _, _, len = string.find(resp, "%s+(%d+)%s*$") - len = tonumber(len) - local res = len > 0 and client:receive(len) or "Invalid stack information." - print("Error in expression: " .. res) - return nil, nil, res - else - print("Unknown error") - return nil, nil, "Debugger error: unexpected response after STACK" - end - elseif command == "output" then - local _, _, stream, mode = string.find(params, "^[a-z]+%s+(%w+)%s+([dcr])%s*$") - if stream and mode then - client:send("OUTPUT "..stream.." "..mode.."\n") - local resp = client:receive() - local _, _, status = string.find(resp, "^(%d+)%s+%w+%s*$") - if status == "200" then - print("Stream "..stream.." redirected") - outputs[stream] = type(options) == 'table' and options.handler or nil - else - print("Unknown error") - return nil, nil, "Debugger error: can't redirect "..stream - end - else - print("Invalid command") - end - elseif command == "basedir" then - local _, _, dir = string.find(params, "^[a-z]+%s+(.+)$") - if dir then - dir = string.gsub(dir, "\\", "/") -- convert slash - if not string.find(dir, "/$") then dir = dir .. "/" end + local file = string.gsub(exp, "\\", "/") -- convert slash + file = removebasedir(file, basedir) + client:send("LOAD " .. #lines .. " " .. file .. "\n") + if #lines > 0 then client:send(lines) end + end + while true do + local params, err = client:receive() + if not params then + return nil, nil, "Debugger connection " .. (err or "error") + end + local done = true + local _, _, status, len = string.find(params, "^(%d+).-%s+(%d+)%s*$") + if status == "200" then + len = tonumber(len) + if len > 0 then + local status, res + local str = client:receive(len) + -- handle serialized table with results + local func, err = loadstring(str) + if func then + status, res = pcall(func) + if not status then err = res + elseif type(res) ~= "table" then + err = "received "..type(res).." instead of expected 'table'" + end + end + if err then + print("Error in processing results: " .. err) + return nil, nil, "Error in processing results: " .. err + end + print(unpack(res)) + return res[1], res + end + elseif status == "201" then + _, _, file, line = string.find(params, "^201 Started%s+(.-)%s+(%d+)%s*$") + elseif status == "202" or params == "200 OK" then + -- do nothing; this only happens when RE/LOAD command gets the response + -- that was for the original command that was aborted + elseif status == "204" then + local _, _, stream, size = string.find(params, "^204 Output (%w+) (%d+)$") + if stream and size then + local msg = client:receive(tonumber(size)) + print(msg) + if outputs[stream] then outputs[stream](msg) end + -- this was just the output, so go back reading the response + done = false + end + elseif status == "401" then + len = tonumber(len) + local res = client:receive(len) + print("Error in expression: " .. res) + return nil, nil, res + else + print("Unknown error") + return nil, nil, "Debugger error: unexpected response after EXEC/LOAD '" .. params .. "'" + end + if done then break end + end + else + print("Invalid command") + end + elseif command == "listb" then + for l, v in pairs(breakpoints) do + for f in pairs(v) do + print(f .. ": " .. l) + end + end + elseif command == "listw" then + for i, v in pairs(watches) do + print("Watch exp. " .. i .. ": " .. v) + end + elseif command == "suspend" then + client:send("SUSPEND\n") + elseif command == "stack" then + client:send("STACK\n") + local resp = client:receive() + local _, _, status, res = string.find(resp, "^(%d+)%s+%w+%s+(.+)%s*$") + if status == "200" then + local func, err = loadstring(res) + if func == nil then + print("Error in stack information: " .. err) + return nil, nil, err + end + local ok, stack = pcall(func) + if not ok then + print("Error in stack information: " .. stack) + return nil, nil, stack + end + for _,frame in ipairs(stack) do + print(serpent.line(frame[1], {comment = false})) + end + return stack + elseif status == "401" then + local _, _, len = string.find(resp, "%s+(%d+)%s*$") + len = tonumber(len) + local res = len > 0 and client:receive(len) or "Invalid stack information." + print("Error in expression: " .. res) + return nil, nil, res + else + print("Unknown error") + return nil, nil, "Debugger error: unexpected response after STACK" + end + elseif command == "output" then + local _, _, stream, mode = string.find(params, "^[a-z]+%s+(%w+)%s+([dcr])%s*$") + if stream and mode then + client:send("OUTPUT "..stream.." "..mode.."\n") + local resp = client:receive() + local _, _, status = string.find(resp, "^(%d+)%s+%w+%s*$") + if status == "200" then + print("Stream "..stream.." redirected") + outputs[stream] = type(options) == 'table' and options.handler or nil + else + print("Unknown error") + return nil, nil, "Debugger error: can't redirect "..stream + end + else + print("Invalid command") + end + elseif command == "basedir" then + local _, _, dir = string.find(params, "^[a-z]+%s+(.+)$") + if dir then + dir = string.gsub(dir, "\\", "/") -- convert slash + if not string.find(dir, "/$") then dir = dir .. "/" end - local remdir = dir:match("\t(.+)") - if remdir then dir = dir:gsub("/?\t.+", "/") end - basedir = dir + local remdir = dir:match("\t(.+)") + if remdir then dir = dir:gsub("/?\t.+", "/") end + basedir = dir - client:send("BASEDIR "..(remdir or dir).."\n") - local resp = client:receive() - local _, _, status = string.find(resp, "^(%d+)%s+%w+%s*$") - if status == "200" then - print("New base directory is " .. basedir) - else - print("Unknown error") - return nil, nil, "Debugger error: unexpected response after BASEDIR" - end - else - print(basedir) - end - elseif command == "help" then - print("setb -- sets a breakpoint") - print("delb -- removes a breakpoint") - print("delallb -- removes all breakpoints") - print("setw -- adds a new watch expression") - print("delw -- removes the watch expression at index") - print("delallw -- removes all watch expressions") - print("run -- runs until next breakpoint") - print("step -- runs until next line, stepping into function calls") - print("over -- runs until next line, stepping over function calls") - print("out -- runs until line after returning from current function") - print("listb -- lists breakpoints") - print("listw -- lists watch expressions") - print("eval -- evaluates expression on the current context and returns its value") - print("exec -- executes statement on the current context") - print("load -- loads a local file for debugging") - print("reload -- restarts the current debugging session") - print("stack -- reports stack trace") - print("output stdout -- capture and redirect io stream (default|copy|redirect)") - print("basedir [] -- sets the base path of the remote application, or shows the current one") - print("exit -- exits debugger") - else - local _, _, spaces = string.find(params, "^(%s*)$") - if not spaces then - print("Invalid command") - return nil, nil, "Invalid command" - end - end - return file, line + client:send("BASEDIR "..(remdir or dir).."\n") + local resp = client:receive() + local _, _, status = string.find(resp, "^(%d+)%s+%w+%s*$") + if status == "200" then + print("New base directory is " .. basedir) + else + print("Unknown error") + return nil, nil, "Debugger error: unexpected response after BASEDIR" + end + else + print(basedir) + end + elseif command == "help" then + print("setb -- sets a breakpoint") + print("delb -- removes a breakpoint") + print("delallb -- removes all breakpoints") + print("setw -- adds a new watch expression") + print("delw -- removes the watch expression at index") + print("delallw -- removes all watch expressions") + print("run -- runs until next breakpoint") + print("step -- runs until next line, stepping into function calls") + print("over -- runs until next line, stepping over function calls") + print("out -- runs until line after returning from current function") + print("listb -- lists breakpoints") + print("listw -- lists watch expressions") + print("eval -- evaluates expression on the current context and returns its value") + print("exec -- executes statement on the current context") + print("load -- loads a local file for debugging") + print("reload -- restarts the current debugging session") + print("stack -- reports stack trace") + print("output stdout -- capture and redirect io stream (default|copy|redirect)") + print("basedir [] -- sets the base path of the remote application, or shows the current one") + print("exit -- exits debugger") + else + local _, _, spaces = string.find(params, "^(%s*)$") + if not spaces then + print("Invalid command") + return nil, nil, "Invalid command" + end + end + return file, line end -- Starts debugging server local function listen(host, port) - host = host or "*" - port = port or mobdebug.port + host = host or "*" + port = port or mobdebug.port - local socket = require "socket" + local socket = require "socket" - print("Lua Remote Debugger") - print("Run the program you wish to debug") + print("Lua Remote Debugger") + print("Run the program you wish to debug") - local server = socket.bind(host, port) - local client = server:accept() + local server = socket.bind(host, port) + local client = server:accept() - client:send("STEP\n") - client:receive() + client:send("STEP\n") + client:receive() - local breakpoint = client:receive() - local _, _, file, line = string.find(breakpoint, "^202 Paused%s+(.-)%s+(%d+)%s*$") - if file and line then - print("Paused at file " .. file ) - print("Type 'help' for commands") - else - local _, _, size = string.find(breakpoint, "^401 Error in Execution (%d+)%s*$") - if size then - print("Error in remote application: ") - print(client:receive(size)) - end - end + local breakpoint = client:receive() + local _, _, file, line = string.find(breakpoint, "^202 Paused%s+(.-)%s+(%d+)%s*$") + if file and line then + print("Paused at file " .. file ) + print("Type 'help' for commands") + else + local _, _, size = string.find(breakpoint, "^401 Error in Execution (%d+)%s*$") + if size then + print("Error in remote application: ") + print(client:receive(size)) + end + end - while true do - io.write("> ") - local line = io.read("*line") - handle(line, client) - end + while true do + io.write("> ") + local line = io.read("*line") + handle(line, client) + end end local cocreate local function coro() - if cocreate then return end -- only set once - cocreate = cocreate or coroutine.create - coroutine.create = function(f, ...) - return cocreate(function(...) - require("mobdebug").on() - return f(...) - end, ...) - end + if cocreate then return end -- only set once + cocreate = cocreate or coroutine.create + coroutine.create = function(f, ...) + return cocreate(function(...) + require("mobdebug").on() + return f(...) + end, ...) + end end local moconew local function moai() - if moconew then return end -- only set once - moconew = moconew or (MOAICoroutine and MOAICoroutine.new) - if not moconew then return end - MOAICoroutine.new = function(...) - local thread = moconew(...) - -- need to support both thread.run and getmetatable(thread).run, which - -- was used in earlier MOAI versions - local mt = thread.run and thread or getmetatable(thread) - local patched = mt.run - mt.run = function(self, f, ...) - return patched(self, function(...) - require("mobdebug").on() - return f(...) - end, ...) - end - return thread - end + if moconew then return end -- only set once + moconew = moconew or (MOAICoroutine and MOAICoroutine.new) + if not moconew then return end + MOAICoroutine.new = function(...) + local thread = moconew(...) + -- need to support both thread.run and getmetatable(thread).run, which + -- was used in earlier MOAI versions + local mt = thread.run and thread or getmetatable(thread) + local patched = mt.run + mt.run = function(self, f, ...) + return patched(self, function(...) + require("mobdebug").on() + return f(...) + end, ...) + end + return thread + end end -- this is a function that removes all hooks and closes the socket to -- report back to the controller that the debugging is done. -- the script that called `done` can still continue. local function done() - if not (isrunning() and server) then return end + if not (isrunning() and server) then return end - if not jit then - for co, debugged in pairs(coroutines) do - if debugged then debug.sethook(co) end - end - end + if not jit then + for co, debugged in pairs(coroutines) do + if debugged then debug.sethook(co) end + end + end - debug.sethook() - server:close() + debug.sethook() + server:close() - coro_debugger = nil -- to make sure isrunning() returns `false` - seen_hook = nil -- to make sure that the next start() call works - abort = nil -- to make sure that callback calls use proper "abort" value + coro_debugger = nil -- to make sure isrunning() returns `false` + seen_hook = nil -- to make sure that the next start() call works + abort = nil -- to make sure that callback calls use proper "abort" value end -- make public functions available diff --git a/spine-corona/spine-corona/spine.lua b/spine-corona/spine-corona/spine.lua index 9bfff5e21..6ecfd0989 100644 --- a/spine-corona/spine-corona/spine.lua +++ b/spine-corona/spine-corona/spine.lua @@ -71,7 +71,7 @@ spine.utils.readFile = function (fileName, base) io.close(file) return contents end - + local json = require "json" spine.utils.readJSON = function (text) return json.decode(text) @@ -111,15 +111,15 @@ end function spine.Skeleton:updateWorldTransform() spine.Skeleton.updateWorldTransform_super(self) local premultipliedAlpha = self.premultipliedAlpha - + self.batches = 0 - + -- Remove old drawing group, we will start anew if self.drawingGroup then self.drawingGroup:removeSelf() end local drawingGroup = display.newGroup() self.drawingGroup = drawingGroup self.group:insert(drawingGroup) - + local drawOrder = self.drawOrder local currentGroup = nil local groupVertices = {} @@ -149,12 +149,12 @@ function spine.Skeleton:updateWorldTransform() color = { vertices[5], vertices[6], vertices[7], vertices[8] } blendMode = toCoronaBlendMode(slot.data.blendMode) end - + if texture and vertices and indices then if not lastTexture then lastTexture = texture end if not lastColor then lastColor = color end if not lastBlendMode then lastBlendMode = blendMode end - + if (texture ~= lastTexture or not colorEquals(color, lastColor) or blendMode ~= lastBlendMode) then self:flush(groupVertices, groupUvs, groupIndices, lastTexture, lastColor, lastBlendMode, drawingGroup) lastTexture = texture @@ -164,12 +164,12 @@ function spine.Skeleton:updateWorldTransform() groupUvs = {} groupIndices = {} end - + self:batch(vertices, indices, groupVertices, groupUvs, groupIndices) end end end - + if #groupVertices > 0 then self:flush(groupVertices, groupUvs, groupIndices, texture, color, blendMode, drawingGroup) end @@ -202,7 +202,7 @@ function spine.Skeleton:batch(vertices, indices, groupVertices, groupUvs, groupI indexStart = indexStart + 1 i = i + 1 end - + i = 1 local numVertices = #vertices local vertexStart = #groupVertices + 1 diff --git a/spine-csharp/src/Animation.cs b/spine-csharp/src/Animation.cs index dc9315b8e..97b3339c5 100644 --- a/spine-csharp/src/Animation.cs +++ b/spine-csharp/src/Animation.cs @@ -661,7 +661,7 @@ namespace Spine { : base(frameCount) { frames = new float[frameCount * ENTRIES]; } - + /// Sets the time, mix and bend direction of the specified keyframe. public void SetFrame (int frameIndex, float time, float mix, int bendDirection) { frameIndex *= ENTRIES; @@ -836,7 +836,7 @@ namespace Spine { public PathConstraintMixTimeline (int frameCount) : base(frameCount) { frames = new float[frameCount * ENTRIES]; - } + } /** Sets the time and mixes of the specified keyframe. */ public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix) { diff --git a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs index bc1e94ca2..76d9e2270 100644 --- a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs +++ b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs @@ -71,7 +71,7 @@ namespace Spine { attachment.regionOriginalWidth = region.originalWidth; attachment.regionOriginalHeight = region.originalHeight; return attachment; - } + } public BoundingBoxAttachment NewBoundingBoxAttachment (Skin skin, String name) { return new BoundingBoxAttachment(name); diff --git a/spine-csharp/src/Attachments/PathAttachment.cs b/spine-csharp/src/Attachments/PathAttachment.cs index 3a4d9db87..fa5890679 100644 --- a/spine-csharp/src/Attachments/PathAttachment.cs +++ b/spine-csharp/src/Attachments/PathAttachment.cs @@ -43,6 +43,6 @@ namespace Spine { public PathAttachment (String name) : base(name) { - } + } } } diff --git a/spine-csharp/src/Attachments/RegionAttachment.cs b/spine-csharp/src/Attachments/RegionAttachment.cs index 70cf57576..c6c29e0d8 100644 --- a/spine-csharp/src/Attachments/RegionAttachment.cs +++ b/spine-csharp/src/Attachments/RegionAttachment.cs @@ -136,7 +136,7 @@ namespace Spine { public void ComputeWorldVertices (Bone bone, float[] worldVertices) { Skeleton skeleton = bone.skeleton; - float x = skeleton.x + bone.worldX, y = skeleton.y + bone.worldY; + float x = skeleton.x + bone.worldX, y = skeleton.y + bone.worldY; float a = bone.a, b = bone.b, c = bone.c, d = bone.d; float[] offset = this.offset; worldVertices[X1] = offset[X1] * a + offset[Y1] * b + x; diff --git a/spine-csharp/src/Attachments/VertexAttachment.cs b/spine-csharp/src/Attachments/VertexAttachment.cs index 1f3e87e8b..b9df01ab2 100644 --- a/spine-csharp/src/Attachments/VertexAttachment.cs +++ b/spine-csharp/src/Attachments/VertexAttachment.cs @@ -112,6 +112,6 @@ namespace Spine { /// Returns true if a deform originally applied to the specified attachment should be applied to this attachment. virtual public bool ApplyDeform (VertexAttachment sourceAttachment) { return this == sourceAttachment; - } + } } } diff --git a/spine-csharp/src/Bone.cs b/spine-csharp/src/Bone.cs index 203c28d69..a155bb153 100644 --- a/spine-csharp/src/Bone.cs +++ b/spine-csharp/src/Bone.cs @@ -294,7 +294,7 @@ namespace Spine { appliedRotation = rotation; } - public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) { + public void WorldToLocal (float worldX, float worldY, out float localX, out float localY) { float a = this.a, b = this.b, c = this.c, d = this.d; float invDet = 1 / (a * d - b * c); float x = worldX - this.worldX, y = worldY - this.worldY; diff --git a/spine-csharp/src/Json.cs b/spine-csharp/src/Json.cs index 9a2e1e72f..73fbed789 100644 --- a/spine-csharp/src/Json.cs +++ b/spine-csharp/src/Json.cs @@ -54,7 +54,7 @@ namespace Spine { * * Changes made: * - * - Optimized parser speed (deserialize roughly near 3x faster than original) + * - Optimized parser speed (deserialize roughly near 3x faster than original) * - Added support to handle lexer/parser error messages with line numbers * - Added more fine grained control over type conversions during the parsing * - Refactory API (Separate Lexer code from Parser code and the Encoder from Decoder) @@ -133,19 +133,19 @@ namespace SharpJson { int idx = 0; StringBuilder builder = null; - + SkipWhiteSpaces(); - + // " char c = json[index++]; - + bool failed = false; bool complete = false; - + while (!complete && !failed) { if (index == json.Length) break; - + c = json[index++]; if (c == '"') { complete = true; @@ -153,9 +153,9 @@ namespace SharpJson } else if (c == '\\') { if (index == json.Length) break; - + c = json[index++]; - + switch (c) { case '"': stringBuffer[idx++] = '"'; @@ -185,10 +185,10 @@ namespace SharpJson int remainingLength = json.Length - index; if (remainingLength >= 4) { var hex = new string(json, index, 4); - + // XXX: handle UTF stringBuffer[idx++] = (char) Convert.ToInt32(hex, 16); - + // skip 4 chars index += 4; } else { @@ -199,38 +199,38 @@ namespace SharpJson } else { stringBuffer[idx++] = c; } - + if (idx >= stringBuffer.Length) { if (builder == null) builder = new StringBuilder(); - + builder.Append(stringBuffer, 0, idx); idx = 0; } } - + if (!complete) { success = false; return null; } - + if (builder != null) return builder.ToString (); else return new string (stringBuffer, 0, idx); } - + string GetNumberString() { SkipWhiteSpaces(); int lastIndex = GetLastIndexOfNumber(index); int charLength = (lastIndex - index) + 1; - + var result = new string (json, index, charLength); - + index = lastIndex + 1; - + return result; } @@ -238,10 +238,10 @@ namespace SharpJson { float number; var str = GetNumberString (); - + if (!float.TryParse (str, NumberStyles.Float, CultureInfo.InvariantCulture, out number)) return 0; - + return number; } @@ -249,25 +249,24 @@ namespace SharpJson { double number; var str = GetNumberString (); - + if (!double.TryParse(str, NumberStyles.Any, CultureInfo.InvariantCulture, out number)) return 0; - + return number; } - + int GetLastIndexOfNumber(int index) { int lastIndex; - + for (lastIndex = index; lastIndex < json.Length; lastIndex++) { char ch = json[lastIndex]; - - if ((ch < '0' || ch > '9') && ch != '+' && ch != '-' - && ch != '.' && ch != 'e' && ch != 'E') + + if ((ch < '0' || ch > '9') && ch != '+' && ch != '-' && ch != '.' && ch != 'e' && ch != 'E') break; } - + return lastIndex - 1; } @@ -302,9 +301,9 @@ namespace SharpJson { if (index == json.Length) return Token.None; - + char c = json[index++]; - + switch (c) { case '{': return Token.CurlyOpen; @@ -327,38 +326,41 @@ namespace SharpJson } index--; - + int remainingLength = json.Length - index; - + // false if (remainingLength >= 5) { if (json[index] == 'f' && - json[index + 1] == 'a' && - json[index + 2] == 'l' && - json[index + 3] == 's' && - json[index + 4] == 'e') { + json[index + 1] == 'a' && + json[index + 2] == 'l' && + json[index + 3] == 's' && + json[index + 4] == 'e' + ) { index += 5; return Token.False; } } - + // true if (remainingLength >= 4) { if (json[index] == 't' && - json[index + 1] == 'r' && - json[index + 2] == 'u' && - json[index + 3] == 'e') { + json[index + 1] == 'r' && + json[index + 2] == 'u' && + json[index + 3] == 'e' + ) { index += 4; return Token.True; } } - + // null if (remainingLength >= 4) { if (json[index] == 'n' && - json[index + 1] == 'u' && - json[index + 2] == 'l' && - json[index + 3] == 'l') { + json[index + 1] == 'u' && + json[index + 2] == 'l' && + json[index + 3] == 'l' + ) { index += 4; return Token.Null; } @@ -438,25 +440,25 @@ namespace SharpJson TriggerError("Invalid token; expected ':'"); return null; } - + // value object value = ParseValue(); if (errorMessage != null) return null; - + table[name] = value; break; } } - + //return null; // Unreachable code } IList ParseArray() { var array = new List(); - + // [ lexer.NextToken(); @@ -483,7 +485,7 @@ namespace SharpJson break; } } - + //return null; // Unreachable code } diff --git a/spine-csharp/src/MathUtils.cs b/spine-csharp/src/MathUtils.cs index 8a64c3a5e..78f761677 100644 --- a/spine-csharp/src/MathUtils.cs +++ b/spine-csharp/src/MathUtils.cs @@ -62,12 +62,12 @@ namespace Spine { static public float Cos (float radians) { return sin[(int)((radians + PI / 2) * radToIndex) & SIN_MASK]; } - + /// Returns the sine in radians from a lookup table. static public float SinDeg (float degrees) { return sin[(int)(degrees * degToIndex) & SIN_MASK]; } - + /// Returns the cosine in radians from a lookup table. static public float CosDeg (float degrees) { return sin[(int)((degrees + 90) * degToIndex) & SIN_MASK]; diff --git a/spine-csharp/src/PathConstraint.cs b/spine-csharp/src/PathConstraint.cs index 3c0262b06..5fd2208cc 100644 --- a/spine-csharp/src/PathConstraint.cs +++ b/spine-csharp/src/PathConstraint.cs @@ -68,7 +68,7 @@ namespace Spine { public void Apply () { Update(); } - + public void Update () { PathAttachment attachment = target.Attachment as PathAttachment; if (attachment == null) return; diff --git a/spine-csharp/src/PathConstraintData.cs b/spine-csharp/src/PathConstraintData.cs index 20d5f5b09..72c8f4723 100644 --- a/spine-csharp/src/PathConstraintData.cs +++ b/spine-csharp/src/PathConstraintData.cs @@ -42,7 +42,7 @@ namespace Spine { internal float position, spacing, rotateMix, translateMix; public ExposedList Bones { get { return bones; } } - public SlotData Target { get { return target; } set { target = value; } } + public SlotData Target { get { return target; } set { target = value; } } public PositionMode PositionMode { get { return positionMode; } set { positionMode = value; } } public SpacingMode SpacingMode { get { return spacingMode; } set { spacingMode = value; } } public RotateMode RotateMode { get { return rotateMode; } set { rotateMode = value; } } @@ -58,9 +58,9 @@ namespace Spine { this.name = name; } } - + public enum PositionMode { - Fixed, Percent + Fixed, Percent } public enum SpacingMode { diff --git a/spine-csharp/src/Skeleton.cs b/spine-csharp/src/Skeleton.cs index 4ba922287..c8316ab65 100644 --- a/spine-csharp/src/Skeleton.cs +++ b/spine-csharp/src/Skeleton.cs @@ -78,7 +78,7 @@ namespace Spine { foreach (BoneData boneData in data.bones) { Bone bone; if (boneData.parent == null) { - bone = new Bone(boneData, this, null); + bone = new Bone(boneData, this, null); } else { Bone parent = bones.Items[boneData.parent.index]; bone = new Bone(boneData, this, parent); @@ -416,7 +416,7 @@ namespace Spine { } throw new Exception("Slot not found: " + slotName); } - + /// May be null. public IkConstraint FindIkConstraint (String constraintName) { if (constraintName == null) throw new ArgumentNullException("constraintName", "constraintName cannot be null."); diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index b642bf164..6dab99d75 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -74,7 +74,7 @@ namespace Spine { this.attachmentLoader = attachmentLoader; Scale = 1; } - + #if !ISUNITY && WINDOWS_STOREAPP private async Task ReadFile(string path) { var folder = Windows.ApplicationModel.Package.Current.InstalledLocation; @@ -127,7 +127,7 @@ namespace Spine { String name = ReadString(input); BoneData parent = i == 0 ? null : skeletonData.bones.Items[ReadVarint(input, true)]; BoneData data = new BoneData(i, name, parent); - data.rotation = ReadFloat(input); + data.rotation = ReadFloat(input); data.x = ReadFloat(input) * scale; data.y = ReadFloat(input) * scale; data.scaleX = ReadFloat(input); @@ -171,7 +171,7 @@ namespace Spine { for (int i = 0, n = ReadVarint(input, true); i < n; i++) { TransformConstraintData data = new TransformConstraintData(ReadString(input)); for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) - data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); + data.bones.Add(skeletonData.bones.Items[ReadVarint(input, true)]); data.target = skeletonData.bones.Items[ReadVarint(input, true)]; data.offsetRotation = ReadFloat(input); data.offsetX = ReadFloat(input) * scale; @@ -277,7 +277,7 @@ namespace Spine { switch (type) { case AttachmentType.Region: { String path = ReadString(input); - float rotation = ReadFloat(input); + float rotation = ReadFloat(input); float x = ReadFloat(input); float y = ReadFloat(input); float scaleX = ReadFloat(input); @@ -308,18 +308,18 @@ namespace Spine { int vertexCount = ReadVarint(input, true); Vertices vertices = ReadVertices(input, vertexCount); if (nonessential) ReadInt(input); //int color = nonessential ? ReadInt(input) : 0; // Avoid unused local warning. - + BoundingBoxAttachment box = attachmentLoader.NewBoundingBoxAttachment(skin, name); if (box == null) return null; box.worldVerticesLength = vertexCount << 1; box.vertices = vertices.vertices; - box.bones = vertices.bones; + box.bones = vertices.bones; return box; } case AttachmentType.Mesh: { String path = ReadString(input); int color = ReadInt(input); - int vertexCount = ReadVarint(input, true); + int vertexCount = ReadVarint(input, true); float[] uvs = ReadFloatArray(input, vertexCount << 1, 1); int[] triangles = ReadShortArray(input); Vertices vertices = ReadVertices(input, vertexCount); @@ -400,8 +400,8 @@ namespace Spine { path.vertices = vertices.vertices; path.bones = vertices.bones; path.lengths = lengths; - return path; - } + return path; + } } return null; } @@ -540,7 +540,7 @@ namespace Spine { } // IK timelines. - for (int i = 0, n = ReadVarint(input, true); i < n; i++) { + for (int i = 0, n = ReadVarint(input, true); i < n; i++) { int index = ReadVarint(input, true); int frameCount = ReadVarint(input, true); IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount); @@ -587,7 +587,7 @@ namespace Spine { if (data.positionMode == PositionMode.Fixed) timelineScale = scale; } timeline.pathConstraintIndex = index; - for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { + for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) { timeline.SetFrame(frameIndex, ReadFloat(input), ReadFloat(input) * timelineScale); if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); } @@ -651,7 +651,7 @@ namespace Spine { timeline.SetFrame(frameIndex, time, deform); if (frameIndex < frameCount - 1) ReadCurve(input, frameIndex, timeline); - } + } timelines.Add(timeline); duration = Math.Max(duration, timeline.frames[frameCount - 1]); } diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 7858b91fe..549c4752e 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -144,7 +144,7 @@ namespace Spine { data.b = ToColor(color, 2); data.a = ToColor(color, 3); } - + data.attachmentName = GetString(slotMap, "attachment", null); if (slotMap.ContainsKey("blend")) data.blendMode = (BlendMode)Enum.Parse(typeof(BlendMode), (String)slotMap["blend"], false); @@ -164,7 +164,7 @@ namespace Spine { if (bone == null) throw new Exception("IK constraint bone not found: " + boneName); data.bones.Add(bone); } - + String targetName = (String)constraintMap["target"]; data.target = skeletonData.FindBone(targetName); if (data.target == null) throw new Exception("Target bone not found: " + targetName); @@ -289,7 +289,7 @@ namespace Spine { } catch (Exception e) { throw new Exception("Error reading animation: " + entry.Key, e); } - } + } } skeletonData.bones.TrimExcess(); @@ -632,7 +632,7 @@ namespace Spine { var timeline = new DeformTimeline(values.Count); timeline.slotIndex = slotIndex; timeline.attachment = attachment; - + int frameIndex = 0; foreach (Dictionary valueMap in values) { float[] deform; diff --git a/spine-csharp/src/TransformConstraint.cs b/spine-csharp/src/TransformConstraint.cs index ce35b7a67..f675aa7e8 100644 --- a/spine-csharp/src/TransformConstraint.cs +++ b/spine-csharp/src/TransformConstraint.cs @@ -57,7 +57,7 @@ namespace Spine { bones = new ExposedList(); foreach (BoneData boneData in data.bones) bones.Add (skeleton.FindBone (boneData.name)); - + target = skeleton.FindBone(data.target.name); } diff --git a/spine-love/main.lua b/spine-love/main.lua index d2a10ea96..439ca2057 100644 --- a/spine-love/main.lua +++ b/spine-love/main.lua @@ -34,74 +34,74 @@ local skeletons = {} local activeSkeleton = 1 function loadSkeleton (jsonFile, atlasFile, animation, skin, scale, x, y) - local loader = function (path) return love.graphics.newImage("data/" .. path) end - local atlas = spine.TextureAtlas.new(spine.utils.readFile("data/" .. atlasFile .. ".atlas"), loader) - - local json = spine.SkeletonJson.new(spine.AtlasAttachmentLoader.new(atlas)) - json.scale = scale - local skeletonData = json:readSkeletonDataFile("data/" .. jsonFile .. ".json") - local skeleton = spine.Skeleton.new(skeletonData) - skeleton.x = x - skeleton.y = y - skeleton.flipX = false - skeleton.flipY = true - if skin then - skeleton:setSkin(skin) - end - skeleton:setToSetupPose() - - local stateData = spine.AnimationStateData.new(skeletonData) - local state = spine.AnimationState.new(stateData) - state:setAnimationByName(0, animation, true) - - state.onStart = function (trackIndex) - print(trackIndex.." start: "..state:getCurrent(trackIndex).animation.name) - end - state.onEnd = function (trackIndex) - print(trackIndex.." end: "..state:getCurrent(trackIndex).animation.name) - end - state.onComplete = function (trackIndex, loopCount) - print(trackIndex.." complete: "..state:getCurrent(trackIndex).animation.name..", "..loopCount) - end - state.onEvent = function (trackIndex, event) - print(trackIndex.." event: "..state:getCurrent(trackIndex).animation.name..", "..event.data.name..", "..event.intValue..", "..event.floatValue..", '"..(event.stringValue or "").."'") - end - - state:update(0.5) - state:apply(skeleton) - - return { state = state, skeleton = skeleton } + local loader = function (path) return love.graphics.newImage("data/" .. path) end + local atlas = spine.TextureAtlas.new(spine.utils.readFile("data/" .. atlasFile .. ".atlas"), loader) + + local json = spine.SkeletonJson.new(spine.AtlasAttachmentLoader.new(atlas)) + json.scale = scale + local skeletonData = json:readSkeletonDataFile("data/" .. jsonFile .. ".json") + local skeleton = spine.Skeleton.new(skeletonData) + skeleton.x = x + skeleton.y = y + skeleton.flipX = false + skeleton.flipY = true + if skin then + skeleton:setSkin(skin) + end + skeleton:setToSetupPose() + + local stateData = spine.AnimationStateData.new(skeletonData) + local state = spine.AnimationState.new(stateData) + state:setAnimationByName(0, animation, true) + + state.onStart = function (trackIndex) + print(trackIndex.." start: "..state:getCurrent(trackIndex).animation.name) + end + state.onEnd = function (trackIndex) + print(trackIndex.." end: "..state:getCurrent(trackIndex).animation.name) + end + state.onComplete = function (trackIndex, loopCount) + print(trackIndex.." complete: "..state:getCurrent(trackIndex).animation.name..", "..loopCount) + end + state.onEvent = function (trackIndex, event) + print(trackIndex.." event: "..state:getCurrent(trackIndex).animation.name..", "..event.data.name..", "..event.intValue..", "..event.floatValue..", '"..(event.stringValue or "").."'") + end + + state:update(0.5) + state:apply(skeleton) + + return { state = state, skeleton = skeleton } end function love.load(arg) - if arg[#arg] == "-debug" then require("mobdebug").start() end - table.insert(skeletons, loadSkeleton("test", "test", "animation", nil, 0.5, 400, 300)) - table.insert(skeletons, loadSkeleton("spineboy", "spineboy", "walk", nil, 0.5, 400, 500)) - table.insert(skeletons, loadSkeleton("raptor", "raptor", "walk", nil, 0.3, 400, 500)) - table.insert(skeletons, loadSkeleton("goblins-mesh", "goblins", "walk", "goblin", 1, 400, 500)) - table.insert(skeletons, loadSkeleton("tank", "tank", "drive", nil, 0.2, 600, 500)) - table.insert(skeletons, loadSkeleton("vine", "vine", "animation", nil, 0.3, 400, 500)) - table.insert(skeletons, loadSkeleton("stretchyman", "stretchyman", "sneak", nil, 0.3, 200, 500)) - skeletonRenderer = spine.SkeletonRenderer.new() + if arg[#arg] == "-debug" then require("mobdebug").start() end + table.insert(skeletons, loadSkeleton("test", "test", "animation", nil, 0.5, 400, 300)) + table.insert(skeletons, loadSkeleton("spineboy", "spineboy", "walk", nil, 0.5, 400, 500)) + table.insert(skeletons, loadSkeleton("raptor", "raptor", "walk", nil, 0.3, 400, 500)) + table.insert(skeletons, loadSkeleton("goblins-mesh", "goblins", "walk", "goblin", 1, 400, 500)) + table.insert(skeletons, loadSkeleton("tank", "tank", "drive", nil, 0.2, 600, 500)) + table.insert(skeletons, loadSkeleton("vine", "vine", "animation", nil, 0.3, 400, 500)) + table.insert(skeletons, loadSkeleton("stretchyman", "stretchyman", "sneak", nil, 0.3, 200, 500)) + skeletonRenderer = spine.SkeletonRenderer.new() end function love.update (delta) -- Update the state with the delta time, apply it, and update the world transforms. - local state = skeletons[activeSkeleton].state - local skeleton = skeletons[activeSkeleton].skeleton + local state = skeletons[activeSkeleton].state + local skeleton = skeletons[activeSkeleton].skeleton state:update(delta) state:apply(skeleton) skeleton:updateWorldTransform() end function love.draw () - love.graphics.setBackgroundColor(128, 128, 128, 255) + love.graphics.setBackgroundColor(128, 128, 128, 255) love.graphics.setColor(255, 255, 255) - local skeleton = skeletons[activeSkeleton].skeleton - skeletonRenderer:draw(skeleton) + local skeleton = skeletons[activeSkeleton].skeleton + skeletonRenderer:draw(skeleton) end function love.mousepressed (x, y, button, istouch) - activeSkeleton = activeSkeleton + 1 - if activeSkeleton > #skeletons then activeSkeleton = 1 end + activeSkeleton = activeSkeleton + 1 + if activeSkeleton > #skeletons then activeSkeleton = 1 end end diff --git a/spine-love/spine-love/dkjson.lua b/spine-love/spine-love/dkjson.lua index d5d31a42a..9c10dac3d 100644 --- a/spine-love/spine-love/dkjson.lua +++ b/spine-love/spine-love/dkjson.lua @@ -27,26 +27,26 @@ cycles. `state` is an optional table with the following fields: - - `indent` + - `indent` When `indent` (a boolean) is set, the created string will contain newlines and indentations. Otherwise it will be one long line. - - `keyorder` + - `keyorder` `keyorder` is an array to specify the ordering of keys in the encoded output. If an object has keys which are not in this array they are written after the sorted keys. - - `level` + - `level` This is the initial level of indentation used when `indent` is set. For each level two spaces are added. When absent it is set to 0. - - `buffer` + - `buffer` `buffer` is an array to store the strings for the result so they can be concatenated at once. When it isn't given, the encode function will create it temporary and will return the concatenated result. - - `bufferlen` + - `bufferlen` When `bufferlen` is set, it has to be the index of the last element of `buffer`. - - `tables` + - `tables` `tables` is a set to detect reference cycles. It is created temporary when absent. Every table that is currently processed is used as key, the value is `true`. @@ -187,9 +187,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.