I am badly stuck trying to get my userInfo reference. One of my method is returning instance of the object. Everytime createUserInfo is called, it will return the userInfoObject to the lua.
However, when I call a method for userInfo object from Lua, I am not able to get the reference of the userInfo object (lua_touserdata(L,1))
static int getUserName (lua_State *L){
UserInfo **userInfo = (UserInfo**)lua_touserdata(L,1);
// The following is throwing null! Need help.
// Not able to access the userInfo object.
NSLog(#"UserInfo Object: %#", *userInfo);
}
static const luaL_reg userInstance_methods[] = {
{"getUserName", getUserName},
{NULL, NULL}
}
int createUserInfo(lua_State *L){
UserInfo *userInfo = [[UserInfo alloc] init];
UserInfoData **userInfoData = (UserInfoData **)lua_newuserdata(L, sizeof(userInfo*));
*userInfoData = userInfo;
luaL_openlib(L, "userInstance", userInstance_methods, 0);
luaL_getmetatable(L, "userInfoMeta");
lua_setmetatable(L, -2);
return 1;
}
// I have binded newUserInfo to the createUserInfo method.
// I have also created the metatable for this userInfo Object in the init method.
// luaL_newmetatable(L, "userInfoMeta");
// lua_pushstring(L, "__index");
// lua_pushvalue(L, -2);
// lua_settable(L, -3);
// luaL_register(L, NULL, userInstance_methods);
Please let me know if I am missing anything!
My LuaCode snippet:
local library = require('plugin.user')
local userInfo = library.newUserInfo()
print(userInfo.getUserName())
Update
I got rid of null, after using lua_upvalueindex(1) This is giving reference back to the user info instance.
UserInfo **userInfo = (UserInfo**)lua_touserdata(L,lua_upvalueindex( 1 ));
Hope it helps others too!
I think it may be the way your dealing with the userdata's metatable. Specifically, I think what you're returning from createUserInfo() is a table not a userdata. What I suggest is that you create the metatable once e.g. in luaopen, and then just set that on the new userdata. Something like this...
int createUserInfo(lua_State *L) {
UserInfo *userInfo = [[UserInfo alloc] init];
UserInfoData **userInfoData = (UserInfoData **)lua_newuserdata(L, sizeof(userInfo));
*userInfoData = userInfo;
luaL_getmetatable(L, "userInfoMeta");
lua_setmetatable(L, -2);
return 1;
}
LUALIB_API int luaopen_XXX(lua_State *L)
{
luaL_newmetatable(L,"userInfoMeta");
luaL_openlib(L, NULL, userInstance_methods, 0);
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
...
lua_upvalueindex(1) has fixed the nil error.
UserInfo **userInfo = (UserInfo**)lua_touserdata(L,lua_upvalueindex( 1 ));
I want to explain in brief on what is actually happening. function(s) in C will get stack of params passed to the method. Lua online document has example of an Array, where all of its methods take first parameter of array instance. so, lua_touserdata(L,1) worked fine as the first parameter is the array instance.
Example from lua.org shows
a = array.new(10) --size 10
array.insert(a, 1, 1) --array.insert(instance, index, value).
lua_touserdata(L,1) works as the first param is the array instance.
In my case, I was invoking the method over instance without any params. So, the lua stack was empty in my C function and lua_touserdata(L,1) was throwing null.
Example:
a = array.new(10)
a.showValues() --the method is not over array. it is called on instance.
So, inorder to get access to the instance in showValues, I need to call lua_touserdata(L, lua_upvalueindex(1)). This will give the array instance object.
Related
I post a request to get some json data and try to convert the data to Model
var newDrugs = Array<DxyDrugInfo>()
newDrugs = self.parseDrugJsonToModels(data) ?? []
private func parseDrugJsonToModels(data: NSData) -> Array<DxyDrugInfo>?{
let json = JSON(data: data)
if let drugsArray = json["data"].arrayObject as? Array<[String : AnyObject]>, success = json["success"].bool where success {
var sortedArray = Array<DxyDrugInfo>()
for drugDic in drugsArray {
let drugInfo = DxyDrugInfo()
drugInfo.setValuesForKeysWithDictionary(drugDic)
sortedArray.append(drugInfo)
}
return sortedArray
}
return nil
}
DxyDrugInfo is a Objective-C Model Class
My question is the sortedArray's type is Array of DxyDrugInfo, and it changed to
when assigned to newDrugs.
I want to convert it to DxyDrugInfo and I also want to what the type is.
Thank you
That type you're looking at, #lvalue [DxyDrugInfo], is "array of DxyDrugInfo". You're already done! The square brackets around a type are shorthand for "array", that is:
[DxyDrugInfo] == Array<DxyDrugInfo>
and #lvalue just means that newDrugs is a variable you can assign to. (It's the opposite of "r-value", which is a value you could assign to an "l-value".)
This code here is js-ctypes. The code is crashing and I think it's to my misunderstanding of reading about doing stuff on the main thread from other threads.
My end goal is to set a window always on top from a secondary thread, so I have to run performSelectorOnMainThread:withObject:waitUntilDone:
What I am doing is:
Create a class named setWinAlwaysOnTop_class with objc_allocateClassPair
Write my methods in javascript and convert to C (currently i wrote them to just log to console but after I get it working ill update to do NSWindow stuff)
Register unique selector name for my method with sel_registerName
Use class_addMethod to add my C methods from step 2 to class from step 1
objc_registerClassPair on the class from step 1
Allocation my class with alloc
Create instance of class with init
Do performSelectorOnMainThread:withObject:waitUntilDone: on my instance of class created in step 7 with first argument being the unique selector name from step 3, setting 2nd arg withObject to nil, and final arg waitUntilDone to YES
Is this logic correct? And my crash is not due to my logic? Code is below:
var NSObject = ostypes.HELPER.class('NSObject');
if (!OSStuff.setWinAlwaysOnTop_class) {
throw new Error('setWinAlwaysOnTop_class was not previously cleaned up!!')
}
var needsRegistration = true;
OSStuff.setWinAlwaysOnTop_class = ostypes.API('objc_allocateClassPair')(NSObject, 'setWinAlwaysOnTop_class', 0);
if (OSStuff.setWinAlwaysOnTop_class.isNull()) {
console.info('setWinAlwaysOnTop_class:', class_NoitOnScrnSvrDelgt.toString());
throw new Error('setWinAlwaysOnTop_class is null, so objc_allocateClassPair failed');
}
OSStuff.setWinAlwaysOnTop_jsMethods = {}; // holds key of aArrHwndPtrStr and value is js method
var IMP_for_mainThreadSelector = ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, []);
OSStuff.setWinAlwaysOnTop_jsMethods[aArrHwndPtrStr[0]] = function() {
console.log('setWinAlwaysOnTop_jsMethods ' + 0 + ' called');
};
OSStuff.setWinAlwaysOnTop_cMethods = {};
OSStuff.setWinAlwaysOnTop_cMethods[aArrHwndPtrStr[0]] = IMP_for_mainThreadSelector.ptr(OSStuff.setWinAlwaysOnTop_jsMethods[aArrHwndPtrStr[0]]);
OSStuff.setWinAlwaysOnTop_methodSelectors = {};
OSStuff.setWinAlwaysOnTop_methodSelectors[aArrHwndPtrStr[0]] = ostypes.API('sel_registerName')(aArrHwndPtrStr[0]);
var rez_class_addMethod = ostypes.API('class_addMethod')(OSStuff.setWinAlwaysOnTop_class, OSStuff.setWinAlwaysOnTop_methodSelectors[aArrHwndPtrStr[0]], OSStuff.setWinAlwaysOnTop_cMethods[aArrHwndPtrStr[0]], 'v');
if (needsRegistration) {
ostypes.API('objc_registerClassPair')(OSStuff.setWinAlwaysOnTop_class);
OSStuff.setWinAlwaysOnTop_allocation = ostypes.API('objc_msgSend')(OSStuff.setWinAlwaysOnTop_class, ostypes.HELPER.sel('alloc'));
OSStuff.setWinAlwaysOnTop_instance = ostypes.API('objc_msgSend')(OSStuff.setWinAlwaysOnTop_allocation, ostypes.HELPER.sel('init'));
}
var rez_perform = ostypes.API('objc_msgSend')(OSStuff.setWinAlwaysOnTop_instance, ostypes.HELPER.sel('performSelectorOnMainThread:withObject:waitUntilDone:'), ostypes.HELPER.sel(aArrHwndPtrStr[0]), ostypes.CONST.NIL, ostypes.CONST.YES);
console.error('perform done!!');
I know this question has been asked a bunch of times, but none of the answers (or at least what i took away from them) was a help to my particiular problem.
I want to dynamically change a part of the variable path, so i don't have to repeat the same code x-times with just two characters changing.
Here's what i got:
In the beginning of my script, i'm setting the reference to PlayerData scripts, attached to the GameManager object like this:
var P1 : P1_Data;
var P2 : P2_Data;
function Start(){
P1 = GameObject.Find("GameManager").GetComponent.<P1_Data>();
P2 = GameObject.Find("GameManager").GetComponent.<P2_Data>();
}
Later, i want to access these scripts using the currentPlayer variable to dynamically adjust the path:
var currentPlayer : String = "P1"; //this actually happens along with some other stuff in the SwitchPlayers function, i just put it here for better understanding
if (currentPlayer.PlayerEnergy >= value){
// do stuff
}
As i was afraid, i got an error saying, that PlayerEnergy was not a part of UnityEngine.String.
So how do I get unity to read "currentPlayer" as part of the variable path?
Maybe some parse function I haven't found?
Or am I going down an entirely wrong road here?
Cheers
PS: I also tried putting the P1 and P2 variables into an array and access them like this:
if (PlayerData[CurrentPlayerInt].PlayerEnergy >= value){
// do stuff
}
to no success.
First of all,
var currentPlayer : String = "P1"
here P1 is just string, not the previous P1/P2 which are referenced to two scripts. So, if you want, you can change
currentPlayer.PlayerEnergy >= value
to
P1.PlayerEnergy >= value
or,
P2.PlayerEnergy >= value
But if you just want one function for them, like
currentPlayer.PlayerEnergy >= value
Then you have to first set currentPlayer to P1/P2 which I assume you are trying to do. You must have some codes that can verify which player is selected. Then, maybe this can help -
var playerSelected: int = 0;
var currentPlayerEnergy: int = 0;
.....
//Use your codes to verify which player is selected and then,
if (playerSelected == 1) {
currentPlayerEnergy = P1.PlayerEnergy;
} else if (playerSelected == 2) {
currentPlayerEnergy = P2.PlayerEnergy;
}
//Now use your favorite function
if (currentPlayerEnergy >= value) {
//Do stuff
}
As there was no reply providing the answer I needed, I'll share the solution that did the trick for me, provided by a fellow student.
Instead of having the PlayerData scripts pre-written, I generate them using a public class function in a Playermanager script. This generates the Playerdata as attached scripts, saved into an array.
I can then access them through Playermanager.Playerlist[Playernumber].targetvariable.
Which is what I wanted to do, only with the Playerdata being attached to a script instead of a gameobject. And it works great!
Here's the full code of my Playermanager Script:
//initialise max players
public var maxplayers : int = 2;
// Initialise Playerlist
static var Players = new List.<PlayerData>();
function Start () {
for (var i : int = 0; i < maxplayers; i++){
var Player = new PlayerData();
Players.Add(Player);
Players[i].PlayerName = "Player " + i;
}
DontDestroyOnLoad (transform.gameObject);
}
public class PlayerData {
public var PlayerName : String;
public var PlayerEnergy : int = 15;
public var Fleet : List.<GameObject> = new List.<GameObject>();
}
As you see, you can put any type of variable in this class.
I hope this helps some of you who have the same problem.
cheers,
Tux
The AddressBook Framework offers a great method for initializing an ABPerson with a vCard, by using the initWithVCardRepresentation: method.
What I want to do is update a contact with a certain vCard. I can't use initWithVCardRepresentation: because this will give me a new ABPerson object with a new uniqueId and I want to keep the uniqueId in between these changes.
What's an easy way to go about doing something like this?
Thanks!
initWithVCardRepresentation is still the slickest way to turn your vCard into an ABPerson.
Just use the results of it to find the matching person in your Address Book and then iterate over the vCard properties, placing them into the existing record. The save at the end will harden your changes.
The following example assumes that the unique "keys" will be last-name, first-name. You can modify the search element if you want to include companies where no name is listed or whatever, or you could change the iteration scheme by getting [AddressBook people], and then iterating over the people and using only those records where the key-value pairs match to your satisfaction.
- (void)initOrUpdateVCardData:(NSData*)newVCardData {
ABPerson* newVCard = [[ABPerson alloc] initWithVCardRepresentation:newVCardData];
ABSearchEleemnt* lastNameSearchElement
= [ABPerson searchElementForProperty:kABLastNameProperty
label:nil
key:nil
value:[newVCard valueForProperty:kABLastNameProperty]
comparison:kABEqualCaseInsensitive];
ABSearchEleemnt* firstNameSearchElement
= [ABPerson searchElementForProperty:kABFirstNameProperty
label:nil
key:nil
value:[newVCard valueForProperty:kABFirstNameProperty]
comparison:kABEqualCaseInsensitive];
NSArray* searchElements
= [NSArray arrayWithObjects:lastNameSearchElement, firstNameSearchElement, nil];
ABSearchElement* searchCriteria
= [ABSearchElement searchElementForConjunction:kABSearchAnd children:searchElements];
AddressBook* myAddressBook = [AddressBook sharedAddressBook];
NSArray* matchingPersons = [myAddressBook recordsMatchingSearchElement:searchCriteria];
if (matchingPersons.count == 0)
{
[myAddressBook addRecord:newVCard];
}
else if (matchingPersons.count > 1)
{
// decide how to handle error yourself here: return, or resolve conflict, or whatever
}
else
{
ABRecord* existingPerson = matchingPersons.lastObject;
for (NSString* property in [ABPerson properties]) // i.e. *all* potential properties
{
// if the property doesn't exist in the address book, value will be nil
id value = [newVCard valueForProperty:property];
if (value)
{
NSError* error;
if (![existingPerson setValue:value forProperty:property error:&error] || error)
// handle error
}
}
// newVCard with it's new unique-id will now be thrown away
}
[myAddressBook save];
}
I've been teaching myself programming from scratch by creating a simple app in Objective C. Today, I was faced with the issue that I had to write a method that didn't know what type of object it was going to get. With the help of Google, I was delighted to discover something called "casting". :)
I am using casting like so:
- (void)aCustomViewControllerNeedsToChangeStuff:(id)viewController
{
((SpecialViewController *)viewController).aProperty = somethingInteresting;
((SpecialViewController *)viewController).anotherProperty = somethingElse;
((SpecialViewController *)viewController).yetAnotherProperty = moreStuff;
}
Do I have to cast on every line like that, or is there a way I can cast "viewController" once in the scope of the method, to make my code neater?
You can cast your controller to temp variable and use it (also added type check - just in case) :
- (void)aCustomViewControllerNeedsToChangeStuff:(id)viewController
{
if ([viewController isKindOfClass:[SpecialViewController class]]){
SpecialViewController *special = (SpecialViewController *)viewController;
special.aProperty = somethingInteresting;
special.anotherProperty = somethingElse;
special.yetAnotherProperty = moreStuff;
}
}
How about:
- (void)aCustomViewControllerNeedsToChangeStuff:(id)viewController
{
SpecialViewController * controller = (SpecialViewController *)viewController;
controller.aProperty = somethingInteresting;
controller.anotherProperty = somethingElse;
controller.yetAnotherProperty = moreStuff;
}
Use one variable like
SpecialViewController *tempController = (SpecialViewController *)viewController;
than use this variable to access value like
tempController.aProperty