V0.5.0 commit

lazy
This commit is contained in:
沈瑗杰 2021-08-13 12:25:29 +08:00
parent 0b4a4b6145
commit 25299a5efb
48 changed files with 1905 additions and 2066 deletions

View File

@ -9,6 +9,7 @@ QQ: 3695888"""
import os
import sys
import traceback
## TODO 默认位置配置文件+可自定义工作路径
@ -30,5 +31,9 @@ if __name__ == '__main__':
os.chdir(sys.path[0]) # TODO 没做完.ing
print(hi)
game = main.Game()
game.start()
try:
game = main.Game()
game.start()
except:
error = traceback.format_exc()
print(error)

View File

@ -1,32 +1,29 @@
# Difficult-Rocket
# [Difficult-Rocket(github)](https://github.com/shenjackyuanjie/Difficult-Rocket)
# [Difficult-Rocket(gitee)](https://gitee.com/shenjackyuanjie/Difficult-Rocket)
### Version
## Version
0.4.3 (developing DEMO)
- (developing DEMO)(coming s∞n)
- 0.4.6 release
- 0.5.0 developing
[Update logs](https://github.com/shenjackyuanjie/Difficult-Rocket/blob/main/docs/update_logs.md)
[Plan feature list](https://github.com/shenjackyuanjie/Difficult-Rocket/tree/main/docs/plan_features)
中文README请移步 [这里](https://github.com/shenjackyuanjie/Difficult-Rocket/blob/main/docs/README-cn.md)
## 中文README请移步 [这里](./docs/README-cn.md)
> It's a Simple Rocket liked game build with Python
## Advantage
None. Not even one!
> Lighter than Vanilla SR
(Because we are still writing the description, coming s∞n)
## [Plan feature list](./docs/plan_features)
## Planed feature
- All In [here](https://github.com/shenjackyuanjie/Difficult-Rocket/projects)
## [Update logs](./docs/update_logs.md)
## Environment (been tested / develop on)
-
- `Develop platform 1 - Windows 10`
- `Python 3.8.7`
- `Python 3.8.10`
- `Windows10 x64`
- `Pyglet 1.5.16`
- `Json5 0.9.5`
@ -43,9 +40,10 @@ None. Not even one!
## Required python modules
- json5 (pre-installed V0.9.5 path:`./bin/libs/json5`)
- pyglet (pre-installed V1.5.16 path:`./bin/libs/pyglet`)
- json5 (pre-installed V0.9.6 path:`./bin/libs/json5`)
- pyglet (pre-installed V1.5.18 path:`./bin/libs/pyglet`)
- pillow
- semver
## thanks to
@ -54,6 +52,8 @@ None. Not even one!
- [@Rayawa](https://github.com/Rayawa) : check mistake in docs
- [@Billchyi](https://github.com/Billchyi) : check mistake in docs
## Other links
## About License
#### https://creativecommons.org/licenses/by-nc-sa/4.0/

View File

@ -1,8 +1,23 @@
"""
writen by shenjackyuanjie
mail: 3695888@qq.com
github: @shenjackyuanjie
"""
# import folders
#
from .new_thread import new_thread
from .tools import config
# import in this folder
__all__ = [
'new_thread',
'config'
]
@new_thread('think')
def think(some_thing_to_think):
gotcha = 'think_result'
return gotcha

View File

@ -2,7 +2,8 @@
writen by shenjackyuanjie
mail: 3695888@qq.com
"""
import configparser
import logging
import multiprocessing as mp
import os
import sys
@ -22,49 +23,44 @@ except (ModuleNotFoundError, ImportError, ImportWarning):
import configs
class client(mp.Process):
def __init__(self, logger, dev_dic=None, dev_list=None, language='zh-cn', net_mode='local'):
mp.Process.__init__(self)
class Client:
def __init__(self, dev_dic=None, dev_list=None, net_mode='local'):
pass
# mp.Process.__init__(self)
# logging
self.logger = logger
self.logger = logging.getLogger('client')
# share memory
self.dev_list = dev_list
self.dev_dic = dev_dic
# config
self.config = tools.config('configs/main.config')
# lang
self.lang = tools.config('configs/sys_value/lang/%s.json5' % language, 'client')
self.lang = tools.config('configs/lang/%s.json5' % self.config['runtime']['language'], 'client')
# value
self.process_id = 'Client'
self.process_name = 'Client process'
self.process_pid = os.getpid()
self.view = 'space'
self.net_mode = net_mode
self.window_config = tools.config('configs/sys_value/window.json5')
self.caption = self.window_config['caption']
self.caption = tools.name_handler(self.caption, {'version': self.window_config['caption_option']['version']})
self.window = window(logger=logger,
dev_dic=dev_dic,
dev_list=dev_list,
language=language,
net_mode=net_mode,
width=int(self.window_config['width']),
height=int(self.window_config['height']),
fullscreen=tools.format_bool(self.window_config['full_screen']),
caption=self.caption,
resizable=tools.format_bool(self.window_config['resizable']),
visible=tools.format_bool(self.window_config['visible']))
self.log_config()
def log_config(self):
self.logger.info('%s: %s %s' % (self.lang['os.pid_is1'], self.process_pid, self.lang['os.pid_is2']))
self.caption = tools.name_handler(self.config['window']['caption'], {'version': self.config['runtime']['version']})
self.window = ClientWindow(dev_dic=self.dev_dic,
dev_list=self.dev_list,
net_mode=self.net_mode,
width=int(self.config['window']['width']),
height=int(self.config['window']['height']),
fullscreen=tools.format_bool(self.config['window']['full_screen']),
caption=self.caption,
resizable=tools.format_bool(self.config['window']['resizable']),
visible=tools.format_bool(self.config['window']['visible']))
def run(self) -> None:
pyglet.app.run()
class window(pyglet.window.Window):
class ClientWindow(pyglet.window.Window):
def __init__(self, logger, dev_dic=None, dev_list=None, language='zh-cn', net_mode='local', *args, **kwargs):
super(window, self).__init__(*args, **kwargs)
def __init__(self, dev_dic=None, dev_list=None, net_mode='local', *args, **kwargs):
super().__init__(*args, **kwargs)
"""
:param dev_list: 共享内存
:param dev_dic: 共享内存
@ -72,79 +68,47 @@ class window(pyglet.window.Window):
:param net_mode: 网络模式 # local / ip
"""
# logging
self.logger = logger
self.logger = logging.getLogger('client')
# share memory
self.dev_list = dev_list
self.dev_dic = dev_dic
# value
self.FPS = int(tools.config('configs/sys_value/window.json5')['fps'])
self.SPF = 1.0 / self.FPS
self.view = 'space'
self.net_mode = net_mode
self.config_file = tools.config('configs/main.config')
self.FPS = int(self.config_file['runtime']['fps'])
self.SPF = 1.0 / self.FPS
# FPS
self.max_fps = [self.FPS, time.time()]
self.min_fps = [self.FPS, time.time()]
self.fps_wait = 5
# lang
self.lang = tools.config('configs/sys_value/lang/%s.json5' % language, 'client')
self.lang = tools.config('configs/lang/%s.json5' % self.config_file['runtime']['language'], 'client')
# configs
self.view = tools.config('configs/view.json5')
self.map_view = [configs.basic_poi(poi_type='chunk')]
self.part_list = tools.config('configs/sys_value/parts.json5')
pyglet.resource.path = ['textures']
pyglet.resource.reindex()
# dic
self.button_hitbox = {}
self.button_toggled = {}
self.ships = {} # all ship(part)
self.planet_system = tools.config('configs/sys_value/planet.json5') # hole planet system
# list
# batch
self.part_batch = pyglet.graphics.Batch()
self.label_batch = pyglet.graphics.Batch()
self.runtime_batch = pyglet.graphics.Batch()
# window
self.logger.info('%s' % self.lang['setup.done'])
self.logger.info(self.lang['setup.done'])
self.textures = {}
# setup
self.setup()
pyglet.clock.schedule_interval(self.update, self.SPF)
def setup(self):
self.logger.info(self.lang['os.pid_is'].format(os.getpid(), os.getppid()))
# values
# net_mode
if self.net_mode == 'local':
pass
# parts textures
self.textures['part'] = {}
parts = tools.config('configs/sys_value/parts.json5')
for part in parts:
path = parts[part][2][0]
part_image = pyglet.resource.image(path)
self.textures['part'][part] = part_image
# runtimes textures
self.textures['runtime'] = {}
runtimes = tools.config('configs/sys_value/runtime.json5')
# load textures
for runtime in runtimes['textures']:
path = runtimes['textures'][runtime]
runtime_image = pyglet.resource.image(path)
self.textures['runtime'][runtime] = runtime_image
# load button's textures
for runtime in runtimes['button']:
if runtime == 'logic':
continue
path = runtimes['button'][runtime]
runtime_image = pyglet.resource.image(path)
runtime_sprite = pyglet.sprite.Sprite(img=runtime_image, batch=self.runtime_batch, x=self.width + 1,
y=self.height + 1)
# self.textures['runtime'][runtime] = runtime_image
self.textures['runtime'][runtime] = runtime_sprite
self.button_hitbox[runtime] = [runtime_image.width, runtime_image.height]
self.button_toggled[runtime] = -1
# info_label
self.info_label = pyglet.text.Label(text='test %s' % pyglet.clock.get_fps(),
x=10, y=self.height - 10,
@ -176,11 +140,7 @@ class window(pyglet.window.Window):
self.info_label.y = self.height - 10
def hit_box_update(self):
for hit_box in self.button_hitbox:
box_ = self.button_hitbox[hit_box]
button = self.textures['runtime'][hit_box]
box = [button.x, button.y, button.x + button.width, button.y + button.height]
self.button_hitbox[hit_box] = box
pass
def on_draw(self):
self.clear()
@ -193,49 +153,8 @@ class window(pyglet.window.Window):
self.label_batch.draw()
def build_draw(self):
self.textures['runtime']['trash_can'].blit(x=self.width - 90, y=self.height - 90)
self.textures['runtime']['trash_can'].blit(x=self.width - 90, y=self.height - 90)
# button tool bar
# start from 20 20
# between 30
# size 50*50
tool_y = 25
back = 0
while back < self.width:
self.textures['runtime']['toolbar_light'].blit(x=back, y=0)
back += self.textures['runtime']['toolbar_light'].width - 1
self.textures['runtime']['to_menu'].x = 20
self.textures['runtime']['to_menu'].y = tool_y
self.textures['runtime']['add_part'].x = 100
self.textures['runtime']['add_part'].y = tool_y
self.textures['runtime']['stage'].x = 180
self.textures['runtime']['stage'].y = tool_y
self.textures['runtime']['zoom'].x = 260
self.textures['runtime']['zoom'].y = tool_y
self.textures['runtime']['play'].x = self.width - 50 - 20
self.textures['runtime']['play'].y = tool_y
if self.button_toggled['zoom'] != -1:
self.textures['runtime']['zoom_in'].x = 260 - 40
self.textures['runtime']['zoom_in'].y = tool_y + 25 + 50
self.textures['runtime']['zoom_out'].x = 260 + 40
self.textures['runtime']['zoom_out'].y = tool_y + 25 + 50
else:
self.button_toggled['zoom_in'] = -1
self.button_toggled['zoom_out'] = -1
self.textures['runtime']['zoom_in'].x = self.width + 1
self.textures['runtime']['zoom_out'].x = self.width + 1
# //todo 把所有要素都加进来+整个设置图标+布局
def space_draw(self):
# render parts
for ship in self.ships:
# get ship poi
ship_poi = ship['brain'][3]
distances = tools.distance(ship_poi, self.map_view)
for part in ship:
pass
pass
# //todo 重写整个渲染机制
def draw_label(self):
pass
@ -244,33 +163,40 @@ class window(pyglet.window.Window):
keyboard and mouse input
"""
def on_mouse_motion(self, x, y, dx, dy):
def on_mouse_motion(self, x, y, dx, dy) -> None:
# self.logger.debug('按键移动 %s %s %s %s' % (x, y, dx, dy))
pass
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers):
def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers) -> None:
# self.logger.debug('按键拖拽 %s %s %s %s %s %s' %(x, y, dx, dy, buttons, modifiers))
pass
def on_mouse_press(self, x, y, button, modifiers):
def on_mouse_press(self, x, y, button, modifiers) -> None:
if button == mouse.LEFT:
self.logger.debug('左键! 在 x:%s y:%s' % (x, y))
for hit_box in self.button_hitbox:
box = self.button_hitbox[hit_box]
if (box[0] <= x <= box[2]) and (box[1] <= y <= box[3]):
self.button_toggled[hit_box] *= -1
self.logger.debug('%s %s %s' % (hit_box,
self.lang['button.been_press'],
self.button_toggled[hit_box]))
break
self.logger.debug(self.lang['mouse.press_at'].format([x, y], self.lang['mouse.left']))
elif button == mouse.RIGHT:
self.logger.debug('右键! 在 x:%s y:%s' % (x, y))
self.logger.debug(self.lang['mouse.press_at'].format([x, y], self.lang['mouse.right']))
def on_key_press(self, symbol, modifiers):
def on_mouse_release(self, x, y, button, modifiers) -> None:
pass
def on_key_press(self, symbol, modifiers) -> None:
if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK |
key.MOD_CAPSLOCK |
key.MOD_SCROLLLOCK)):
self.dispatch_event('on_close')
def on_key_release(self, symbol, modifiers):
def on_key_release(self, symbol, modifiers) -> None:
pass
def on_close(self) -> None:
config_file = configparser.ConfigParser()
config_file.read('configs/main.config')
config_file['window']['width'] = str(self.width)
config_file['window']['height'] = str(self.height)
config_file.write(open('configs/main.config', 'w', encoding='utf-8'))
# pyglet source code
self.has_exit = True
from pyglet import app
if app.event_loop.is_running:
self.close()

View File

@ -17,6 +17,7 @@
from .lib import load, loads, dump, dumps
from .version import VERSION
__all__ = [
'VERSION',
'dump',

View File

@ -16,5 +16,6 @@ import sys # pragma: no cover
from .tool import main # pragma: no cover
if __name__ == '__main__': # pragma: no cover
sys.exit(main())

View File

@ -17,6 +17,7 @@ import shutil
import sys
import tempfile
if sys.version_info[0] < 3:
# pylint: disable=redefined-builtin, invalid-name
str = unicode

View File

@ -19,9 +19,12 @@ import unicodedata
from .parser import Parser
if sys.version_info[0] < 3:
str_types = (str, unicode)
str = unicode # pylint: disable=redefined-builtin, invalid-name
else:
str_types = (str,)
long = int # pylint: disable=redefined-builtin, invalid-name
@ -86,7 +89,7 @@ def loads(s, encoding=None, cls=None, object_hook=None, parse_float=None,
elif object_hook:
dictify = lambda pairs: object_hook(dict(pairs))
else:
dictify = lambda pairs: dict(pairs) # pylint: disable=unnecessary-lambda
dictify = lambda pairs: dict(pairs) # pylint: disable=unnecessary-lambda
if not allow_duplicate_keys:
_orig_dictify = dictify
@ -107,7 +110,6 @@ def _reject_duplicate_keys(pairs, dictify):
keys.add(key)
return dictify(pairs)
def _walk_ast(el, dictify, parse_float, parse_int, parse_constant):
if el == 'None':
return None
@ -240,24 +242,23 @@ def _dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, indent,
separators, default, sort_keys,
quote_keys, trailing_commas, allow_duplicate_keys,
seen, level, is_key):
s = None
if obj is True:
s = u'true'
if obj is False:
elif obj is False:
s = u'false'
if obj is None:
elif obj is None:
s = u'null'
t = type(obj)
if t == type('') or t == type(u''):
elif isinstance(obj, str_types):
if (is_key and _is_ident(obj) and not quote_keys
and not _is_reserved_word(obj)):
and not _is_reserved_word(obj)):
return True, obj
return True, _dump_str(obj, ensure_ascii)
if t is float:
s = _dump_float(obj, allow_nan)
if t is int:
elif isinstance(obj, float):
s = _dump_float(obj,allow_nan)
elif isinstance(obj, int):
s = str(obj)
else:
s = None
if is_key:
if s is not None:
@ -300,14 +301,14 @@ def _dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, indent,
# In Python3, we'd check if this was an abc.Mapping or an abc.Sequence.
# For now, just check for the attrs we need to iterate over the object.
if hasattr(t, 'keys') and hasattr(t, '__getitem__'):
if hasattr(obj, 'keys') and hasattr(obj, '__getitem__'):
s = _dump_dict(obj, skipkeys, ensure_ascii,
check_circular, allow_nan, indent,
separators, default, sort_keys,
quote_keys, trailing_commas,
allow_duplicate_keys, seen, level,
item_sep, kv_sep, indent_str, end_str)
elif hasattr(t, '__getitem__') and hasattr(t, '__iter__'):
elif hasattr(obj, '__getitem__') and hasattr(obj, '__iter__'):
s = _dump_array(obj, skipkeys, ensure_ascii,
check_circular, allow_nan, indent,
separators, default, sort_keys,
@ -459,7 +460,6 @@ def _is_id_continue(ch):
_reserved_word_re = None
def _is_reserved_word(k):
global _reserved_word_re

View File

@ -2,6 +2,7 @@
import sys
if sys.version_info[0] < 3:
# pylint: disable=redefined-builtin,invalid-name
chr = unichr
@ -474,8 +475,7 @@ class Parser(object):
lambda: self._bind(self._hex_, 'b'),
lambda: self._bind(self._hex_, 'c'),
lambda: self._bind(self._hex_, 'd'),
lambda: self._succeed(
self._xtou(self._get('a') + self._get('b') + self._get('c') + self._get('d')))])
lambda: self._succeed(self._xtou(self._get('a') + self._get('b') + self._get('c') + self._get('d')))])
self._pop('unicode_esc')
def _element_list_(self):

View File

@ -12,4 +12,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
VERSION = '0.9.5'
VERSION = '0.9.6'

View File

@ -53,15 +53,7 @@ _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run
#:
#: Valid only if pyglet was installed from a source or binary distribution
#: (i.e. not in a checked-out copy from SVN).
#:
#: Use setuptools if you need to check for a specific release version, e.g.::
#:
#: >>> import pyglet
#: >>> from pkg_resources import parse_version
#: >>> parse_version(pyglet.version) >= parse_version('1.1')
#: True
#:
version = '1.5.16'
version = '1.5.18'
if sys.version_info < (3, 6):

View File

@ -35,7 +35,7 @@
import pyglet
import warnings
from pyglet import app
from .base import Display, Screen, ScreenMode, Canvas
@ -56,12 +56,13 @@ class HeadlessDisplay(Display):
if num_devices.value > 0:
headless_device = pyglet.options['headless_device']
if headless_device < 0 or headless_device >= num_devices.value:
raise ValueError('Invalid EGL devide id: %d' % headless_device)
raise ValueError('Invalid EGL devide id: %d' % headless_device)
devices = (eglext.EGLDeviceEXT * num_devices.value)()
eglext.eglQueryDevicesEXT(num_devices.value, devices, byref(num_devices))
self._display_connection = eglext.eglGetPlatformDisplayEXT(eglext.EGL_PLATFORM_DEVICE_EXT, devices[headless_device], None)
self._display_connection = eglext.eglGetPlatformDisplayEXT(
eglext.EGL_PLATFORM_DEVICE_EXT, devices[headless_device], None)
else:
warning.warn('No device available for EGL device platform. Using native display type.')
warnings.warn('No device available for EGL device platform. Using native display type.')
display = egl.EGLNativeDisplayType()
self._display_connection = egl.eglGetDisplay(display)
@ -76,9 +77,10 @@ class HeadlessDisplay(Display):
class HeadlessCanvas(Canvas):
def __init__(self, display, egl_surface):
super(HeadlessCanvas, self).__init__(display)
super().__init__(display)
self.egl_surface = egl_surface
class HeadlessScreen(Screen):
def __init__(self, display, x, y, width, height):
super().__init__(display, x, y, width, height)
@ -89,7 +91,6 @@ class HeadlessScreen(Screen):
# XXX deprecate
for config in configs:
config.screen = self
# print("Canvas", canvas, "configs", configs)
return configs
def get_modes(self):

File diff suppressed because it is too large Load Diff

View File

@ -144,7 +144,7 @@ def load(name=None, size=None, bold=False, italic=False, stretch=False, dpi=None
font = _font_class(name, size, bold=bold, italic=italic, stretch=stretch, dpi=dpi)
# Save parameters for new-style layout classes to recover
font.name = name
# TODO: add properties to the Font classes, so these can be queried:
font.size = size
font.bold = bold
font.italic = italic

View File

@ -303,6 +303,11 @@ class Font:
self.textures = []
self.glyphs = {}
@property
def name(self):
"""Return the Family Name of the font as a string."""
raise NotImplementedError
@classmethod
def add_font_data(cls, data):
"""Add font data to the font loader.
@ -394,7 +399,6 @@ class Font:
glyphs.append(self.glyphs[c])
return glyphs
def get_glyphs_for_width(self, text, width):
"""Return a list of glyphs for `text` that fit within the given width.

View File

@ -1821,7 +1821,7 @@ class Win32DirectWriteFont(base.Font):
self._font_index, self._collection = self.get_collection(name)
assert self._collection is not None, "Font: {} not found in loaded or system font collection.".format(name)
self.name = name
self._name = name
self.bold = bold
self.size = size
self.italic = italic
@ -1867,7 +1867,7 @@ class Win32DirectWriteFont(base.Font):
# Create the text format this font will use permanently.
# Could technically be recreated, but will keep to be inline with other font objects.
self._text_format = IDWriteTextFormat()
self._write_factory.CreateTextFormat(self.name,
self._write_factory.CreateTextFormat(self._name,
self._collection,
self._weight,
self._style,
@ -1903,6 +1903,10 @@ class Win32DirectWriteFont(base.Font):
self.max_glyph_height = (self._font_metrics.ascent + self._font_metrics.descent) * self.font_scale_ratio
self.line_gap = self._font_metrics.lineGap * self.font_scale_ratio
@property
def name(self):
return self._name
def render_to_image(self, text, width=10000, height=80):
"""This process takes Pyglet out of the equation and uses only DirectWrite to shape and render text.
This may allow more accurate fonts (bidi, rtl, etc) in very special circumstances at the cost of

View File

@ -168,7 +168,7 @@ class FreeTypeFont(base.Font):
warnings.warn("The current font render does not support stretching.")
super().__init__()
self.name = name
self._name = name
self.size = size
self.bold = bold
self.italic = italic
@ -177,6 +177,10 @@ class FreeTypeFont(base.Font):
self._load_font_face()
self.metrics = self.face.get_font_metrics(self.size, self.dpi)
@property
def name(self):
return self.face.family_name
@property
def ascent(self):
return self.metrics.ascent
@ -191,14 +195,14 @@ class FreeTypeFont(base.Font):
return self.face.get_glyph_slot(glyph_index)
def _load_font_face(self):
self.face = self._memory_faces.get(self.name, self.bold, self.italic)
self.face = self._memory_faces.get(self._name, self.bold, self.italic)
if self.face is None:
self._load_font_face_from_system()
def _load_font_face_from_system(self):
match = get_fontconfig().find_font(self.name, self.size, self.bold, self.italic)
match = get_fontconfig().find_font(self._name, self.size, self.bold, self.italic)
if not match:
raise base.FontException('Could not match font "%s"' % self.name)
raise base.FontException('Could not match font "%s"' % self._name)
self.face = FreeTypeFace.from_fontconfig(match)
@classmethod
@ -320,7 +324,7 @@ class FreeTypeFace:
descent=-ascent // 4) # arbitrary.
def _get_best_name(self):
self.name = self.family_name
self._name = self.family_name
self._get_font_family_from_ttf
def _get_font_family_from_ttf(self):
@ -339,7 +343,7 @@ class FreeTypeFace:
name.encoding_id == TT_MS_ID_UNICODE_CS):
continue
# name.string is not 0 terminated! use name.string_len
self.name = name.string.decode('utf-16be', 'ignore')
self._name = name.string.decode('utf-16be', 'ignore')
except:
continue

