game.cpp 116 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201
  1. /*
  2. Minetest
  3. Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "game.h"
  17. #include "irrlichttypes_extrabloated.h"
  18. #include <IGUICheckBox.h>
  19. #include <IGUIEditBox.h>
  20. #include <IGUIButton.h>
  21. #include <IGUIStaticText.h>
  22. #include <IGUIFont.h>
  23. #include <IMaterialRendererServices.h>
  24. #include "IMeshCache.h"
  25. #include "client.h"
  26. #include "server.h"
  27. #include "guiPasswordChange.h"
  28. #include "guiVolumeChange.h"
  29. #include "guiKeyChangeMenu.h"
  30. #include "guiFormSpecMenu.h"
  31. #include "tool.h"
  32. #include "guiChatConsole.h"
  33. #include "config.h"
  34. #include "version.h"
  35. #include "clouds.h"
  36. #include "particles.h"
  37. #include "camera.h"
  38. #include "mapblock.h"
  39. #include "settings.h"
  40. #include "profiler.h"
  41. #include "mainmenumanager.h"
  42. #include "gettext.h"
  43. #include "log.h"
  44. #include "filesys.h"
  45. // Needed for determining pointing to nodes
  46. #include "nodedef.h"
  47. #include "nodemetadata.h"
  48. #include "main.h" // For g_settings
  49. #include "itemdef.h"
  50. #include "tile.h" // For TextureSource
  51. #include "shader.h" // For ShaderSource
  52. #include "logoutputbuffer.h"
  53. #include "subgame.h"
  54. #include "quicktune_shortcutter.h"
  55. #include "clientmap.h"
  56. #include "hud.h"
  57. #include "sky.h"
  58. #include "sound.h"
  59. #if USE_SOUND
  60. #include "sound_openal.h"
  61. #endif
  62. #include "event_manager.h"
  63. #include <iomanip>
  64. #include <list>
  65. #include "util/directiontables.h"
  66. #include "util/pointedthing.h"
  67. #include "drawscene.h"
  68. #include "content_cao.h"
  69. #include "fontengine.h"
  70. #ifdef HAVE_TOUCHSCREENGUI
  71. #include "touchscreengui.h"
  72. #endif
  73. /*
  74. Text input system
  75. */
  76. struct TextDestNodeMetadata : public TextDest {
  77. TextDestNodeMetadata(v3s16 p, Client *client)
  78. {
  79. m_p = p;
  80. m_client = client;
  81. }
  82. // This is deprecated I guess? -celeron55
  83. void gotText(std::wstring text)
  84. {
  85. std::string ntext = wide_to_narrow(text);
  86. infostream << "Submitting 'text' field of node at (" << m_p.X << ","
  87. << m_p.Y << "," << m_p.Z << "): " << ntext << std::endl;
  88. std::map<std::string, std::string> fields;
  89. fields["text"] = ntext;
  90. m_client->sendNodemetaFields(m_p, "", fields);
  91. }
  92. void gotText(std::map<std::string, std::string> fields)
  93. {
  94. m_client->sendNodemetaFields(m_p, "", fields);
  95. }
  96. v3s16 m_p;
  97. Client *m_client;
  98. };
  99. struct TextDestPlayerInventory : public TextDest {
  100. TextDestPlayerInventory(Client *client)
  101. {
  102. m_client = client;
  103. m_formname = "";
  104. }
  105. TextDestPlayerInventory(Client *client, std::string formname)
  106. {
  107. m_client = client;
  108. m_formname = formname;
  109. }
  110. void gotText(std::map<std::string, std::string> fields)
  111. {
  112. m_client->sendInventoryFields(m_formname, fields);
  113. }
  114. Client *m_client;
  115. };
  116. struct LocalFormspecHandler : public TextDest {
  117. LocalFormspecHandler();
  118. LocalFormspecHandler(std::string formname) :
  119. m_client(0)
  120. {
  121. m_formname = formname;
  122. }
  123. LocalFormspecHandler(std::string formname, Client *client) :
  124. m_client(client)
  125. {
  126. m_formname = formname;
  127. }
  128. void gotText(std::wstring message)
  129. {
  130. errorstream << "LocalFormspecHandler::gotText old style message received" << std::endl;
  131. }
  132. void gotText(std::map<std::string, std::string> fields)
  133. {
  134. if (m_formname == "MT_PAUSE_MENU") {
  135. if (fields.find("btn_sound") != fields.end()) {
  136. g_gamecallback->changeVolume();
  137. return;
  138. }
  139. if (fields.find("btn_key_config") != fields.end()) {
  140. g_gamecallback->keyConfig();
  141. return;
  142. }
  143. if (fields.find("btn_exit_menu") != fields.end()) {
  144. g_gamecallback->disconnect();
  145. return;
  146. }
  147. if (fields.find("btn_exit_os") != fields.end()) {
  148. g_gamecallback->exitToOS();
  149. return;
  150. }
  151. if (fields.find("btn_change_password") != fields.end()) {
  152. g_gamecallback->changePassword();
  153. return;
  154. }
  155. if (fields.find("quit") != fields.end()) {
  156. return;
  157. }
  158. if (fields.find("btn_continue") != fields.end()) {
  159. return;
  160. }
  161. }
  162. if (m_formname == "MT_CHAT_MENU") {
  163. assert(m_client != 0);
  164. if ((fields.find("btn_send") != fields.end()) ||
  165. (fields.find("quit") != fields.end())) {
  166. if (fields.find("f_text") != fields.end()) {
  167. m_client->typeChatMessage(narrow_to_wide(fields["f_text"]));
  168. }
  169. return;
  170. }
  171. }
  172. if (m_formname == "MT_DEATH_SCREEN") {
  173. assert(m_client != 0);
  174. if ((fields.find("btn_respawn") != fields.end())) {
  175. m_client->sendRespawn();
  176. return;
  177. }
  178. if (fields.find("quit") != fields.end()) {
  179. m_client->sendRespawn();
  180. return;
  181. }
  182. }
  183. // don't show error message for unhandled cursor keys
  184. if ((fields.find("key_up") != fields.end()) ||
  185. (fields.find("key_down") != fields.end()) ||
  186. (fields.find("key_left") != fields.end()) ||
  187. (fields.find("key_right") != fields.end())) {
  188. return;
  189. }
  190. errorstream << "LocalFormspecHandler::gotText unhandled >" << m_formname << "< event" << std::endl;
  191. int i = 0;
  192. for (std::map<std::string, std::string>::iterator iter = fields.begin();
  193. iter != fields.end(); iter++) {
  194. errorstream << "\t" << i << ": " << iter->first << "=" << iter->second << std::endl;
  195. i++;
  196. }
  197. }
  198. Client *m_client;
  199. };
  200. /* Form update callback */
  201. class NodeMetadataFormSource: public IFormSource
  202. {
  203. public:
  204. NodeMetadataFormSource(ClientMap *map, v3s16 p):
  205. m_map(map),
  206. m_p(p)
  207. {
  208. }
  209. std::string getForm()
  210. {
  211. NodeMetadata *meta = m_map->getNodeMetadata(m_p);
  212. if (!meta)
  213. return "";
  214. return meta->getString("formspec");
  215. }
  216. std::string resolveText(std::string str)
  217. {
  218. NodeMetadata *meta = m_map->getNodeMetadata(m_p);
  219. if (!meta)
  220. return str;
  221. return meta->resolveString(str);
  222. }
  223. ClientMap *m_map;
  224. v3s16 m_p;
  225. };
  226. class PlayerInventoryFormSource: public IFormSource
  227. {
  228. public:
  229. PlayerInventoryFormSource(Client *client):
  230. m_client(client)
  231. {
  232. }
  233. std::string getForm()
  234. {
  235. LocalPlayer *player = m_client->getEnv().getLocalPlayer();
  236. return player->inventory_formspec;
  237. }
  238. Client *m_client;
  239. };
  240. /*
  241. Check if a node is pointable
  242. */
  243. inline bool isPointableNode(const MapNode &n,
  244. Client *client, bool liquids_pointable)
  245. {
  246. const ContentFeatures &features = client->getNodeDefManager()->get(n);
  247. return features.pointable ||
  248. (liquids_pointable && features.isLiquid());
  249. }
  250. /*
  251. Find what the player is pointing at
  252. */
  253. PointedThing getPointedThing(Client *client, v3f player_position,
  254. v3f camera_direction, v3f camera_position, core::line3d<f32> shootline,
  255. f32 d, bool liquids_pointable, bool look_for_object, v3s16 camera_offset,
  256. std::vector<aabb3f> &hilightboxes, ClientActiveObject *&selected_object)
  257. {
  258. PointedThing result;
  259. hilightboxes.clear();
  260. selected_object = NULL;
  261. INodeDefManager *nodedef = client->getNodeDefManager();
  262. ClientMap &map = client->getEnv().getClientMap();
  263. f32 mindistance = BS * 1001;
  264. // First try to find a pointed at active object
  265. if (look_for_object) {
  266. selected_object = client->getSelectedActiveObject(d * BS,
  267. camera_position, shootline);
  268. if (selected_object != NULL) {
  269. if (selected_object->doShowSelectionBox()) {
  270. aabb3f *selection_box = selected_object->getSelectionBox();
  271. // Box should exist because object was
  272. // returned in the first place
  273. assert(selection_box);
  274. v3f pos = selected_object->getPosition();
  275. hilightboxes.push_back(aabb3f(
  276. selection_box->MinEdge + pos - intToFloat(camera_offset, BS),
  277. selection_box->MaxEdge + pos - intToFloat(camera_offset, BS)));
  278. }
  279. mindistance = (selected_object->getPosition() - camera_position).getLength();
  280. result.type = POINTEDTHING_OBJECT;
  281. result.object_id = selected_object->getId();
  282. }
  283. }
  284. // That didn't work, try to find a pointed at node
  285. v3s16 pos_i = floatToInt(player_position, BS);
  286. /*infostream<<"pos_i=("<<pos_i.X<<","<<pos_i.Y<<","<<pos_i.Z<<")"
  287. <<std::endl;*/
  288. s16 a = d;
  289. s16 ystart = pos_i.Y + 0 - (camera_direction.Y < 0 ? a : 1);
  290. s16 zstart = pos_i.Z - (camera_direction.Z < 0 ? a : 1);
  291. s16 xstart = pos_i.X - (camera_direction.X < 0 ? a : 1);
  292. s16 yend = pos_i.Y + 1 + (camera_direction.Y > 0 ? a : 1);
  293. s16 zend = pos_i.Z + (camera_direction.Z > 0 ? a : 1);
  294. s16 xend = pos_i.X + (camera_direction.X > 0 ? a : 1);
  295. // Prevent signed number overflow
  296. if (yend == 32767)
  297. yend = 32766;
  298. if (zend == 32767)
  299. zend = 32766;
  300. if (xend == 32767)
  301. xend = 32766;
  302. for (s16 y = ystart; y <= yend; y++)
  303. for (s16 z = zstart; z <= zend; z++)
  304. for (s16 x = xstart; x <= xend; x++) {
  305. MapNode n;
  306. bool is_valid_position;
  307. n = map.getNodeNoEx(v3s16(x, y, z), &is_valid_position);
  308. if (!is_valid_position)
  309. continue;
  310. if (!isPointableNode(n, client, liquids_pointable))
  311. continue;
  312. std::vector<aabb3f> boxes = n.getSelectionBoxes(nodedef);
  313. v3s16 np(x, y, z);
  314. v3f npf = intToFloat(np, BS);
  315. for (std::vector<aabb3f>::const_iterator
  316. i = boxes.begin();
  317. i != boxes.end(); i++) {
  318. aabb3f box = *i;
  319. box.MinEdge += npf;
  320. box.MaxEdge += npf;
  321. for (u16 j = 0; j < 6; j++) {
  322. v3s16 facedir = g_6dirs[j];
  323. aabb3f facebox = box;
  324. f32 d = 0.001 * BS;
  325. if (facedir.X > 0)
  326. facebox.MinEdge.X = facebox.MaxEdge.X - d;
  327. else if (facedir.X < 0)
  328. facebox.MaxEdge.X = facebox.MinEdge.X + d;
  329. else if (facedir.Y > 0)
  330. facebox.MinEdge.Y = facebox.MaxEdge.Y - d;
  331. else if (facedir.Y < 0)
  332. facebox.MaxEdge.Y = facebox.MinEdge.Y + d;
  333. else if (facedir.Z > 0)
  334. facebox.MinEdge.Z = facebox.MaxEdge.Z - d;
  335. else if (facedir.Z < 0)
  336. facebox.MaxEdge.Z = facebox.MinEdge.Z + d;
  337. v3f centerpoint = facebox.getCenter();
  338. f32 distance = (centerpoint - camera_position).getLength();
  339. if (distance >= mindistance)
  340. continue;
  341. if (!facebox.intersectsWithLine(shootline))
  342. continue;
  343. v3s16 np_above = np + facedir;
  344. result.type = POINTEDTHING_NODE;
  345. result.node_undersurface = np;
  346. result.node_abovesurface = np_above;
  347. mindistance = distance;
  348. hilightboxes.clear();
  349. if (!g_settings->getBool("enable_node_highlighting")) {
  350. for (std::vector<aabb3f>::const_iterator
  351. i2 = boxes.begin();
  352. i2 != boxes.end(); i2++) {
  353. aabb3f box = *i2;
  354. box.MinEdge += npf + v3f(-d, -d, -d) - intToFloat(camera_offset, BS);
  355. box.MaxEdge += npf + v3f(d, d, d) - intToFloat(camera_offset, BS);
  356. hilightboxes.push_back(box);
  357. }
  358. }
  359. }
  360. }
  361. } // for coords
  362. return result;
  363. }
  364. /* Profiler display */
  365. void update_profiler_gui(gui::IGUIStaticText *guitext_profiler, FontEngine *fe,
  366. u32 show_profiler, u32 show_profiler_max, s32 screen_height)
  367. {
  368. if (show_profiler == 0) {
  369. guitext_profiler->setVisible(false);
  370. } else {
  371. std::ostringstream os(std::ios_base::binary);
  372. g_profiler->printPage(os, show_profiler, show_profiler_max);
  373. std::wstring text = narrow_to_wide(os.str());
  374. guitext_profiler->setText(text.c_str());
  375. guitext_profiler->setVisible(true);
  376. s32 w = fe->getTextWidth(text.c_str());
  377. if (w < 400)
  378. w = 400;
  379. unsigned text_height = fe->getTextHeight();
  380. core::position2di upper_left, lower_right;
  381. upper_left.X = 6;
  382. upper_left.Y = (text_height + 5) * 2;
  383. lower_right.X = 12 + w;
  384. lower_right.Y = upper_left.Y + (text_height + 1) * MAX_PROFILER_TEXT_ROWS;
  385. if (lower_right.Y > screen_height * 2 / 3)
  386. lower_right.Y = screen_height * 2 / 3;
  387. core::rect<s32> rect(upper_left, lower_right);
  388. guitext_profiler->setRelativePosition(rect);
  389. guitext_profiler->setVisible(true);
  390. }
  391. }
  392. class ProfilerGraph
  393. {
  394. private:
  395. struct Piece {
  396. Profiler::GraphValues values;
  397. };
  398. struct Meta {
  399. float min;
  400. float max;
  401. video::SColor color;
  402. Meta(float initial = 0,
  403. video::SColor color = video::SColor(255, 255, 255, 255)):
  404. min(initial),
  405. max(initial),
  406. color(color)
  407. {}
  408. };
  409. std::list<Piece> m_log;
  410. public:
  411. u32 m_log_max_size;
  412. ProfilerGraph():
  413. m_log_max_size(200)
  414. {}
  415. void put(const Profiler::GraphValues &values)
  416. {
  417. Piece piece;
  418. piece.values = values;
  419. m_log.push_back(piece);
  420. while (m_log.size() > m_log_max_size)
  421. m_log.erase(m_log.begin());
  422. }
  423. void draw(s32 x_left, s32 y_bottom, video::IVideoDriver *driver,
  424. gui::IGUIFont *font) const
  425. {
  426. std::map<std::string, Meta> m_meta;
  427. for (std::list<Piece>::const_iterator k = m_log.begin();
  428. k != m_log.end(); k++) {
  429. const Piece &piece = *k;
  430. for (Profiler::GraphValues::const_iterator i = piece.values.begin();
  431. i != piece.values.end(); i++) {
  432. const std::string &id = i->first;
  433. const float &value = i->second;
  434. std::map<std::string, Meta>::iterator j =
  435. m_meta.find(id);
  436. if (j == m_meta.end()) {
  437. m_meta[id] = Meta(value);
  438. continue;
  439. }
  440. if (value < j->second.min)
  441. j->second.min = value;
  442. if (value > j->second.max)
  443. j->second.max = value;
  444. }
  445. }
  446. // Assign colors
  447. static const video::SColor usable_colors[] = {
  448. video::SColor(255, 255, 100, 100),
  449. video::SColor(255, 90, 225, 90),
  450. video::SColor(255, 100, 100, 255),
  451. video::SColor(255, 255, 150, 50),
  452. video::SColor(255, 220, 220, 100)
  453. };
  454. static const u32 usable_colors_count =
  455. sizeof(usable_colors) / sizeof(*usable_colors);
  456. u32 next_color_i = 0;
  457. for (std::map<std::string, Meta>::iterator i = m_meta.begin();
  458. i != m_meta.end(); i++) {
  459. Meta &meta = i->second;
  460. video::SColor color(255, 200, 200, 200);
  461. if (next_color_i < usable_colors_count)
  462. color = usable_colors[next_color_i++];
  463. meta.color = color;
  464. }
  465. s32 graphh = 50;
  466. s32 textx = x_left + m_log_max_size + 15;
  467. s32 textx2 = textx + 200 - 15;
  468. // Draw background
  469. /*{
  470. u32 num_graphs = m_meta.size();
  471. core::rect<s32> rect(x_left, y_bottom - num_graphs*graphh,
  472. textx2, y_bottom);
  473. video::SColor bgcolor(120,0,0,0);
  474. driver->draw2DRectangle(bgcolor, rect, NULL);
  475. }*/
  476. s32 meta_i = 0;
  477. for (std::map<std::string, Meta>::const_iterator i = m_meta.begin();
  478. i != m_meta.end(); i++) {
  479. const std::string &id = i->first;
  480. const Meta &meta = i->second;
  481. s32 x = x_left;
  482. s32 y = y_bottom - meta_i * 50;
  483. float show_min = meta.min;
  484. float show_max = meta.max;
  485. if (show_min >= -0.0001 && show_max >= -0.0001) {
  486. if (show_min <= show_max * 0.5)
  487. show_min = 0;
  488. }
  489. s32 texth = 15;
  490. char buf[10];
  491. snprintf(buf, 10, "%.3g", show_max);
  492. font->draw(narrow_to_wide(buf).c_str(),
  493. core::rect<s32>(textx, y - graphh,
  494. textx2, y - graphh + texth),
  495. meta.color);
  496. snprintf(buf, 10, "%.3g", show_min);
  497. font->draw(narrow_to_wide(buf).c_str(),
  498. core::rect<s32>(textx, y - texth,
  499. textx2, y),
  500. meta.color);
  501. font->draw(narrow_to_wide(id).c_str(),
  502. core::rect<s32>(textx, y - graphh / 2 - texth / 2,
  503. textx2, y - graphh / 2 + texth / 2),
  504. meta.color);
  505. s32 graph1y = y;
  506. s32 graph1h = graphh;
  507. bool relativegraph = (show_min != 0 && show_min != show_max);
  508. float lastscaledvalue = 0.0;
  509. bool lastscaledvalue_exists = false;
  510. for (std::list<Piece>::const_iterator j = m_log.begin();
  511. j != m_log.end(); j++) {
  512. const Piece &piece = *j;
  513. float value = 0;
  514. bool value_exists = false;
  515. Profiler::GraphValues::const_iterator k =
  516. piece.values.find(id);
  517. if (k != piece.values.end()) {
  518. value = k->second;
  519. value_exists = true;
  520. }
  521. if (!value_exists) {
  522. x++;
  523. lastscaledvalue_exists = false;
  524. continue;
  525. }
  526. float scaledvalue = 1.0;
  527. if (show_max != show_min)
  528. scaledvalue = (value - show_min) / (show_max - show_min);
  529. if (scaledvalue == 1.0 && value == 0) {
  530. x++;
  531. lastscaledvalue_exists = false;
  532. continue;
  533. }
  534. if (relativegraph) {
  535. if (lastscaledvalue_exists) {
  536. s32 ivalue1 = lastscaledvalue * graph1h;
  537. s32 ivalue2 = scaledvalue * graph1h;
  538. driver->draw2DLine(v2s32(x - 1, graph1y - ivalue1),
  539. v2s32(x, graph1y - ivalue2), meta.color);
  540. }
  541. lastscaledvalue = scaledvalue;
  542. lastscaledvalue_exists = true;
  543. } else {
  544. s32 ivalue = scaledvalue * graph1h;
  545. driver->draw2DLine(v2s32(x, graph1y),
  546. v2s32(x, graph1y - ivalue), meta.color);
  547. }
  548. x++;
  549. }
  550. meta_i++;
  551. }
  552. }
  553. };
  554. class NodeDugEvent: public MtEvent
  555. {
  556. public:
  557. v3s16 p;
  558. MapNode n;
  559. NodeDugEvent(v3s16 p, MapNode n):
  560. p(p),
  561. n(n)
  562. {}
  563. const char *getType() const
  564. {
  565. return "NodeDug";
  566. }
  567. };
  568. class SoundMaker
  569. {
  570. ISoundManager *m_sound;
  571. INodeDefManager *m_ndef;
  572. public:
  573. float m_player_step_timer;
  574. SimpleSoundSpec m_player_step_sound;
  575. SimpleSoundSpec m_player_leftpunch_sound;
  576. SimpleSoundSpec m_player_rightpunch_sound;
  577. SoundMaker(ISoundManager *sound, INodeDefManager *ndef):
  578. m_sound(sound),
  579. m_ndef(ndef),
  580. m_player_step_timer(0)
  581. {
  582. }
  583. void playPlayerStep()
  584. {
  585. if (m_player_step_timer <= 0 && m_player_step_sound.exists()) {
  586. m_player_step_timer = 0.03;
  587. m_sound->playSound(m_player_step_sound, false);
  588. }
  589. }
  590. static void viewBobbingStep(MtEvent *e, void *data)
  591. {
  592. SoundMaker *sm = (SoundMaker *)data;
  593. sm->playPlayerStep();
  594. }
  595. static void playerRegainGround(MtEvent *e, void *data)
  596. {
  597. SoundMaker *sm = (SoundMaker *)data;
  598. sm->playPlayerStep();
  599. }
  600. static void playerJump(MtEvent *e, void *data)
  601. {
  602. //SoundMaker *sm = (SoundMaker*)data;
  603. }
  604. static void cameraPunchLeft(MtEvent *e, void *data)
  605. {
  606. SoundMaker *sm = (SoundMaker *)data;
  607. sm->m_sound->playSound(sm->m_player_leftpunch_sound, false);
  608. }
  609. static void cameraPunchRight(MtEvent *e, void *data)
  610. {
  611. SoundMaker *sm = (SoundMaker *)data;
  612. sm->m_sound->playSound(sm->m_player_rightpunch_sound, false);
  613. }
  614. static void nodeDug(MtEvent *e, void *data)
  615. {
  616. SoundMaker *sm = (SoundMaker *)data;
  617. NodeDugEvent *nde = (NodeDugEvent *)e;
  618. sm->m_sound->playSound(sm->m_ndef->get(nde->n).sound_dug, false);
  619. }
  620. static void playerDamage(MtEvent *e, void *data)
  621. {
  622. SoundMaker *sm = (SoundMaker *)data;
  623. sm->m_sound->playSound(SimpleSoundSpec("player_damage", 0.5), false);
  624. }
  625. static void playerFallingDamage(MtEvent *e, void *data)
  626. {
  627. SoundMaker *sm = (SoundMaker *)data;
  628. sm->m_sound->playSound(SimpleSoundSpec("player_falling_damage", 0.5), false);
  629. }
  630. void registerReceiver(MtEventManager *mgr)
  631. {
  632. mgr->reg("ViewBobbingStep", SoundMaker::viewBobbingStep, this);
  633. mgr->reg("PlayerRegainGround", SoundMaker::playerRegainGround, this);
  634. mgr->reg("PlayerJump", SoundMaker::playerJump, this);
  635. mgr->reg("CameraPunchLeft", SoundMaker::cameraPunchLeft, this);
  636. mgr->reg("CameraPunchRight", SoundMaker::cameraPunchRight, this);
  637. mgr->reg("NodeDug", SoundMaker::nodeDug, this);
  638. mgr->reg("PlayerDamage", SoundMaker::playerDamage, this);
  639. mgr->reg("PlayerFallingDamage", SoundMaker::playerFallingDamage, this);
  640. }
  641. void step(float dtime)
  642. {
  643. m_player_step_timer -= dtime;
  644. }
  645. };
  646. // Locally stored sounds don't need to be preloaded because of this
  647. class GameOnDemandSoundFetcher: public OnDemandSoundFetcher
  648. {
  649. std::set<std::string> m_fetched;
  650. public:
  651. void fetchSounds(const std::string &name,
  652. std::set<std::string> &dst_paths,
  653. std::set<std::string> &dst_datas)
  654. {
  655. if (m_fetched.count(name))
  656. return;
  657. m_fetched.insert(name);
  658. std::string base = porting::path_share + DIR_DELIM + "testsounds";
  659. dst_paths.insert(base + DIR_DELIM + name + ".ogg");
  660. dst_paths.insert(base + DIR_DELIM + name + ".0.ogg");
  661. dst_paths.insert(base + DIR_DELIM + name + ".1.ogg");
  662. dst_paths.insert(base + DIR_DELIM + name + ".2.ogg");
  663. dst_paths.insert(base + DIR_DELIM + name + ".3.ogg");
  664. dst_paths.insert(base + DIR_DELIM + name + ".4.ogg");
  665. dst_paths.insert(base + DIR_DELIM + name + ".5.ogg");
  666. dst_paths.insert(base + DIR_DELIM + name + ".6.ogg");
  667. dst_paths.insert(base + DIR_DELIM + name + ".7.ogg");
  668. dst_paths.insert(base + DIR_DELIM + name + ".8.ogg");
  669. dst_paths.insert(base + DIR_DELIM + name + ".9.ogg");
  670. }
  671. };
  672. class GameGlobalShaderConstantSetter : public IShaderConstantSetter
  673. {
  674. Sky *m_sky;
  675. bool *m_force_fog_off;
  676. f32 *m_fog_range;
  677. Client *m_client;
  678. bool m_fogEnabled;
  679. public:
  680. void onSettingsChange(const std::string &name)
  681. {
  682. if (name == "enable_fog")
  683. m_fogEnabled = g_settings->getBool("enable_fog");
  684. }
  685. static void SettingsCallback(const std::string name, void *userdata)
  686. {
  687. reinterpret_cast<GameGlobalShaderConstantSetter*>(userdata)->onSettingsChange(name);
  688. }
  689. GameGlobalShaderConstantSetter(Sky *sky, bool *force_fog_off,
  690. f32 *fog_range, Client *client) :
  691. m_sky(sky),
  692. m_force_fog_off(force_fog_off),
  693. m_fog_range(fog_range),
  694. m_client(client)
  695. {
  696. g_settings->registerChangedCallback("enable_fog", SettingsCallback, this);
  697. m_fogEnabled = g_settings->getBool("enable_fog");
  698. }
  699. ~GameGlobalShaderConstantSetter()
  700. {
  701. g_settings->deregisterChangedCallback("enable_fog", SettingsCallback, this);
  702. }
  703. virtual void onSetConstants(video::IMaterialRendererServices *services,
  704. bool is_highlevel)
  705. {
  706. if (!is_highlevel)
  707. return;
  708. // Background color
  709. video::SColor bgcolor = m_sky->getBgColor();
  710. video::SColorf bgcolorf(bgcolor);
  711. float bgcolorfa[4] = {
  712. bgcolorf.r,
  713. bgcolorf.g,
  714. bgcolorf.b,
  715. bgcolorf.a,
  716. };
  717. services->setPixelShaderConstant("skyBgColor", bgcolorfa, 4);
  718. // Fog distance
  719. float fog_distance = 10000 * BS;
  720. if (g_settings->getBool("enable_fog") && !*m_force_fog_off)
  721. fog_distance = *m_fog_range;
  722. services->setPixelShaderConstant("fogDistance", &fog_distance, 1);
  723. // Day-night ratio
  724. u32 daynight_ratio = m_client->getEnv().getDayNightRatio();
  725. float daynight_ratio_f = (float)daynight_ratio / 1000.0;
  726. services->setPixelShaderConstant("dayNightRatio", &daynight_ratio_f, 1);
  727. u32 animation_timer = porting::getTimeMs() % 100000;
  728. float animation_timer_f = (float)animation_timer / 100000.0;
  729. services->setPixelShaderConstant("animationTimer", &animation_timer_f, 1);
  730. services->setVertexShaderConstant("animationTimer", &animation_timer_f, 1);
  731. LocalPlayer *player = m_client->getEnv().getLocalPlayer();
  732. v3f eye_position = player->getEyePosition();
  733. services->setPixelShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3);
  734. services->setVertexShaderConstant("eyePosition", (irr::f32 *)&eye_position, 3);
  735. // Uniform sampler layers
  736. int layer0 = 0;
  737. int layer1 = 1;
  738. int layer2 = 2;
  739. // before 1.8 there isn't a "integer interface", only float
  740. #if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
  741. services->setPixelShaderConstant("baseTexture" , (irr::f32 *)&layer0, 1);
  742. services->setPixelShaderConstant("normalTexture" , (irr::f32 *)&layer1, 1);
  743. services->setPixelShaderConstant("useNormalmap" , (irr::f32 *)&layer2, 1);
  744. #else
  745. services->setPixelShaderConstant("baseTexture" , (irr::s32 *)&layer0, 1);
  746. services->setPixelShaderConstant("normalTexture" , (irr::s32 *)&layer1, 1);
  747. services->setPixelShaderConstant("useNormalmap" , (irr::s32 *)&layer2, 1);
  748. #endif
  749. }
  750. };
  751. bool nodePlacementPrediction(Client &client,
  752. const ItemDefinition &playeritem_def, v3s16 nodepos, v3s16 neighbourpos)
  753. {
  754. std::string prediction = playeritem_def.node_placement_prediction;
  755. INodeDefManager *nodedef = client.ndef();
  756. ClientMap &map = client.getEnv().getClientMap();
  757. MapNode node;
  758. bool is_valid_position;
  759. node = map.getNodeNoEx(nodepos, &is_valid_position);
  760. if (!is_valid_position)
  761. return false;
  762. if (prediction != "" && !nodedef->get(node).rightclickable) {
  763. verbosestream << "Node placement prediction for "
  764. << playeritem_def.name << " is "
  765. << prediction << std::endl;
  766. v3s16 p = neighbourpos;
  767. // Place inside node itself if buildable_to
  768. MapNode n_under = map.getNodeNoEx(nodepos, &is_valid_position);
  769. if (is_valid_position)
  770. {
  771. if (nodedef->get(n_under).buildable_to)
  772. p = nodepos;
  773. else {
  774. node = map.getNodeNoEx(p, &is_valid_position);
  775. if (is_valid_position &&!nodedef->get(node).buildable_to)
  776. return false;
  777. }
  778. }
  779. // Find id of predicted node
  780. content_t id;
  781. bool found = nodedef->getId(prediction, id);
  782. if (!found) {
  783. errorstream << "Node placement prediction failed for "
  784. << playeritem_def.name << " (places "
  785. << prediction
  786. << ") - Name not known" << std::endl;
  787. return false;
  788. }
  789. // Predict param2 for facedir and wallmounted nodes
  790. u8 param2 = 0;
  791. if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED) {
  792. v3s16 dir = nodepos - neighbourpos;
  793. if (abs(dir.Y) > MYMAX(abs(dir.X), abs(dir.Z))) {
  794. param2 = dir.Y < 0 ? 1 : 0;
  795. } else if (abs(dir.X) > abs(dir.Z)) {
  796. param2 = dir.X < 0 ? 3 : 2;
  797. } else {
  798. param2 = dir.Z < 0 ? 5 : 4;
  799. }
  800. }
  801. if (nodedef->get(id).param_type_2 == CPT2_FACEDIR) {
  802. v3s16 dir = nodepos - floatToInt(client.getEnv().getLocalPlayer()->getPosition(), BS);
  803. if (abs(dir.X) > abs(dir.Z)) {
  804. param2 = dir.X < 0 ? 3 : 1;
  805. } else {
  806. param2 = dir.Z < 0 ? 2 : 0;
  807. }
  808. }
  809. assert(param2 <= 5);
  810. //Check attachment if node is in group attached_node
  811. if (((ItemGroupList) nodedef->get(id).groups)["attached_node"] != 0) {
  812. static v3s16 wallmounted_dirs[8] = {
  813. v3s16(0, 1, 0),
  814. v3s16(0, -1, 0),
  815. v3s16(1, 0, 0),
  816. v3s16(-1, 0, 0),
  817. v3s16(0, 0, 1),
  818. v3s16(0, 0, -1),
  819. };
  820. v3s16 pp;
  821. if (nodedef->get(id).param_type_2 == CPT2_WALLMOUNTED)
  822. pp = p + wallmounted_dirs[param2];
  823. else
  824. pp = p + v3s16(0, -1, 0);
  825. if (!nodedef->get(map.getNodeNoEx(pp)).walkable)
  826. return false;
  827. }
  828. // Add node to client map
  829. MapNode n(id, 0, param2);
  830. try {
  831. LocalPlayer *player = client.getEnv().getLocalPlayer();
  832. // Dont place node when player would be inside new node
  833. // NOTE: This is to be eventually implemented by a mod as client-side Lua
  834. if (!nodedef->get(n).walkable ||
  835. g_settings->getBool("enable_build_where_you_stand") ||
  836. (client.checkPrivilege("noclip") && g_settings->getBool("noclip")) ||
  837. (nodedef->get(n).walkable &&
  838. neighbourpos != player->getStandingNodePos() + v3s16(0, 1, 0) &&
  839. neighbourpos != player->getStandingNodePos() + v3s16(0, 2, 0))) {
  840. // This triggers the required mesh update too
  841. client.addNode(p, n);
  842. return true;
  843. }
  844. } catch (InvalidPositionException &e) {
  845. errorstream << "Node placement prediction failed for "
  846. << playeritem_def.name << " (places "
  847. << prediction
  848. << ") - Position not loaded" << std::endl;
  849. }
  850. }
  851. return false;
  852. }
  853. static inline void create_formspec_menu(GUIFormSpecMenu **cur_formspec,
  854. InventoryManager *invmgr, IGameDef *gamedef,
  855. IWritableTextureSource *tsrc, IrrlichtDevice *device,
  856. IFormSource *fs_src, TextDest *txt_dest, Client *client)
  857. {
  858. if (*cur_formspec == 0) {
  859. *cur_formspec = new GUIFormSpecMenu(device, guiroot, -1, &g_menumgr,
  860. invmgr, gamedef, tsrc, fs_src, txt_dest, client);
  861. (*cur_formspec)->doPause = false;
  862. /*
  863. Caution: do not call (*cur_formspec)->drop() here --
  864. the reference might outlive the menu, so we will
  865. periodically check if *cur_formspec is the only
  866. remaining reference (i.e. the menu was removed)
  867. and delete it in that case.
  868. */
  869. } else {
  870. (*cur_formspec)->setFormSource(fs_src);
  871. (*cur_formspec)->setTextDest(txt_dest);
  872. }
  873. }
  874. #define SIZE_TAG "size[11,5.5]"
  875. static void show_chat_menu(GUIFormSpecMenu **cur_formspec,
  876. InventoryManager *invmgr, IGameDef *gamedef,
  877. IWritableTextureSource *tsrc, IrrlichtDevice *device,
  878. Client *client, std::string text)
  879. {
  880. std::string formspec =
  881. FORMSPEC_VERSION_STRING
  882. SIZE_TAG
  883. "field[3,2.35;6,0.5;f_text;;" + text + "]"
  884. "button_exit[4,3;3,0.5;btn_send;" + wide_to_narrow(wstrgettext("Proceed")) + "]"
  885. ;
  886. /* Create menu */
  887. /* Note: FormspecFormSource and LocalFormspecHandler
  888. * are deleted by guiFormSpecMenu */
  889. FormspecFormSource *fs_src = new FormspecFormSource(formspec);
  890. LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_CHAT_MENU", client);
  891. create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
  892. }
  893. static void show_deathscreen(GUIFormSpecMenu **cur_formspec,
  894. InventoryManager *invmgr, IGameDef *gamedef,
  895. IWritableTextureSource *tsrc, IrrlichtDevice *device, Client *client)
  896. {
  897. std::string formspec =
  898. std::string(FORMSPEC_VERSION_STRING) +
  899. SIZE_TAG
  900. "bgcolor[#320000b4;true]"
  901. "label[4.85,1.35;You died.]"
  902. "button_exit[4,3;3,0.5;btn_respawn;" + gettext("Respawn") + "]"
  903. ;
  904. /* Create menu */
  905. /* Note: FormspecFormSource and LocalFormspecHandler
  906. * are deleted by guiFormSpecMenu */
  907. FormspecFormSource *fs_src = new FormspecFormSource(formspec);
  908. LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_DEATH_SCREEN", client);
  909. create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
  910. }
  911. /******************************************************************************/
  912. static void show_pause_menu(GUIFormSpecMenu **cur_formspec,
  913. InventoryManager *invmgr, IGameDef *gamedef,
  914. IWritableTextureSource *tsrc, IrrlichtDevice *device,
  915. bool singleplayermode)
  916. {
  917. #ifdef __ANDROID__
  918. std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
  919. "No menu visible:\n"
  920. "- single tap: button activate\n"
  921. "- double tap: place/use\n"
  922. "- slide finger: look around\n"
  923. "Menu/Inventory visible:\n"
  924. "- double tap (outside):\n"
  925. " -->close\n"
  926. "- touch stack, touch slot:\n"
  927. " --> move stack\n"
  928. "- touch&drag, tap 2nd finger\n"
  929. " --> place single item to slot\n"
  930. ));
  931. #else
  932. std::string control_text = wide_to_narrow(wstrgettext("Default Controls:\n"
  933. "- WASD: move\n"
  934. "- Space: jump/climb\n"
  935. "- Shift: sneak/go down\n"
  936. "- Q: drop item\n"
  937. "- I: inventory\n"
  938. "- Mouse: turn/look\n"
  939. "- Mouse left: dig/punch\n"
  940. "- Mouse right: place/use\n"
  941. "- Mouse wheel: select item\n"
  942. "- T: chat\n"
  943. ));
  944. #endif
  945. float ypos = singleplayermode ? 0.5 : 0.1;
  946. std::ostringstream os;
  947. os << FORMSPEC_VERSION_STRING << SIZE_TAG
  948. << "button_exit[4," << (ypos++) << ";3,0.5;btn_continue;"
  949. << wide_to_narrow(wstrgettext("Continue")) << "]";
  950. if (!singleplayermode) {
  951. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_change_password;"
  952. << wide_to_narrow(wstrgettext("Change Password")) << "]";
  953. }
  954. #ifndef __ANDROID__
  955. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_sound;"
  956. << wide_to_narrow(wstrgettext("Sound Volume")) << "]";
  957. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_key_config;"
  958. << wide_to_narrow(wstrgettext("Change Keys")) << "]";
  959. #endif
  960. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_menu;"
  961. << wide_to_narrow(wstrgettext("Exit to Menu")) << "]";
  962. os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
  963. << wide_to_narrow(wstrgettext("Exit to OS")) << "]"
  964. << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]"
  965. << "textarea[0.4,0.25;3.5,6;;" << "Minetest\n"
  966. << minetest_build_info << "\n"
  967. << "path_user = " << wrap_rows(porting::path_user, 20)
  968. << "\n;]";
  969. /* Create menu */
  970. /* Note: FormspecFormSource and LocalFormspecHandler *
  971. * are deleted by guiFormSpecMenu */
  972. FormspecFormSource *fs_src = new FormspecFormSource(os.str());
  973. LocalFormspecHandler *txt_dst = new LocalFormspecHandler("MT_PAUSE_MENU");
  974. create_formspec_menu(cur_formspec, invmgr, gamedef, tsrc, device, fs_src, txt_dst, NULL);
  975. (*cur_formspec)->doPause = true;
  976. }
  977. /******************************************************************************/
  978. static void updateChat(Client &client, f32 dtime, bool show_debug,
  979. const v2u32 &screensize, bool show_chat, u32 show_profiler,
  980. ChatBackend &chat_backend, gui::IGUIStaticText *guitext_chat)
  981. {
  982. // Add chat log output for errors to be shown in chat
  983. static LogOutputBuffer chat_log_error_buf(LMT_ERROR);
  984. // Get new messages from error log buffer
  985. while (!chat_log_error_buf.empty()) {
  986. chat_backend.addMessage(L"", narrow_to_wide(chat_log_error_buf.get()));
  987. }
  988. // Get new messages from client
  989. std::wstring message;
  990. while (client.getChatMessage(message)) {
  991. chat_backend.addUnparsedMessage(message);
  992. }
  993. // Remove old messages
  994. chat_backend.step(dtime);
  995. // Display all messages in a static text element
  996. unsigned int recent_chat_count = chat_backend.getRecentBuffer().getLineCount();
  997. std::wstring recent_chat = chat_backend.getRecentChat();
  998. unsigned int line_height = g_fontengine->getLineHeight();
  999. guitext_chat->setText(recent_chat.c_str());
  1000. // Update gui element size and position
  1001. s32 chat_y = 5 + line_height;
  1002. if (show_debug)
  1003. chat_y += line_height;
  1004. // first pass to calculate height of text to be set
  1005. s32 width = std::min(g_fontengine->getTextWidth(recent_chat) + 10,
  1006. porting::getWindowSize().X - 20);
  1007. core::rect<s32> rect(10, chat_y, width, chat_y + porting::getWindowSize().Y);
  1008. guitext_chat->setRelativePosition(rect);
  1009. //now use real height of text and adjust rect according to this size
  1010. rect = core::rect<s32>(10, chat_y, width,
  1011. chat_y + guitext_chat->getTextHeight());
  1012. guitext_chat->setRelativePosition(rect);
  1013. // Don't show chat if disabled or empty or profiler is enabled
  1014. guitext_chat->setVisible(
  1015. show_chat && recent_chat_count != 0 && !show_profiler);
  1016. }
  1017. /****************************************************************************
  1018. Fast key cache for main game loop
  1019. ****************************************************************************/
  1020. /* This is faster than using getKeySetting with the tradeoff that functions
  1021. * using it must make sure that it's initialised before using it and there is
  1022. * no error handling (for example bounds checking). This is really intended for
  1023. * use only in the main running loop of the client (the_game()) where the faster
  1024. * (up to 10x faster) key lookup is an asset. Other parts of the codebase
  1025. * (e.g. formspecs) should continue using getKeySetting().
  1026. */
  1027. struct KeyCache {
  1028. KeyCache() { populate(); }
  1029. enum {
  1030. // Player movement
  1031. KEYMAP_ID_FORWARD,
  1032. KEYMAP_ID_BACKWARD,
  1033. KEYMAP_ID_LEFT,
  1034. KEYMAP_ID_RIGHT,
  1035. KEYMAP_ID_JUMP,
  1036. KEYMAP_ID_SPECIAL1,
  1037. KEYMAP_ID_SNEAK,
  1038. // Other
  1039. KEYMAP_ID_DROP,
  1040. KEYMAP_ID_INVENTORY,
  1041. KEYMAP_ID_CHAT,
  1042. KEYMAP_ID_CMD,
  1043. KEYMAP_ID_CONSOLE,
  1044. KEYMAP_ID_FREEMOVE,
  1045. KEYMAP_ID_FASTMOVE,
  1046. KEYMAP_ID_NOCLIP,
  1047. KEYMAP_ID_SCREENSHOT,
  1048. KEYMAP_ID_TOGGLE_HUD,
  1049. KEYMAP_ID_TOGGLE_CHAT,
  1050. KEYMAP_ID_TOGGLE_FORCE_FOG_OFF,
  1051. KEYMAP_ID_TOGGLE_UPDATE_CAMERA,
  1052. KEYMAP_ID_TOGGLE_DEBUG,
  1053. KEYMAP_ID_TOGGLE_PROFILER,
  1054. KEYMAP_ID_CAMERA_MODE,
  1055. KEYMAP_ID_INCREASE_VIEWING_RANGE,
  1056. KEYMAP_ID_DECREASE_VIEWING_RANGE,
  1057. KEYMAP_ID_RANGESELECT,
  1058. KEYMAP_ID_QUICKTUNE_NEXT,
  1059. KEYMAP_ID_QUICKTUNE_PREV,
  1060. KEYMAP_ID_QUICKTUNE_INC,
  1061. KEYMAP_ID_QUICKTUNE_DEC,
  1062. KEYMAP_ID_DEBUG_STACKS,
  1063. // Fake keycode for array size and internal checks
  1064. KEYMAP_INTERNAL_ENUM_COUNT
  1065. };
  1066. void populate();
  1067. KeyPress key[KEYMAP_INTERNAL_ENUM_COUNT];
  1068. };
  1069. void KeyCache::populate()
  1070. {
  1071. key[KEYMAP_ID_FORWARD] = getKeySetting("keymap_forward");
  1072. key[KEYMAP_ID_BACKWARD] = getKeySetting("keymap_backward");
  1073. key[KEYMAP_ID_LEFT] = getKeySetting("keymap_left");
  1074. key[KEYMAP_ID_RIGHT] = getKeySetting("keymap_right");
  1075. key[KEYMAP_ID_JUMP] = getKeySetting("keymap_jump");
  1076. key[KEYMAP_ID_SPECIAL1] = getKeySetting("keymap_special1");
  1077. key[KEYMAP_ID_SNEAK] = getKeySetting("keymap_sneak");
  1078. key[KEYMAP_ID_DROP] = getKeySetting("keymap_drop");
  1079. key[KEYMAP_ID_INVENTORY] = getKeySetting("keymap_inventory");
  1080. key[KEYMAP_ID_CHAT] = getKeySetting("keymap_chat");
  1081. key[KEYMAP_ID_CMD] = getKeySetting("keymap_cmd");
  1082. key[KEYMAP_ID_CONSOLE] = getKeySetting("keymap_console");
  1083. key[KEYMAP_ID_FREEMOVE] = getKeySetting("keymap_freemove");
  1084. key[KEYMAP_ID_FASTMOVE] = getKeySetting("keymap_fastmove");
  1085. key[KEYMAP_ID_NOCLIP] = getKeySetting("keymap_noclip");
  1086. key[KEYMAP_ID_SCREENSHOT] = getKeySetting("keymap_screenshot");
  1087. key[KEYMAP_ID_TOGGLE_HUD] = getKeySetting("keymap_toggle_hud");
  1088. key[KEYMAP_ID_TOGGLE_CHAT] = getKeySetting("keymap_toggle_chat");
  1089. key[KEYMAP_ID_TOGGLE_FORCE_FOG_OFF]
  1090. = getKeySetting("keymap_toggle_force_fog_off");
  1091. key[KEYMAP_ID_TOGGLE_UPDATE_CAMERA]
  1092. = getKeySetting("keymap_toggle_update_camera");
  1093. key[KEYMAP_ID_TOGGLE_DEBUG]
  1094. = getKeySetting("keymap_toggle_debug");
  1095. key[KEYMAP_ID_TOGGLE_PROFILER]
  1096. = getKeySetting("keymap_toggle_profiler");
  1097. key[KEYMAP_ID_CAMERA_MODE]
  1098. = getKeySetting("keymap_camera_mode");
  1099. key[KEYMAP_ID_INCREASE_VIEWING_RANGE]
  1100. = getKeySetting("keymap_increase_viewing_range_min");
  1101. key[KEYMAP_ID_DECREASE_VIEWING_RANGE]
  1102. = getKeySetting("keymap_decrease_viewing_range_min");
  1103. key[KEYMAP_ID_RANGESELECT]
  1104. = getKeySetting("keymap_rangeselect");
  1105. key[KEYMAP_ID_QUICKTUNE_NEXT] = getKeySetting("keymap_quicktune_next");
  1106. key[KEYMAP_ID_QUICKTUNE_PREV] = getKeySetting("keymap_quicktune_prev");
  1107. key[KEYMAP_ID_QUICKTUNE_INC] = getKeySetting("keymap_quicktune_inc");
  1108. key[KEYMAP_ID_QUICKTUNE_DEC] = getKeySetting("keymap_quicktune_dec");
  1109. key[KEYMAP_ID_DEBUG_STACKS] = getKeySetting("keymap_print_debug_stacks");
  1110. }
  1111. /****************************************************************************
  1112. ****************************************************************************/
  1113. const float object_hit_delay = 0.2;
  1114. struct FpsControl {
  1115. u32 last_time, busy_time, sleep_time;
  1116. };
  1117. /* The reason the following structs are not anonymous structs within the
  1118. * class is that they are not used by the majority of member functions and
  1119. * many functions that do require objects of thse types do not modify them
  1120. * (so they can be passed as a const qualified parameter)
  1121. */
  1122. struct CameraOrientation {
  1123. f32 camera_yaw; // "right/left"
  1124. f32 camera_pitch; // "up/down"
  1125. };
  1126. struct GameRunData {
  1127. u16 dig_index;
  1128. u16 new_playeritem;
  1129. PointedThing pointed_old;
  1130. bool digging;
  1131. bool ldown_for_dig;
  1132. bool left_punch;
  1133. bool update_wielded_item_trigger;
  1134. bool reset_jump_timer;
  1135. float nodig_delay_timer;
  1136. float dig_time;
  1137. float dig_time_complete;
  1138. float repeat_rightclick_timer;
  1139. float object_hit_delay_timer;
  1140. float time_from_last_punch;
  1141. ClientActiveObject *selected_object;
  1142. float jump_timer;
  1143. float damage_flash;
  1144. float update_draw_list_timer;
  1145. float statustext_time;
  1146. f32 fog_range;
  1147. v3f update_draw_list_last_cam_dir;
  1148. u32 profiler_current_page;
  1149. u32 profiler_max_page; // Number of pages
  1150. float time_of_day;
  1151. float time_of_day_smooth;
  1152. };
  1153. struct Jitter {
  1154. f32 max, min, avg, counter, max_sample, min_sample, max_fraction;
  1155. };
  1156. struct RunStats {
  1157. u32 drawtime;
  1158. u32 beginscenetime;
  1159. u32 endscenetime;
  1160. Jitter dtime_jitter, busy_time_jitter;
  1161. };
  1162. /* Flags that can, or may, change during main game loop
  1163. */
  1164. struct VolatileRunFlags {
  1165. bool invert_mouse;
  1166. bool show_chat;
  1167. bool show_hud;
  1168. bool force_fog_off;
  1169. bool show_debug;
  1170. bool show_profiler_graph;
  1171. bool disable_camera_update;
  1172. bool first_loop_after_window_activation;
  1173. bool camera_offset_changed;
  1174. };
  1175. /****************************************************************************
  1176. THE GAME
  1177. ****************************************************************************/
  1178. /* This is not intended to be a public class. If a public class becomes
  1179. * desirable then it may be better to create another 'wrapper' class that
  1180. * hides most of the stuff in this class (nothing in this class is required
  1181. * by any other file) but exposes the public methods/data only.
  1182. */
  1183. class Game
  1184. {
  1185. public:
  1186. Game();
  1187. ~Game();
  1188. bool startup(bool *kill,
  1189. bool random_input,
  1190. InputHandler *input,
  1191. IrrlichtDevice *device,
  1192. const std::string &map_dir,
  1193. const std::string &playername,
  1194. const std::string &password,
  1195. // If address is "", local server is used and address is updated
  1196. std::string *address,
  1197. u16 port,
  1198. std::wstring *error_message,
  1199. ChatBackend *chat_backend,
  1200. const SubgameSpec &gamespec, // Used for local game
  1201. bool simple_singleplayer_mode);
  1202. void run();
  1203. void shutdown();
  1204. protected:
  1205. void extendedResourceCleanup();
  1206. // Basic initialisation
  1207. bool init(const std::string &map_dir, std::string *address,
  1208. u16 port,
  1209. const SubgameSpec &gamespec);
  1210. bool initSound();
  1211. bool createSingleplayerServer(const std::string map_dir,
  1212. const SubgameSpec &gamespec, u16 port, std::string *address);
  1213. // Client creation
  1214. bool createClient(const std::string &playername,
  1215. const std::string &password, std::string *address, u16 port,
  1216. std::wstring *error_message);
  1217. bool initGui(std::wstring *error_message);
  1218. // Client connection
  1219. bool connectToServer(const std::string &playername,
  1220. const std::string &password, std::string *address, u16 port,
  1221. bool *connect_ok, bool *aborted);
  1222. bool getServerContent(bool *aborted);
  1223. // Main loop
  1224. void updateInteractTimers(GameRunData *args, f32 dtime);
  1225. bool checkConnection();
  1226. bool handleCallbacks();
  1227. void processQueues();
  1228. void updateProfilers(const GameRunData &run_data, const RunStats &stats,
  1229. const FpsControl &draw_times, f32 dtime);
  1230. void addProfilerGraphs(const RunStats &stats, const FpsControl &draw_times,
  1231. f32 dtime);
  1232. void updateStats(RunStats *stats, const FpsControl &draw_times, f32 dtime);
  1233. void processUserInput(VolatileRunFlags *flags, GameRunData *interact_args,
  1234. f32 dtime);
  1235. void processKeyboardInput(VolatileRunFlags *flags,
  1236. float *statustext_time,
  1237. float *jump_timer,
  1238. bool *reset_jump_timer,
  1239. u32 *profiler_current_page,
  1240. u32 profiler_max_page);
  1241. void processItemSelection(u16 *new_playeritem);
  1242. void dropSelectedItem();
  1243. void openInventory();
  1244. void openConsole();
  1245. void toggleFreeMove(float *statustext_time);
  1246. void toggleFreeMoveAlt(float *statustext_time, float *jump_timer);
  1247. void toggleFast(float *statustext_time);
  1248. void toggleNoClip(float *statustext_time);
  1249. void toggleChat(float *statustext_time, bool *flag);
  1250. void toggleHud(float *statustext_time, bool *flag);
  1251. void toggleFog(float *statustext_time, bool *flag);
  1252. void toggleDebug(float *statustext_time, bool *show_debug,
  1253. bool *show_profiler_graph);
  1254. void toggleUpdateCamera(float *statustext_time, bool *flag);
  1255. void toggleProfiler(float *statustext_time, u32 *profiler_current_page,
  1256. u32 profiler_max_page);
  1257. void increaseViewRange(float *statustext_time);
  1258. void decreaseViewRange(float *statustext_time);
  1259. void toggleFullViewRange(float *statustext_time);
  1260. void updateCameraDirection(CameraOrientation *cam, VolatileRunFlags *flags);
  1261. void updateCameraOrientation(CameraOrientation *cam,
  1262. const VolatileRunFlags &flags);
  1263. void updatePlayerControl(const CameraOrientation &cam);
  1264. void step(f32 *dtime);
  1265. void processClientEvents(CameraOrientation *cam, float *damage_flash);
  1266. void updateCamera(VolatileRunFlags *flags, u32 busy_time, f32 dtime,
  1267. float time_from_last_punch);
  1268. void updateSound(f32 dtime);
  1269. void processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
  1270. GameRunData *runData, f32 dtime, bool show_hud,
  1271. bool show_debug);
  1272. void handlePointingAtNode(GameRunData *runData,
  1273. const PointedThing &pointed, const ItemDefinition &playeritem_def,
  1274. const ToolCapabilities &playeritem_toolcap, f32 dtime);
  1275. void handlePointingAtObject(GameRunData *runData,
  1276. const PointedThing &pointed, const ItemStack &playeritem,
  1277. const v3f &player_position, bool show_debug);
  1278. void handleDigging(GameRunData *runData, const PointedThing &pointed,
  1279. const v3s16 &nodepos, const ToolCapabilities &playeritem_toolcap,
  1280. f32 dtime);
  1281. void updateFrame(std::vector<aabb3f> &highlight_boxes, ProfilerGraph *graph,
  1282. RunStats *stats, GameRunData *runData,
  1283. f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam);
  1284. void updateGui(float *statustext_time, const RunStats &stats,
  1285. const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
  1286. const CameraOrientation &cam);
  1287. void updateProfilerGraphs(ProfilerGraph *graph);
  1288. // Misc
  1289. void limitFps(FpsControl *fps_timings, f32 *dtime);
  1290. void showOverlayMessage(const wchar_t *msg, float dtime, int percent,
  1291. bool draw_clouds = true);
  1292. private:
  1293. InputHandler *input;
  1294. Client *client;
  1295. Server *server;
  1296. IWritableTextureSource *texture_src;
  1297. IWritableShaderSource *shader_src;
  1298. // When created, these will be filled with data received from the server
  1299. IWritableItemDefManager *itemdef_manager;
  1300. IWritableNodeDefManager *nodedef_manager;
  1301. GameOnDemandSoundFetcher soundfetcher; // useful when testing
  1302. ISoundManager *sound;
  1303. bool sound_is_dummy;
  1304. SoundMaker *soundmaker;
  1305. ChatBackend *chat_backend;
  1306. GUIFormSpecMenu *current_formspec;
  1307. EventManager *eventmgr;
  1308. QuicktuneShortcutter *quicktune;
  1309. GUIChatConsole *gui_chat_console; // Free using ->Drop()
  1310. MapDrawControl *draw_control;
  1311. Camera *camera;
  1312. Clouds *clouds; // Free using ->Drop()
  1313. Sky *sky; // Free using ->Drop()
  1314. Inventory *local_inventory;
  1315. Hud *hud;
  1316. /* 'cache'
  1317. This class does take ownership/responsibily for cleaning up etc of any of
  1318. these items (e.g. device)
  1319. */
  1320. IrrlichtDevice *device;
  1321. video::IVideoDriver *driver;
  1322. scene::ISceneManager *smgr;
  1323. bool *kill;
  1324. std::wstring *error_message;
  1325. IGameDef *gamedef; // Convenience (same as *client)
  1326. scene::ISceneNode *skybox;
  1327. bool random_input;
  1328. bool simple_singleplayer_mode;
  1329. /* End 'cache' */
  1330. /* Pre-calculated values
  1331. */
  1332. int crack_animation_length;
  1333. /* GUI stuff
  1334. */
  1335. gui::IGUIStaticText *guitext; // First line of debug text
  1336. gui::IGUIStaticText *guitext2; // Second line of debug text
  1337. gui::IGUIStaticText *guitext_info; // At the middle of the screen
  1338. gui::IGUIStaticText *guitext_status;
  1339. gui::IGUIStaticText *guitext_chat; // Chat text
  1340. gui::IGUIStaticText *guitext_profiler; // Profiler text
  1341. std::wstring infotext;
  1342. std::wstring statustext;
  1343. KeyCache keycache;
  1344. IntervalLimiter profiler_interval;
  1345. /* TODO: Add a callback function so these can be updated when a setting
  1346. * changes. At this point in time it doesn't matter (e.g. /set
  1347. * is documented to change server settings only)
  1348. *
  1349. * TODO: Local caching of settings is not optimal and should at some stage
  1350. * be updated to use a global settings object for getting thse values
  1351. * (as opposed to the this local caching). This can be addressed in
  1352. * a later release.
  1353. */
  1354. bool m_cache_doubletap_jump;
  1355. bool m_cache_enable_node_highlighting;
  1356. bool m_cache_enable_clouds;
  1357. bool m_cache_enable_particles;
  1358. bool m_cache_enable_fog;
  1359. f32 m_cache_mouse_sensitivity;
  1360. f32 m_repeat_right_click_time;
  1361. };
  1362. Game::Game() :
  1363. client(NULL),
  1364. server(NULL),
  1365. texture_src(NULL),
  1366. shader_src(NULL),
  1367. itemdef_manager(NULL),
  1368. nodedef_manager(NULL),
  1369. sound(NULL),
  1370. sound_is_dummy(false),
  1371. soundmaker(NULL),
  1372. chat_backend(NULL),
  1373. current_formspec(NULL),
  1374. eventmgr(NULL),
  1375. quicktune(NULL),
  1376. gui_chat_console(NULL),
  1377. draw_control(NULL),
  1378. camera(NULL),
  1379. clouds(NULL),
  1380. sky(NULL),
  1381. local_inventory(NULL),
  1382. hud(NULL)
  1383. {
  1384. m_cache_doubletap_jump = g_settings->getBool("doubletap_jump");
  1385. m_cache_enable_node_highlighting = g_settings->getBool("enable_node_highlighting");
  1386. m_cache_enable_clouds = g_settings->getBool("enable_clouds");
  1387. m_cache_enable_particles = g_settings->getBool("enable_particles");
  1388. m_cache_enable_fog = g_settings->getBool("enable_fog");
  1389. m_cache_mouse_sensitivity = g_settings->getFloat("mouse_sensitivity");
  1390. m_repeat_right_click_time = g_settings->getFloat("repeat_rightclick_time");
  1391. m_cache_mouse_sensitivity = rangelim(m_cache_mouse_sensitivity, 0.001, 100.0);
  1392. }
  1393. /****************************************************************************
  1394. MinetestApp Public
  1395. ****************************************************************************/
  1396. Game::~Game()
  1397. {
  1398. delete client;
  1399. delete soundmaker;
  1400. if (!sound_is_dummy)
  1401. delete sound;
  1402. delete server; // deleted first to stop all server threads
  1403. delete hud;
  1404. delete local_inventory;
  1405. delete camera;
  1406. delete quicktune;
  1407. delete eventmgr;
  1408. delete texture_src;
  1409. delete shader_src;
  1410. delete nodedef_manager;
  1411. delete itemdef_manager;
  1412. delete draw_control;
  1413. extendedResourceCleanup();
  1414. }
  1415. bool Game::startup(bool *kill,
  1416. bool random_input,
  1417. InputHandler *input,
  1418. IrrlichtDevice *device,
  1419. const std::string &map_dir,
  1420. const std::string &playername,
  1421. const std::string &password,
  1422. std::string *address, // can change if simple_singleplayer_mode
  1423. u16 port,
  1424. std::wstring *error_message,
  1425. ChatBackend *chat_backend,
  1426. const SubgameSpec &gamespec,
  1427. bool simple_singleplayer_mode)
  1428. {
  1429. // "cache"
  1430. this->device = device;
  1431. this->kill = kill;
  1432. this->error_message = error_message;
  1433. this->random_input = random_input;
  1434. this->input = input;
  1435. this->chat_backend = chat_backend;
  1436. this->simple_singleplayer_mode = simple_singleplayer_mode;
  1437. driver = device->getVideoDriver();
  1438. smgr = device->getSceneManager();
  1439. smgr->getParameters()->setAttribute(scene::OBJ_LOADER_IGNORE_MATERIAL_FILES, true);
  1440. if (!init(map_dir, address, port, gamespec))
  1441. return false;
  1442. if (!createClient(playername, password, address, port, error_message))
  1443. return false;
  1444. return true;
  1445. }
  1446. void Game::run()
  1447. {
  1448. ProfilerGraph graph;
  1449. RunStats stats = { 0 };
  1450. CameraOrientation cam_view = { 0 };
  1451. GameRunData runData = { 0 };
  1452. FpsControl draw_times = { 0 };
  1453. VolatileRunFlags flags = { 0 };
  1454. f32 dtime; // in seconds
  1455. runData.time_from_last_punch = 10.0;
  1456. runData.profiler_max_page = 3;
  1457. runData.update_wielded_item_trigger = true;
  1458. flags.show_chat = true;
  1459. flags.show_hud = true;
  1460. flags.show_debug = g_settings->getBool("show_debug");
  1461. flags.invert_mouse = g_settings->getBool("invert_mouse");
  1462. flags.first_loop_after_window_activation = true;
  1463. /* Clear the profiler */
  1464. Profiler::GraphValues dummyvalues;
  1465. g_profiler->graphGet(dummyvalues);
  1466. draw_times.last_time = device->getTimer()->getTime();
  1467. shader_src->addGlobalConstantSetter(new GameGlobalShaderConstantSetter(
  1468. sky,
  1469. &flags.force_fog_off,
  1470. &runData.fog_range,
  1471. client));
  1472. std::vector<aabb3f> highlight_boxes;
  1473. set_light_table(g_settings->getFloat("display_gamma"));
  1474. while (device->run() && !(*kill || g_gamecallback->shutdown_requested)) {
  1475. /* Must be called immediately after a device->run() call because it
  1476. * uses device->getTimer()->getTime()
  1477. */
  1478. limitFps(&draw_times, &dtime);
  1479. updateStats(&stats, draw_times, dtime);
  1480. updateInteractTimers(&runData, dtime);
  1481. if (!checkConnection())
  1482. break;
  1483. if (!handleCallbacks())
  1484. break;
  1485. processQueues();
  1486. infotext = L"";
  1487. hud->resizeHotbar();
  1488. updateProfilers(runData, stats, draw_times, dtime);
  1489. processUserInput(&flags, &runData, dtime);
  1490. // Update camera before player movement to avoid camera lag of one frame
  1491. updateCameraDirection(&cam_view, &flags);
  1492. updatePlayerControl(cam_view);
  1493. step(&dtime);
  1494. processClientEvents(&cam_view, &runData.damage_flash);
  1495. updateCamera(&flags, draw_times.busy_time, dtime,
  1496. runData.time_from_last_punch);
  1497. updateSound(dtime);
  1498. processPlayerInteraction(highlight_boxes, &runData, dtime,
  1499. flags.show_hud, flags.show_debug);
  1500. updateFrame(highlight_boxes, &graph, &stats, &runData, dtime,
  1501. flags, cam_view);
  1502. updateProfilerGraphs(&graph);
  1503. }
  1504. }
  1505. void Game::shutdown()
  1506. {
  1507. showOverlayMessage(wgettext("Shutting down..."), 0, 0, false);
  1508. if (clouds)
  1509. clouds->drop();
  1510. if (gui_chat_console)
  1511. gui_chat_console->drop();
  1512. if (sky)
  1513. sky->drop();
  1514. /* cleanup menus */
  1515. while (g_menumgr.menuCount() > 0) {
  1516. g_menumgr.m_stack.front()->setVisible(false);
  1517. g_menumgr.deletingMenu(g_menumgr.m_stack.front());
  1518. }
  1519. if (current_formspec) {
  1520. current_formspec->drop();
  1521. current_formspec = NULL;
  1522. }
  1523. chat_backend->addMessage(L"", L"# Disconnected.");
  1524. chat_backend->addMessage(L"", L"");
  1525. if (client) {
  1526. client->Stop();
  1527. while (!client->isShutdown()) {
  1528. assert(texture_src != NULL);
  1529. assert(shader_src != NULL);
  1530. texture_src->processQueue();
  1531. shader_src->processQueue();
  1532. sleep_ms(100);
  1533. }
  1534. }
  1535. }
  1536. /****************************************************************************
  1537. Startup
  1538. ****************************************************************************/
  1539. bool Game::init(
  1540. const std::string &map_dir,
  1541. std::string *address,
  1542. u16 port,
  1543. const SubgameSpec &gamespec)
  1544. {
  1545. showOverlayMessage(wgettext("Loading..."), 0, 0);
  1546. texture_src = createTextureSource(device);
  1547. shader_src = createShaderSource(device);
  1548. itemdef_manager = createItemDefManager();
  1549. nodedef_manager = createNodeDefManager();
  1550. eventmgr = new EventManager();
  1551. quicktune = new QuicktuneShortcutter();
  1552. if (!(texture_src && shader_src && itemdef_manager && nodedef_manager
  1553. && eventmgr && quicktune))
  1554. return false;
  1555. if (!initSound())
  1556. return false;
  1557. // Create a server if not connecting to an existing one
  1558. if (*address == "") {
  1559. if (!createSingleplayerServer(map_dir, gamespec, port, address))
  1560. return false;
  1561. }
  1562. return true;
  1563. }
  1564. bool Game::initSound()
  1565. {
  1566. #if USE_SOUND
  1567. if (g_settings->getBool("enable_sound")) {
  1568. infostream << "Attempting to use OpenAL audio" << std::endl;
  1569. sound = createOpenALSoundManager(&soundfetcher);
  1570. if (!sound)
  1571. infostream << "Failed to initialize OpenAL audio" << std::endl;
  1572. } else
  1573. infostream << "Sound disabled." << std::endl;
  1574. #endif
  1575. if (!sound) {
  1576. infostream << "Using dummy audio." << std::endl;
  1577. sound = &dummySoundManager;
  1578. sound_is_dummy = true;
  1579. }
  1580. soundmaker = new SoundMaker(sound, nodedef_manager);
  1581. if (!soundmaker)
  1582. return false;
  1583. soundmaker->registerReceiver(eventmgr);
  1584. return true;
  1585. }
  1586. bool Game::createSingleplayerServer(const std::string map_dir,
  1587. const SubgameSpec &gamespec, u16 port, std::string *address)
  1588. {
  1589. showOverlayMessage(wgettext("Creating server..."), 0, 5);
  1590. std::string bind_str = g_settings->get("bind_address");
  1591. Address bind_addr(0, 0, 0, 0, port);
  1592. if (g_settings->getBool("ipv6_server")) {
  1593. bind_addr.setAddress((IPv6AddressBytes *) NULL);
  1594. }
  1595. try {
  1596. bind_addr.Resolve(bind_str.c_str());
  1597. } catch (ResolveError &e) {
  1598. infostream << "Resolving bind address \"" << bind_str
  1599. << "\" failed: " << e.what()
  1600. << " -- Listening on all addresses." << std::endl;
  1601. }
  1602. if (bind_addr.isIPv6() && !g_settings->getBool("enable_ipv6")) {
  1603. *error_message = L"Unable to listen on " +
  1604. narrow_to_wide(bind_addr.serializeString()) +
  1605. L" because IPv6 is disabled";
  1606. errorstream << wide_to_narrow(*error_message) << std::endl;
  1607. return false;
  1608. }
  1609. server = new Server(map_dir, gamespec, simple_singleplayer_mode,
  1610. bind_addr.isIPv6());
  1611. server->start(bind_addr);
  1612. return true;
  1613. }
  1614. bool Game::createClient(const std::string &playername,
  1615. const std::string &password, std::string *address, u16 port,
  1616. std::wstring *error_message)
  1617. {
  1618. showOverlayMessage(wgettext("Creating client..."), 0, 10);
  1619. draw_control = new MapDrawControl;
  1620. if (!draw_control)
  1621. return false;
  1622. bool could_connect, connect_aborted;
  1623. if (!connectToServer(playername, password, address, port,
  1624. &could_connect, &connect_aborted))
  1625. return false;
  1626. if (!could_connect) {
  1627. if (*error_message == L"" && !connect_aborted) {
  1628. // Should not happen if error messages are set properly
  1629. *error_message = L"Connection failed for unknown reason";
  1630. errorstream << wide_to_narrow(*error_message) << std::endl;
  1631. }
  1632. return false;
  1633. }
  1634. if (!getServerContent(&connect_aborted)) {
  1635. if (*error_message == L"" && !connect_aborted) {
  1636. // Should not happen if error messages are set properly
  1637. *error_message = L"Connection failed for unknown reason";
  1638. errorstream << wide_to_narrow(*error_message) << std::endl;
  1639. }
  1640. return false;
  1641. }
  1642. // Update cached textures, meshes and materials
  1643. client->afterContentReceived(device, g_fontengine->getFont());
  1644. /* Camera
  1645. */
  1646. camera = new Camera(smgr, *draw_control, gamedef);
  1647. if (!camera || !camera->successfullyCreated(*error_message))
  1648. return false;
  1649. /* Clouds
  1650. */
  1651. if (m_cache_enable_clouds) {
  1652. clouds = new Clouds(smgr->getRootSceneNode(), smgr, -1, time(0));
  1653. if (!clouds) {
  1654. *error_message = L"Memory allocation error";
  1655. *error_message += narrow_to_wide(" (clouds)");
  1656. errorstream << wide_to_narrow(*error_message) << std::endl;
  1657. return false;
  1658. }
  1659. }
  1660. /* Skybox
  1661. */
  1662. sky = new Sky(smgr->getRootSceneNode(), smgr, -1, texture_src);
  1663. skybox = NULL; // This is used/set later on in the main run loop
  1664. local_inventory = new Inventory(itemdef_manager);
  1665. if (!(sky && local_inventory)) {
  1666. *error_message = L"Memory allocation error";
  1667. *error_message += narrow_to_wide(" (sky or local inventory)");
  1668. errorstream << wide_to_narrow(*error_message) << std::endl;
  1669. return false;
  1670. }
  1671. /* Pre-calculated values
  1672. */
  1673. video::ITexture *t = texture_src->getTexture("crack_anylength.png");
  1674. if (t) {
  1675. v2u32 size = t->getOriginalSize();
  1676. crack_animation_length = size.Y / size.X;
  1677. } else {
  1678. crack_animation_length = 5;
  1679. }
  1680. if (!initGui(error_message))
  1681. return false;
  1682. /* Set window caption
  1683. */
  1684. core::stringw str = L"Minetest [";
  1685. str += driver->getName();
  1686. str += "]";
  1687. device->setWindowCaption(str.c_str());
  1688. LocalPlayer *player = client->getEnv().getLocalPlayer();
  1689. player->hurt_tilt_timer = 0;
  1690. player->hurt_tilt_strength = 0;
  1691. hud = new Hud(driver, smgr, guienv, gamedef, player, local_inventory);
  1692. if (!hud) {
  1693. *error_message = L"Memory error: could not create HUD";
  1694. errorstream << wide_to_narrow(*error_message) << std::endl;
  1695. return false;
  1696. }
  1697. return true;
  1698. }
  1699. bool Game::initGui(std::wstring *error_message)
  1700. {
  1701. // First line of debug text
  1702. guitext = guienv->addStaticText(
  1703. L"Minetest",
  1704. core::rect<s32>(0, 0, 0, 0),
  1705. false, false, guiroot);
  1706. // Second line of debug text
  1707. guitext2 = guienv->addStaticText(
  1708. L"",
  1709. core::rect<s32>(0, 0, 0, 0),
  1710. false, false, guiroot);
  1711. // At the middle of the screen
  1712. // Object infos are shown in this
  1713. guitext_info = guienv->addStaticText(
  1714. L"",
  1715. core::rect<s32>(0, 0, 400, g_fontengine->getTextHeight() * 5 + 5) + v2s32(100, 200),
  1716. false, true, guiroot);
  1717. // Status text (displays info when showing and hiding GUI stuff, etc.)
  1718. guitext_status = guienv->addStaticText(
  1719. L"<Status>",
  1720. core::rect<s32>(0, 0, 0, 0),
  1721. false, false, guiroot);
  1722. guitext_status->setVisible(false);
  1723. // Chat text
  1724. guitext_chat = guienv->addStaticText(
  1725. L"",
  1726. core::rect<s32>(0, 0, 0, 0),
  1727. //false, false); // Disable word wrap as of now
  1728. false, true, guiroot);
  1729. // Remove stale "recent" chat messages from previous connections
  1730. chat_backend->clearRecentChat();
  1731. // Chat backend and console
  1732. gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
  1733. -1, chat_backend, client);
  1734. if (!gui_chat_console) {
  1735. *error_message = L"Could not allocate memory for chat console";
  1736. errorstream << wide_to_narrow(*error_message) << std::endl;
  1737. return false;
  1738. }
  1739. // Profiler text (size is updated when text is updated)
  1740. guitext_profiler = guienv->addStaticText(
  1741. L"<Profiler>",
  1742. core::rect<s32>(0, 0, 0, 0),
  1743. false, false, guiroot);
  1744. guitext_profiler->setBackgroundColor(video::SColor(120, 0, 0, 0));
  1745. guitext_profiler->setVisible(false);
  1746. guitext_profiler->setWordWrap(true);
  1747. #ifdef HAVE_TOUCHSCREENGUI
  1748. if (g_touchscreengui)
  1749. g_touchscreengui->init(texture_src, porting::getDisplayDensity());
  1750. #endif
  1751. return true;
  1752. }
  1753. bool Game::connectToServer(const std::string &playername,
  1754. const std::string &password, std::string *address, u16 port,
  1755. bool *connect_ok, bool *aborted)
  1756. {
  1757. *connect_ok = false; // Let's not be overly optimistic
  1758. *aborted = false;
  1759. bool local_server_mode = false;
  1760. showOverlayMessage(wgettext("Resolving address..."), 0, 15);
  1761. Address connect_address(0, 0, 0, 0, port);
  1762. try {
  1763. connect_address.Resolve(address->c_str());
  1764. if (connect_address.isZero()) { // i.e. INADDR_ANY, IN6ADDR_ANY
  1765. //connect_address.Resolve("localhost");
  1766. if (connect_address.isIPv6()) {
  1767. IPv6AddressBytes addr_bytes;
  1768. addr_bytes.bytes[15] = 1;
  1769. connect_address.setAddress(&addr_bytes);
  1770. } else {
  1771. connect_address.setAddress(127, 0, 0, 1);
  1772. }
  1773. local_server_mode = true;
  1774. }
  1775. } catch (ResolveError &e) {
  1776. *error_message = L"Couldn't resolve address: " + narrow_to_wide(e.what());
  1777. errorstream << wide_to_narrow(*error_message) << std::endl;
  1778. return false;
  1779. }
  1780. if (connect_address.isIPv6() && !g_settings->getBool("enable_ipv6")) {
  1781. *error_message = L"Unable to connect to " +
  1782. narrow_to_wide(connect_address.serializeString()) +
  1783. L" because IPv6 is disabled";
  1784. errorstream << wide_to_narrow(*error_message) << std::endl;
  1785. return false;
  1786. }
  1787. client = new Client(device,
  1788. playername.c_str(), password,
  1789. *draw_control, texture_src, shader_src,
  1790. itemdef_manager, nodedef_manager, sound, eventmgr,
  1791. connect_address.isIPv6());
  1792. if (!client)
  1793. return false;
  1794. gamedef = client; // Client acts as our GameDef
  1795. infostream << "Connecting to server at ";
  1796. connect_address.print(&infostream);
  1797. infostream << std::endl;
  1798. client->connect(connect_address, *address,
  1799. simple_singleplayer_mode || local_server_mode);
  1800. /*
  1801. Wait for server to accept connection
  1802. */
  1803. try {
  1804. input->clear();
  1805. FpsControl fps_control = { 0 };
  1806. f32 dtime; // in seconds
  1807. while (device->run()) {
  1808. limitFps(&fps_control, &dtime);
  1809. // Update client and server
  1810. client->step(dtime);
  1811. if (server != NULL)
  1812. server->step(dtime);
  1813. // End condition
  1814. if (client->getState() == LC_Init) {
  1815. *connect_ok = true;
  1816. break;
  1817. }
  1818. // Break conditions
  1819. if (client->accessDenied()) {
  1820. *error_message = L"Access denied. Reason: "
  1821. + client->accessDeniedReason();
  1822. errorstream << wide_to_narrow(*error_message) << std::endl;
  1823. break;
  1824. }
  1825. if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
  1826. *aborted = true;
  1827. infostream << "Connect aborted [Escape]" << std::endl;
  1828. break;
  1829. }
  1830. // Update status
  1831. showOverlayMessage(wgettext("Connecting to server..."), dtime, 20);
  1832. }
  1833. } catch (con::PeerNotFoundException &e) {
  1834. // TODO: Should something be done here? At least an info/error
  1835. // message?
  1836. return false;
  1837. }
  1838. return true;
  1839. }
  1840. bool Game::getServerContent(bool *aborted)
  1841. {
  1842. input->clear();
  1843. FpsControl fps_control = { 0 };
  1844. f32 dtime; // in seconds
  1845. while (device->run()) {
  1846. limitFps(&fps_control, &dtime);
  1847. // Update client and server
  1848. client->step(dtime);
  1849. if (server != NULL)
  1850. server->step(dtime);
  1851. // End condition
  1852. if (client->mediaReceived() && client->itemdefReceived() &&
  1853. client->nodedefReceived()) {
  1854. break;
  1855. }
  1856. // Error conditions
  1857. if (client->accessDenied()) {
  1858. *error_message = L"Access denied. Reason: "
  1859. + client->accessDeniedReason();
  1860. errorstream << wide_to_narrow(*error_message) << std::endl;
  1861. return false;
  1862. }
  1863. if (client->getState() < LC_Init) {
  1864. *error_message = L"Client disconnected";
  1865. errorstream << wide_to_narrow(*error_message) << std::endl;
  1866. return false;
  1867. }
  1868. if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
  1869. *aborted = true;
  1870. infostream << "Connect aborted [Escape]" << std::endl;
  1871. return false;
  1872. }
  1873. // Display status
  1874. int progress = 25;
  1875. if (!client->itemdefReceived()) {
  1876. const wchar_t *text = wgettext("Item definitions...");
  1877. progress = 25;
  1878. draw_load_screen(text, device, guienv, dtime, progress);
  1879. delete[] text;
  1880. } else if (!client->nodedefReceived()) {
  1881. const wchar_t *text = wgettext("Node definitions...");
  1882. progress = 30;
  1883. draw_load_screen(text, device, guienv, dtime, progress);
  1884. delete[] text;
  1885. } else {
  1886. std::stringstream message;
  1887. message.precision(3);
  1888. message << gettext("Media...");
  1889. if ((USE_CURL == 0) ||
  1890. (!g_settings->getBool("enable_remote_media_server"))) {
  1891. float cur = client->getCurRate();
  1892. std::string cur_unit = gettext(" KB/s");
  1893. if (cur > 900) {
  1894. cur /= 1024.0;
  1895. cur_unit = gettext(" MB/s");
  1896. }
  1897. message << " ( " << cur << cur_unit << " )";
  1898. }
  1899. progress = 30 + client->mediaReceiveProgress() * 35 + 0.5;
  1900. draw_load_screen(narrow_to_wide(message.str()), device,
  1901. guienv, dtime, progress);
  1902. }
  1903. }
  1904. return true;
  1905. }
  1906. /****************************************************************************
  1907. Run
  1908. ****************************************************************************/
  1909. inline void Game::updateInteractTimers(GameRunData *args, f32 dtime)
  1910. {
  1911. if (args->nodig_delay_timer >= 0)
  1912. args->nodig_delay_timer -= dtime;
  1913. if (args->object_hit_delay_timer >= 0)
  1914. args->object_hit_delay_timer -= dtime;
  1915. args->time_from_last_punch += dtime;
  1916. }
  1917. /* returns false if game should exit, otherwise true
  1918. */
  1919. inline bool Game::checkConnection()
  1920. {
  1921. if (client->accessDenied()) {
  1922. *error_message = L"Access denied. Reason: "
  1923. + client->accessDeniedReason();
  1924. errorstream << wide_to_narrow(*error_message) << std::endl;
  1925. return false;
  1926. }
  1927. return true;
  1928. }
  1929. /* returns false if game should exit, otherwise true
  1930. */
  1931. inline bool Game::handleCallbacks()
  1932. {
  1933. if (g_gamecallback->disconnect_requested) {
  1934. g_gamecallback->disconnect_requested = false;
  1935. return false;
  1936. }
  1937. if (g_gamecallback->changepassword_requested) {
  1938. (new GUIPasswordChange(guienv, guiroot, -1,
  1939. &g_menumgr, client))->drop();
  1940. g_gamecallback->changepassword_requested = false;
  1941. }
  1942. if (g_gamecallback->changevolume_requested) {
  1943. (new GUIVolumeChange(guienv, guiroot, -1,
  1944. &g_menumgr, client))->drop();
  1945. g_gamecallback->changevolume_requested = false;
  1946. }
  1947. if (g_gamecallback->keyconfig_requested) {
  1948. (new GUIKeyChangeMenu(guienv, guiroot, -1,
  1949. &g_menumgr))->drop();
  1950. g_gamecallback->keyconfig_requested = false;
  1951. }
  1952. if (g_gamecallback->keyconfig_changed) {
  1953. keycache.populate(); // update the cache with new settings
  1954. g_gamecallback->keyconfig_changed = false;
  1955. }
  1956. return true;
  1957. }
  1958. void Game::processQueues()
  1959. {
  1960. texture_src->processQueue();
  1961. itemdef_manager->processQueue(gamedef);
  1962. shader_src->processQueue();
  1963. }
  1964. void Game::updateProfilers(const GameRunData &run_data, const RunStats &stats,
  1965. const FpsControl &draw_times, f32 dtime)
  1966. {
  1967. float profiler_print_interval =
  1968. g_settings->getFloat("profiler_print_interval");
  1969. bool print_to_log = true;
  1970. if (profiler_print_interval == 0) {
  1971. print_to_log = false;
  1972. profiler_print_interval = 5;
  1973. }
  1974. if (profiler_interval.step(dtime, profiler_print_interval)) {
  1975. if (print_to_log) {
  1976. infostream << "Profiler:" << std::endl;
  1977. g_profiler->print(infostream);
  1978. }
  1979. update_profiler_gui(guitext_profiler, g_fontengine,
  1980. run_data.profiler_current_page, run_data.profiler_max_page,
  1981. driver->getScreenSize().Height);
  1982. g_profiler->clear();
  1983. }
  1984. addProfilerGraphs(stats, draw_times, dtime);
  1985. }
  1986. void Game::addProfilerGraphs(const RunStats &stats,
  1987. const FpsControl &draw_times, f32 dtime)
  1988. {
  1989. g_profiler->graphAdd("mainloop_other",
  1990. draw_times.busy_time / 1000.0f - stats.drawtime / 1000.0f);
  1991. if (draw_times.sleep_time != 0)
  1992. g_profiler->graphAdd("mainloop_sleep", draw_times.sleep_time / 1000.0f);
  1993. g_profiler->graphAdd("mainloop_dtime", dtime);
  1994. g_profiler->add("Elapsed time", dtime);
  1995. g_profiler->avg("FPS", 1. / dtime);
  1996. }
  1997. void Game::updateStats(RunStats *stats, const FpsControl &draw_times,
  1998. f32 dtime)
  1999. {
  2000. f32 jitter;
  2001. Jitter *jp;
  2002. /* Time average and jitter calculation
  2003. */
  2004. jp = &stats->dtime_jitter;
  2005. jp->avg = jp->avg * 0.96 + dtime * 0.04;
  2006. jitter = dtime - jp->avg;
  2007. if (jitter > jp->max)
  2008. jp->max = jitter;
  2009. jp->counter += dtime;
  2010. if (jp->counter > 0.0) {
  2011. jp->counter -= 3.0;
  2012. jp->max_sample = jp->max;
  2013. jp->max_fraction = jp->max_sample / (jp->avg + 0.001);
  2014. jp->max = 0.0;
  2015. }
  2016. /* Busytime average and jitter calculation
  2017. */
  2018. jp = &stats->busy_time_jitter;
  2019. jp->avg = jp->avg + draw_times.busy_time * 0.02;
  2020. jitter = draw_times.busy_time - jp->avg;
  2021. if (jitter > jp->max)
  2022. jp->max = jitter;
  2023. if (jitter < jp->min)
  2024. jp->min = jitter;
  2025. jp->counter += dtime;
  2026. if (jp->counter > 0.0) {
  2027. jp->counter -= 3.0;
  2028. jp->max_sample = jp->max;
  2029. jp->min_sample = jp->min;
  2030. jp->max = 0.0;
  2031. jp->min = 0.0;
  2032. }
  2033. }
  2034. /****************************************************************************
  2035. Input handling
  2036. ****************************************************************************/
  2037. void Game::processUserInput(VolatileRunFlags *flags,
  2038. GameRunData *interact_args, f32 dtime)
  2039. {
  2040. // Reset input if window not active or some menu is active
  2041. if (device->isWindowActive() == false
  2042. || noMenuActive() == false
  2043. || guienv->hasFocus(gui_chat_console)) {
  2044. input->clear();
  2045. }
  2046. if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) {
  2047. gui_chat_console->closeConsoleAtOnce();
  2048. }
  2049. // Input handler step() (used by the random input generator)
  2050. input->step(dtime);
  2051. #ifdef HAVE_TOUCHSCREENGUI
  2052. if (g_touchscreengui) {
  2053. g_touchscreengui->step(dtime);
  2054. }
  2055. #endif
  2056. #ifdef __ANDROID__
  2057. if (current_formspec != 0)
  2058. current_formspec->getAndroidUIInput();
  2059. #endif
  2060. // Increase timer for double tap of "keymap_jump"
  2061. if (m_cache_doubletap_jump && interact_args->jump_timer <= 0.2)
  2062. interact_args->jump_timer += dtime;
  2063. processKeyboardInput(
  2064. flags,
  2065. &interact_args->statustext_time,
  2066. &interact_args->jump_timer,
  2067. &interact_args->reset_jump_timer,
  2068. &interact_args->profiler_current_page,
  2069. interact_args->profiler_max_page);
  2070. processItemSelection(&interact_args->new_playeritem);
  2071. }
  2072. void Game::processKeyboardInput(VolatileRunFlags *flags,
  2073. float *statustext_time,
  2074. float *jump_timer,
  2075. bool *reset_jump_timer,
  2076. u32 *profiler_current_page,
  2077. u32 profiler_max_page)
  2078. {
  2079. //TimeTaker tt("process kybd input", NULL, PRECISION_NANO);
  2080. if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DROP])) {
  2081. dropSelectedItem();
  2082. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INVENTORY])) {
  2083. openInventory();
  2084. } else if (input->wasKeyDown(EscapeKey) || input->wasKeyDown(CancelKey)) {
  2085. show_pause_menu(&current_formspec, client, gamedef, texture_src, device,
  2086. simple_singleplayer_mode);
  2087. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CHAT])) {
  2088. show_chat_menu(&current_formspec, client, gamedef, texture_src, device,
  2089. client, "");
  2090. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CMD])) {
  2091. show_chat_menu(&current_formspec, client, gamedef, texture_src, device,
  2092. client, "/");
  2093. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CONSOLE])) {
  2094. openConsole();
  2095. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FREEMOVE])) {
  2096. toggleFreeMove(statustext_time);
  2097. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP])) {
  2098. toggleFreeMoveAlt(statustext_time, jump_timer);
  2099. *reset_jump_timer = true;
  2100. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_FASTMOVE])) {
  2101. toggleFast(statustext_time);
  2102. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_NOCLIP])) {
  2103. toggleNoClip(statustext_time);
  2104. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_SCREENSHOT])) {
  2105. client->makeScreenshot(device);
  2106. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_HUD])) {
  2107. toggleHud(statustext_time, &flags->show_hud);
  2108. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_CHAT])) {
  2109. toggleChat(statustext_time, &flags->show_chat);
  2110. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_FORCE_FOG_OFF])) {
  2111. toggleFog(statustext_time, &flags->force_fog_off);
  2112. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_UPDATE_CAMERA])) {
  2113. toggleUpdateCamera(statustext_time, &flags->disable_camera_update);
  2114. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_DEBUG])) {
  2115. toggleDebug(statustext_time, &flags->show_debug, &flags->show_profiler_graph);
  2116. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_TOGGLE_PROFILER])) {
  2117. toggleProfiler(statustext_time, profiler_current_page, profiler_max_page);
  2118. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_INCREASE_VIEWING_RANGE])) {
  2119. increaseViewRange(statustext_time);
  2120. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DECREASE_VIEWING_RANGE])) {
  2121. decreaseViewRange(statustext_time);
  2122. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_RANGESELECT])) {
  2123. toggleFullViewRange(statustext_time);
  2124. } else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_NEXT]))
  2125. quicktune->next();
  2126. else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_PREV]))
  2127. quicktune->prev();
  2128. else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_INC]))
  2129. quicktune->inc();
  2130. else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_QUICKTUNE_DEC]))
  2131. quicktune->dec();
  2132. else if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_DEBUG_STACKS])) {
  2133. // Print debug stacks
  2134. dstream << "-----------------------------------------"
  2135. << std::endl;
  2136. dstream << DTIME << "Printing debug stacks:" << std::endl;
  2137. dstream << "-----------------------------------------"
  2138. << std::endl;
  2139. debug_stacks_print();
  2140. }
  2141. if (!input->isKeyDown(getKeySetting("keymap_jump")) && *reset_jump_timer) {
  2142. *reset_jump_timer = false;
  2143. *jump_timer = 0.0;
  2144. }
  2145. //tt.stop();
  2146. if (quicktune->hasMessage()) {
  2147. std::string msg = quicktune->getMessage();
  2148. statustext = narrow_to_wide(msg);
  2149. *statustext_time = 0;
  2150. }
  2151. }
  2152. void Game::processItemSelection(u16 *new_playeritem)
  2153. {
  2154. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2155. /* Item selection using mouse wheel
  2156. */
  2157. *new_playeritem = client->getPlayerItem();
  2158. s32 wheel = input->getMouseWheel();
  2159. u16 max_item = MYMIN(PLAYER_INVENTORY_SIZE - 1,
  2160. player->hud_hotbar_itemcount - 1);
  2161. if (wheel < 0)
  2162. *new_playeritem = *new_playeritem < max_item ? *new_playeritem + 1 : 0;
  2163. else if (wheel > 0)
  2164. *new_playeritem = *new_playeritem > 0 ? *new_playeritem - 1 : max_item;
  2165. // else wheel == 0
  2166. /* Item selection using keyboard
  2167. */
  2168. for (u16 i = 0; i < 10; i++) {
  2169. static const KeyPress *item_keys[10] = {
  2170. NumberKey + 1, NumberKey + 2, NumberKey + 3, NumberKey + 4,
  2171. NumberKey + 5, NumberKey + 6, NumberKey + 7, NumberKey + 8,
  2172. NumberKey + 9, NumberKey + 0,
  2173. };
  2174. if (input->wasKeyDown(*item_keys[i])) {
  2175. if (i < PLAYER_INVENTORY_SIZE && i < player->hud_hotbar_itemcount) {
  2176. *new_playeritem = i;
  2177. infostream << "Selected item: " << new_playeritem << std::endl;
  2178. }
  2179. break;
  2180. }
  2181. }
  2182. }
  2183. void Game::dropSelectedItem()
  2184. {
  2185. IDropAction *a = new IDropAction();
  2186. a->count = 0;
  2187. a->from_inv.setCurrentPlayer();
  2188. a->from_list = "main";
  2189. a->from_i = client->getPlayerItem();
  2190. client->inventoryAction(a);
  2191. }
  2192. void Game::openInventory()
  2193. {
  2194. infostream << "the_game: " << "Launching inventory" << std::endl;
  2195. PlayerInventoryFormSource *fs_src = new PlayerInventoryFormSource(client);
  2196. TextDest *txt_dst = new TextDestPlayerInventory(client);
  2197. create_formspec_menu(&current_formspec, client, gamedef, texture_src,
  2198. device, fs_src, txt_dst, client);
  2199. InventoryLocation inventoryloc;
  2200. inventoryloc.setCurrentPlayer();
  2201. current_formspec->setFormSpec(fs_src->getForm(), inventoryloc);
  2202. }
  2203. void Game::openConsole()
  2204. {
  2205. if (!gui_chat_console->isOpenInhibited()) {
  2206. // Open up to over half of the screen
  2207. gui_chat_console->openConsole(0.6);
  2208. guienv->setFocus(gui_chat_console);
  2209. }
  2210. }
  2211. void Game::toggleFreeMove(float *statustext_time)
  2212. {
  2213. static const wchar_t *msg[] = { L"free_move disabled", L"free_move enabled" };
  2214. bool free_move = !g_settings->getBool("free_move");
  2215. g_settings->set("free_move", bool_to_cstr(free_move));
  2216. *statustext_time = 0;
  2217. statustext = msg[free_move];
  2218. if (free_move && !client->checkPrivilege("fly"))
  2219. statustext += L" (note: no 'fly' privilege)";
  2220. }
  2221. void Game::toggleFreeMoveAlt(float *statustext_time, float *jump_timer)
  2222. {
  2223. if (m_cache_doubletap_jump && *jump_timer < 0.2f)
  2224. toggleFreeMove(statustext_time);
  2225. }
  2226. void Game::toggleFast(float *statustext_time)
  2227. {
  2228. static const wchar_t *msg[] = { L"fast_move disabled", L"fast_move enabled" };
  2229. bool fast_move = !g_settings->getBool("fast_move");
  2230. g_settings->set("fast_move", bool_to_cstr(fast_move));
  2231. *statustext_time = 0;
  2232. statustext = msg[fast_move];
  2233. if (fast_move && !client->checkPrivilege("fast"))
  2234. statustext += L" (note: no 'fast' privilege)";
  2235. }
  2236. void Game::toggleNoClip(float *statustext_time)
  2237. {
  2238. static const wchar_t *msg[] = { L"noclip disabled", L"noclip enabled" };
  2239. bool noclip = !g_settings->getBool("noclip");
  2240. g_settings->set("noclip", bool_to_cstr(noclip));
  2241. *statustext_time = 0;
  2242. statustext = msg[noclip];
  2243. if (noclip && !client->checkPrivilege("noclip"))
  2244. statustext += L" (note: no 'noclip' privilege)";
  2245. }
  2246. void Game::toggleChat(float *statustext_time, bool *flag)
  2247. {
  2248. static const wchar_t *msg[] = { L"Chat hidden", L"Chat shown" };
  2249. *flag = !*flag;
  2250. *statustext_time = 0;
  2251. statustext = msg[*flag];
  2252. }
  2253. void Game::toggleHud(float *statustext_time, bool *flag)
  2254. {
  2255. static const wchar_t *msg[] = { L"HUD hidden", L"HUD shown" };
  2256. *flag = !*flag;
  2257. *statustext_time = 0;
  2258. statustext = msg[*flag];
  2259. if (g_settings->getBool("enable_node_highlighting"))
  2260. client->setHighlighted(client->getHighlighted(), *flag);
  2261. }
  2262. void Game::toggleFog(float *statustext_time, bool *flag)
  2263. {
  2264. static const wchar_t *msg[] = { L"Fog enabled", L"Fog disabled" };
  2265. *flag = !*flag;
  2266. *statustext_time = 0;
  2267. statustext = msg[*flag];
  2268. }
  2269. void Game::toggleDebug(float *statustext_time, bool *show_debug,
  2270. bool *show_profiler_graph)
  2271. {
  2272. // Initial / 3x toggle: Chat only
  2273. // 1x toggle: Debug text with chat
  2274. // 2x toggle: Debug text with profiler graph
  2275. if (!*show_debug) {
  2276. *show_debug = true;
  2277. *show_profiler_graph = false;
  2278. statustext = L"Debug info shown";
  2279. } else if (*show_profiler_graph) {
  2280. *show_debug = false;
  2281. *show_profiler_graph = false;
  2282. statustext = L"Debug info and profiler graph hidden";
  2283. } else {
  2284. *show_profiler_graph = true;
  2285. statustext = L"Profiler graph shown";
  2286. }
  2287. *statustext_time = 0;
  2288. }
  2289. void Game::toggleUpdateCamera(float *statustext_time, bool *flag)
  2290. {
  2291. static const wchar_t *msg[] = {
  2292. L"Camera update enabled",
  2293. L"Camera update disabled"
  2294. };
  2295. *flag = !*flag;
  2296. *statustext_time = 0;
  2297. statustext = msg[*flag];
  2298. }
  2299. void Game::toggleProfiler(float *statustext_time, u32 *profiler_current_page,
  2300. u32 profiler_max_page)
  2301. {
  2302. *profiler_current_page = (*profiler_current_page + 1) % (profiler_max_page + 1);
  2303. // FIXME: This updates the profiler with incomplete values
  2304. update_profiler_gui(guitext_profiler, g_fontengine, *profiler_current_page,
  2305. profiler_max_page, driver->getScreenSize().Height);
  2306. if (*profiler_current_page != 0) {
  2307. std::wstringstream sstr;
  2308. sstr << "Profiler shown (page " << *profiler_current_page
  2309. << " of " << profiler_max_page << ")";
  2310. statustext = sstr.str();
  2311. } else {
  2312. statustext = L"Profiler hidden";
  2313. }
  2314. *statustext_time = 0;
  2315. }
  2316. void Game::increaseViewRange(float *statustext_time)
  2317. {
  2318. s16 range = g_settings->getS16("viewing_range_nodes_min");
  2319. s16 range_new = range + 10;
  2320. g_settings->set("viewing_range_nodes_min", itos(range_new));
  2321. statustext = narrow_to_wide("Minimum viewing range changed to "
  2322. + itos(range_new));
  2323. *statustext_time = 0;
  2324. }
  2325. void Game::decreaseViewRange(float *statustext_time)
  2326. {
  2327. s16 range = g_settings->getS16("viewing_range_nodes_min");
  2328. s16 range_new = range - 10;
  2329. if (range_new < 0)
  2330. range_new = range;
  2331. g_settings->set("viewing_range_nodes_min", itos(range_new));
  2332. statustext = narrow_to_wide("Minimum viewing range changed to "
  2333. + itos(range_new));
  2334. *statustext_time = 0;
  2335. }
  2336. void Game::toggleFullViewRange(float *statustext_time)
  2337. {
  2338. static const wchar_t *msg[] = {
  2339. L"Disabled full viewing range",
  2340. L"Enabled full viewing range"
  2341. };
  2342. draw_control->range_all = !draw_control->range_all;
  2343. infostream << msg[draw_control->range_all] << std::endl;
  2344. statustext = msg[draw_control->range_all];
  2345. *statustext_time = 0;
  2346. }
  2347. void Game::updateCameraDirection(CameraOrientation *cam,
  2348. VolatileRunFlags *flags)
  2349. {
  2350. if ((device->isWindowActive() && noMenuActive()) || random_input) {
  2351. #ifndef __ANDROID__
  2352. if (!random_input) {
  2353. // Mac OSX gets upset if this is set every frame
  2354. if (device->getCursorControl()->isVisible())
  2355. device->getCursorControl()->setVisible(false);
  2356. }
  2357. #endif
  2358. if (flags->first_loop_after_window_activation)
  2359. flags->first_loop_after_window_activation = false;
  2360. else
  2361. updateCameraOrientation(cam, *flags);
  2362. input->setMousePos((driver->getScreenSize().Width / 2),
  2363. (driver->getScreenSize().Height / 2));
  2364. } else {
  2365. #ifndef ANDROID
  2366. // Mac OSX gets upset if this is set every frame
  2367. if (device->getCursorControl()->isVisible() == false)
  2368. device->getCursorControl()->setVisible(true);
  2369. #endif
  2370. if (!flags->first_loop_after_window_activation)
  2371. flags->first_loop_after_window_activation = true;
  2372. }
  2373. }
  2374. void Game::updateCameraOrientation(CameraOrientation *cam,
  2375. const VolatileRunFlags &flags)
  2376. {
  2377. #ifdef HAVE_TOUCHSCREENGUI
  2378. if (g_touchscreengui) {
  2379. cam->camera_yaw = g_touchscreengui->getYaw();
  2380. cam->camera_pitch = g_touchscreengui->getPitch();
  2381. } else {
  2382. #endif
  2383. s32 dx = input->getMousePos().X - (driver->getScreenSize().Width / 2);
  2384. s32 dy = input->getMousePos().Y - (driver->getScreenSize().Height / 2);
  2385. if (flags.invert_mouse
  2386. || camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT) {
  2387. dy = -dy;
  2388. }
  2389. cam->camera_yaw -= dx * m_cache_mouse_sensitivity;
  2390. cam->camera_pitch += dy * m_cache_mouse_sensitivity;
  2391. #ifdef HAVE_TOUCHSCREENGUI
  2392. }
  2393. #endif
  2394. cam->camera_pitch = rangelim(cam->camera_pitch, -89.5, 89.5);
  2395. }
  2396. void Game::updatePlayerControl(const CameraOrientation &cam)
  2397. {
  2398. //TimeTaker tt("update player control", NULL, PRECISION_NANO);
  2399. PlayerControl control(
  2400. input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]),
  2401. input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]),
  2402. input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]),
  2403. input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]),
  2404. input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]),
  2405. input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]),
  2406. input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]),
  2407. input->getLeftState(),
  2408. input->getRightState(),
  2409. cam.camera_pitch,
  2410. cam.camera_yaw
  2411. );
  2412. client->setPlayerControl(control);
  2413. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2414. player->keyPressed =
  2415. ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_FORWARD]) & 0x1) << 0) |
  2416. ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_BACKWARD]) & 0x1) << 1) |
  2417. ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_LEFT]) & 0x1) << 2) |
  2418. ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_RIGHT]) & 0x1) << 3) |
  2419. ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_JUMP]) & 0x1) << 4) |
  2420. ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SPECIAL1]) & 0x1) << 5) |
  2421. ( (u32)(input->isKeyDown(keycache.key[KeyCache::KEYMAP_ID_SNEAK]) & 0x1) << 6) |
  2422. ( (u32)(input->getLeftState() & 0x1) << 7) |
  2423. ( (u32)(input->getRightState() & 0x1) << 8
  2424. );
  2425. //tt.stop();
  2426. }
  2427. inline void Game::step(f32 *dtime)
  2428. {
  2429. bool can_be_and_is_paused =
  2430. (simple_singleplayer_mode && g_menumgr.pausesGame());
  2431. if (can_be_and_is_paused) { // This is for a singleplayer server
  2432. *dtime = 0; // No time passes
  2433. } else {
  2434. if (server != NULL) {
  2435. //TimeTaker timer("server->step(dtime)");
  2436. server->step(*dtime);
  2437. }
  2438. //TimeTaker timer("client.step(dtime)");
  2439. client->step(*dtime);
  2440. }
  2441. }
  2442. void Game::processClientEvents(CameraOrientation *cam, float *damage_flash)
  2443. {
  2444. ClientEvent event = client->getClientEvent();
  2445. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2446. for ( ; event.type != CE_NONE; event = client->getClientEvent()) {
  2447. if (event.type == CE_PLAYER_DAMAGE &&
  2448. client->getHP() != 0) {
  2449. //u16 damage = event.player_damage.amount;
  2450. //infostream<<"Player damage: "<<damage<<std::endl;
  2451. *damage_flash += 100.0;
  2452. *damage_flash += 8.0 * event.player_damage.amount;
  2453. player->hurt_tilt_timer = 1.5;
  2454. player->hurt_tilt_strength = event.player_damage.amount / 4;
  2455. player->hurt_tilt_strength = rangelim(player->hurt_tilt_strength, 1.0, 4.0);
  2456. MtEvent *e = new SimpleTriggerEvent("PlayerDamage");
  2457. gamedef->event()->put(e);
  2458. } else if (event.type == CE_PLAYER_FORCE_MOVE) {
  2459. cam->camera_yaw = event.player_force_move.yaw;
  2460. cam->camera_pitch = event.player_force_move.pitch;
  2461. } else if (event.type == CE_DEATHSCREEN) {
  2462. show_deathscreen(&current_formspec, client, gamedef, texture_src,
  2463. device, client);
  2464. chat_backend->addMessage(L"", L"You died.");
  2465. /* Handle visualization */
  2466. *damage_flash = 0;
  2467. player->hurt_tilt_timer = 0;
  2468. player->hurt_tilt_strength = 0;
  2469. } else if (event.type == CE_SHOW_FORMSPEC) {
  2470. FormspecFormSource *fs_src =
  2471. new FormspecFormSource(*(event.show_formspec.formspec));
  2472. TextDestPlayerInventory *txt_dst =
  2473. new TextDestPlayerInventory(client, *(event.show_formspec.formname));
  2474. create_formspec_menu(&current_formspec, client, gamedef,
  2475. texture_src, device, fs_src, txt_dst, client);
  2476. delete(event.show_formspec.formspec);
  2477. delete(event.show_formspec.formname);
  2478. } else if ((event.type == CE_SPAWN_PARTICLE) ||
  2479. (event.type == CE_ADD_PARTICLESPAWNER) ||
  2480. (event.type == CE_DELETE_PARTICLESPAWNER)) {
  2481. client->getParticleManager()->handleParticleEvent(&event, gamedef,
  2482. smgr, player);
  2483. } else if (event.type == CE_HUDADD) {
  2484. u32 id = event.hudadd.id;
  2485. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2486. HudElement *e = player->getHud(id);
  2487. if (e != NULL) {
  2488. delete event.hudadd.pos;
  2489. delete event.hudadd.name;
  2490. delete event.hudadd.scale;
  2491. delete event.hudadd.text;
  2492. delete event.hudadd.align;
  2493. delete event.hudadd.offset;
  2494. delete event.hudadd.world_pos;
  2495. delete event.hudadd.size;
  2496. continue;
  2497. }
  2498. e = new HudElement;
  2499. e->type = (HudElementType)event.hudadd.type;
  2500. e->pos = *event.hudadd.pos;
  2501. e->name = *event.hudadd.name;
  2502. e->scale = *event.hudadd.scale;
  2503. e->text = *event.hudadd.text;
  2504. e->number = event.hudadd.number;
  2505. e->item = event.hudadd.item;
  2506. e->dir = event.hudadd.dir;
  2507. e->align = *event.hudadd.align;
  2508. e->offset = *event.hudadd.offset;
  2509. e->world_pos = *event.hudadd.world_pos;
  2510. e->size = *event.hudadd.size;
  2511. u32 new_id = player->addHud(e);
  2512. //if this isn't true our huds aren't consistent
  2513. assert(new_id == id);
  2514. delete event.hudadd.pos;
  2515. delete event.hudadd.name;
  2516. delete event.hudadd.scale;
  2517. delete event.hudadd.text;
  2518. delete event.hudadd.align;
  2519. delete event.hudadd.offset;
  2520. delete event.hudadd.world_pos;
  2521. delete event.hudadd.size;
  2522. } else if (event.type == CE_HUDRM) {
  2523. HudElement *e = player->removeHud(event.hudrm.id);
  2524. if (e != NULL)
  2525. delete(e);
  2526. } else if (event.type == CE_HUDCHANGE) {
  2527. u32 id = event.hudchange.id;
  2528. HudElement *e = player->getHud(id);
  2529. if (e == NULL) {
  2530. delete event.hudchange.v3fdata;
  2531. delete event.hudchange.v2fdata;
  2532. delete event.hudchange.sdata;
  2533. delete event.hudchange.v2s32data;
  2534. continue;
  2535. }
  2536. switch (event.hudchange.stat) {
  2537. case HUD_STAT_POS:
  2538. e->pos = *event.hudchange.v2fdata;
  2539. break;
  2540. case HUD_STAT_NAME:
  2541. e->name = *event.hudchange.sdata;
  2542. break;
  2543. case HUD_STAT_SCALE:
  2544. e->scale = *event.hudchange.v2fdata;
  2545. break;
  2546. case HUD_STAT_TEXT:
  2547. e->text = *event.hudchange.sdata;
  2548. break;
  2549. case HUD_STAT_NUMBER:
  2550. e->number = event.hudchange.data;
  2551. break;
  2552. case HUD_STAT_ITEM:
  2553. e->item = event.hudchange.data;
  2554. break;
  2555. case HUD_STAT_DIR:
  2556. e->dir = event.hudchange.data;
  2557. break;
  2558. case HUD_STAT_ALIGN:
  2559. e->align = *event.hudchange.v2fdata;
  2560. break;
  2561. case HUD_STAT_OFFSET:
  2562. e->offset = *event.hudchange.v2fdata;
  2563. break;
  2564. case HUD_STAT_WORLD_POS:
  2565. e->world_pos = *event.hudchange.v3fdata;
  2566. break;
  2567. case HUD_STAT_SIZE:
  2568. e->size = *event.hudchange.v2s32data;
  2569. break;
  2570. }
  2571. delete event.hudchange.v3fdata;
  2572. delete event.hudchange.v2fdata;
  2573. delete event.hudchange.sdata;
  2574. delete event.hudchange.v2s32data;
  2575. } else if (event.type == CE_SET_SKY) {
  2576. sky->setVisible(false);
  2577. if (skybox) {
  2578. skybox->remove();
  2579. skybox = NULL;
  2580. }
  2581. // Handle according to type
  2582. if (*event.set_sky.type == "regular") {
  2583. sky->setVisible(true);
  2584. } else if (*event.set_sky.type == "skybox" &&
  2585. event.set_sky.params->size() == 6) {
  2586. sky->setFallbackBgColor(*event.set_sky.bgcolor);
  2587. skybox = smgr->addSkyBoxSceneNode(
  2588. texture_src->getTexture((*event.set_sky.params)[0]),
  2589. texture_src->getTexture((*event.set_sky.params)[1]),
  2590. texture_src->getTexture((*event.set_sky.params)[2]),
  2591. texture_src->getTexture((*event.set_sky.params)[3]),
  2592. texture_src->getTexture((*event.set_sky.params)[4]),
  2593. texture_src->getTexture((*event.set_sky.params)[5]));
  2594. }
  2595. // Handle everything else as plain color
  2596. else {
  2597. if (*event.set_sky.type != "plain")
  2598. infostream << "Unknown sky type: "
  2599. << (*event.set_sky.type) << std::endl;
  2600. sky->setFallbackBgColor(*event.set_sky.bgcolor);
  2601. }
  2602. delete event.set_sky.bgcolor;
  2603. delete event.set_sky.type;
  2604. delete event.set_sky.params;
  2605. } else if (event.type == CE_OVERRIDE_DAY_NIGHT_RATIO) {
  2606. bool enable = event.override_day_night_ratio.do_override;
  2607. u32 value = event.override_day_night_ratio.ratio_f * 1000;
  2608. client->getEnv().setDayNightRatioOverride(enable, value);
  2609. }
  2610. }
  2611. }
  2612. void Game::updateCamera(VolatileRunFlags *flags, u32 busy_time,
  2613. f32 dtime, float time_from_last_punch)
  2614. {
  2615. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2616. /*
  2617. For interaction purposes, get info about the held item
  2618. - What item is it?
  2619. - Is it a usable item?
  2620. - Can it point to liquids?
  2621. */
  2622. ItemStack playeritem;
  2623. {
  2624. InventoryList *mlist = local_inventory->getList("main");
  2625. if (mlist && client->getPlayerItem() < mlist->getSize())
  2626. playeritem = mlist->getItem(client->getPlayerItem());
  2627. }
  2628. ToolCapabilities playeritem_toolcap =
  2629. playeritem.getToolCapabilities(itemdef_manager);
  2630. v3s16 old_camera_offset = camera->getOffset();
  2631. if (input->wasKeyDown(keycache.key[KeyCache::KEYMAP_ID_CAMERA_MODE])) {
  2632. camera->toggleCameraMode();
  2633. GenericCAO *playercao = player->getCAO();
  2634. assert(playercao != NULL);
  2635. playercao->setVisible(camera->getCameraMode() > CAMERA_MODE_FIRST);
  2636. }
  2637. float full_punch_interval = playeritem_toolcap.full_punch_interval;
  2638. float tool_reload_ratio = time_from_last_punch / full_punch_interval;
  2639. tool_reload_ratio = MYMIN(tool_reload_ratio, 1.0);
  2640. camera->update(player, dtime, busy_time / 1000.0f, tool_reload_ratio,
  2641. client->getEnv());
  2642. camera->step(dtime);
  2643. v3f camera_position = camera->getPosition();
  2644. v3f camera_direction = camera->getDirection();
  2645. f32 camera_fov = camera->getFovMax();
  2646. v3s16 camera_offset = camera->getOffset();
  2647. flags->camera_offset_changed = (camera_offset != old_camera_offset);
  2648. if (!flags->disable_camera_update) {
  2649. client->getEnv().getClientMap().updateCamera(camera_position,
  2650. camera_direction, camera_fov, camera_offset);
  2651. if (flags->camera_offset_changed) {
  2652. client->updateCameraOffset(camera_offset);
  2653. client->getEnv().updateCameraOffset(camera_offset);
  2654. if (clouds)
  2655. clouds->updateCameraOffset(camera_offset);
  2656. }
  2657. }
  2658. }
  2659. void Game::updateSound(f32 dtime)
  2660. {
  2661. // Update sound listener
  2662. v3s16 camera_offset = camera->getOffset();
  2663. sound->updateListener(camera->getCameraNode()->getPosition() + intToFloat(camera_offset, BS),
  2664. v3f(0, 0, 0), // velocity
  2665. camera->getDirection(),
  2666. camera->getCameraNode()->getUpVector());
  2667. sound->setListenerGain(g_settings->getFloat("sound_volume"));
  2668. // Update sound maker
  2669. soundmaker->step(dtime);
  2670. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2671. ClientMap &map = client->getEnv().getClientMap();
  2672. MapNode n = map.getNodeNoEx(player->getStandingNodePos());
  2673. soundmaker->m_player_step_sound = nodedef_manager->get(n).sound_footstep;
  2674. }
  2675. void Game::processPlayerInteraction(std::vector<aabb3f> &highlight_boxes,
  2676. GameRunData *runData, f32 dtime, bool show_hud, bool show_debug)
  2677. {
  2678. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2679. ItemStack playeritem;
  2680. {
  2681. InventoryList *mlist = local_inventory->getList("main");
  2682. if (mlist && client->getPlayerItem() < mlist->getSize())
  2683. playeritem = mlist->getItem(client->getPlayerItem());
  2684. }
  2685. const ItemDefinition &playeritem_def =
  2686. playeritem.getDefinition(itemdef_manager);
  2687. v3f player_position = player->getPosition();
  2688. v3f camera_position = camera->getPosition();
  2689. v3f camera_direction = camera->getDirection();
  2690. v3s16 camera_offset = camera->getOffset();
  2691. /*
  2692. Calculate what block is the crosshair pointing to
  2693. */
  2694. f32 d = playeritem_def.range; // max. distance
  2695. f32 d_hand = itemdef_manager->get("").range;
  2696. if (d < 0 && d_hand >= 0)
  2697. d = d_hand;
  2698. else if (d < 0)
  2699. d = 4.0;
  2700. core::line3d<f32> shootline;
  2701. if (camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT) {
  2702. shootline = core::line3d<f32>(camera_position,
  2703. camera_position + camera_direction * BS * (d + 1));
  2704. } else {
  2705. // prevent player pointing anything in front-view
  2706. if (camera->getCameraMode() == CAMERA_MODE_THIRD_FRONT)
  2707. shootline = core::line3d<f32>(0, 0, 0, 0, 0, 0);
  2708. }
  2709. #ifdef HAVE_TOUCHSCREENGUI
  2710. if ((g_settings->getBool("touchtarget")) && (g_touchscreengui)) {
  2711. shootline = g_touchscreengui->getShootline();
  2712. shootline.start += intToFloat(camera_offset, BS);
  2713. shootline.end += intToFloat(camera_offset, BS);
  2714. }
  2715. #endif
  2716. PointedThing pointed = getPointedThing(
  2717. // input
  2718. client, player_position, camera_direction,
  2719. camera_position, shootline, d,
  2720. playeritem_def.liquids_pointable,
  2721. !runData->ldown_for_dig,
  2722. camera_offset,
  2723. // output
  2724. highlight_boxes,
  2725. runData->selected_object);
  2726. if (pointed != runData->pointed_old) {
  2727. infostream << "Pointing at " << pointed.dump() << std::endl;
  2728. if (m_cache_enable_node_highlighting) {
  2729. if (pointed.type == POINTEDTHING_NODE) {
  2730. client->setHighlighted(pointed.node_undersurface, show_hud);
  2731. } else {
  2732. client->setHighlighted(pointed.node_undersurface, false);
  2733. }
  2734. }
  2735. }
  2736. /*
  2737. Stop digging when
  2738. - releasing left mouse button
  2739. - pointing away from node
  2740. */
  2741. if (runData->digging) {
  2742. if (input->getLeftReleased()) {
  2743. infostream << "Left button released"
  2744. << " (stopped digging)" << std::endl;
  2745. runData->digging = false;
  2746. } else if (pointed != runData->pointed_old) {
  2747. if (pointed.type == POINTEDTHING_NODE
  2748. && runData->pointed_old.type == POINTEDTHING_NODE
  2749. && pointed.node_undersurface
  2750. == runData->pointed_old.node_undersurface) {
  2751. // Still pointing to the same node, but a different face.
  2752. // Don't reset.
  2753. } else {
  2754. infostream << "Pointing away from node"
  2755. << " (stopped digging)" << std::endl;
  2756. runData->digging = false;
  2757. }
  2758. }
  2759. if (!runData->digging) {
  2760. client->interact(1, runData->pointed_old);
  2761. client->setCrack(-1, v3s16(0, 0, 0));
  2762. runData->dig_time = 0.0;
  2763. }
  2764. }
  2765. if (!runData->digging && runData->ldown_for_dig && !input->getLeftState()) {
  2766. runData->ldown_for_dig = false;
  2767. }
  2768. runData->left_punch = false;
  2769. soundmaker->m_player_leftpunch_sound.name = "";
  2770. if (input->getRightState())
  2771. runData->repeat_rightclick_timer += dtime;
  2772. else
  2773. runData->repeat_rightclick_timer = 0;
  2774. if (playeritem_def.usable && input->getLeftState()) {
  2775. if (input->getLeftClicked())
  2776. client->interact(4, pointed);
  2777. } else if (pointed.type == POINTEDTHING_NODE) {
  2778. ToolCapabilities playeritem_toolcap =
  2779. playeritem.getToolCapabilities(itemdef_manager);
  2780. handlePointingAtNode(runData, pointed, playeritem_def,
  2781. playeritem_toolcap, dtime);
  2782. } else if (pointed.type == POINTEDTHING_OBJECT) {
  2783. handlePointingAtObject(runData, pointed, playeritem,
  2784. player_position, show_debug);
  2785. } else if (input->getLeftState()) {
  2786. // When button is held down in air, show continuous animation
  2787. runData->left_punch = true;
  2788. }
  2789. runData->pointed_old = pointed;
  2790. if (runData->left_punch || input->getLeftClicked())
  2791. camera->setDigging(0); // left click animation
  2792. input->resetLeftClicked();
  2793. input->resetRightClicked();
  2794. input->resetLeftReleased();
  2795. input->resetRightReleased();
  2796. }
  2797. void Game::handlePointingAtNode(GameRunData *runData,
  2798. const PointedThing &pointed, const ItemDefinition &playeritem_def,
  2799. const ToolCapabilities &playeritem_toolcap, f32 dtime)
  2800. {
  2801. v3s16 nodepos = pointed.node_undersurface;
  2802. v3s16 neighbourpos = pointed.node_abovesurface;
  2803. /*
  2804. Check information text of node
  2805. */
  2806. ClientMap &map = client->getEnv().getClientMap();
  2807. NodeMetadata *meta = map.getNodeMetadata(nodepos);
  2808. if (meta) {
  2809. infotext = narrow_to_wide(meta->getString("infotext"));
  2810. } else {
  2811. MapNode n = map.getNodeNoEx(nodepos);
  2812. if (nodedef_manager->get(n).tiledef[0].name == "unknown_node.png") {
  2813. infotext = L"Unknown node: ";
  2814. infotext += narrow_to_wide(nodedef_manager->get(n).name);
  2815. }
  2816. }
  2817. if (runData->nodig_delay_timer <= 0.0 && input->getLeftState()
  2818. && client->checkPrivilege("interact")) {
  2819. handleDigging(runData, pointed, nodepos, playeritem_toolcap, dtime);
  2820. }
  2821. if ((input->getRightClicked() ||
  2822. runData->repeat_rightclick_timer >= m_repeat_right_click_time) &&
  2823. client->checkPrivilege("interact")) {
  2824. runData->repeat_rightclick_timer = 0;
  2825. infostream << "Ground right-clicked" << std::endl;
  2826. if (meta && meta->getString("formspec") != "" && !random_input
  2827. && !input->isKeyDown(getKeySetting("keymap_sneak"))) {
  2828. infostream << "Launching custom inventory view" << std::endl;
  2829. InventoryLocation inventoryloc;
  2830. inventoryloc.setNodeMeta(nodepos);
  2831. NodeMetadataFormSource *fs_src = new NodeMetadataFormSource(
  2832. &client->getEnv().getClientMap(), nodepos);
  2833. TextDest *txt_dst = new TextDestNodeMetadata(nodepos, client);
  2834. create_formspec_menu(&current_formspec, client, gamedef,
  2835. texture_src, device, fs_src, txt_dst, client);
  2836. current_formspec->setFormSpec(meta->getString("formspec"), inventoryloc);
  2837. } else {
  2838. // Report right click to server
  2839. camera->setDigging(1); // right click animation (always shown for feedback)
  2840. // If the wielded item has node placement prediction,
  2841. // make that happen
  2842. bool placed = nodePlacementPrediction(*client,
  2843. playeritem_def,
  2844. nodepos, neighbourpos);
  2845. if (placed) {
  2846. // Report to server
  2847. client->interact(3, pointed);
  2848. // Read the sound
  2849. soundmaker->m_player_rightpunch_sound =
  2850. playeritem_def.sound_place;
  2851. } else {
  2852. soundmaker->m_player_rightpunch_sound =
  2853. SimpleSoundSpec();
  2854. }
  2855. if (playeritem_def.node_placement_prediction == "" ||
  2856. nodedef_manager->get(map.getNodeNoEx(nodepos)).rightclickable)
  2857. client->interact(3, pointed); // Report to server
  2858. }
  2859. }
  2860. }
  2861. void Game::handlePointingAtObject(GameRunData *runData,
  2862. const PointedThing &pointed,
  2863. const ItemStack &playeritem,
  2864. const v3f &player_position,
  2865. bool show_debug)
  2866. {
  2867. infotext = narrow_to_wide(runData->selected_object->infoText());
  2868. if (infotext == L"" && show_debug) {
  2869. infotext = narrow_to_wide(runData->selected_object->debugInfoText());
  2870. }
  2871. if (input->getLeftState()) {
  2872. bool do_punch = false;
  2873. bool do_punch_damage = false;
  2874. if (runData->object_hit_delay_timer <= 0.0) {
  2875. do_punch = true;
  2876. do_punch_damage = true;
  2877. runData->object_hit_delay_timer = object_hit_delay;
  2878. }
  2879. if (input->getLeftClicked())
  2880. do_punch = true;
  2881. if (do_punch) {
  2882. infostream << "Left-clicked object" << std::endl;
  2883. runData->left_punch = true;
  2884. }
  2885. if (do_punch_damage) {
  2886. // Report direct punch
  2887. v3f objpos = runData->selected_object->getPosition();
  2888. v3f dir = (objpos - player_position).normalize();
  2889. bool disable_send = runData->selected_object->directReportPunch(
  2890. dir, &playeritem, runData->time_from_last_punch);
  2891. runData->time_from_last_punch = 0;
  2892. if (!disable_send)
  2893. client->interact(0, pointed);
  2894. }
  2895. } else if (input->getRightClicked()) {
  2896. infostream << "Right-clicked object" << std::endl;
  2897. client->interact(3, pointed); // place
  2898. }
  2899. }
  2900. void Game::handleDigging(GameRunData *runData,
  2901. const PointedThing &pointed, const v3s16 &nodepos,
  2902. const ToolCapabilities &playeritem_toolcap, f32 dtime)
  2903. {
  2904. if (!runData->digging) {
  2905. infostream << "Started digging" << std::endl;
  2906. client->interact(0, pointed);
  2907. runData->digging = true;
  2908. runData->ldown_for_dig = true;
  2909. }
  2910. LocalPlayer *player = client->getEnv().getLocalPlayer();
  2911. ClientMap &map = client->getEnv().getClientMap();
  2912. MapNode n = client->getEnv().getClientMap().getNodeNoEx(nodepos);
  2913. // NOTE: Similar piece of code exists on the server side for
  2914. // cheat detection.
  2915. // Get digging parameters
  2916. DigParams params = getDigParams(nodedef_manager->get(n).groups,
  2917. &playeritem_toolcap);
  2918. // If can't dig, try hand
  2919. if (!params.diggable) {
  2920. const ItemDefinition &hand = itemdef_manager->get("");
  2921. const ToolCapabilities *tp = hand.tool_capabilities;
  2922. if (tp)
  2923. params = getDigParams(nodedef_manager->get(n).groups, tp);
  2924. }
  2925. if (params.diggable == false) {
  2926. // I guess nobody will wait for this long
  2927. runData->dig_time_complete = 10000000.0;
  2928. } else {
  2929. runData->dig_time_complete = params.time;
  2930. if (m_cache_enable_particles) {
  2931. const ContentFeatures &features =
  2932. client->getNodeDefManager()->get(n);
  2933. client->getParticleManager()->addPunchingParticles(gamedef, smgr,
  2934. player, nodepos, features.tiles);
  2935. }
  2936. }
  2937. if (runData->dig_time_complete >= 0.001) {
  2938. runData->dig_index = (float)crack_animation_length
  2939. * runData->dig_time
  2940. / runData->dig_time_complete;
  2941. } else {
  2942. // This is for torches
  2943. runData->dig_index = crack_animation_length;
  2944. }
  2945. SimpleSoundSpec sound_dig = nodedef_manager->get(n).sound_dig;
  2946. if (sound_dig.exists() && params.diggable) {
  2947. if (sound_dig.name == "__group") {
  2948. if (params.main_group != "") {
  2949. soundmaker->m_player_leftpunch_sound.gain = 0.5;
  2950. soundmaker->m_player_leftpunch_sound.name =
  2951. std::string("default_dig_") +
  2952. params.main_group;
  2953. }
  2954. } else {
  2955. soundmaker->m_player_leftpunch_sound = sound_dig;
  2956. }
  2957. }
  2958. // Don't show cracks if not diggable
  2959. if (runData->dig_time_complete >= 100000.0) {
  2960. } else if (runData->dig_index < crack_animation_length) {
  2961. //TimeTaker timer("client.setTempMod");
  2962. //infostream<<"dig_index="<<dig_index<<std::endl;
  2963. client->setCrack(runData->dig_index, nodepos);
  2964. } else {
  2965. infostream << "Digging completed" << std::endl;
  2966. client->interact(2, pointed);
  2967. client->setCrack(-1, v3s16(0, 0, 0));
  2968. bool is_valid_position;
  2969. MapNode wasnode = map.getNodeNoEx(nodepos, &is_valid_position);
  2970. if (is_valid_position)
  2971. client->removeNode(nodepos);
  2972. if (m_cache_enable_particles) {
  2973. const ContentFeatures &features =
  2974. client->getNodeDefManager()->get(wasnode);
  2975. client->getParticleManager()->addDiggingParticles(gamedef, smgr,
  2976. player, nodepos, features.tiles);
  2977. }
  2978. runData->dig_time = 0;
  2979. runData->digging = false;
  2980. runData->nodig_delay_timer =
  2981. runData->dig_time_complete / (float)crack_animation_length;
  2982. // We don't want a corresponding delay to
  2983. // very time consuming nodes
  2984. if (runData->nodig_delay_timer > 0.3)
  2985. runData->nodig_delay_timer = 0.3;
  2986. // We want a slight delay to very little
  2987. // time consuming nodes
  2988. const float mindelay = 0.15;
  2989. if (runData->nodig_delay_timer < mindelay)
  2990. runData->nodig_delay_timer = mindelay;
  2991. // Send event to trigger sound
  2992. MtEvent *e = new NodeDugEvent(nodepos, wasnode);
  2993. gamedef->event()->put(e);
  2994. }
  2995. if (runData->dig_time_complete < 100000.0) {
  2996. runData->dig_time += dtime;
  2997. } else {
  2998. runData->dig_time = 0;
  2999. client->setCrack(-1, nodepos);
  3000. }
  3001. camera->setDigging(0); // left click animation
  3002. }
  3003. void Game::updateFrame(std::vector<aabb3f> &highlight_boxes,
  3004. ProfilerGraph *graph, RunStats *stats, GameRunData *runData,
  3005. f32 dtime, const VolatileRunFlags &flags, const CameraOrientation &cam)
  3006. {
  3007. LocalPlayer *player = client->getEnv().getLocalPlayer();
  3008. /*
  3009. Fog range
  3010. */
  3011. if (draw_control->range_all) {
  3012. runData->fog_range = 100000 * BS;
  3013. } else {
  3014. runData->fog_range = draw_control->wanted_range * BS
  3015. + 0.0 * MAP_BLOCKSIZE * BS;
  3016. runData->fog_range = MYMIN(
  3017. runData->fog_range,
  3018. (draw_control->farthest_drawn + 20) * BS);
  3019. runData->fog_range *= 0.9;
  3020. }
  3021. /*
  3022. Calculate general brightness
  3023. */
  3024. u32 daynight_ratio = client->getEnv().getDayNightRatio();
  3025. float time_brightness = decode_light_f((float)daynight_ratio / 1000.0);
  3026. float direct_brightness;
  3027. bool sunlight_seen;
  3028. if (g_settings->getBool("free_move")) {
  3029. direct_brightness = time_brightness;
  3030. sunlight_seen = true;
  3031. } else {
  3032. ScopeProfiler sp(g_profiler, "Detecting background light", SPT_AVG);
  3033. float old_brightness = sky->getBrightness();
  3034. direct_brightness = client->getEnv().getClientMap()
  3035. .getBackgroundBrightness(MYMIN(runData->fog_range * 1.2, 60 * BS),
  3036. daynight_ratio, (int)(old_brightness * 255.5), &sunlight_seen)
  3037. / 255.0;
  3038. }
  3039. float time_of_day = runData->time_of_day;
  3040. float time_of_day_smooth = runData->time_of_day_smooth;
  3041. time_of_day = client->getEnv().getTimeOfDayF();
  3042. const float maxsm = 0.05;
  3043. const float todsm = 0.05;
  3044. if (fabs(time_of_day - time_of_day_smooth) > maxsm &&
  3045. fabs(time_of_day - time_of_day_smooth + 1.0) > maxsm &&
  3046. fabs(time_of_day - time_of_day_smooth - 1.0) > maxsm)
  3047. time_of_day_smooth = time_of_day;
  3048. if (time_of_day_smooth > 0.8 && time_of_day < 0.2)
  3049. time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
  3050. + (time_of_day + 1.0) * todsm;
  3051. else
  3052. time_of_day_smooth = time_of_day_smooth * (1.0 - todsm)
  3053. + time_of_day * todsm;
  3054. runData->time_of_day = time_of_day;
  3055. runData->time_of_day_smooth = time_of_day_smooth;
  3056. sky->update(time_of_day_smooth, time_brightness, direct_brightness,
  3057. sunlight_seen, camera->getCameraMode(), player->getYaw(),
  3058. player->getPitch());
  3059. /*
  3060. Update clouds
  3061. */
  3062. if (clouds) {
  3063. v3f player_position = player->getPosition();
  3064. if (sky->getCloudsVisible()) {
  3065. clouds->setVisible(true);
  3066. clouds->step(dtime);
  3067. clouds->update(v2f(player_position.X, player_position.Z),
  3068. sky->getCloudColor());
  3069. } else {
  3070. clouds->setVisible(false);
  3071. }
  3072. }
  3073. /*
  3074. Update particles
  3075. */
  3076. client->getParticleManager()->step(dtime);
  3077. /*
  3078. Fog
  3079. */
  3080. if (m_cache_enable_fog && !flags.force_fog_off) {
  3081. driver->setFog(
  3082. sky->getBgColor(),
  3083. video::EFT_FOG_LINEAR,
  3084. runData->fog_range * 0.4,
  3085. runData->fog_range * 1.0,
  3086. 0.01,
  3087. false, // pixel fog
  3088. false // range fog
  3089. );
  3090. } else {
  3091. driver->setFog(
  3092. sky->getBgColor(),
  3093. video::EFT_FOG_LINEAR,
  3094. 100000 * BS,
  3095. 110000 * BS,
  3096. 0.01,
  3097. false, // pixel fog
  3098. false // range fog
  3099. );
  3100. }
  3101. /*
  3102. Get chat messages from client
  3103. */
  3104. v2u32 screensize = driver->getScreenSize();
  3105. updateChat(*client, dtime, flags.show_debug, screensize,
  3106. flags.show_chat, runData->profiler_current_page,
  3107. *chat_backend, guitext_chat);
  3108. /*
  3109. Inventory
  3110. */
  3111. if (client->getPlayerItem() != runData->new_playeritem)
  3112. client->selectPlayerItem(runData->new_playeritem);
  3113. // Update local inventory if it has changed
  3114. if (client->getLocalInventoryUpdated()) {
  3115. //infostream<<"Updating local inventory"<<std::endl;
  3116. client->getLocalInventory(*local_inventory);
  3117. runData->update_wielded_item_trigger = true;
  3118. }
  3119. if (runData->update_wielded_item_trigger) {
  3120. // Update wielded tool
  3121. InventoryList *mlist = local_inventory->getList("main");
  3122. if (mlist && (client->getPlayerItem() < mlist->getSize())) {
  3123. ItemStack item = mlist->getItem(client->getPlayerItem());
  3124. camera->wield(item);
  3125. }
  3126. runData->update_wielded_item_trigger = false;
  3127. }
  3128. /*
  3129. Update block draw list every 200ms or when camera direction has
  3130. changed much
  3131. */
  3132. runData->update_draw_list_timer += dtime;
  3133. v3f camera_direction = camera->getDirection();
  3134. if (runData->update_draw_list_timer >= 0.2
  3135. || runData->update_draw_list_last_cam_dir.getDistanceFrom(camera_direction) > 0.2
  3136. || flags.camera_offset_changed) {
  3137. runData->update_draw_list_timer = 0;
  3138. client->getEnv().getClientMap().updateDrawList(driver);
  3139. runData->update_draw_list_last_cam_dir = camera_direction;
  3140. }
  3141. updateGui(&runData->statustext_time, *stats, *runData, dtime, flags, cam);
  3142. /*
  3143. make sure menu is on top
  3144. 1. Delete formspec menu reference if menu was removed
  3145. 2. Else, make sure formspec menu is on top
  3146. */
  3147. if (current_formspec) {
  3148. if (current_formspec->getReferenceCount() == 1) {
  3149. current_formspec->drop();
  3150. current_formspec = NULL;
  3151. } else if (!noMenuActive()) {
  3152. guiroot->bringToFront(current_formspec);
  3153. }
  3154. }
  3155. /*
  3156. Drawing begins
  3157. */
  3158. video::SColor skycolor = sky->getSkyColor();
  3159. TimeTaker tt_draw("mainloop: draw");
  3160. {
  3161. TimeTaker timer("beginScene");
  3162. driver->beginScene(true, true, skycolor);
  3163. stats->beginscenetime = timer.stop(true);
  3164. }
  3165. draw_scene(driver, smgr, *camera, *client, player, *hud, guienv,
  3166. highlight_boxes, screensize, skycolor, flags.show_hud);
  3167. /*
  3168. Profiler graph
  3169. */
  3170. if (flags.show_profiler_graph)
  3171. graph->draw(10, screensize.Y - 10, driver, g_fontengine->getFont());
  3172. /*
  3173. Damage flash
  3174. */
  3175. if (runData->damage_flash > 0.0) {
  3176. video::SColor color(std::min(runData->damage_flash, 180.0f),
  3177. 180,
  3178. 0,
  3179. 0);
  3180. driver->draw2DRectangle(color,
  3181. core::rect<s32>(0, 0, screensize.X, screensize.Y),
  3182. NULL);
  3183. runData->damage_flash -= 100.0 * dtime;
  3184. }
  3185. /*
  3186. Damage camera tilt
  3187. */
  3188. if (player->hurt_tilt_timer > 0.0) {
  3189. player->hurt_tilt_timer -= dtime * 5;
  3190. if (player->hurt_tilt_timer < 0)
  3191. player->hurt_tilt_strength = 0;
  3192. }
  3193. /*
  3194. End scene
  3195. */
  3196. {
  3197. TimeTaker timer("endScene");
  3198. driver->endScene();
  3199. stats->endscenetime = timer.stop(true);
  3200. }
  3201. stats->drawtime = tt_draw.stop(true);
  3202. g_profiler->graphAdd("mainloop_draw", stats->drawtime / 1000.0f);
  3203. }
  3204. void Game::updateGui(float *statustext_time, const RunStats &stats,
  3205. const GameRunData& runData, f32 dtime, const VolatileRunFlags &flags,
  3206. const CameraOrientation &cam)
  3207. {
  3208. v2u32 screensize = driver->getScreenSize();
  3209. LocalPlayer *player = client->getEnv().getLocalPlayer();
  3210. v3f player_position = player->getPosition();
  3211. if (flags.show_debug) {
  3212. static float drawtime_avg = 0;
  3213. drawtime_avg = drawtime_avg * 0.95 + stats.drawtime * 0.05;
  3214. u16 fps = 1.0 / stats.dtime_jitter.avg;
  3215. //s32 fps = driver->getFPS();
  3216. std::ostringstream os(std::ios_base::binary);
  3217. os << std::fixed
  3218. << "Minetest " << minetest_version_hash
  3219. << " FPS = " << fps
  3220. << " (R: range_all=" << draw_control->range_all << ")"
  3221. << std::setprecision(0)
  3222. << " drawtime = " << drawtime_avg
  3223. << std::setprecision(1)
  3224. << ", dtime_jitter = "
  3225. << (stats.dtime_jitter.max_fraction * 100.0) << " %"
  3226. << std::setprecision(1)
  3227. << ", v_range = " << draw_control->wanted_range
  3228. << std::setprecision(3)
  3229. << ", RTT = " << client->getRTT();
  3230. guitext->setText(narrow_to_wide(os.str()).c_str());
  3231. guitext->setVisible(true);
  3232. } else if (flags.show_hud || flags.show_chat) {
  3233. std::ostringstream os(std::ios_base::binary);
  3234. os << "Minetest " << minetest_version_hash;
  3235. guitext->setText(narrow_to_wide(os.str()).c_str());
  3236. guitext->setVisible(true);
  3237. } else {
  3238. guitext->setVisible(false);
  3239. }
  3240. if (guitext->isVisible()) {
  3241. core::rect<s32> rect(
  3242. 5, 5,
  3243. screensize.X, 5 + g_fontengine->getTextHeight()
  3244. );
  3245. guitext->setRelativePosition(rect);
  3246. }
  3247. if (flags.show_debug) {
  3248. std::ostringstream os(std::ios_base::binary);
  3249. os << std::setprecision(1) << std::fixed
  3250. << "(" << (player_position.X / BS)
  3251. << ", " << (player_position.Y / BS)
  3252. << ", " << (player_position.Z / BS)
  3253. << ") (yaw=" << (wrapDegrees_0_360(cam.camera_yaw))
  3254. << ") (seed = " << ((u64)client->getMapSeed())
  3255. << ")";
  3256. if (runData.pointed_old.type == POINTEDTHING_NODE) {
  3257. ClientMap &map = client->getEnv().getClientMap();
  3258. const INodeDefManager *nodedef = client->getNodeDefManager();
  3259. MapNode n = map.getNodeNoEx(runData.pointed_old.node_undersurface);
  3260. if (n.getContent() != CONTENT_IGNORE && nodedef->get(n).name != "unknown") {
  3261. const ContentFeatures &features = nodedef->get(n);
  3262. os << " (pointing_at = " << nodedef->get(n).name
  3263. << " - " << features.tiledef[0].name.c_str()
  3264. << ")";
  3265. }
  3266. }
  3267. guitext2->setText(narrow_to_wide(os.str()).c_str());
  3268. guitext2->setVisible(true);
  3269. core::rect<s32> rect(
  3270. 5, 5 + g_fontengine->getTextHeight(),
  3271. screensize.X, 5 + g_fontengine->getTextHeight() * 2
  3272. );
  3273. guitext2->setRelativePosition(rect);
  3274. } else {
  3275. guitext2->setVisible(false);
  3276. }
  3277. guitext_info->setText(infotext.c_str());
  3278. guitext_info->setVisible(flags.show_hud && g_menumgr.menuCount() == 0);
  3279. float statustext_time_max = 1.5;
  3280. if (!statustext.empty()) {
  3281. *statustext_time += dtime;
  3282. if (*statustext_time >= statustext_time_max) {
  3283. statustext = L"";
  3284. *statustext_time = 0;
  3285. }
  3286. }
  3287. guitext_status->setText(statustext.c_str());
  3288. guitext_status->setVisible(!statustext.empty());
  3289. if (!statustext.empty()) {
  3290. s32 status_width = guitext_status->getTextWidth();
  3291. s32 status_height = guitext_status->getTextHeight();
  3292. s32 status_y = screensize.Y - 150;
  3293. s32 status_x = (screensize.X - status_width) / 2;
  3294. core::rect<s32> rect(
  3295. status_x , status_y - status_height,
  3296. status_x + status_width, status_y
  3297. );
  3298. guitext_status->setRelativePosition(rect);
  3299. // Fade out
  3300. video::SColor initial_color(255, 0, 0, 0);
  3301. if (guienv->getSkin())
  3302. initial_color = guienv->getSkin()->getColor(gui::EGDC_BUTTON_TEXT);
  3303. video::SColor final_color = initial_color;
  3304. final_color.setAlpha(0);
  3305. video::SColor fade_color = initial_color.getInterpolated_quadratic(
  3306. initial_color, final_color,
  3307. pow(*statustext_time / statustext_time_max, 2.0f));
  3308. guitext_status->setOverrideColor(fade_color);
  3309. guitext_status->enableOverrideColor(true);
  3310. }
  3311. }
  3312. /* Log times and stuff for visualization */
  3313. inline void Game::updateProfilerGraphs(ProfilerGraph *graph)
  3314. {
  3315. Profiler::GraphValues values;
  3316. g_profiler->graphGet(values);
  3317. graph->put(values);
  3318. }
  3319. /****************************************************************************
  3320. Misc
  3321. ****************************************************************************/
  3322. /* On some computers framerate doesn't seem to be automatically limited
  3323. */
  3324. inline void Game::limitFps(FpsControl *fps_timings, f32 *dtime)
  3325. {
  3326. // not using getRealTime is necessary for wine
  3327. device->getTimer()->tick(); // Maker sure device time is up-to-date
  3328. u32 time = device->getTimer()->getTime();
  3329. u32 last_time = fps_timings->last_time;
  3330. if (time > last_time) // Make sure time hasn't overflowed
  3331. fps_timings->busy_time = time - last_time;
  3332. else
  3333. fps_timings->busy_time = 0;
  3334. u32 frametime_min = 1000 / (g_menumgr.pausesGame()
  3335. ? g_settings->getFloat("pause_fps_max")
  3336. : g_settings->getFloat("fps_max"));
  3337. if (fps_timings->busy_time < frametime_min) {
  3338. fps_timings->sleep_time = frametime_min - fps_timings->busy_time;
  3339. device->sleep(fps_timings->sleep_time);
  3340. } else {
  3341. fps_timings->sleep_time = 0;
  3342. }
  3343. /* Get the new value of the device timer. Note that device->sleep() may
  3344. * not sleep for the entire requested time as sleep may be interrupted and
  3345. * therefore it is arguably more accurate to get the new time from the
  3346. * device rather than calculating it by adding sleep_time to time.
  3347. */
  3348. device->getTimer()->tick(); // Update device timer
  3349. time = device->getTimer()->getTime();
  3350. if (time > last_time) // Make sure last_time hasn't overflowed
  3351. *dtime = (time - last_time) / 1000.0;
  3352. else
  3353. *dtime = 0;
  3354. fps_timings->last_time = time;
  3355. }
  3356. void Game::showOverlayMessage(const wchar_t *msg, float dtime,
  3357. int percent, bool draw_clouds)
  3358. {
  3359. draw_load_screen(msg, device, guienv, dtime, percent, draw_clouds);
  3360. delete[] msg;
  3361. }
  3362. /****************************************************************************
  3363. Shutdown / cleanup
  3364. ****************************************************************************/
  3365. void Game::extendedResourceCleanup()
  3366. {
  3367. // Extended resource accounting
  3368. infostream << "Irrlicht resources after cleanup:" << std::endl;
  3369. infostream << "\tRemaining meshes : "
  3370. << device->getSceneManager()->getMeshCache()->getMeshCount() << std::endl;
  3371. infostream << "\tRemaining textures : "
  3372. << driver->getTextureCount() << std::endl;
  3373. for (unsigned int i = 0; i < driver->getTextureCount(); i++) {
  3374. irr::video::ITexture *texture = driver->getTextureByIndex(i);
  3375. infostream << "\t\t" << i << ":" << texture->getName().getPath().c_str()
  3376. << std::endl;
  3377. }
  3378. clearTextureNameCache();
  3379. infostream << "\tRemaining materials: "
  3380. << driver-> getMaterialRendererCount()
  3381. << " (note: irrlicht doesn't support removing renderers)" << std::endl;
  3382. }
  3383. /****************************************************************************
  3384. extern function for launching the game
  3385. ****************************************************************************/
  3386. void the_game(bool *kill,
  3387. bool random_input,
  3388. InputHandler *input,
  3389. IrrlichtDevice *device,
  3390. const std::string &map_dir,
  3391. const std::string &playername,
  3392. const std::string &password,
  3393. const std::string &address, // If empty local server is created
  3394. u16 port,
  3395. std::wstring &error_message,
  3396. ChatBackend &chat_backend,
  3397. const SubgameSpec &gamespec, // Used for local game
  3398. bool simple_singleplayer_mode)
  3399. {
  3400. Game game;
  3401. /* Make a copy of the server address because if a local singleplayer server
  3402. * is created then this is updated and we don't want to change the value
  3403. * passed to us by the calling function
  3404. */
  3405. std::string server_address = address;
  3406. try {
  3407. if (game.startup(kill, random_input, input, device, map_dir,
  3408. playername, password, &server_address, port,
  3409. &error_message, &chat_backend, gamespec,
  3410. simple_singleplayer_mode)) {
  3411. game.run();
  3412. game.shutdown();
  3413. }
  3414. } catch (SerializationError &e) {
  3415. error_message = L"A serialization error occurred:\n"
  3416. + narrow_to_wide(e.what()) + L"\n\nThe server is probably "
  3417. L" running a different version of Minetest.";
  3418. errorstream << wide_to_narrow(error_message) << std::endl;
  3419. } catch (ServerError &e) {
  3420. error_message = narrow_to_wide(e.what());
  3421. errorstream << "ServerError: " << e.what() << std::endl;
  3422. } catch (ModError &e) {
  3423. errorstream << "ModError: " << e.what() << std::endl;
  3424. error_message = narrow_to_wide(e.what()) + wstrgettext("\nCheck debug.txt for details.");
  3425. }
  3426. }