| 10-24-2008, 07:16 PM | #1 |
I've created this library to globally improve speed and readability. Personally I think its almost done, but before I release it into the public I'd like some feedback of the pro's. This library was first build to use variables instead of natives which almost any time return the same value. However, after using this sometime I've modified it and it now holds data which can be used in almost any type of script. Making extensions to this library is pretty easy, However I did not make it so it does anything more then just being a "userdata" library. Making things for example "changing a player name or "hold SetPlayerColor" into account is something rather easy to make. I've made an example script on how to use this script, just look how easy it is to create an advanced script without having to worry about anything about player id's and variable names, etc.. The example code is just a tiny fraction of what this system can do. Anyways, tell me what you think of it, what can perhaps be improved, bugs.. etc. Userdata Library JASS:library UserData requires HandleVars scope ReadMe //************************** //* UserData //* ¯¯¯¯¯¯¯¯ //* The struct UserData holds values of players used globaly in any map. //* Making scripts never was so easy! //* //* There is a lot of crazy things going on this struct for example local code. //* This requires experience with vJass in order to get the maximum use out of it. //* It also detects the host for you and detect leavers with configurable game messages displaying on the event. //* If the host leaves the game, It will find you automatticaly a new host. //* All differnt kinds of data are stored in this struct, heres a list. //* //* .localUser = returns the local user. //* .localUser.isLocal = returns a boolean if the user is local. //* .localPlayer = returns the local player. ( so you never have to call the native again ) //* .host = returns the user id, accessable by ( user[.host].nameColor ) //* //* user[0].p = Player(0) //* user[1].name = "Player 2" //* user[0].hex = "FF0202" ( reds color code ) //* user[0].hexId = Returns the user id matching his player color. //* user[4].nameColor = Returns players 4's name in color matching his player color. //* user[0].nameColorId = "|cffFF0202Player 1|r" ( Returns player 0's name in color matching the slot id. ) //* user[3].color = returns the player color of player 3. //* user[2].colorId = returns the player id matching the color. //* user[0].rgbRed = returns user[0].p's rgb number of red. //* user[0].rgbGreen = returns user[0].p's rgb number of green. //* user[0].rgbBlue = returns user[0].p's rgb number of blue. //* //* user[0].isUser = is user[0].p a user? //* user[0].isComputer = is user[0].p a computer? //* user[0].isCreep = is user[0].p a creep? //* user[0].isNeutral = is user[0].p a neutral? //* user[0].isNone = is user[0].p a none? //* user[0].isRescuable = is user[0].p rescuable? //* //* user[0].isPlaying = is user[0].p playing? //* user[0].isLeaver = is user[0].p a leaver? //* user[0].isEmpty = is user[0].p a empty slot? //* user[0].isPlayingUser = is user[0].p still playing and is a user? //* //* //* As you can see this library requires HandleVars. //* The only thing that this library does that is usefull for this one is. //* //* H2I = Handle to Integer return abuse function. //* SetGameCache = Initialize the gamecache (because struct inits run before library initializers) //* GetGameCache = Returns the initialized gamecache for the FindHost method. //* //* Credits goto Themis@Azeroth and StealthOfKing@Azeroth both members of clan BoM. //* [url]www.clanbom.net[/url] //**************************************************************************************************************** endscope scope Configuration globals constant boolean USER_DATA_SHOW_LEAVE_MESSAGE = true constant boolean USER_DATA_SHOW_HOST_MESSAGE = true constant string USER_DATA_LEAVE_MESSAGE = "has left the game." constant string USER_DATA_HOST_MESSAGE = "is selected as host." constant string USER_DATA_HOST_LEAVE_ADDON = " ( the host ) " constant string USER_DATA_HEX_STRING = "FF02020041FF1BE6D8530080FFFC00FE890D1FBF00E55AAF9495967DBEF10F61454D2903272727272727272727272727" constant string USER_DATA_RGB_R_STRING = "255000027083255254031229148125015077039039039039" constant string USER_DATA_RGB_G_STRING = "002065230000252137191090149190097041039039039039" constant string USER_DATA_RGB_B_STRING = "002255184128000013000175150241069003039039039039" endglobals endscope struct user extends array static user localUser static user host static player localPlayer public player p public boolean isLocal public string name public string hex public string nameColor public playercolor color public string hexId public integer colorId public string nameColorId public integer rgbRed public integer rgbGreen public integer rgbBlue public boolean isUser public boolean isComputer public boolean isCreep public boolean isNeutral public boolean isRescuable public boolean isNone public boolean isPlaying public boolean isLeaver public boolean isEmpty public boolean isPlayingUser static method onEventPlayerLeave takes nothing returns nothing local user u = GetPlayerId( GetTriggerPlayer() ) set u.isPlaying = false set u.isLeaver = true set u.isPlayingUser = false if u == .host then set .host = .findHost() call user[.localUser].displayText( u.nameColor + USER_DATA_HOST_LEAVE_ADDON + USER_DATA_LEAVE_MESSAGE, 10 ) if USER_DATA_SHOW_HOST_MESSAGE then call user[.localUser].displayText( u.nameColor + " " + USER_DATA_HOST_MESSAGE, 10 ) endif else call user[.localUser].displayText( u.nameColor + " " + USER_DATA_LEAVE_MESSAGE, 10 ) endif endmethod private static method getColorHex takes user u returns string return SubString( USER_DATA_HEX_STRING, u * 6, ( u * 6 ) + 6 ) endmethod private static method getColorRgb takes string s, user u returns integer return S2I( SubString( s, u * 3, ( u * 3 ) + 3 ) ) endmethod private static method findHost takes nothing returns user local integer array count local user syncedPlayer = 0 local user tempPlayer = 0 call SetGameCache() loop exitwhen tempPlayer == 12 set tempPlayer:count = 0 set tempPlayer = tempPlayer + 1 endloop loop exitwhen tempPlayer == 0 call StoreInteger( GetGameCache(), "map", "host", .localUser + 1 ) call TriggerSyncStart() call SyncStoredInteger( GetGameCache(), "map", "host" ) call TriggerSyncReady() set syncedPlayer = GetStoredInteger( GetGameCache(), "map", "host" ) - 1 set syncedPlayer:count = syncedPlayer:count + 1 set tempPlayer = tempPlayer - 1 endloop set syncedPlayer = 0 loop exitwhen tempPlayer == 12 if syncedPlayer:count < tempPlayer:count then set syncedPlayer = tempPlayer endif set tempPlayer = tempPlayer + 1 endloop return syncedPlayer endmethod method displayText takes string s, real duration returns nothing call DisplayTimedTextToPlayer( user[this].p, 0, 0, duration, s ) endmethod static method onInit takes nothing returns nothing local trigger t = CreateTrigger() local user u = 0 loop exitwhen integer( u ) > 15 set u.p = Player( u ) set u.name = GetPlayerName( u.p ) set u.color = GetPlayerColor( u.p ) set u.colorId = H2I( u.color ) set u.hex = .getColorHex( u.colorId ) set u.hexId = .getColorHex( u ) set u.nameColor = "|cff" + u.hex + u.name + "|r" set u.nameColorId = "|cff" + u.hexId + u.name + "|r" set u.rgbRed = .getColorRgb( USER_DATA_RGB_R_STRING, u ) set u.rgbGreen = .getColorRgb( USER_DATA_RGB_G_STRING, u ) set u.rgbBlue = .getColorRgb( USER_DATA_RGB_B_STRING, u ) set u.isUser = GetPlayerController( u.p ) == MAP_CONTROL_USER set u.isComputer = GetPlayerController( u.p ) == MAP_CONTROL_COMPUTER set u.isCreep = GetPlayerController( u.p ) == MAP_CONTROL_CREEP set u.isNeutral = GetPlayerController( u.p ) == MAP_CONTROL_NEUTRAL set u.isNone = GetPlayerController( u.p ) == MAP_CONTROL_NONE set u.isRescuable = GetPlayerController( u.p ) == MAP_CONTROL_RESCUABLE set u.isPlaying = GetPlayerSlotState( u.p ) == PLAYER_SLOT_STATE_PLAYING set u.isLeaver = GetPlayerSlotState( u.p ) == PLAYER_SLOT_STATE_LEFT set u.isEmpty = GetPlayerSlotState( u.p ) == PLAYER_SLOT_STATE_EMPTY set u.isPlayingUser = u.isPlaying and u.isUser if u.isPlayingUser then call TriggerRegisterPlayerEvent( t, u.p, EVENT_PLAYER_LEAVE ) endif set u = integer( u ) + 1 endloop set .localPlayer = GetLocalPlayer() set .localUser = GetPlayerId( .localPlayer ) set .localUser.isLocal = true set .host = .findHost() call TriggerAddAction( t, function user.onEventPlayerLeave ) set t = null endmethod endstruct endlibrary HandleVars Library ( Required by the UserData Library ) JASS:library HandleVars initializer SetGameCache globals private gamecache mG = null endglobals //! textmacro Type2Type takes NAME, TYPE, RETURN, RETURNVALUE function $NAME$ takes $TYPE$ t returns $RETURN$ return t return $RETURNVALUE$ endfunction //! endtextmacro //! textmacro HandleVars takes NAME, TYPE function SetHandle$NAME$ takes handle subject, string name, $TYPE$ value returns nothing call Store$NAME$( mG, I2S( H2I( subject ) ), name, value ) endfunction function GetHandle$NAME$ takes handle subject, string name returns $TYPE$ return GetStored$NAME$( mG, I2S( H2I( subject ) ), name ) endfunction //! endtextmacro //! textmacro HandleVarsEx takes NAME, TYPE function SetHandle$NAME$ takes handle subject, string name, $TYPE$ value returns nothing call StoreInteger( mG, I2S( H2I( subject ) ), name, H2I( value ) ) endfunction function GetHandle$NAME$ takes handle subject, string name returns $TYPE$ return GetStoredInteger( mG, I2S( H2I( subject ) ), name ) return null endfunction //! endtextmacro //! runtextmacro Type2Type( "H2I", "handle", "integer", "0" ) //! runtextmacro Type2Type( "i2s", "integer", "string", "\"\"" ) //! runtextmacro Type2Type( "s2i", "string", "integer", "0" ) // runtextmacro HandleVars( "Integer", "integer" ) // runtextmacro HandleVars( "Real", "real" ) // runtextmacro HandleVars( "Boolean", "boolean" ) // runtextmacro HandleVars( "String", "string" ) // runtextmacro HandleVarsEx( "Handle", "handle" ) // runtextmacro HandleVarsEx( "Unit", "unit" ) // runtextmacro HandleVarsEx( "Timer", "timer" ) // runtextmacro HandleVarsEx( "Trigger", "trigger" ) // runtextmacro HandleVarsEx( "Effect", "effect" ) // runtextmacro HandleVarsEx( "Group", "group" ) // runtextmacro HandleVarsEx( "Lightning", "lightning" ) // runtextmacro HandleVarsEx( "Widget", "widget" ) function FlushHandle takes handle subject returns nothing call FlushStoredMission( mG, I2S( H2I( subject ) ) ) endfunction function SetGameCache takes nothing returns nothing if mG == null then set mG = InitGameCache( "HandleVars.w3v" ) call StoreInteger( mG, "init", "cache", 1 ) endif endfunction function GetGameCache takes nothing returns gamecache return mG endfunction function GetSetGameCache takes nothing returns gamecache call SetGameCache() return mG endfunction endlibrary Example Script ( For the UserData library ) JASS:scope exampleforuserdata initializer init function actions takes nothing returns nothing local integer i = 0 local unit uDying = GetDyingUnit() local unit uKiller = GetKillingUnit() local user dying = GetPlayerId( GetOwningPlayer( uDying ) ) local user killer = GetPlayerId( GetOwningPlayer( uKiller ) ) call dying.displayText( "Your unit has died!", 10 ) call killer.displayText( "You killed " + dying.nameColor + "'s unit!", 10 ) if dying.isLocal or killer.isLocal then set uDying = null set uKiller = null return endif call PingMinimapEx( GetUnitX( uDying ), GetUnitY( uDying ), 10, dying.rgbRed, dying.rgbGreen, dying.rgbBlue, false ) call PingMinimapEx( GetUnitX( uKiller ), GetUnitY( uKiller ), 10, killer.rgbRed, killer.rgbGreen, killer.rgbBlue, true ) loop exitwhen i > 11 if i != integer( dying ) and i != integer( killer ) then call user[i].displayText( killer.nameColor + " has killed " + dying.nameColor + "'s unit!", 10 ) endif set i = i + 1 endloop set uDying = null set uKiller = null endfunction function init takes nothing returns nothing local trigger t = CreateTrigger() local user u = 0 loop exitwhen integer( u ) > 11 if u.isPlayingUser then call TriggerRegisterPlayerUnitEvent( t, u.p, EVENT_PLAYER_UNIT_DEATH, null ) endif set u = integer( u ) + 1 endloop call TriggerAddAction( t, function actions ) set t = null endfunction endscope |
| 10-24-2008, 07:43 PM | #2 |
I2H is long considered an unsafe practice in coding. You should make it your first prerogative to eliminate it from your coding practices and code. |
| 10-24-2008, 08:01 PM | #3 | |
Quote:
I don't use the HandleVars library for that, notice that the textmacro's for I2H aren't runned. I use the library mainly for the type2type macro, in this case H2I. However, I added extensions to it so if for any reason whatsover i need those functions I can enable it easily without having to create 2 functions. I see many scripts with a private H2I function, I think its rather idiotic to do that. It just takes 4 extra lines of coding for no reason, perhaps a few bytes but you can't host maps with a higher size of 4mbs, in that case those few bytes do help. |
| 10-24-2008, 09:44 PM | #4 | |
Quote:
Also, that find host function can be really unreliable if the host lags when the game loads or if the map is hosted via a third party program (as many of the things on BNet these days are). |
| 10-24-2008, 10:18 PM | #5 |
So perhaps add an option to either use findHost or have default player assigned to the host. And when the host leaves, a new host is assigned. However, adding the option to update the host when the game is fully loaded could be an improvement aswell. Anyways, even with third party programs the host still should respond the fastest. |
| 10-25-2008, 10:45 PM | #6 | |
Quote:
It's a tricky problem to create a solution for, since all solutions are very inelegant and unpredictable. |
| 10-26-2008, 07:10 AM | #7 |
Smart method, using the indices of the struct created as player numbers. Quite nice. And yeah, I think you should use a private H2I. That way, your system wouldn't require external systems (especially outdated ones). A 4-lines snippet of code is definitely worth the portability IMO. |
| 10-26-2008, 05:21 PM | #8 |
I actually think its fine the way it is, I'd prefer to think h2i, s2i, i2s, etc.. are a native. This is currently either achieve able by creating those functions in the map header, or by creating a library out of them that is required by others. However, I need a initialized game cache for the findHost method and i need a h2i function. Updating the findHost method so that it not uses a gamecache system is rather inefficient because I can either create a global for it, or use a local for it. Extra lines of code only because you guys think HandleVars is outdated and refuse to use it. As I see it, you guys treat HandleVars as a fad and because there is something new doesn't mean that a system like that can be thrown out the window. HandleVars is an exention to Jass, just as vJass is. Perhaps there are better and faster ways to do things now, but duplicating 1 function each time a library requires it is just fundamental bullshit. I have created a few extensions for example a recycle textmacro library Creating all 3 library's + the one of the user data library is a total of 4 requests for the h2i function, which is a total 4 x 4 = 16 additional lines of code. And throughout my library's I need a private function about 12 times created if I listen to you. 12 x 4 = 48 lines of additional bullshit code. Not to mention a few s2i and i2s functions. I created the HandleVars library for reasons - 1 time a GameCache initialization so never would it exceed a 265 creation limit. - Add a function ( such as h2i ) by just running 1 text macro. - When text macro's aren't executed, no function is created. - So I never have to create h2i and other functions of the return bug. - Unused functions are removed by the optimizer. Lets say you work for a company that creates programs or games, And you come with things like this you guys get fired right away. This is not the way to program, duplicates of functions wth. This is what makes me an individual, unlike you guys. I think this HandleVars library should be a mandatory library for scripts that use the return bug. As Rising_Dusk said that perhaps someday Vexorian will auto make those functions to vJass should be a great improvement, however I still need an initialized game cache which is another reason of the use of the HandleVars library. |
| 10-26-2008, 06:44 PM | #9 | ||
![]() Firstly, that thing about the number of extra lines is just illogical. Going with your argument, I should import the entire Caster System because I want CS_H2I() and CS_SafeX() and a few other functions? That just defeats the purpose of modularity and portability, the latter being one of the paradigms vJass was built on (Vex said something like that IIRC). Quote:
Quote:
Well, I guess it does, but not necessarily in a good way. I don't really want to argue with you, so, use whichever method you feel more comfortable with. |
| 10-26-2008, 07:14 PM | #10 | ||
JASS:// runtextmacro HandleVars( "Integer", "integer" ) // runtextmacro HandleVars( "Real", "real" ) // runtextmacro HandleVars( "Boolean", "boolean" ) // runtextmacro HandleVars( "String", "string" ) // runtextmacro HandleVarsEx( "Handle", "handle" ) // runtextmacro HandleVarsEx( "Unit", "unit" ) // runtextmacro HandleVarsEx( "Timer", "timer" ) // runtextmacro HandleVarsEx( "Trigger", "trigger" ) // runtextmacro HandleVarsEx( "Effect", "effect" ) // runtextmacro HandleVarsEx( "Group", "group" ) // runtextmacro HandleVarsEx( "Lightning", "lightning" ) // runtextmacro HandleVarsEx( "Widget", "widget" ) Quote:
It's not going to break the map in any way to have a duplicate of the function, and it's far more convienient, it can reduce importing requirements (which import two libraries, one of which is only required for a single function, when I can just import one?), and I doubt anyone, at this stage, could be arsed recoding their system, adding extra stuff which isn't even needed, just to replace something they already had... If everyone could work together simultaneously, there probably would be only one H2I function per map, but sadly, something like that is probably never going to happen. I don't know the exact length of the Handle Vars code, but it's just an example figure to make the point Quote:
|
| 10-26-2008, 07:26 PM | #11 | ||||||||
Quote:
Quote:
Quote:
There is no doubt that Vexorian is a great programmer, but when Vexorian says Its good to inject yourself with HIV, would you take his advise blindly and do it yourself too? Everybody has its own way of programming, so does Vexorian. If Vexorian likes to create duplicates of functions, let him. It doesn't mean you have to do it as well. Quote:
Quote:
Quote:
It just pisses me off that people say that without giving me a good reason enough why not to do it. I made a Read Me scope, said what HandleVars is and why it is required for. If you plan on using this, you can make up your own mind of rather to customize the library or not. I totally agree on your statement "use whichever method you feel more comfortable with". But if you say that I need to use private I2H functions your just kindly saying to me that I do it wrong and that your way is the way to program it right. Quote:
Quote:
For example if I created a "Caster System" it would only hold methods and structs that has to do with "casters". Functions like cs_h2i and cs_safex has nothing to do with that, its a manner of conversion which was used and called in the past in "handlevars" system. |
| 10-26-2008, 07:51 PM | #12 |
Sorry for off_topic but "wth" means "what the hell" ? |
| 10-26-2008, 11:52 PM | #13 |
Yes. |
| 10-27-2008, 04:25 AM | #14 | |||||||
A lot of your arguments are just fallacious, and don't make any sense. Quote:
Quote:
If you're concerned with the bulk of an imported system, you're only supporting my point that you should duplicate needed functions, and undermining your own. Quote:
Quote:
Quote:
Quote:
Quote:
|
| 10-27-2008, 05:26 AM | #15 | |
Vex really should implement something like: Quote:
I agree with dead_or_alivex, you should be including H2I. |