View File

@ -231,9 +231,17 @@ class QuartzFont(base.Font):
cf.CFRelease(descriptor)
assert self.ctFont, "Couldn't load font: " + name
string = c_void_p(ct.CTFontCopyFamilyName(self.ctFont))
self._family_name = str(cocoapy.cfstring_to_string(string))
cf.CFRelease(string)
self.ascent = int(math.ceil(ct.CTFontGetAscent(self.ctFont)))
self.descent = -int(math.ceil(ct.CTFontGetDescent(self.ctFont)))
@property
def name(self):
return self._family_name
def __del__(self):
cf.CFRelease(self.ctFont)

View File

@ -492,26 +492,25 @@ class GDIPlusFont(Win32Font):
if stretch:
warnings.warn("The current font render does not support stretching.")
super(GDIPlusFont, self).__init__(name, size, bold, italic, stretch, dpi)
super().__init__(name, size, bold, italic, stretch, dpi)
self._name = name
family = ctypes.c_void_p()
name = ctypes.c_wchar_p(name)
# Look in private collection first:
if self._private_fonts:
gdiplus.GdipCreateFontFamilyFromName(name,
self._private_fonts, ctypes.byref(family))
gdiplus.GdipCreateFontFamilyFromName(name, self._private_fonts, ctypes.byref(family))
# Then in system collection:
if not family:
gdiplus.GdipCreateFontFamilyFromName(name,
None, ctypes.byref(family))
gdiplus.GdipCreateFontFamilyFromName(name, None, ctypes.byref(family))
# Nothing found, use default font.
if not family:
name = self._default_name
gdiplus.GdipCreateFontFamilyFromName(ctypes.c_wchar_p(name),
None, ctypes.byref(family))
self._name = self._default_name
gdiplus.GdipCreateFontFamilyFromName(ctypes.c_wchar_p(self._name), None, ctypes.byref(family))
if dpi is None:
unit = UnitPoint
@ -527,13 +526,16 @@ class GDIPlusFont(Win32Font):
if italic:
style |= FontStyleItalic
self._gdipfont = ctypes.c_void_p()
gdiplus.GdipCreateFont(family, ctypes.c_float(size),
style, unit, ctypes.byref(self._gdipfont))
gdiplus.GdipCreateFont(family, ctypes.c_float(size), style, unit, ctypes.byref(self._gdipfont))
gdiplus.GdipDeleteFontFamily(family)
@property
def name(self):
return self._name
def __del__(self):
super(GDIPlusFont, self).__del__()
result = gdiplus.GdipDeleteFont(self._gdipfont)
gdiplus.GdipDeleteFont(self._gdipfont)
@classmethod
def add_font_data(cls, data):

