• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

SharpCraft, a managed replacement for Reinventing the Craft

Status
Not open for further replies.
Level 12
Joined
May 20, 2009
Messages
822
Can we get some basic understanding of what some of the provided plugins can do? I would think WarAPI and JassAPI are more or less self-explanatory, but what can I do with InterfaceAPI, or JassDebugger? Are there things that already explain this stuff? If so why aren't there links to them in the OP?

The sources to these plugins would also be much appreciated.
 
Level 21
Joined
Aug 3, 2004
Messages
710
Can we get some basic understanding of what some of the provided plugins can do? I would think WarAPI and JassAPI are more or less self-explanatory, but what can I do with InterfaceAPI, or JassDebugger? Are there things that already explain this stuff? If so why aren't there links to them in the OP?

The sources to these plugins would also be much appreciated.

The second post has a short description of all the provided plugins. Writing documentation is a tedious task I haven't gotten around to do yet. I'll see if I can clean up the source a bit for the modules, so you can poke around.
 
Level 21
Joined
Aug 3, 2004
Messages
710
The Debugger plugin has been updated with a Cheat callback function which dumps a functions bytecode in a somewhat readable format, as requested by EdwardElric.
JASS:
// The following dumps the function code to a text file name "out.txt" in the "logs" folder in SharpCraft.
call Cheat("scdbg funcdump txt main out.txt")
// The following dumps the function code to a csv file name "out.csv" in the "logs" folder in SharpCraft.
call Cheat("scdbg funcdump csv main out.csv")
 
Level 12
Joined
Mar 13, 2012
Messages
1,121
Yay the source is coming! Is the following already in the debugger?
Cheat("war3err_LocalHTSize") reports size of global hashtable
Cheat("war3err_GlobalHTSize") reports size of local hashtable
Cheat("war3err_DumpLocalHT") prints local hash table to war3err log
Cheat("war3err_DumpGlobalHT") prints global hash table to war3err log

Cheat("war3err_Break") (for conveniently setting a breakpoint somewhere in a function for a debugging program)

edit: that is another problem with having no documentation and no source. Nobody knows how to use the API's ^^.
 
Last edited:
Level 12
Joined
May 20, 2009
Messages
822
The second post has a short description of all the provided plugins. Writing documentation is a tedious task I haven't gotten around to do yet. I'll see if I can clean up the source a bit for the modules, so you can poke around.

I get that there's a brief description there, but "Allows you to interact with the interface" is very vague. The WarCraft III interface? Your own interface? If WC3, how much of the interface can we change? All of it? Can we do stuff with the interface in real time? Does this also mean the pre-game menus, like the loading screen, the race selection screen, etc?

But you are right, writing documentation is a very time-consuming task. OpenRA kind of solves the problem by having a generated document by putting in some special strings and arrays in a file, but that still requires putting those in.
 
Yep, everything from the zip is in there. I don't have a virus scanner. Also set everything to launch as admin, and I have full ownership/rights on the WC3 directory and child objects.

Apparently it's because it's in a subdirectory of WC3.
I get this error (not in cmd) when I don't run as admin:
Code:
System.IO.FileNotFoundException: Could not load file or assembly 'EasyHook, Version=2.7.4761.0, Culture=neutral, PublicKeyToken=4b580fca19d0b0c5' or one of its dependencies. The system cannot find the file specified.

File name: 'EasyHook, Version=2.7.4761.0, Culture=neutral, PublicKeyToken=4b580fca19d0b0c5'

   at EasyHook.RemoteHooking.CreateAndInject(String InEXEPath, String InCommandLine, Int32 InProcessCreationFlags, String InLibraryPath_x86, String InLibraryPath_x64, Int32& OutProcessId, Object[] InPassThruArgs)

   at TinkerWorX.SharpCraft.Launcher.Common.StartGame(String[] args)

   at TinkerWorX.SharpCraft.Launcher.SharpCraftApplication.StartDirect(String[] args)



WRN: Assembly binding logging is turned OFF.

To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.

Note: There is some performance penalty associated with assembly bind failure logging.

To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].

