PageRenderTime 47ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/engine/src/main/java/org/terasology/engine/subsystem/config/BindsSubsystem.java

http://github.com/MovingBlocks/Terasology
Java | 480 lines | 394 code | 60 blank | 26 comment | 37 complexity | 7a48e294ca6748351afa912b967c26df MD5 | raw file
Possible License(s): Apache-2.0
  1. /*
  2. * Copyright 2018 MovingBlocks
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.terasology.engine.subsystem.config;
  17. import com.google.common.collect.Lists;
  18. import com.google.common.collect.Maps;
  19. import org.slf4j.Logger;
  20. import org.slf4j.LoggerFactory;
  21. import org.terasology.config.BindsConfig;
  22. import org.terasology.config.Config;
  23. import org.terasology.config.facade.BindsConfiguration;
  24. import org.terasology.context.Context;
  25. import org.terasology.engine.SimpleUri;
  26. import org.terasology.engine.module.ModuleManager;
  27. import org.terasology.engine.subsystem.EngineSubsystem;
  28. import org.terasology.input.BindAxisEvent;
  29. import org.terasology.input.BindButtonEvent;
  30. import org.terasology.input.BindableAxis;
  31. import org.terasology.input.BindableButton;
  32. import org.terasology.input.ControllerInput;
  33. import org.terasology.input.DefaultBinding;
  34. import org.terasology.input.Input;
  35. import org.terasology.input.InputType;
  36. import org.terasology.input.MouseInput;
  37. import org.terasology.input.RegisterBindAxis;
  38. import org.terasology.input.RegisterBindButton;
  39. import org.terasology.input.RegisterRealBindAxis;
  40. import org.terasology.input.events.AxisEvent;
  41. import org.terasology.input.events.ButtonEvent;
  42. import org.terasology.input.internal.AbstractBindableAxis;
  43. import org.terasology.input.internal.BindableAxisImpl;
  44. import org.terasology.input.internal.BindableButtonImpl;
  45. import org.terasology.input.internal.BindableRealAxis;
  46. import org.terasology.module.DependencyResolver;
  47. import org.terasology.module.ModuleEnvironment;
  48. import org.terasology.module.ResolutionResult;
  49. import org.terasology.module.predicates.FromModule;
  50. import org.terasology.naming.Name;
  51. import java.lang.annotation.Annotation;
  52. import java.util.Collection;
  53. import java.util.List;
  54. import java.util.Map;
  55. import static java.util.Collections.unmodifiableCollection;
  56. public class BindsSubsystem implements EngineSubsystem, BindsManager {
  57. private static final Logger logger = LoggerFactory.getLogger(BindsSubsystem.class);
  58. private BindsConfiguration bindsConfiguration;
  59. private BindsConfiguration defaultBindsConfig = new BindsConfigAdapter(new BindsConfig());
  60. private Map<SimpleUri, BindableButton> buttonLookup = Maps.newHashMap();
  61. private List<BindableButton> buttonBinds = Lists.newArrayList();
  62. private Context context;
  63. private Map<String, BindableRealAxis> axisLookup = Maps.newHashMap();
  64. private List<AbstractBindableAxis> axisBinds = Lists.newArrayList();
  65. // Links between primitive inputs and bind buttons
  66. private Map<Integer, BindableButton> keyBinds = Maps.newHashMap();
  67. private Map<MouseInput, BindableButton> mouseButtonBinds = Maps.newHashMap();
  68. private Map<ControllerInput, BindableButton> controllerBinds = Maps.newHashMap();
  69. private Map<Input, BindableRealAxis> controllerAxisBinds = Maps.newHashMap();
  70. private BindableButton mouseWheelUpBind;
  71. private BindableButton mouseWheelDownBind;
  72. @Override
  73. public BindableButton getMouseWheelUpBind() {
  74. return mouseWheelUpBind;
  75. }
  76. @Override
  77. public BindableButton getMouseWheelDownBind() {
  78. return mouseWheelDownBind;
  79. }
  80. @Override
  81. public Map<MouseInput, BindableButton> getMouseButtonBinds() {
  82. return mouseButtonBinds;
  83. }
  84. @Override
  85. public List<AbstractBindableAxis> getAxisBinds() {
  86. return axisBinds;
  87. }
  88. @Override
  89. public Map<Input, BindableRealAxis> getControllerAxisBinds() {
  90. return controllerAxisBinds;
  91. }
  92. @Override
  93. public Map<ControllerInput, BindableButton> getControllerBinds() {
  94. return controllerBinds;
  95. }
  96. @Override
  97. public Map<Integer, BindableButton> getKeyBinds() {
  98. return keyBinds;
  99. }
  100. @Override
  101. public String getName() {
  102. return "Binds";
  103. }
  104. @Override
  105. public List<BindableButton> getButtonBinds() {
  106. return buttonBinds;
  107. }
  108. @Override
  109. public void preInitialise(Context passedContext) {
  110. context = passedContext;
  111. bindsConfiguration = passedContext.get(BindsConfiguration.class);
  112. passedContext.put(BindsManager.class, this);
  113. }
  114. @Override
  115. public BindsConfig getDefaultBindsConfig() {
  116. BindsConfig copy = new BindsConfig();
  117. //SimpleUri and Input are immutable, no need for a deep copy
  118. copy.setBinds(defaultBindsConfig.getBindsConfig());
  119. return copy;
  120. }
  121. @Override
  122. public void updateConfigWithDefaultBinds() {
  123. //default bindings are overridden
  124. defaultBindsConfig = new BindsConfigAdapter(new BindsConfig());
  125. updateDefaultBinds(context, defaultBindsConfig);
  126. //actual bindings may be actualized
  127. updateDefaultBinds(context, bindsConfiguration);
  128. }
  129. private void updateDefaultBinds(Context passedContext, BindsConfiguration config) {
  130. ModuleManager moduleManager = passedContext.get(ModuleManager.class);
  131. DependencyResolver resolver = new DependencyResolver(moduleManager.getRegistry());
  132. for (Name moduleId : moduleManager.getRegistry().getModuleIds()) {
  133. if (moduleManager.getRegistry().getLatestModuleVersion(moduleId).isCodeModule()) {
  134. ResolutionResult result = resolver.resolve(moduleId);
  135. if (result.isSuccess()) {
  136. try (ModuleEnvironment environment = moduleManager.loadEnvironment(result.getModules(), false)) {
  137. FromModule filter = new FromModule(environment, moduleId);
  138. Iterable<Class<?>> buttons = environment.getTypesAnnotatedWith(RegisterBindButton.class, filter);
  139. Iterable<Class<?>> axes = environment.getTypesAnnotatedWith(RegisterRealBindAxis.class, filter);
  140. addButtonDefaultsFor(moduleId, buttons, config);
  141. addAxisDefaultsFor(moduleId, axes, config);
  142. }
  143. }
  144. }
  145. }
  146. }
  147. private void addButtonDefaultsFor(Name moduleId, Iterable<Class<?>> classes, BindsConfiguration config) {
  148. for (Class<?> buttonEvent : classes) {
  149. if (ButtonEvent.class.isAssignableFrom(buttonEvent)) {
  150. RegisterBindButton info = buttonEvent.getAnnotation(RegisterBindButton.class);
  151. SimpleUri bindUri = new SimpleUri(moduleId, info.id());
  152. if (!config.hasBinds(bindUri)) {
  153. addDefaultBindings(bindUri, buttonEvent, config);
  154. }
  155. }
  156. }
  157. }
  158. private void addAxisDefaultsFor(Name moduleId, Iterable<Class<?>> classes, BindsConfiguration config) {
  159. for (Class<?> axisEvent : classes) {
  160. if (AxisEvent.class.isAssignableFrom(axisEvent)) {
  161. RegisterRealBindAxis info = axisEvent.getAnnotation(RegisterRealBindAxis.class);
  162. SimpleUri bindUri = new SimpleUri(moduleId, info.id());
  163. if (!config.hasBinds(bindUri)) {
  164. addDefaultBindings(bindUri, axisEvent, config);
  165. }
  166. }
  167. }
  168. }
  169. private void addDefaultBindings(SimpleUri bindUri, Class<?> event, BindsConfiguration config) {
  170. List<Input> defaultInputs = fetchDefaultBindings(event, config);
  171. config.setBinds(bindUri, defaultInputs);
  172. }
  173. private List<Input> fetchDefaultBindings(Class<?> event, BindsConfiguration config) {
  174. List<Input> defaultInputs = Lists.newArrayList();
  175. Collection<Input> boundInputs = config.getBoundInputs();
  176. for (Annotation annotation : event.getAnnotationsByType(DefaultBinding.class)) {
  177. DefaultBinding defaultBinding = (DefaultBinding) annotation;
  178. Input defaultInput = defaultBinding.type().getInput(defaultBinding.id());
  179. if (!boundInputs.contains(defaultInput)) {
  180. defaultInputs.add(defaultInput);
  181. } else {
  182. logger.warn("Input {} is already registered, can not use it as default for event {}", defaultInput, event);
  183. }
  184. }
  185. return defaultInputs;
  186. }
  187. @Override
  188. public void registerBinds() {
  189. ModuleManager moduleManager = context.get(ModuleManager.class);
  190. ModuleEnvironment environment = moduleManager.getEnvironment();
  191. clearBinds();
  192. registerButtonBinds(environment);
  193. registerAxisBinds(environment);
  194. registerRealAxisBinds(environment);
  195. }
  196. private void clearBinds() {
  197. buttonLookup.clear();
  198. buttonBinds.clear();
  199. axisLookup.clear();
  200. axisBinds.clear();
  201. keyBinds.clear();
  202. controllerBinds.clear();
  203. controllerAxisBinds.clear();
  204. mouseButtonBinds.clear();
  205. mouseWheelUpBind = null;
  206. mouseWheelDownBind = null;
  207. }
  208. private void registerButtonBinds(ModuleEnvironment environment) {
  209. Iterable<Class<?>> classes = environment.getTypesAnnotatedWith(RegisterBindButton.class);
  210. for (Class<?> registerBindClass : classes) {
  211. RegisterBindButton info = registerBindClass.getAnnotation(RegisterBindButton.class);
  212. SimpleUri bindUri = new SimpleUri(environment.getModuleProviding(registerBindClass), info.id());
  213. if (BindButtonEvent.class.isAssignableFrom(registerBindClass)) {
  214. try {
  215. BindableButton bindButton = registerBindButton(bindUri, info.description(), (BindButtonEvent) registerBindClass.newInstance());
  216. bindButton.setMode(info.mode());
  217. bindButton.setRepeating(info.repeating());
  218. bindsConfiguration.getBinds(bindUri).stream().filter(input -> input != null).forEach(input -> linkBindButtonToInput(input, bindUri));
  219. logger.debug("Registered button bind: {}", bindUri);
  220. } catch (InstantiationException | IllegalAccessException e) {
  221. logger.error("Failed to register button bind \"{}\"", e);
  222. }
  223. } else {
  224. logger.error("Failed to register button bind \"{}\", does not extend BindButtonEvent", bindUri);
  225. }
  226. }
  227. }
  228. private void registerAxisBinds(ModuleEnvironment environment) {
  229. Iterable<Class<?>> classes = environment.getTypesAnnotatedWith(RegisterBindAxis.class);
  230. for (Class<?> registerBindClass : classes) {
  231. RegisterBindAxis info = registerBindClass.getAnnotation(RegisterBindAxis.class);
  232. Name moduleId = environment.getModuleProviding(registerBindClass);
  233. SimpleUri id = new SimpleUri(moduleId, info.id());
  234. if (BindAxisEvent.class.isAssignableFrom(registerBindClass)) {
  235. BindableButton positiveButton = getBindButton(new SimpleUri(info.positiveButton()));
  236. BindableButton negativeButton = getBindButton(new SimpleUri(info.negativeButton()));
  237. if (positiveButton == null) {
  238. logger.warn("Failed to register axis \"{}\", missing positive button \"{}\"", id, info.positiveButton());
  239. continue;
  240. }
  241. if (negativeButton == null) {
  242. logger.warn("Failed to register axis \"{}\", missing negative button \"{}\"", id, info.negativeButton());
  243. continue;
  244. }
  245. try {
  246. BindableAxis bindAxis = registerBindAxis(id.toString(), (BindAxisEvent) registerBindClass.newInstance(), positiveButton, negativeButton);
  247. bindAxis.setSendEventMode(info.eventMode());
  248. logger.debug("Registered axis bind: {}", id);
  249. } catch (InstantiationException | IllegalAccessException e) {
  250. logger.error("Failed to register axis bind \"{}\"", id, e);
  251. }
  252. } else {
  253. logger.error("Failed to register axis bind \"{}\", does not extend BindAxisEvent", id);
  254. }
  255. }
  256. }
  257. private void registerRealAxisBinds(ModuleEnvironment environment) {
  258. Iterable<Class<?>> classes = environment.getTypesAnnotatedWith(RegisterRealBindAxis.class);
  259. for (Class<?> registerBindClass : classes) {
  260. RegisterRealBindAxis info = registerBindClass.getAnnotation(RegisterRealBindAxis.class);
  261. Name moduleId = environment.getModuleProviding(registerBindClass);
  262. SimpleUri id = new SimpleUri(moduleId, info.id());
  263. if (BindAxisEvent.class.isAssignableFrom(registerBindClass)) {
  264. try {
  265. BindAxisEvent instance = (BindAxisEvent) registerBindClass.newInstance();
  266. BindableAxis bindAxis = registerRealBindAxis(id.toString(), instance);
  267. bindAxis.setSendEventMode(info.eventMode());
  268. for (Input input : bindsConfiguration.getBinds(id)) {
  269. linkAxisToInput(input, id);
  270. }
  271. logger.debug("Registered axis bind: {}", id);
  272. } catch (InstantiationException | IllegalAccessException e) {
  273. logger.error("Failed to register axis bind \"{}\"", id, e);
  274. }
  275. } else {
  276. logger.error("Failed to register axis bind \"{}\", does not extend BindAxisEvent", id);
  277. }
  278. }
  279. }
  280. private void linkAxisToInput(Input input, SimpleUri bindId) {
  281. BindableRealAxis bindInfo = axisLookup.get(bindId.toString());
  282. controllerAxisBinds.put(input, bindInfo);
  283. }
  284. @Override
  285. public void linkBindButtonToKey(int key, SimpleUri bindId) {
  286. BindableButton bindInfo = buttonLookup.get(bindId);
  287. keyBinds.put(key, bindInfo);
  288. }
  289. private void linkBindButtonToMouse(MouseInput mouseButton, SimpleUri bindId) {
  290. BindableButton bindInfo = buttonLookup.get(bindId);
  291. mouseButtonBinds.put(mouseButton, bindInfo);
  292. }
  293. private void linkBindButtonToMouseWheel(int direction, SimpleUri bindId) {
  294. if (direction > 0) {
  295. mouseWheelDownBind = buttonLookup.get(bindId);
  296. } else if (direction < 0) {
  297. mouseWheelUpBind = buttonLookup.get(bindId);
  298. }
  299. }
  300. private void linkBindButtonToController(ControllerInput button, SimpleUri bindId) {
  301. BindableButton bindInfo = buttonLookup.get(bindId);
  302. controllerBinds.put(button, bindInfo);
  303. }
  304. private void linkBindButtonToInput(Input input, SimpleUri bindId) {
  305. switch (input.getType()) {
  306. case KEY:
  307. linkBindButtonToKey(input.getId(), bindId);
  308. break;
  309. case MOUSE_BUTTON:
  310. MouseInput button = MouseInput.find(input.getType(), input.getId());
  311. linkBindButtonToMouse(button, bindId);
  312. break;
  313. case MOUSE_WHEEL:
  314. linkBindButtonToMouseWheel(input.getId(), bindId);
  315. break;
  316. case CONTROLLER_BUTTON:
  317. linkBindButtonToController((ControllerInput) input, bindId);
  318. break;
  319. default:
  320. break;
  321. }
  322. }
  323. private BindableButton getBindButton(SimpleUri bindId) {
  324. return buttonLookup.get(bindId);
  325. }
  326. private BindableAxis registerBindAxis(String id, BindAxisEvent event, BindableButton positiveButton, BindableButton negativeButton) {
  327. BindableAxisImpl axis = new BindableAxisImpl(id, event, positiveButton, negativeButton);
  328. axisBinds.add(axis);
  329. return axis;
  330. }
  331. private BindableAxis registerRealBindAxis(String id, BindAxisEvent event) {
  332. BindableRealAxis axis = new BindableRealAxis(id.toString(), event);
  333. axisBinds.add(axis);
  334. axisLookup.put(id, axis);
  335. return axis;
  336. }
  337. private BindableButton registerBindButton(SimpleUri bindId, String displayName, BindButtonEvent event) {
  338. BindableButtonImpl bind = new BindableButtonImpl(bindId, displayName, event);
  339. buttonLookup.put(bindId, bind);
  340. buttonBinds.add(bind);
  341. return bind;
  342. }
  343. @Override
  344. public BindsConfig getBindsConfig() {
  345. return bindsConfiguration.getBindsConfig();
  346. }
  347. @Override
  348. public void shutdown() {
  349. saveBindsConfig();
  350. }
  351. @Override
  352. public void saveBindsConfig() {
  353. //TODO replace with flexible config when binds are no longer saved in the Config.
  354. context.get(Config.class).save();
  355. }
  356. /**
  357. * Enumerates all active input bindings for a given binding.
  358. * TODO: Restored for API reasons, may be duplicating code elsewhere. Should be reviewed
  359. * @param bindId the ID
  360. * @return a list of keyboard/mouse inputs that trigger the binding.
  361. */
  362. public List<Input> getInputsForBindButton(SimpleUri bindId) {
  363. List<Input> inputs = Lists.newArrayList();
  364. for (Map.Entry<Integer, BindableButton> entry : keyBinds.entrySet()) {
  365. if (entry.getValue().getId().equals(bindId)) {
  366. inputs.add(InputType.KEY.getInput(entry.getKey()));
  367. }
  368. }
  369. for (Map.Entry<MouseInput, BindableButton> entry : mouseButtonBinds.entrySet()) {
  370. if (entry.getValue().getId().equals(bindId)) {
  371. inputs.add(entry.getKey());
  372. }
  373. }
  374. if (mouseWheelUpBind.getId().equals(bindId)) {
  375. inputs.add(MouseInput.WHEEL_UP);
  376. }
  377. if (mouseWheelDownBind.getId().equals(bindId)) {
  378. inputs.add(MouseInput.WHEEL_DOWN);
  379. }
  380. return inputs;
  381. }
  382. protected static class BindsConfigAdapter implements BindsConfiguration {
  383. private BindsConfig bindsConfig;
  384. public BindsConfigAdapter(BindsConfig bindsConfig) {
  385. this.bindsConfig = bindsConfig;
  386. }
  387. @Override
  388. public boolean isBound(Input newInput) {
  389. return bindsConfig.isBound(newInput);
  390. }
  391. @Override
  392. public void setBinds(BindsConfig other) {
  393. bindsConfig.setBinds(other);
  394. }
  395. @Override
  396. public List<Input> getBinds(SimpleUri uri) {
  397. return bindsConfig.getBinds(uri);
  398. }
  399. @Override
  400. public boolean hasBinds(SimpleUri uri) {
  401. return bindsConfig.hasBinds(uri);
  402. }
  403. @Override
  404. public void setBinds(SimpleUri bindUri, Input... inputs) {
  405. bindsConfig.setBinds(bindUri, inputs);
  406. }
  407. @Override
  408. public void setBinds(SimpleUri bindUri, Iterable<Input> inputs) {
  409. bindsConfig.setBinds(bindUri, inputs);
  410. }
  411. @Override
  412. public BindsConfig getBindsConfig() {
  413. return bindsConfig;
  414. }
  415. @Override
  416. public Collection<Input> getBoundInputs() {
  417. return unmodifiableCollection(bindsConfig.getBoundInputs());
  418. }
  419. }
  420. }