View File

@ -39,8 +39,8 @@
import array
import itertools
from pyglet.image import *
from pyglet.image.codecs import *
from pyglet.image import ImageData, ImageDecodeException
from pyglet.image.codecs import ImageDecoder, ImageEncoder
import pyglet.extlibs.png as pypng
@ -54,8 +54,7 @@ class PNGImageDecoder(ImageDecoder):
reader = pypng.Reader(file=file)
width, height, pixels, metadata = reader.asDirect()
except Exception as e:
raise ImageDecodeException(
'PyPNG cannot read %r: %s' % (filename or file, e))
raise ImageDecodeException('PyPNG cannot read %r: %s' % (filename or file, e))
if metadata['greyscale']:
if metadata['alpha']:
@ -95,10 +94,10 @@ class PNGImageEncoder(ImageEncoder):
image.pitch = -(image.width * len(image.format))
writer = pypng.Writer(image.width, image.height, bytes_per_sample=1, greyscale=greyscale, alpha=has_alpha)
writer = pypng.Writer(image.width, image.height, greyscale=greyscale, alpha=has_alpha)
data = array.array('B')
data.fromstring(image.get_data(image.format, image.pitch))
data.frombytes(image.get_data(image.format, image.pitch))
writer.write_array(file, data)

View File

@ -236,7 +236,8 @@ def get_devices(display=None):
def _create_joystick(device):
if device._type in (dinput.DI8DEVTYPE_JOYSTICK,
dinput.DI8DEVTYPE_1STPERSON,
dinput.DI8DEVTYPE_GAMEPAD):
dinput.DI8DEVTYPE_GAMEPAD,
dinput.DI8DEVTYPE_SUPPLEMENTAL):
return base.Joystick(device)

