Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

655 lines
13 KiB

  1. //=== ======= Copyright � 2008, Valve Corporation, All rights reserved. ========
  2. //
  3. // Purpose: Script initially run after squirrel VM is initialized
  4. //
  5. //=============================================================================
  6. //-----------------------------------------------------------------------------
  7. // General
  8. //-----------------------------------------------------------------------------
  9. function printl( text )
  10. {
  11. return print( text + "\n" );
  12. }
  13. function Msg( text )
  14. {
  15. return print( text );
  16. }
  17. function Assert( b, msg = null )
  18. {
  19. if ( b )
  20. return;
  21. if ( msg != null )
  22. {
  23. throw "Assertion failed: " + msg;
  24. }
  25. else
  26. {
  27. throw "Assertion failed";
  28. }
  29. }
  30. //-----------------------------------------------------------------------------
  31. // Documentation table
  32. //-----------------------------------------------------------------------------
  33. if ( developer() > 0 )
  34. {
  35. Documentation <-
  36. {
  37. classes = {}
  38. functions = {}
  39. instances = {}
  40. }
  41. function RetrieveNativeSignature( nativeFunction )
  42. {
  43. if ( nativeFunction in NativeFunctionSignatures )
  44. {
  45. return NativeFunctionSignatures[nativeFunction]
  46. }
  47. return "<unnamed>"
  48. }
  49. function RegisterFunctionDocumentation( func, name, signature, description )
  50. {
  51. if ( description.len() )
  52. {
  53. local b = ( description[0] == '#' );
  54. if ( description[0] == '#' )
  55. {
  56. local colon = description.find( ":" );
  57. if ( colon == null )
  58. {
  59. colon = description.len();
  60. }
  61. local alias = description.slice( 1, colon );
  62. description = description.slice( colon + 1 );
  63. name = alias;
  64. signature = "#";
  65. }
  66. }
  67. Documentation.functions[name] <- [ signature, description ]
  68. }
  69. function Document( symbolOrTable, itemIfSymbol = null, descriptionIfSymbol = null )
  70. {
  71. if ( typeof( symbolOrTable ) == "table" )
  72. {
  73. foreach( symbol, itemDescription in symbolOrTable )
  74. {
  75. Assert( typeof(symbol) == "string" )
  76. Document( symbol, itemDescription[0], itemDescription[1] );
  77. }
  78. }
  79. else
  80. {
  81. printl( symbolOrTable + ":" + itemIfSymbol.tostring() + "/" + descriptionIfSymbol );
  82. }
  83. }
  84. function PrintHelp( string = "*", exact = false )
  85. {
  86. local matches = []
  87. if ( string == "*" || !exact )
  88. {
  89. foreach( name, documentation in Documentation.functions )
  90. {
  91. if ( string != "*" && name.tolower().find( string.tolower() ) == null )
  92. {
  93. continue;
  94. }
  95. matches.append( name );
  96. }
  97. }
  98. else if ( exact )
  99. {
  100. if ( string in Documentation.functions )
  101. matches.append( string )
  102. }
  103. if ( matches.len() == 0 )
  104. {
  105. printl( "Symbol " + string + " not found" );
  106. return;
  107. }
  108. matches.sort();
  109. foreach( name in matches )
  110. {
  111. local result = name;
  112. local documentation = Documentation.functions[name];
  113. printl( "Function: " + name );
  114. local signature;
  115. if ( documentation[0] != "#" )
  116. {
  117. signature = documentation[0];
  118. }
  119. else
  120. {
  121. signature = GetFunctionSignature( this[name], name );
  122. }
  123. printl( "Signature: " + signature );
  124. if ( documentation[1].len() )
  125. printl( "Description: " + documentation[1] );
  126. print( "\n" );
  127. }
  128. }
  129. }
  130. else
  131. {
  132. function RetrieveNativeSignature( nativeFunction ) { return "<unnamed>"; }
  133. function Document( symbolOrTable, itemIfSymbol = null, descriptionIfSymbol = null ) {}
  134. function PrintHelp( string = "*", exact = false ) {}
  135. }
  136. //-----------------------------------------------------------------------------
  137. // VSquirrel support functions
  138. //-----------------------------------------------------------------------------
  139. function VSquirrel_OnCreateScope( name, outer )
  140. {
  141. local result;
  142. if ( !(name in outer) )
  143. {
  144. result = outer[name] <- { __vname=name, __vrefs = 1 };
  145. delegate outer : result;
  146. }
  147. else
  148. {
  149. result = outer[name];
  150. result.__vrefs += 1;
  151. }
  152. return result;
  153. }
  154. function VSquirrel_OnReleaseScope( scope )
  155. {
  156. scope.__vrefs -= 1;
  157. if ( scope.__vrefs < 0 )
  158. {
  159. throw "Bad reference counting on scope " + scope.__vname;
  160. }
  161. else if ( scope.__vrefs == 0 )
  162. {
  163. delete scope.parent[scope.__vname];
  164. scope.__vname = null;
  165. delegate null : scope;
  166. }
  167. }
  168. //-----------------------------------------------------------------------------
  169. //
  170. //-----------------------------------------------------------------------------
  171. class CCallChainer
  172. {
  173. constructor( prefixString, scopeForThis = null )
  174. {
  175. prefix = prefixString;
  176. if ( scopeForThis != null )
  177. scope = scopeForThis;
  178. else
  179. scope = ::getroottable();
  180. chains = {};
  181. // Expose a bound global function to dispatch to this object
  182. scope[ "Dispatch" + prefixString ] <- Call.bindenv( this );
  183. }
  184. function PostScriptExecute()
  185. {
  186. foreach( key, value in scope )
  187. {
  188. if ( typeof( value ) == "function" )
  189. {
  190. if ( key.find( prefix ) == 0 )
  191. {
  192. key = key.slice( prefix.len() );
  193. if ( !(key in chains) )
  194. {
  195. //::print( "Creating new call chain " + key + "\n");
  196. chains[key] <- [];
  197. }
  198. local chain = chains[key];
  199. if ( !chain.len() || chain.top() != value )
  200. {
  201. chain.push( value );
  202. //::print( "Added " + value + " to call chain " + key + "\n" );
  203. }
  204. }
  205. }
  206. }
  207. }
  208. function Call( event, ... )
  209. {
  210. if ( event in chains )
  211. {
  212. local chain = chains[event];
  213. if ( chain.len() )
  214. {
  215. local i;
  216. local args = [];
  217. if ( vargc > 0 )
  218. {
  219. args.push( scope );
  220. for ( i = 0; i < vargc; i++ )
  221. {
  222. args.push( vargv[i] );
  223. }
  224. }
  225. for ( i = chain.len() - 1; i >= 0; i -= 1 )
  226. {
  227. local func = chain[i];
  228. local result;
  229. if ( !args.len() )
  230. {
  231. result = func();
  232. }
  233. else
  234. {
  235. result = func.acall( args );
  236. }
  237. if ( result != null && !result )
  238. return false;
  239. }
  240. }
  241. }
  242. return true;
  243. }
  244. scope = null;
  245. prefix = null;
  246. chains = null;
  247. };
  248. //-----------------------------------------------------------------------------
  249. //
  250. //-----------------------------------------------------------------------------
  251. class CSimpleCallChainer
  252. {
  253. constructor( prefixString, scopeForThis = null, exactNameMatch = false )
  254. {
  255. prefix = prefixString;
  256. if ( scopeForThis != null )
  257. scope = scopeForThis;
  258. else
  259. scope = ::getroottable();
  260. chain = [];
  261. // Expose a bound global function to dispatch to this object
  262. scope[ "Dispatch" + prefixString ] <- Call.bindenv( this );
  263. exactMatch = exactNameMatch
  264. }
  265. function PostScriptExecute()
  266. {
  267. foreach( key, value in scope )
  268. {
  269. if ( typeof( value ) == "function" )
  270. {
  271. local foundMatch = false;
  272. if ( exactMatch )
  273. {
  274. foundMatch = ( prefix == key );
  275. }
  276. else
  277. {
  278. foundMatch = ( key.find( prefix ) == 0 )
  279. }
  280. if ( foundMatch )
  281. {
  282. if ( !exactMatch )
  283. key = key.slice( prefix.len() );
  284. if ( !(chain) )
  285. {
  286. //::print( "Creating new call simple chain\n");
  287. chain <- [];
  288. }
  289. if ( !chain.len() || chain != value )
  290. {
  291. chain.push( value );
  292. //::print( "Added " + value + " to call chain.\n" );
  293. }
  294. }
  295. }
  296. }
  297. }
  298. function Call( ... )
  299. {
  300. if ( chain.len() )
  301. {
  302. local i;
  303. local args = [];
  304. if ( vargc > 0 )
  305. {
  306. args.push( scope );
  307. for ( i = 0; i < vargc; i++ )
  308. {
  309. args.push( vargv[i] );
  310. }
  311. }
  312. for ( i = chain.len() - 1; i >= 0; i -= 1 )
  313. {
  314. local func = chain[i];
  315. local result;
  316. if ( !args.len() )
  317. {
  318. result = func.pcall( scope );
  319. }
  320. else
  321. {
  322. result = func.pacall( scope, args );
  323. }
  324. if ( result != null && !result )
  325. return false;
  326. }
  327. }
  328. return true;
  329. }
  330. exactMatch = false
  331. scope = null;
  332. prefix = null;
  333. chain = null;
  334. };
  335. //-----------------------------------------------------------------------------
  336. // Late binding: allows a table to refer to parts of itself, it's children,
  337. // it's owner, and then have the references fixed up after it's fully parsed
  338. //
  339. // Usage:
  340. // lateBinder <- LateBinder();
  341. // lateBinder.Begin( this );
  342. //
  343. // Test1 <-
  344. // {
  345. // Foo=1
  346. // }
  347. //
  348. // Test2 <-
  349. // {
  350. // FooFoo = "I'm foo foo"
  351. // BarBar="@Test1.Foo"
  352. // SubTable = { boo=[bah, "@Test2.FooFoo", "@Test1.Foo"], booboo2={one=bah, two="@Test2.FooFoo", three="@Test1.Foo"} }
  353. // booboo=[bah, "@Test2.FooFoo", "@Test1.Foo"]
  354. // booboo2={one=bah, two="@Test2.FooFoo", three="@Test1.Foo"}
  355. // bah=wha
  356. // }
  357. //
  358. // lateBinder.End();
  359. // delete lateBinder;
  360. //
  361. // When End() is called, all of the unresolved symbols in the tables and arrays will be resolved,
  362. // any left unresolved will become a string prepended with '~', which later code can deal with
  363. //-----------------------------------------------------------------------------
  364. class LateBinder
  365. {
  366. // public:
  367. function Begin( target, log = false )
  368. {
  369. m_log = log;
  370. HookRootMetamethod( "_get", function( key ) { return "^" + key; } );
  371. HookRootMetamethod( "_newslot", function( key, value ) { if ( typeof value == "table" ) { m_fixupSet.push( [ key, value ] ); this.rawset( key, value ); }; }.bindenv(this) );
  372. m_targetTable = target;
  373. Log( "Begin late bind on table " + m_targetTable );
  374. }
  375. function End()
  376. {
  377. UnhookRootMetamethod( "_get" );
  378. UnhookRootMetamethod( "_newslot" );
  379. Log( "End late bind on table " + m_targetTable );
  380. foreach( subTablePair in m_fixupSet )
  381. {
  382. EstablishDelegation( m_targetTable, subTablePair[1] );
  383. }
  384. Log( "Begin resolution... " )
  385. m_logIndent++;
  386. local found = true;
  387. while ( found )
  388. {
  389. foreach( subTablePair in m_fixupSet )
  390. {
  391. Log( subTablePair[0] + " = " );
  392. Log( "{" );
  393. if ( !Resolve( subTablePair[1], subTablePair[1], false ) )
  394. {
  395. found = false;
  396. }
  397. Log( "}" );
  398. }
  399. }
  400. m_logIndent--;
  401. foreach( subTablePair in m_fixupSet )
  402. {
  403. RemoveDelegation( subTablePair[1] );
  404. }
  405. Log( "...end resolution" );
  406. }
  407. // private:
  408. function HookRootMetamethod( name, value )
  409. {
  410. local saved = null;
  411. local roottable = getroottable();
  412. if ( name in roottable )
  413. {
  414. saved = roottable[name];
  415. }
  416. roottable[name] <- value;
  417. roottable["__saved" + name] <- saved;
  418. }
  419. function UnhookRootMetamethod( name )
  420. {
  421. local saveSlot = "__saved" + name;
  422. local roottable = getroottable();
  423. local saved = roottable[saveSlot];
  424. if ( saved != null )
  425. {
  426. roottable[name] = saved;
  427. }
  428. else
  429. {
  430. delete roottable[name];
  431. }
  432. delete roottable[saveSlot];
  433. }
  434. function EstablishDelegation( parentTable, childTable )
  435. {
  436. delegate parentTable : childTable;
  437. foreach( key, value in childTable )
  438. {
  439. local type = typeof value;
  440. if ( type == "table" )
  441. {
  442. EstablishDelegation( childTable, value );
  443. }
  444. }
  445. }
  446. function RemoveDelegation( childTable )
  447. {
  448. delegate null : childTable;
  449. foreach( key, value in childTable )
  450. {
  451. local type = typeof value;
  452. if ( type == "table" )
  453. {
  454. RemoveDelegation( value );
  455. }
  456. }
  457. }
  458. function Resolve( lookupTable, subTableOrArray, throwException = false )
  459. {
  460. m_logIndent++;
  461. local found = false;
  462. foreach( key, value in subTableOrArray )
  463. {
  464. local type = typeof value;
  465. if ( type == "string" )
  466. {
  467. if ( value.len() )
  468. {
  469. local unresolvedId = null;
  470. local controlChar = value[0]
  471. if ( controlChar == '^' )
  472. {
  473. found = true;
  474. value = value.slice( 1 );
  475. if ( value in lookupTable )
  476. {
  477. subTableOrArray[key] = lookupTable[value];
  478. Log( key + " = " + lookupTable[value] + " <-- " + value );
  479. }
  480. else
  481. {
  482. subTableOrArray[key] = "~" + value;
  483. unresolvedId = value;
  484. Log( key + " = \"" + "~" + value + "\" (unresolved)" );
  485. }
  486. }
  487. else if ( controlChar == '@' )
  488. {
  489. found = true;
  490. local identifiers = [];
  491. local iLast = 1;
  492. local iNext;
  493. while ( ( iNext = value.find( ".", iLast ) ) != null )
  494. {
  495. identifiers.push( value.slice( iLast, iNext ) );
  496. iLast = iNext + 1;
  497. }
  498. identifiers.push( value.slice( iLast ) );
  499. local depthSuccess = 0;
  500. local result = lookupTable;
  501. foreach( identifier in identifiers )
  502. {
  503. if ( identifier in result )
  504. {
  505. depthSuccess++;
  506. result = result[identifier];
  507. }
  508. else
  509. {
  510. break;
  511. }
  512. }
  513. if ( depthSuccess == identifiers.len() )
  514. {
  515. subTableOrArray[key] = result;
  516. Log( key + " = " + result + " <-- " + value );
  517. }
  518. else
  519. {
  520. subTableOrArray[key] = "~" + value.slice( 1 );
  521. unresolvedId = value;
  522. Log( key + " = \"" + "~" + value + "\" (unresolved)" );
  523. }
  524. }
  525. if ( unresolvedId != null )
  526. {
  527. if ( throwException )
  528. {
  529. local exception = "Unresolved symbol: " + bind + " in ";
  530. foreach ( entry in m_bindNamesStack )
  531. {
  532. exception += entry;
  533. exception += "."
  534. }
  535. exception += unresolvedId;
  536. throw exception;
  537. }
  538. }
  539. }
  540. }
  541. }
  542. foreach( key, value in subTableOrArray )
  543. {
  544. local type = typeof value;
  545. local isTable = ( type == "table" );
  546. local isArray = ( type == "array" )
  547. if ( isTable || isArray )
  548. {
  549. Log( key + " =" );
  550. Log( isTable ? "{" : "[" );
  551. m_bindNamesStack.push( key );
  552. if ( Resolve( ( isTable ) ? value : lookupTable, value, throwException ) )
  553. {
  554. found = true;
  555. }
  556. m_bindNamesStack.pop();
  557. Log( isTable ? "}" : "]" );
  558. }
  559. }
  560. m_logIndent--;
  561. return found;
  562. }
  563. function Log( string )
  564. {
  565. if ( m_log )
  566. {
  567. for ( local i = 0; i < m_logIndent; i++ )
  568. {
  569. print( " " );
  570. }
  571. printl( string );
  572. }
  573. }
  574. m_targetTable = null;
  575. m_fixupSet = [];
  576. m_bindNamesStack = [];
  577. m_log = false;
  578. m_logIndent = 0;
  579. }