bedrockplus 0.1.0.dev1__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. bedrockplus/__init__.py +95 -0
  2. bedrockplus/__main__.py +4 -0
  3. bedrockplus/_assets/Initialize-BedrockPlusServer.ps1 +395 -0
  4. bedrockplus/_assets/README.md +85 -0
  5. bedrockplus/_assets/SHA256SUMS +20 -0
  6. bedrockplus/_assets/Start-BedrockPlus.ps1 +125 -0
  7. bedrockplus/_assets/THIRD_PARTY_NOTICES.md +26 -0
  8. bedrockplus/_assets/bedrockplus.exe +0 -0
  9. bedrockplus/_assets/bedrockplus_bootstrap.dll +0 -0
  10. bedrockplus/_assets/bedrockplus_plugin_host.dll +0 -0
  11. bedrockplus/_assets/bedrockplus_plugin_worker.dll +0 -0
  12. bedrockplus/_assets/bedrockplus_runtime.dll +0 -0
  13. bedrockplus/_assets/bin/bedrockplus.ps1 +192 -0
  14. bedrockplus/_assets/compatibility/bds/1.26.51.1/windows-x64.json +24 -0
  15. bedrockplus/_assets/compatibility/bds/supported-versions.json +14 -0
  16. bedrockplus/_assets/licenses/microsoft-detours-MIT.txt +23 -0
  17. bedrockplus/_assets/plugins/README.txt +11 -0
  18. bedrockplus/_assets/release-manifest.json +34 -0
  19. bedrockplus/_assets/server/README.txt +10 -0
  20. bedrockplus/_assets/tools/bppack.py +1865 -0
  21. bedrockplus/_assets/tools/init_python_plugin.py +140 -0
  22. bedrockplus/_assets/tools/pack_plugin.py +142 -0
  23. bedrockplus/_assets/tools/world_packs.py +530 -0
  24. bedrockplus/api.py +83 -0
  25. bedrockplus/cli.py +121 -0
  26. bedrockplus/commands.py +50 -0
  27. bedrockplus/events.py +138 -0
  28. bedrockplus/objects.py +413 -0
  29. bedrockplus/py.typed +1 -0
  30. bedrockplus-0.1.0.dev1.dist-info/METADATA +41 -0
  31. bedrockplus-0.1.0.dev1.dist-info/RECORD +36 -0
  32. bedrockplus-0.1.0.dev1.dist-info/WHEEL +4 -0
  33. bedrockplus-0.1.0.dev1.dist-info/entry_points.txt +2 -0
  34. bedrockplus-0.1.0.dev1.dist-info/licenses/THIRD_PARTY_NOTICES.md +26 -0
  35. bedrockplus-0.1.0.dev1.dist-info/licenses/microsoft-detours-MIT.txt +23 -0
  36. bedrockplus-0.1.0.dev1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,95 @@