View File

@ -1,9 +1,8 @@
from ctypes import *
import pyglet.lib
from pyglet.gl.lib import missing_function
import pyglet
import pyglet.util
from pyglet.util import asbytes
__all__ = ['link_EGL']
@ -22,11 +21,11 @@ def link_EGL(name, restype, argtypes, requires=None, suggestions=None):
func.argtypes = argtypes
return func
except AttributeError:
bname = cast(pointer(create_string_buffer(asbytes(name))), POINTER(c_ubyte))
bname = cast(pointer(create_string_buffer(pyglet.util.asbytes(name))), POINTER(c_ubyte))
addr = eglGetProcAddress(bname)
if addr:
ftype = CFUNCTYPE(*((restype,) + tuple(argtypes)))
func = cast(addr, ftype)
return func
return missing_function(name, requires, suggestions)
return pyglet.gl.lib.missing_function(name, requires, suggestions)

View File

@ -410,18 +410,6 @@ class StreamingSource(Source):
:class:`~pyglet.media.player.Player`.
"""
@property
def is_queued(self):
"""
bool: Determine if this source is a player current source.
Check on a :py:class:`~pyglet.media.player.Player` if this source
is the current source.
:deprecated: Use :attr:`is_player_source` instead.
"""
return self.is_player_source
def get_queue_source(self):
"""Return the ``Source`` to be used as the source for a player.

View File

@ -116,7 +116,7 @@ def ffmpeg_get_audio_buffer_size(audio_format):
Buffer size can accomodate 1 sec of audio data.
"""
return audio_format.bytes_per_second
return audio_format.bytes_per_second + FF_INPUT_BUFFER_PADDING_SIZE
def ffmpeg_init():
@ -558,8 +558,8 @@ class FFmpegSource(StreamingSource):
self._max_len_audioq = 50 # Need to figure out a correct amount
if self.audio_format:
# Buffer 1 sec worth of audio
self._audio_buffer = \
(c_uint8 * ffmpeg_get_audio_buffer_size(self.audio_format))()
nbytes = ffmpeg_get_audio_buffer_size(self.audio_format)
self._audio_buffer = (c_uint8 * nbytes)()
self.videoq = deque()
self._max_len_videoq = 50 # Need to figure out a correct amount
@ -887,7 +887,9 @@ class FFmpegSource(StreamingSource):
width = self.video_format.width
height = self.video_format.height
pitch = width * 4
buffer = (c_uint8 * (pitch * height))()
# https://ffmpeg.org/doxygen/3.3/group__lavc__decoding.html#ga8f5b632a03ce83ac8e025894b1fc307a
nbytes = (pitch * height + FF_INPUT_BUFFER_PADDING_SIZE)
buffer = (c_uint8 * nbytes)()
try:
result = self._ffmpeg_decode_video(video_packet.packet,
buffer)

View File

@ -67,7 +67,12 @@ class _GLibMainLoopThread(Thread):
class _MessageHandler:
"""Message Handler class for GStreamer Sources."""
"""Message Handler class for GStreamer Sources.
This separate class holds a weak reference to the
Source, preventing garbage collection issues.
"""
def __init__(self, source):
self.source = weakref.proxy(source)
@ -208,13 +213,13 @@ class GStreamerSource(StreamingSource):
self._file.close()
try:
# self._pipeline.bus.remove_signal_watch()
while not self.queue.empty():
self.queue.get_nowait()
sink = self.appsink.get_static_pad("sink")
if sink.handler_is_connected(self.caps_handler):
sink.disconnect(self.caps_handler)
while not self.queue.empty():
self.queue.get_nowait()
self._pipeline.set_state(Gst.State.NULL)
self._pipeline.bus.remove_signal_watch()
self.filesrc.set_property("location", None)
except (ImportError, AttributeError):
pass

View File

@ -71,11 +71,7 @@ def get_audio_driver():
from pyglet.libs.win32.constants import WINDOWS_8_OR_GREATER
if WINDOWS_8_OR_GREATER:
from . import xaudio2
try:
_audio_driver = xaudio2.create_audio_driver()
except ImportError:
# Occurs when default audio device is not found, and cannot bind.
pass
_audio_driver = xaudio2.create_audio_driver()
break
elif driver_name == 'directsound':
from . import directsound
@ -94,6 +90,9 @@ def get_audio_driver():
print('Error importing driver %s:' % driver_name)
import traceback
traceback.print_exc()
else:
from . import silent
_audio_driver = silent.create_audio_driver()
return _audio_driver

View File

@ -56,12 +56,12 @@ def _gain2db(gain):
Convert linear gain in range [0.0, 1.0] to 100ths of dB.
Power gain = P1/P2
dB = 10 log(P1/P2)
dB = 2 log(P1/P2)
dB * 100 = 1000 * log(power gain)
"""
if gain <= 0:
return -10000
return max(-10000, min(int(1000 * math.log10(min(gain, 1))), 0))
return max(-10000, min(int(1000 * math.log2(min(gain, 1))), 0))
def _db2gain(db):

