Jump to content


  • Content Count

  • Joined

  • Last visited

  • Days Won


Everything posted by DavidRM

  1. This... procedure PXTeamCreate(aTeam: TTeamStruct; aCallback: TPXMessageCallback); begin var aData: Variant := new JObject; aData.team := aTeam.Save; PXSend(PXM_TEAMCREATE, aData, aCallback); end; Compiles to this: function PXTeamCreate(aTeam$4, aCallback$4) { var aData$5; aData$5 = {}; aData$5.team = {}; PXSend(122,aData$5,aCallback$4); }; All I can figure is that TTeamStruct.Save returns a *record* type, and somehow the poor result is being scoped out of existence before it even gets a chance. function TTeamStruct.Save: TTeamXfer; begin Result.TeamID := TeamID; Result.Name := Name; Result.Desc := Desc; Result.Web := Web; Result.Bonus := Bonus; Result.Shots := Shots; Result.Splats := Splats; Result.BonusCurrent := BonusCurrent; Result.ShotsCurrent := ShotsCurrent; Result.SplatsCurrent := SplatsCurrent; end; TTeamXfer = record TeamID: integer; external 'teamID'; Name: string; external 'name'; Desc: string; external 'desc'; Web: string; external 'web'; Bonus: integer; external 'bonus'; Shots: integer; external 'shots'; Splats: integer; external 'splats'; BonusCurrent: integer; external 'bonusCurrent'; ShotsCurrent: integer; external 'shotsCurrent'; SplatsCurrent: integer; external 'splatsCurrent'; end; Here's my current workaround: procedure PXTeamCreate(aTeam: TTeamStruct; aCallback: TPXMessageCallback); begin var aData: Variant := new JObject; var aTeamXfer := aTeam.Save; asm (@aData).team = @aTeamXfer; end; PXSend(PXM_TEAMCREATE, aData, aCallback); end;
  2. Back in the mid-90's, my brother created what he called an "action MUD". He called it "Paintball Net". PBN used ANSI text terminal commands to create a combined roll-and-scroll and animated text experience. ! (exclamation points) were trees, _ (underscores) were grass, ^ (up carets) were mountains, and more. Your "avatar" was a "Y" (uppercase y) and enemies were * (asterisks). You used MUD-like commands to move around the world and target your paintball gun and buy and sell and more. I wish I had a screenshot of what the first version looked like, but you'll just have to imagine. 😃 He asked me to create the GUI for the game. That was 1996, and I had just purchased Delphi 2 and wrapped up my first "learn Delphi" project. This sounded like a good next step, and "PBTerm" was born. I *do* have screenshots of that. Oh, yeah. Love those 16-color Windows graphics. 😃 During the years it was online, the game evolved and upgraded to look more like this: We were never going to win any awards, but we had a very devoted player community. 😃 Paintball Net was online from the summer of 1996 through the summer of 2000, when we took it offline to focus on other projects. The game was never huge, but we had thousands of players come through over that period. And since then, every year at least a few of the players have reached out and asked/begged/pleaded/demanded if we were going to put it back online. There really wasn't much chance of the original version going back online. The original server, written in ANSI C for Linux had proven rather fragile, and was a big part of why we took the game offline. It was taking hours every day just to keep it up and running, in addition to time spent managing the community of players. On top of that, a hard drive incident in 2008 had cost me the source code of a number of core third-party/modified components for PBTerm. This past summer, though, I realized I might be able to make the game live again using Smart Mobile Studio. I would do a straight port of the ANSI C server to Smart Pascal using Node.JS and WebSocket, and I would rebuild the PBTerm client as a browser-based client. I'm not going to say it was *easy*, but it has been a lot of fun. ANSI C converts to Pascal without a lot of painful gyrations. And Node.JS seems a LOT more flexible, stable, and powerful than the TCP sockets approach we were using before. Also, game development is a lot easier when you already own all the graphic and audio resources. 😃 This is the server "in action"... Recreating the client has been more complicated. 20+ years of Delphi habits had to be adapted to the new reality of SMS and a browser-based UI. But that's coming together too. It might be obvious, but I'm not targeting this game at mobile. Paintball Net needed a mouse and keyboard in 1996, it's gonna need a mouse and keyboard in 2018. Also, I've made as few modifications to the gameplay as possible. I really wanted to bring back the original as much as I could. Today I got the handful of sound effects integrated, which was easier than I expected. I'm planning to start testing soon. I just need to line up a server to use and find a few volunteers. I'm excited. This would never have happened without Smart Mobile Studio. So I figured I would share. Merry Xmas! -David
  3. I find myself doing this a lot: type TTeamStruct = class (TObject) TeamID: integer; Name, Desc, Web: string; Bonus, Shots, Splats, BonusCurrent, ShotsCurrent, SplatsCurrent: integer; procedure FromJSON(json: string); function ToJSON: string; procedure FromJObject(aObj: Variant); function AsJObject: Variant; end; procedure TTeamStruct.FromJSON(json: string); var aObj: Variant; begin asm @aObj = JSON.parse(@json); end; FromJObject(aObj); end; function TTeamStruct.ToJSON: string; var aObj: Variant; begin aObj := Self.AsJObject; asm @Result = JSON.stringify(@aObj); end; end; procedure TTeamStruct.FromJObject(aObj: Variant); begin TeamID := aObj.teamID; Name := aObj.name; Desc := aObj.desc; Web := aObj.web; Bonus := aObj.bonus; Shots := aObj.shots; Splats := aObj.splats; BonusCurrent := aObj.bonusCurrent; ShotsCurrent := aObj.shotsCurrent; SplatsCurrent := aObj.splatsCurrent; end; function TTeamStruct.AsJObject: Variant; begin Result := new JObject; Result.teamID := TeamID; Result.name := Name; Result.desc := Desc; Result.web := Web; Result.bonus := Bonus; Result.shots := Shots; Result.splats := Splats; Result.bonusCurrent := BonusCurrent; Result.shotsCurrent := ShotsCurrent; Result.splatsCurrent := SplatsCurrent; end; I do this to simplify the Json storage, especially for transmitting between client and server. If I don't do this, I end up with a lot of SMS "artifacts" in my Json because SMS makes sure all field names are unique (which I totally get). Is there some trick I can use to *not* have to write this code for pretty much every object flying back and forth? Thanks! -David
  4. That did what I wanted. Thanks! -David
  5. That can work in simple projects, but as soon as you have to 2 record types with the same field name, the Object Pascal field name no longer matches the JSON field name. For example, "CategoryID" would be emitted as "CategoryID" (a perfect match) for one record type, but "CategoryID$1" (not so perfect) for another. -David
  6. DavidRM

    lamda doesn't highlight its end

    Clicking on keyword "lambda" doesn't highlight its "end". In the same vein, cilcking on "end" for a lambda, highlights a totally wrong "begin". -David
  7. DavidRM

    Declaring Record Type Constants

    Why won't this work? type TMySqlCreateTable = record TableName: string; CreateFunc: function: JPromise; end; const _TableList: array of TMySqlCreateTable = [ (TableName: 'Server'; CreateFunc: @CreateServer) ]; This would be valid in Delphi, and the SMS parser even recognizes the validity of "(TableName: 'Server'; CreateFunc: @CreateServer)", but gives the error "constant expression expected". But it *IS* a constant expression. Thanks. -David
  8. DavidRM

    Declaring Record Type Constants

    I want *less* stuff to type, not more. BUt thanks for the options. 😃 -David
  9. DavidRM

    Declaring Record Type Constants

    Here's my solution: type TMySqlCreateTable = class TableName: string; CreateFunc: function: JPromise; constructor Create(const aTableName: string; aCreateFunc: function: JPromise); begin TableName := aTableName; CreateFunc := aCreateFunc; end; end; var _TableList: array of TMySqlCreateTable; procedure CreateTableList; begin _TableList.Clear; _TableList.Add(TMySqlCreateTable.Create('Server', @CreateServerTable)); _TableList.Add(TMySqlCreateTable.Create('GameData', @CreateGameDataTable)); _TableList.Add(TMySqlCreateTable.Create('Team', @CreateTeamTable)); _TableList.Add(TMySqlCreateTable.Create('Player', @CreatePlayerTable)); _TableList.Add(TMySqlCreateTable.Create('PlayerBody', @CreatePlayerBodyTable)); _TableList.Add(TMySqlCreateTable.Create('PlayerLocker', @CreatePlayerLockerTable)); _TableList.Add(TMySqlCreateTable.Create('PlayerAliases', @CreatePlayerAliasesTable)); _TableList.Add(TMySqlCreateTable.Create('PlayerIgnores', @CreatePlayerIgnoresTable)); _TableList.Add(TMySqlCreateTable.Create('PlayerButtons', @CreatePlayerButtonsTable)); _TableList.Add(TMySqlCreateTable.Create('PlayerNotes', @CreatePlayerNotesTable)); _TableList.Add(TMySqlCreateTable.Create('PlayerUIOptions', @CreatePlayerUIOptionsTable)); _TableList.Add(TMySqlCreateTable.Create('Purchases', @CreatePurchasesTable)); _TableList.Add(TMySqlCreateTable.Create('Rankings', @CreateRankingsTable)); end; Turn it into a class, declare an array variable, write a function to fill it up. I would rather just declare it as a constant. -David
  10. DavidRM

    Enable User Selection of Text

    THis is what I have: type TPBTScrollItem = class(TW3CustomControl) protected FItemText: string; procedure InitializeItem; virtual; procedure SetItemText(aValue: string); public function CreationFlags: TW3CreationFlags; override; procedure UpdateDisplay; virtual; property ItemText: string read FItemText write SetItemText; end; // TPBTScrollItem procedure TPBTScrollItem.InitializeItem; begin SetContentSelectionMode(tsmAuto); end; procedure TPBTScrollItem.SetItemText(aValue: string); begin if aValue <> FItemText then begin FItemText := aValue; UpdateDisplay; end; end; function TPBTScrollItem.CreationFlags: TW3CreationFlags; begin inherited; Include(Result, cfAllowSelection); end; procedure TPBTScrollItem.UpdateDisplay; begin InnerHTML := Format('<span>%s</span>', [TString.EncodeTags(FItemText)]); end; But the user can never actually click-and-drag to select the content. Why? And: How do I get around whatever silly reason is the "Why?" Thanks! -David
  11. DavidRM

    Enable User Selection of Text

    @jarto That did it, yup. Thanks! And, yes, I had to use tmsText. Also, I was able to get the span to work by doing this: InnerHTML := Format('<span style="color:%s;user-select:text;-moz-user-select:text;-webkit-user-select:text;-ms-user-select:text;-khtml-user-select:text"><b>%s</b>: %s</span>', [ColorToWebStr(FChatColor), TString.EncodeTags(FChatHeader), TString.EncodeTags(FChatText)]); Not sure why the <span> didn't just inherit the item's user-select, though. Thanks! -David
  12. DavidRM

    NodeJS file utils and native app

    Here's my example: Files := TW3NodeStorageDevice.Create(nil); Files.Mount(nil, procedure (Sender: TW3StorageDevice; Success: boolean) begin if Success then Writeln(PBNRS_FILE_SYSTEM_MOUNTED) else Writeln(Format(PBNRS_ERROR_MOUNTING_FILE_SYSTEM, [Files.LastError])); end ); And reading a file: Files.Load(PBNConfig.HelpFile, procedure (Sender: TW3StorageDevice; Filename: string; Data: TStream; Success: boolean) begin if Success then begin ... end else ... end
  13. DavidRM

    Node.JS Deprecation Warning

    I get this notification from Node.JS (10.15.0) sometimes: [DEP0005] DeprecationWarning: Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead. -David
  14. DavidRM

    Node.JS Deprecation Warning

    @IElite Yes, I did. But I don't use that directly. I'm guessing it's in the SMS RTL for NodeJS, so I wanted to pass it along. -David
  15. DavidRM

    My SMS Project: Paintball Net Revival

    I wondered if any old players would find this page. 😃 Testing is now underway. When I have a few more the bugs shaken out [*], I'll post the link here. -David [*] You post 10's of thousands of lines of ANSI C to Object Pascal and see if you don't have some typos...
  16. DavidRM

    Packing Observation

    Not really a *bug*...but it looks like case statements get compiled to if-then-else statements that aren't "packed". That is, each case statement condition is kept on a single line, not combined with the line ahead of it. Is that by design? -David
  17. DavidRM

    Packing Observation

    @jarto That does add a bit more obfuscation, yup yup. Thanks! 😃 -David
  18. DavidRM


    I've been using @warleyalex's promises for my projects. They work great. -David
  19. DavidRM

    Packing Observation

    I wonder if the not-packing is related to ASM blocks? I have no idea. I can scroll through my packed-and-obfuscated JS file and see blocks like this: I think that's from RTL code, not mine. This is from mine: I don't know that it makes a lot of difference, but there it is. -David
  20. DavidRM

    Packing Observation

    I'll poke at it some more. I just noticed because I was packing and obfuscating for deployment.
  21. With obfuscation active, variable names within an asm block aren't always being replaced with their obfuscated versions. This: var aJsonArray: string; asm @aJsonArray = JSON.stringify(@aArray); end; Is becoming this: MbN = JSON.stringify(ocs); But this: var aJsonArray: string := Storage.GetKeyStr('SavedLogins', ''); if aJsonArray <> '' then begin var aArray: Variant := TVariant.CreateArray; asm @aArray = JSON.parse(aJsonArray); end; is becoming this: ZZK = rpZ.yYO(wH7,"SavedLogins",""); if (ZZK!="") { laI = zb.Vc(); laI = JSON.parse(aJsonArray); So...it usually works? Just not always? For some reason? Could it be related to the scope of the variable? Thanks! -David PS
  22. DavidRM

    Obfuscation Issue with Asm Block

    Oops. I forgot an @. That's why there was an issue. -David
  23. DavidRM

    Browser Autofill

    Do I need to do anything special on a form to get Chrome (or any browser) to autofill the edits? Like for a login form, autofilling the login name and password? Thanks! -David
  24. DavidRM

    Browser Autofill

    OK. So the autofill relies on forms? I guess I get to provide that myself, using local storage. Thanks! -David
  25. I'm not sure if this is a bug report (The built-in browser doesn't play audio elements.), a request for help (How do I get the built-in browser to play audio elements?), or a suggestion (Hey! It'd be great if the built-in browser would play audio elements.) 😃 -David