I'm doing some work in Actionscript 2.0 for the first time in a while (really simple stuff, just pulling content from a text file), and I can not fathom for the life of me why I'm getting such unpredictable output here.
Sometimes when I test build a simple script like this, variables are listed as undefined, and sometimes they aren't.
I'd assume that this might be because the data loaded in from cookware.txt wasn't loaded into the memory yet, but this doesn't seem to be the case - according to the Actionscript dictionary here, the onLoad function only fires when data has been loaded as is accessible to the rest of the program.
Can anyone shed some light? Or see why this could be happening?
Contents of cookware.txt:
pots=44&kettles=43
Code
_global.pots;
_global.kettles;
trace('variables not assigned')
trace('before: kettles (global) = ' + _global.kettles);
trace('before: pots (global) = ' + _global.pots);
var my_lv:LoadVars = new LoadVars();
my_lv.onLoad = function(success:Boolean):Void {
if (success) {
trace('variables clearly loaded: kettles = ' + kettles);
_global.kettles = this.kettles;
trace('assigned during loop: kettles in = ' + _global.kettles);
trace('pots = ' + kettles);
_global.pots = this.pots;
trace('during: pots = ' + _global.pots);
} else {
trace("Error");
}
}
my_lv.load("cookware.txt");
trace('after: kettles (global) = ' + _global.kettles);
trace('after: pots (global) = ' + _global.pots);
Output::
Sometimes it's this:
variables not assigned
before: kettles (global) = undefined
before: pots (global) = undefined
after: kettles (global) = undefined
after: pots (global) = undefined
variables clearly loaded: kettles = undefined
assigned during loop: kettles in = 43
pots = 43
during: pots = 43
and sometimes it's this:
variables not assigned
before: kettles (global) = 43
before: pots (global) = 44
after: kettles (global) = 43
after: pots (global) = 44
variables clearly loaded: kettles = 43
assigned during loop: kettles in = 43
pots = 43
during: pots = 43
Again, whether variables are undefined or not seems totally random. What is going on here?
You are likely having scope chain issues. The way it works in Flash is very confusing. The short answer is to name your _global.pots and _global.kettles something different than what is being used in your external file to avoid collisions.
The problem being that inside your function, the "kettles" object will refer to the local "this.kettles" object but only if it is defined. If it is not, then it will refer to the "_global.kettles" object. Which makes the setting behavior of OnLoad difficult to predict.
Little scope suggestions: create a variable currentTimeline = _level0 (or your levelN), you can use inside the success section of your onLoad function, avoid conflicting names and use _global only if strictly necessary.
There are possibly some scope issues here, but I just can't see how
before: kettles (global) = 43
ever happens - you haven't even called load yet. And
after: kettles (global) = 43
probably shouldn't work either as the load will execute asynchronously.
It would seem to suggest that the flash player is caching _global variables in between plays, but I haven't heard of that happening before. How are you building this? Are you just compiling the swf in the Flash IDE each time?
And yes, what David said about scoping and this, especially in that I would trace this.kettles instead of just kettles for consistency.
Thanks for the help guys - I didn't click before that LoadVars() was an asynchronous function, and that the rest of the script would continue without checking if it was loaded.
I managed to fix this by checking it loadVar has completed on each frame, then executing the code after the loadVar was completed.
I had compiling the swf for testing (command + return).
Related
So I have been coding a nextbot in Gmod for a while now and have found a bug that causes Gmod to crash every time the function is run. here is the function:
function ENT:RecomputeTargetPath(path_target)
if self.testmode then
PrintMessage(HUD_PRINTTALK, 'recomputing target path')
end
self.path = Path("Chase")
if (CurTime() - self.LastPathingInfraction < 5) then
return
end
local targetPos = path_target:GetPos()
-- Run toward the position below the ENTity we're targetting, since we can't fly.
trace.start = targetPos
trace.filter = self:GetEnemy()
local tr = util.TraceEntity(trace, self:GetEnemy())
-- Of course, we sure that there IS a "below the target."
if (tr.Hit and util.IsInWorld(tr.HitPos)) then
targetPos = tr.HitPos
end
local rTime = SysTime()
self.path:Compute(self, targetPos)
if (SysTime() - rTime > 0.005) then
self.LastPathingInfraction = CurTime()
end
end
When this function is run, it recomputes the target path for the nextbot to make it move differently.
It's used in the context of an if that looks like this or something very close:
if (CurTime() - self.LastPathRecompute > 0.1) then
self.LastPathRecompute = CurTime()
self:RecomputeTargetPath(self:GetEnemy():GetPos())
end
As far as I know, all variables in the if are called before each.
As far as I know, all variables in the if are called before each.
I tried recalling them but that didn't work at all... I don't know what is happening.
Also, side note, the function gets called multiple times per second based on the player's movement.
I've also added debug prints to every line of the code to see where the issue was and the bug fixed itself when I added them but when I removed them the bug came back...
Any help?
Hello, I am looking for help to find & solve an error.
Having searched the documentation & finding nothing, I thought this might help some!
!1: --# Code | LocalScript --#
!2: function ChangedRO()
!3: RemoveOpen.Value = OOPetsBase + OOSacrificesBase + OOSettingsBase + OOStatsBase
!4: end
!5: OOPetsBase.Changed:Connect(ChangedRO)
!6: OOSacrificesBase.Changed:Connect(ChangedRO)
!7: SettingsBase.Changed:Connect(ChangedRO)
!8: StatsBase.Changed:Connect(ChangedRO)
--# (Here is the mistake) --#
!5: attempt to index number with 'Changed'
Thank you in advance for your answers!
You are getting this error because you are doing 5.Changed
Changed is a method of an instance, so make sure you do it on the instance not on the .Value e.g.
local intVal = Instance.new("IntValue")
intVal.Changed:Connect(...)
-- And not
local val = intVal.Value
val.Changed:Connect(...)
Can you post the the full code next time
This code is for a modding engine, Unitale base on Unity Written in Lua
So I am trying to use a Boolean Variable in my script poseur.lua, so when certain conditions are met so I can pass it to the other script encounter.lua, where a engine Predefined functions is being uses to make actions happens base on the occurring moment.
I tried to read the engine documentation multiple times, follow the exact syntax of Lua's fonction like GetVar(), SetVar(), SetGobal(),GetGlobal().
Searching and google thing about the Language, post on the subreddit and Game Exchange and tried to solve it by myself for hours... I just can't do it and I can't understand why ?
I will show parts of my codes for each.
poseur:
-- A basic monster script skeleton you can copy and modify for your own creations.
comments = {"Smells like the work\rof an enemy stand.",
"Nidhogg_Warrior is posing like his\rlife depends on it.",
"Nidhogg_Warrior's limbs shouldn't\rbe moving in this way."}
commands = {"GREET", "JUMP", "FLIRT", "CRINGE"}
EndDialougue = {" ! ! !","ouiii"}
sprite = "poseur" --Always PNG. Extension is added automatically.
name = "Nidhogg_Warrior"
hp = 99
atk = 1
def = 1
check = "The Nidhogg_Warrior is\rsearching for the Nidhogg"
dialogbubble = "rightlarge" -- See documentation for what bubbles you have available.
canspare = false
cancheck = true
GreetCounter = 5
Berserk = false
encounter:
-- A basic encounter script skeleton you can copy and modify for your own creations.
encountertext = "Nidhogg_Warrior is\rrunning frantically"
nextwaves = {"bullettest_chaserorb"}
wavetimer = 5.0
arenasize = {155, 130}
music = "musAncientGuardian"
enemies = {"poseur"}
require("Monsters.poseur")
enemypositions = {{0, 0}}
-- A custom list with attacks to choose from.
-- Actual selection happens in EnemyDialogueEnding().
-- Put here in case you want to use it.
possible_attacks = {"bullettest_bouncy", "bullettest_chaserorb", "bullettest_touhou"}
function EncounterStarting()
-- If you want to change the game state immediately, this is the place.
Player.lv = 20
Player.hp = 99
Player.name = "Teemies"
poseur.GetVar("Berserk")
end
Thank you for reading.
The answer to my problem was to use SetGobal(), GetGobal().
For some reasons my previous attempt to simply use SetGobal()Resulted in nil value despite writing it like that SetGobal("Berserk",true) gave me a nill value error, as soon as I launch the game.
But I still used them wrong. First I needed to put it SetGobal() at the end of the condition instead of at the start of the the poseur.lua script because the change of value... for some reasons was being overwritten by it first installment.
And to test the variable in the function in my encounter.lua, I needed to write it like that
function EnemyDialogueStarting()
-- Good location for setting monster dialogue depending on how the battle is going.
if GetGlobal("Jimmies") == true then
TEEEST()
end
end
Also any tips an suggestions are still welcome !
Well firstly, in lua simple values like bool and number are copied on assignment:
global={}
a=2
global.a=a--this is a copy
a=4--this change won't affect value in table
print(global.a)--2
print(a)--4
Secondly,
SetGobal and the other mentioned functions are not part of lua language, they must be related to your engine. Probably, they use word 'Global' not as lua 'global' but in a sense defined by engine.
Depending on the engine specifics these functions might as well do a deep copy of any variable they're given (or might as well not work with complicated objects).
I am having a problem with variables inside tables. this is essential since I use tables as configuration for my program.
so I have tested the following code that works:
> x = "X"
> t = {["ref"]="table with value: "..x}
> print(t["ref"])
table with value: X
> x = "Y"
> t = {["ref"]="table with value: "..x}
> print(t["ref"])
table with value: Y
it however doesn't work without the second > t = ["ref"]="table with value: "..x
now I went to implement this into my main program witch consists of two files, one witch returns the configuration table. And one file with all the functions and stuff. it looks as following
FILE A (main.lua):
testString = "test1"
print(testString)
local CONFIG = require'config'
print(CONIFG[1].test)
testString = "test2"
print(testString)
local CONFIG = require'config'
print(CONIFG[1].test)
FILE B (config.lua):
local CONFIG = {
{["test"]=[[this is a test: ]]..testString}
}
return CONFIG
now when i run file A (a.k.a. main.lua) i get the following output:
test1
this is a test: test1
test2
this is a test: test1
i can't figure out what i am doing wrong here.. i thought it had something to do with that it was a single string so i made testString a table but that gave me the same result...
(that title really seems scary.. sorry)
require, by design, caches the return value. So if you call require with the same string, it will not execute the script again. It will simply return the previously returned value.
require is for loading modules. And modules should not change their return values based on other global state.
The function you're probably looking for is dofile. This will always load and execute the file (but it has none of the path searching properties of require). Alternatively, you can use loadfile to load the file as a function, then execute that function to regenerate the table whenever you want.
Also:
I am having a problem with variables inside tables.
There are no "variables inside tables". Or at least not the way you mean. Expecting a change to a variable to affect some other value is like expecting this:
a = 5
b = a + 5
a = 10
assert(b == 15, "This will never be true.")
When an expression (whether a + 5 or "table with value: " .. x) is evaluated, it results in a value. The resulting value is in no way dependent on any value or variable from the expression that generated it.
That's why you had to regenerate the value; because values don't change just because some variable changes.
How do you disable the default action for .torrent files/content-type application/x-bittorrent(eg open with dialog or run program) and instead handle the data in a extension?
There are multiple ways, that all boil down to nsIMimeService/nsIHandlerService and nsIMimeInfo and setting the appropriate nsIHandlerInfo. E.g. see PDF.js making itself the handler for PDF files (by effectively disabling all handler or plugins and implementing a stream converter), or my answer on how to register a web protocol handler (not mime related but protocol related, but the handler info stuff still applies).
Depending on how you'd like to handle things, you may use the nsIHandlerApp-ervied interfaces e.g. to pass the uri (protocols) or file (mime) directly to some local or web application, or implement a full blown stream converter like PDF.js.
In theory, it would be also possible to implement new kinds of nsIHandlerApp-derived interfaces, implementing in particular launchWithURI (protocols) or launchWithFile (mime content types and file extensions (downloads)). However, this is a bit tricky as nsIHandlerService only handles the built-in interfaces.
Based on #nmaiers post this is how you do it:
This is how you do it IF the mime type already exists. If it doesn't exist I don't know how to add it, probably some register function.
For some reason the type for my torrents is application/x-download I have no idea why. If you want info on how I figured that out than I'll tell you. So in the example below I use that as file type.
When we console.log(wrappedHandlerInfo) we see that it looks like this:
so now let's do that enumerate all application handlers (i got this from here: MXR :: gApplicationsPane, and if .type == 'application/x-download' let'sbreak` so we can than play with that object.
var handlerService = Cc['#mozilla.org/uriloader/handler-service;1'].getService(Ci.nsIHandlerService);
var listOfWrappedHandlers = handlerService.enumerate();
var i = 0;
while (listOfWrappedHandlers.hasMoreElements()) {
var wrappedHandlerInfo = listOfWrappedHandlers.getNext().QueryInterface(Ci.nsIHandlerInfo);
console.log(i, 'handler for', wrappedHandlerInfo.type, wrappedHandlerInfo);
if (wrappedHandlerInfo.type == 'application/x-download') {
break;
}
i++;
}
console.log('Listed ', i, ' handlers');
console.log('wrappedHandlerInfo=', wrappedHandlerInfo); //should be the application/x-download one as we broke the loop once it found that
now we have to set its properties then save it.
// Change and save mime handler settings.
wrappedHandlerInfo.alwaysAskBeforeHandling = false;
wrappedHandlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
handlerService.store(wrappedHandlerInfo);
I'm not sure what to change those properties too though, maybe #nmaier can advise us on that.
We see here on MXR :: nsIHandlerService.idl #L69 that store does this:
69 * Save the preferred action, preferred handler, possible handlers, and
70 * always ask properties of the given handler info object to the datastore.
71 * Updates an existing record or creates a new one if necessary.
72 *
73 * Note: if preferred action is undefined or invalid, then we assume
74 * the default value nsIHandlerInfo::useHelperApp.
75 *
76 * #param aHandlerInfo the handler info object
77 */
78 void store(in nsIHandlerInfo aHandlerInfo);
ANOTHER WAY
Ok i found an even better way, this way you don't need to loop to find the handler.
Do this:
var mimeService = Cc['#mozilla.org/mime;1'].getService(Ci.nsIMIMEService);
var CONTENT_TYPE = ''; //'application/x-download'; can leave this blank
var TYPE_EXTENSION = 'torrent';
var handlerInfo = mimeService.getFromTypeAndExtension(CONTENT_TYPE, TYPE_EXTENSION);
console.info('handlerInfo:', handlerInfo); //http://i.imgur.com/dUKox24.png
// Change and save mime handler settings.
handlerInfo.alwaysAskBeforeHandling = false;
handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally;
handlerService.store(handlerInfo);
This handlerInfo object is slightly different in that it has a primaryExtension attribute which holds torrent.
Problem with both ways
The problem with both ways is that, if the mime type doesn't exist, you have to register it somehow, I don't know how. Probably use mime service and some register function.
Update August 3rd 2014
I think i found a solution for the problem mentioned in bullet above (problem with both ways).
MXR :: addPossibleApplicationHandler
235 addPossibleApplicationHandler: function(aNewHandler) {
236 var possibleApps = this.possibleApplicationHandlers.enumerate();
237 while (possibleApps.hasMoreElements()) {
238 if (possibleApps.getNext().equals(aNewHandler))
239 return;
240 }
241 this.possibleApplicationHandlers.appendElement(aNewHandler, false);
242 },
243
This is code for addPossibleApplicationHandler, we probably just need to copy that and edit somehow.
Update August 3rd 2014
Ok this is how to add protocol handler (it only adds a nsIWebAppHandler but im sure to add a local meaning a nsIAppHandler it should be similar just no need for uri param:
https://gist.github.com/Noitidart/2faaac70c62bc13e7773#add-a-handler-to-a-protocol
Info on functions available in nsIMIMEService: MXR :: nsIMIMEService.idl