1
+ """Public, typed BedrockPlus Python plugin API."""
2
+
3
+ __version__ = "0.1.0.dev1"
4
+
5
+ from .api import EventCallback, Plugin, PluginHost, TaskCallback
6
+ from .commands import (
7
+ CommandArgument,
8
+ CommandArgumentType,
9
+ CommandCallback,
10
+ CommandInvocation,
11
+ CommandOverload,
12
+ CommandResult,
13
+ CommandReturn,
14
+ CommandSourceKind,
15
+ CommandValue,
16
+ )
17
+ from .events import (
18
+ BlockBreakEvent,
19
+ BlockPlaceEvent,
20
+ CancellableEvent,
21
+ Event,
22
+ EventName,
23
+ EventPriority,
24
+ PlayerChatEvent,
25
+ PlayerCommandFields,
26
+ PlayerCommandEvent,
27
+ PlayerJoinEvent,
28
+ PlayerJumpEvent,
29
+ PlayerKickEvent,
30
+ PlayerLoginEvent,
31
+ PlayerMoveEvent,
32
+ PlayerQuitEvent,
33
+ PlayerLifecycleFields,
34
+ ServerCommandFields,
35
+ ServerCommandEvent,
36
+ ServerListPingEvent,
37
+ ServerTickEvent,
38
+ )
39
+ from .objects import (
40
+ ConnectionInfo,
41
+ Entity,
42
+ ItemStack,
43
+ Level,
44
+ Location,
45
+ Player,
46
+ Server,
47
+ SocketAddress,
48
+ Vector3,
49
+ )
50
+
51
+ __all__ = [
52
+ "__version__",
53
+ "BlockBreakEvent",
54
+ "BlockPlaceEvent",
55
+ "CancellableEvent",
56
+ "CommandArgument",
57
+ "CommandArgumentType",
58
+ "CommandCallback",
59
+ "CommandInvocation",
60
+ "CommandOverload",
61
+ "CommandResult",
62
+ "CommandReturn",
63
+ "CommandSourceKind",
64
+ "CommandValue",
65
+ "ConnectionInfo",
66
+ "Entity",
67
+ "Event",
68
+ "EventCallback",
69
+ "EventName",
70
+ "EventPriority",
71
+ "PlayerChatEvent",
72
+ "PlayerCommandFields",
73
+ "PlayerCommandEvent",
74
+ "PlayerJoinEvent",
75
+ "PlayerJumpEvent",
76
+ "PlayerKickEvent",
77
+ "PlayerLoginEvent",
78
+ "PlayerMoveEvent",
79
+ "PlayerQuitEvent",
80
+ "PlayerLifecycleFields",
81
+ "Player",
82
+ "ItemStack",
83
+ "Level",
84
+ "Location",
85
+ "Plugin",
86
+ "PluginHost",
87
+ "ServerCommandEvent",
88
+ "ServerCommandFields",
89
+ "ServerListPingEvent",
90
+ "ServerTickEvent",
91
+ "Server",
92
+ "SocketAddress",
93
+ "TaskCallback",
94
+ "Vector3",
95
+ ]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+
4
+ raise SystemExit(main())
@@ -0,0 +1,395 @@
1
+ [CmdletBinding()]
2
+ param(
3
+ [string]$DestinationRoot = (Get-Location).Path,
4
+ [string]$BdsVersion = '',
5
+ [switch]$Yes,
6
+ [switch]$ListVersions,
7
+ [string]$ManifestPath = (Join-Path $PSScriptRoot 'compatibility\bds\supported-versions.json'),
8
+ [string]$ArchivePath = ''
9
+ )
10
+
11
+ Set-StrictMode -Version Latest
12
+ $ErrorActionPreference = 'Stop'
13
+
14
+ function Set-HiddenDirectory {
15
+ param([Parameter(Mandatory)][string]$Path)
16
+
17
+ $item = Get-Item -LiteralPath $Path -Force
18
+ $item.Attributes = $item.Attributes -bor [IO.FileAttributes]::Hidden
19
+ }
20
+
21
+ function Get-Sha256 {
22
+ param([Parameter(Mandatory)][string]$Path)
23
+
24
+ $stream = [IO.File]::OpenRead($Path)
25
+ $hasher = [Security.Cryptography.SHA256]::Create()
26
+ try {
27
+ return ([BitConverter]::ToString($hasher.ComputeHash($stream))).Replace('-', '').ToLowerInvariant()
28
+ } finally {
29
+ $hasher.Dispose()
30
+ $stream.Dispose()
31
+ }
32
+ }
33
+
34
+ function Invoke-BdsDownload {
35
+ param(
36
+ [Parameter(Mandatory)][string]$Uri,
37
+ [Parameter(Mandatory)][string]$Destination,
38
+ [Parameter(Mandatory)][string]$Version
39
+ )
40
+
41
+ Add-Type -AssemblyName System.Net.Http
42
+ $client = [Net.Http.HttpClient]::new()
43
+ $response = $null
44
+ $input = $null
45
+ $output = $null
46
+ try {
47
+ $response = $client.GetAsync(
48
+ $Uri,
49
+ [Net.Http.HttpCompletionOption]::ResponseHeadersRead
50
+ ).GetAwaiter().GetResult()
51
+ $response.EnsureSuccessStatusCode()
52
+ $total = $response.Content.Headers.ContentLength
53
+ $input = $response.Content.ReadAsStreamAsync().GetAwaiter().GetResult()
54
+ $output = [IO.File]::Open($Destination, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
55
+ $buffer = [byte[]]::new(1024 * 1024)
56
+ [long]$received = 0
57
+ while (($count = $input.Read($buffer, 0, $buffer.Length)) -gt 0) {
58
+ $output.Write($buffer, 0, $count)
59
+ $received += $count
60
+ if ($total -and $total -gt 0) {
61
+ $percent = [Math]::Min(100, [int](($received * 100) / $total))
62
+ Write-Progress -Activity "Downloading BDS $Version" -Status "$percent% ($([Math]::Round($received / 1MB, 1)) / $([Math]::Round($total / 1MB, 1)) MiB)" -PercentComplete $percent
63
+ } else {
64
+ Write-Progress -Activity "Downloading BDS $Version" -Status "$([Math]::Round($received / 1MB, 1)) MiB received" -PercentComplete 0
65
+ }
66
+ }
67
+ } finally {
68
+ Write-Progress -Activity "Downloading BDS $Version" -Completed
69
+ if ($output) { $output.Dispose() }
70
+ if ($input) { $input.Dispose() }
71
+ if ($response) { $response.Dispose() }
72
+ $client.Dispose()
73
+ }
74
+ }
75
+
76
+ function Expand-BdsArchive {
77
+ param(
78
+ [Parameter(Mandatory)][string]$Path,
79
+ [Parameter(Mandatory)][string]$Destination,
80
+ [Parameter(Mandatory)][string]$Version
81
+ )
82
+
83
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
84
+ $archive = [IO.Compression.ZipFile]::OpenRead($Path)
85
+ try {
86
+ $total = $archive.Entries.Count
87
+ for ($index = 0; $index -lt $total; $index++) {
88
+ $entry = $archive.Entries[$index]
89
+ $target = [IO.Path]::GetFullPath((Join-Path $Destination $entry.FullName.Replace('/', '\')))
90
+ if ([string]::IsNullOrEmpty($entry.Name)) {
91
+ New-Item -ItemType Directory -Force -Path $target | Out-Null
92
+ } else {
93
+ $parent = Split-Path -Parent $target
94
+ if (-not (Test-Path -LiteralPath $parent -PathType Container)) {
95
+ New-Item -ItemType Directory -Force -Path $parent | Out-Null
96
+ }
97
+ [IO.Compression.ZipFileExtensions]::ExtractToFile($entry, $target, $false)
98
+ }
99
+ $percent = [int]((($index + 1) * 100) / [Math]::Max(1, $total))
100
+ Write-Progress -Activity "Installing BDS $Version" -Status "$percent% ($($index + 1) of $total files)" -PercentComplete $percent
101
+ }
102
+ } finally {
103
+ Write-Progress -Activity "Installing BDS $Version" -Completed
104
+ $archive.Dispose()
105
+ }
106
+ }
107
+
108
+ function Assert-SafeArchive {
109
+ param(
110
+ [Parameter(Mandatory)][string]$Path,
111
+ [Parameter(Mandatory)][string]$ExtractionRoot
112
+ )
113
+
114
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
115
+ $root = [IO.Path]::GetFullPath($ExtractionRoot).TrimEnd('\') + '\'
116
+ $archive = [IO.Compression.ZipFile]::OpenRead($Path)
117
+ try {
118
+ if ($archive.Entries.Count -eq 0 -or $archive.Entries.Count -gt 20000) {
119
+ throw 'The BDS archive has an invalid number of entries.'
120
+ }
121
+ foreach ($entry in $archive.Entries) {
122
+ $name = $entry.FullName.Replace('/', '\')
123
+ if ([string]::IsNullOrWhiteSpace($name) -or
124
+ [IO.Path]::IsPathRooted($name) -or
125
+ $name.Contains(':') -or
126
+ $name.Contains([char]0)) {
127
+ throw "The BDS archive contains an unsafe path: $name"
128
+ }
129
+ $segments = @($name.Split('\') | Where-Object { $_ -ne '' })
130
+ if ($segments.Count -eq 0 -or $segments -contains '.' -or $segments -contains '..') {
131
+ throw "The BDS archive contains an unsafe path: $name"
132
+ }
133
+ $target = [IO.Path]::GetFullPath((Join-Path $ExtractionRoot $name))
134
+ if (-not $target.StartsWith($root, [StringComparison]::OrdinalIgnoreCase)) {
135
+ throw "The BDS archive path escapes its staging directory: $name"
136
+ }
137
+ $unixType = (($entry.ExternalAttributes -shr 16) -band 0xF000)
138
+ if ($unixType -eq 0xA000) {
139
+ throw "The BDS archive contains a symbolic link: $name"
140
+ }
141
+ }
142
+ } finally {
143
+ $archive.Dispose()
144
+ }
145
+ }
146
+
147
+ function Set-ServerProperties {
148
+ param([Parameter(Mandatory)][string]$Path)
149
+
150
+ $properties = [IO.File]::ReadAllText($Path)
151
+ foreach ($setting in @(
152
+ @('server-name', 'BedrockPlus'),
153
+ @('level-name', 'BedrockPlus')
154
+ )) {
155
+ $key = $setting[0]
156
+ $value = $setting[1]
157
+ $pattern = "(?m)^$([Text.RegularExpressions.Regex]::Escape($key))=.*$"
158
+ if ($properties -match $pattern) {
159
+ $properties = [Text.RegularExpressions.Regex]::Replace(
160
+ $properties,
161
+ $pattern,
162
+ "$key=$value"
163
+ )
164
+ } else {
165
+ $properties = $properties.TrimEnd("`r", "`n") + "`r`n$key=$value`r`n"
166
+ }
167
+ }
168
+ [IO.File]::WriteAllText($Path, $properties, [Text.UTF8Encoding]::new($false))
169
+ }
170
+
171
+ function Write-BedrockPlusSettings {
172
+ param([Parameter(Mandatory)][string]$Path)
173
+
174
+ $settings = @'
175
+ ; BedrockPlus settings. server.properties remains owned by vanilla BDS.
176
+ [general]
177
+ channel=stable
178
+ auto-update=true
179
+
180
+ ; These switches apply equally to the console and human-readable .log files.
181
+ ; Full structured metadata is always retained in the parallel .jsonl log.
182
+ [logging]
183
+ prefix-timestamp=true
184
+ timestamp-pattern=[{year}-{month}-{day}T{hour}:{minute}:{second}.{millisecond}Z]
185
+ prefix-level=true
186
+ prefix-source=true
187
+ fields=false
188
+ '@
189
+ [IO.File]::WriteAllText($Path, $settings.TrimStart() + "`r`n", [Text.UTF8Encoding]::new($false))
190
+ }
191
+
192
+ if (-not (Test-Path -LiteralPath $ManifestPath -PathType Leaf)) {
193
+ throw "Supported-version manifest was not found: $ManifestPath"
194
+ }
195
+ $manifest = Get-Content -LiteralPath $ManifestPath -Raw | ConvertFrom-Json
196
+ if ($manifest.format_version -ne 1 -or -not $manifest.versions) {
197
+ throw 'The supported-version manifest is invalid.'
198
+ }
199
+
200
+ if ($ListVersions) {
201
+ foreach ($entry in $manifest.versions) {
202
+ $default = if ($entry.version -eq $manifest.default_version) { ' (default)' } else { '' }
203
+ Write-Output "$($entry.version) [$($entry.channel), $($entry.platform)]$default"
204
+ }
205
+ exit 0
206
+ }
207
+
208
+ if ([string]::IsNullOrWhiteSpace($BdsVersion)) {
209
+ if (-not $Yes) {
210
+ Write-Host 'BDS versions supported by this BedrockPlus build:'
211
+ foreach ($entry in $manifest.versions) {
212
+ $default = if ($entry.version -eq $manifest.default_version) { ' (default)' } else { '' }
213
+ Write-Host " $($entry.version) [$($entry.channel)]$default"
214
+ }
215
+ if (@($manifest.versions).Count -gt 1) {
216
+ $answer = Read-Host "Select BDS version [$($manifest.default_version)]"
217
+ if (-not [string]::IsNullOrWhiteSpace($answer)) {
218
+ $BdsVersion = $answer.Trim()
219
+ }
220
+ }
221
+ }
222
+ if ([string]::IsNullOrWhiteSpace($BdsVersion)) {
223
+ $BdsVersion = [string]$manifest.default_version
224
+ }
225
+ }
226
+ $matches = @($manifest.versions | Where-Object { $_.version -eq $BdsVersion })
227
+ if ($matches.Count -ne 1) {
228
+ $supported = @($manifest.versions | ForEach-Object { $_.version }) -join ', '
229
+ throw "BDS $BdsVersion is not supported by this BedrockPlus build. Supported: $supported"
230
+ }
231
+ $selected = $matches[0]
232
+ if ($selected.platform -ne 'windows-x64' -or
233
+ $selected.download_url -notmatch '^https://www\.minecraft\.net/' -or
234
+ $selected.archive_sha256 -notmatch '^[0-9a-fA-F]{64}$') {
235
+ throw 'The selected BDS manifest entry is invalid or uses an unapproved download host.'
236
+ }
237
+
238
+ $DestinationRoot = [IO.Path]::GetFullPath($DestinationRoot).TrimEnd('\')
239
+ if ($DestinationRoot -split '[\\/]' -contains 'referenceOnly') {
240
+ throw 'A BedrockPlus server cannot be initialized inside referenceOnly.'
241
+ }
242
+ $downloadsRoot = Join-Path $DestinationRoot 'Downloads'
243
+ $archiveName = "bedrock-server-$($selected.version).zip"
244
+ $cachedArchive = Join-Path $downloadsRoot $archiveName
245
+ if (Test-Path -LiteralPath $DestinationRoot) {
246
+ if (-not (Test-Path -LiteralPath $DestinationRoot -PathType Container)) {
247
+ throw "DestinationRoot is not a directory: $DestinationRoot"
248
+ }
249
+ $existingItems = @(Get-ChildItem -LiteralPath $DestinationRoot -Force)
250
+ $unexpectedItems = @($existingItems | Where-Object { $_.Name -cne 'Downloads' })
251
+ if ($unexpectedItems.Count -ne 0) {
252
+ throw "DestinationRoot must be empty except for the managed Downloads cache; no files were changed: $DestinationRoot"
253
+ }
254
+ if ($existingItems.Count -eq 1) {
255
+ $downloadsItem = $existingItems[0]
256
+ if (-not $downloadsItem.PSIsContainer -or
257
+ ($downloadsItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
258
+ throw "The Downloads cache must be a normal local directory: $downloadsRoot"
259
+ }
260
+ foreach ($item in Get-ChildItem -LiteralPath $downloadsRoot -Force) {
261
+ if ($item.PSIsContainer -or
262
+ ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
263
+ $item.Name -notmatch '^bedrock-server-[0-9A-Za-z.+-]+\.zip$') {
264
+ throw "The Downloads cache contains an unmanaged entry; no files were changed: $($item.FullName)"
265
+ }
266
+ }
267
+ }
268
+ } else {
269
+ New-Item -ItemType Directory -Path $DestinationRoot | Out-Null
270
+ }
271
+
272
+ if (-not $Yes) {
273
+ Write-Host "BedrockPlus supports BDS $($selected.version) ($($selected.channel))."
274
+ Write-Host 'The official BDS archive will be downloaded from minecraft.net.'
275
+ Write-Host 'By continuing, you confirm that you accept the applicable Minecraft server terms.'
276
+ $answer = Read-Host "Install it into '$DestinationRoot'? [Y/n]"
277
+ if (-not [string]::IsNullOrWhiteSpace($answer) -and
278
+ $answer -notin @('y', 'Y', 'yes', 'YES', 'Yes')) {
279
+ Write-Host 'Initialization cancelled; no server files were installed.'
280
+ exit 2
281
+ }
282
+ }
283
+
284
+ $temporaryRoot = Join-Path ([IO.Path]::GetTempPath()) ("bedrockplus-init-" + [Guid]::NewGuid().ToString('N'))
285
+ $download = Join-Path $temporaryRoot $archiveName
286
+ $staging = Join-Path $temporaryRoot 'extracted'
287
+ $installedPaths = [Collections.Generic.List[string]]::new()
288
+ New-Item -ItemType Directory -Path $temporaryRoot, $staging | Out-Null
289
+
290
+ try {
291
+ if ([string]::IsNullOrWhiteSpace($ArchivePath)) {
292
+ $useCachedArchive = $false
293
+ if (Test-Path -LiteralPath $cachedArchive -PathType Leaf) {
294
+ $cachedHash = Get-Sha256 -Path $cachedArchive
295
+ $expectedHash = ([string]$selected.archive_sha256).ToLowerInvariant()
296
+ if ($cachedHash -eq $expectedHash) {
297
+ Write-Host "Using verified cached BDS $($selected.version) from Downloads."
298
+ Copy-Item -LiteralPath $cachedArchive -Destination $download
299
+ $useCachedArchive = $true
300
+ } else {
301
+ Write-Warning "The cached BDS $($selected.version) archive failed verification and will be replaced."
302
+ }
303
+ }
304
+ if (-not $useCachedArchive) {
305
+ Write-Host "Downloading official BDS $($selected.version)..."
306
+ Invoke-BdsDownload -Uri $selected.download_url -Destination $download -Version $selected.version
307
+ }
308
+ } else {
309
+ Copy-Item -LiteralPath ([IO.Path]::GetFullPath($ArchivePath)) -Destination $download
310
+ }
311
+
312
+ $actualHash = Get-Sha256 -Path $download
313
+ $expectedHash = ([string]$selected.archive_sha256).ToLowerInvariant()
314
+ if ($actualHash -ne $expectedHash) {
315
+ throw "BDS archive verification failed. Expected $expectedHash but received $actualHash."
316
+ }
317
+
318
+ Assert-SafeArchive -Path $download -ExtractionRoot $staging
319
+ Expand-BdsArchive -Path $download -Destination $staging -Version $selected.version
320
+
321
+ $serverExecutable = Join-Path $staging 'bedrock_server.exe'
322
+ $serverProperties = Join-Path $staging 'server.properties'
323
+ if (-not (Test-Path -LiteralPath $serverExecutable -PathType Leaf) -or
324
+ -not (Test-Path -LiteralPath $serverProperties -PathType Leaf)) {
325
+ throw 'The verified archive does not contain a complete Windows BDS layout.'
326
+ }
327
+
328
+ Set-ServerProperties -Path $serverProperties
329
+ Write-BedrockPlusSettings -Path (Join-Path $staging 'BedrockPlus.ini')
330
+ [IO.File]::WriteAllText(
331
+ (Join-Path $staging 'allowlist.json'),
332
+ "[]`r`n",
333
+ [Text.UTF8Encoding]::new($false)
334
+ )
335
+ [IO.File]::WriteAllText(
336
+ (Join-Path $staging 'permissions.json'),
337
+ "[]`r`n",
338
+ [Text.UTF8Encoding]::new($false)
339
+ )
340
+ New-Item -ItemType Directory -Force -Path `
341
+ (Join-Path $staging 'plugins'), `
342
+ (Join-Path $staging 'logs'), `
343
+ (Join-Path $staging 'Downloads'), `
344
+ (Join-Path $staging '.bedrockplus'), `
345
+ (Join-Path $staging '.bedrockplus\plugin-data') | Out-Null
346
+ Move-Item -LiteralPath $download -Destination (Join-Path $staging "Downloads\$archiveName")
347
+
348
+ [ordered]@{
349
+ product = 'BedrockPlus'
350
+ format_version = 1
351
+ bds_version = [string]$selected.version
352
+ bds_channel = [string]$selected.channel
353
+ created_by = 'Turkmen'
354
+ created_at_utc = [DateTime]::UtcNow.ToString('o')
355
+ } | ConvertTo-Json | Set-Content -LiteralPath `
356
+ (Join-Path $staging 'bedrockplus.instance.json') -Encoding UTF8
357
+
358
+ $installItems = @(Get-ChildItem -LiteralPath $staging -Force)
359
+ for ($index = 0; $index -lt $installItems.Count; $index++) {
360
+ $item = $installItems[$index]
361
+ if ($item.Name -ceq 'Downloads' -and
362
+ (Test-Path -LiteralPath $downloadsRoot -PathType Container)) {
363
+ foreach ($archiveItem in Get-ChildItem -LiteralPath $item.FullName -Force) {
364
+ Move-Item -LiteralPath $archiveItem.FullName -Destination $downloadsRoot -Force
365
+ }
366
+ Remove-Item -LiteralPath $item.FullName -Force
367
+ } else {
368
+ Move-Item -LiteralPath $item.FullName -Destination $DestinationRoot
369
+ $installedPaths.Add((Join-Path $DestinationRoot $item.Name))
370
+ }
371
+ $percent = [int]((($index + 1) * 100) / [Math]::Max(1, $installItems.Count))
372
+ Write-Progress -Activity 'Finalizing BedrockPlus server' -Status "$percent%" -PercentComplete $percent
373
+ }
374
+ Write-Progress -Activity 'Finalizing BedrockPlus server' -Completed
375
+ Set-HiddenDirectory -Path (Join-Path $DestinationRoot '.bedrockplus')
376
+ } catch {
377
+ # Remove only entries successfully moved by this initializer. A file created
378
+ # concurrently by the operator is never included in this rollback list.
379
+ foreach ($path in $installedPaths) {
380
+ Remove-Item -LiteralPath $path -Recurse -Force -ErrorAction SilentlyContinue
381
+ }
382
+ throw
383
+ } finally {
384
+ $tempBase = [IO.Path]::GetFullPath([IO.Path]::GetTempPath()).TrimEnd('\') + '\'
385
+ $resolvedTemporary = [IO.Path]::GetFullPath($temporaryRoot)
386
+ if ($resolvedTemporary.StartsWith($tempBase, [StringComparison]::OrdinalIgnoreCase) -and
387
+ (Split-Path $resolvedTemporary -Leaf).StartsWith('bedrockplus-init-', [StringComparison]::Ordinal)) {
388
+ Remove-Item -LiteralPath $resolvedTemporary -Recurse -Force -ErrorAction SilentlyContinue
389
+ }
390
+ }
391
+
392
+ Write-Host "Created BedrockPlus server: $DestinationRoot"
393
+ Write-Host "BDS version: $($selected.version)"
394
+ Write-Host "BDS archive: $(Join-Path $DestinationRoot "Downloads\$archiveName")"
395
+ Write-Host "Plugin inbox: $(Join-Path $DestinationRoot 'plugins')"
@@ -0,0 +1,85 @@
1
+ # BedrockPlus Windows x64 pre-release
2
+
3
+ BedrockPlus is a BDS-native server and plugin platform created by Turkmen. This
4
+ package supports the exact BDS version recorded in `release-manifest.json` and
5
+ contains the BedrockPlus console launcher, runtime, Python and C++ plugin host,
6
+ plugin packaging tools, server initializer, documentation, and required legal
7
+ notices.
8
+
9
+ The package does **not** contain Minecraft Bedrock Dedicated Server, worlds,
10
+ plugins, private source code, debug symbols, signing keys, build directories,
11
+ or private development files. BDS is downloaded from the official source only
12
+ after the operator accepts the applicable Minecraft server terms.
13
+
14
+ This is a pre-release. Back up worlds before testing and use only the listed BDS
15
+ version. Python plugins require 64-bit CPython 3.13, which is also the supported
16
+ Python version for installing the BedrockPlus command package.
17
+
18
+ ## Installed commands
19
+
20
+ After installing the public Python wheel, open Command Prompt in any directory:
21
+
22
+ ```text
23
+ bedrockplus
24
+ bedrockplus init
25
+ bedrockplus start
26
+ bedrockplus doctor
27
+ bedrockplus list-versions
28
+ bedrockplus plugin python init "My Plugin"
29
+ bedrockplus plugin pack
30
+ ```
31
+
32
+ Running `bedrockplus` in a fresh directory offers to install the supported BDS
33
+ version with Yes as the default. The verified official archive is retained in
34
+ the server-root `Downloads` directory. A matching archive already stored there
35
+ is verified and reused without another download.
36
+
37
+ ## Release contents
38
+
39
+ The archive is assembled from an explicit allowlist and contains only:
40
+
41
+ - release BedrockPlus executables and DLLs;
42
+ - the command, initializer, launcher, and plugin packaging scripts;
43
+ - supported-version and compatibility data;
44
+ - this guide and third-party notices;
45
+ - a generated payload manifest and SHA-256 checksum list.
46
+
47
+ `release-manifest.json` records every distributed payload with its role, exact
48
+ byte size, and SHA-256. `SHA256SUMS` covers the payload and manifest. The outer
49
+ ZIP has a separate SHA-256 file. Do not use a package whose verification fails.
50
+
51
+ ## Manual ZIP usage
52
+
53
+ The Python wheel is the recommended installation. For manual testing:
54
+
55
+ 1. Install 64-bit CPython 3.13.
56
+ 2. Extract this release to a new local directory.
57
+ 3. Open Command Prompt in the directory where the server should be created.
58
+ 4. Run the release's `bin\bedrockplus.ps1` through PowerShell.
59
+ 5. Confirm the official BDS installation and start the server.
60
+
61
+ Keep `bedrock_server.exe`, `server.properties`, `BedrockPlus.ini`, `plugins`,
62
+ `Downloads`, `worlds`, and `logs` together in one server directory. BedrockPlus
63
+ keeps private runtime state and installed plugin contents below the hidden
64
+ `.bedrockplus` directory.
65
+
66
+ ## Publisher build and validation
67
+
68
+ Use Visual Studio 2022 and the locked project-local environment:
69
+
70
+ ```powershell
71
+ . .\toolchain\enter-dev-shell.ps1
72
+ pwsh -NoProfile -File .\toolchain\package-release.ps1
73
+ ```
74
+
75
+ The packaging task builds Release configuration, runs the complete test suite,
76
+ creates the allowlisted ZIP and checksum, extracts it into an isolated staging
77
+ directory, and verifies its exact contents. Artifacts are written below:
78
+
79
+ ```text
80
+ out/build/windows-vs2022-release/packages/
81
+ ```
82
+
83
+ The public Python wheel is built separately from the verified runtime package.
84
+ Only the wheel is uploaded to PyPI; do not upload the source distribution or
85
+ the private repository.
@@ -0,0 +1,20 @@
1
+ 21b26e40e80158a29915435597c1e5cd507ffa505203a48f27f548212e2cf477 Initialize-BedrockPlusServer.ps1
2
+ 337a506531315df1960cd422bf5ba6242e6d56349d52c6d03d7764f7f58487ae README.md
3
+ 30d710f7a7a008bf95d051d59514f7b15e737f27ca15a9a053f08b7fcd6f2091 Start-BedrockPlus.ps1
4
+ 30cc67398bd5edf60256d54cad96abac79c35d8fb1dbbefaf1148c6cd2297f7f THIRD_PARTY_NOTICES.md
5
+ f77ce2bfa371e75a8f3429c4468ea29dae634e8a57a820c695df579ecbcb16d8 bedrockplus.exe
6
+ 66c97116e32d1aa903eff8ea84794d52f87e9f29eb9e54c2d9526c533174a314 bedrockplus_bootstrap.dll
7
+ 54a7cd58f4ec4ade088c1d441e304d16d6ea9fad0ae7844b12d20efb219164c7 bedrockplus_plugin_host.dll
8
+ 85bf9440f7b831348d69415ec34a744303104e7a6c201f582e36b83cafa1b8f8 bedrockplus_plugin_worker.dll
9
+ 54dec12091e8189cbe1c4b2a229732dfe733a9f7903f3659f73c9af8b0dc9d63 bedrockplus_runtime.dll
10
+ 526f5d583a52bd0401453652e18e972674e36e59af504958648a62d3560112b8 bin/bedrockplus.ps1
11
+ cd2116d1469e669219278c682aa04ae13316229c7f81c404b87b397286f96ae1 compatibility/bds/1.26.51.1/windows-x64.json
12
+ a4de32d6b8a41436df3f8e80a510f78219c64dc45fdbe0e0bd7122e0e927d274 compatibility/bds/supported-versions.json
13
+ 16e3c9cdb4fa14a8627bc4b5ef0237773a79c3ad1d012c288f37e29573e116cc licenses/microsoft-detours-MIT.txt
14
+ 9b4d42a12794e6c0df73785ccd2e5f97c784c3c419f9ad2d503ce5f3506064b6 plugins/README.txt
15
+ b4c08f5cfe58c11f0079b21b31329e3aecfb542d3c532b31825cde5d746c87e1 release-manifest.json
16
+ 37f068a582e198da3632dbf78a07cfd2271472beddc4f9dce6c3b818410a76eb server/README.txt
17
+ e8b893d568602ae09814a08156e59bad060b7d797b59f7966eee6a229ea5f38f tools/bppack.py
18
+ 9f0eee45ef8d73c2d2ede002b2245892270fc9d70b8359bf2bedee136abf3f63 tools/init_python_plugin.py
19
+ db4fdf82921e39763f6a024bdf9d27fa0eea92edf3cf6946377c832635b8712b tools/pack_plugin.py
20
+ c0508000cb545ed1735032609729eeb99ea64a89ac4dc2fc7380a8bc349c9cfb tools/world_packs.py