Fixes and enhancements from my MCPE repository. (https://github.com/mschiller890/mcpe64)

This commit is contained in:
Michal Schiller
2026-03-10 19:31:40 +01:00
parent 0c97ceb340
commit 2fc323639a
69 changed files with 2509 additions and 639 deletions

201
.gitignore vendored
View File

@@ -1,3 +1,202 @@
# Build output
build/
linux-build/
out/
bin/
lib/
cmake-build-*/
CMakeFiles/
CMakeCache.txt
cmake_install.cmake
Makefile
*.cmake
!cmake/CPM.cmake
# Compiled object files
*.o
*.obj
*.a
*.lib
*.so
*.dylib
*.dll
*.exe
*.out
# CMake generated
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_CPack_Packages/
*.cbp
# Visual Studio
*.sdf
*.suo
*.user
*.userosscache
*.sln.docstates
*.VC.db
*.VC.opendb
.vs/
ipch/
x64/
x86/
Debug/
Release/
RelWithDebInfo/
MinSizeRel/
# Xcode
*.xcworkspace
xcuserdata/
*.xccheckout
*.moved-aside
DerivedData/
*.hmap
*.ipa
*.dSYM.zip
*.dSYM
# Android
*.apk
*.aab
*.ap_
local.properties
.gradle/
project/.gradle/
project/android/bin/
project/android/gen/
project/android/obj/
project/android_java/bin/
project/android_java/gen/
project/android_java/obj/
project/android/libs/*.so
# macOS
.DS_Store
.AppleDouble
.LSOverride
._*
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
# Linux
*~
.nfs*
# CLion / JetBrains
.idea/
*.iml
*.ipr
*.iws
# Eclipse CDT
.project
.cproject
.settings/
# Logs
*.log
# Ninja
.ninja_deps
.ninja_log
build.ninja
# VS Code
.vscode/
*.code-workspace
# Visual Studio additional
*.ncb
*.aps
*.pdb
*.ilk
*.exp
*.iobj
*.ipdb
*.tlog
*.lastbuildstate
*.pch
*.ipch
*.VC.db-shm
*.VC.db-wal
*.svclog
*.vcxproj.user
# CMake generated project files
ALL_BUILD.vcxproj
ALL_BUILD.vcxproj.filters
INSTALL.vcxproj
INSTALL.vcxproj.filters
PACKAGE.vcxproj
PACKAGE.vcxproj.filters
ZERO_CHECK.vcxproj
ZERO_CHECK.vcxproj.filters
MinecraftPE.vcxproj
MinecraftPE.vcxproj.filters
MinecraftPE.sln
CPackConfig.cmake
CPackSourceConfig.cmake
cpm-package-lock.cmake
MinecraftPE.dir/
_deps/
# Test / coverage
Testing/
CTestResults/
*.gcno
*.gcda
*.gcov
coverage/
lcov.info
# Precompiled headers
*.gch
# Compiler / linker intermediates
*.map
*.res
*.d
# Patch / diff leftovers
*.orig
*.rej
*.patch
# Backup / temp
*.bak
*.swp
*.swo
*.tmp
*~.nib
# Tags
tags
TAGS
.tags
ctags
# macOS extra
.Spotlight-V100
.Trashes
.fseventsd
Icon\r
# Windows extra
*.lnk
[Dd]esktop.ini
# User / local config
options.txt
local.cmake
games/
# Dependency cache
vcpkg_installed/
.cache/
conan/

View File

@@ -136,6 +136,13 @@ if(PLATFORM STREQUAL "PLATFORM_GLFW")
list(APPEND SOURCES "src/AppPlatform_glfw.cpp")
endif()
# Explicitly list files added after the initial glob so they are always included
list(APPEND SOURCES
"src/client/gui/screens/ConsoleScreen.cpp"
"src/client/gui/screens/UsernameScreen.cpp"
"src/client/gui/screens/CreditsScreen.cpp"
)
add_executable(${PROJECT_NAME}
${SOURCES}
"glad/src/glad.c"

302
build.ps1 Normal file
View File

@@ -0,0 +1,302 @@
# ============================================================
# MCPE 0.4.0 Android Build Script — from-scratch capable
# Works on a clean machine; creates all dirs, keystore and
# stub Java files automatically.
#
# Usage:
# .\build.ps1 # full build (NDK + Java + APK + install)
# .\build.ps1 -NoCpp # skip NDK rebuild (Java/assets changed)
# .\build.ps1 -NoJava # skip Java recompile (C++ changed only)
# .\build.ps1 -NoBuild # repackage + install only (no recompile)
# ============================================================
param(
[switch]$NoCpp,
[switch]$NoJava,
[switch]$NoBuild
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
# ── Paths ────────────────────────────────────────────────────
$repo = $PSScriptRoot
$apkbuild = "C:\apkbuild"
$ndk = "C:\android-ndk-r14b"
$sdkTools = "$env:LOCALAPPDATA\Android\Sdk\build-tools\35.0.0"
$androidJar = "$env:LOCALAPPDATA\Android\Sdk\platforms\android-36\android.jar"
$adb = "$env:LOCALAPPDATA\Android\Sdk\platform-tools\adb.exe"
$keystore = "$apkbuild\debug.keystore"
$pkg = "com.mojang.minecraftpe"
# Auto-detect keytool from JAVA_HOME, then from common install locations
$keytool = if ($env:JAVA_HOME) { "$env:JAVA_HOME\bin\keytool.exe" } else {
$found = Get-ChildItem "C:\Program Files\Java","C:\Program Files\Eclipse Adoptium" `
-Filter keytool.exe -Recurse -ErrorAction SilentlyContinue |
Sort-Object FullName -Descending | Select-Object -First 1 -ExpandProperty FullName
if (-not $found) { throw "keytool not found. Set JAVA_HOME or install a JDK." }
$found
}
$jniDir = "$repo\project\android\jni"
$libSrc = "$repo\project\android\libs\arm64-v8a\libminecraftpe.so"
$libDst = "$apkbuild\lib\arm64-v8a\libminecraftpe.so"
$manifest = "$repo\project\android_java\AndroidManifest.xml"
$res = "$repo\project\android_java\res"
$javaSrc = "$repo\project\android_java\src"
$stubsDir = "$apkbuild\stubs"
$rJava = "$apkbuild\gen\R.java"
$classesDir = "$apkbuild\classes"
$dexOut = "$apkbuild\classes.dex"
$dataDir = "$repo\data"
$unsigned = "$apkbuild\minecraftpe-unsigned.apk"
$aligned = "$apkbuild\minecraftpe-aligned.apk"
$signed = "$apkbuild\minecraftpe-debug.apk"
Add-Type -Assembly "System.IO.Compression.FileSystem"
function Write-Step([string]$msg) { Write-Host "`n==> $msg" -ForegroundColor Cyan }
function Assert-ExitCode([string]$step) {
if ($LASTEXITCODE -ne 0) { Write-Host "FAILED: $step (exit $LASTEXITCODE)" -ForegroundColor Red; exit 1 }
}
function New-Dir([string]$path) { New-Item $path -ItemType Directory -Force | Out-Null }
function Write-Stub([string]$rel, [string]$content) {
$full = "$stubsDir\$rel"
New-Dir (Split-Path $full -Parent)
if (-not (Test-Path $full)) { [System.IO.File]::WriteAllText($full, $content); Write-Host " stub: $rel" }
}
# ── 0. Bootstrap ─────────────────────────────────────────────
Write-Step "Bootstrap"
New-Dir $apkbuild
New-Dir "$apkbuild\lib\arm64-v8a"
New-Dir "$apkbuild\gen"
New-Dir $stubsDir
if (-not (Test-Path $keystore)) {
Write-Host " generating debug.keystore..."
$eap = $ErrorActionPreference; $ErrorActionPreference = "Continue"
& $keytool -genkeypair `
-keystore $keystore -storepass android -keypass android `
-alias androiddebugkey -keyalg RSA -keysize 2048 -validity 10000 `
-dname "CN=Android Debug,O=Android,C=US" 2>&1 | Out-Null
$ErrorActionPreference = $eap
Assert-ExitCode "keytool"
Write-Host " keystore created"
} else { Write-Host " keystore OK" }
Write-Stub "com\mojang\android\StringValue.java" "package com.mojang.android;`npublic interface StringValue { String getStringValue(); }`n"
Write-Stub "com\mojang\android\licensing\LicenseCodes.java" "package com.mojang.android.licensing;`npublic class LicenseCodes { public static final int LICENSE_OK = 0; }`n"
Write-Stub "com\mojang\android\EditTextAscii.java" @"
package com.mojang.android;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.widget.EditText;
public class EditTextAscii extends EditText implements TextWatcher {
public EditTextAscii(Context c) { super(c); addTextChangedListener(this); }
public EditTextAscii(Context c, AttributeSet a) { super(c,a); addTextChangedListener(this); }
public EditTextAscii(Context c, AttributeSet a, int d) { super(c,a,d); addTextChangedListener(this); }
@Override public void onTextChanged(CharSequence s,int st,int b,int co){}
public void beforeTextChanged(CharSequence s,int st,int co,int aft){}
public void afterTextChanged(Editable e){
String s=e.toString(),san=sanitize(s);
if(!s.equals(san))e.replace(0,e.length(),san);
}
static public String sanitize(String s){
StringBuilder sb=new StringBuilder();
for(int i=0;i<s.length();i++){char c=s.charAt(i);if(c<128)sb.append(c);}
return sb.toString();
}
}
"@
Write-Stub "com\mojang\android\preferences\SliderPreference.java" @"
package com.mojang.android.preferences;
import android.content.Context;
import android.content.res.Resources;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.SeekBar;
import android.widget.TextView;
public class SliderPreference extends DialogPreference implements SeekBar.OnSeekBarChangeListener {
private static final String NS="http://schemas.android.com/apk/res/android";
private Context _ctx; private TextView _tv; private SeekBar _sb;
private String _suf; private int _def,_max,_val,_min;
public SliderPreference(Context ctx,AttributeSet a){
super(ctx,a); _ctx=ctx;
_suf=gStr(a,NS,"text",""); _def=gInt(a,NS,"defaultValue",0);
_max=gInt(a,NS,"max",100); _min=gInt(a,null,"min",0);
setDefaultValue(_def);
}
@Override protected View onCreateDialogView(){
LinearLayout l=new LinearLayout(_ctx); l.setOrientation(LinearLayout.VERTICAL); l.setPadding(6,6,6,6);
_tv=new TextView(_ctx); _tv.setGravity(Gravity.CENTER_HORIZONTAL); _tv.setTextSize(32);
l.addView(_tv,new LinearLayout.LayoutParams(-1,-2));
_sb=new SeekBar(_ctx); _sb.setOnSeekBarChangeListener(this);
l.addView(_sb,new LinearLayout.LayoutParams(-1,-2));
if(shouldPersist())_val=getPersistedInt(_def);
_sb.setMax(_max); _sb.setProgress(_val); return l;
}
@Override protected void onSetInitialValue(boolean r,Object d){
super.onSetInitialValue(r,d);
_val=r?(shouldPersist()?getPersistedInt(_def):0):(Integer)d;
}
public void onProgressChanged(SeekBar s,int v,boolean f){
_val=v+_min; _tv.setText(_val+_suf);
if(shouldPersist())persistInt(_val); callChangeListener(Integer.valueOf(_val));
}
public void onStartTrackingTouch(SeekBar s){}
public void onStopTrackingTouch(SeekBar s){}
private int gInt(AttributeSet a,String ns,String n,int d){int id=a.getAttributeResourceValue(ns,n,0);return id!=0?getContext().getResources().getInteger(id):a.getAttributeIntValue(ns,n,d);}
private String gStr(AttributeSet a,String ns,String n,String d){int id=a.getAttributeResourceValue(ns,n,0);if(id!=0)return getContext().getResources().getString(id);String v=a.getAttributeValue(ns,n);return v!=null?v:d;}
}
"@
Write-Stub "com\mojang\minecraftpe\MainMenuOptionsActivity.java" @"
package com.mojang.minecraftpe;
import android.app.Activity;
public class MainMenuOptionsActivity extends Activity {
public static final String Internal_Game_DifficultyPeaceful="internal_game_difficulty_peaceful";
public static final String Game_DifficultyLevel="game_difficulty";
public static final String Controls_Sensitivity="controls_sensitivity";
}
"@
Write-Stub "com\mojang\minecraftpe\Minecraft_Market.java" @"
package com.mojang.minecraftpe;
import android.app.Activity; import android.content.Intent; import android.os.Bundle;
public class Minecraft_Market extends Activity {
@Override protected void onCreate(Bundle s){super.onCreate(s);startActivity(new Intent(this,MainActivity.class));finish();}
}
"@
Write-Stub "com\mojang\minecraftpe\Minecraft_Market_Demo.java" @"
package com.mojang.minecraftpe;
import android.content.Intent; import android.net.Uri;
public class Minecraft_Market_Demo extends MainActivity {
@Override public void buyGame(){startActivity(new Intent(Intent.ACTION_VIEW,Uri.parse("market://details?id=com.mojang.minecraftpe")));}
@Override protected boolean isDemo(){return true;}
}
"@
Write-Stub "com\mojang\minecraftpe\GameModeButton.java" @"
package com.mojang.minecraftpe;
import com.mojang.android.StringValue;
import android.content.Context; import android.util.AttributeSet;
import android.view.View; import android.view.View.OnClickListener;
import android.widget.TextView; import android.widget.ToggleButton;
public class GameModeButton extends ToggleButton implements OnClickListener,StringValue {
static final int Creative=0,Survival=1;
private int _type=0; private boolean _attached=false;
public GameModeButton(Context c,AttributeSet a){super(c,a);setOnClickListener(this);}
public void onClick(View v){_update();}
@Override protected void onFinishInflate(){super.onFinishInflate();_update();}
@Override protected void onAttachedToWindow(){if(!_attached){_update();_attached=true;}}
private void _update(){_set(isChecked()?Survival:Creative);}
private void _set(int i){
_type=i<Creative?Creative:(i>Survival?Survival:i);
int id=_type==Survival?R.string.gamemode_survival_summary:R.string.gamemode_creative_summary;
String desc=getContext().getString(id);
View v=getRootView().findViewById(R.id.labelGameModeDesc);
if(desc!=null&&v instanceof TextView)((TextView)v).setText(desc);
}
public String getStringValue(){return new String[]{"creative","survival"}[_type];}
static public String getStringForType(int i){int c=i<Creative?Creative:(i>Survival?Survival:i);return new String[]{"creative","survival"}[c];}
}
"@
Write-Host " stubs OK"
# ── 1. NDK build ─────────────────────────────────────────────
if (-not $NoCpp -and -not $NoBuild) {
Write-Step "NDK build (arm64-v8a)"
# NDK r14b on Windows hits the 32K CreateProcess limit with long paths.
# Work around it by building through a short junction C:\m -> repo root.
$junctionBase = "C:\m"
if (-not (Test-Path $junctionBase)) {
& cmd.exe /c "mklink /J `"$junctionBase`" `"$repo`"" | Out-Null
}
Push-Location "$junctionBase\project\android\jni"
$env:NDK_MODULE_PATH = "$junctionBase\project\lib_projects"
# run ndk-build and capture everything; let user see full output for debugging
$ndkOutput = & "$ndk\ndk-build.cmd" NDK_PROJECT_PATH="$junctionBase\project\android" APP_BUILD_SCRIPT="$junctionBase\project\android\jni\Android.mk" 2>&1 | Tee-Object -Variable ndkOutput
# dump entire output for diagnosis
Write-Host "---- NDK BUILD OUTPUT BEGIN ----"
$ndkOutput | ForEach-Object { Write-Host $_ }
Write-Host "---- NDK BUILD OUTPUT END ----"
# optionally highlight errors/warnings afterwards
$ndkOutput | Where-Object { $_ -match "error:|warning:|libminecraftpe|In file included" }
Pop-Location
Assert-ExitCode "ndk-build"
Copy-Item $libSrc $libDst -Force
Write-Host " .so -> $libDst"
}
# ── 2. Java compile ──────────────────────────────────────────
if (-not $NoJava -and -not $NoBuild) {
Write-Step "Java compile"
New-Dir (Split-Path $rJava -Parent)
& "$sdkTools\aapt.exe" package -f -M $manifest -S $res -I $androidJar -J "$apkbuild\gen" -F "$apkbuild\_rgen.apk" 2>&1 | Out-Null
Assert-ExitCode "aapt R.java"
Remove-Item "$apkbuild\_rgen.apk" -ea SilentlyContinue
$srcs = @(
Get-ChildItem $javaSrc -Recurse -Filter "*.java" | Select-Object -Exp FullName
Get-ChildItem $stubsDir -Recurse -Filter "*.java" | Select-Object -Exp FullName
$rJava
)
Remove-Item $classesDir -Recurse -Force -ea SilentlyContinue
New-Dir $classesDir
$eap = $ErrorActionPreference; $ErrorActionPreference = "Continue"
$errors = & javac --release 8 -cp $androidJar -d $classesDir @srcs 2>&1 |
Where-Object { $_ -match "error:" }
$ErrorActionPreference = $eap
if ($errors) { Write-Host $errors -ForegroundColor Red; exit 1 }
Write-Host " javac OK"
$classFiles = Get-ChildItem $classesDir -Recurse -Filter "*.class" | Select-Object -Exp FullName
& "$sdkTools\d8.bat" --min-api 21 --output $apkbuild $classFiles
Assert-ExitCode "d8"
Write-Host " d8 -> $dexOut"
}
# ── 3. Package APK ───────────────────────────────────────────
Write-Step "Package APK"
Remove-Item $unsigned,$aligned,$signed -ea SilentlyContinue
& "$sdkTools\aapt.exe" package -f -M $manifest -S $res -I $androidJar -F $unsigned
Assert-ExitCode "aapt package"
$zip = [System.IO.Compression.ZipFile]::Open($unsigned, 'Update')
try {
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip,$dexOut,"classes.dex",[System.IO.Compression.CompressionLevel]::Fastest)|Out-Null
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip,$libDst,"lib/arm64-v8a/libminecraftpe.so",[System.IO.Compression.CompressionLevel]::NoCompression)|Out-Null
Get-ChildItem $dataDir -Recurse -File | ForEach-Object {
$rel=$_.FullName.Substring("$dataDir\".Length).Replace('\','/')
[System.IO.Compression.ZipFileExtensions]::CreateEntryFromFile($zip,$_.FullName,"assets/$rel",[System.IO.Compression.CompressionLevel]::NoCompression)|Out-Null
}
} finally { $zip.Dispose() }
Write-Host " APK assembled"
& "$sdkTools\zipalign.exe" -p 4 $unsigned $aligned; Assert-ExitCode "zipalign"
& "$sdkTools\apksigner.bat" sign --ks $keystore --ks-pass pass:android --key-pass pass:android --out $signed $aligned; Assert-ExitCode "apksigner"
Write-Host " signed -> $signed"
# ── 4. Install ───────────────────────────────────────────────
Write-Step "Install"
& $adb shell am force-stop $pkg
& $adb uninstall $pkg 2>$null
& $adb install --no-incremental $signed
Assert-ExitCode "adb install"
Write-Host "`nDone." -ForegroundColor Green

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -144,6 +144,12 @@ options.fov.max=Quake Pro
options.gamma=Brightness
options.gamma.min=Moody
options.gamma.max=Bright
options.group.mojang=Login
options.group.game=Game
options.group.controls=Controls
options.group.graphics=Graphics
options.thirdperson=Third Person
options.servervisible=Server Visible
options.sensitivity=Sensitivity
options.sensitivity.min=*yawn*
options.sensitivity.max=HYPERSPEED!!!
@@ -156,6 +162,8 @@ options.viewBobbing=View Bobbing
options.ao=Smooth Lighting
options.anaglyph=3D Anaglyph
options.framerateLimit=Performance
options.limitFramerate=Limit Framerate
options.vsync=VSync
options.difficulty=Difficulty
options.difficulty.peaceful=Peaceful
options.difficulty.easy=Easy
@@ -170,6 +178,7 @@ options.guiScale.auto=Auto
options.guiScale.small=Small
options.guiScale.normal=Normal
options.guiScale.large=Large
options.guiScale.larger=Larger
options.advancedOpengl=Advanced OpenGL
options.renderClouds=Clouds
options.farWarning1=A 64 bit Java installation is recommended

View File

@@ -6,8 +6,8 @@
android:installLocation="preferExternal">
<!-- This is the platform API where NativeActivity was introduced. -->
<uses-sdk android:minSdkVersion="9"
android:targetSdkVersion="9" />
<uses-sdk android:minSdkVersion="21"
android:targetSdkVersion="21" />
<!-- uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="true"/ -->

View File

@@ -170,6 +170,7 @@ options.guiScale.auto=Auto
options.guiScale.small=Small
options.guiScale.normal=Normal
options.guiScale.large=Large
options.guiScale.larger=Larger
options.advancedOpengl=Advanced OpenGL
options.renderClouds=Clouds
options.farWarning1=A 64 bit Java installation is recommended

View File

@@ -4,7 +4,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := minecraftpe
LOCAL_SRC_FILES := ../../../src/main.cpp \
../../../src/main_android.cpp \
../../../src/main_android_java.cpp \
../../../src/platform/audio/SoundSystemSL.cpp \
../../../src/platform/input/Controller.cpp \
../../../src/platform/input/Keyboard.cpp \
@@ -40,6 +40,7 @@ LOCAL_SRC_FILES := ../../../src/main.cpp \
../../../src/client/gui/components/ScrolledSelectionList.cpp \
../../../src/client/gui/components/ScrollingPane.cpp \
../../../src/client/gui/components/Slider.cpp \
../../../src/client/gui/components/TextBox.cpp \
../../../src/client/gui/components/SmallButton.cpp \
../../../src/client/gui/Font.cpp \
../../../src/client/gui/Gui.cpp \
@@ -48,6 +49,10 @@ LOCAL_SRC_FILES := ../../../src/main.cpp \
../../../src/client/gui/screens/ScreenChooser.cpp \
../../../src/client/gui/screens/ArmorScreen.cpp \
../../../src/client/gui/screens/ChatScreen.cpp \
../../../src/client/gui/screens/ChooseLevelScreen.cpp \
../../../src/client/gui/screens/SimpleChooseLevelScreen.cpp \
../../../src/client/gui/screens/ConsoleScreen.cpp \
../../../src/client/gui/screens/UsernameScreen.cpp \
../../../src/client/gui/screens/ConfirmScreen.cpp \
../../../src/client/gui/screens/ChestScreen.cpp \
../../../src/client/gui/screens/DeathScreen.cpp \
@@ -56,7 +61,8 @@ LOCAL_SRC_FILES := ../../../src/main.cpp \
../../../src/client/gui/screens/IngameBlockSelectionScreen.cpp \
../../../src/client/gui/screens/JoinGameScreen.cpp \
../../../src/client/gui/screens/OptionsScreen.cpp \
../../../src/client/gui/screens/PauseScreen.cpp \
../../../src/client/gui/screens/CreditsScreen.cpp \
../../../src/client/gui/screens/PauseScreen.cpp \
../../../src/client/gui/screens/ProgressScreen.cpp \
../../../src/client/gui/screens/RenameMPLevelScreen.cpp \
../../../src/client/gui/screens/SelectWorldScreen.cpp \
@@ -245,7 +251,9 @@ LOCAL_SRC_FILES := ../../../src/main.cpp \
../../../src/world/level/tile/entity/FurnaceTileEntity.cpp \
../../../src/world/phys/HitResult.cpp
LOCAL_CFLAGS := -Wno-psabi $(LOCAL_CFLAGS)
LOCAL_CFLAGS := -DPLATFORM_ANDROID -DPRE_ANDROID23 -Wno-narrowing $(LOCAL_CFLAGS)
LOCAL_CPPFLAGS := -std=c++11
LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../src
#LOCAL_CFLAGS := -DANDROID_PUBLISH -DDEMO_MODE $(LOCAL_CFLAGS)
#LOCAL_CFLAGS := -DANDROID_PUBLISH $(LOCAL_CFLAGS)

View File

@@ -1,5 +1,5 @@
APP_PLATFORM := android-9
APP_STL := stlport_static
APP_PLATFORM := android-21
APP_STL := gnustl_static
APP_OPTIM := release
APP_ABI := armeabi-v7a
APP_ABI := arm64-v8a
#APP_ABI := armeabi-v7a x86

View File

@@ -2,43 +2,73 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mojang.minecraftpe"
android:versionCode="4000"
android:versionName="0.4.0"
>
<!-- android:installLocation="preferExternal" -->
android:versionName="0.4.0">
<uses-sdk android:targetSdkVersion="8" android:minSdkVersion="7"/>
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="28"/>
<uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="true"/>
<uses-feature
android:name="android.hardware.touchscreen.multitouch"
android:required="true"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<application android:icon="@drawable/icon"
android:label="@string/app_name"
>
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:hardwareAccelerated="true"
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true">
<activity android:name="com.mojang.minecraftpe.Minecraft_Market"
android:label="@string/app_name_short"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<activity
android:name="com.mojang.minecraftpe.Minecraft_Market"
android:label="@string/app_name_short"
android:configChanges="orientation|keyboardHidden|screenSize|uiMode"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:exported="true">
<meta-data android:name="android.app.lib_name"
android:value="minecraftpe" />
<meta-data
android:name="android.app.lib_name"
android:value="minecraftpe"/>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="com.mojang.minecraftpe.MainMenuOptionsActivity"></activity>
<meta-data android:name="xperiaplayoptimized_content" android:resource="@string/xperiaplayoptimized_content" />
<meta-data android:name="game_display_name" android:resource="@string/app_name" />
<meta-data android:name="game_icon" android:resource="@drawable/iconx" />
<activity
android:name="com.mojang.minecraftpe.MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize|uiMode"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
android:exported="false"/>
<activity
android:name="com.mojang.minecraftpe.MainMenuOptionsActivity"
android:exported="false"/>
<meta-data
android:name="xperiaplayoptimized_content"
android:resource="@string/xperiaplayoptimized_content"/>
<meta-data
android:name="game_display_name"
android:resource="@string/app_name"/>
<meta-data
android:name="game_icon"
android:resource="@drawable/iconx"/>
<uses-library
android:name="xperiaplaycertified"
android:required="false"/>
<uses-library android:name="xperiaplaycertified" android:required="false"/>
</application>
</manifest>

View File

@@ -40,7 +40,6 @@ import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -58,6 +57,8 @@ public class MainActivity extends Activity {
private GLView _glView;
public float invScale = 1.0f;// / 1.5f;
private int _screenWidth = 0;
private int _screenHeight = 0;
Vector<MotionEvent> _touchEvents = new Vector<MotionEvent>();
Vector<KeyEvent> _keyEvents = new Vector<KeyEvent>();
@@ -68,7 +69,12 @@ public class MainActivity extends Activity {
super.onCreate(savedInstanceState);
nativeRegisterThis();
nativeOnCreate();
// Cache screen dimensions once to avoid re-entrant getDisplayMetrics() callbacks
android.util.DisplayMetrics _dm = getResources().getDisplayMetrics();
_screenWidth = Math.max(_dm.widthPixels, _dm.heightPixels);
_screenHeight = Math.min(_dm.widthPixels, _dm.heightPixels);
nativeOnCreate(_screenWidth, _screenHeight);
_glView = new GLView(getApplication(), this);
//_glView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
@@ -105,15 +111,22 @@ public class MainActivity extends Activity {
}
public boolean isTouchscreen() { return true; }
public boolean supportsTouchscreen() { return true; }
static public boolean isXperiaPlay() { return false; }
static private boolean _isPowerVr = false;
public void setIsPowerVR(boolean status) { MainActivity._isPowerVr = status; }
static public boolean isPowerVR() { return _isPowerVr; }
@SuppressWarnings("deprecation")
public void vibrate(int milliSeconds) {
Vibrator v = (Vibrator)this.getSystemService(VIBRATOR_SERVICE);
v.vibrate(milliSeconds);
Vibrator v = (Vibrator) getSystemService(VIBRATOR_SERVICE);
if (android.os.Build.VERSION.SDK_INT >= 26) {
v.vibrate(android.os.VibrationEffect.createOneShot(milliSeconds,
android.os.VibrationEffect.DEFAULT_AMPLITUDE));
} else {
v.vibrate(milliSeconds);
}
}
private void createAlertDialog(boolean hasOkButton, boolean hasCancelButton, boolean preventBackKey) {
@@ -144,9 +157,16 @@ public class MainActivity extends Activity {
@Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
//System.out.println("Focus has changed. Has Focus? " + hasFocus);
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
getWindow().getDecorView().setSystemUiVisibility(
View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
| View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
}
}
@Override
@@ -184,9 +204,12 @@ public class MainActivity extends Activity {
return;
}
if (event.getAction() == KeyEvent.ACTION_DOWN)
if (event.getAction() == KeyEvent.ACTION_DOWN) {
nativeOnKeyDown(keyCode);
else if (event.getAction() == KeyEvent.ACTION_UP)
int unicodeChar = event.getUnicodeChar(event.getMetaState());
if (unicodeChar > 0)
nativeTextChar(unicodeChar);
} else if (event.getAction() == KeyEvent.ACTION_UP)
nativeOnKeyUp(keyCode);
}
@@ -347,22 +370,15 @@ public class MainActivity extends Activity {
}
public int getScreenWidth() {
Display display = ((WindowManager)this.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int out = Math.max(display.getWidth(), display.getHeight());
//System.out.println("getwidth: " + out);
return out;
return _screenWidth;
}
public int getScreenHeight() {
Display display = ((WindowManager)this.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
int out = Math.min(display.getWidth(), display.getHeight());
//System.out.println("getheight: " + out);
return out;
return _screenHeight;
}
public float getPixelsPerMillimeter() {
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
android.util.DisplayMetrics metrics = getResources().getDisplayMetrics();
return (metrics.xdpi + metrics.ydpi) * 0.5f / 25.4f;
}
@@ -465,8 +481,8 @@ public class MainActivity extends Activity {
MainActivity.this.mDialog.show();
MainActivity.this.mDialog.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
MainActivity.this.mDialog.getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
//MainActivity.this.getWindow().setLayout(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
MainActivity.this.mDialog.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
//MainActivity.this.getWindow().setLayout(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
});
}
@@ -516,6 +532,11 @@ public class MainActivity extends Activity {
public void buyGame() {}
public int getKeyFromKeyCode(int keyCode, int metaState, int deviceId) {
android.view.KeyCharacterMap kcm = android.view.KeyCharacterMap.load(deviceId);
return kcm.get(keyCode, metaState);
}
public String getPlatformStringVar(int id) {
if (id == 0) return android.os.Build.MODEL;
return null;
@@ -604,10 +625,11 @@ public class MainActivity extends Activity {
//
native void nativeRegisterThis();
native void nativeUnregisterThis();
native static void nativeOnCreate();
native static void nativeOnCreate(int screenWidth, int screenHeight);
native static void nativeOnDestroy();
native static void nativeOnKeyDown(int key);
native static void nativeOnKeyUp(int key);
native static void nativeTextChar(int unicodeChar);
native static boolean nativeHandleBack(boolean isDown);
native static void nativeMouseDown(int pointerId, int buttonId, float x, float y);
native static void nativeMouseUp(int pointerId, int buttonId, float x, float y);

View File

@@ -5,7 +5,7 @@
#include <stdio.h> // RAKNET_DEBUG_PRINTF
#include "RakAssert.h"
#if defined(ANDROID)
#include <asm/io.h>
// <asm/io.h> not needed and unavailable on arm64
#elif defined(_WIN32) || defined(__CYGWIN__)
#include <io.h>

View File

@@ -119,6 +119,8 @@ public:
virtual void buyGame() {}
virtual void openURL(const std::string& url) {}
virtual void finish() {}
virtual bool supportsTouchscreen() { return false; }

View File

@@ -104,6 +104,7 @@ public:
int getScreenWidth() { return _screenWidth; }
int getScreenHeight() { return _screenHeight; }
void setScreenDimensions(int w, int h) { _screenWidth = w; _screenHeight = h; }
// Note, this has to be called from the main thread (e.g. do it from JNI_onLoad)
// Somewhere between calling this, and calling the AppPlatform methods,
@@ -192,14 +193,20 @@ public:
// be rewritten later on anyway
int initConsts() {
LOGI("initConsts: start\n");
JVMAttacher ta(_vm);
JNIEnv* env = ta.getEnv();
LOGI("initConsts: getting method IDs\n");
jmethodID fWidth = env->GetMethodID( _activityClass, "getScreenWidth", "()I");
LOGI("initConsts: got fWidth=%p\n", fWidth);
jmethodID fHeight = env->GetMethodID( _activityClass, "getScreenHeight", "()I");
LOGI("initConsts: got fHeight=%p, calling getScreenWidth\n", fHeight);
_screenWidth = env->CallIntMethod(instance, fWidth);
LOGI("initConsts: screenWidth=%d, calling getScreenHeight\n", _screenWidth);
_screenHeight = env->CallIntMethod(instance, fHeight);
LOGI("initConsts: screenHeight=%d, done\n", _screenHeight);
}
void tick() {
@@ -376,8 +383,9 @@ public:
JVMAttacher ta(_vm);
JNIEnv* env = ta.getEnv();
std::string path = textureFolder ? "images/" + filename : filename;
jintArray arr = (jintArray)env->CallObjectMethod(
instance, _getImageData, env->NewStringUTF(filename.c_str()));
instance, _getImageData, env->NewStringUTF(path.c_str()));
if (!arr)
return TextureData();

View File

@@ -1,5 +1,74 @@
#include "AppPlatform_glfw.h"
#ifdef WIN32
#include <Windows.h>
#endif
// Loader that tries GLFW first, then falls back to opengl32.dll for any
// function that glfwGetProcAddress can't resolve.
void* winGLLoader(const char* name) {
void* p = (void*)glfwGetProcAddress(name);
#ifdef WIN32
if (!p) {
static HMODULE opengl32 = LoadLibraryA("opengl32.dll");
if (opengl32) p = (void*)GetProcAddress(opengl32, name);
}
#endif
return p;
}
// -----------------------------------------------------------------------
// Compatibility wrappers for OpenGL ES functions absent from desktop GL.
// The desktop equivalents use double parameters and are not declared in
// the GLES1 GLAD header, so look them up at runtime via winGLLoader.
// -----------------------------------------------------------------------
static void APIENTRY compat_glDepthRangef(GLfloat n, GLfloat f) {
typedef void(APIENTRY *fn_t)(double, double);
static fn_t fn = (fn_t)winGLLoader("glDepthRange");
if (fn) fn((double)n, (double)f);
}
static void APIENTRY compat_glClearDepthf(GLfloat d) {
typedef void(APIENTRY *fn_t)(double);
static fn_t fn = (fn_t)winGLLoader("glClearDepth");
if (fn) fn((double)d);
}
static void APIENTRY compat_glOrthof(GLfloat l, GLfloat r, GLfloat b,
GLfloat t, GLfloat n, GLfloat f) {
typedef void(APIENTRY *fn_t)(double,double,double,double,double,double);
static fn_t fn = (fn_t)winGLLoader("glOrtho");
if (fn) fn(l, r, b, t, n, f);
}
static void APIENTRY compat_glFrustumf(GLfloat l, GLfloat r, GLfloat b,
GLfloat t, GLfloat n, GLfloat f) {
typedef void(APIENTRY *fn_t)(double,double,double,double,double,double);
static fn_t fn = (fn_t)winGLLoader("glFrustum");
if (fn) fn(l, r, b, t, n, f);
}
// glFogx / glFogxv are OpenGL ES fixed-point variants that don't exist in
// desktop opengl32.dll. Wrap them via the float equivalents. For fog-mode
// enum params the GLfixed value IS the enum integer, so the cast is exact.
static void APIENTRY compat_glFogx(GLenum pname, GLfixed param) {
glFogf(pname, (GLfloat)param);
}
static void APIENTRY compat_glFogxv(GLenum pname, const GLfixed* params) {
// Only GL_FOG_COLOR uses an array; convert each element.
GLfloat fp[4] = {
(GLfloat)params[0], (GLfloat)params[1],
(GLfloat)params[2], (GLfloat)params[3]
};
glFogfv(pname, fp);
}
void glPatchDesktopCompat() {
if (!glad_glDepthRangef) glad_glDepthRangef = compat_glDepthRangef;
if (!glad_glClearDepthf) glad_glClearDepthf = compat_glClearDepthf;
if (!glad_glOrthof) glad_glOrthof = compat_glOrthof;
if (!glad_glFrustumf) glad_glFrustumf = compat_glFrustumf;
if (!glad_glFogx) glad_glFogx = compat_glFogx;
if (!glad_glFogxv) glad_glFogxv = compat_glFogxv;
}
float AppPlatform_glfw::getPixelsPerMillimeter() {
GLFWmonitor* monitor = glfwGetPrimaryMonitor();

View File

@@ -10,6 +10,10 @@
#include <fstream>
#include <sstream>
#include <GLFW/glfw3.h>
#ifdef _WIN32
#include <windows.h>
#include <shellapi.h>
#endif
static void png_funcReadFile(png_structp pngPtr, png_bytep data, png_size_t length) {
((std::istream*)png_get_io_ptr(pngPtr))->read((char*)data, length);
@@ -123,7 +127,16 @@ public:
virtual bool supportsTouchscreen() { return true; }
virtual bool hasBuyButtonWhenInvalidLicense() { return false; }
virtual void openURL(const std::string& url) {
#ifdef _WIN32
ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL);
#endif
}
private:
};
void* winGLLoader(const char* name);
void glPatchDesktopCompat();
#endif /*APPPLATFORM_GLFW_H__*/

View File

@@ -9,6 +9,8 @@
#include <cmath>
#include <fstream>
#include <sstream>
#include <windows.h>
#include <shellapi.h>
static void png_funcReadFile(png_structp pngPtr, png_bytep data, png_size_t length) {
((std::istream*)png_get_io_ptr(pngPtr))->read((char*)data, length);
@@ -122,6 +124,10 @@ public:
virtual bool supportsTouchscreen();
virtual bool hasBuyButtonWhenInvalidLicense();
virtual void openURL(const std::string& url) {
ShellExecuteA(NULL, "open", url.c_str(), NULL, NULL, SW_SHOWNORMAL);
}
private:
};

View File

@@ -18,6 +18,7 @@
//#include "world/level/storage/FolderMethods.h"
#ifndef STANDALONE_SERVER
#include "client/gui/screens/StartMenuScreen.h"
#include "client/gui/screens/UsernameScreen.h"
#endif
#include "client/player/LocalPlayer.h"
#ifndef STANDALONE_SERVER
@@ -110,6 +111,9 @@ void NinecraftApp::init()
#ifndef STANDALONE_SERVER
LOGI("This: %p\n", this);
screenChooser.setScreen(SCREEN_STARTMENU);
if (options.username.empty()) {
setScreen(new UsernameScreen());
}
#else
user->name = "Server";
hostMultiplayer();

View File

@@ -4,7 +4,13 @@ namespace Common {
std::string getGameVersionString(const std::string& versionSuffix /* = "" */)
{
return std::string("v0.6.1") + versionSuffix + " alpha";
std::string result = std::string("v0.6.1") + versionSuffix;
// append 64-bit port marker only on Android 64bit targets
#if defined(ANDROID) && (defined(__aarch64__) || defined(__x86_64__))
result += " (64-bit port)";
#endif
result += " alpha";
return result;
}
};

View File

@@ -31,6 +31,7 @@
#include "gui/Screen.h"
#include "gui/Font.h"
#include "gui/screens/RenameMPLevelScreen.h"
#include "gui/screens/ConsoleScreen.h"
#include "sound/SoundEngine.h"
#endif
#include "../platform/CThread.h"
@@ -490,15 +491,14 @@ void Minecraft::update() {
checkGlError("Update finished");
if (options.renderDebug) {
#ifndef PLATFORM_DESKTOP
if (!PerfTimer::enabled) {
PerfTimer::reset();
PerfTimer::enabled = true;
}
//TIMER_PUSH("debugfps");
_perfRenderer->renderFpsMeter(1);
checkGlError("render debug");
//TIMER_POP();
#endif
} else {
PerfTimer::enabled = false;
}
@@ -721,10 +721,13 @@ void Minecraft::tickInput() {
#endif
}
#endif
#if defined(PLATFORM_DESKTOP) || defined (_WIN32)
#if defined(PLATFORM_DESKTOP)
if (key == Keyboard::KEY_E) {
screenChooser.setScreen(SCREEN_BLOCKSELECTION);
}
if (!screen && key == Keyboard::KEY_T && level) {
setScreen(new ConsoleScreen());
}
if (!screen && key == Keyboard::KEY_O || key == 250) {
releaseMouse();
}
@@ -737,109 +740,99 @@ void Minecraft::tickInput() {
printf("%d\t%f\n", i, noise.grad2(i, 3, 8));
*/
}
#endif
#if defined(WIN32)
if (key == Keyboard::KEY_F) {
options.isFlying = !options.isFlying;
player->noPhysics = options.isFlying;
}
// Change distance
if (key == Keyboard::KEY_F)
if (key == Keyboard::KEY_L)
options.viewDistance = (options.viewDistance + 1) % 4;
#ifdef CHEATS
if (key == Keyboard::KEY_J) {
options.isFlying = !options.isFlying;
player->noPhysics = options.isFlying;
if (key == Keyboard::KEY_U) {
onGraphicsReset();
player->heal(100);
}
if (key == Keyboard::KEY_B || key == 108) // Toggle the game mode
setIsCreativeMode(!isCreativeMode());
if (key == Keyboard::KEY_P) // Step forward in time
level->setTime( level->getTime() + 1000);
if (key == Keyboard::KEY_G) {
setScreen(new ArmorScreen());
/*
std::vector<AABB>& boxs = level->getCubes(NULL, AABB(128.1f, 73, 128.1f, 128.9f, 74.9f, 128.9f));
LOGI("boxes: %d\n", (int)boxs.size());
*/
}
if (key == Keyboard::KEY_Y) {
textures->reloadAll();
player->hurtTo(2);
}
if (key == Keyboard::KEY_Z || key == 108) {
for (int i = 0; i < 1; ++i) {
Mob* mob = NULL;
int forceId = 0;//MobTypes::Sheep;
int types[] = {
MobTypes::Sheep,
MobTypes::Pig,
MobTypes::Chicken,
MobTypes::Cow,
};
int mobType = (forceId > 0)? forceId : types[Mth::random(sizeof(types) / sizeof(int))];
mob = MobFactory::CreateMob(mobType, level);
//((Animal*)mob)->setAge(-1000);
float dx = 4 - 8 * Mth::random() + 4 * Mth::sin(Mth::DEGRAD * player->yRot);
float dz = 4 - 8 * Mth::random() + 4 * Mth::cos(Mth::DEGRAD * player->yRot);
if (mob && !MobSpawner::addMob(level, mob, player->x + dx, player->y, player->z + dz, Mth::random()*360, 0, true))
delete mob;
}
}
if (key == Keyboard::KEY_O) {
useAmbientOcclusion = !useAmbientOcclusion;
options.ambientOcclusion = useAmbientOcclusion;
levelRenderer->allChanged();
if (key == Keyboard::KEY_X) {
const EntityList& entities = level->getAllEntities();
for (int i = entities.size()-1; i >= 0; --i) {
Entity* e = entities[i];
if (!e->isPlayer())
level->removeEntity(e);
}
}
if (key == Keyboard::KEY_L)
options.viewDistance = (options.viewDistance + 1) % 4;
if (key == Keyboard::KEY_C /*|| key == 4*/) {
player->inventory->clearInventoryWithDefault();
// @todo: Add saving here for benchmarking
}
if (key == Keyboard::KEY_H) {
setScreen( new PrerenderTilesScreen() );
}
if (key == Keyboard::KEY_U) {
onGraphicsReset();
player->heal(100);
if (key == Keyboard::KEY_O) {
for (int i = Inventory::MAX_SELECTION_SIZE; i < player->inventory->getContainerSize(); ++i)
if (player->inventory->getItem(i))
player->inventory->dropSlot(i, false);
}
if (key == Keyboard::KEY_F3) {
options.renderDebug = !options.renderDebug;
}
if (key == Keyboard::KEY_M) {
options.difficulty = (options.difficulty == Difficulty::PEACEFUL)?
Difficulty::NORMAL : Difficulty::PEACEFUL;
//setIsCreativeMode( !isCreativeMode() );
}
if (options.renderDebug) {
if (key >= '0' && key <= '9') {
_perfRenderer->debugFpsMeterKeyPress(key - '0');
}
if (key == Keyboard::KEY_B || key == 108) // Toggle the game mode
setIsCreativeMode(!isCreativeMode());
if (key == Keyboard::KEY_P) // Step forward in time
level->setTime( level->getTime() + 1000);
if (key == Keyboard::KEY_G) {
setScreen(new ArmorScreen());
/*
std::vector<AABB>& boxs = level->getCubes(NULL, AABB(128.1f, 73, 128.1f, 128.9f, 74.9f, 128.9f));
LOGI("boxes: %d\n", (int)boxs.size());
*/
}
if (key == Keyboard::KEY_Y) {
textures->reloadAll();
player->hurtTo(2);
}
if (key == Keyboard::KEY_Z || key == 108) {
for (int i = 0; i < 1; ++i) {
Mob* mob = NULL;
int forceId = 0;//MobTypes::Sheep;
int types[] = {
MobTypes::Sheep,
MobTypes::Pig,
MobTypes::Chicken,
MobTypes::Cow,
};
int mobType = (forceId > 0)? forceId : types[Mth::random(sizeof(types) / sizeof(int))];
mob = MobFactory::CreateMob(mobType, level);
//((Animal*)mob)->setAge(-1000);
float dx = 4 - 8 * Mth::random() + 4 * Mth::sin(Mth::DEGRAD * player->yRot);
float dz = 4 - 8 * Mth::random() + 4 * Mth::cos(Mth::DEGRAD * player->yRot);
if (mob && !MobSpawner::addMob(level, mob, player->x + dx, player->y, player->z + dz, Mth::random()*360, 0, true))
delete mob;
}
}
if (key == Keyboard::KEY_X) {
const EntityList& entities = level->getAllEntities();
for (int i = entities.size()-1; i >= 0; --i) {
Entity* e = entities[i];
if (!e->isPlayer())
level->removeEntity(e);
}
}
if (key == Keyboard::KEY_C /*|| key == 4*/) {
player->inventory->clearInventoryWithDefault();
// @todo: Add saving here for benchmarking
}
if (key == Keyboard::KEY_H) {
setScreen( new PrerenderTilesScreen() );
}
if (key == Keyboard::KEY_O) {
for (int i = Inventory::MAX_SELECTION_SIZE; i < player->inventory->getContainerSize(); ++i)
if (player->inventory->getItem(i))
player->inventory->dropSlot(i, false);
}
if (key == Keyboard::KEY_F3) {
options.renderDebug = !options.renderDebug;
}
if (key == Keyboard::KEY_M) {
options.difficulty = (options.difficulty == Difficulty::PEACEFUL)?
Difficulty::NORMAL : Difficulty::PEACEFUL;
//setIsCreativeMode( !isCreativeMode() );
}
if (options.renderDebug) {
if (key >= '0' && key <= '9') {
_perfRenderer->debugFpsMeterKeyPress(key - '0');
}
}
#endif
}
#endif
#ifndef PLATFORM_DESKTOP
@@ -871,35 +864,40 @@ void Minecraft::tickInput() {
static bool prevMouseDownLeft = false;
// Destroy and attack is on same button
if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) {
auto baiFlags = BuildActionIntention::BAI_REMOVE | BuildActionIntention::BAI_ATTACK;
if (!prevMouseDownLeft) baiFlags |= BuildActionIntention::BAI_FIRSTREMOVE;
BuildActionIntention bai(baiFlags);
handleBuildAction(&bai);
}
if (Mouse::getButtonState(MouseAction::ACTION_LEFT) == 0) {
gameMode->stopDestroyBlock();
}
prevMouseDownLeft = Mouse::isButtonDown(MouseAction::ACTION_LEFT);
static int buildHoldTicks = 0;
// Build and use/interact is on same button
// USPESHNO spizheno
if (Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) {
if (buildHoldTicks >= 5) buildHoldTicks = 0;
if (++buildHoldTicks == 1) {
BuildActionIntention bai(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT);
if (useTouchscreen()) {
// Touch: gesture recognizer classifies the action type (turn/destroy/build)
BuildActionIntention bai;
if (inputHolder && inputHolder->getBuildInput()->tickBuild(player, &bai)) {
handleBuildAction(&bai);
}
} else {
buildHoldTicks = 0;
// Desktop: left mouse = destroy/attack
if (Mouse::isButtonDown(MouseAction::ACTION_LEFT)) {
auto baiFlags = BuildActionIntention::BAI_REMOVE | BuildActionIntention::BAI_ATTACK;
if (!prevMouseDownLeft) baiFlags |= BuildActionIntention::BAI_FIRSTREMOVE;
BuildActionIntention bai(baiFlags);
handleBuildAction(&bai);
}
prevMouseDownLeft = Mouse::isButtonDown(MouseAction::ACTION_LEFT);
// Build and use/interact is on same button
// USPESHNO spizheno
static int buildHoldTicks = 0;
if (Mouse::isButtonDown(MouseAction::ACTION_RIGHT)) {
BuildActionIntention bai(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT);
handleBuildAction(&bai);
if (buildHoldTicks >= 5) buildHoldTicks = 0;
if (++buildHoldTicks == 1) {
BuildActionIntention bai(BuildActionIntention::BAI_BUILD | BuildActionIntention::BAI_INTERACT);
handleBuildAction(&bai);
}
} else {
buildHoldTicks = 0;
}
}
lastTickTime = getTimeMs();
@@ -1146,24 +1144,37 @@ void Minecraft::setSize(int w, int h) {
width = w;
height = h;
if (width >= 1000) {
// determine gui scale, optionally overriding auto
if (options.guiScale != 0) {
// manual selection: 1->small, 2->normal, 3->large, 4->larger
switch (options.guiScale) {
case 1: Gui::GuiScale = 2.0f; break;
case 2: Gui::GuiScale = 3.0f; break;
case 3: Gui::GuiScale = 4.0f; break;
case 4: Gui::GuiScale = 5.0f; break; // bigger than large
default: Gui::GuiScale = 1.0f; break; // auto
}
} else {
// auto compute from resolution
if (width >= 1000) {
#ifdef __APPLE__
Gui::GuiScale = (width > 2000)? 8.0f : 4.0f;
#else
Gui::GuiScale = 4.0f;
#endif
}
else if (width >= 800) {
else if (width >= 800) {
#ifdef __APPLE__
Gui::GuiScale = 4.0f;
#else
Gui::GuiScale = 3.0f;
#endif
}
else if (width >= 400)
Gui::GuiScale = 2.0f;
else
Gui::GuiScale = 1.0f;
else if (width >= 400)
Gui::GuiScale = 2.0f;
else
Gui::GuiScale = 1.0f;
}
Gui::InvGuiScale = 1.0f / Gui::GuiScale;
int screenWidth = (int)(width * Gui::InvGuiScale);
@@ -1216,7 +1227,12 @@ void Minecraft::_reloadInput() {
#ifndef STANDALONE_SERVER
delete inputHolder;
if (useTouchscreen() && !PLATFORM_DESKTOP) {
#ifdef PLATFORM_DESKTOP
const bool useTouchHolder = false;
#else
const bool useTouchHolder = useTouchscreen();
#endif
if (useTouchHolder) {
inputHolder = new TouchInputHolder(this, &options);
} else {
#if defined(ANDROID) || defined(__APPLE__)
@@ -1526,5 +1542,8 @@ void Minecraft::optionUpdated( const Options::Option* option, float value ) {
}
void Minecraft::optionUpdated( const Options::Option* option, int value ) {
if(option == &Options::Option::GUI_SCALE) {
// reapply screen scaling using current window size
setSize(width, height);
}
}

View File

@@ -5,6 +5,8 @@ const char* OptionStrings::Multiplayer_ServerVisible = "mp_server_visible_defa
const char* OptionStrings::Graphics_Fancy = "gfx_fancygraphics";
const char* OptionStrings::Graphics_LowQuality = "gfx_lowquality";
const char* OptionStrings::Graphics_Vsync = "gfx_vsync";
const char* OptionStrings::Graphics_GUIScale = "gfx_guiscale";
const char* OptionStrings::Controls_Sensitivity = "ctrl_sensitivity";
const char* OptionStrings::Controls_InvertMouse = "ctrl_invertmouse";

View File

@@ -8,7 +8,8 @@ public:
static const char* Graphics_Fancy;
static const char* Graphics_LowQuality;
static const char* Graphics_GUIScale;
static const char* Graphics_Vsync;
static const char* Controls_Sensitivity;
static const char* Controls_InvertMouse;
static const char* Controls_UseTouchScreen;

View File

@@ -34,6 +34,7 @@ void Options::initDefaultValues() {
bobView = true;
anaglyph3d = false;
limitFramerate = false;
vsync = true;
fancyGraphics = true;//false;
ambientOcclusion = false;
if(minecraft->supportNonTouchScreen())
@@ -44,7 +45,7 @@ void Options::initDefaultValues() {
//useMouseForDigging = true;
//skin = "Default";
username = "Steve";
username = "";
serverVisible = true;
keyUp = KeyMapping("key.forward", Keyboard::KEY_W);
@@ -144,7 +145,8 @@ const Options::Option
Options::Option::USE_TOUCHSCREEN (16, "options.usetouchscreen", false, true),
Options::Option::USE_TOUCH_JOYPAD (17, "options.usetouchpad", false, true),
Options::Option::DESTROY_VIBRATION (18, "options.destroyvibration", false, true),
Options::Option::PIXELS_PER_MILLIMETER(19, "options.pixelspermilimeter", true, false);
Options::Option::PIXELS_PER_MILLIMETER(19, "options.pixelspermilimeter", true, false),
Options::Option::VSYNC (20, "options.vsync", false, true);
/* private */
const float Options::SOUND_MIN_VALUE = 0.0f;
@@ -181,7 +183,8 @@ const char* Options::GUI_SCALE[] = {
"options.guiScale.auto",
"options.guiScale.small",
"options.guiScale.normal",
"options.guiScale.large"
"options.guiScale.large",
"options.guiScale.larger"
};
void Options::update()
@@ -235,6 +238,13 @@ void Options::update()
fancyGraphics = false;
}
}
// Graphics extras
if (key == OptionStrings::Graphics_Vsync)
readBool(value, vsync);
if (key == OptionStrings::Graphics_GUIScale) {
int v;
if (readInt(value, v)) guiScale = v % 5;
}
// Game
if (key == OptionStrings::Game_DifficultyLevel) {
readInt(value, difficulty);
@@ -294,6 +304,8 @@ void Options::load()
void Options::save()
{
StringVector stringVec;
// Login
addOptionToSaveOutput(stringVec, OptionStrings::Multiplayer_Username, username);
// Game
addOptionToSaveOutput(stringVec, OptionStrings::Multiplayer_ServerVisible, serverVisible);
addOptionToSaveOutput(stringVec, OptionStrings::Game_DifficultyLevel, difficulty);
@@ -305,6 +317,8 @@ void Options::save()
addOptionToSaveOutput(stringVec, OptionStrings::Controls_UseTouchScreen, useTouchScreen);
addOptionToSaveOutput(stringVec, OptionStrings::Controls_UseTouchJoypad, isJoyTouchArea);
addOptionToSaveOutput(stringVec, OptionStrings::Controls_FeedbackVibration, destroyVibration);
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_Vsync, vsync);
addOptionToSaveOutput(stringVec, OptionStrings::Graphics_GUIScale, guiScale);
//
// static const Option MUSIC;
// static const Option SOUND;
@@ -348,6 +362,7 @@ void Options::save()
// System.out.println("Failed to save options");
// e.printStackTrace();
//}
optionsFile.save(stringVec);
}
void Options::addOptionToSaveOutput(StringVector& stringVector, std::string name, bool boolValue) {
std::stringstream ss;
@@ -364,6 +379,9 @@ void Options::addOptionToSaveOutput(StringVector& stringVector, std::string name
ss << name << ":" << intValue;
stringVector.push_back(ss.str());
}
void Options::addOptionToSaveOutput(StringVector& stringVector, std::string name, const std::string& strValue) {
stringVector.push_back(name + ":" + strValue);
}
std::string Options::getMessage( const Option* item )
{

View File

@@ -48,6 +48,7 @@ public:
static const Option DESTROY_VIBRATION;
static const Option PIXELS_PER_MILLIMETER;
static const Option VSYNC;
/*
static Option* getItem(int id) {
@@ -113,6 +114,7 @@ public:
bool bobView;
bool anaglyph3d;
bool limitFramerate;
bool vsync;
bool fancyGraphics;
bool ambientOcclusion;
bool useMouseForDigging;
@@ -206,19 +208,21 @@ public:
} else if (item == &Option::PIXELS_PER_MILLIMETER) {
pixelsPerMillimeter = value;
}
notifyOptionUpdate(item, value);
}
notifyOptionUpdate(item, value); save(); }
void set(const Option* item, int value) {
if(item == &Option::DIFFICULTY) {
difficulty = value;
} else if(item == &Option::GUI_SCALE) {
guiScale = value % 5;
}
notifyOptionUpdate(item, value);
save();
}
void toggle(const Option* option, int dir) {
if (option == &Option::INVERT_MOUSE) invertYMouse = !invertYMouse;
if (option == &Option::RENDER_DISTANCE) viewDistance = (viewDistance + dir) & 3;
if (option == &Option::GUI_SCALE) guiScale = (guiScale + dir) & 3;
if (option == &Option::GUI_SCALE) guiScale = (guiScale + dir) % 5;
if (option == &Option::VIEW_BOBBING) bobView = !bobView;
if (option == &Option::THIRD_PERSON) thirdPersonView = !thirdPersonView;
if (option == &Option::HIDE_GUI) hideGui = !hideGui;
@@ -232,6 +236,7 @@ public:
//minecraft->textures.reloadAll();
}
if (option == &Option::LIMIT_FRAMERATE) limitFramerate = !limitFramerate;
if (option == &Option::VSYNC) vsync = !vsync;
if (option == &Option::DIFFICULTY) difficulty = (difficulty + dir) & 3;
if (option == &Option::GRAPHICS) {
fancyGraphics = !fancyGraphics;
@@ -247,6 +252,7 @@ public:
int getIntValue(const Option* item) {
if(item == &Option::DIFFICULTY) return difficulty;
if(item == &Option::GUI_SCALE) return guiScale;
return 0;
}
@@ -267,6 +273,8 @@ public:
return anaglyph3d;
if (item == &Option::LIMIT_FRAMERATE)
return limitFramerate;
if (item == &Option::VSYNC)
return vsync;
if (item == &Option::AMBIENT_OCCLUSION)
return ambientOcclusion;
if (item == &Option::THIRD_PERSON)
@@ -312,6 +320,7 @@ public:
void addOptionToSaveOutput(StringVector& stringVector, std::string name, bool boolValue);
void addOptionToSaveOutput(StringVector& stringVector, std::string name, float floatValue);
void addOptionToSaveOutput(StringVector& stringVector, std::string name, int intValue);
void addOptionToSaveOutput(StringVector& stringVector, std::string name, const std::string& strValue);
void notifyOptionUpdate(const Option* option, bool value);
void notifyOptionUpdate(const Option* option, float value);
void notifyOptionUpdate(const Option* option, int value);

View File

@@ -1,6 +1,7 @@
#include "OptionsFile.h"
#include <stdio.h>
#include <string.h>
#include <platform/log.h>
OptionsFile::OptionsFile() {
#ifdef __APPLE__
@@ -19,19 +20,32 @@ void OptionsFile::save(const StringVector& settings) {
fprintf(pFile, "%s\n", it->c_str());
}
fclose(pFile);
} else {
LOGI("OptionsFile::save failed to open '%s' for writing: %s", settingsPath.c_str(), strerror(errno));
}
}
StringVector OptionsFile::getOptionStrings() {
StringVector returnVector;
FILE* pFile = fopen(settingsPath.c_str(), "w");
FILE* pFile = fopen(settingsPath.c_str(), "r");
if(pFile != NULL) {
char lineBuff[128];
while(fgets(lineBuff, sizeof lineBuff, pFile)) {
if(strlen(lineBuff) > 2)
returnVector.push_back(std::string(lineBuff));
// Strip trailing newline
size_t len = strlen(lineBuff);
while(len > 0 && (lineBuff[len-1] == '\n' || lineBuff[len-1] == '\r'))
lineBuff[--len] = '\0';
if(len < 3) continue;
// Split "key:value" into two separate entries to match update() pairing
char* colon = strchr(lineBuff, ':');
if(colon) {
returnVector.push_back(std::string(lineBuff, colon - lineBuff));
returnVector.push_back(std::string(colon + 1));
}
}
fclose(pFile);
} else {
LOGI("OptionsFile::getOptionStrings failed to open '%s' for reading: %s", settingsPath.c_str(), strerror(errno));
}
return returnVector;
}

View File

@@ -21,6 +21,8 @@
#include "../../platform/input/Mouse.h"
#include "../../world/level/Level.h"
#include "../../world/PosTranslator.h"
#include "../../platform/time.h"
#include <cmath>
float Gui::InvGuiScale = 1.0f / 3.0f;
float Gui::GuiScale = 1.0f / Gui::InvGuiScale;
@@ -116,6 +118,9 @@ void Gui::render(float a, bool mouseFree, int xMouse, int yMouse) {
#endif
#if defined(RPI)
renderDebugInfo();
#elif defined(PLATFORM_DESKTOP)
if (minecraft->options.renderDebug)
renderDebugInfo();
#endif
glDisable(GL_BLEND);
@@ -632,17 +637,87 @@ void Gui::onLevelGenerated() {
}
void Gui::renderDebugInfo() {
static char buf[256];
float xx = minecraft->player->x;
float yy = minecraft->player->y - minecraft->player->heightOffset;
float zz = minecraft->player->z;
posTranslator.to(xx, yy, zz);
sprintf(buf, "pos: %3.1f, %3.1f, %3.1f\n", xx, yy, zz);
// FPS counter (updates once per second)
static float fps = 0.0f;
static float fpsLastTime = 0.0f;
static int fpsFrames = 0;
float now = getTimeS();
fpsFrames++;
if (now - fpsLastTime >= 1.0f) {
fps = fpsFrames / (now - fpsLastTime);
fpsFrames = 0;
fpsLastTime = now;
}
LocalPlayer* p = minecraft->player;
Level* lvl = minecraft->level;
// Position
float px = p->x, py = p->y - p->heightOffset, pz = p->z;
posTranslator.to(px, py, pz);
int bx = (int)floorf(px), by = (int)floorf(py), bz = (int)floorf(pz);
int cx = bx >> 4, cz = bz >> 4;
// Facing direction
float yMod = fmodf(p->yRot, 360.0f);
if (yMod < 0) yMod += 360.0f;
const char* facing;
const char* axis;
if (yMod < 45 || yMod >= 315) { facing = "South"; axis = "+Z"; }
else if (yMod < 135) { facing = "West"; axis = "-X"; }
else if (yMod < 225) { facing = "North"; axis = "-Z"; }
else { facing = "East"; axis = "+X"; }
// Biome
const char* biomeName = "unknown";
if (lvl) {
Biome* biome = lvl->getBiome(bx, bz);
if (biome) biomeName = biome->name.c_str();
}
// Time
long worldTime = lvl ? lvl->getTime() : 0;
long dayTime = worldTime % Level::TICKS_PER_DAY;
long day = worldTime / Level::TICKS_PER_DAY;
long seed = lvl ? lvl->getSeed() : 0;
// Build lines (NULL entry = blank gap)
static char ln[8][96];
sprintf(ln[0], "Minecraft PE 0.6.1 alpha (mcpe64)");
sprintf(ln[1], "%.1f fps", fps);
ln[2][0] = '\0'; // blank separator
sprintf(ln[3], "XYZ: %.3f / %.3f / %.3f", px, py, pz);
sprintf(ln[4], "Block: %d %d %d Chunk: %d %d", bx, by, bz, cx, cz);
sprintf(ln[5], "Facing: %s (%s) (%.1f / %.1f)", facing, axis, p->yRot, p->xRot);
sprintf(ln[6], "Biome: %s", biomeName);
sprintf(ln[7], "Day %ld Time: %ld Seed: %ld", day, dayTime, seed);
const int N = 8;
const float LH = (float)Font::DefaultLineHeight; // 10 font-pixels
const float MGN = 2.0f; // left/top margin in font-pixels
const float PAD = 2.0f; // horizontal padding for background
Font* font = minecraft->font;
// 1) Draw semi-transparent background boxes behind each line
for (int i = 0; i < N; i++) {
if (ln[i][0] == '\0') continue;
float w = (float)font->width(ln[i]);
float x0 = MGN - PAD;
float y0 = MGN + i * LH - 1.0f;
float x1 = MGN + w + PAD;
float y1 = MGN + (i + 1) * LH - 1.0f;
fill(x0, y0, x1, y1, 0x90000000);
}
// 2) Draw text (no extra scale — font coords are in GUI units, same as fill)
Tesselator& t = Tesselator::instance;
t.beginOverride();
t.scale2d(InvGuiScale, InvGuiScale);
minecraft->font->draw(buf, 2, 2, 0xffffff);
t.resetScale();
for (int i = 0; i < N; i++) {
if (ln[i][0] == '\0') continue;
float y = MGN + i * LH;
int col = (i == 0) ? 0xffFFFF55 : 0xffffffff; // title yellow, rest white
font->draw(ln[i], MGN, y, col);
}
t.endOverrideAndDraw();
}
@@ -676,6 +751,70 @@ void Gui::renderOnSelectItemNameText( const int screenWidth, Font* font, int ySl
}
}
// helper structure used by drawColoredString
struct ColorSegment {
std::string text;
uint32_t color;
};
// parse [tag] and [/tag] markers; tags may contain a color name (gold, green, etc.)
static void parseColorTags(const std::string& in, std::vector<ColorSegment>& out) {
uint32_t curColor = 0xffffff;
size_t pos = 0;
while (pos < in.size()) {
size_t open = in.find('[', pos);
if (open == std::string::npos) {
out.push_back({in.substr(pos), curColor});
break;
}
if (open > pos) {
out.push_back({in.substr(pos, open - pos), curColor});
}
size_t close = in.find(']', open);
if (close == std::string::npos) {
out.push_back({in.substr(open), curColor});
break;
}
std::string tag = in.substr(open + 1, close - open - 1);
if (!tag.empty() && tag[0] == '/') {
curColor = 0xffffff;
} else {
std::string lower;
lower.resize(tag.size());
std::transform(tag.begin(), tag.end(), lower.begin(), ::tolower);
if (lower.find("gold") != std::string::npos) curColor = 0xffd700;
else if (lower.find("green") != std::string::npos) curColor = 0x00ff00;
else if (lower.find("yellow") != std::string::npos) curColor = 0xffff00;
else if (lower.find("red") != std::string::npos) curColor = 0xff0000;
else if (lower.find("blue") != std::string::npos) curColor = 0x0000ff;
}
pos = close + 1;
}
}
void Gui::drawColoredString(Font* font, const std::string& text, float x, float y, int alpha) {
std::vector<ColorSegment> segs;
parseColorTags(text, segs);
float cx = x;
for (auto &s : segs) {
int color = s.color + (alpha << 24);
font->drawShadow(s.text, cx, y, color);
cx += font->width(s.text);
}
}
float Gui::getColoredWidth(Font* font, const std::string& text) {
std::vector<ColorSegment> segs;
parseColorTags(text, segs);
float w = 0;
for (auto &s : segs) {
w += font->width(s.text);
}
return w;
}
void Gui::renderChatMessages( const int screenHeight, unsigned int max, bool isChatting, Font* font ) {
// if (minecraft.screen instanceof ChatScreen) {
// max = 20;
@@ -709,7 +848,14 @@ void Gui::renderChatMessages( const int screenHeight, unsigned int max, bool isC
this->fill(x, y - 1, x + MAX_MESSAGE_WIDTH, y + 8, (alpha / 2) << 24);
glEnable(GL_BLEND);
font->drawShadow(msg, x, y, 0xffffff + (alpha << 24));
// special-case join/leave announcements
int baseColor = 0xffffff;
if (msg.find(" joined the game") != std::string::npos ||
msg.find(" left the game") != std::string::npos) {
baseColor = 0xffff00; // yellow
}
// replace previous logic; allow full colour tags now
Gui::drawColoredString(font, msg, x, y, alpha);
}
}
}

View File

@@ -47,6 +47,12 @@ public:
void renderChatMessages( const int screenHeight, unsigned int max, bool isChatting, Font* font );
// draw a string containing simple [color]...[/color] tags; color names are matched
// case-insensitively and default to white. alpha is applied to each segment.
// draw tagged string (ignores simple [color]…[/color] tags)
static void drawColoredString(Font* font, const std::string& text, float x, float y, int alpha);
static float getColoredWidth(Font* font, const std::string& text);
void renderOnSelectItemNameText( const int screenWidth, Font* font, int ySlot );
void renderSleepAnimation( const int screenWidth, const int screenHeight );

View File

@@ -26,6 +26,7 @@ void Screen::render( int xm, int ym, float a )
button->render(minecraft, xm, ym);
}
// render any text boxes after buttons
for (unsigned int i = 0; i < textBoxes.size(); i++) {
TextBox* textbox = textBoxes[i];
textbox->render(minecraft, xm, ym);
@@ -163,6 +164,7 @@ void Screen::keyPressed( int eventKey )
//minecraft->grabMouse();
}
// pass key events to any text boxes first
for (auto& textbox : textBoxes) {
textbox->handleKey(eventKey);
}
@@ -191,14 +193,6 @@ void Screen::keyPressed( int eventKey )
updateTabButtonSelection();
}
void Screen::keyboardNewChar(char inputChar) {
// yeah im using these modern cpp features in this project :sunglasses:
for (auto& textbox : textBoxes) {
textbox->handleChar(inputChar);
}
}
void Screen::updateTabButtonSelection()
{
if (minecraft->useTouchscreen())
@@ -229,6 +223,7 @@ void Screen::mouseClicked( int x, int y, int buttonNum )
}
}
// let textboxes see the click regardless
for (auto& textbox : textBoxes) {
textbox->mouseClicked(minecraft, x, y, buttonNum);
}
@@ -275,9 +270,3 @@ void Screen::toGUICoordinate( int& x, int& y ) {
x = x * width / minecraft->width;
y = y * height / minecraft->height - 1;
}
void Screen::tick() {
for (auto& textbox : textBoxes) {
textbox->tick(minecraft);
}
}

View File

@@ -31,7 +31,7 @@ public:
virtual void keyboardTextEvent();
virtual bool handleBackEvent(bool isDown);
virtual void tick();
virtual void tick() {}
virtual void removed() {}
@@ -58,7 +58,7 @@ protected:
virtual void mouseReleased(int x, int y, int buttonNum);
virtual void keyPressed(int eventKey);
virtual void keyboardNewChar(char inputChar);
virtual void keyboardNewChar(char inputChar) {}
public:
int width;
int height;

View File

@@ -10,7 +10,7 @@ OptionsGroup::OptionsGroup( std::string labelID ) {
void OptionsGroup::setupPositions() {
// First we write the header and then we add the items
int curY = y + 10;
int curY = y + 18;
for(std::vector<GuiElement*>::iterator it = children.begin(); it != children.end(); ++it) {
(*it)->width = width - 5;
@@ -23,7 +23,9 @@ void OptionsGroup::setupPositions() {
}
void OptionsGroup::render( Minecraft* minecraft, int xm, int ym ) {
minecraft->font->draw(label, (float)x + 2, (float)y, 0xffffffff, false);
float padX = 10.0f;
float padY = 5.0f;
minecraft->font->draw(label, (float)x + padX, (float)y + padY, 0xffffffff, false);
super::render(minecraft, xm, ym);
}
@@ -45,6 +47,7 @@ void OptionsGroup::createToggle( const Options::Option* option, Minecraft* minec
def.height = 20 * 0.7f;
OptionButton* element = new OptionButton(option);
element->setImageDef(def, true);
element->updateImage(&minecraft->options);
std::string itemLabel = I18n::get(option->getCaptionId());
OptionsItem* item = new OptionsItem(itemLabel, element);
addChild(item);
@@ -58,11 +61,38 @@ void OptionsGroup::createProgressSlider( const Options::Option* option, Minecraf
minecraft->options.getProgrssMax(option));
element->width = 100;
element->height = 20;
OptionsItem* item = new OptionsItem(label, element);
std::string itemLabel = I18n::get(option->getCaptionId());
OptionsItem* item = new OptionsItem(itemLabel, element);
addChild(item);
setupPositions();
}
void OptionsGroup::createStepSlider( const Options::Option* option, Minecraft* minecraft ) {
// integer-valued option; use step slider
std::vector<int> steps;
// render distance was removed; fall through to other cases
if(option == &Options::Option::DIFFICULTY) {
steps.push_back(0);
steps.push_back(1);
steps.push_back(2);
steps.push_back(3);
} else if(option == &Options::Option::GUI_SCALE) {
// slider order: small,normal,large,larger,auto
steps.push_back(1);
steps.push_back(2);
steps.push_back(3);
steps.push_back(4);
steps.push_back(0);
} else {
// fallback: use single value; duplicate so numSteps>1 and avoid divide-by-zero
steps.push_back(0);
steps.push_back(0);
}
Slider* element = new Slider(minecraft, option, steps);
element->width = 100;
element->height = 20;
std::string itemLabel = I18n::get(option->getCaptionId());
OptionsItem* item = new OptionsItem(itemLabel, element);
addChild(item);
setupPositions();
}

View File

@@ -25,13 +25,17 @@ Slider::Slider(Minecraft* minecraft, const Options::Option* option, const std::v
assert(stepVec.size() > 1);
numSteps = sliderSteps.size();
if(option != NULL) {
curStepValue;
int curStep;
// initialize slider position based on the current option value
curStepValue = minecraft->options.getIntValue(option);
std::vector<int>::iterator currentItem = std::find(sliderSteps.begin(), sliderSteps.end(), curStepValue);
auto currentItem = std::find(sliderSteps.begin(), sliderSteps.end(), curStepValue);
if(currentItem != sliderSteps.end()) {
curStep = currentItem - sliderSteps.begin();
curStep = static_cast<int>(currentItem - sliderSteps.begin());
} else {
// fallback to first step
curStep = 0;
curStepValue = sliderSteps[0];
}
percentage = float(curStep) / float(numSteps - 1);
}
}
@@ -46,10 +50,15 @@ void Slider::render( Minecraft* minecraft, int xm, int ym ) {
//fill(x, y + 8, x + (int)(width * percentage), y + height, 0xffff00ff);
fill(xSliderStart, ySliderStart, xSliderEnd, ySliderEnd, 0xff606060);
if(sliderType == SliderStep) {
int stepDistance = barWidth / (numSteps -1);
for(int a = 0; a <= numSteps - 1; ++a) {
int renderSliderStepPosX = xSliderStart + a * stepDistance + 1;
fill(renderSliderStepPosX - 1, ySliderStart - 2, renderSliderStepPosX + 1, ySliderEnd + 2, 0xff606060);
// numSteps should be >=2; protect against bad input (zero division)
if(numSteps <= 1) {
// nothing to render
} else {
int stepDistance = barWidth / (numSteps -1);
for(int a = 0; a <= numSteps - 1; ++a) {
int renderSliderStepPosX = xSliderStart + a * stepDistance + 1;
fill(renderSliderStepPosX - 1, ySliderStart - 2, renderSliderStepPosX + 1, ySliderEnd + 2, 0xff606060);
}
}
}
minecraft->textures->loadAndBindTexture("gui/touchgui.png");

View File

@@ -1,90 +1,100 @@
#include "TextBox.h"
#include "../Gui.h"
#include "../../Minecraft.h"
#include "../../../AppPlatform.h"
#include "platform/input/Mouse.h"
#include "../../../platform/input/Mouse.h"
TextBox::TextBox( int id, const std::string& msg )
: TextBox(id, 0, 0, msg) {}
// delegate constructors
TextBox::TextBox(int id, const std::string& msg)
: TextBox(id, 0, 0, msg)
{
}
TextBox::TextBox( int id, int x, int y, const std::string& msg )
: TextBox(id, x, y, 24, msg) {}
TextBox::TextBox(int id, int x, int y, const std::string& msg)
: TextBox(id, x, y, 24, Font::DefaultLineHeight + 4, msg)
{
}
TextBox::TextBox( int id, int x, int y, int w, const std::string& msg )
: GuiElement(true, true, x, y, w, Font::DefaultLineHeight + 4),
id(id), hint(msg), focused(false), blink(false) {}
TextBox::TextBox(int id, int x, int y, int w, int h, const std::string& msg)
: GuiElement(true, true, x, y, w, h),
id(id), hint(msg), focused(false), blink(false), blinkTicks(0)
{
}
void TextBox::setFocus(Minecraft* minecraft) {
if(!focused) {
minecraft->platform()->showKeyboard();
focused = true;
blinkTicks = 0;
blink = false;
}
if (!focused) {
minecraft->platform()->showKeyboard();
focused = true;
blinkTicks = 0;
blink = false;
}
}
bool TextBox::loseFocus(Minecraft* minecraft) {
if(focused) {
minecraft->platform()->showKeyboard();
focused = false;
return true;
}
return false;
if (focused) {
minecraft->platform()->hideKeyboard();
focused = false;
return true;
}
return false;
}
void TextBox::mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum) {
if (buttonNum == MouseAction::ACTION_LEFT) {
if (pointInside(x, y)) {
setFocus(minecraft);
} else {
loseFocus(minecraft);
}
}
if (buttonNum == MouseAction::ACTION_LEFT) {
if (pointInside(x, y)) {
setFocus(minecraft);
} else {
loseFocus(minecraft);
}
}
}
void TextBox::handleChar(char c) {
if (focused) {
text.push_back(c);
}
if (focused && c >= 32 && c < 127 && (int)text.size() < 256) {
text.push_back(c);
}
}
void TextBox::handleKey(int key) {
if (focused && key == Keyboard::KEY_BACKSPACE && !text.empty()) {
text.pop_back();
}
if (focused && key == Keyboard::KEY_BACKSPACE && !text.empty()) {
text.pop_back();
}
}
void TextBox::tick(Minecraft* minecraft) {
blinkTicks++;
if (blinkTicks >= 5) {
blink = !blink;
blinkTicks = 0;
}
blinkTicks++;
if (blinkTicks >= 5) {
blink = !blink;
blinkTicks = 0;
}
}
void TextBox::render( Minecraft* minecraft, int xm, int ym ) {
// textbox like in beta 1.7.3
fill(x, y, x + width, y + height, 0xffa0a0a0);
fill(x + 1, y + 1, x + width - 1, y + height - 1, 0xff000000);
void TextBox::render(Minecraft* minecraft, int xm, int ym) {
// textbox like in beta 1.7.3
// change appearance when focused so the user can tell it's active
// active background darker gray with a subtle border
uint32_t bgColor = focused ? 0xffa0a0a0 : 0xffa0a0a0;
uint32_t borderColor = focused ? 0xff000000 : 0xff000000;
fill(x, y, x + width, y + height, bgColor);
fill(x + 1, y + 1, x + width - 1, y + height - 1, borderColor);
glEnable2(GL_SCISSOR_TEST);
glScissor(
Gui::GuiScale * (x + 2),
minecraft->height - Gui::GuiScale * (y + height - 2),
Gui::GuiScale * (width - 2),
Gui::GuiScale * (height - 2)
);
glEnable2(GL_SCISSOR_TEST);
glScissor(
Gui::GuiScale * (x + 2),
minecraft->height - Gui::GuiScale * (y + height - 2),
Gui::GuiScale * (width - 2),
Gui::GuiScale * (height - 2)
);
if (text.empty() && !focused) {
drawString(minecraft->font, hint, x + 2, y + 2, 0xff5e5e5e);
}
if (text.empty() && !focused) {
drawString(minecraft->font, hint, x + 2, y + 2, 0xff5e5e5e);
}
if (focused && blink) text.push_back('_');
if (focused && blink) text.push_back('_');
drawString(minecraft->font, text, x + 2, y + 2, 0xffffffff);
drawString(minecraft->font, text, x + 2, y + 2, 0xffffffff);
if (focused && blink) text.pop_back();
if (focused && blink) text.pop_back();
glDisable2(GL_SCISSOR_TEST);
glDisable2(GL_SCISSOR_TEST);
}

View File

@@ -6,6 +6,8 @@
#include <string>
#include "GuiElement.h"
#include "../../Options.h"
#include "../../../platform/input/Mouse.h"
#include "../../../platform/input/Keyboard.h"
class Font;
class Minecraft;
@@ -14,15 +16,15 @@ class TextBox: public GuiElement
{
public:
TextBox(int id, const std::string& msg);
TextBox(int id, int x, int y, const std::string& msg);
TextBox(int id, int x, int y, int w, const std::string& msg);
TextBox(int id, int x, int y, const std::string& msg);
TextBox(int id, int x, int y, int w, int h, const std::string& msg);
virtual void mouseClicked(Minecraft* minecraft, int x, int y, int buttonNum);
virtual void setFocus(Minecraft* minecraft);
virtual bool loseFocus(Minecraft* minecraft);
virtual void render(Minecraft* minecraft, int xm, int ym);
virtual void render(Minecraft* minecraft, int xm, int ym);
virtual void handleKey(int key);
virtual void handleChar(char c);

View File

@@ -0,0 +1,223 @@
#include "ConsoleScreen.h"
#include "../Gui.h"
#include "../../Minecraft.h"
#include "../../player/LocalPlayer.h"
#include "../../../platform/input/Keyboard.h"
#include "../../../world/level/Level.h"
#include "../../../network/RakNetInstance.h"
#include "../../../network/ServerSideNetworkHandler.h"
#include "../../../network/packet/ChatPacket.h"
#include "../../../platform/log.h"
#include <sstream>
#include <cstdlib>
#include <cctype>
ConsoleScreen::ConsoleScreen()
: _input(""),
_cursorBlink(0)
{
}
void ConsoleScreen::init()
{
}
void ConsoleScreen::tick()
{
_cursorBlink++;
}
bool ConsoleScreen::handleBackEvent(bool /*isDown*/)
{
minecraft->setScreen(NULL);
return true;
}
void ConsoleScreen::keyPressed(int eventKey)
{
if (eventKey == Keyboard::KEY_ESCAPE) {
minecraft->setScreen(NULL);
} else if (eventKey == Keyboard::KEY_RETURN) {
execute();
} else if (eventKey == Keyboard::KEY_BACKSPACE) {
if (!_input.empty())
_input.erase(_input.size() - 1, 1);
} else {
super::keyPressed(eventKey);
}
}
void ConsoleScreen::keyboardNewChar(char inputChar)
{
if (inputChar >= 32 && inputChar < 127)
_input += inputChar;
}
// ---------------------------------------------------------------------------
// execute: run _input as a command, print result, close screen
// ---------------------------------------------------------------------------
void ConsoleScreen::execute()
{
if (_input.empty()) {
minecraft->setScreen(NULL);
return;
}
if (_input[0] == '/') {
// Command
std::string result = processCommand(_input);
if (!result.empty())
minecraft->gui.addMessage(result);
} else {
// Chat message: <name> message
std::string msg = std::string("<") + minecraft->player->name + "> " + _input;
if (minecraft->netCallback && minecraft->raknetInstance->isServer()) {
// Hosting a LAN game: displayGameMessage shows locally + broadcasts MessagePacket to clients
static_cast<ServerSideNetworkHandler*>(minecraft->netCallback)->displayGameMessage(msg);
} else if (minecraft->netCallback) {
// Connected client: send ChatPacket to server; server echoes it back as MessagePacket
ChatPacket chatPkt(msg);
minecraft->raknetInstance->send(chatPkt);
} else {
// Singleplayer: show locally only
minecraft->gui.addMessage(msg);
}
}
minecraft->setScreen(NULL);
}
// ---------------------------------------------------------------------------
// processCommand
// ---------------------------------------------------------------------------
static std::string trim(const std::string& s) {
size_t a = s.find_first_not_of(" \t");
if (a == std::string::npos) return "";
size_t b = s.find_last_not_of(" \t");
return s.substr(a, b - a + 1);
}
std::string ConsoleScreen::processCommand(const std::string& raw)
{
// strip leading '/'
std::string line = raw;
if (!line.empty() && line[0] == '/') line = line.substr(1);
line = trim(line);
// tokenise
std::vector<std::string> args;
{
std::istringstream ss(line);
std::string tok;
while (ss >> tok) args.push_back(tok);
}
if (args.empty()) return "";
Level* level = minecraft->level;
if (!level) return "No level loaded.";
// -----------------------------------------------------------------------
// /time ...
// -----------------------------------------------------------------------
if (args[0] == "time") {
if (args.size() < 2)
return "Usage: /time <add|set|query> ...";
const std::string& sub = args[1];
// -- time add <value> -----------------------------------------------
if (sub == "add") {
if (args.size() < 3) return "Usage: /time add <value>";
long delta = std::atol(args[2].c_str());
long newTime = level->getTime() + delta;
level->setTime(newTime);
std::ostringstream out;
out << "Set the time to " << (newTime % Level::TICKS_PER_DAY);
return out.str();
}
// -- time set <value|day|night|noon|midnight> -----------------------
if (sub == "set") {
if (args.size() < 3) return "Usage: /time set <value|day|night|noon|midnight>";
const std::string& val = args[2];
long t = -1;
if (val == "day") t = 1000;
else if (val == "noon") t = 6000;
else if (val == "night") t = 13000;
else if (val == "midnight") t = 18000;
else {
// numeric — accept positive integers only
bool numeric = true;
for (char c : val)
if (!std::isdigit((unsigned char)c)) { numeric = false; break; }
if (!numeric) return std::string("Unknown value: ") + val;
t = std::atol(val.c_str());
}
// Preserve the total day count so only the time-of-day changes
long dayCount = level->getTime() / Level::TICKS_PER_DAY;
long newTime = dayCount * Level::TICKS_PER_DAY + (t % Level::TICKS_PER_DAY);
level->setTime(newTime);
std::ostringstream out;
out << "Set the time to " << t;
return out.str();
}
// -- time query <daytime|gametime|day> ------------------------------
if (sub == "query") {
if (args.size() < 3) return "Usage: /time query <daytime|gametime|day>";
const std::string& what = args[2];
long total = level->getTime();
long daytime = total % Level::TICKS_PER_DAY;
long day = total / Level::TICKS_PER_DAY;
std::ostringstream out;
if (what == "daytime") { out << "The time of day is " << daytime; }
else if (what == "gametime") { out << "The game time is " << total; }
else if (what == "day") { out << "The day is " << day; }
else return std::string("Unknown query: ") + what;
return out.str();
}
return "Unknown sub-command. Usage: /time <add|set|query> ...";
}
return std::string("Unknown command: /") + args[0];
}
// ---------------------------------------------------------------------------
// render
// ---------------------------------------------------------------------------
void ConsoleScreen::render(int /*xm*/, int /*ym*/, float /*a*/)
{
// Dim the game world slightly
fillGradient(0, 0, width, height, 0x00000000, 0x40000000);
const int boxH = 12;
const int boxY = height - boxH - 2;
const int boxX0 = 2;
const int boxX1 = width - 2;
// Input box background
fill(boxX0, boxY, boxX1, boxY + boxH, 0xc0000000);
// Border
fill(boxX0, boxY, boxX1, boxY + 1, 0xff808080);
fill(boxX0, boxY + boxH - 1, boxX1, boxY + boxH, 0xff808080);
fill(boxX0, boxY, boxX0 + 1, boxY + boxH, 0xff808080);
fill(boxX1 - 1, boxY, boxX1, boxY + boxH, 0xff808080);
// Input text + blinking cursor
std::string displayed = _input;
if ((_cursorBlink / 10) % 2 == 0)
displayed += '_';
// Placeholder hint when empty
if (_input.empty() && (_cursorBlink / 10) % 2 != 0)
font->drawShadow("Type a message or /command", (float)(boxX0 + 2), (float)(boxY + 2), 0xff606060);
else
font->drawShadow(displayed, (float)(boxX0 + 2), (float)(boxY + 2), 0xffffffff);
}

View File

@@ -0,0 +1,34 @@
#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__ConsoleScreen_H__
#define NET_MINECRAFT_CLIENT_GUI_SCREENS__ConsoleScreen_H__
#include "../Screen.h"
#include <string>
class ConsoleScreen: public Screen
{
typedef Screen super;
public:
ConsoleScreen();
virtual ~ConsoleScreen() {}
void init();
void render(int xm, int ym, float a);
void tick();
virtual bool renderGameBehind() { return true; }
virtual bool isInGameScreen() { return true; }
virtual bool isPauseScreen() { return false; }
virtual void keyPressed(int eventKey);
virtual void keyboardNewChar(char inputChar);
virtual bool handleBackEvent(bool isDown);
private:
void execute();
std::string processCommand(const std::string& cmd);
std::string _input;
int _cursorBlink; // tick counter for cursor blink
};
#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__ConsoleScreen_H__*/

View File

@@ -0,0 +1,118 @@
#include "CreditsScreen.h"
#include "StartMenuScreen.h"
#include "OptionsScreen.h"
#include "../../Minecraft.h"
#include "../components/Button.h"
#include "../components/ImageButton.h"
CreditsScreen::CreditsScreen()
: bHeader(NULL), btnBack(NULL)
{}
CreditsScreen::~CreditsScreen() {
if (bHeader) delete bHeader;
if (btnBack) delete btnBack;
}
void CreditsScreen::init() {
bHeader = new Touch::THeader(0, "Credits");
btnBack = new ImageButton(1, "");
{
ImageDef def;
def.name = "gui/touchgui.png";
def.width = 34;
def.height = 26;
def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height));
btnBack->setImageDef(def, true);
}
buttons.push_back(bHeader);
buttons.push_back(btnBack);
// prepare text lines
_lines.clear();
_lines.push_back("Minecraft: Pocket Edition");
_lines.push_back("Original game by Mojang");
_lines.push_back("");
_lines.push_back("Programmers:");
_lines.push_back("mschiller890");
_lines.push_back("InviseDivine");
_lines.push_back("Kolyah35");
_lines.push_back("");
_lines.push_back("[Gold]Join our Discord server:[/Gold] [Green]url.....[/Green]");
_scrollSpeed = 0.5f;
_scrollY = height; // start below screen
}
void CreditsScreen::setupPositions() {
int buttonHeight = btnBack->height;
btnBack->x = width - btnBack->width;
btnBack->y = 0;
if (bHeader) {
bHeader->x = 0;
bHeader->y = 0;
bHeader->width = width - btnBack->width;
bHeader->height = btnBack->height;
}
// reset scroll starting position when screen size changes
_scrollY = height;
}
void CreditsScreen::tick() {
// move text upward
_scrollY -= _scrollSpeed;
// if text has scrolled off the top, restart
float totalHeight = _lines.size() * (minecraft->font->lineHeight + 8);
if (_scrollY + totalHeight < 0) {
_scrollY = height;
}
}
void CreditsScreen::render(int xm, int ym, float a) {
renderBackground();
super::render(xm, ym, a);
int w = width;
Font* font = minecraft->font;
float y = _scrollY;
const float lineHeight = font->lineHeight + 8;
for (size_t i = 0; i < _lines.size(); ++i) {
const std::string& line = _lines[i];
// use color-tag-aware drawing, centre by total width
float lineWidth = Gui::getColoredWidth(font, line);
Gui::drawColoredString(font, line, w/2 - lineWidth/2, (int)y, 255);
// underline hyperlink lines manually
if (line.find("http") != std::string::npos || line.find("discord.gg") != std::string::npos) {
float x0 = w/2 - lineWidth/2;
float y0 = y + font->lineHeight - 1;
this->fill(x0, y0, x0 + lineWidth, y0 + 1, 0xffffffff);
}
y += lineHeight;
}
}
void CreditsScreen::buttonClicked(Button* button) {
if (button->id == 1) {
minecraft->setScreen(new OptionsScreen());
}
}
void CreditsScreen::mouseClicked(int x, int y, int buttonNum) {
// map click to a line in the scrolling text
const float lineHeight = minecraft->font->lineHeight + 8;
for (size_t i = 0; i < _lines.size(); ++i) {
float lineY = _scrollY + i * lineHeight;
if (y >= lineY && y < lineY + lineHeight) {
const std::string& line = _lines[i];
size_t start = line.find("http");
if (start == std::string::npos)
start = line.find("discord.gg");
if (start != std::string::npos) {
// extract until space
size_t end = line.find(' ', start);
std::string url = line.substr(start, (end == std::string::npos) ? std::string::npos : end - start);
minecraft->platform()->openURL(url);
return;
}
}
}
super::mouseClicked(x, y, buttonNum);
}

View File

@@ -0,0 +1,32 @@
#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__CreditsScreen_H__
#define NET_MINECRAFT_CLIENT_GUI_SCREENS__CreditsScreen_H__
#include "../Screen.h"
#include "../components/Button.h"
class ImageButton;
#include <vector>
#include <string>
class CreditsScreen: public Screen {
public:
typedef Screen super;
CreditsScreen();
virtual ~CreditsScreen();
void init();
void setupPositions();
virtual void tick();
void render(int xm, int ym, float a);
void buttonClicked(Button* button);
virtual void mouseClicked(int x, int y, int buttonNum);
private:
Touch::THeader* bHeader;
ImageButton* btnBack;
std::vector<std::string> _lines;
float _scrollY;
float _scrollSpeed;
};
#endif /* NET_MINECRAFT_CLIENT_GUI_SCREENS__CreditsScreen_H__ */

View File

@@ -1,46 +1,69 @@
#include "OptionsScreen.h"
#include "StartMenuScreen.h"
#include "UsernameScreen.h"
#include "DialogDefinitions.h"
#include "../../Minecraft.h"
#include "../../../AppPlatform.h"
#include "CreditsScreen.h"
#include "../components/OptionsPane.h"
#include "../components/ImageButton.h"
#include "../components/OptionsGroup.h"
OptionsScreen::OptionsScreen()
: btnClose(NULL),
bHeader(NULL),
selectedCategory(0) {
: btnClose(NULL),
bHeader(NULL),
btnChangeUsername(NULL),
btnCredits(NULL),
selectedCategory(0) {
}
OptionsScreen::~OptionsScreen() {
if(btnClose != NULL) {
if (btnClose != NULL) {
delete btnClose;
btnClose = NULL;
}
if(bHeader != NULL) {
delete bHeader,
if (bHeader != NULL) {
delete bHeader;
bHeader = NULL;
}
for(std::vector<Touch::TButton*>::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) {
if(*it != NULL) {
delete *it;
if (btnChangeUsername != NULL) {
delete btnChangeUsername;
btnChangeUsername = NULL;
}
if (btnCredits != NULL) {
delete btnCredits;
btnCredits = NULL;
}
for (std::vector<Touch::TButton*>::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) {
if (*it != NULL) {
delete* it;
*it = NULL;
}
}
for(std::vector<OptionsPane*>::iterator it = optionPanes.begin(); it != optionPanes.end(); ++it) {
if(*it != NULL) {
delete *it;
for (std::vector<OptionsPane*>::iterator it = optionPanes.begin(); it != optionPanes.end(); ++it) {
if (*it != NULL) {
delete* it;
*it = NULL;
}
}
categoryButtons.clear();
}
void OptionsScreen::init() {
bHeader = new Touch::THeader(0, "Options");
btnClose = new ImageButton(1, "");
ImageDef def;
def.name = "gui/touchgui.png";
def.width = 34;
@@ -53,129 +76,199 @@ void OptionsScreen::init() {
categoryButtons.push_back(new Touch::TButton(3, "Game"));
categoryButtons.push_back(new Touch::TButton(4, "Controls"));
categoryButtons.push_back(new Touch::TButton(5, "Graphics"));
btnChangeUsername = new Button(10, "Username");
btnCredits = new Button(11, "Credits");
buttons.push_back(bHeader);
buttons.push_back(btnClose);
for(std::vector<Touch::TButton*>::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) {
buttons.push_back(btnChangeUsername);
buttons.push_back(btnCredits);
for (std::vector<Touch::TButton*>::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) {
buttons.push_back(*it);
tabButtons.push_back(*it);
}
generateOptionScreens();
generateOptionScreens();
// start with first category selected
selectCategory(0);
}
void OptionsScreen::setupPositions() {
int buttonHeight = btnClose->height;
btnClose->x = width - btnClose->width;
btnClose->y = 0;
int offsetNum = 1;
for(std::vector<Touch::TButton*>::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) {
for (std::vector<Touch::TButton*>::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) {
(*it)->x = 0;
(*it)->y = offsetNum * buttonHeight;
(*it)->selected = false;
offsetNum++;
}
bHeader->x = 0;
bHeader->y = 0;
bHeader->width = width - btnClose->width;
bHeader->height = btnClose->height;
for(std::vector<OptionsPane*>::iterator it = optionPanes.begin(); it != optionPanes.end(); ++it) {
if(categoryButtons.size() > 0 && categoryButtons[0] != NULL) {
// Username button (bottom-left)
if (btnChangeUsername != NULL) {
btnChangeUsername->width = categoryButtons.empty() ? 80 : categoryButtons[0]->width;
btnChangeUsername->height = btnClose->height;
btnChangeUsername->x = 0;
btnChangeUsername->y = height - btnChangeUsername->height;
}
// Credits button (bottom-right)
if (btnCredits != NULL) {
btnCredits->width = btnChangeUsername->width;
btnCredits->height = btnChangeUsername->height;
btnCredits->x = width - btnCredits->width;
btnCredits->y = height - btnCredits->height;
}
for (std::vector<OptionsPane*>::iterator it = optionPanes.begin(); it != optionPanes.end(); ++it) {
if (categoryButtons.size() > 0 && categoryButtons[0] != NULL) {
(*it)->x = categoryButtons[0]->width;
(*it)->y = bHeader->height;
(*it)->width = width - categoryButtons[0]->width;
(*it)->setupPositions();
}
}
selectCategory(0);
// don't override user selection on resize
}
void OptionsScreen::render( int xm, int ym, float a ) {
void OptionsScreen::render(int xm, int ym, float a) {
renderBackground();
super::render(xm, ym, a);
int xmm = xm * width / minecraft->width;
int ymm = ym * height / minecraft->height - 1;
if(currentOptionPane != NULL)
if (currentOptionPane != NULL)
currentOptionPane->render(minecraft, xmm, ymm);
}
void OptionsScreen::removed()
{
void OptionsScreen::removed() {
}
void OptionsScreen::buttonClicked( Button* button ) {
if(button == btnClose) {
minecraft->reloadOptions();
void OptionsScreen::buttonClicked(Button* button) {
if (button == btnClose) {
minecraft->options.save();
minecraft->screenChooser.setScreen(SCREEN_STARTMENU);
} else if(button->id > 1 && button->id < 7) {
// This is a category button
}
else if (button == btnChangeUsername) {
minecraft->options.save();
minecraft->setScreen(new UsernameScreen());
}
else if (button->id > 1 && button->id < 7) {
int categoryButton = button->id - categoryButtons[0]->id;
selectCategory(categoryButton);
}
else if (button == btnCredits) {
minecraft->setScreen(new CreditsScreen());
}
}
void OptionsScreen::selectCategory( int index ) {
void OptionsScreen::selectCategory(int index) {
int currentIndex = 0;
for(std::vector<Touch::TButton*>::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) {
if(index == currentIndex) {
for (std::vector<Touch::TButton*>::iterator it = categoryButtons.begin(); it != categoryButtons.end(); ++it) {
if (index == currentIndex)
(*it)->selected = true;
} else {
else
(*it)->selected = false;
}
currentIndex++;
}
if(index < (int)optionPanes.size())
if (index < (int)optionPanes.size())
currentOptionPane = optionPanes[index];
}
void OptionsScreen::generateOptionScreens() {
optionPanes.push_back(new OptionsPane());
optionPanes.push_back(new OptionsPane());
optionPanes.push_back(new OptionsPane());
optionPanes.push_back(new OptionsPane());
// Mojang Pane
// Login Pane
optionPanes[0]->createOptionsGroup("options.group.mojang")
//.addOptionItem(&Options::Option::THIRD_PERSON, minecraft);
.addOptionItem(&Options::Option::SENSITIVITY, minecraft);
// int mojangGroup = optionPanes[0]->createOptionsGroup("Mojang");
// static const int arr[] = {5,4,3,15};
// std::vector<int> vec (arr, arr + sizeof(arr) / sizeof(arr[0]) );
// optionPanes[0]->createStepSlider(minecraft, mojangGroup, "This works?", &Options::Option::DIFFICULTY, vec);
//
// // Game Pane
// int gameGroup = optionPanes[1]->createOptionsGroup("Game");
// optionPanes[1]->createToggle(gameGroup, "Third person camera", &Options::Option::THIRD_PERSON);
// optionPanes[1]->createToggle(gameGroup, "Server visible", &Options::Option::SERVER_VISIBLE);
//
// // Input Pane
// int controlsGroup = optionPanes[2]->createOptionsGroup("Controls");
// optionPanes[2]->createToggle(controlsGroup, "Invert X-axis", &Options::Option::INVERT_MOUSE);
// optionPanes[2]->createToggle(controlsGroup, "Lefty", &Options::Option::LEFT_HANDED);
// optionPanes[2]->createToggle(controlsGroup, "Use touch screen", &Options::Option::USE_TOUCHSCREEN);
// optionPanes[2]->createToggle(controlsGroup, "Split touch controls", &Options::Option::USE_TOUCH_JOYPAD);
// int feedBackGroup = optionPanes[2]->createOptionsGroup("Feedback");
// optionPanes[2]->createToggle(feedBackGroup, "Vibrate on destroy", &Options::Option::DESTROY_VIBRATION);
//
// int graphicsGroup = optionPanes[3]->createOptionsGroup("Graphics");
// optionPanes[3]->createProgressSlider(minecraft, graphicsGroup, "Gui Scale", &Options::Option::PIXELS_PER_MILLIMETER, 3, 4);
// optionPanes[3]->createToggle(graphicsGroup, "Fancy Graphics", &Options::Option::INVERT_MOUSE);
// optionPanes[3]->createToggle(graphicsGroup, "Fancy Skies", &Options::Option::INVERT_MOUSE);
// optionPanes[3]->createToggle(graphicsGroup, "Animated water", &Options::Option::INVERT_MOUSE);
// int experimentalGraphicsGroup = optionPanes[3]->createOptionsGroup("Experimental graphics");
// optionPanes[3]->createToggle(experimentalGraphicsGroup, "Soft shadows", &Options::Option::INVERT_MOUSE);
// Game Pane
optionPanes[1]->createOptionsGroup("options.group.game")
.addOptionItem(&Options::Option::DIFFICULTY, minecraft)
.addOptionItem(&Options::Option::SERVER_VISIBLE, minecraft)
.addOptionItem(&Options::Option::THIRD_PERSON, minecraft)
.addOptionItem(&Options::Option::GUI_SCALE, minecraft);
// Controls Pane
optionPanes[2]->createOptionsGroup("options.group.controls")
.addOptionItem(&Options::Option::INVERT_MOUSE, minecraft);
// Graphics Pane
optionPanes[3]->createOptionsGroup("options.group.graphics")
.addOptionItem(&Options::Option::GRAPHICS, minecraft)
.addOptionItem(&Options::Option::VIEW_BOBBING, minecraft)
.addOptionItem(&Options::Option::AMBIENT_OCCLUSION, minecraft)
.addOptionItem(&Options::Option::ANAGLYPH, minecraft)
.addOptionItem(&Options::Option::LIMIT_FRAMERATE, minecraft)
.addOptionItem(&Options::Option::VSYNC, minecraft)
.addOptionItem(&Options::Option::MUSIC, minecraft)
.addOptionItem(&Options::Option::SOUND, minecraft);
}
void OptionsScreen::mouseClicked( int x, int y, int buttonNum ) {
if(currentOptionPane != NULL)
void OptionsScreen::mouseClicked(int x, int y, int buttonNum) {
if (currentOptionPane != NULL)
currentOptionPane->mouseClicked(minecraft, x, y, buttonNum);
super::mouseClicked(x, y, buttonNum);
}
void OptionsScreen::mouseReleased( int x, int y, int buttonNum ) {
if(currentOptionPane != NULL)
void OptionsScreen::mouseReleased(int x, int y, int buttonNum) {
if (currentOptionPane != NULL)
currentOptionPane->mouseReleased(minecraft, x, y, buttonNum);
super::mouseReleased(x, y, buttonNum);
}
void OptionsScreen::tick() {
if(currentOptionPane != NULL)
if (currentOptionPane != NULL)
currentOptionPane->tick(minecraft);
super::tick();
}

View File

@@ -10,28 +10,36 @@ class OptionsPane;
class OptionsScreen: public Screen
{
typedef Screen super;
void init();
void init();
void generateOptionScreens();
public:
OptionsScreen();
~OptionsScreen();
void setupPositions();
void buttonClicked( Button* button );
void buttonClicked(Button* button);
void render(int xm, int ym, float a);
void removed();
void selectCategory(int index);
virtual void mouseClicked( int x, int y, int buttonNum );
virtual void mouseReleased( int x, int y, int buttonNum );
virtual void mouseClicked(int x, int y, int buttonNum);
virtual void mouseReleased(int x, int y, int buttonNum);
virtual void tick();
private:
Touch::THeader* bHeader;
ImageButton* btnClose;
Button* btnChangeUsername;
Button* btnCredits; // <-- ADD THIS
std::vector<Touch::TButton*> categoryButtons;
std::vector<OptionsPane*> optionPanes;
OptionsPane* currentOptionPane;
int selectedCategory;
};

View File

@@ -230,7 +230,6 @@ SelectWorldScreen::SelectWorldScreen()
bBack (3, "Back"),
bWorldView(4, ""),
worldsList(NULL),
_state(_STATE_DEFAULT),
_hasStartedLevel(false)
{
bDelete.active = false;
@@ -244,17 +243,11 @@ SelectWorldScreen::~SelectWorldScreen()
void SelectWorldScreen::buttonClicked(Button* button)
{
if (button->id == bCreate.id) {
//minecraft->setScreen( new CreateWorldScreen() );
//minecraft->locateMultiplayer();
//minecraft->setScreen(new JoinGameScreen());
//minecraft->hostMultiplayer();
//minecraft->setScreen(new ProgressScreen());
if (_state == _STATE_DEFAULT && !_hasStartedLevel) {
minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_CREATE_NEW_WORLD);
_state = _STATE_CREATEWORLD;
}
// open in-game world-creation screen instead of using platform dialog
if (!_hasStartedLevel) {
std::string name = getUniqueLevelName("world");
minecraft->setScreen(new SimpleChooseLevelScreen(name));
}
}
if (button->id == bDelete.id) {
if (isIndexValid(worldsList->selectedItem)) {
@@ -294,70 +287,6 @@ static char ILLEGAL_FILE_CHARACTERS[] = {
void SelectWorldScreen::tick()
{
if (_state == _STATE_CREATEWORLD) {
#if defined(RPI)
std::string levelId = getUniqueLevelName("world");
LevelSettings settings(getEpochTimeS(), GameType::Creative);
minecraft->selectLevel(levelId, levelId, settings);
minecraft->hostMultiplayer();
minecraft->setScreen(new ProgressScreen());
_hasStartedLevel = true;
#elif defined(WIN32)
std::string name = getUniqueLevelName("perf");
minecraft->setScreen(new SimpleChooseLevelScreen(name));
#else
int status = minecraft->platform()->getUserInputStatus();
if (status > -1) {
if (status == 1) {
StringVector sv = minecraft->platform()->getUserInput();
// Read the level name.
// 1) Trim name 2) Remove all bad chars 3) Append '-' chars 'til the name is unique
std::string levelName = Util::stringTrim(sv[0]);
std::string levelId = levelName;
for (int i = 0; i < sizeof(ILLEGAL_FILE_CHARACTERS) / sizeof(char); ++i)
levelId = Util::stringReplace(levelId, std::string(1, ILLEGAL_FILE_CHARACTERS[i]), "");
if ((int)levelId.length() == 0) {
levelId = "no_name";
}
levelId = getUniqueLevelName(levelId);
// Read the seed
int seed = getEpochTimeS();
if (sv.size() >= 2) {
std::string seedString = Util::stringTrim(sv[1]);
if (seedString.length() > 0) {
int tmpSeed;
// Try to read it as an integer
if (sscanf(seedString.c_str(), "%d", &tmpSeed) > 0) {
seed = tmpSeed;
} // Hash the "seed"
else {
seed = Util::hashCode(seedString);
}
}
}
// Read the game mode
bool isCreative = true;
if (sv.size() >= 3 && sv[2] == "survival")
isCreative = false;
// Start a new level with the given name and seed
LevelSettings settings(seed, isCreative? GameType::Creative : GameType::Survival);
LOGI("Creating a level with id '%s', name '%s' and seed '%d'\n", levelId.c_str(), levelName.c_str(), seed);
minecraft->selectLevel(levelId, levelName, settings);
minecraft->hostMultiplayer();
minecraft->setScreen(new ProgressScreen());
_hasStartedLevel = true;
}
_state = _STATE_DEFAULT;
}
#endif
return;
}
worldsList->tick();
if (worldsList->hasPickedLevel) {

View File

@@ -104,9 +104,6 @@ private:
bool _mouseHasBeenUp;
bool _hasStartedLevel;
int _state;
static const int _STATE_DEFAULT = 0;
static const int _STATE_CREATEWORLD = 1;
//LevelStorageSource* levels;
};

View File

@@ -2,139 +2,229 @@
#include "ProgressScreen.h"
#include "ScreenChooser.h"
#include "../components/Button.h"
#include "../components/ImageButton.h"
#include "../../Minecraft.h"
#include "../../../world/level/LevelSettings.h"
#include "../../../platform/time.h"
#include "client/gamemode/GameMode.h"
#include "../../../platform/input/Keyboard.h"
#include "../../../platform/log.h"
SimpleChooseLevelScreen::SimpleChooseLevelScreen(const std::string& levelName)
:
// bCreative(0),
bGamemode(0),
bBack(0),
bCreate(0),
levelName(levelName),
hasChosen(false),
gamemode(GameType::Survival),
tLevelName(0, "World name"),
tSeed(1, "World seed")
: bHeader(0),
bGamemode(0),
bBack(0),
bCreate(0),
levelName(levelName),
hasChosen(false),
gamemode(GameType::Survival),
tLevelName(0, "World name"),
tSeed(1, "World seed")
{
}
SimpleChooseLevelScreen::~SimpleChooseLevelScreen()
{
// delete bCreative;
delete bGamemode;
delete bBack;
if (bHeader) delete bHeader;
delete bGamemode;
delete bBack;
delete bCreate;
}
void SimpleChooseLevelScreen::init()
{
if (minecraft->useTouchscreen()) {
// bCreative = new Touch::TButton(1, "Creative mode");
bGamemode = new Touch::TButton(2, "Survival mode");
bBack = new Touch::TButton(3, "Back");
bCreate = new Touch::TButton(4, "Create");
} else {
// bCreative = new Button(1, "Creative mode");
bGamemode = new Button(2, "Survival mode");
bBack = new Button(3, "Back");
bCreate = new Button(4, "Create");
}
// buttons.push_back(bCreative);
buttons.push_back(bGamemode);
buttons.push_back(bBack);
buttons.push_back(bCreate);
// header + close button
bHeader = new Touch::THeader(0, "Create World");
// create the back/X button as ImageButton like CreditsScreen
bBack = new ImageButton(2, "");
{
ImageDef def;
def.name = "gui/touchgui.png";
def.width = 34;
def.height = 26;
def.setSrc(IntRectangle(150, 0, (int)def.width, (int)def.height));
bBack->setImageDef(def, true);
}
if (minecraft->useTouchscreen()) {
bGamemode = new Touch::TButton(1, "Survival mode");
bCreate = new Touch::TButton(3, "Create");
} else {
bGamemode = new Button(1, "Survival mode");
bCreate = new Button(3, "Create");
}
textBoxes.push_back(&tLevelName);
textBoxes.push_back(&tSeed);
buttons.push_back(bHeader);
buttons.push_back(bBack);
buttons.push_back(bGamemode);
buttons.push_back(bCreate);
// tabButtons.push_back(bCreative);
tabButtons.push_back(bGamemode);
tabButtons.push_back(bBack);
tabButtons.push_back(bCreate);
tabButtons.push_back(bGamemode);
tabButtons.push_back(bBack);
tabButtons.push_back(bCreate);
textBoxes.push_back(&tLevelName);
textBoxes.push_back(&tSeed);
}
void SimpleChooseLevelScreen::setupPositions()
{
const int padding = 5;
int buttonHeight = bBack->height;
/* bCreative->width = */ bGamemode->width = 120;
tLevelName.width = tSeed.width = 120;
bBack->width = bCreate->width = 60 - padding;
// bCreative->x = (width - bCreative->width) / 2;
// bCreative->y = height/3 - 40;
bGamemode->x = (width - bGamemode->width) / 2;
bGamemode->y = 2*height/3 - 30;
bBack->x = bGamemode->x;
bCreate->x = bGamemode->x + bGamemode->width - bCreate->width;
bBack->y = bCreate->y = height - 40;
// position back button in upper-right
bBack->x = width - bBack->width;
bBack->y = 0;
tLevelName.x = tSeed.x = bGamemode->x;
tLevelName.y = 20;
tSeed.y = tLevelName.y + 30;
// header occupies remaining top bar
if (bHeader) {
bHeader->x = 0;
bHeader->y = 0;
bHeader->width = width - bBack->width;
bHeader->height = buttonHeight;
}
// layout the form elements below the header
int centerX = width / 2;
const int padding = 5;
tLevelName.width = tSeed.width = 200;
tLevelName.x = centerX - tLevelName.width / 2;
tLevelName.y = buttonHeight + 20;
tSeed.x = tLevelName.x;
tSeed.y = tLevelName.y + 30;
bGamemode->width = 140;
bGamemode->x = centerX - bGamemode->width / 2;
// compute vertical centre for gamemode in remaining space
{
int bottomPad = 20;
int availTop = buttonHeight + 20 + 30 + 10; // just below seed
int availBottom = height - bottomPad - bCreate->height - 10; // leave some gap before create
int availHeight = availBottom - availTop;
if (availHeight < 0) availHeight = 0;
bGamemode->y = availTop + (availHeight - bGamemode->height) / 2;
}
bCreate->width = 100;
bCreate->x = centerX - bCreate->width / 2;
int bottomPadding = 20;
bCreate->y = height - bottomPadding - bCreate->height;
}
void SimpleChooseLevelScreen::tick()
{
// let any textboxes handle their own blinking/input
for (auto* tb : textBoxes)
tb->tick(minecraft);
}
void SimpleChooseLevelScreen::render( int xm, int ym, float a )
{
renderDirtBackground(0);
renderDirtBackground(0);
glEnable2(GL_BLEND);
const char* str = NULL;
const char* str = NULL;
if (gamemode == GameType::Survival) {
str = "Mobs, health and gather resources";
} else if (gamemode == GameType::Creative) {
str = "Unlimited resources and flying";
}
if (str) {
drawCenteredString(minecraft->font, str, width/2, bGamemode->y + bGamemode->height + 4, 0xffcccccc);
}
if (gamemode == GameType::Survival) {
str = "Mobs, health and gather resources";
} else if (gamemode == GameType::Creative) {
str = "Unlimited resources and flying";
}
drawString(minecraft->font, "World name:", tLevelName.x, tLevelName.y - Font::DefaultLineHeight - 2, 0xffcccccc);
drawString(minecraft->font, "World seed:", tSeed.x, tSeed.y - Font::DefaultLineHeight - 2, 0xffcccccc);
if (str) {
drawCenteredString(minecraft->font, str, width/2, bGamemode->y + bGamemode->height + 4, 0xffcccccc);
}
drawString(minecraft->font, "World name:", tLevelName.x, tLevelName.y - Font::DefaultLineHeight - 2, 0xffcccccc);
drawString(minecraft->font, "World seed:", tSeed.x, tSeed.y - Font::DefaultLineHeight - 2, 0xffcccccc);
Screen::render(xm, ym, a);
Screen::render(xm, ym, a);
glDisable2(GL_BLEND);
}
// mouse clicks should also manage textbox focus explicitly
void SimpleChooseLevelScreen::mouseClicked(int x, int y, int buttonNum)
{
if (buttonNum == MouseAction::ACTION_LEFT) {
// determine if the click landed on either textbox or its label above
int lvlTop = tLevelName.y - (Font::DefaultLineHeight + 4);
int lvlBottom = tLevelName.y + tLevelName.height;
int lvlLeft = tLevelName.x;
int lvlRight = tLevelName.x + tLevelName.width;
bool clickedLevel = x >= lvlLeft && x < lvlRight && y >= lvlTop && y < lvlBottom;
int seedTop = tSeed.y - (Font::DefaultLineHeight + 4);
int seedBottom = tSeed.y + tSeed.height;
int seedLeft = tSeed.x;
int seedRight = tSeed.x + tSeed.width;
bool clickedSeed = x >= seedLeft && x < seedRight && y >= seedTop && y < seedBottom;
if (clickedLevel) {
LOGI("SimpleChooseLevelScreen: level textbox clicked (%d,%d)\n", x, y);
tLevelName.setFocus(minecraft);
tSeed.loseFocus(minecraft);
} else if (clickedSeed) {
LOGI("SimpleChooseLevelScreen: seed textbox clicked (%d,%d)\n", x, y);
tSeed.setFocus(minecraft);
tLevelName.loseFocus(minecraft);
} else {
// click outside both fields -> blur both
tLevelName.loseFocus(minecraft);
tSeed.loseFocus(minecraft);
}
}
// allow normal button and textbox handling too
Screen::mouseClicked(x, y, buttonNum);
}
void SimpleChooseLevelScreen::buttonClicked( Button* button )
{
if (button == bBack) {
minecraft->screenChooser.setScreen(SCREEN_STARTMENU);
return;
}
if (hasChosen)
return;
if (hasChosen)
return;
if (button == bGamemode) {
gamemode ^= 1;
bGamemode->msg = (gamemode == GameType::Survival) ? "Survival mode" : "Creative mode";
}
if (button == bGamemode) {
gamemode ^= 1;
bGamemode->msg = (gamemode == GameType::Survival) ? "Survival mode" : "Creative mode";
return;
}
if (button == bCreate) {
int seed = getEpochTimeS();
if (button == bCreate) {
int seed = getEpochTimeS();
if (!tSeed.text.empty()) {
std::string seedString = Util::stringTrim(tSeed.text);
int tmpSeed;
if (sscanf(seedString.c_str(), "%d", &tmpSeed) > 0) {
seed = tmpSeed;
} else {
seed = Util::hashCode(seedString);
}
}
std::string levelId = getUniqueLevelName(tLevelName.text);
LevelSettings settings(seed, gamemode);
minecraft->selectLevel(levelId, levelId, settings);
minecraft->hostMultiplayer();
minecraft->setScreen(new ProgressScreen());
hasChosen = true;
return;
}
if (!tSeed.text.empty()) {
std::string seedString = Util::stringTrim(tSeed.text);
int tmpSeed;
// Try to read it as an integer
if (sscanf(seedString.c_str(), "%d", &tmpSeed) > 0) {
seed = tmpSeed;
} // Hash the "seed"
else {
seed = Util::hashCode(seedString);
}
}
if (button == bBack) {
minecraft->screenChooser.setScreen(SCREEN_STARTMENU);
}
}
std::string levelId = getUniqueLevelName(tLevelName.text);
LevelSettings settings(seed, gamemode);
minecraft->selectLevel(levelId, levelId, settings);
minecraft->hostMultiplayer();
minecraft->setScreen(new ProgressScreen());
hasChosen = true;
}
void SimpleChooseLevelScreen::keyPressed(int eventKey)
{
if (eventKey == Keyboard::KEY_ESCAPE) {
minecraft->screenChooser.setScreen(SCREEN_STARTMENU);
return;
}
// let base class handle navigation and text box keys
Screen::keyPressed(eventKey);
}
void SimpleChooseLevelScreen::keyboardNewChar(char inputChar)
{
// forward character input to focused textbox(s)
for (auto* tb : textBoxes) tb->handleChar(inputChar);
}
bool SimpleChooseLevelScreen::handleBackEvent(bool isDown) {

View File

@@ -3,8 +3,9 @@
#include "ChooseLevelScreen.h"
#include "../components/TextBox.h"
#include "../components/Button.h" // for Touch::THeader
class Button;
class ImageButton;
class SimpleChooseLevelScreen: public ChooseLevelScreen
{
@@ -14,23 +15,25 @@ public:
virtual ~SimpleChooseLevelScreen();
void init();
void setupPositions();
void tick();
void render(int xm, int ym, float a);
void buttonClicked(Button* button);
bool handleBackEvent(bool isDown);
virtual void keyPressed(int eventKey);
virtual void keyboardNewChar(char inputChar);
virtual void mouseClicked(int x, int y, int buttonNum);
private:
// Button* bCreative;
Touch::THeader* bHeader;
Button* bGamemode;
Button* bBack;
ImageButton* bBack;
Button* bCreate;
bool hasChosen;
std::string levelName;
int gamemode;
TextBox tLevelName;

View File

@@ -1,4 +1,5 @@
#include "StartMenuScreen.h"
#include "UsernameScreen.h"
#include "SelectWorldScreen.h"
#include "ProgressScreen.h"
#include "JoinGameScreen.h"
@@ -38,6 +39,10 @@ StartMenuScreen::~StartMenuScreen()
void StartMenuScreen::init()
{
if (minecraft->options.username.empty()) {
return; // tick() will redirect to UsernameScreen
}
buttons.push_back(&bHost);
buttons.push_back(&bJoin);
//buttons.push_back(&bTest);
@@ -57,11 +62,8 @@ void StartMenuScreen::init()
copyright = "\xffMojang AB";//. Do not distribute!";
#ifdef PRE_ANDROID23
std::string versionString = Common::getGameVersionString("j");
#else
std::string versionString = Common::getGameVersionString();
#endif
// always show base version string, suffix was previously added for Android builds
std::string versionString = Common::getGameVersionString();
#ifdef DEMO_MODE
#ifdef __APPLE__
@@ -106,6 +108,10 @@ void StartMenuScreen::setupPositions() {
}
void StartMenuScreen::tick() {
if (minecraft->options.username.empty()) {
minecraft->setScreen(new UsernameScreen());
return;
}
_updateLicense();
}
@@ -181,8 +187,19 @@ void StartMenuScreen::render( int xm, int ym, float a )
drawString(font, version, versionPosX, 62, /*50,*/ 0xffcccccc);//0x666666);
drawString(font, copyright, copyrightPosX, height - 10, 0xffffff);
Screen::render(xm, ym, a);
glEnable2(GL_BLEND);
glBlendFunc2(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glColor4f2(1, 1, 1, 1);
if (Textures::isTextureIdValid(minecraft->textures->loadAndBindTexture("gui/logo/github.png")))
blit(2, height - 10, 0, 0, 8, 8, 256, 256);
{
std::string txt = "Kolyah35/minecraft-pe-0.6.1";
float wtxt = font->width(txt);
Gui::drawColoredString(font, txt, 12, height - 10, 255);
// underline link
float y0 = height - 10 + font->lineHeight - 1;
this->fill(12, (int)y0, 12 + (int)wtxt, (int)(y0 + 1), 0xffffffff);
}
}
void StartMenuScreen::_updateLicense()
@@ -202,6 +219,17 @@ void StartMenuScreen::_updateLicense()
}
}
void StartMenuScreen::mouseClicked(int x, int y, int buttonNum) {
const int logoX = 2;
const int logoW = 8 + 2 + font->width("Kolyah35/minecraft-pe-0.6.1");
const int logoY = height - 10;
const int logoH = 10;
if (x >= logoX && x <= logoX + logoW && y >= logoY && y <= logoY + logoH)
minecraft->platform()->openURL("https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1");
else
Screen::mouseClicked(x, y, buttonNum);
}
bool StartMenuScreen::handleBackEvent( bool isDown ) {
minecraft->quit();
return true;

View File

@@ -17,6 +17,7 @@ public:
void render(int xm, int ym, float a);
void buttonClicked(Button* button);
virtual void mouseClicked(int x, int y, int buttonNum);
bool handleBackEvent(bool isDown);
bool isInGameScreen();
private:

View File

@@ -0,0 +1,128 @@
#include "UsernameScreen.h"
#include "StartMenuScreen.h"
#include "../../Minecraft.h"
#include "../../User.h"
#include "../Font.h"
#include "../components/Button.h"
#include "../../../platform/input/Keyboard.h"
#include "../../../AppPlatform.h"
UsernameScreen::UsernameScreen()
: _btnDone(0, "Done"),
_input(""),
_cursorBlink(0)
{
}
UsernameScreen::~UsernameScreen()
{
}
void UsernameScreen::init()
{
_input = "";
_btnDone.active = false; // disabled until name typed
buttons.push_back(&_btnDone);
setupPositions();
}
void UsernameScreen::setupPositions()
{
_btnDone.width = 120;
_btnDone.height = 20;
_btnDone.x = (width - _btnDone.width) / 2;
_btnDone.y = height / 2 + 52;
}
void UsernameScreen::tick()
{
_cursorBlink++;
}
void UsernameScreen::keyPressed(int eventKey)
{
if (eventKey == Keyboard::KEY_BACKSPACE) {
if (!_input.empty())
_input.erase(_input.size() - 1, 1);
} else if (eventKey == Keyboard::KEY_RETURN) {
if (!_input.empty())
buttonClicked(&_btnDone);
}
// deliberately do NOT call super::keyPressed — that would close the screen on Escape
_btnDone.active = !_input.empty();
}
void UsernameScreen::keyboardNewChar(char inputChar)
{
if (_input.size() < 16 && inputChar >= 32 && inputChar < 127)
_input += inputChar;
_btnDone.active = !_input.empty();
}
void UsernameScreen::mouseClicked(int x, int y, int button)
{
int cx = width / 2;
int cy = height / 2;
int boxW = 160;
int boxH = 18;
int boxX = cx - boxW / 2;
int boxY = cy - 5;
if (x >= boxX && x <= boxX + boxW && y >= boxY && y <= boxY + boxH) {
minecraft->platform()->showKeyboard();
} else {
super::mouseClicked(x, y, button);
}
}
void UsernameScreen::removed()
{
minecraft->platform()->hideKeyboard();
}
void UsernameScreen::buttonClicked(Button* button)
{
if (button == &_btnDone && !_input.empty()) {
minecraft->options.username = _input;
minecraft->options.save();
minecraft->user->name = _input;
minecraft->setScreen(NULL); // goes to StartMenuScreen
}
}
void UsernameScreen::render(int xm, int ym, float a)
{
// Dark dirt background
renderBackground();
int cx = width / 2;
int cy = height / 2;
// Title
drawCenteredString(font, "Enter your username", cx, cy - 70, 0xffffffff);
// Subtitle
drawCenteredString(font, "Please choose a username so others can easily", cx, cy - 52, 0xffaaaaaa);
drawCenteredString(font, "identify you in chat. Don't worry, you can", cx, cy - 40, 0xffaaaaaa);
drawCenteredString(font, "change it anytime.", cx, cy - 28, 0xffaaaaaa);
// Input box background
int boxW = 160;
int boxH = 18;
int boxX = cx - boxW / 2;
int boxY = cy - 5;
fill(boxX - 1, boxY - 1, boxX + boxW + 1, boxY + boxH + 1, 0xff000000);
fill(boxX, boxY, boxX + boxW, boxY + boxH, 0xff202020);
// Build display string with cursor
std::string display = _input;
if ((_cursorBlink / 10) % 2 == 0)
display += '|';
font->draw(display, (float)(boxX + 4), (float)(boxY + (boxH - 8) / 2 + 1), 0xffffffff, false);
// Hint below box
drawCenteredString(font, "Max 16 characters", cx, cy + 20, 0xff808080);
// Buttons (Done)
super::render(xm, ym, a);
}

View File

@@ -0,0 +1,37 @@
#ifndef NET_MINECRAFT_CLIENT_GUI_SCREENS__UsernameScreen_H__
#define NET_MINECRAFT_CLIENT_GUI_SCREENS__UsernameScreen_H__
#include "../Screen.h"
#include "../components/Button.h"
#include <string>
class UsernameScreen : public Screen
{
typedef Screen super;
public:
UsernameScreen();
virtual ~UsernameScreen();
void init();
virtual void setupPositions() override;
void render(int xm, int ym, float a);
void tick();
virtual bool isPauseScreen() { return false; }
virtual void keyPressed(int eventKey);
virtual void keyboardNewChar(char inputChar);
virtual bool handleBackEvent(bool isDown) { return true; } // block back/escape
virtual void removed();
virtual void mouseClicked(int x, int y, int button);
protected:
virtual void buttonClicked(Button* button);
private:
Button _btnDone;
std::string _input;
int _cursorBlink;
};
#endif /*NET_MINECRAFT_CLIENT_GUI_SCREENS__UsernameScreen_H__*/

View File

@@ -283,8 +283,7 @@ SelectWorldScreen::SelectWorldScreen()
bHeader (0, "Select world"),
bWorldView(4, ""),
worldsList(NULL),
_hasStartedLevel(false),
_state(_STATE_DEFAULT)
_hasStartedLevel(false)
{
bDelete.active = false;
@@ -349,9 +348,9 @@ void SelectWorldScreen::setupPositions() {
void SelectWorldScreen::buttonClicked(Button* button)
{
if (button->id == bCreate.id) {
if (_state == _STATE_DEFAULT && !_hasStartedLevel) {
minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_CREATE_NEW_WORLD);
_state = _STATE_CREATEWORLD;
if (!_hasStartedLevel) {
std::string name = getUniqueLevelName("World");
minecraft->setScreen(new SimpleChooseLevelScreen(name));
}
}
if (button->id == bDelete.id) {
@@ -392,9 +391,10 @@ static char ILLEGAL_FILE_CHARACTERS[] = {
void SelectWorldScreen::tick()
{
#if 0
if (_state == _STATE_CREATEWORLD) {
#if defined(RPI)
std::string levelId = getUniqueLevelName("perf");
std::string levelId = getUniqueLevelName("World");
//int seed = Util::hashCode("/r/Minecraft");
LevelSettings settings(getEpochTimeS(), GameType::Creative);
minecraft->selectLevel(levelId, levelId, settings);
@@ -402,7 +402,7 @@ void SelectWorldScreen::tick()
minecraft->setScreen(new ProgressScreen());
_hasStartedLevel = true;
#elif defined(PLATFORM_DESKTOP)
std::string name = getUniqueLevelName("perf");
std::string name = getUniqueLevelName("World");
minecraft->setScreen(new SimpleChooseLevelScreen(name));
#else
int status = minecraft->platform()->getUserInputStatus();
@@ -461,14 +461,15 @@ void SelectWorldScreen::tick()
worldsList->hasPickedLevel = false;
return;
}
#endif
worldsList->tick();
if (worldsList->hasPickedLevel) {
if (worldsList->pickedIndex == worldsList->levels.size()) {
worldsList->hasPickedLevel = false;
minecraft->platform()->createUserInput(DialogDefinitions::DIALOG_CREATE_NEW_WORLD);
_state = _STATE_CREATEWORLD;
std::string name = getUniqueLevelName("World");
minecraft->setScreen(new SimpleChooseLevelScreen(name));
} else {
minecraft->selectLevel(worldsList->pickedLevel.id, worldsList->pickedLevel.name, LevelSettings::None());
minecraft->hostMultiplayer();

View File

@@ -112,9 +112,6 @@ private:
bool _mouseHasBeenUp;
bool _hasStartedLevel;
int _state;
static const int _STATE_DEFAULT = 0;
static const int _STATE_CREATEWORLD = 1;
//LevelStorageSource* levels;
};
};

View File

@@ -22,6 +22,62 @@
#include "../DialogDefinitions.h"
#include "../SimpleChooseLevelScreen.h"
//
// Buy Button implementation
//
BuyButton::BuyButton(int id)
: super(id, "")
{
ImageDef def;
// Setup the source rectangle
def.setSrc(IntRectangle(64, 182, 190, 55));
def.width = 75;//rc.w / 3;
def.height = 75 * (55.0f / 190.0f);//rc.h / 3;
def.name = "gui/gui.png";
setImageDef(def, true);
}
void BuyButton::render(Minecraft* minecraft, int xm, int ym) {
glColor4f2(1, 1, 1, 1);
bool hovered = active && (minecraft->useTouchscreen()? (xm >= x && ym >= y && xm < x + width && ym < y + height) : false);
renderBg(minecraft, xm, ym);
TextureId texId = (_imageDef.name.length() > 0)? minecraft->textures->loadAndBindTexture(_imageDef.name) : Textures::InvalidId;
if ( Textures::isTextureIdValid(texId) ) {
const ImageDef& d = _imageDef;
Tesselator& t = Tesselator::instance;
t.begin();
if (!active) t.color(0xff808080);
else if (hovered||selected) t.color(0xffcccccc);
//else t.color(0xffe0e0e0);
else t.color(0xffffffff);
float hx = ((float) d.width) * 0.5f;
float hy = ((float) d.height) * 0.5f;
const float cx = ((float)x+d.x) + hx;
const float cy = ((float)y+d.y) + hy;
if (hovered) {
hx *= 0.95f;
hy *= 0.95f;
}
const TextureData* td = minecraft->textures->getTemporaryTextureData(texId);
const IntRectangle* src = _imageDef.getSrc();
if (td != NULL && src != NULL) {
float u0 = (src->x) / (float)td->w;
float u1 = (src->x+src->w) / (float)td->w;
float v0 = (src->y) / (float)td->h;
float v1 = (src->y+src->h) / (float)td->h;
t.vertexUV(cx-hx, cy-hy, blitOffset, u0, v0);
t.vertexUV(cx-hx, cy+hy, blitOffset, u0, v1);
t.vertexUV(cx+hx, cy+hy, blitOffset, u1, v1);
t.vertexUV(cx+hx, cy-hy, blitOffset, u1, v0);
}
t.draw();
}
}
namespace Touch {
//
@@ -32,7 +88,8 @@ namespace Touch {
StartMenuScreen::StartMenuScreen()
: bHost( 2, "Start Game"),
bJoin( 3, "Join Game"),
bOptions( 4, "Options")
bOptions( 4, "Options"),
bBuy( 5)
{
ImageDef def;
bJoin.width = 75;
@@ -61,19 +118,21 @@ void StartMenuScreen::init()
buttons.push_back(&bJoin);
buttons.push_back(&bOptions);
//buttons.push_back(&bTest);
tabButtons.push_back(&bHost);
tabButtons.push_back(&bJoin);
tabButtons.push_back(&bOptions);
#ifdef DEMO_MODE
buttons.push_back(&bBuy);
tabButtons.push_back(&bBuy);
#endif
copyright = "\xffMojang AB";//. Do not distribute!";
#ifdef PRE_ANDROID23
std::string versionString = Common::getGameVersionString("j");
#else
std::string versionString = Common::getGameVersionString();
#endif
// always show base version string
std::string versionString = Common::getGameVersionString();
#ifdef DEMO_MODE
#ifdef __APPLE__
@@ -103,31 +162,26 @@ void StartMenuScreen::setupPositions() {
bOptions.y = yBase;
//#endif
//bTest.x = 0; //width - bTest.w;
//bTest.y = height - bTest.h;
// Center buttons
bJoin.x = 0*buttonWidth + (int)(1*spacing);
bHost.x = 1*buttonWidth + (int)(2*spacing);
bOptions.x = 2*buttonWidth + (int)(3*spacing);
//bBuy.y = bOptions.y - bBuy.h - 6;
//bBuy.x = bOptions.x + bOptions.w - bBuy.w;
bBuy.y = height - bBuy.height - 3;
bBuy.x = (width - bBuy.width) / 2;
copyrightPosX = width - minecraft->font->width(copyright) - 1;
versionPosX = (width - minecraft->font->width(version)) / 2;// - minecraft->font->width(version) - 2;
}
void StartMenuScreen::tick() {
Screen::tick();
_updateLicense();
}
void StartMenuScreen::buttonClicked(::Button* button) {
//if (button->id == bTest.id) {
// minecraft->selectLevel("Broken", "Broken", 1317199248);
// minecraft->hostMultiplayer();
// minecraft->setScreen(new ProgressScreen());
//}
if (button->id == bHost.id)
{
#if defined(DEMO_MODE) || defined(APPLE_DEMO_PROMOTION)
@@ -149,6 +203,11 @@ void StartMenuScreen::buttonClicked(::Button* button) {
{
minecraft->setScreen(new OptionsScreen());
}
if (button->id == bBuy.id)
{
minecraft->platform()->buyGame();
//minecraft->setScreen(new BuyGameScreen());
}
}
bool StartMenuScreen::isInGameScreen() { return false; }
@@ -187,6 +246,10 @@ void StartMenuScreen::render( int xm, int ym, float a )
drawString(font, version, versionPosX, (int)(y+h)+2, /*50,*/ 0xffcccccc);//0x666666);
drawString(font, copyright, copyrightPosX, height - 10, 0xffffff);
glColor4f2(1, 1, 1, 1);
if (Textures::isTextureIdValid(minecraft->textures->loadAndBindTexture("gui/logo/github.png")))
blit(2, height - 10, 0, 0, 8, 8, 256, 256);
drawString(font, "Kolyah35/minecraft-pe-0.6.1", 12, height - 10, 0xffcccccc);
//patch->draw(t, 0, 20);
}
Screen::render(xm, ym, a);
@@ -210,9 +273,21 @@ void StartMenuScreen::_updateLicense()
}
}
void StartMenuScreen::mouseClicked(int x, int y, int buttonNum) {
const int logoX = 2;
const int logoW = 8 + 2 + font->width("Kolyah35/minecraft-pe-0.6.1");
const int logoY = height - 10;
const int logoH = 10;
if (x >= logoX && x <= logoX + logoW && y >= logoY && y <= logoY + logoH)
minecraft->platform()->openURL("https://gitea.sffempire.ru/Kolyah35/minecraft-pe-0.6.1");
else
Screen::mouseClicked(x, y, buttonNum);
}
bool StartMenuScreen::handleBackEvent( bool isDown ) {
minecraft->quit();
return true;
}
};
} // namespace Touch

View File

@@ -5,6 +5,14 @@
#include "../../components/LargeImageButton.h"
#include "../../components/TextBox.h"
class BuyButton: public ImageButton {
typedef ImageButton super;
public:
BuyButton(int id);
void render(Minecraft* minecraft, int xm, int ym);
};
namespace Touch {
class StartMenuScreen: public Screen
@@ -20,6 +28,7 @@ public:
void render(int xm, int ym, float a);
void buttonClicked(Button* button);
virtual void mouseClicked(int x, int y, int buttonNum);
bool handleBackEvent(bool isDown);
bool isInGameScreen();
private:
@@ -28,6 +37,7 @@ private:
LargeImageButton bHost;
LargeImageButton bJoin;
LargeImageButton bOptions;
BuyButton bBuy;
std::string copyright;
int copyrightPosX;

View File

@@ -50,7 +50,10 @@ LocalPlayer::LocalPlayer(Minecraft* minecraft, Level* level, User* user, int dim
sentInventoryItemId(-1),
sentInventoryItemData(-1),
autoJumpEnabled(true),
armorTypeHash(0)
armorTypeHash(0),
sprinting(false),
sprintDoubleTapTimer(0),
prevForwardHeld(false)
{
this->dimension = dimension;
_init();
@@ -176,6 +179,25 @@ void LocalPlayer::aiStep() {
if (!screenCovering)
input->tick(this);
// Sprint: detect W double-tap
{
bool forwardHeld = (input->ya > 0);
if (forwardHeld && !prevForwardHeld) {
// leading edge of W press
if (sprintDoubleTapTimer > 0)
sprinting = true;
else
sprintDoubleTapTimer = 7;
}
if (!forwardHeld) {
sprinting = false;
}
if (sprintDoubleTapTimer > 0) sprintDoubleTapTimer--;
prevForwardHeld = forwardHeld;
}
if (input->sneaking || abilities.flying)
sprinting = false;
if (input->sneaking) {
if (ySlideOffset < 0.2f) ySlideOffset = 0.2f;
}
@@ -296,6 +318,10 @@ void LocalPlayer::releaseAllKeys()
if (input) input->releaseAllKeys();
}
float LocalPlayer::getWalkingSpeedModifier() {
return sprinting ? 1.3f : 1.0f;
}
float LocalPlayer::getFieldOfViewModifier() {
float targetFov = 1.0f;
if(abilities.flying) targetFov *= 1.1f;

View File

@@ -70,6 +70,7 @@ public:
void swing();
virtual void openTextEdit( TileEntity* tileEntity );
virtual float getWalkingSpeedModifier();
private:
void calculateFlight(float xa, float ya, float za);
bool isSolidTile(int x, int y, int z);
@@ -99,6 +100,11 @@ private:
int sentInventoryItemData;
int armorTypeHash;
// sprinting
bool sprinting;
int sprintDoubleTapTimer;
bool prevForwardHeld;
};
#endif /*NET_MINECRAFT_CLIENT_PLAYER__LocalPlayer_H__*/

View File

@@ -270,15 +270,15 @@ void GameRenderer::renderLevel(float a) {
screenScissorArea.w, screenScissorArea.h);
}
// if(mc->options.fancyGraphics) {
// setupFog(-1);
// TIMER_POP_PUSH("sky");
// glFogf(GL_FOG_START, renderDistance * 0.2f);
// glFogf(GL_FOG_END, renderDistance *0.75);
// levelRenderer->renderSky(a);
// glFogf(GL_FOG_START, renderDistance * 0.6f);
// glFogf(GL_FOG_END, renderDistance);
// }
if(mc->options.fancyGraphics) {
setupFog(-1);
TIMER_POP_PUSH("sky");
glFogf(GL_FOG_START, renderDistance * 0.2f);
glFogf(GL_FOG_END, renderDistance *0.75);
levelRenderer->renderSky(a);
glFogf(GL_FOG_START, renderDistance * 0.6f);
glFogf(GL_FOG_END, renderDistance);
}
glEnable2(GL_FOG);
setupFog(1);

View File

@@ -16,11 +16,11 @@
#if defined(__APPLE__)
#import <OpenGLES/ES1/gl.height>
#import <OpenGLES/ES1/glext.height>
#elif defined(ANDROID)
#include <GLES/gl.h>
#include <GLES/glext.h>
#else
#include <glad/glad.h>
#if defined(ANDROID)
#include<GLES/glext.h>
#endif
#endif
#else
// Uglyness to fix redeclaration issues

View File

@@ -43,6 +43,7 @@ public:
void destroy() const {
if (isValid()) {
delete buffer;
buffer = 0;
}
}

View File

@@ -230,10 +230,13 @@ void SoundEngine::playUI(const std::string& name, float volume, float pitch) {}
void SoundEngine::play(const std::string& name, float x, float y, float z, float volume, float pitch) {
if ((volume *= options->sound) <= 0) return;
volume = Mth::clamp(volume, 0.0f, 1.0f);
volume = Mth::clamp( volume * _getVolumeMult(x, y, z), 0.0f, 1.0f);
if (/*!loaded || */options->sound == 0 || volume <= 0) return;
SoundDesc sound;
if (sounds.get(name, sound)) {
float dist = SOUND_DISTANCE;
if (volume > 1) dist *= volume;
soundSystem.playAt(sound, x-_x, y-_y, z-_z, volume, pitch);
}
}

View File

@@ -2,9 +2,10 @@
#define NET_MINECRAFT_CLIENT_SOUND__SoundEngine_H__
//package net.minecraft.client.sound;
#if defined(ANDROID) && !defined(PRE_ANDROID23)
#include "../../platform/audio/SoundSystemSL.h"
#elif (defined(__APPLE__) || defined (PLATFORM_DESKTOP)) && !defined(NO_SOUND)
#elif defined(__APPLE__) || defined(PLATFORM_DESKTOP)
#include "../../platform/audio/SoundSystemAL.h"
#else
#include "../../platform/audio/SoundSystem.h"
@@ -22,7 +23,7 @@ class SoundEngine
#if defined(ANDROID) && !defined(PRE_ANDROID23) && !defined(RPI)
SoundSystemSL soundSystem;
#elif (defined(__APPLE__) || defined (PLATFORM_DESKTOP)) && !defined(NO_SOUND)
#elif defined(__APPLE__) || defined(PLATFORM_DESKTOP)
SoundSystemAL soundSystem;
#else
SoundSystem soundSystem;

View File

@@ -1,5 +1,6 @@
#include "App.h"
#include "AppPlatform_android.h"
#include <android_native_app_glue.h>
// Horrible, I know. / A
#ifndef MAIN_CLASS
@@ -38,7 +39,15 @@ static void setupExternalPath(struct android_app* state, MAIN_CLASS* app)
const char* str = env->GetStringUTFChars((jstring) pathString, NULL);
app->externalStoragePath = str;
app->externalCacheStoragePath = str;
LOGI(str);
LOGI("%s", str);
// ensure the process working directory is set to a writable location
// on Android the default cwd may be '/' which isn't writable. By chdir'ing
// to the external storage path we make relative fopen calls (e.g. "options.txt")
// succeed and persist across launches, fixing the "options never save" bug.
if (chdir(str) != 0) {
LOGI("chdir to %s failed: %s", str, strerror(errno));
}
env->ReleaseStringUTFChars((jstring)pathString, str);

View File

@@ -3,6 +3,9 @@
//#include "main_android_java.h"
#include "platform/input/Multitouch.h"
#include <unistd.h>
#include <sys/syscall.h>
#define gettid() ((int)syscall(SYS_gettid))
// Horrible, I know. / A
#ifndef MAIN_CLASS
@@ -39,7 +42,12 @@ static void setupExternalPath(JNIEnv* env, MAIN_CLASS* app)
const char* str = env->GetStringUTFChars((jstring) pathString, NULL);
app->externalStoragePath = str;
app->externalCacheStoragePath = str;
LOGI(str);
LOGI("%s", str);
// same fix as the native entry point: make sure cwd is writable
if (chdir(str) != 0) {
LOGI("chdir to %s failed: %s", str, strerror(errno));
}
env->ReleaseStringUTFChars((jstring)pathString, str);
}
@@ -57,6 +65,7 @@ static void pointerMove(int pointerId, int x, int y) {
static App* gApp = 0;
static AppContext gContext;
static bool g_inNativeOnCreate = false;
extern "C" {
JNIEXPORT jint JNICALL
@@ -81,34 +90,53 @@ Java_com_mojang_minecraftpe_MainActivity_nativeUnregisterThis(JNIEnv* env, jobje
}
JNIEXPORT void JNICALL
Java_com_mojang_minecraftpe_MainActivity_nativeOnCreate(JNIEnv* env) {
LOGI("@nativeOnCreate\n");
Java_com_mojang_minecraftpe_MainActivity_nativeOnCreate(JNIEnv* env, jobject thiz, jint screenWidth, jint screenHeight) {
LOGI("@nativeOnCreate w=%d h=%d\n", (int)screenWidth, (int)screenHeight);
g_inNativeOnCreate = true;
appPlatform.instance = g_pActivity;
appPlatform.initConsts();
appPlatform.setScreenDimensions((int)screenWidth, (int)screenHeight);
LOGI("nativeOnCreate: screen set, no initConsts needed\n");
gContext.doRender = false;
gContext.platform = &appPlatform;
LOGI("nativeOnCreate: creating gApp\n");
gApp = new MAIN_CLASS();
LOGI("nativeOnCreate: gApp=%p\n", gApp);
setupExternalPath(env, (MAIN_CLASS*)gApp);
if (env->ExceptionOccurred()) {
LOGI("nativeOnCreate: exception after setupExternalPath!\n");
env->ExceptionDescribe();
env->ExceptionClear();
}
LOGI("nativeOnCreate: done\n");
g_inNativeOnCreate = false;
//gApp->init(gContext);
}
static int s_surfaceCreatedCount = 0;
JNIEXPORT void JNICALL
Java_com_mojang_minecraftpe_GLRenderer_nativeOnSurfaceCreated(JNIEnv* env) {
LOGI("@nativeOnSurfaceCreated\n");
s_surfaceCreatedCount++;
if (g_inNativeOnCreate) {
// Skip re-entrant surface callbacks that fire during nativeOnCreate
return;
}
LOGI("@nativeOnSurfaceCreated #%d tid=%d\n", s_surfaceCreatedCount, (int)gettid());
if (gApp) {
// gApp->setSize( gContext.platform->getScreenWidth(),
// gContext.platform->getScreenHeight(),
// gContext.platform->isTouchscreen());
// Don't call onGraphicsReset the first time
if (gApp->isInited())
if (gApp->isInited()) {
LOGI("nativeOnSurfaceCreated: calling onGraphicsReset\n");
gApp->onGraphicsReset(gContext);
}
if (!gApp->isInited())
if (!gApp->isInited()) {
LOGI("nativeOnSurfaceCreated: calling init\n");
gApp->init(gContext);
LOGI("nativeOnSurfaceCreated: init done, isInited=%d\n", (int)gApp->isInited());
}
}
}
@@ -159,6 +187,11 @@ Java_com_mojang_minecraftpe_MainActivity_nativeOnKeyDown(JNIEnv* env, jclass cls
Keyboard::feed(keyCode, true);
}
JNIEXPORT void JNICALL
Java_com_mojang_minecraftpe_MainActivity_nativeTextChar(JNIEnv* env, jclass cls, jint unicodeChar) {
if (unicodeChar > 0 && unicodeChar < 128)
Keyboard::feedText((char)unicodeChar);
}
JNIEXPORT void JNICALL
Java_com_mojang_minecraftpe_MainActivity_nativeOnKeyUp(JNIEnv* env, jclass cls, jint keyCode) {
LOGI("@nativeOnKeyUp: %d\n", (int)keyCode);
Keyboard::feed(keyCode, false);

View File

@@ -7,6 +7,8 @@
#include "SharedConstants.h"
#include <cstdio>
#include <chrono>
#include <thread>
#include "platform/input/Keyboard.h"
#include "platform/input/Mouse.h"
#include "platform/input/Multitouch.h"
@@ -32,6 +34,20 @@ int transformKey(int glfwkey) {
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if(action == GLFW_REPEAT) return;
if (key == GLFW_KEY_F11 && action == GLFW_PRESS) {
GLFWmonitor* monitor = glfwGetWindowMonitor(window);
if (monitor) {
// Currently fullscreen → go windowed
glfwSetWindowMonitor(window, NULL, 80, 80, 854, 480, 0);
} else {
// Currently windowed → go fullscreen on primary monitor
GLFWmonitor* primary = glfwGetPrimaryMonitor();
const GLFWvidmode* mode = glfwGetVideoMode(primary);
glfwSetWindowMonitor(window, primary, 0, 0, mode->width, mode->height, mode->refreshRate);
}
return;
}
Keyboard::feed(transformKey(key), action);
}
@@ -126,8 +142,9 @@ int main(void) {
glfwSetWindowSizeCallback(window, window_size_callback);
glfwMakeContextCurrent(window);
gladLoadGLES1Loader((GLADloadproc)glfwGetProcAddress);
glfwSwapInterval(1);
gladLoadGLES1Loader((GLADloadproc)winGLLoader);
glfwSwapInterval(0);
glPatchDesktopCompat();
#endif
App* app = new MAIN_CLASS();
@@ -139,11 +156,23 @@ int main(void) {
g_app->setSize(appContext.platform->getScreenWidth(), appContext.platform->getScreenHeight());
// Main event loop
using clock = std::chrono::steady_clock;
while(!glfwWindowShouldClose(window) && !app->wantToQuit()) {
auto frameStart = clock::now();
app->update();
glfwSwapBuffers(window);
glfwPollEvents();
glfwSwapInterval(((MAIN_CLASS*)app)->options.vsync ? 1 : 0);
if(((MAIN_CLASS*)app)->options.limitFramerate) {
auto frameEnd = clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(frameEnd - frameStart);
auto target = std::chrono::microseconds(33333); // ~30 fps
if(elapsed < target)
std::this_thread::sleep_for(target - elapsed);
}
}
delete app;

View File

@@ -134,6 +134,11 @@ void ServerSideNetworkHandler::displayGameMessage(const std::string& message)
raknetInstance->send(packet);
}
void ServerSideNetworkHandler::handle(const RakNet::RakNetGUID& source, ChatPacket* packet)
{
displayGameMessage(packet->message);
}
void ServerSideNetworkHandler::onNewClient(const RakNet::RakNetGUID& clientGuid)
{
LOGI("onNewClient, client guid: %s\n", clientGuid.ToString());
@@ -256,6 +261,11 @@ void ServerSideNetworkHandler::onReady_ClientGeneration(const RakNet::RakNetGUID
SetTimePacket(level->getTime()).write(&bitStream);
rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false);
// send adventure settings (includes showNameTags, etc.)
bitStream.Reset();
AdventureSettingsPacket(level->adventureSettings).write(&bitStream);
rakPeer->Send(&bitStream, HIGH_PRIORITY, RELIABLE_ORDERED, 0, source, false);
// send all pre-existing players to the new player
const PlayerList& players = level->players;
for (unsigned int i = 0; i < players.size(); i++) {

View File

@@ -57,18 +57,18 @@ public:
virtual void handle(const RakNet::RakNetGUID& source, ContainerSetSlotPacket* packet);
virtual void handle(const RakNet::RakNetGUID& source, ContainerClosePacket* packet);
virtual void handle(const RakNet::RakNetGUID& source, SignUpdatePacket* packet);
virtual void handle(const RakNet::RakNetGUID& source, ChatPacket* packet);
bool allowsIncomingConnections() { return _allowIncoming; }
void allowIncomingConnections(bool doAllow);
Player* popPendingPlayer(const RakNet::RakNetGUID& source);
void displayGameMessage(const std::string& message);
private:
void redistributePacket(Packet* packet, const RakNet::RakNetGUID& fromPlayer);
void displayGameMessage(const std::string& message);
Player* getPlayer(const RakNet::RakNetGUID& source);
private:
Minecraft* minecraft;
Level* level;

View File

@@ -4,6 +4,7 @@
//package net.minecraft.network.packet;
#include "../Packet.h"
#include <cstdint>
class SetTimePacket: public Packet {
public:
@@ -11,7 +12,7 @@ public:
}
SetTimePacket(long time)
: time(time)
: time((int32_t)time)
{}
void write(RakNet::BitStream* bitStream)
@@ -30,7 +31,7 @@ public:
callback->handle(source, (SetTimePacket*)this);
}
long time;
int32_t time;
};
#endif /*NET_MINECRAFT_NETWORK_PACKET__SetTimePacket_H__*/

View File

@@ -2,11 +2,12 @@
#define NET_MINECRAFT_NETWORK_PACKET__StartGamePacket_H__
#include "../Packet.h"
#include <cstdint>
class StartGamePacket : public Packet
{
public:
long levelSeed;
int32_t levelSeed;
int levelGeneratorVersion;
int gameType;
@@ -18,7 +19,7 @@ public:
}
StartGamePacket(long seed, int levelGeneratorVersion, int gameType, int entityId, float x, float y, float z)
: levelSeed(seed),
: levelSeed((int32_t)seed),
levelGeneratorVersion(levelGeneratorVersion),
gameType(gameType),
entityId(entityId),

View File

@@ -64,7 +64,8 @@
TerminateThread(m_threadHandle, 0);
#endif
#if defined(LINUX) || defined(ANDROID) || defined(__APPLE__) || defined(POSIX)
pthread_join(m_thread, NULL);
// Thread was created detached; pthread_join on a detached thread is undefined
// and causes SIGABRT when the pthread_t is no longer valid.
pthread_attr_destroy(&m_attributes);
#endif
}

View File

@@ -140,7 +140,7 @@ void SoundSystemAL::playAt( const SoundDesc& sound, float x, float y, float z, f
{
if (pitch < 0.01f) pitch = 1;
LOGI("playing sound '%s' with volume/pitch: %f, %f @ %f, %f, %f\n", sound.name.c_str(), volume, pitch, x, y, z);
//LOGI("playing sound '%s' with volume/pitch: %f, %f @ %f, %f, %f\n", sound.name.c_str(), volume, pitch, x, y, z);
ALuint bufferID;
if (!getBufferId(sound, &bufferID)) {
@@ -151,7 +151,7 @@ void SoundSystemAL::playAt( const SoundDesc& sound, float x, float y, float z, f
}
errIdString = "Get buffer";
checkError();
LOGI("playing sound %d - '%s' with volume/pitch: %f, %f @ %f, %f, %f\n", bufferID, sound.name.c_str(), volume, pitch, x, y, z);
//LOGI("playing sound %d - '%s' with volume/pitch: %f, %f @ %f, %f, %f\n", bufferID, sound.name.c_str(), volume, pitch, x, y, z);
int sourceIndex;
errIdString = "Get free index";
@@ -232,12 +232,12 @@ bool SoundSystemAL::getBufferId(const SoundDesc& sound, ALuint* buf) {
: (sound.channels==2? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8);
alBufferData(bufferID, format, sound.frames, sound.size, sound.frameRate);
LOGI("Creating %d (%p) from sound: '%s'\n", bufferID, sound.frames, sound.name.c_str());
//LOGI("Creating %d (%p) from sound: '%s'\n", bufferID, sound.frames, sound.name.c_str());
errIdString = "Buffer data";
LOGI("Creating buffer with data: %d (%d), %p, %d, %d\n", format, sound.byteWidth, sound.frames, sound.size, sound.frameRate);
//LOGI("Creating buffer with data: %d (%d), %p, %d, %d\n", format, sound.byteWidth, sound.frames, sound.size, sound.frameRate);
checkError();
LOGI("Sound ch: %d, fmt: %d, frames: %p, len: %f, fr: %d, sz: %d, numfr: %d\n", sound.channels, format, sound.frames, sound.length(), sound.frameRate, sound.size, sound.numFrames);
//LOGI("Sound ch: %d, fmt: %d, frames: %p, len: %f, fr: %d, sz: %d, numfr: %d\n", sound.channels, format, sound.frames, sound.length(), sound.frameRate, sound.size, sound.numFrames);
Buffer buffer;
@@ -248,6 +248,10 @@ bool SoundSystemAL::getBufferId(const SoundDesc& sound, ALuint* buf) {
_buffers.push_back(buffer);
// @huge @attn @note @fix: The original data is free'd
// On PLATFORM_DESKTOP the PCM data lives in static arrays (not heap),
// so calling delete[] on them causes a debug-heap __debugbreak crash.
#if !defined(PLATFORM_DESKTOP)
sound.destroy();
#endif
return true;
}

View File

@@ -91,7 +91,7 @@ void SoundSystemSL::setListenerPos( float x, float y, float z )
return;
}
SLVec3D pos = {1000.0f * x, 1000.0f * y, 1000.0f * z};
SLVec3D pos = {(SLint32)(1000.0f * x), (SLint32)(1000.0f * y), (SLint32)(1000.0f * z)};
SLresult res = (*listener)->SetLocationCartesian(listener, &pos);
checkErr(res);
}
@@ -115,10 +115,10 @@ void SoundSystemSL::playAt( const SoundDesc& sound, float x, float y, float z, f
SLDataLocator_AndroidSimpleBufferQueue uri = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2};
SLDataFormat_PCM mime = {
SL_DATAFORMAT_PCM,
sound.channels,
sound.frameRate * 1000,
sound.byteWidth << 3,
sound.byteWidth << 3,
(SLuint32)sound.channels,
(SLuint32)(sound.frameRate * 1000),
(SLuint32)(sound.byteWidth << 3),
(SLuint32)(sound.byteWidth << 3),
sound.channels==1? SL_SPEAKER_FRONT_CENTER :
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
SL_BYTEORDER_LITTLEENDIAN

View File

@@ -2236,6 +2236,6 @@ AdventureSettings::AdventureSettings()
noPvM(false),
noMvP(false),
immutableWorld(false),
showNameTags(false)
showNameTags(true)
{
}