When I do run as admin, I get what I said in the previous post.
It only happens when it's in a WC3 subdirectory though, so I'm guessing it's having trouble hooking into war3.exe. I do have it set to run as admin though.
 
That's weird, it only doesn't work when the folder is called SharpCraft. I renamed the folder and it works now!
Thanks for the help!

I have a few more questions though. I want to do some simple modifications to WC3:
-In the campaign screen, I want to intercept when either a campaign or a mission is clicked, and change the behaviour (I want to add another "level"). So I'm guessing I need to extend a native function.
-In the main menu, I want to load a different map when the Credits button is clicked.

Could you help me on my way? Do I need specific plugins to be able to do that? Can I write my own plugin for that?
 
Level 13
Joined
Jan 2, 2016
Messages
978
Hmm, are there any guides how to install it?
I'm getting this message when I try to run the EasyHook64Svc:
Code:
Cannot start service from the command line or a debugger. A Windows Service
must first be installed (using installutil.exe) and then started with the
ServerExplorer, Windowns Services Administrative or the NET START
command.
 
Level 21
Joined
Aug 3, 2004
Messages
710
Hmm, are there any guides how to install it?
I'm getting this message when I try to run the EasyHook64Svc:
Code:
Cannot start service from the command line or a debugger. A Windows Service
must first be installed (using installutil.exe) and then started with the
ServerExplorer, Windowns Services Administrative or the NET START
command.

You are supposed to run the launcher using either -game or -editor. There are batch files included to make this easier, like "Start game windowed.bat".
 
Level 12
Joined
Mar 13, 2012
Messages
1,121
If there is enough interest, I'll implement backwards compatibility.
It might be convenient since there are people around the world playing on older versions. Also for testing purposes, watching replays, etc one might switch to older versions of wc3.

As I understand it, compatibility is about some address constants so it's a rather clean implementation. So imo everyone should just need to download the newest version of SharpCraft to have a working solution and not complicate things more.

As I said in the past, open sourcing also the plugins would be welcomed. I do realize you have a life though, so do it when you find time :).
 
Level 21
Joined
Aug 3, 2004
Messages
710
As I understand it, compatibility is about some address constants so it's a rather clean implementation.

It's not quite as simple. In the future, API's might change to use new things I find. So while I can easily support old features with new addresses, supporting new features with old addresses is a problem. But like I said, if there is enough of an interest I'll see if I can cook something up. At the very least, I can make it fail graciously.
As I said in the past, open sourcing also the plugins would be welcomed. I do realize you have a life though, so do it when you find time :).

Sharing the source for the plugins is no problem. The reason I removed them from the repo was because they have a very chaotic update cycle and lots of experimentation, which lead to a mess of version control.
 
Level 4
Joined
May 25, 2009
Messages
100
I've got the problem that i can't start maps with custom natives. The pluginsystem works (i think), but when i want to start the map i get thrown back to the mapselection.

Debugger:
http://www.directupload.net/file/d/4302/3rss7luv_jpg

Code:
Code:
package Test

native getMaNumber() returns int

function bla()
	print("bla")
	print(getMaNumber().toString())

init
	trigger t = CreateTrigger()
		..registerTimerEvent(0.1,false)
		..addAction(function bla)
(I tried it with vJass aswell...same results)

Plugin:
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TinkerWorX.SharpCraft;
using MindWorX.SharpCraft.Modules.JassAPI;

namespace TestPlugin
{
    [Requires(typeof(JassAPIPlugin))]
    public class Class1 : IMapPlugin
    {
        private delegate JassInteger getMaNumberProto();
        private JassInteger getMaNumber()
        {
            return 42;
        }

        void IMapPlugin.OnMapStart()
        {
            Natives.Add(new getMaNumberProto(this.getMaNumber));
        }

        void IMapPlugin.OnMapEnd()
        {

        }
    }
}
 
Level 4
Joined
May 25, 2009
Messages
100
I just tried out the exact code you posted, and it worked just fine. Could you zip up the map file and send it to me? If it throws you back, it is usually a problem with the map code. I tested with Cohadar's JassHelper.

Attached