View File

@ -76,6 +76,8 @@ class XAudio2Driver:
self._players = [] # Only used for resetting/restoring xaudio2. Store players to callback.
self._create_xa2()
if self.restart_on_error:
audio_devices = get_audio_device_manager()
if audio_devices:
@ -87,8 +89,6 @@ class XAudio2Driver:
pyglet.clock.schedule_interval_soft(self._check_state, 0.5)
self._create_xa2()
def _check_state(self, dt):
"""Hack/workaround, you cannot shutdown/create XA2 within a COM callback, set a schedule to check state."""
if self._dead is True:
@ -115,7 +115,11 @@ class XAudio2Driver:
def _create_xa2(self, device_id=None):
self._xaudio2 = lib.IXAudio2()
lib.XAudio2Create(ctypes.byref(self._xaudio2), 0, self.processor)
try:
lib.XAudio2Create(ctypes.byref(self._xaudio2), 0, self.processor)
except OSError:
raise ImportError("XAudio2 driver could not be initialized.")
if _debug:
# Debug messages are found in Windows Event Viewer, you must enable event logging:

View File

@ -260,6 +260,8 @@ class Player(pyglet.event.EventDispatcher):
The internal audio player and the texture will be deleted.
"""
if self._source:
self.source.is_player_source = False
if self._audio_player:
self._audio_player.delete()
self._audio_player = None

View File

@ -36,12 +36,12 @@
"""2D shapes.
This module provides classes for a variety of simplistic 2D shapes,
such as Rectangles, Circles, and Lines. These shapes are are made
such as Rectangles, Circles, and Lines. These shapes are made
internally from OpenGL primitives, and provide excellent performance
when drawn as part of a :py:class:`~pyglet.graphics.Batch`.
Convenience methods are provided for positioning, changing color
and opacity, and rotation (where applicible). To create more
complex shapes than what is provided here, the lower evel
complex shapes than what is provided here, the lower level
graphics API is more appropriate.
See the :ref:`guide_graphics` for more details.
@ -60,6 +60,7 @@ A simple example of drawing shapes::
rectangle.rotation = 33
line = shapes.Line(100, 100, 100, 200, width=19, batch=batch)
line2 = shapes.Line(150, 150, 444, 111, width=4, color=(200, 20, 20), batch=batch)
star = shapes.Star(800, 400, 60, 40, num_spikes=20, color=(255, 255, 0), batch=batch)
@window.event
def on_draw():
@ -467,7 +468,7 @@ class Circle(_ShapeBase):
points = [(x + (r * math.cos(i * tau_segs)),
y + (r * math.sin(i * tau_segs))) for i in range(self._segments)]
# Create a list of trianges from the points:
# Create a list of triangles from the points:
vertices = []
for i, point in enumerate(points):
triangle = x, y, *points[i - 1], *point
@ -946,4 +947,125 @@ class Triangle(_ShapeBase):
self._update_position()
__all__ = ('Arc', 'Circle', 'Line', 'Rectangle', 'BorderedRectangle', 'Triangle')
class Star(_ShapeBase):
def __init__(self, x, y, outer_radius, inner_radius, num_spikes, rotation=0,
color=(255, 255, 255), batch=None, group=None) -> None:
"""Create a star.
The star's anchor point (x, y) defaults to the center of the star.
:Parameters:
`x` : float
The X coordinate of the star.
`y` : float
The Y coordinate of the star.
`outer_radius` : float
The desired outer radius of the star.
`inner_radius` : float
The desired inner radius of the star.
`num_spikes` : float
The desired number of spikes of the star.
`rotation` : float
The rotation of the star in degrees. A rotation of 0 degrees
will result in one spike lining up with the X axis in
positive direction.
`color` : (int, int, int)
The RGB color of the star, specified as
a tuple of three ints in the range of 0-255.
`batch` : `~pyglet.graphics.Batch`
Optional batch to add the star to.
`group` : `~pyglet.graphics.Group`
Optional parent group of the star.
"""
self._x = x
self._y = y
self._outer_radius = outer_radius
self._inner_radius = inner_radius
self._num_spikes = num_spikes
self._rgb = color
self._rotation = rotation
self._batch = batch or Batch()
self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, group)
self._vertex_list = self._batch.add(self._num_spikes*6, GL_TRIANGLES,
self._group, 'v2f', 'c4B')
self._update_position()
self._update_color()
def _update_position(self):
if not self._visible:
vertices = (0, 0) * self._num_spikes * 6
else:
x = self._x + self._anchor_x
y = self._y + self._anchor_y
r_i = self._inner_radius
r_o = self._outer_radius
# get angle covered by each line (= half a spike)
d_theta = math.pi / self._num_spikes
# phase shift rotation
phi = self._rotation / 180 * math.pi
# calculate alternating points on outer and outer circles
points = []
for i in range(self._num_spikes):
points.append((x + (r_o * math.cos(2*i * d_theta + phi)),
y + (r_o * math.sin(2*i * d_theta + phi))))
points.append((x + (r_i * math.cos((2*i+1) * d_theta + phi)),
y + (r_i * math.sin((2*i+1) * d_theta + phi))))
# create a list of doubled-up points from the points
vertices = []
for i, point in enumerate(points):
triangle = x, y, *points[i - 1], *point
vertices.extend(triangle)
self._vertex_list.vertices[:] = vertices
def _update_color(self):
self._vertex_list.colors[:] = [*self._rgb, int(self._opacity)] * self._num_spikes * 6
@property
def outer_radius(self):
"""The outer radius of the star."""
return self._outer_radius
@outer_radius.setter
def outer_radius(self, value):
self._outer_radius = value
self._update_position()
@property
def inner_radius(self):
"""The innter radius of the star."""
return self._inner_radius
@inner_radius.setter
def inner_radius(self, value):
self._inner_radius = value
self._update_position()
@property
def num_spikes(self):
"""Number of spikes of the star."""
return self._num_spikes
@num_spikes.setter
def num_spikes(self, value):
self._num_spikes = value
self._update_position()
@property
def rotation(self):
"""Rotation of the star, in degrees.
"""
return self._rotation
@rotation.setter
def rotation(self, rotation):
self._rotation = rotation
self._update_position()
__all__ = ('Arc', 'Circle', 'Line', 'Rectangle', 'BorderedRectangle', 'Triangle', 'Star')

View File

