Search Unity

SimplePatchTool - open source patching solution for standalone platforms

Discussion in 'Assets and Asset Store' started by yasirkula, Jul 26, 2018.

  1. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    Thank you! Then I've good news for you: I've been working on an update for a while and it will be out very soon :)
     
    Shadowing, hopeful and zyzyx like this.
  2. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    The big update is finally out on GitHub!

    P.S. The update is now live on Asset Store, as well.

    Changelog

    - Introduced "Project"s to create patches much more easily
    - Added "Installer Patch" method (might be useful for launchers downloading a whole app)
    - Log file is now stored inside the directory application resides and stores more useful information (most of the previously unhandled exceptions are now logged to the log file)
    - Added GZIP compression method as a faster alternative to LZMA*
    - Added NONE compression method (applies no compression)*
    - Added SimplePatchTool.FetchOverallProgress
    - Added SimplePatchTool.CheckForMultipleRunningInstances to abort if multiple instances of the application are running
    - Added SimplePatchTool.IListener interface and its PatcherListener implementation to receive callbacks from SimplePatchTool
    - Added VersionInfoFetched callback to SimplePatchTool.IListener (i.e. it's now possible to modify the VersionInfo just before the patch starts)
    - Added PatchMethod property to SimplePatchTool to see which patch method is being applied
    - Added NewVersion property to SimplePatchTool to fetch new version's version number (e.g. 1.2.0)
    - Added PatchUtils.GetCurrentAppVersion to fetch app's current version
    - Added PatchUtils.GetDefaultSelfPatcherExecutablePath
    - Added Binary Diff Quality to produce smaller incremental patches (by trying different chunk sizes for each file)
    - Added ApplyingSelfPatch to SimplePatchTool.PatchOperation
    - SimplePatchTool automatically replaces {ROOT_PATH} and/or {APPLICATION_DIRECTORY} in rootPath argument with the application directory
    - SimplePatchTool no longer comes bundled with a self patcher executable on Windows, you need to build the self patcher executable yourself
    - Updated the wiki, check it out: https://github.com/yasirkula/SimplePatchTool/wiki/
    - Updated demo scenes
    - Removed SimplePatchTool.UseRepair, use UseRepairPatch instead
    - Numerous bugfixes

    *If you already have a published app that uses SimplePatchTool, keep using LZMA compression, other compression methods are not backwards-compatible. Or, see this post: https://github.com/yasirkula/SimplePatchTool/wiki/About-Backwards-Compatibility

    Migrating existing projects to "Project"s

    I'm assuming that you're planning to continue with the LZMA compression method which is the only backwards-compatible one as described above.
    1. Create a project
    2. If your latest version was e.g. 1.4.0, then create a subdirectory called
      1.4.0
      inside the Versions folder of the project
    3. Move your latest version's files into the 1.4.0 subdirectory (the files inside version 1.4.0 of your app's build directory, which contains the
      {PROJECT_NAME}_vers.sptv
      file)
    4. Open the project's Settings.xml and change change its Name property to match {PROJECT_NAME}
    5. If you had any incremental patches, create a subdirectory called IncrementalPatch inside the Output folder of the project and copy these incremental patches there
    6. Copy your latest VersionInfo.info to the Output folder of the project
    7. All done! Now you can continue creating your patches via this project (I've updated all the tutorials on GitHub, as well)
    8. If you don't want to use projects (using projects is much easier, trust me), you can always access the legacy build options from the "Open Legacy Window" option of Window-Simple Patch Tool
     
    Last edited: May 28, 2019
    AthrunVLokiz and hopeful like this.
  3. Airmouse

    Airmouse

    Joined:
    Jan 12, 2019
    Posts:
    37
    I keep getting error when using google drive:

    "Error: no suitable patch method found"


    What am I doing wrong?

    I used the DownloadLinkGeneratorForGoogleDrive and saved to txt file then updated my versioninfo.info like it says to here

    But after I click the update button, the error immediately shows (and with no debugging).

    What do I do now?


    Is there a reason for this?
     
    Last edited: May 28, 2019
  4. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    If SimplePatchTool is configured like
    UseRepairPatch(false).UseIncrementalPatch(false)
    or if you haven't created repair and/or incremental patches while generating the patch, this error occurs. Can you send me your VersionInfo via private message?

    I don't have a certificate from a trusted CA to sign the self patcher executable with, so it is probable that users' Windows OSes or anti virus softwares complain about the file being potentially harmful. At least if developers build their own self patcher executables themselves, they are responsible from either shipping the self patcher executable without signing it or signing it with a certificate from a trusted CA. On Windows, it is pretty easy to build a self patcher executable:

    - download this repository: https://github.com/yasirkula/SimplePatchTool
    - open Extras/SimplePatchToolExtras.sln
    - build the SelfPatcherWinForms project
    - move the generated SelfPatcher.exe and SelfPatcherCore.dll to the self patcher executable directory
     
    Last edited: May 28, 2019
  5. BlueTera

    BlueTera

    Joined:
    Jul 19, 2018
    Posts:
    21
    Will you make a updated version with launcher example. ?
    Becuse i really want to use it when i have gone further into development. on my current project.
     
  6. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    I've updated all the example scenes. Do you mean something like a video tutorial?
     
  7. BlueTera

    BlueTera

    Joined:
    Jul 19, 2018
    Posts:
    21
    Yea. that could be awsome like the old one but isen`t that one outdated ?.
     
  8. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    The video tutorials are old but will continue working, it is still possible to create patches the old way. The only difference is that you have to build the self patcher executable yourself and move it into your Unity build directory (inside a subdirectory called SPPatcher, to be precise).

    I've spent some time making sure that the written tutorials are clear and on point. You can give them a try, as well: https://github.com/yasirkula/UnitySimplePatchTool#examples
     
  9. tindolt

    tindolt

    Joined:
    Mar 28, 2014
    Posts:
    1
    Hey so the patcher is saying no suitable patch method found and I cant find where to set it so useIncrementalPatch(true) I have looked so hard and now its annoyed me.

    Ok so I found use incremental patch but its already true...
     
    Last edited: Jun 3, 2019
  10. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    No incremental patch will be generated in your first patch because incremental patch requires a "from" version and a "to" version in order to generate the binary diffs. Make sure that you generate either repair patch or installer patch, as well.
     
  11. AndyHubbard

    AndyHubbard

    Joined:
    Jul 9, 2015
    Posts:
    6
    Hi,
    I have got this integrated as a self patching Unity application but I get
    ERROR: downloaded file DS3D_Data\globalgamemanagers is corrupt

    Permissions seem OK on the server the files are trying to download from

    Can you please help me figure out what I am doing wrong?

    Many thanks,

    Andy
     
    Last edited: Jun 12, 2019
  12. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    I don't understand why the PatchInfo downloaded by SimplePatchTool contains a
    <html xmlns=''>
    line while the same file downloaded manually obviously doesn't contain that line. It might be the case that when SimplePatchTool downloads several files one after another, server returns an HTML page stating that there is an unusual traffic, instead of returning the actual file.

    When a patch is not completed successfully, the patch files remain untouched. You can navigate to
    %APPDATA%/../Local/SimplePatchToolDls
    in file explorer and open globalgamemanagers.lzdat with Notepad++ to see its contents. Check if it matches the contents of the actual globalgamemanagers.lzdat file.
     
  13. AndyHubbard

    AndyHubbard

    Joined:
    Jul 9, 2015
    Posts:
    6
    Thanks for helping. I looked at the
    %APPDATA%/../Local/SimplePatchToolDls/
    globalgamemanagers file and it is actually the webpage html of /DS3D/Distribution !
    So the actual file is not being downloaded. Is my <BaseDownloadURL> set properly in VersionInfo.info? Or could my server (.htaccess?) be preventing the file download?
     
  14. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    Oh, didn't even check BaseDownloadURL. It should be
    https://www.dreamscapes.dyn.dhs.org/DS3D/Distribution/
    . You can simply change that value and upload the new VersionInfo to the server, no need create the patches from scratch.
     
  15. AndyHubbard

    AndyHubbard

    Joined:
    Jul 9, 2015
    Posts:
    6
    Duh, that was it, many thanks for the great support :)
     
    yasirkula likes this.
  16. ssuniljust4u

    ssuniljust4u

    Joined:
    Jun 27, 2016
    Posts:
    3
    hi,

    will this patch work with UWP also.

    Best regards,
    Sunil
     
  17. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    Honestly, haven't tried it. But it may not work because UWP may no allow accessing files by their absolute file paths.
     
  18. KuPAfoo

    KuPAfoo

    Joined:
    Aug 24, 2013
    Posts:
    15
    I feel like a complete noob...
    I've read what seems to be every article of documentation and I cannot seem to find out how to manage the contents of the versioninfo.info file...

    I created a dummy
    i uploaded it to the cloud
    i generated a sharable link
    i replaced the app's pointer to the link

    now it shows "ERROR: version info is invalid"
    all I've learned from documentation about versioninfo.info is the <BaseDownloadURL> tag should be... existant...

    I also downloaded that "Download link generator"
    but everyone else's example versioninfo.info files have all kinds of xml in it...
    that includes file sizes... Now i'm thinking, if that's the case, how am I supposed to update the versioninfo to download NEW files, without manually adding each <VersionItem> manually...

    I'm so confused. It literally took 2 hours to figure out I can use the LauncherUI in the hierarchy to change the variables...

    The documentation on github is like a gigantic 6 hour goose chase with no answers. I don't mean to be rude, but this really looks a WHOLE LOT easier than your documentation is making it out to be.
     
  19. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    VersionInfo is auto-generated when you create a patch. Then, you make any modifications to it (like updating the download links) and then replace the dummy VersionInfo with the real one. Were the step-by-step tutorials confusing on this subject?
     
  20. KuPAfoo

    KuPAfoo

    Joined:
    Aug 24, 2013
    Posts:
    15
    The link you provided me is for the "Self patching app demo"
    https://github.com/yasirkula/UnitySimplePatchTool/#selfpatchingappdemo

    I'm trying to understand the "Launcher demo" before I continue to discover self-patching.

    but it seems in the inspector, self-patching is already integrated into your unity project "LauncherDemo" scene.


    You can test this scene as follows:

    • follow these steps and paste VersionInfo's url to the Version Info URL
    The link listed in step 1 shows you to create a versioninfo.info file and upload it to the cloud... easy process.
    • variable of SelfPatchingAppUI in the Inspector
    what is this step even trying to say?
    • tweak the value of Check Version Only as you like
    where are we looking for this? We haven't opened unity, visual studio... I have no idea what file or inspector you're looking at...
    What is this step even about?
    "A project is basically a directory that contains the following files and folders:"
    and then it goes on to describe how changing these variables will force you to access executables... within this launcher's file folders?

    Wait... are we not hosting our own separate program on the cloud, and keeping the launcher a separate entity? Or is this creating a build where I update the launcher, and that automatically updates the game?
    • as this app uses self patching, we need a self patcher executable: create a self patcher executable and put it inside the SelfPatcher directory of the project
    Right, self patcher executable... but is this still setup process or is this customization?

    So back to the original point... if the versioninfo.info is generated on runtime, but the client says the versioninfo.info in my cloud (which is a blank dummy) is incorrect, how am I supposed to get the correct version?

    that's not covered in the link you sent me either...

    again, I really don't mean to be rude, and I appreciate your creation and sharing of this gem, but the documentation seems to have too much legacy knowledge, and not enough unity information...

    I wish you could just look from the end user's perspective (knowing nothing about your architecture) and make a video on how to do a simple setup.
     
  21. KuPAfoo

    KuPAfoo

    Joined:
    Aug 24, 2013
    Posts:
    15
    Well, I found this video
    it's listed as (Legacy) in the github documentation... This definitely has the best guide for getting started. It shows you where to navigate in the hierarchy and how to upload the files...


    When I choose "Create patch" it shows nothing in the unity console, and generates a folder that does not contain versioninfo.info
     
    Last edited: Jun 14, 2019
  22. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    • follow these steps and paste VersionInfo's url to the Version Info URL variable of SelfPatchingAppUI in the Inspector

    SelfPatchingAppUI (or LauncherUI) is the name of the GameObject in the example scene that handles patching the app. When you select it, in Inspector you'll see some variables, one of which is "Version Info URL". Simply paste the download URL of the dummy VersionInfo there.

    • tweak the value of Check Version Only as you like

    Again, a value exposed in the Inspector. You can hover over it for a tooltip.

    • create a project for this app

    A "project" is a SimplePatchTool-project that makes patch creation process much easier. It was actually more difficult to create patches in the past, trust me :) You put each new build of your app inside a new subdirectory in the project's Versions folder and after you start generating the patch, the project looks at the configuration in Settings.xml and generates the patch files accordingly.

    • Wait... are we not hosting our own separate program on the cloud, and keeping the launcher a separate entity? Or is this creating a build where I update the launcher, and that automatically updates the game?

    You are hosting all the patch files on the server of your choice. That's actually why SimplePatchTool might be harder to grasp than other solutions: as SimplePatchTool can't guess where the files will be hosted, you are responsible from providing the correct download links for the patch files in VersionInfo.

    • as this app uses self patching, we need a self patcher executable: create a self patcher executable and put it inside the SelfPatcher directory of the project

    If you don't need the launcher the update itself, then you don't need a self patcher executable. Otherwise, you'll need one because the launcher can't update itself while it is still running, a companion app (self patcher executable) is needed for this job.

    • So back to the original point... if the versioninfo.info is generated on runtime, but the client says the versioninfo.info in my cloud (which is a blank dummy) is incorrect, how am I supposed to get the correct version?

    VersionInfo is not generated at runtime. It is generated automatically by the "project" after you generate a patch. Then, you'll need to upload the patch files to the server of your choice and update the files' download links in VersionInfo. Finally, you'll update the dummy VersionInfo with this new one. So your users will not be fetching a dummy file.

    • Creating a Project

    You can find the Unity section at the bottom of the same page: https://github.com/yasirkula/SimplePatchTool/wiki/Project:-Create#via-unity-plugin. SimplePatchTool is not a Unity-specific library, it can be used for native C# applications, as well. Thus, I explain all the possible ways to handle things in the wiki (via console app, Scripting API or Unity plugin). If there is a "Via Unity plugin" section in the page you are looking, you can ignore the other methods.

    • Legacy video tutorial

    I find the legacy method more confusing than using projects, and I intend to update those videos in the near future. One key change that has happened since the video was released is that, SimplePatchTool plugin doesn't come bundled with a self patcher executable anymore and you must create your own self patcher executable yourself (it is as simple as building a Visual Studio project). The reasoning behind this decision is explained here.

    If Create Patch does nothing, are you sure that all log types (errors, warnings, logs) are enabled in the console? There is supposed to be at least an error message.
     
  23. KuPAfoo

    KuPAfoo

    Joined:
    Aug 24, 2013
    Posts:
    15
    Beautiful response! I'll have to scour this message for some better understanding!

    My apologies for being so hard on you at first, but honestly if I hadn't found that legacy video i'd probably be ripping my hair out right now!

    your project is so beautiful, and it boasts about some really grand features. I can understand how this architecture is working, but the setup and configuration process was really outside of my limited scope. If you can go into lame-man's terms in documentation, (Like, simple patch tool for dummies xD)
    and possibly update your videos... that legacy one I shared is GOLD! but out of date...

    Thank you so much for this asset, I think it's finally coming together for me.
     
  24. AndyHubbard

    AndyHubbard

    Joined:
    Jul 9, 2015
    Posts:
    6
    Hi,
    I am struggling to get incremental patching working. The patch generation seems OK and I copy the IncrementalPatch folder, and the RepairFolder, to my server. The IncrementalPatch folder has the .info files and the patch files in it.
    What I get is this:

    Code (CSharp):
    1.  
    2. 94% Downloading 0_0__0_2.patch: 2.55/2.69MB (370.53KB/s)
    3. 98% Downloading 0_0__0_2.patch: 2.66/2.69MB (521.15KB/s)
    4. 100% Downloading 0_0__0_2.patch: 2.69/2.69MB (480.21KB/s) 0_0__0_2 downloaded in 4.920 seconds ...Decompressing patch 0_0__0_2... ...Updating 27 file(s)... ERROR: patch file for globalgamemanagers couldn't be found ERROR: patch file for globalgamemanagers.assets couldn't be found ERROR: patch file for level0 couldn't be found
    5. ERROR: patch file for level1 couldn't be found
    6. etc. etc.
    7. ERROR: patch file for sharedassets1.assets couldn't be found
    8. etc. etc.
    9. .......
    10. 0/27 files updated successfully
    11. ...Patch applied in 5.740 seconds...
    12. ...Checking if files are up-to-date...
    13. ...Some files are still not up-to-date, looking for solutions...
    14. ...Calculating new or changed files...
    15. ...Calculating files to download...
    16. ...Downloading 18 new or updated file(s)...
    17. ...Updating 18 file(s)...
    18. 1/18 Downloading: DS3D_Data\sharedassets1.assets.resS (250.68MB)
    19.  
    So it seems to fail on the incremental patch and then do a full repairpatch download. This takes a long time to complete.

    Any ideas?

    Cheers,

    Andy
     
  25. sophieannebusog3

    sophieannebusog3

    Joined:
    Mar 18, 2019
    Posts:
    5
    I'm having troubles with the selfpatcher, everytime I upload the second version, it always tell me "Self patcher does not exist" :( This is a Mac build
     
  26. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    @AndyHubbard Indeed, it looks like a serious issue. Shortly after the incremental patch fails, can you stop the app (so that the repair patch is interrupted), navigate to
    %APPDATA%\..\Local\SimplePatchToolDls
    in file explorer and check out the 0_0__0_2 directory to see if the incremental patch files are actually there? It might also be useful if you could send me your 0_0__0_2.info file via private message.

    @sophieannebusog3 Did you generate a self patcher executable and copy it to the SelfPatcher directory of your project? It is actually really simple: https://github.com/yasirkula/SimplePatchTool/wiki/Creating-Self-Patcher-Executable

    If you already did that, can you see your self patcher executable files' metadata in your patch's VersionInfo.info? While generating the second version, the project should automatically copy the self patcher executable files from SelfPatcher to your second version's SPPatcher subdirectory (this folder will be created automatically). Can you see the self patcher's files in there?
     
  27. AndyHubbard

    AndyHubbard

    Joined:
    Jul 9, 2015
    Posts:
    6
    Hi,
    I interrupted the patch after incremental patch failure and looked in the folder. I do not see the 0_0__0_2 folder.
    I do see a 'Downloads' folder and also a 'Files' folder, and also a time.dat file.
    I am actually now on v0.3 (just to see if another increment helped, it didn't) so I'll private message you my 0_0__0_3.info file now.

    Cheers,
    Andy
     
  28. MM47

    MM47

    Joined:
    Sep 7, 2012
    Posts:
    10
    --UPDATE--
    I uploaded to Dropbox and it's fine. There's something wrong with downloading from a localhost server.
    -----
    Hi,
    I'm getting the corrupt file error as Andy. I check in AppData and all the files it attempts to download are 0kb.
    I host the files locally, so they are public and they download correctly froma web browser request.
    I'm not sure what's wrong. I never did a 0.0 patch as in the manual but that shouldn't matter because the installer patch fails too.
     
    Last edited: Jul 2, 2019
  29. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    While using FileZilla to upload the patch files to a server, files must be transferred in Binary mode and not in ASCII mode because otherwise, contents of some patch files may change slightly during the upload process (maybe file endings are converted to some other format automatically?). I'm guessing that something similar is happening in your localhost. Even if a single byte of the patch file changes, SimplePatchTool throws a corrupt download error for security reasons.
     
    MM47 likes this.
  30. Dirkulez

    Dirkulez

    Joined:
    Jul 2, 2019
    Posts:
    4
    Hi,
    I am faced with similar problems @AndyHubbard posted here. Only Repair Patch works. Incremental Patch and Installer Patch fail.
    I debugged SimplePatchToolCore to get an idea of the issue. It seems that IncrementalPatchApplier/InstallerPatchApplier expects the file at a different path than the file is extracted to. In my case SelfPatchingAppDemo.exe needs to be updated. It is extracted to the following path:
    AppData\Local\SimplePatchToolDls\SelfPatchingAppDemo\1_0__2_0\SelfPatchingApp_Project\Temp\Incremental\Temp\SelfPatchingAppDemo.exe

    But IncrementalPatchApplier expects the file at this location:
    AppData\Local\SimplePatchToolDls\SelfPatchingAppDemo\1_0__2_0\SelfPatchingAppDemo.exe

    The Incremental Patch has the following internal structure:
    1_0__2_0.patch\1_0__2_0\SelfPatchingApp_Project\Temp\Incremental\Temp\SelfPatchingAppDemo.exe
    .

    Am I doing anything wrong or is this a problem in the SimplePatchTool?

    Best regards
    Dirk
     
  31. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    Yes, this is the same issue. For a quick test, can you please follow these steps and let me know of the result:

    1. Download this repository: https://github.com/yasirkula/SimplePatchTool
    2. Open
    SimplePatchTool\Extras\SimplePatchToolExtras.sln
    and rebuild the SimplePatchToolConsoleApp project
    3. Open cmd and navigate to
    SimplePatchTool\Extras\SimplePatchToolConsoleApp\bin\Release

    4. Type
    Patcher project_generate_patch -projectRoot="SOME_EMPTY_TEST_DIRECTORY"
    to create project
    5. Inside the project's Versions directory, create a
    1.0
    folder and create a dummy .txt file in it with placeholder text
    6. Type
    Patcher project_generate_patch -projectRoot="SOME_EMPTY_TEST_DIRECTORY"
    to generate the initial patch
    7. Inside the project's Versions directory, create a
    2.0
    folder and create a different dummy .txt file in it
    8. Type
    Patcher project_generate_patch -projectRoot="SOME_EMPTY_TEST_DIRECTORY"
    to generate the second patch
    9. Open
    SOME_EMPTY_TEST_DIRECTORY\Output\IncrementalPatch\1_0__2_0.patch
    with 7-zip and see if it suffers the same issue. For me, it didn't have the
    Distribution\Temp\Incremental\Temp\
    part.
     
  32. Dirkulez

    Dirkulez

    Joined:
    Jul 2, 2019
    Posts:
    4
    The test looks good. Now I also don't have this "Distribution\Temp\Incremental\Temp\" stuff in the patch, just 1_0__2_0.patch\1_0__2_0\DUMMY.txt
    and an empty folder 1_0__2_0.patch\1_0__2_0\1_0__2_0~\

    And I think I found the root cause for this issue. For my first test I performed a Rebuild of the SimplePatchTool Solution. To get this work I had to remove some inline out parameter declarations in the TarExtendedHeaderReader.cs and TarInputStream.cs. Since I work with Visual Studio 2015 I can't use this C#7 Feature.
    My assumption is that the Rebuild of the SimplePatchTool with VS 2015 causes this strange behavior.
    What do you think?
     
  33. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    I don't know why the incremental patch would be structured like this:
    1_0__2_0.patch\1_0__2_0\SelfPatchingApp_Project\Temp\Incremental\Temp\SelfPatchingAppDemo.exe


    I find it unlikely that some inlined C# 7 parameters would cause this issue. May I ask, on which Unity version did you test SimplePatchTool and in summary, what steps did you follow to generate your patches? If I can reproduce the issue myself, then I believe I'll be able to resolve the issue.
     
  34. Dirkulez

    Dirkulez

    Joined:
    Jul 2, 2019
    Posts:
    4
    I don't use Unity. I want to use SimplePatchTool in a standalone C# application. I downloaded it from GitHub and followed the steps described in your tutorial. So my steps were quite the same as in the test that produces the correct patch.

    So I performed some further tests to figure out what causes the different results.
    My previous assumption is not correct. Even with the SimplePatchTool.dll I have built with VS2015 I was able to produce a correct patch.
    But the
    -projectRoot
    parameter at the patcher call seems to cause the different behavior. The patcher call with
    -projectRoot="NewTest10"
    produces a correct patch. But when I define
    -projectRoot="..\NewTest10"
    to target a directory above my current position the resulting patch shows a strange directory structure. Also when I want to target a directory below my current position and I use an absolute path, the resulting patch shows a strange directory structure.
    Are there any restriction for the projectRoot Parameter?
     
  35. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    Thank you for the feedback, "when I want to target a directory below my current position" helped me a lot and I was able to reproduce the issue. Can you try replacing ZipUtils.cs as following and see if it resolves the issue:

    Code (CSharp):
    1. using ICSharpCode.SharpZipLib.Tar;
    2. using System;
    3. using System.Collections.Generic;
    4. using System.IO;
    5. using System.IO.Compression;
    6. using System.Text.RegularExpressions;
    7. using Compression = SevenZip.Compression;
    8.  
    9. namespace SimplePatchToolCore
    10. {
    11.     public enum CompressionFormat { LZMA = 0, GZIP = 1, NONE = 9 };
    12.  
    13.     public static class ZipUtils
    14.     {
    15.         public static void CompressFile( string inFile, string outFile, CompressionFormat format )
    16.         {
    17.             using( FileStream input = new FileStream( inFile, FileMode.Open, FileAccess.Read ) )
    18.             using( FileStream output = new FileStream( outFile, FileMode.Create ) )
    19.             {
    20.                 if( format == CompressionFormat.LZMA )
    21.                 {
    22.                     // Credit: http://stackoverflow.com/questions/7646328/how-to-use-the-7z-sdk-to-compress-and-decompress-a-file
    23.                     Compression.LZMA.Encoder coder = new Compression.LZMA.Encoder();
    24.  
    25.                     // Write the encoder properties
    26.                     coder.WriteCoderProperties( output );
    27.  
    28.                     // Write the decompressed file size.
    29.                     output.Write( BitConverter.GetBytes( input.Length ), 0, 8 );
    30.  
    31.                     // Encode the file.
    32.                     coder.Code( input, output, input.Length, -1, null );
    33.                 }
    34.                 else if( format == CompressionFormat.GZIP )
    35.                 {
    36.                     using( GZipStream compressionStream = new GZipStream( output, CompressionMode.Compress ) )
    37.                     {
    38.                         input.CopyTo( compressionStream );
    39.                     }
    40.                 }
    41.                 else
    42.                     input.CopyTo( output );
    43.             }
    44.         }
    45.  
    46.         public static void DecompressFile( string inFile, string outFile, CompressionFormat format )
    47.         {
    48.             using( FileStream input = new FileStream( inFile, FileMode.Open, FileAccess.Read ) )
    49.             using( FileStream output = new FileStream( outFile, FileMode.Create ) )
    50.             {
    51.                 if( format == CompressionFormat.LZMA )
    52.                 {
    53.                     // Credit: http://stackoverflow.com/questions/7646328/how-to-use-the-7z-sdk-to-compress-and-decompress-a-file
    54.                     Compression.LZMA.Decoder coder = new Compression.LZMA.Decoder();
    55.  
    56.                     // Read the decoder properties
    57.                     byte[] properties = new byte[5];
    58.                     input.Read( properties, 0, 5 );
    59.  
    60.                     // Read in the decompress file size.
    61.                     byte[] fileLengthBytes = new byte[8];
    62.                     input.Read( fileLengthBytes, 0, 8 );
    63.                     long fileLength = BitConverter.ToInt64( fileLengthBytes, 0 );
    64.  
    65.                     coder.SetDecoderProperties( properties );
    66.                     coder.Code( input, output, input.Length, fileLength, null );
    67.                 }
    68.                 else if( format == CompressionFormat.GZIP )
    69.                 {
    70.                     using( GZipStream decompressionStream = new GZipStream( input, CompressionMode.Decompress ) )
    71.                     {
    72.                         decompressionStream.CopyTo( output );
    73.                     }
    74.                 }
    75.                 else
    76.                     input.CopyTo( output );
    77.             }
    78.         }
    79.  
    80.         public static void CompressFolder( string inFolder, string outFile, CompressionFormat format )
    81.         {
    82.             CompressFolder( inFolder, outFile, format, new List<Regex>( 0 ) );
    83.         }
    84.  
    85.         internal static void CompressFolder( string inFolder, string outFile, CompressionFormat format, List<Regex> ignoredPathsRegex )
    86.         {
    87.             string tarFilePath = outFile + "tmptar";
    88.  
    89.             // Source: https://github.com/icsharpcode/SharpZipLib/wiki/GZip-and-Tar-Samples#-create-a-tar-or-tgz-with-control-over-filenames-and-data-source
    90.             using( FileStream outputStream = File.Create( tarFilePath ) )
    91.             using( TarOutputStream tarOutputStream = new TarOutputStream( outputStream ) )
    92.             {
    93.                 CreateTarRecursive( tarOutputStream, new byte[32 * 1024], new DirectoryInfo( inFolder ), "", ignoredPathsRegex );
    94.             }
    95.  
    96.             CompressFile( tarFilePath, outFile, format );
    97.             File.Delete( tarFilePath );
    98.         }
    99.  
    100.         public static void DecompressFolder( string inFile, string outFolder, CompressionFormat format )
    101.         {
    102.             string tarFilePath = outFolder + "tmptar.tar";
    103.             DecompressFile( inFile, tarFilePath, format );
    104.  
    105.             // Source: https://github.com/icsharpcode/SharpZipLib/wiki/GZip-and-Tar-Samples#--simple-full-extract-from-a-tar-archive
    106.             using( Stream inStream = File.OpenRead( tarFilePath ) )
    107.             using( TarArchive tarArchive = TarArchive.CreateInputTarArchive( inStream ) )
    108.             {
    109.                 tarArchive.ExtractContents( outFolder );
    110.             }
    111.  
    112.             File.Delete( tarFilePath );
    113.         }
    114.  
    115.         // Source: https://github.com/icsharpcode/SharpZipLib/wiki/GZip-and-Tar-Samples#-create-a-tar-or-tgz-with-control-over-filenames-and-data-source
    116.         private static void CreateTarRecursive( TarOutputStream tarOutputStream, byte[] fileCopyBuffer, DirectoryInfo directory, string relativePath, List<Regex> ignoredPathsRegex )
    117.         {
    118.             FileInfo[] files = directory.GetFiles();
    119.             for( int i = 0; i < files.Length; i++ )
    120.             {
    121.                 string fileRelativePath = relativePath + files[i].Name;
    122.                 if( !ignoredPathsRegex.PathMatchesPattern( fileRelativePath ) )
    123.                 {
    124.                     using( Stream inputStream = File.OpenRead( files[i].FullName ) )
    125.                     {
    126.                         TarEntry tarEntry = TarEntry.CreateTarEntry( fileRelativePath.Replace( '\\', '/' ) );
    127.                         tarEntry.Size = inputStream.Length;
    128.                         tarOutputStream.PutNextEntry( tarEntry );
    129.  
    130.                         int numRead;
    131.                         while( ( numRead = inputStream.Read( fileCopyBuffer, 0, fileCopyBuffer.Length ) ) > 0 )
    132.                             tarOutputStream.Write( fileCopyBuffer, 0, numRead );
    133.                     }
    134.  
    135.                     tarOutputStream.CloseEntry();
    136.                 }
    137.             }
    138.  
    139.             DirectoryInfo[] subDirectories = directory.GetDirectories();
    140.             for( int i = 0; i < subDirectories.Length; i++ )
    141.             {
    142.                 string directoryRelativePath = relativePath + subDirectories[i].Name + Path.DirectorySeparatorChar;
    143.                 if( !ignoredPathsRegex.PathMatchesPattern( directoryRelativePath ) )
    144.                     CreateTarRecursive( tarOutputStream, fileCopyBuffer, subDirectories[i], directoryRelativePath, ignoredPathsRegex );
    145.             }
    146.         }
    147.  
    148.         // Credit: https://stackoverflow.com/a/5730893/2373034
    149.         private static void CopyTo( this Stream input, Stream output )
    150.         {
    151.             byte[] buffer = new byte[8 * 1024];
    152.             int bytesRead;
    153.  
    154.             while( ( bytesRead = input.Read( buffer, 0, buffer.Length ) ) > 0 )
    155.             {
    156.                 output.Write( buffer, 0, bytesRead );
    157.             }
    158.         }
    159.     }
    160. }
     
  36. Dirkulez

    Dirkulez

    Joined:
    Jul 2, 2019
    Posts:
    4
    Looks good. Now the patches seem to have the correct structure regardless of the -projectRoot parameter.

    Thanks for the good support!!
     
    yasirkula likes this.
  37. KuPAfoo

    KuPAfoo

    Joined:
    Aug 24, 2013
    Posts:
    15
    So, I finally found a free repository for hosting the launcher files, that isn't as tedious as google drive, and doesn't require the knowledge of a custom server...


    I'm using github to host my files, and everything appears to be working now!

    well, that is until I try to update the launcher itself. I'm getting the following in my Launcher's text log. I'm unsure what creates the self patcher.

    I'll continue researching, but I figured someone might already have the answer and can shed some light on it for us all.
    Thank you.


    ...Calculating obsolete files...
    ...No obsolete files...
    ...No obsolete files...
    ...Patch successfully completed in 6.522 seconds...
    ...Waiting for the self patcher to complete the update...

    === 07/09/2019 18:41:57 ===
    ERROR: self patcher does not exist


    I do want to apologize to the original creator for my first response.

    I'm incredibly grateful for your creation and dedication to your project. I am utilizing it to handle my own MMORPG's client files. Here's what i've done to your launcher :)
    https://i.gyazo.com/46bffe68d472cc3ae0fb189d10714255.mp4

    It seems unfortunate that it's documentation is so well organized to it's creator, that as a new mind I'm unsure of where the process begins.

    I would like to suggest/request two things to better your progress.
    1) Your video tutorial for the legacy setup in unity is very well versed. I would request an updated version in the same format as the video I shared above.
    a) The legacy interface for simple patch tool still works, and the legacy video shows us how to use that, so I don't believe this needs a re-make. There is one exception here; the self patcher update you access in LauncherUI that isn't shown in the video needs to be explained.
    b) I'm interested to see how your new patch add-on works, it looks very slick. But I trust in your video.

    2) A video for the self patcher tool in unity, how it would work as a step 2 for the above video request.


    Again, I have to thank you so much and apologize for my rude introduction. The amount of scopes to cover as a solo indie mmo dev is insane, and you were an incredible time saving break through mad genius! I commend and applaud you for your creation and execution. Please consider a more direct path for full implementation of your grand project.

    Thank you again.
     
    Last edited: Jul 10, 2019
  38. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    Thank you for the compliments and congratulations for your launcher!

    I'm always looking for ways to improve the quality of the documentation, you can be sure of that. I also want to make the integration process as smooth and easy as possible. Thus, these sorts of feedback are always welcome.

    If you are using the legacy method, for each new build of your Unity project, create a SPPatcher subdirectory inside the build path, generate self patcher executable and move its files to SPPatcher. Only then should you create patches.
     
  39. Voxely

    Voxely

    Joined:
    Apr 21, 2017
    Posts:
    3
    Hello ! Can you make a video of this patcher ? I can't undertstand how to use it, I saw all exemples but no, I can't :(
     
    yasirkula likes this.
  40. rwanim8or

    rwanim8or

    Joined:
    Jan 22, 2017
    Posts:
    5
    I very recently went through the tutorials, and I recommend watching the videos for the legacy method first. A lot of stuff will make much more sense after seeing how all the pieces come together.
     
  41. odcheban

    odcheban

    Joined:
    Oct 13, 2017
    Posts:
    4
    Hi, I get a (rarely) error:
    NullReferenceException: UnityWebRequest has already been destroyed
    at (wrapper managed-to-native) UnityEngine.Networking.UnityWebRequest: get_isError ()
    at SimplePatchToolUnity.CookieAwareWebClient + <DownloadString> c__Iterator0.MoveNext () [0x00000] in <filename unknown>: 0
    at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>: 0
    How can I fix it?
     
  42. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    @odcheban I'll check it out, thanks for reporting this. Does it happen when doing something in particular?

    @Voxely Improved tutorials are definitely in my to-do list, including video tutorials. Thanks for the support.
     
  43. odcheban

    odcheban

    Joined:
    Oct 13, 2017
    Posts:
    4
  44. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    It shouldn't throw any exceptions but I'll check it out since it is throwing exceptions in your case. Do you remember the error message you receive and its exact line number?
     
  45. Voxely

    Voxely

    Joined:
    Apr 21, 2017
    Posts:
    3
    Hello ! Now I understand some things, but I have a last question.
    What should I copy ? I compiled my Unity game project (separated from the unity project of the launcher),
    In my buildedGame folder, I have MonoBleedingEdge, PetSim Data, PetSim.exe, UnityCrashHandler_64.exe, and two dll.
    Thanks.

    EDIT : I found it, but I have Invalid URL VersionInfo...
     
    Last edited: Jul 31, 2019
  46. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    Sorry about the confusion. Let me provide a more detailed step-by-step tutorial below. Please follow these steps (don't click any links in those pages to avoid confusion):

    1) create a project (let's say PROJECT_PATH is the path of the project): https://github.com/yasirkula/SimplePatchTool/wiki/Project:-Create#via-unity-plugin
    2) create a self patcher executable: https://github.com/yasirkula/Simple...-using-a-pre-existing-self-patcher-executable
    3) Move self patcher executable's files to PROJECT_PATH/SelfPatcher directory
    4) Generate a versionInfoURL: https://github.com/yasirkula/SimplePatchTool/wiki/Generating-versionInfoURL
    5) Copy that url to your app's Version Info URL (for simplicity's sake, let's start with the SelfPatchingAppDemo scene in Unity. In this case, paste the url to the SelfPatchingAppUI object's "Version Info URL" property from the Inspector)
    6) If your self patcher executable has a different name than "SelfPatcher.exe", change the value of "SelfPatchingAppDemo->Self Patcher Executable" property
    7) Create PROJECT_PATH/Versions/1.0 directory
    8) Inside Unity's Build Settings, add the SelfPatchingAppDemo scene to "Scenes in Build" and move it to the top (you can disable the other scenes while testing). Then, build your project to the PROJECT_PATH/Versions/1.0 directory
    9) Generate a patch for version 1.0: https://github.com/yasirkula/SimplePatchTool/wiki/Project:-Generating-Patch#via-unity-plugin
    10) The generated patch files (including VersionInfo.info) are located at PROJECT_PATH/Output. Upload the folders inside it to the server of your choice: https://github.com/yasirkula/SimplePatchTool/wiki/Hosting-Patch-Files
    11.a) If the server you've hosted your files at supports downloading the files from urls like SERVER_URL/RepairPatch/Game.exe.lzdat or SERVER_URL/RepairPatch/Game_Data/globalgamemanagers.lzdat (these kinds of urls are not possible for Drive or Dropbox because they append unique ids to each file's download url), then open PROJECT_PATH/Output/VersionInfo.info with Notepad++ or Notepad and change its BaseDownloadURL parameter like this: <BaseDownloadURL>SERVER_URL/</BaseDownloadURL> (of course, replace SERVER_URL with the url of the patch files directory on your server and end the path with a slash)
    11.b) Otherwise, as each file's download url contains a unique id in it, each file's download url must be set manually in VersionInfo.info. For Drive and Dropbox, I've created helper scripts to kind of automate this process. First, generate the download urls of the patch files: https://github.com/yasirkula/Simple...oad-Links#b1-generating-download-links-holder. Then, copy these download urls and paste them to PROJECT_PATH/Other/DownloadLinks.txt. Finally, update the urls inside VersionInfo.info using DownloadLinks.txt: https://github.com/yasirkula/SimplePatchTool/wiki/Project:-Updating-Download-Links#via-unity-plugin
    12) Open PROJECT_PATH/Output/VersionInfo.info with Notepad++ or Notepad and verify that each file now has a download url in its metadata. Now, remember the VersionInfo.info you've uploaded to your server in step 4? Simply replace its contents with PROJECT_PATH/Output/VersionInfo.info's contents
    13) All done, your first patch is live! Let's quickly test it. Open the PatcherControlPanelDemo scene in Unity, go into Play mode and paste your Version Info URL to the corresponding input field. Then, enter an empty directory's path to "Root path" and hit "PATCH!" (don't select "Self patching" since the PatcherControlPanelDemo is acting like a launcher right now, so the self patcher executable isn't needed in this case). After the patch is complete, check the "Root path" and verify that it contains the version 1.0 of your app
    14) Now let's create a 1.0.1 version of your app. Make some visible changes to the SelfPatchingAppDemo scene (change camera's background color, add some cubes around and etc.). Then, create PROJECT_PATH/Versions/1.0.1 directory and build your project to that path
    15) Repeat steps 9-12
    16) Copy the PROJECT_PATH/Versions/1.0 directory to some other place for testing purposes. Open the app inside it and verify that it prompts you about the 1.0.1 update. After updating the app, verify that now the scene has the changes you've applied in step 14

    Hope it helps :)
     
  47. Voxely

    Voxely

    Joined:
    Apr 21, 2017
    Posts:
    3
    I will try and I tell you if it's work :)
     
  48. Spartikus3

    Spartikus3

    Joined:
    Dec 27, 2013
    Posts:
    38
    Good moring all. @yasirkula

    You have tried so diligently to explain how this works to the average layperson but I am pretty dense and still having a few questions if you had a moment:

    1) When you use the Unity plugin, what is the "Project directory" as it relates to your Unity project files?
    • Is the Simple patch tool "project directory" a completely new folder and it's own stand alone project?
    • Is it the actual project directory of your Unity project?
    • Should it be the root of your build of the unity project you are trying to build the patcher for?
    2) Does this patcher work off of the Build of a project or the raw files?
    • I dont see anywhere in the instructions about doing a build of the project you are trying to patch so doe sit work on the raw files inside Unity and somehow patch those into the resource files of the built project?
    Thank you sir!
     
  49. yasirkula

    yasirkula

    Joined:
    Aug 1, 2011
    Posts:
    1,182
    Hello!

    1) Project directory is not related to the Unity project, it is a stand alone SimplePatchTool-project in a completely new folder. After creating a project at an empty directory, some files and folders will automatically be created inside it, one of which is "Versions". This is where you put each new build of your Unity project and the SimplePatchTool-project will create patches using these files whenever you press "Generate Patch" (to be precise, you should put your Unity build files inside a subdirectory of Versions, e.g.
    Versions/1.0
    or
    Versions/0.5.1
    ). Patch creation process can be customized by changing the values inside Settings.xml in your SimplePatchTool-project path.

    2) It works on your builds, with each build going to a new subdirectory in your SimplePatchTool-project's Versions folder.
     
  50. Spartikus3

    Spartikus3

    Joined:
    Dec 27, 2013
    Posts:
    38
    You are the man Yasir. Thank you kindly sir. I think I have it working. Doing more testing. Honestly a fantastic resource. Great assett to the community.
     
    yasirkula likes this.