war3map.j:
JASS:
// this script was compiled with wurst 1.5.0.0-jenkins-Wurst-125
globals
hashtable w=null
integer u=0
item r=null
boolean array s
timer t=null
real i=0.
integer array S
integer c=0
integer o=0
integer array O
integer l=0
integer b=0
code y=null
code p=null
code e=null
endglobals
native getMaNumber takes nothing returns integer
function j takes nothing returns integer
local integer k
if c==0 then
set o=o+1
set k=o
else
set c=c-1
set k=S[c]
endif
set O[k]=3
return k
endfunction
function E takes nothing returns nothing
local string W
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"bla")
set W=I2S(getMaNumber())
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,W)
endfunction
function Q takes nothing returns nothing
set i=i+0.03
endfunction
function m takes nothing returns nothing
local player x = GetTriggerPlayer()
local integer v = GetPlayerId(x)
if s[v] then
set s[v]=false
call DisplayTimedTextToPlayer(x,0.,0.,20.,"|cff9C9C9CDEBUG-View|r |cffB55208deactivated")
else
set s[v]=true
call DisplayTimedTextToPlayer(x,0.,0.,20.,"|cff9C9C9CDEBUG-View|r |cff3AAD42activated")
endif
set x=null
endfunction
function q takes nothing returns nothing
set c=0
set o=0
set l=0
set b=0
set y=function m
set p=function Q
set e=function E
endfunction
function main takes nothing returns nothing
local real a
local player n
local player d
local trigger f
local integer R
local integer T
local integer Y
local integer G
call q()
call SetCameraBounds((-3328.0)+GetCameraMargin(CAMERA_MARGIN_LEFT),(-3584.0)+GetCameraMargin(CAMERA_MARGIN_BOTTOM),3328.0-GetCameraMargin(CAMERA_MARGIN_RIGHT),3072.0-GetCameraMargin(CAMERA_MARGIN_TOP),(-3328.0)+GetCameraMargin(CAMERA_MARGIN_LEFT),3072.0-GetCameraMargin(CAMERA_MARGIN_TOP),3328.0-GetCameraMargin(CAMERA_MARGIN_RIGHT),(-3584.0)+GetCameraMargin(CAMERA_MARGIN_BOTTOM))
call SetDayNightModels("Environment\\DNC\\DNCLordaeron\\DNCLordaeronTerrain\\DNCLordaeronTerrain.mdl","Environment\\DNC\\DNCLordaeron\\DNCLordaeronUnit\\DNCLordaeronUnit.mdl")
call NewSoundEnvironment("Default")
if bj_dayAmbientSound!=null then
call StopSound(bj_dayAmbientSound,true,true)
endif
set bj_dayAmbientSound=CreateMIDISound("LordaeronSummerDay",20,20)
set a=GetFloatGameState(GAME_STATE_TIME_OF_DAY)
if a>=bj_TOD_DAWN and a<bj_TOD_DUSK then
call StartSound(bj_dayAmbientSound)
endif
if bj_nightAmbientSound!=null then
call StopSound(bj_nightAmbientSound,true,true)
endif
set bj_nightAmbientSound=CreateMIDISound("LordaeronSummerNight",20,20)
set a=GetFloatGameState(GAME_STATE_TIME_OF_DAY)
if a<bj_TOD_DAWN or a>=bj_TOD_DUSK then
call StartSound(bj_nightAmbientSound)
endif
call SetMapMusic("Music",true,0)
call CreateUnit(Player(0),1214409837,89.0,174.6,307.594)
set d=Player(bj_PLAYER_NEUTRAL_VICTIM)
set Y=0
loop
set n=Player(Y)
call SetPlayerAlliance(d,n,ALLIANCE_PASSIVE,true)
call SetPlayerAlliance(n,d,ALLIANCE_PASSIVE,false)
set Y=Y+1
exitwhen Y==bj_MAX_PLAYERS
endloop
set n=Player(PLAYER_NEUTRAL_AGGRESSIVE)
call SetPlayerAlliance(d,n,ALLIANCE_PASSIVE,true)
call SetPlayerAlliance(n,d,ALLIANCE_PASSIVE,true)
call SetPlayerState(d,PLAYER_STATE_GIVES_BOUNTY,0)
call Filter(function IssueHauntOrderAtLocBJFilter)
call Filter(function EnumDestructablesInCircleBJFilter)
call Filter(function GetUnitsInRectOfPlayerFilter)
call Filter(function GetUnitsOfTypeIdAllFilter)
call Filter(function GetUnitsOfPlayerAndTypeIdFilter)
call Filter(function MeleeTrainedUnitIsHeroBJFilter)
call Filter(function LivingPlayerUnitsOfTypeIdFilter)
set T=0
loop
exitwhen T==bj_MAX_PLAYER_SLOTS
set bj_FORCE_PLAYER[T]=CreateForce()
call ForceAddPlayer(bj_FORCE_PLAYER[T],Player(T))
set T=T+1
endloop
set bj_FORCE_ALL_PLAYERS=CreateForce()
call ForceEnumPlayers(bj_FORCE_ALL_PLAYERS,null)
call GetGameSpeed()
call IsFogEnabled()
call IsFogMaskEnabled()
set T=0
loop
exitwhen T>=bj_MAX_QUEUED_TRIGGERS
set bj_queuedExecTriggers[T]=null
set bj_queuedExecUseConds[T]=false
set T=T+1
endloop
set Y=0
set T=0
loop
exitwhen T>=bj_MAX_PLAYERS
if GetPlayerController(Player(T))==MAP_CONTROL_USER and GetPlayerSlotState(Player(T))==PLAYER_SLOT_STATE_PLAYING then
set Y=Y+1
endif
set T=T+1
endloop
set bj_rescueSound=CreateSoundFromLabel("Rescue",false,false,false,10000,10000)
call CreateSoundFromLabel("QuestNew",false,false,false,10000,10000)
call CreateSoundFromLabel("QuestUpdate",false,false,false,10000,10000)
call CreateSoundFromLabel("QuestCompleted",false,false,false,10000,10000)
call CreateSoundFromLabel("QuestFailed",false,false,false,10000,10000)
call CreateSoundFromLabel("Hint",false,false,false,10000,10000)
call CreateSoundFromLabel("SecretFound",false,false,false,10000,10000)
call CreateSoundFromLabel("ItemReward",false,false,false,10000,10000)
call CreateSoundFromLabel("Warning",false,false,false,10000,10000)
call CreateSoundFromLabel("QuestCompleted",false,false,false,10000,10000)
call CreateSoundFromLabel("QuestFailed",false,false,false,10000,10000)
set bj_delayedSuspendDecayTrig=CreateTrigger()
call TriggerRegisterTimerExpireEvent(bj_delayedSuspendDecayTrig,bj_delayedSuspendDecayTimer)
call TriggerAddAction(bj_delayedSuspendDecayTrig,function DelayedSuspendDecay)
call VersionGet()
set bj_queuedExecTimeout=CreateTrigger()
call TriggerRegisterTimerExpireEvent(bj_queuedExecTimeout,bj_queuedExecTimeoutTimer)
call TriggerAddAction(bj_queuedExecTimeout,function QueuedTriggerDoneBJ)
call InitRescuableBehaviorBJ()
set bj_dawnSound=CreateSoundFromLabel("RoosterSound",false,false,false,10000,10000)
set bj_duskSound=CreateSoundFromLabel("WolfSound",false,false,false,10000,10000)
set bj_dncSoundsDawn=CreateTrigger()
call TriggerRegisterGameStateEvent(bj_dncSoundsDawn,GAME_STATE_TIME_OF_DAY,EQUAL,bj_TOD_DAWN)
call TriggerAddAction(bj_dncSoundsDawn,function SetDNCSoundsDawn)
set bj_dncSoundsDusk=CreateTrigger()
call TriggerRegisterGameStateEvent(bj_dncSoundsDusk,GAME_STATE_TIME_OF_DAY,EQUAL,bj_TOD_DUSK)
call TriggerAddAction(bj_dncSoundsDusk,function SetDNCSoundsDusk)
set bj_dncSoundsDay=CreateTrigger()
call TriggerRegisterGameStateEvent(bj_dncSoundsDay,GAME_STATE_TIME_OF_DAY,GREATER_THAN_OR_EQUAL,bj_TOD_DAWN)
call TriggerRegisterGameStateEvent(bj_dncSoundsDay,GAME_STATE_TIME_OF_DAY,LESS_THAN,bj_TOD_DUSK)
call TriggerAddAction(bj_dncSoundsDay,function SetDNCSoundsDay)
set bj_dncSoundsNight=CreateTrigger()
call TriggerRegisterGameStateEvent(bj_dncSoundsNight,GAME_STATE_TIME_OF_DAY,LESS_THAN,bj_TOD_DAWN)
call TriggerRegisterGameStateEvent(bj_dncSoundsNight,GAME_STATE_TIME_OF_DAY,GREATER_THAN_OR_EQUAL,bj_TOD_DUSK)
call TriggerAddAction(bj_dncSoundsNight,function SetDNCSoundsNight)
call Rect(GetCameraBoundMinX()-GetCameraMargin(CAMERA_MARGIN_LEFT),GetCameraBoundMinY()-GetCameraMargin(CAMERA_MARGIN_BOTTOM),GetCameraBoundMaxX()+GetCameraMargin(CAMERA_MARGIN_RIGHT),GetCameraBoundMaxY()+GetCameraMargin(CAMERA_MARGIN_TOP))
call Rect(GetCameraBoundMinX(),GetCameraBoundMinY(),GetCameraBoundMaxX(),GetCameraBoundMaxY())
set Y=0
loop
if  not GetPlayerTechResearched(Player(Y),1382576756,true) then
call SetPlayerTechMaxAllowed(Player(Y),1752331380,0)
endif
if  not GetPlayerTechResearched(Player(Y),1383031403,true) then
call SetPlayerTechMaxAllowed(Player(Y),1869898347,0)
endif
call SetPlayerTechMaxAllowed(Player(Y),1970498405,bj_MAX_SKELETONS)
set Y=Y+1
exitwhen Y==bj_MAX_PLAYERS
endloop
set Y=0
loop
set bj_stockAllowedPermanent[Y]=false
set bj_stockAllowedCharged[Y]=false
set bj_stockAllowedArtifact[Y]=false
set Y=Y+1
exitwhen Y>bj_MAX_ITEM_LEVEL
endloop
call SetAllItemTypeSlots(bj_MAX_STOCK_ITEM_SLOTS)
call SetAllUnitTypeSlots(bj_MAX_STOCK_UNIT_SLOTS)
set bj_stockUpdateTimer=CreateTimer()
call TimerStart(bj_stockUpdateTimer,bj_STOCK_RESTOCK_INITIAL_DELAY,false,function StartStockUpdates)
set bj_stockItemPurchased=CreateTrigger()
call TriggerRegisterPlayerUnitEvent(bj_stockItemPurchased,Player(PLAYER_NEUTRAL_PASSIVE),EVENT_PLAYER_UNIT_SELL_ITEM,null)
call TriggerAddAction(bj_stockItemPurchased,function RemovePurchasedItem)
set bj_gameStartedTimer=CreateTimer()
call TimerStart(bj_gameStartedTimer,bj_GAME_STARTED_THRESHOLD,false,function MarkGameStarted)
call Player(15)
call Player(12)
call StringLength("0123456789")
call StringLength("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
set w=InitHashtable()
call j()
set T=0
set Y=bj_MAX_PLAYER_SLOTS-1
loop
exitwhen T>Y
call Player(T)
set T=T+1
endloop
set f=CreateTrigger()
call TriggerAddAction(f,y)
set Y=0
loop
exitwhen Y>11
set s[Y]=false
call TriggerRegisterPlayerChatEvent(f,Player(Y),"-d",true)
set Y=Y+1
endloop
set t=CreateTimer()
call TimerStart(t,100000.,false,null)
call TimerStart(CreateTimer(),0.03,true,p)
set Y=0
loop
exitwhen Y>9
call I2S(Y)
set Y=Y+1
endloop
set u=j()
set R=0
loop
exitwhen R>9
set T=u
set Y=StringHash(I2S(R))
if O[T]==0 then
if T==0 then
set G=StringHash("Nullpointer exception when calling Table.saveInt")
if HaveSavedInteger(w,-1,G) then
if LoadInteger(w,-1,G)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
call SaveInteger(w,-1,G,R2I(i))
call SaveBoolean(w,-1,G,false)
elseif HaveSavedBoolean(w,-1,G) then
if  not LoadBoolean(w,-1,G) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,G,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,G,true)
endif
else
call SaveInteger(w,-1,G,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
endif
call I2S(1/0)
else
set G=StringHash("Called Table.saveInt on invalid object.")
if HaveSavedInteger(w,-1,G) then
if LoadInteger(w,-1,G)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
call SaveInteger(w,-1,G,R2I(i))
call SaveBoolean(w,-1,G,false)
elseif HaveSavedBoolean(w,-1,G) then
if  not LoadBoolean(w,-1,G) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,G,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,G,true)
endif
else
call SaveInteger(w,-1,G,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
endif
call I2S(1/0)
endif
endif
call SaveInteger(w,T,Y,R)
set R=R+1
endloop
set Y=u
set T=StringHash("A")
if O[Y]==0 then
if Y==0 then
set R=StringHash("Nullpointer exception when calling Table.saveInt")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
endif
call I2S(1/0)
else
set R=StringHash("Called Table.saveInt on invalid object.")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
endif
call I2S(1/0)
endif
endif
call SaveInteger(w,Y,T,10)
set Y=u
set T=StringHash("B")
if O[Y]==0 then
if Y==0 then
set R=StringHash("Nullpointer exception when calling Table.saveInt")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
endif
call I2S(1/0)
else
set R=StringHash("Called Table.saveInt on invalid object.")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
endif
call I2S(1/0)
endif
endif
call SaveInteger(w,Y,T,11)
set Y=u
set T=StringHash("C")
if O[Y]==0 then
if Y==0 then
set R=StringHash("Nullpointer exception when calling Table.saveInt")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
endif
call I2S(1/0)
else
set R=StringHash("Called Table.saveInt on invalid object.")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
endif
call I2S(1/0)
endif
endif
call SaveInteger(w,Y,T,12)
set Y=u
set T=StringHash("D")
if O[Y]==0 then
if Y==0 then
set R=StringHash("Nullpointer exception when calling Table.saveInt")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
endif
call I2S(1/0)
else
set R=StringHash("Called Table.saveInt on invalid object.")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
endif
call I2S(1/0)
endif
endif
call SaveInteger(w,Y,T,13)
set T=u
set Y=StringHash("E")
if O[T]==0 then
if T==0 then
set R=StringHash("Nullpointer exception when calling Table.saveInt")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
endif
call I2S(1/0)
else
set R=StringHash("Called Table.saveInt on invalid object.")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
endif
call I2S(1/0)
endif
endif
call SaveInteger(w,T,Y,14)
set Y=u
set T=StringHash("F")
if O[Y]==0 then
if Y==0 then
set R=StringHash("Nullpointer exception when calling Table.saveInt")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Nullpointer exception when calling Table.saveInt"+"")
endif
call I2S(1/0)
else
set R=StringHash("Called Table.saveInt on invalid object.")
if HaveSavedInteger(w,-1,R) then
if LoadInteger(w,-1,R)+60<i then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
call SaveInteger(w,-1,R,R2I(i))
call SaveBoolean(w,-1,R,false)
elseif HaveSavedBoolean(w,-1,R) then
if  not LoadBoolean(w,-1,R) then
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Excessive repeating errors are being omitted")
call SaveBoolean(w,-1,R,true)
endif
else
call SaveInteger(w,-1,R,R2I(i))
call DisplayTimedTextToPlayer(GetLocalPlayer(),0.,0.,45.,"|cffFF3A29Error:|r "+"Called Table.saveInt on invalid object."+"")
endif
call I2S(1/0)
endif
endif
call SaveInteger(w,Y,T,15)
if l==0 then
set b=b+1
else
set l=l-1
endif
if l==0 then
set b=b+1
else
set l=l-1
endif
if l==0 then
set b=b+1
else
set l=l-1
endif
if l==0 then
set b=b+1
else
set l=l-1
endif
if l==0 then
set b=b+1
else
set l=l-1
endif
call j()
call CreateGroup()
call Location(0.,0.)
set r=null
call Rect(0.,0.,128.,128.)
set r=CreateItem(2003790951,0.,0.)
call SetItemVisible(r,false)
call j()
call j()
set f=CreateTrigger()
call TriggerRegisterTimerEvent(f,0.1,false)
call TriggerAddAction(f,e)
set n=null
set d=null
set f=null
endfunction
function config takes nothing returns nothing
local integer g
local integer h
local gametype F
call SetMapName("Just another Warcraft III map")
call SetMapDescription("Nondescript")
call SetPlayers(1)
call SetTeams(1)
call SetGamePlacement(MAP_PLACEMENT_USE_MAP_SETTINGS)
call DefineStartLocation(0,-2048.0,1856.0)
call SetPlayerStartLocation(Player(0),0)
call SetPlayerColor(Player(0),ConvertPlayerColor(0))
call SetPlayerRacePreference(Player(0),RACE_PREF_HUMAN)
call SetPlayerRaceSelectable(Player(0),true)
call SetPlayerController(Player(0),MAP_CONTROL_USER)
set g=GetPlayerId(Player(0))
if  not bj_slotControlReady then
set h=0
loop
set bj_slotControlUsed[h]=false
set h=h+1
exitwhen h==bj_MAX_PLAYERS
endloop
set bj_slotControlReady=true
endif
set bj_slotControlUsed[g]=true
set F=GetGameTypeSelected()
if F==GAME_TYPE_MELEE then
call TeamInitPlayerSlots(bj_MAX_PLAYERS)
elseif F==GAME_TYPE_FFA then
call TeamInitPlayerSlots(bj_MAX_PLAYERS)
elseif F==GAME_TYPE_USE_MAP_SETTINGS then
elseif F==GAME_TYPE_ONE_ON_ONE then
call SetTeams(2)
call SetPlayers(2)
call TeamInitPlayerSlots(2)
elseif F==GAME_TYPE_TWO_TEAM_PLAY then
call TeamInitPlayerSlots(2)
elseif F==GAME_TYPE_THREE_TEAM_PLAY then
call TeamInitPlayerSlots(3)
elseif F==GAME_TYPE_FOUR_TEAM_PLAY then
call TeamInitPlayerSlots(4)
endif
set F=null
endfunction
 

Attachments

  • test.w3x
    20.9 KB · Views: 77
Level 21
Joined
Aug 3, 2004
Messages
710
Oh, I just noticed on your screenshot that you don't have any plugins in the plugin directory. To make natives, you need WarAPI and JassAPI from the second post. Although I have no idea how what you have is kinda working, since it does show the message about adding the native. :p I'll need to check that out later.
 
Level 4
Joined
May 25, 2009
Messages
100
Oh, I just noticed on your screenshot that you don't have any plugins in the plugin directory. To make natives, you need WarAPI and JassAPI from the second post. Although I have no idea how what you have is kinda working, since it does show the message about adding the native. :p I'll need to check that out later.

Ok it's working now! I got an error when putting them into the plugins-folder, so i thought they belong into the sharpcraft-folder. Fact is: I forgot to unblock the .zip file...

Edit: Since it's working now, I would really like the sourcefiles and a Doc :D
 
This tool adds new natives, right? So a list of all natives with some documentation would be useful. Is this simply jAPI/ Reinventing the Craft? At the moment I have no idea how to use this and what it adds to Warcraft.

It is a tool that you can use to add code your own natives. But iirc there are already a couple of useful samples that come with the installation.
 
Level 21
Joined
Aug 3, 2004
Messages
710
This tool adds new natives, right? So a list of all natives with some documentation would be useful. Is this simply jAPI/ Reinventing the Craft? At the moment I have no idea how to use this and what it adds to Warcraft.

SharpCraft allows modding Warcraft III, by injecting assemblies at the right time in the right dependency sorted order, essentially making it a glorified DLL loader. The plugins on the second post gives you some more functionality, in particular JassAPI will allow you to add new natives. I'll get a tutorial on how to make your own plugin and how to add natives done today.
 
Last edited:
Level 21
Joined
Aug 3, 2004
Messages
710
By the way... if we make a map, using our own natives and stuff, will "normal" players, which haven't touched the Editor ever before, be able to play the map (without having to download extra stuff)?

In all likelyhood all players will need to have SharpCraft as well. It is possible to do some things in a way so only one player needs SharpCraft, but this is a more advanced topic. To keep things simple, just consider SharpCraft a total conversion.
 
Level 9
Joined
Jul 30, 2012
Messages
156
MindWorX, I have a question about SharpCraft: do you provide any standard way to communicate with the game from the C# code? I mean, is there anything available that allows me to read/write values or call functions from game.dll? After all, what is the point of creating new natives if they can't interact with the game?

Suppose I want to create aGetUnitArmor native. This is how I could make it directly inside game.dll:
attachment.php
And this is how I can make it with pure JASS, by using the [thread=276196]memory exploit[/thread]:
JASS:
function ConvertHandle takes handle h returns integer
    return Memory[Memory[Memory[Memory[GameState]/4+7]/4+103]/4 + GetHandleId(h)*3 - 0x2FFFFF]
endfunction

function GetUnitArmor takes unit u returns real
    return indexToReal(Memory[ConvertHandle(u)/4+56])
endfunction
So my question is, how could I implement such a native using SharpCraft? I think it would be useful if SharpCraft could expose some utility functions from game.dll (like ConvertHandle for example, which takes a handle from the JASS world and returns the memory address of the corresponding object). Is it possible?
 

Attachments

  • GetUnitArmor.png
    GetUnitArmor.png
    77.5 KB · Views: 330
Level 21
Joined
Aug 3, 2004
Messages
710
MindWorX, I have a question about SharpCraft: do you provide any standard way to communicate with the game from the C# code? I mean, is there anything available that allows me to read/write values or call functions from game.dll? After all, what is the point of creating new natives if they can't interact with the game?

Suppose I want to create aGetUnitArmor native. This is how I could make it directly inside game.dll:
attachment.php
And this is how I can make it with pure JASS, by using the [thread=276196]memory exploit[/thread]:
JASS:
function ConvertHandle takes handle h returns integer
    return Memory[Memory[Memory[Memory[GameState]/4+7]/4+103]/4 + GetHandleId(h)*3 - 0x2FFFFF]
endfunction

function GetUnitArmor takes unit u returns real
    return indexToReal(Memory[ConvertHandle(u)/4+56])
endfunction
So my question is, how could I implement such a native using SharpCraft? I think it would be useful if SharpCraft could expose some utility functions from game.dll (like ConvertHandle for example, which takes a handle from the JASS world and returns the memory address of the corresponding object). Is it possible?

I provide a static utility class called Memory which allows you to read and write anything and also inject function hooks. There is also a method on the JassUnit class, which returns a pointer to the unmanaged CUnit class, kinda like your ConvertHandle suggestion, which has all the unit instance data, like attacks, abilities, etc. A few posts back, I have an example of how to change abilities at run-time and even make them unique for each unit, with different values, even if they're based on the same base ability.

EDIT:
I just checked with my own code, and defense appears to be fieldoffset 224/0xE0 on the unit. When I update, it's just a matter of reading/writing the field somewhat like this:
Code:
var unit = unitHandle.ToCUnit();
unit->Defense = 1000; // changes the unit defense to 1000, but only for this particular instance.
 
Last edited:
Level 9
Joined
Jul 30, 2012
Messages
156
I provide a static utility class called Memory which allows you to read and write anything and also inject function hooks. There is also a method on the JassUnit class, which returns a pointer to the unmanaged CUnit class, kinda like your ConvertHandle suggestion, which has all the unit instance data, like attacks, abilities, etc. A few posts back, I have an example of how to change abilities at run-time and even make them unique for each unit, with different values, even if they're based on the same base ability.

EDIT:
I just checked with my own code, and defense appears to be fieldoffset 224/0xE0 on the unit. When I update, it's just a matter of reading/writing the field somewhat like this:
Code:
var unit = unitHandle.ToCUnit();
unit->Defense = 1000; // changes the unit defense to 1000, but only for this particular instance.

Where do I find the definition of JassUnit class in your code? I didn't find it anywhere in your github tree. Did you actually make it public?
 
Status
Not open for further replies.
Top