@ -40,7 +40,7 @@ Abstract representation
Styled text in pyglet is represented by one of the :py:class:`~pyglet.text.document.AbstractDocument` classes,
which manage the state representation of text and style independently of how
it is loaded or rendered.
it is loaded or rendered.
A document consists of the document text (a Unicode string) and a set of
named style ranges. For example, consider the following (artificial)
@ -131,7 +131,7 @@ entire paragraph, otherwise results are undefined.
``align``
``left`` (default), ``center`` or ``right``.
``indent``
Additional horizontal space to insert before the first
Additional horizontal space to insert before the first
``leading``
Additional space to insert between consecutive lines within a paragraph,
in points. Defaults to 0.
@ -159,7 +159,7 @@ document; it will be ignored by the built-in text classes.
All style attributes (including those not present in a document) default to
``None`` (including the so-called "boolean" styles listed above). The meaning
of a ``None`` style is style- and application-dependent.
of a ``None`` style is style- and application-dependent.
.. versionadded:: 1.1
"""
@ -181,8 +181,8 @@ class InlineElement:
Elements behave like a single glyph in the document. They are
measured by their horizontal advance, ascent above the baseline, and
descent below the baseline.
descent below the baseline.
The pyglet layout classes reserve space in the layout for elements and
call the element's methods to ensure they are rendered at the
appropriate position.
@ -225,8 +225,8 @@ class InlineElement:
It is the responsibility of the element to clip itself against
the layout boundaries, and position itself appropriately with respect
to the layout's position and viewport offset.
to the layout's position and viewport offset.
The `TextLayout.top_state` graphics state implements this transform
and clipping into window space.
@ -263,7 +263,7 @@ class AbstractDocument(event.EventDispatcher):
This class can be overridden to interface pyglet with a third-party
document format. It may be easier to implement the document format in
terms of one of the supplied concrete classes :py:class:`~pyglet.text.document.FormattedDocument` or
:py:class:`~pyglet.text.document.UnformattedDocument`.
:py:class:`~pyglet.text.document.UnformattedDocument`.
"""
_previous_paragraph_re = re.compile(u'\n[^\n\u2029]*$')
_next_paragraph_re = re.compile(u'[\n\u2029]')
@ -717,13 +717,13 @@ class _FontStyleRunsRangeIterator:
from pyglet import font
for start, end, styles in self.zip_iter.ranges(start, end):
font_name, font_size, bold, italic, stretch = styles
ft = font.load(font_name, font_size, bold=bold, italic=italic, stretch=stretch, dpi=self.dpi)
ft = font.load(font_name, font_size, bold=bool(bold), italic=bool(italic), stretch=stretch, dpi=self.dpi)
yield start, end, ft
def __getitem__(self, index):
from pyglet import font
font_name, font_size, bold, italic, stretch = self.zip_iter[index]
return font.load(font_name, font_size, bold=bold, italic=italic, stretch=stretch, dpi=self.dpi)
return font.load(font_name, font_size, bold=bool(bold), italic=bool(italic), stretch=stretch, dpi=self.dpi)
class _NoStyleRangeIterator:

View File

@ -595,27 +595,18 @@ class IncrementalTextLayoutGroup(graphics.Group):
def set_state(self):
glPushAttrib(GL_ENABLE_BIT | GL_TRANSFORM_BIT | GL_CURRENT_BIT)
glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
# Disable clipping planes to check culling.
glEnable(GL_CLIP_PLANE0)
glEnable(GL_CLIP_PLANE1)
glEnable(GL_CLIP_PLANE2)
glEnable(GL_CLIP_PLANE3)
# Left
glClipPlane(GL_CLIP_PLANE0, (GLdouble * 4)(1, 0, 0, -(self._clip_x - 1)))
# Top
glClipPlane(GL_CLIP_PLANE1, (GLdouble * 4)(0, -1, 0, self._clip_y))
# Right
glClipPlane(GL_CLIP_PLANE2, (GLdouble * 4)(-1, 0, 0, self._clip_x + self._clip_width + 1))
# Bottom
glClipPlane(GL_CLIP_PLANE3, (GLdouble * 4)(0, 1, 0, -(self._clip_y - self._clip_height)))
glEnable(GL_SCISSOR_TEST)
glScissor(self._clip_x, self._clip_y - self._clip_height, self._clip_width, self._clip_height)
glTranslatef(self.translate_x, self.translate_y, 0)
def unset_state(self):
glTranslatef(-self.translate_x, -self.translate_y, 0)
glDisable(GL_SCISSOR_TEST)
glPopAttrib()
@property
@ -941,7 +932,9 @@ class TextLayout:
else:
dx = x - self._x
for vertex_list in self._vertex_lists:
vertex_list.vertices[::2] = [v + dx for v in vertex_list.vertices[::2]]
vertices = vertex_list.vertices[:]
vertices[::2] = [x + dx for x in vertices[::2]]
vertex_list.vertices[:] = vertices
self._x = x
@property
@ -965,7 +958,9 @@ class TextLayout:
else:
dy = y - self._y
for vertex_list in self._vertex_lists:
vertex_list.vertices[1::2] = [v + dy for v in vertex_list.vertices[1::2]]
vertices = vertex_list.vertices[:]
vertices[1::2] = [y + dy for y in vertices[1::2]]
vertex_list.vertices[:] = vertices
self._y = y
@property

View File

@ -125,6 +125,7 @@ above, "Working with multiple screens")::
import sys
import math
import warnings
import pyglet
import pyglet.window.key
@ -647,6 +648,15 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
app.windows.add(self)
self._create()
# Raise a warning if an OpenGL 2.0 context is not available. This is a common case
# with virtual machines, or on Windows without fully supported GPU drivers.
gl_info = context.get_info()
if not gl_info.have_version(2, 0):
message = ("\nYour graphics drivers do not support OpenGL 2.0.\n"
"You may experience rendering issues or crashes.\n"
f"{gl_info.get_vendor()}\n{gl_info.get_renderer()}\n{gl_info.get_version()}")
warnings.warn(message)
self.switch_to()
if visible:
self.set_visible(True)

View File

