Last active
December 1, 2024 21:21
-
-
Save HBIDamian/2f7ffd349651373ec4e877b7fe41859d to your computer and use it in GitHub Desktop.
PocketMine Installer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[Setup] | |
AppName=PocketMine-MP Win-x64 Installer | |
AppVersion=1.0.0.0 | |
AppVerName=PocketMine-MP Win-x64 Installer v1.0.0 | |
AppPublisher=PocketMine-MP | |
AppPublisherURL=pmmp.io | |
AppSupportURL=pmmp.io | |
AppID=PM-Win-x64-Installer | |
DefaultDirName={userdesktop}\PocketMine-MP | |
DefaultGroupName=PocketMine-MP | |
OutputBaseFilename=PocketMine-MP-Installer | |
SetupIconFile=installer_icon.ico | |
LicenseFile=license.txt | |
SolidCompression=yes | |
Compression=lzma2 | |
PrivilegesRequired=admin | |
ArchitecturesInstallIn64BitMode=x64 | |
DisableProgramGroupPage=yes | |
DisableDirPage=no | |
DisableReadyPage=yes | |
DisableFinishedPage=no | |
WizardImageFile=sideBar.bmp | |
WizardSmallImageFile=logo.bmp | |
Uninstallable=no | |
CreateUninstallRegKey=no | |
[Code] | |
const | |
S_OK = 0; | |
JSON_URL = 'https://update.pmmp.io/'; | |
VCRUNTIME_URL = 'https://aka.ms/vs/17/release/vc_redist.x64.exe'; | |
START_CMD_URL = 'https://raw.githubusercontent.com/pmmp/PocketMine-MP/stable/start.cmd'; | |
START_PS1_URL = 'https://raw.githubusercontent.com/pmmp/PocketMine-MP/stable/start.ps1'; | |
USER_AGENT = 'PocketMine-MP WIN-x64 Installer'; | |
SHCONTCH_NOPROGRESSBOX = $00000001; | |
SHCONTCH_RESPONDYESTOALL = $00000004; | |
NO_PROGRESS_BOX = 4; | |
RESPOND_YES_TO_ALL = 16; | |
var | |
CurrentStep: Integer; | |
procedure InitializeWizard; | |
begin | |
WizardForm.DiskSpaceLabel.Visible := false; // False to hide | |
WizardForm.ProgressGauge.Visible := true; // True to show | |
CurrentStep := 0; | |
end; | |
procedure UnZip(ZipPath, FileName, TargetPath: string); | |
var | |
Shell: Variant; | |
ZipFile: Variant; | |
Item: Variant; | |
TargetFolder: Variant; | |
begin | |
try | |
Shell := CreateOleObject('Shell.Application'); | |
if VarIsEmpty(Shell) then | |
RaiseException('Failed to create Shell.Application object.'); | |
ZipFile := Shell.NameSpace(ZipPath); | |
if VarIsEmpty(ZipFile) then | |
RaiseException(Format('Cannot open ZIP file "%s" or it does not exist.', [ZipPath])); | |
if FileName <> '' then | |
begin | |
Item := ZipFile.ParseName(FileName); | |
if VarIsEmpty(Item) then | |
RaiseException(Format('Cannot find "%s" in ZIP file "%s".', [FileName, ZipPath])); | |
end | |
else | |
Item := ZipFile.Items; | |
TargetFolder := Shell.NameSpace(TargetPath); | |
if VarIsEmpty(TargetFolder) then | |
RaiseException(Format('Target path "%s" does not exist.', [TargetPath])); | |
TargetFolder.CopyHere(Item, NO_PROGRESS_BOX or RESPOND_YES_TO_ALL); | |
DeleteFile(ZipPath); | |
except | |
MsgBox('Error during unzipping: ' + GetExceptionMessage, mbError, MB_OK); | |
end; | |
end; | |
procedure UnzipWithProgress(const ZipPath, TargetPath: string); | |
var | |
Shell: Variant; | |
ZipFile: Variant; | |
TargetFolder: Variant; | |
TotalItems, ExtractedItems: Integer; | |
Item: Variant; | |
begin | |
try | |
Shell := CreateOleObject('Shell.Application'); | |
if VarIsEmpty(Shell) then | |
begin | |
MsgBox('Failed to create Shell.Application object.', mbError, MB_OK); | |
Exit; | |
end; | |
ZipFile := Shell.NameSpace(ZipPath); | |
if VarIsEmpty(ZipFile) then | |
begin | |
MsgBox('Failed to open ZIP file: ' + ZipPath, mbError, MB_OK); | |
Exit; | |
end; | |
TargetFolder := Shell.NameSpace(TargetPath); | |
if VarIsEmpty(TargetFolder) then | |
begin | |
MsgBox('Target path "' + TargetPath + '" does not exist.', mbError, MB_OK); | |
Exit; | |
end; | |
TotalItems := ZipFile.Items.Count; | |
WizardForm.ProgressGauge.Min := 0; | |
WizardForm.ProgressGauge.Max := TotalItems; | |
WizardForm.ProgressGauge.Position := 0; | |
WizardForm.StatusLabel.Caption := 'Extracting Binary Files. Please wait...'; | |
// Extract items one by one | |
for ExtractedItems := 0 to TotalItems - 1 do | |
begin | |
Item := ZipFile.Items.Item(ExtractedItems); | |
TargetFolder.CopyHere(Item, 16); // Flags: 16 = Respond Yes to All | |
// Update progress bar | |
WizardForm.ProgressGauge.Position := ExtractedItems + 1; | |
// Add a small delay to improve UI responsiveness | |
Sleep(50); | |
end; | |
// Optionally delete the ZIP file after extraction | |
DeleteFile(ZipPath); | |
except | |
MsgBox('Error occurred during unzipping!', mbError, MB_OK); | |
end; | |
end; | |
function PosEx(const SubStr, S: string; Offset: Integer): Integer; | |
begin | |
if Offset > Length(S) then | |
begin | |
Result := 0; | |
Exit; | |
end; | |
Result := Pos(SubStr, Copy(S, Offset, MaxInt)); | |
if Result <> 0 then | |
Result := Result + Offset - 1; | |
end; | |
function StringReplace(const S, OldPattern, NewPattern: string): string; | |
var | |
FoundPos, LenOld, LenNew: Integer; | |
begin | |
LenOld := Length(OldPattern); | |
LenNew := Length(NewPattern); | |
Result := S; | |
FoundPos := 1; | |
while FoundPos <= Length(Result) do | |
begin | |
FoundPos := PosEx(OldPattern, Result, FoundPos); | |
if FoundPos = 0 then | |
Break; | |
Delete(Result, FoundPos, LenOld); | |
Insert(NewPattern, Result, FoundPos); | |
FoundPos := FoundPos + LenNew; | |
end; | |
end; | |
function URLDownloadToFile(Caller: Integer; URL, FileName: PAnsiChar; Reserved: DWORD; StatusCB: Integer): Integer; | |
external '[email protected] stdcall'; | |
function URLExists(const URL: string): Boolean; | |
var | |
HTTPRequest: Variant; | |
StatusCode: Integer; | |
ValidURL: string; | |
begin | |
// Ensure the URL starts with "http://" or "https://" | |
ValidURL := Trim(URL); | |
if (Pos('http://', ValidURL) <> 1) and (Pos('https://', ValidURL) <> 1) then | |
ValidURL := 'https://' + ValidURL; | |
try | |
HTTPRequest := CreateOleObject('WinHttp.WinHttpRequest.5.1'); | |
HTTPRequest.Open('HEAD', ValidURL, False); | |
HTTPRequest.SetRequestHeader('User-Agent', USER_AGENT); | |
HTTPRequest.Send; | |
StatusCode := HTTPRequest.Status; | |
Result := (StatusCode = 200); | |
except | |
// Log or show the exception to help with troubleshooting | |
MsgBox('Error checking URL: ' + GetExceptionMessage, mbError, MB_OK); | |
Result := False; | |
end; | |
end; | |
function DownloadFile(const URL, DestFile: string): Boolean; | |
begin | |
// Check if the URL is valid and the file exists before attempting to download | |
if not URLExists(URL) then | |
begin | |
MsgBox('The file at ' + URL + ' does not exist or is unreachable.', mbError, MB_OK); | |
Result := False; | |
Exit; | |
end; | |
// Proceed with downloading the file if it exists | |
Result := URLDownloadToFile(0, PAnsiChar(URL), PAnsiChar(DestFile), 0, 0) = 0; | |
if not Result then | |
begin | |
MsgBox('Failed to download: ' + URL + ' to ' + DestFile, mbError, MB_OK); | |
end | |
else | |
begin | |
// MsgBox('Downloaded ' + URL + ' to ' + DestFile, mbInformation, MB_OK); | |
end; | |
end; | |
function DownloadString(const URL: string): string; | |
var | |
TempFile: string; | |
Lines: TStringList; | |
begin | |
TempFile := ExpandConstant('{tmp}\temp.json'); | |
if not DownloadFile(URL, TempFile) then | |
Abort; | |
Lines := TStringList.Create; | |
try | |
Lines.LoadFromFile(TempFile); | |
Result := Lines.Text; | |
finally | |
Lines.Free; | |
end; | |
end; | |
function GetJSONValue(const JSON, Key: string): string; | |
var | |
StartPos, EndPos: Integer; | |
begin | |
StartPos := Pos('"' + Key + '":', JSON) + Length(Key) + 3; | |
if StartPos > Length(JSON) then | |
begin | |
Result := ''; | |
Exit; | |
end; | |
EndPos := PosEx(',', JSON, StartPos); | |
if EndPos = 0 then | |
EndPos := PosEx('}', JSON, StartPos); | |
Result := Copy(JSON, StartPos, EndPos - StartPos); | |
Result := StringReplace(Result, '"', ''); | |
end; | |
function ExtractZip(const ZipFile, OutputFolder: string): Boolean; | |
begin | |
UnZip(ZipFile, '', OutputFolder); | |
end; | |
procedure CurStepChanged(CurStep: TSetupStep); | |
var | |
OutputFolder, JSONData, PHPZipURL, PharURL: string; | |
PHPVersion, BASEVersion, BASEVersionMajor: string; | |
TotalSteps, CurrentStep: Integer; | |
ResultCode: Integer; | |
begin | |
if CurStep = ssInstall then | |
begin | |
OutputFolder := ExpandConstant('{app}'); | |
TotalSteps := 8; | |
WizardForm.ProgressGauge.Min := 0; | |
WizardForm.ProgressGauge.Max := TotalSteps; | |
WizardForm.ProgressGauge.Position := 0; | |
CurrentStep := 0; // Reset global CurrentStep | |
if not DirExists(OutputFolder) then | |
if not CreateDir(OutputFolder) then | |
begin | |
MsgBox('Failed to create output directory: ' + OutputFolder, mbError, MB_OK); | |
Abort; | |
end; | |
// Step 1: Download JSON data | |
WizardForm.StatusLabel.Caption := 'Downloading update.pmmp.io data...'; | |
JSONData := DownloadString(JSON_URL); | |
CurrentStep := CurrentStep + 1; | |
WizardForm.ProgressGauge.Position := CurrentStep; | |
if JSONData = '' then | |
Abort; | |
// Step 2: Parse JSON | |
WizardForm.StatusLabel.Caption := 'Parsing JSON data...'; | |
PHPZipURL := GetJSONValue(JSONData, 'php_download_url'); | |
PHPVersion := GetJSONValue(JSONData, 'php_version'); | |
PHPVersion := StringReplace(PHPVersion, ' ', ''); | |
BASEVersion := GetJSONValue(JSONData, 'base_version'); | |
BASEVersionMajor := Copy(BASEVersion, 2, 1); | |
PHPZipURL := 'https://github.com/pmmp/PHP-Binaries/releases/download/pm' + BASEVersionMajor + '-php-' + PHPVersion + '-latest/PHP-' + PHPVersion + '-Windows-x64-PM' + BASEVersionMajor + '.zip'; | |
PharURL := GetJSONValue(JSONData, 'download_url'); | |
CurrentStep := CurrentStep + 1; | |
WizardForm.ProgressGauge.Position := CurrentStep; | |
if (PHPZipURL = '') or (PharURL = '') then | |
Abort; | |
// Step 3: Download PHP binary | |
WizardForm.StatusLabel.Caption := 'Downloading PHP binary...'; | |
CurrentStep := CurrentStep + 1; | |
WizardForm.ProgressGauge.Position := CurrentStep; | |
if not DownloadFile(PHPZipURL, ExpandConstant(OutputFolder + '\php.zip')) then | |
Abort; | |
// Step 4: Extract PHP binary | |
UnzipWithProgress(ExpandConstant(OutputFolder + '\php.zip'), OutputFolder); | |
CurrentStep := CurrentStep + 1; | |
WizardForm.ProgressGauge.Position := CurrentStep; | |
// Step 5: Download PocketMine-MP phar | |
WizardForm.StatusLabel.Caption := 'Downloading PocketMine-MP phar...'; | |
CurrentStep := CurrentStep + 1; | |
WizardForm.ProgressGauge.Position := CurrentStep; | |
if not DownloadFile(PharURL, OutputFolder + '\PocketMine-MP.phar') then | |
MsgBox('Failed to download PocketMine-MP.phar. You can download it manually from the PocketMine-MP GitHub repository Release page.', mbError, MB_OK); | |
// Step 6: Download start scripts | |
WizardForm.StatusLabel.Caption := 'Downloading start scripts...'; | |
CurrentStep := CurrentStep + 1; | |
WizardForm.ProgressGauge.Position := CurrentStep; | |
if not DownloadFile(START_CMD_URL, OutputFolder + '\start.cmd') then | |
MsgBox('Failed to download start.cmd. You can download it manually from the PocketMine-MP GitHub repository.', mbError, MB_OK); | |
if not DownloadFile(START_PS1_URL, OutputFolder + '\start.ps1') then | |
MsgBox('Failed to download start.ps1. You can download it manually from the PocketMine-MP GitHub repository.', mbError, MB_OK); | |
// Step 7: Download and install Visual C++ Redistributable | |
if not DownloadFile(VCRUNTIME_URL, OutputFolder + '\vc_redist.x64.exe') then | |
Abort; | |
WizardForm.StatusLabel.Caption := 'Installing Visual C++ Redistributable...'; | |
Exec(ExpandConstant(OutputFolder + '\vc_redist.x64.exe'), '/install /quiet /norestart', '', SW_HIDE, ewWaitUntilTerminated, ResultCode); | |
if ResultCode <> S_OK then | |
MsgBox('Failed to install Visual C++ Redistributable.', mbError, MB_OK); | |
CurrentStep := CurrentStep + 1; | |
WizardForm.ProgressGauge.Position := CurrentStep; | |
WizardForm.StatusLabel.Caption := 'Verifying installation...'; | |
// Step 8: Verify installation (If phar file, start scripts and PHP binary don't exist, then installation failed, else success) | |
if not FileExists(OutputFolder + '\PocketMine-MP.phar') or | |
not FileExists(OutputFolder + '\bin\php\php.exe') then | |
begin | |
MsgBox('Installation failed. Please try again.', mbError, MB_OK); | |
Abort; | |
end | |
else | |
begin | |
WizardForm.StatusLabel.Caption := 'Installation completed successfully.'; | |
end; | |
end; | |
end; | |
[Run] | |
Filename: "{app}\start.cmd"; Description: "Start PocketMine-MP"; Flags: postinstall skipifsilent |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment