├── screenshot.png ├── debian └── common │ ├── usr │ ├── bin │ │ └── mcpil │ └── share │ │ ├── pixmaps │ │ └── mcpil.png │ │ └── applications │ │ └── mcpil.desktop │ └── DEBIAN │ └── control ├── .gitmodules ├── .idea └── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml ├── .github ├── ISSUE_TEMPLATE │ ├── other.md │ ├── feature_request.md │ └── bug_report.md └── workflows │ ├── package.yml │ └── codeql-analysis.yml ├── .gitignore ├── scripts └── package.sh ├── CHANGELOG.md ├── src ├── config.py ├── splashes.py ├── launcher.py └── mcpil.py ├── README.md └── LICENSE /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCPI-Revival/MCPIL/HEAD/screenshot.png -------------------------------------------------------------------------------- /debian/common/usr/bin/mcpil: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | exec python3 /opt/mcpil/mcpil.py -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/proxy"] 2 | path = src/proxy 3 | url = https://github.com/MCPI-Devs/proxy.git 4 | -------------------------------------------------------------------------------- /debian/common/usr/share/pixmaps/mcpil.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MCPI-Revival/MCPIL/HEAD/debian/common/usr/share/pixmaps/mcpil.png -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/other.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Other 3 | about: Anything else that doesn't fit into the other 2 templates 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Old stuff 2 | .old/ 3 | 4 | # Builds 5 | *.pyc 6 | *.so 7 | 8 | # Python stuff 9 | __pycache__/ 10 | 11 | # Debian 12 | mcpil_*-*.deb 13 | out/ 14 | 15 | # PyCharm 16 | .idea/* 17 | !.idea/codeStyles 18 | 19 | # Debian 20 | debian/tmp 21 | out/deb 22 | 23 | # Version 24 | src/VERSION 25 | -------------------------------------------------------------------------------- /debian/common/usr/share/applications/mcpil.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=MCPIL 3 | Comment=Minecraft Pi Launcher 4 | Icon=/usr/share/pixmaps/mcpil.png 5 | StartupNotify=false 6 | StartupWMClass=mcpil 7 | Exec=/usr/bin/mcpil 8 | Terminal=false 9 | Type=Application 10 | Categories=Application;Game; 11 | -------------------------------------------------------------------------------- /debian/common/DEBIAN/control: -------------------------------------------------------------------------------- 1 | Package: mcpil 2 | Version: ${VERSION} 3 | Maintainer: TheBrokenRail 4 | Description: Minecraft Pi Launcher 5 | Homepage: https://mcpi.tk 6 | Architecture: all 7 | Depends: python3, python3-tk, python3-ttkthemes, minecraft-pi-reborn-virgl | minecraft-pi-reborn-native | mcpirdl 8 | -------------------------------------------------------------------------------- /scripts/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | # Set Version 6 | echo "$(git describe --tags --dirty)" > src/VERSION 7 | 8 | # Prepare 9 | rm -rf debian/tmp 10 | mkdir debian/tmp 11 | rm -rf out 12 | mkdir -p out 13 | 14 | # Copy 15 | mkdir -p debian/tmp/opt/mcpil 16 | rsync -r debian/common/ debian/tmp 17 | rsync -r src/ debian/tmp/opt/mcpil 18 | 19 | # Substitute 20 | sed -i 's/${VERSION}/'"$(cat src/VERSION)"'/g' debian/tmp/DEBIAN/control 21 | 22 | # Build DEB 23 | dpkg-deb -b --root-owner-group debian/tmp out 24 | 25 | # Clean Up 26 | rm -rf debian/tmp 27 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | **0.2.0 (Release Candidate 1)** 4 | - Add Splash Text 5 | - Use Dark Theme 6 | - Add Option To Hide Launcher While Game Is Running 7 | - Add "Optimized MCPE" Profile 8 | 9 | **0.1.9** 10 | * Update Proxy 11 | 12 | **0.1.8** 13 | * Add Render Distance Options 14 | 15 | **0.1.7** 16 | * Pre-Populate Server 17 | 18 | **0.1.6** 19 | * Fix Typo 20 | 21 | **0.1.5** 22 | * Drop The ``-R`` 23 | 24 | **0.1.4** 25 | * Update To Reflect MCPI-Reborn Name Change 26 | 27 | **0.1.3** 28 | * New Logo 29 | 30 | **0.1.2** 31 | * Fix Multiplayer Proxy Not Starting/Stopping Correctly 32 | 33 | **0.1.1** 34 | * Update Multiplayer Proxy 35 | 36 | **0.1.0** 37 | * Initial Release 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for MCPIL 4 | title: "[Feature Request] " 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a bug report to help us improve MCPIL 4 | title: "[Bug] " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Raspbian] 28 | - MCPIL Version [e.g. 0.2.0] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 13 | 14 | 15 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/config.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from pathlib import Path 4 | 5 | import launcher 6 | 7 | def _get_config_path() -> str: 8 | return str(Path.home()) + '/.mcpil.json' 9 | 10 | def _copy(src: dict, dst: dict): 11 | for key in dst: 12 | if key in src and type(src[key]) is type(dst[key]): 13 | if isinstance(dst[key], dict): 14 | _copy(src[key], dst[key]) 15 | else: 16 | dst[key] = src[key] 17 | 18 | def load() -> dict: 19 | obj: dict 20 | try: 21 | with open(_get_config_path(), 'r') as file: 22 | obj = json.load(file) 23 | except (FileNotFoundError, json.JSONDecodeError): 24 | obj = {} 25 | out = {'general': {'custom-features': [], 'render-distance': 'Short', 'username': 'StevePi', 'hide-launcher': True}, 'server': {'ip': 'thebrokenrail.com', 'port': '19132'}} 26 | for key in launcher.AVAILABLE_FEATURES: 27 | if launcher.AVAILABLE_FEATURES[key]: 28 | out['general']['custom-features'].append(key) 29 | _copy(obj, out) 30 | return out 31 | 32 | def save(obj: dict): 33 | with open(_get_config_path(), 'w') as file: 34 | json.dump(obj, file) 35 | -------------------------------------------------------------------------------- /src/splashes.py: -------------------------------------------------------------------------------- 1 | SPLASHES = [ 2 | 'Sexy!', 3 | 'We Fixed The Rail!', 4 | 'Teh', 5 | 'Who Broke The Rail?', 6 | 'The Work Of Notch!', 7 | 'The World Of Notch!', 8 | 'WDYM Discontinued?', 9 | 'Oh, OK, Pigmen...', 10 | 'Check Out The Non-Existant Far Lands!', 11 | 'mcpi.tk!', 12 | 'Come Back @Alvarito!', 13 | 'Classic!', 14 | 'Wow! Modded MCPI!', 15 | 'Now With Fly-Hacks!', 16 | 'Where Is My Pie?', 17 | 'I Was Promised Pie!', 18 | '@Banana', 19 | 'BANANA!', 20 | 'To "Na" Or Not To "Na", That Is The Question!' 21 | 'Not Minecraft Java!', 22 | 'Not Minecraft Bedrock (Technically)!', 23 | 'Definitely Not Minecraft Console!', 24 | 'Oh Yeah, That Version...', 25 | 'Join The Discord!', 26 | 'Segmentation fault', 27 | 'Segmentation fault (core dumped)', 28 | 'qemu: uncaught target signal 11 (Segmentation fault) - core dumped', 29 | 'It\'s The Segment\'s Fault!', 30 | 'As Seen On Discord!', 31 | 'Obfuscated!', 32 | 'As Not Seen On TV!', 33 | 'Why Must You Hurt Me?', 34 | 'Who Is StevePi?', 35 | 'Made By Notch!', 36 | 'Open-Source!', 37 | 'FREE!', 38 | 'RIP MCPIL-Legacy, 2020-2020', 39 | 'Boba was here!' 40 | ] 41 | -------------------------------------------------------------------------------- /.github/workflows/package.yml: -------------------------------------------------------------------------------- 1 | name: Package 2 | on: [push, pull_request] 3 | jobs: 4 | debian: 5 | name: Debian 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout Repository 9 | uses: actions/checkout@v2 10 | with: 11 | submodules: recursive 12 | fetch-depth: 0 13 | - name: Install Dependencies 14 | run: sudo apt-get install -y dpkg-dev 15 | - name: Package 16 | run: ./scripts/package.sh 17 | - name: Archive Artifacts 18 | uses: actions/upload-artifact@v2 19 | with: 20 | if-no-files-found: error 21 | name: debian 22 | path: | 23 | out 24 | publish: 25 | name: Publish 26 | runs-on: ubuntu-latest 27 | if: startsWith(github.ref, 'refs/tags/') 28 | needs: debian 29 | steps: 30 | - name: Download Artifacts 31 | uses: actions/download-artifact@v2 32 | with: 33 | name: debian 34 | path: out 35 | - name: Create Release 36 | uses: actions/create-release@v1 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | tag_name: ${{ github.ref }} 41 | release_name: Release ${{ github.ref }} 42 | draft: false 43 | prerelease: false 44 | - name: Upload Release Assets 45 | uses: alexellis/upload-assets@0.2.3 46 | env: 47 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 48 | with: 49 | asset_paths: '["./out/*"]' 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

MCPIL

2 | 3 |

4 | Simple GUI launcher for Minecraft: Pi Edition and MCPI-Reborn. 5 |

6 | 7 |

8 | 9 | GPL-2.0 10 | 11 | 12 | Required Python version 13 | 14 | 15 | CodeQL results 16 | 17 |

18 | 19 |

20 | screenshot 21 |

22 | 23 | > This is a rewrite of the original [MCPIL](https://github.com/MCPI-Devs/MCPIL-Legacy) project meant to be less fragile and more compatible. 24 | 25 | ## ⚠️ Deprecation notice 26 | This version of the Launcher is deprecated, and won't work with MCPI-Reborn 2.0. Please use [gMCPIL](https://github.com/MCPI-Revival/gMCPIL) or [jMCPIL](https://github.com/MCPI-Revival/jMCPIL) instead. 27 | 28 | ## Getting started 29 | 30 | ### Prerequisites 31 | - Python >= 3.7.x 32 | - [MCPI-Reborn](https://gitea.thebrokenrail.com/TheBrokenRail/minecraft-pi-reborn) 33 | 34 | ### System Requirements 35 | Debian/Raspbian Buster/Ubuntu 20.04 Or Higher 36 | 37 | ### Installation 38 | [![badge](https://github.com/Botspot/pi-apps/blob/master/icons/badge.png?raw=true)](https://github.com/Botspot/pi-apps) 39 | 40 | ## Features 41 | + Switch between Minecraft Pi and MCPE GUI/Touch GUI 42 | + Multiplayer and multiplayer username changing 43 | + Render distance toggle 44 | + Coming soon: More stuff 45 | 46 | ## Changelog 47 | [View Changelog](CHANGELOG.md) 48 | 49 | ## Debian Packages 50 | [GitHub Releases](https://github.com/MCPI-Revival/MCPIL/releases/latest) 51 | 52 | ## Compiling/Packaging 53 | ```sh 54 | git clone --recurse-submodules https://github.com/MCPI-Revival/MCPIL.git 55 | cd MCPIL 56 | ./scripts/package.sh 57 | ``` 58 | Then install the resulting .deb package, eg: 59 | ```sudo apt install ./out/mcpil_*_.deb'``` 60 | NOTE: You may need to run `pip3 install ttkthemes` if you are getting a missing module error upon run. 61 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | pull_request: 11 | schedule: 12 | - cron: '0 19 * * 3' 13 | 14 | jobs: 15 | analyze: 16 | name: Analyze 17 | runs-on: ubuntu-latest 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | # Override automatic language detection by changing the below list 23 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 24 | language: ['python'] 25 | # Learn more... 26 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 27 | 28 | steps: 29 | - name: Checkout repository 30 | uses: actions/checkout@v2 31 | with: 32 | # We must fetch at least the immediate parents so that if this is 33 | # a pull request then we can checkout the head. 34 | fetch-depth: 2 35 | 36 | # If this run was triggered by a pull request event, then checkout 37 | # the head of the pull request instead of the merge commit. 38 | - run: git checkout HEAD^2 39 | if: ${{ github.event_name == 'pull_request' }} 40 | 41 | # Initializes the CodeQL tools for scanning. 42 | - name: Initialize CodeQL 43 | uses: github/codeql-action/init@v1 44 | with: 45 | languages: ${{ matrix.language }} 46 | # If you wish to specify custom queries, you can do so here or in a config file. 47 | # By default, queries listed here will override any specified in a config file. 48 | # Prefix the list here with "+" to use these queries and those in the config file. 49 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 50 | 51 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 52 | # If this step fails, then you should remove it and run the build manually (see below) 53 | - name: Autobuild 54 | uses: github/codeql-action/autobuild@v1 55 | 56 | # ℹ️ Command-line programs to run using the OS shell. 57 | # 📚 https://git.io/JvXDl 58 | 59 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 60 | # and modify them (or add more) to build your code if your project 61 | # uses a compiled language 62 | 63 | #- run: | 64 | # make bootstrap 65 | # make release 66 | 67 | - name: Perform CodeQL Analysis 68 | uses: github/codeql-action/analyze@v1 69 | -------------------------------------------------------------------------------- /src/launcher.py: -------------------------------------------------------------------------------- 1 | # Licensed Uniquely Under MIT Because This File Might Be Useful For Other Projects 2 | # 3 | # MIT License 4 | # 5 | # Copyright (c) 2020 TheBrokenRail 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | import subprocess 26 | import os 27 | from typing import Dict 28 | 29 | # Feature Parse Failure 30 | def _parse_fail(): 31 | raise Exception('Unable To Parse Features') 32 | 33 | # Get All Available MCPI-Docker Features 34 | def _get_features() -> Dict[str, bool]: 35 | out: Dict[str, bool] = {} 36 | 37 | # Block X11 If Using Older MCPi-Docker 38 | env = os.environ.copy() 39 | if 'DISPLAY' in env: 40 | del env['DISPLAY'] 41 | 42 | result: subprocess.CompletedProcess = subprocess.run(['/usr/bin/minecraft-pi', '--print-features'], capture_output=True, text=True, env=env) 43 | result.check_returncode() 44 | 45 | stage = 0 46 | skip = 0 47 | escaped = False 48 | current_default = False 49 | current_name = '' 50 | for part in str(result.stdout): 51 | if skip > 0: 52 | skip -= 1 53 | continue 54 | if stage == 0: 55 | if part == 'T': 56 | current_default = True 57 | skip = 3 58 | stage += 1 59 | elif part == 'F': 60 | current_default = False 61 | skip = 4 62 | stage += 1 63 | elif part != ' ' and part != '\n': 64 | _parse_fail() 65 | elif stage == 1: 66 | if part == '\'': 67 | stage += 1 68 | elif stage == 2: 69 | is_escaped = False 70 | if part == '\\': 71 | escaped = True 72 | elif escaped: 73 | is_escaped = True 74 | escaped = False 75 | if part == 'n': 76 | # Hide Newline 77 | part = '' 78 | elif part == 't': 79 | # Add Tab 80 | part = '\t' 81 | if part == '\'' and not is_escaped: 82 | out[current_name] = current_default 83 | current_name = '' 84 | current_default = False 85 | stage = 0 86 | else: 87 | current_name += part 88 | else: 89 | _parse_fail() 90 | 91 | return out 92 | 93 | # All Available MCPI-Docker Features 94 | AVAILABLE_FEATURES = _get_features() 95 | print('Loaded Available Features: ' + str(AVAILABLE_FEATURES)) 96 | 97 | # Run MCPI-Docker 98 | def run(features: list, render_distance: str, username: str) -> subprocess.Popen: 99 | env = os.environ.copy() 100 | env['MCPI_FEATURES'] = '|'.join(features) 101 | env['MCPI_RENDER_DISTANCE'] = render_distance 102 | env['MCPI_USERNAME'] = username 103 | return subprocess.Popen(['/usr/bin/minecraft-pi'], env=env, preexec_fn=os.setsid) 104 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /src/mcpil.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # mcpil.py 5 | # 6 | # Copyright 2020-2021 Alvarito050506 7 | # Copyright 2020-2021 StealthHydrac/StealthHydra179/a1ma 8 | # Copyright 2020-2021 JumpeR6790 9 | # Copyright 2021 Boba 10 | # Copyright 2021 LEHAtupointow 11 | # 12 | # This program is free software; you can redistribute it and/or modify 13 | # it under the terms of the GNU General Public License as published by 14 | # the Free Software Foundation; version 2 of the License. 15 | # 16 | # This program is distributed in the hope that it will be useful, 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | # GNU General Public License for more details. 20 | # 21 | # You should have received a copy of the GNU General Public License 22 | # along with this program; if not, write to the Free Software 23 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 24 | # MA 02110-1301, USA. 25 | # 26 | 27 | import signal 28 | 29 | from typing import Dict 30 | 31 | from proxy.proxy import Proxy 32 | 33 | import launcher 34 | import config 35 | 36 | from splashes import SPLASHES 37 | import random 38 | 39 | from os import kill, killpg, getpid, getpgid 40 | import platform 41 | import threading 42 | 43 | from subprocess import Popen 44 | 45 | from tkinter import * 46 | from tkinter import ttk 47 | from tkinter.messagebox import showerror 48 | 49 | from ttkthemes import ThemedTk 50 | 51 | import webbrowser 52 | 53 | import random 54 | from datetime import date 55 | 56 | ''' 57 | Global variables. 58 | ''' 59 | 60 | # Root Window 61 | window: Tk 62 | 63 | # Constants 64 | DESCRIPTIONS = [ 65 | 'Classic Minecraft Pi Edition.\n(Not Recommended)\nAll optional features disabled.', 66 | 'Modded Minecraft Pi Edition.\nDefault MCPI-Reborn optional features without Touch GUI.', 67 | 'Minecraft Pocket Edition.\n(Recommended)\nDefault MCPI-Reborn optional features.', 68 | 'Optimized Minecraft Pocket Edition.\nDefault MCPI-Reborn optional features with lower quality graphics.', 69 | 'Custom Profile.\nModify its settings in the Features tab.' 70 | ] 71 | RENDER_DISTANCES = [ 72 | 'Far', 73 | 'Normal', 74 | 'Short', 75 | 'Tiny', 76 | ] 77 | 78 | # Current Profile 79 | current_profile_selection = 2 80 | # Current Profile Description Text 81 | description_text: StringVar 82 | 83 | # Launch Button 84 | launch_button: ttk.Button 85 | 86 | # Settings 87 | current_render_distance: StringVar 88 | current_username: StringVar 89 | current_hide_launcher: IntVar 90 | 91 | # Proxy Settings 92 | current_ip: StringVar 93 | current_port: StringVar 94 | 95 | # Custom Profile Features 96 | current_features = [] 97 | feature_widgets: Dict[str, ttk.Checkbutton] = {} 98 | 99 | # Current Process 100 | current_process: Popen = None 101 | 102 | # Current Config 103 | current_config = {} 104 | 105 | # Proxy 106 | proxy_lock = threading.Lock() 107 | proxy_thread: threading.Thread = None 108 | proxy = Proxy() 109 | 110 | ''' 111 | Helper classes. 112 | ''' 113 | 114 | # Hyper-Link 115 | class HyperLink(ttk.Label): 116 | def __init__(self, parent, url, text=None, cursor=None, *args, **kwargs): 117 | self.url = url 118 | super().__init__(parent, text=(text or url), cursor=(cursor or 'hand2'), *args, **kwargs) 119 | self.bind('', self.web_open) 120 | 121 | def web_open(self, event): 122 | return webbrowser.open(self.url) 123 | 124 | # Frame With Scrollbar 125 | class ScrollableFrame(ttk.Frame): 126 | def __init__(self, root): 127 | super().__init__(root) 128 | 129 | self.grid_columnconfigure(0, weight=1) 130 | self.grid_rowconfigure(0, weight=1) 131 | 132 | self.canvas = Canvas(self) 133 | scrollbar = ttk.Scrollbar(self, orient='vertical', command=self.canvas.yview) 134 | self.canvas.configure(yscrollcommand=scrollbar.set) 135 | 136 | scrollbar.grid(row=0, column=1, sticky='NSE') 137 | self.canvas.grid(row=0, column=0, sticky='NSEW') 138 | 139 | self.scrollable_frame = ttk.Frame(self.canvas) 140 | scrollable_frame_id = self.canvas.create_window(0, 0, window=self.scrollable_frame, anchor='nw') 141 | 142 | def configure_scrollable_frame(event): 143 | self.canvas.configure(scrollregion=self.canvas.bbox("all")) 144 | 145 | self.scrollable_frame.bind('', configure_scrollable_frame) 146 | 147 | def configure_canvas(event): 148 | self.canvas.itemconfig(scrollable_frame_id, width=event.width, height=(self.scrollable_frame.winfo_height() if self.scrollable_frame.winfo_height() > event.height else event.height)) 149 | 150 | self.canvas.bind('', configure_canvas) 151 | 152 | 153 | 154 | class ToolTip(object): 155 | def __init__(self, widget, text=None): 156 | self.widget = widget 157 | self.text = text 158 | widget.bind("", self.mouse_enter) 159 | widget.bind("", self.mouse_leave) 160 | 161 | 162 | def mouse_enter(self, _event): 163 | self.show_tooltip() 164 | 165 | def mouse_leave(self, _event_): 166 | self.hide_tooltip() 167 | 168 | def show_tooltip(self): 169 | x_left = self.widget.winfo_rooty() 170 | y_top = self.widget.winfo_rootx() - 18 171 | self.tip_window = Toplevel(self.widget) 172 | self.tip_window.overrideredirect(True) 173 | self.tip_window.geometry("+%d+%d" % (x_left, y_top)) 174 | label = Label(self.tip_window, text=self.text, justify=LEFT, background="#ffffe0", relief=SOLID, borderwidth=1, font=("tahoma", 8, "normal")) 175 | label.pack(ipadx=1) 176 | 177 | def hide_tooltip(self): 178 | if self.tip_window: 179 | self.tip_window.destroy() 180 | 181 | ''' 182 | Helper functions and back-end. 183 | ''' 184 | 185 | # Get Path Base-Name 186 | def basename(path): 187 | return path.split('/')[-1] 188 | 189 | # Convert Dict Of Features To List Of Enabled Features 190 | def features_dict_to_list(features: Dict[str, bool]): 191 | out = [] 192 | for key in features: 193 | if features[key]: 194 | out.append(key) 195 | return out 196 | # Get Features From Selected Profile 197 | def get_features() -> list: 198 | global current_profile_selection, current_features 199 | if current_profile_selection == 0: 200 | # No Features 201 | return [] 202 | if current_profile_selection == 1: 203 | # Default Features Minus Touch GUI 204 | mods = launcher.AVAILABLE_FEATURES.copy() 205 | mods['Touch GUI'] = False 206 | return features_dict_to_list(mods) 207 | if current_profile_selection == 2: 208 | # Default Features 209 | return features_dict_to_list(launcher.AVAILABLE_FEATURES.copy()) 210 | if current_profile_selection == 3: 211 | # Default Features With Lower Quality Graphics 212 | mods = launcher.AVAILABLE_FEATURES.copy() 213 | mods['Fancy Graphics'] = False 214 | mods['Smooth Lighting'] = False 215 | mods['Animated Water'] = False 216 | mods['Disable gui_blocks Atlas'] = False 217 | return features_dict_to_list(mods) 218 | if current_profile_selection == 4: 219 | # Custom Features (Use Features Tab) 220 | return current_features 221 | # Impossible 222 | raise ValueError 223 | # Update Features From Widgets 224 | def update_features(): 225 | global current_features, feature_widgets 226 | current_features = [] 227 | for key in feature_widgets: 228 | if feature_widgets[key].instate(['selected']): 229 | current_features.append(key) 230 | 231 | # Launch Minecraft 232 | def launch(): 233 | global current_render_distance, current_username, current_process 234 | launch_button.state(['disabled']) 235 | if current_process is None or current_process.poll() is not None: 236 | current_process = launcher.run(get_features(), current_render_distance.get(), current_username.get()) 237 | return 0 238 | 239 | # Hide/Show Window 240 | window_shown = True 241 | def hide_window(): 242 | global window, window_shown 243 | if window_shown: 244 | window.withdraw() 245 | window_shown = False 246 | 247 | def show_window(): 248 | global window, window_shown 249 | if not window_shown: 250 | window.update() 251 | window.deiconify() 252 | window_shown = True 253 | 254 | # Update Launch Button 255 | def update_launch_button(): 256 | global launch_button, current_hide_launcher 257 | 258 | game_closed = current_process is None or current_process.poll() is not None 259 | 260 | if (not game_closed) and current_hide_launcher.get(): 261 | hide_window() 262 | else: 263 | show_window() 264 | 265 | if game_closed and launch_button.instate(['disabled']): 266 | launch_button.state(['!disabled']) 267 | 268 | launch_button.after(10, update_launch_button) 269 | 270 | # Close MCPIL 271 | def quit(): 272 | global current_process 273 | if current_process is not None and current_process.poll() is None: 274 | killpg(getpgid(current_process.pid), signal.SIGTERM) 275 | 276 | window.destroy() 277 | kill(getpid(), signal.SIGTERM) 278 | return 0 279 | 280 | # Start/Stop Proxy 281 | def update_proxy(): 282 | global proxy, proxy_thread, proxy_lock, current_ip, current_port 283 | proxy_lock.acquire() 284 | if proxy_thread is not None: 285 | proxy.stop() 286 | proxy_thread.join() 287 | try: 288 | proxy.set_option("src_addr", current_ip.get()) 289 | proxy.set_option("src_port", int(current_port.get())) 290 | proxy_thread = threading.Thread(target=lambda *args: proxy.run()) 291 | proxy_thread.start() 292 | except ValueError: 293 | # Invalid Port 294 | pass 295 | proxy_lock.release() 296 | 297 | # Save/Load Config 298 | def load(): 299 | global current_config, current_render_distance, current_username, current_features, feature_widgets, current_hide_launcher 300 | current_config = config.load() 301 | current_render_distance.set(current_config['general']['render-distance']) 302 | current_username.set(current_config['general']['username']) 303 | current_features = current_config['general']['custom-features'].copy() 304 | for key in feature_widgets: 305 | feature_widgets[key].state(['!alternate']) 306 | if key in current_features: 307 | feature_widgets[key].state(['selected']) 308 | else: 309 | feature_widgets[key].state(['!selected']) 310 | current_hide_launcher.set(int(current_config['general']['hide-launcher'])) 311 | current_ip.set(current_config['server']['ip']) 312 | current_port.set(current_config['server']['port']) 313 | update_proxy() 314 | def save(): 315 | global current_config, current_render_distance, current_username, current_features, current_hide_launcher 316 | current_config['general']['render-distance'] = current_render_distance.get() 317 | current_config['general']['username'] = current_username.get() 318 | current_config['general']['custom-features'] = current_features.copy() 319 | current_config['general']['hide-launcher'] = bool(current_hide_launcher.get()) 320 | current_config['server']['ip'] = current_ip.get() 321 | current_config['server']['port'] = current_port.get() 322 | config.save(current_config) 323 | 324 | ''' 325 | Event handlers. 326 | ''' 327 | 328 | def select_version(version: int): 329 | global current_profile_selection 330 | try: 331 | current_profile_selection = int(version) 332 | description_text.set(DESCRIPTIONS[current_profile_selection]) 333 | except IndexError: 334 | pass 335 | except Exception as err: 336 | return 'Critical error {}'.format(err) 337 | def on_select_versions(event): 338 | select_version(event.widget.selection()[0]) 339 | return 0 340 | 341 | ''' 342 | Tabs. 343 | ''' 344 | 345 | # Play Tab 346 | def play_tab(parent): 347 | global description_text, launch_button 348 | 349 | tab = ttk.Frame(parent) 350 | 351 | today = date.today() 352 | randomnumber = random.randint(1,100) 353 | if today.month == 4 and today.day == 1: 354 | title = ttk.Label(tab, text='Banana Launcher') 355 | else: 356 | if randomnumber == 1: 357 | title = ttk.Label(tab, text='Minceraft Pi Launcher') 358 | else: 359 | title = ttk.Label(tab, text='Minecraft Pi Launcher') 360 | 361 | 362 | title.config(font=('', 24)) 363 | title.grid(row=0) 364 | 365 | splash = random.choice(SPLASHES) 366 | if today.month == 4 and today.day == 1: 367 | splash = "Happy B-Day Alvarito050506" 368 | elif today.month == 8 and today.day == 24: 369 | splash = "Happy Birthday LEHAtupointow" 370 | elif today == 2 and today.day == 20: 371 | splash = "Happy Birthday Boba" 372 | elif today == 7 and today.day == 15: 373 | splash = "Happy Birthday RPiNews!" 374 | elif today.month == 5 and today.day == 5: 375 | splash = random.randint(["I shifted them a bit", "We're moving to gMCPIL or jMCPIL", "Should have come back", "YOU DON'T LIKE POTATOES?"]) 376 | 377 | splash_text = ttk.Label(tab, text=splash, foreground='yellow') 378 | splash_text.grid(row=1, pady=4) 379 | 380 | choose_text = ttk.Label(tab, text='Choose a Minecraft version to launch.') 381 | choose_text.grid(row=2, pady=(0, 16)) 382 | 383 | versions_frame = ttk.Frame(tab) 384 | 385 | tab.columnconfigure(0, weight=1) 386 | versions_frame.columnconfigure(0, weight=1) 387 | tab.rowconfigure(2, weight=1) 388 | versions_frame.rowconfigure(0, weight=1) 389 | 390 | description_text = StringVar(versions_frame) 391 | description_text_label = ttk.Label(versions_frame, textvariable=description_text, wraplength=256, anchor='center', justify='center') 392 | description_text_label.tooltip = ToolTip(description_text_label, text="The description of the profile shows up here.") 393 | 394 | versions = ttk.Treeview(versions_frame, selectmode='browse', show='tree') 395 | versions.insert('', 'end', text='Classic MCPI', iid=0) 396 | versions.insert('', 'end', text='Modded MCPI', iid=1) 397 | versions.insert('', 'end', text='Modded MCPE', iid=2) 398 | versions.insert('', 'end', text='Optimized MCPE', iid=3) 399 | versions.insert('', 'end', text='Custom Profile', iid=4) 400 | versions.bind('<>', on_select_versions) 401 | versions.grid(row=0, column=0, sticky='NSEW') 402 | versions.selection_set(2) 403 | select_version(versions.selection()[0]) 404 | versions.tooltip = ToolTip(versions, text="Select a profile from this menu.") 405 | 406 | description_text_label.grid(row=0, column=1, pady=48, padx=48, sticky='NSE') 407 | 408 | versions_frame.grid(row=3, sticky='NSEW') 409 | 410 | launch_frame = ttk.Frame(tab) 411 | launch_button = ttk.Button(launch_frame, text='Launch', command=launch, cursor="shuttle") 412 | launch_button.tooltip = ToolTip(launch_button, text="Click this to launch MCPI-Reborn") 413 | launch_button.pack(side=RIGHT, anchor=S) 414 | launch_frame.grid(row=4, sticky='SE') 415 | 416 | launch_button.after(0, update_launch_button) 417 | 418 | return tab 419 | 420 | def settings_tab(parent): 421 | global current_render_distance, current_username, current_hide_launcher 422 | 423 | tab = ttk.Frame(parent) 424 | 425 | tab.rowconfigure(0, weight=1) 426 | tab.columnconfigure(0, weight=1) 427 | 428 | main_frame = ttk.Frame(tab) 429 | 430 | main_frame.columnconfigure(1, weight=1) 431 | 432 | render_distance_label = ttk.Label(main_frame, text='Render Distance:') 433 | render_distance_label.grid(row=0, column=0, padx=6, pady=6, sticky='W') 434 | current_render_distance = StringVar(main_frame) 435 | render_distance = ttk.Combobox(main_frame, textvariable=current_render_distance, values=RENDER_DISTANCES, width=24) 436 | render_distance.state(['readonly']) 437 | render_distance.tooltip = ToolTip(render_distance, text="Select the chunk render distance here") 438 | render_distance.grid(row=0, column=1, padx=6, pady=6, sticky='EW') 439 | 440 | username_label = ttk.Label(main_frame, text='Username:') 441 | username_label.grid(row=1, column=0, padx=6, pady=6, sticky='W') 442 | current_username = StringVar(main_frame) 443 | username = ttk.Entry(main_frame, width=24, textvariable=current_username) 444 | username.tooltip = ToolTip(username, text="Username for servers") 445 | 446 | username.grid(row=1, column=1, padx=6, pady=6, sticky='EW') 447 | 448 | hide_launcher_label = ttk.Label(main_frame, text='Hide Launcher While Game Is Open:') 449 | hide_launcher_label.grid(row=2, column=0, padx=6, pady=6, sticky='W') 450 | current_hide_launcher = IntVar(main_frame) 451 | hide_launcher = ttk.Checkbutton(main_frame, variable=current_hide_launcher) 452 | hide_launcher.tooltip = ToolTip(hide_launcher, text="Check this if you want the launcher to dissapear when you start the game.") 453 | hide_launcher.grid(row=2, column=1, padx=6, pady=6, sticky='EW') 454 | 455 | main_frame.grid(row=0, sticky='NEW') 456 | 457 | save_frame = ttk.Frame(tab) 458 | save_button = ttk.Button(save_frame, text='Save', command=save) 459 | save_button.pack(side=RIGHT, anchor=S) 460 | save_button.tooltip = ToolTip(save_button, text="Save your options before launching.") 461 | save_frame.grid(row=1, sticky='SE') 462 | 463 | return tab 464 | 465 | def features_tab(parent): 466 | global feature_widgets 467 | 468 | tab = ttk.Frame(parent) 469 | 470 | tab.rowconfigure(0, weight=1) 471 | tab.columnconfigure(0, weight=1) 472 | 473 | main_frame = ScrollableFrame(tab) 474 | 475 | main_frame.scrollable_frame.columnconfigure(1, weight=1) 476 | 477 | row = 0 478 | for key in launcher.AVAILABLE_FEATURES: 479 | check = ttk.Checkbutton(main_frame.scrollable_frame, command=update_features, text=key) 480 | check.tooltip = ToolTip(check, text=f"Check this if you want {key} to be enabled.") 481 | check.pack(padx=6, pady=6, anchor='w') 482 | feature_widgets[key] = check 483 | 484 | row += 1 485 | 486 | main_frame.grid(row=0, sticky='NSEW') 487 | 488 | save_frame = ttk.Frame(tab) 489 | save_button = ttk.Button(save_frame, text='Save', command=save) 490 | save_button.tooltip = ToolTip(save_button, text="Save your options before launching.") 491 | save_button.pack(side=RIGHT, anchor=S) 492 | save_frame.grid(row=1, sticky='SE') 493 | 494 | return tab 495 | 496 | def multiplayer_tab(parent): 497 | global current_ip, current_port 498 | 499 | tab = ttk.Frame(parent) 500 | 501 | tab.rowconfigure(0, weight=1) 502 | tab.columnconfigure(0, weight=1) 503 | 504 | main_frame = ttk.Frame(tab) 505 | 506 | main_frame.columnconfigure(1, weight=1) 507 | 508 | ip_label = ttk.Label(main_frame, text='IP:') 509 | ip_label.grid(row=0, column=0, padx=6, pady=6, sticky='W') 510 | current_ip = StringVar(main_frame) 511 | current_ip.trace('w', lambda *args: update_proxy()) 512 | ip = ttk.Entry(main_frame, width=24, textvariable=current_ip) 513 | ip.tooltip = ToolTip(ip, text="This is the IP of a multiplayer server.") 514 | ip.grid(row=0, column=1, padx=6, pady=6, sticky='EW') 515 | 516 | port_label = ttk.Label(main_frame, text='Port:') 517 | port_label.grid(row=1, column=0, padx=6, pady=6, sticky='W') 518 | current_port = StringVar(main_frame) 519 | current_port.trace('w', lambda *args: update_proxy()) 520 | port = ttk.Entry(main_frame, width=24, textvariable=current_port) 521 | port.tooltip = ToolTip(port, text="Set the port.") 522 | port.grid(row=1, column=1, padx=6, pady=6, sticky='EW') 523 | 524 | main_frame.grid(row=0, sticky='NEW') 525 | 526 | save_frame = ttk.Frame(tab) 527 | save_button = ttk.Button(save_frame, text='Save', command=save) 528 | save_button.tooltip = ToolTip(save_button, text="Save your options before launching.") 529 | save_button.pack(side=RIGHT, anchor=S) 530 | save_frame.grid(row=1, sticky='SE') 531 | 532 | return tab 533 | 534 | # Get Version 535 | def get_version() -> str: 536 | try: 537 | with open('/opt/mcpil/VERSION', 'r') as file: 538 | return 'v' + file.readline().strip() 539 | except OSError: 540 | # File Does Not Exists Or Is Inaccessible 541 | pass 542 | return 'Unknown Version' 543 | 544 | def about_tab(parent): 545 | tab = ttk.Frame(parent) 546 | 547 | main_frame = ttk.Frame(tab) 548 | 549 | main_frame.columnconfigure(0, weight=1) 550 | 551 | title = ttk.Label(main_frame, text='Minecraft Pi Launcher', anchor='center') 552 | title.config(font=('', 24)) 553 | title.grid(row=0, sticky='NSEW') 554 | 555 | version = ttk.Label(main_frame, text=get_version(), anchor='center') 556 | version.config(font=('', 10)) 557 | version.grid(row=1, sticky='NSEW') 558 | 559 | authors = HyperLink(main_frame, 'https://github.com/MCPI-Revival/MCPIL/graphs/contributors', text='by all its contributors', anchor='center') 560 | authors.config(font=('', 10)) 561 | authors.grid(row=2, sticky='NSEW') 562 | 563 | url = HyperLink(main_frame, 'https://github.com/MCPI-Revival/MCPIL', anchor='center', foreground='blue') 564 | url.config(font=('', 10)) 565 | url.grid(row=3, sticky='NSEW') 566 | 567 | main_frame.pack(expand=True) 568 | 569 | return tab 570 | 571 | def main(): 572 | if platform.system() != 'Linux': 573 | showerror('Error', 'Linux Is Required') 574 | return 1 575 | 576 | global window 577 | 578 | window = ThemedTk(theme='equilux', className='mcpil') 579 | window.title('MCPIL') 580 | window.geometry('512x400') 581 | window.resizable(True, True) 582 | 583 | # Set icon in taskbar 584 | window.iconphoto(True, PhotoImage(file="/usr/share/pixmaps/mcpil.png")) 585 | 586 | tabs = ttk.Notebook(window) 587 | tabs.add(play_tab(tabs), text='Play') 588 | tabs.add(features_tab(tabs), text='Features') 589 | tabs.add(multiplayer_tab(tabs), text='Multiplayer') 590 | tabs.add(settings_tab(tabs), text='Settings') 591 | tabs.add(about_tab(tabs), text='About') 592 | tabs.pack(fill=BOTH, expand=True) 593 | 594 | load() 595 | save() 596 | 597 | window.wm_protocol('WM_DELETE_WINDOW', quit) 598 | signal.signal(signal.SIGINT, lambda *args: quit()) 599 | 600 | try: 601 | window.mainloop() 602 | except KeyboardInterrupt: 603 | quit() 604 | 605 | return 0 606 | 607 | if __name__ == '__main__': 608 | sys.exit(main()) 609 | --------------------------------------------------------------------------------