@ -3,10 +3,11 @@ writen by shenjackyuanjie
mail: 3695888@qq.com
"""
import logging
import os
import sys
import time
import logging
import multiprocessing
# share memory
from multiprocessing import Manager as share
@ -38,11 +39,11 @@ class Game:
# lang_config
self.language = tools.config('configs/sys_value/basic_config.json5')
self.language = self.language['language']
self.lang = tools.config('configs/sys_value/lang/%s.json5' % self.language, 'main')
self.lang = tools.config('configs/lang/%s.json5' % self.language, 'main')
# logger
self.log_config = tools.config('configs/logging.json5', 'file')
self.log_filename = tools.name_handler(self.log_config['filename']['main'],
{'date': self.log_config['date_fmt']})
{'{date}': self.log_config['date_fmt']})
self.root_logger_fmt = logging.Formatter(self.log_config['fmt'], self.log_config['date_fmt'])
self.root_logger_stream_handler = logging.StreamHandler()
self.root_logger_stream_handler.setFormatter(self.root_logger_fmt)
@ -59,19 +60,18 @@ class Game:
logging.getLogger().addHandler(self.root_logger_stream_handler)
logging.getLogger().addHandler(self.root_logger_file_handler)
# create logger
self.main_logger = logging.getLogger().getChild('main')
self.server_logger = logging.getLogger().getChild('server')
self.client_logger = logging.getLogger().getChild('client')
self.main_logger = logging.getLogger('main')
# output info
self.main_logger.info(self.lang['logger.created'])
self.main_logger.info(self.lang['logger.main_done'])
self.log_configs()
# version check
self.python_version_check()
# client and server
self.client = client.client(self.client_logger, self.dicts, self.lists, self.language, net_mode='local')
self.server = server.server(self.lists, self.dicts, self.server_logger, language=self.language,
net_mode='local')
self.setup()
def setup(self):
self.client = client.Client(self.lists, self.dicts, net_mode='local')
self.server = server.server(self.lists, self.dicts, net_mode='local')
def log_configs(self):
self.main_logger.info('%s %s' % (self.lang['logger.language'], self.lang['lang.language']))
@ -81,15 +81,15 @@ class Game:
self.main_logger.debug('%s %s' % (self.lang['logger.logfile_fmt'], self.log_config['fmt']))
self.main_logger.debug('%s %s' % (self.lang['logger.logfile_datefmt'], self.log_config['date_fmt']))
def python_version_check(self): # best 3.8+ and write at 3.8.9
def python_version_check(self): # best 3.8+ and write at 3.8.10
self.main_logger.info('%s %s' % (self.lang['version.now_on'], self.on_python_v))
if self.on_python_v_info[0] == 2:
self.main_logger.critical('%s' % self.lang['version.need3+'])
raise SystemError('%s' % self.lang['version.need3+'])
elif self.on_python_v_info[1] < 9:
elif self.on_python_v_info[1] < 8:
warning = tools.name_handler(self.lang['version.best3.8+'])
self.main_logger.warning(warning)
def start(self):
# start
self.server.run()
self.client.run()

View File

@ -27,7 +27,7 @@ def new_thread(thread_name: Optional[str or Callable] = None):
# bring the signature of the func to the wrap function
# so inspect.getfullargspec(func) works correctly
# https://stackoverflow.com/questions/39926567find/python-create-decorator-preserving-function-arguments
# https://stackoverflow.com/questions/39926567/python-create-decorator-preserving-function-arguments
wrap.__signature__ = inspect.signature(func)
return wrap

View File

@ -2,7 +2,7 @@
writen by shenjackyuanjie
mail: 3695888@qq.com
"""
import logging
import multiprocessing as mp
import os
@ -12,23 +12,24 @@ except (ModuleNotFoundError, ImportError, ImportWarning):
import tools
class server(mp.Process):
def __init__(self, dev_list, dev_dic, logger, language='zh-cn', net_mode='local'):
class server:
def __init__(self, dev_list, dev_dic, net_mode='local'):
# father class __init__()
mp.Process.__init__(self)
# mp.Process.__init__(self)
# logging
self.logger = logger
self.logger = logging.getLogger('server')
# value
self.process_id = 'Server'
self.process_name = 'server process'
self.process_pid = os.getpid()
# config
self.config = tools.config('configs/main.config')
# lang
self.lang = tools.config('configs/sys_value/lang/%s.json5' % language, 'server')
self.lang = tools.config('configs/lang/%s.json5' % self.config['runtime']['language'], 'server')
# share memory
self.dev_list = dev_list
self.dev_dic = dev_dic
self.logger.info('%s: %s %s' % (self.lang['os.pid_is1'], self.process_pid, self.lang['os.pid_is2']))
self.logger.info('%s' % self.lang['setup.done'])
def run(self):
pass
self.logger.info(self.lang['os.pid_is'].format(os.getpid(), os.getppid()))

View File

@ -5,12 +5,14 @@ github: @shenjackyuanjie
"""
import configparser
import traceback
import decimal
import logging
import semver
import math
import os
import sys
import time
import sys
import os
from xml.dom.minidom import parse
sys.path.append('./bin/libs/')
@ -31,14 +33,14 @@ file configs
def report_file_error(filetype: str, error_type, filename: str, stack: any):
if error_type == FileNotFoundError:
log = 'no config %s file \n file name: %s \n file type: %s \n stack: %s' % (filetype, filename, filetype, stack)
elif error_type == KeyError:
error = traceback.format_exc()
if isinstance(error_type, FileNotFoundError):
log = 'no {} file was found!: \n file name: {} \n file type: {} \n stack: {} \n traceback: {}'.format(filetype, filename, filetype, stack, error)
elif isinstance(error_type, KeyError):
log = 'no stack in %s file: %s \n file type: %s \n stack: %s' % (filetype, filename, filetype, stack)
else:
log = 'some error has been found! \n error type: %s \n file name: %s \n file type: %s \n stack: %s' % (error_type, filename, filetype, stack)
tools_logger.exception(log)
raise error_type(log)
def config(file_name: str, stack=None):
@ -73,12 +75,23 @@ def config(file_name: str, stack=None):
if stack:
rd = rd[stack]
return rd
except FileNotFoundError:
log = 'no {} file was found!: \n file name: {} \n file type: {} \n stack: {}'.format(f_type, file_name, f_type, stack)
tools_logger.error(log)
raise
except KeyError:
log = 'no stack in {} file {} was found! \n file type: {} \n file name: {} \n stack: {}'.format(f_type, file_name, f_type, file_name, stack)
tools_logger.error(log)
raise
except Exception as exp:
report_file_error(f_type, exp, file_name, stack)
log = 'some error has been found!\n error type: {} \n file name: {} \n file type: {} \n stack: {}'.format(type(exp), file_name, f_type, stack)
tools_logger.error(log)
raise
# main config
main_config_file = config('./configs/main.config')
Version = semver.VersionInfo.parse(str(main_config_file['runtime']['version']))
def get_At(name, in_xml, need_type=str):
@ -117,17 +130,21 @@ def default_name_handler(name_: str) -> str:
name = name.replace('{dir}', str(os.getcwd()))
name = name.replace('{py_v}', str(sys.version.split(' ')[0]))
name = name.replace('{version}', str(main_config_file['runtime']['version']))
name = name.replace('{write_v}', str(main_config_file['runtime']['write_py_v']))
return name
def name_handler(name: str, formats=None) -> str:
def name_handler(name: str, formats: dict = None) -> str:
if formats is None:
return default_name_handler(name)
name = default_name_handler(name)
for need_replace in formats:
replace = formats[need_replace]
if need_replace == '{date}':
replace = time.strftime(formats['{date}'], time.gmtime(time.time()))
if '{date}' in formats:
replace = time.strftime(formats['{date}'], time.gmtime(time.time()))
else:
replace = time.strftime(main_config_file['runtime']['date_fmt'], time.gmtime(time.time()))
name = name.replace(need_replace, replace)
return name

View File

@ -2,7 +2,7 @@
'main': {
'version.now_on': 'Difficult Rocket is running on Python Vision',
'version.need3+': 'Difficult Rocket need Python vision 3.0+',
'': '',
'version.best3.8+': 'Difficult Rocket is writen in Python {write_py_v}, and now is running on Python {py_v} may cause BUG',
'lang.language': 'English (EN-US)',
'logger.language': 'Logging language is: ',
'logger.created': 'Log handler created',
@ -15,14 +15,15 @@
},
'client': {
'setup.done': 'Client load complete ',
'os.pid_is1': 'Client is using PID',
'os.pid_is2': ' To Run',
'os.pid_is': 'Client is using PID',
'button.been_press': 'The button is pressed, the current state of the button is:',
'mouse.press_at': 'mouse was click at {} button is: {}',
'mouse.right': 'right button',
'mouse.left': 'left button'
},
'server': {
'setup.done': 'server load complete ',
'os.pid_is1': 'server is using PID',
'os.pid_is2': 'To Run'
'os.pid_is': 'Client is using PID: {} PPID: {}'
}
}
/*

View File

@ -2,12 +2,12 @@
'main': {
'version.now_on': '困难火箭的运行Python环境为',
'version.need3+': '困难火箭需要Python3.0+的环境',
'version.best3.8+': '困难火箭是在Python3.8的环境下编写的目前正在运行在Python{py_v}的环境下可能产生BUG',
'version.best3.8+': '困难火箭是在Python{write_py_v}的环境下编写的目前正在运行在Python{py_v}的环境下可能产生BUG',
'lang.language': '简 体 中 文 zh-cn',
'logger.language': '日志记录语言为:',
'logger.created': '日志处理器创建完成',
'logger.main_done': '主日志处理器创建完成',
'logger.logfile_name': '日志文件文件名:',
'logger.logfile_name': '日志文件文件名:',
'logger.logfile_level': '日志文件记录级别:',
'logger.logfile_fmt': '日志文件记录格式:',
'logger.logfile_datefmt': '日志文件日期格式:',
@ -15,13 +15,14 @@
},
'client': {
'setup.done': '客户端载入完成',
'os.pid_is1': '客户端正在以pid',
'os.pid_is2': '运行',
'button.been_press': '按钮被按下,目前状态为:'
'os.pid_is': '客户端 PID: {} PPID: {}',
'button.been_press': '按钮被按下,目前状态为:',
'mouse.press_at': '鼠标在 {} 被按下,按键为:{}',
'mouse.right': '右键',
'mouse.left': '左键'
},
'server': {
'setup.done': '服务端载入完成',
'os.pid_is1': '服务端正在以pid',
'os.pid_is2': '运行'
'os.pid_is': '服务端 PID: {} PPID: {}'
}
}

View File

@ -1,12 +1,20 @@
[runtime]
version = 0.4.4
fps = 60
version = 0.4.5
language = zh-CN
date_fmt = %%Y-%%m-%%d %%H-%%M-%%S
write_py_v = 3.8.10
[window]
style = None
width = 1024
height = 768
visible = true
caption = Difficult Rocket {version}
resizable = true
full_screen = false
caption = Difficult Rocket {version}
[window_default]
width = 1024
height = 768

View File

@ -1,2 +0,0 @@
{
}

View File

@ -1,24 +0,0 @@
{
'textures': {
'toolbar_dark': 'Editor/ToolbarDark.png',
'toolbar_light': 'Editor/ToolbarLight.png',
'trash_can': 'Editor/TrashCan.png'
},
'button': {
'rotate': 'Editor/RotateButton.png',
'add_part': 'Editor/ToolbarIconAddPart.png',
'to_menu': 'Editor/ToolbarIconMenu.png',
// 'part_button': 'Editor/PartButton.png',
'stage': 'Editor/ToolbarIconStaging.png',
'zoom': 'Editor/ToolbarIconZoom.png',
'zoom_in': 'Editor/ToolbarIconZoomIn.png',
'zoom_out': 'Editor/ToolbarIconZoomOut.png',
'play': 'Editor/ToolbarIconPlay.png',
'logic': {
'zoom': {
'zoom_in': 'child',
'zoom_out': 'child'
}
},
}
}

View File

@ -1,22 +0,0 @@
{
'width': '1024',
'height': '768',
'full_screen': 'false',
// bool
'caption': 'Simple Rocket {version}',
// {version} -> version of SR
'caption_option': {
'version': '0.4.4'
},
'resizable': 'true',
// bool
'visible': 'true',
// bool
'style': 'None',
'fps': 60,
'views': [
'space',
'map',
'menu'
]
}

23
configs/textures.json5 Normal file
View File

@ -0,0 +1,23 @@
{
'Editor': {
'runtime': {
'toolbar.dark': 'Editor/ToolbarDark.png',
'toolbar.light': 'Editor/ToolbarLight.png',
'button_side.dark': 'Editor/ButtonDarkSide.png',
'button_side.light': 'Editor/ButtonLightSide.png'
},
'toggle_button': {
'stage': 'Editor/ToolbarIconStaging.png',
'add_part': 'Editor/ToolbarIconAddPart.png',
'menu': 'Editor/ToolbarIconMenu.png'
},
'push_button': {
'zoom': 'Editor/ToolbarIconZoom.png',
'zoom.in': 'Editor/ToolbarIconZoomIn.png',
'zoom.out': 'Editor/ToolbarIconZoomOut.png',
'play': 'Editor/ToolbarIconPlay.png',
'rotate': 'Editor/RotateButton.png',
'trash_can': 'Editor/TrashCan.png'
}
}
}

View File

@ -1,32 +1,29 @@
# Difficult-Rocket
# [Difficult-Rocket(github)](https://github.com/shenjackyuanjie/Difficult-Rocket)
# [Difficult-Rocket(gitee)](https://gitee.com/shenjackyuanjie/Difficult-Rocket)
## 版本
0.4.3 (开发DEMO.ing)
- (开发DEMO.ing)(coming s∞n)
- 0.4.6 预览版
- 0.5.0 开发中.ing
[更新日志](https://github.com/shenjackyuanjie/Difficult-Rocket/blob/main/docs/update_logs.md)
[计划特性列表](https://github.com/shenjackyuanjie/Difficult-Rocket/tree/main/docs/plan_features)
For an English version of readme, please move [here](https://github.com/shenjackyuanjie/Difficult-Rocket).
### For an English version of readme, please move [here](https://github.com/shenjackyuanjie/Difficult-Rocket).
> 这是一个用Python制作的类Simple Rocket游戏
## 优势
没有。一个也没有
> 相对于原版SR比较“轻量化”
(因为还没写完呢)
## [计划特性列表](./plan_features)
## 计划特性
- 都在 [](https://github.com/shenjackyuanjie/Difficult-Rocket/projects)
## [更新日志](./update_logs.md)
## 环境需求 (测试过的 / 开发平台)
-
- `开发平台1 Windows 10`
- `Python 3.8.7`
- `Python 3.8.10`
- `Windows10 x64`
- `Pyglet 1.5.16`
- `Json5 0.9.5`
@ -43,9 +40,10 @@ For an English version of readme, please move [here](https://github.com/shenjack
## 需要的Python模块
- json5 (已经内置V0.9.5 路径:`./bin/libs/json5`)
- pyglet (已经内置V1.5.16 路径:`./bin/libs/pyglet`)
- json5 (已经内置V0.9.6 路径:`./bin/libs/json5`)
- pyglet (已经内置V1.5.18 路径:`./bin/libs/pyglet`)
- pillow
- semver
## 感谢
@ -54,6 +52,8 @@ For an English version of readme, please move [here](https://github.com/shenjack
- [@Rayawa](https://github.com/Rayawa) : 文档矫正
- [@Billchyi](https://github.com/Billchyi) : 文档矫正
## 相关链接
## 关于分享协议
#### https://creativecommons.org/licenses/by-nc-sa/4.0/

View File

@ -6,6 +6,54 @@
- [中文README](README-cn.md)
- Using [SemVer 2.0.0](https://semver.org/) to manage version
## 202108 V 0.5.0
making
## 20210811 V 0.4.6
### DEBUG
- game window doesn't use input `(*args, **kwargs)` to setup
### Change
- language type `zh-cn` -> `zh-CN`
- game window config now use `configs/main.config` to config
- `on_mouse_click` function's debug message now use `self.lang` to debug message (witch is multi-language)
- server client and main now use `logging.getLogger()`
### Add
- `[window_default]` in `configs/main.config`
- server and client now will output `PID` and `PPID` info as `INFO`
- `client` now change to `ClientWindow`
### Delete
- all game window render has been deleted
- - will be rewritten in 0.5.0
- delete some useless code
- delete some useless file
## 20210723 V 0.4.5
### DEBUG
- `new_thread.py` link of stackoverflow have an extra 'find' in the middle REMOVED
### Add
- `new_thread.py` now can use @new_thread to get a threaded fun
### Change
- `README.md` and `README-cn.md` change URL to file path
- `README.md` and `README-cn.md` some label style change
- Pre-installed `pyglet` upgrade from `1.5.16` -> `1.5.18`
- Pre-installed `json5` upgrade from `0.9.5` -> `0.9.6`
## 20210708 V 0.4.4
### PS

View File

@ -1,3 +1,5 @@
-i https://pypi.tuna.tsinghua.edu.cn/simple
semver
pyglet
json5
pillow
pillow
json5

39
tests/test_process.py Normal file
View File

@ -0,0 +1,39 @@
"""
writen by shenjackyuanjie
mail: 3695888@qq.com
github: @shenjackyuanjie
"""
from multiprocessing import Process
import os
def testsss(a):
print(os.getpid())
print(os.getppid())
if __name__ == '__main__':
print(os.getpid())
print(os.getppid())
pro = Process(target=testsss, args=('a',))
pro.start()
pro.join()
def info(title):
print(title)
print('module name:', __name__)
print('parent process:', os.getppid())
print('process id:', os.getpid())
def f(name):
info('function f')
print('hello', name)
if __name__ == '__main__':
info('main line')
p = Process(target=f, args=('bob',))
p.start()
p.join()